diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..445fc4d
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,80 @@
+name: CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ branches: [main]
+
+jobs:
+ lint-and-test:
+ name: Lint, Typecheck & Test
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup Bun
+ uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+
+ - name: Install dependencies
+ run: |
+ cd tui
+ bun install --frozen-lockfile
+
+ - name: Run ESLint
+ run: |
+ cd tui
+ bun run lint
+
+ - name: Run TypeScript check
+ run: |
+ cd tui
+ bun run typecheck
+
+ - name: Run tests
+ run: |
+ cd tui
+ bun test
+
+ - name: Build
+ run: |
+ cd tui
+ bun run build
+
+ # Validate plugin structure
+ validate-plugin:
+ name: Validate Plugin Structure
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Check required files exist
+ run: |
+ test -f .claude-plugin/plugin.json || (echo "Missing plugin.json" && exit 1)
+ test -f hooks/hooks.json || (echo "Missing hooks.json" && exit 1)
+ test -f scripts/session-start.sh || (echo "Missing session-start.sh" && exit 1)
+ test -f scripts/capture-event.sh || (echo "Missing capture-event.sh" && exit 1)
+ echo "All required plugin files present"
+
+ - name: Validate plugin.json syntax
+ run: |
+ cat .claude-plugin/plugin.json | python3 -m json.tool > /dev/null
+ echo "plugin.json is valid JSON"
+
+ - name: Validate hooks.json syntax
+ run: |
+ cat hooks/hooks.json | python3 -m json.tool > /dev/null
+ echo "hooks.json is valid JSON"
+
+ - name: Check shell scripts are executable
+ run: |
+ test -x scripts/session-start.sh || chmod +x scripts/session-start.sh
+ test -x scripts/capture-event.sh || chmod +x scripts/capture-event.sh
+ test -x scripts/cleanup.sh || chmod +x scripts/cleanup.sh
+ echo "Shell scripts are executable"
diff --git a/tui/src/components/AgentList.test.tsx b/tui/src/components/AgentList.test.tsx
index 2948e5a..df1654d 100644
--- a/tui/src/components/AgentList.test.tsx
+++ b/tui/src/components/AgentList.test.tsx
@@ -126,4 +126,28 @@ describe('AgentList', () => {
const { lastFrame } = render();
expect(lastFrame()).toContain('1m');
});
+
+ it('should handle tool with filename-only target', () => {
+ const tools = [createTool({ target: 'file.ts' })];
+ const agents = [createAgent({ tools })];
+ const { lastFrame } = render();
+ expect(lastFrame()).toContain('file.ts');
+ });
+
+ it('should truncate long tool targets', () => {
+ const tools = [createTool({ target: '/path/to/very-long-filename-here.ts' })];
+ const agents = [createAgent({ tools })];
+ const { lastFrame } = render();
+ const frame = lastFrame() || '';
+ expect(frame).toContain('very-long-filen');
+ expect(frame).not.toContain('very-long-filename-here.ts');
+ });
+
+ it('should handle tool with empty target', () => {
+ const tools = [createTool({ target: '' })];
+ const agents = [createAgent({ tools })];
+ const { lastFrame } = render();
+ expect(lastFrame()).toContain('Grep');
+ expect(lastFrame()).not.toContain(': ');
+ });
});