Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,3 @@ jobs:

- name: Test app-core
run: cd kanban/frontend/packages/app-core && pnpm run test

- name: Bundle API (verify esbuild)
run: cd kanban && npm run build:api
13 changes: 12 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
# Dependencies
node_modules/
.worktrees/

# Build output
dist/

# Environment
.env
.env.local
*.local

# macOS
.DS_Store

# Project
.worktrees/
.ref
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ help:

# ── Install ──────────────────────────────────────────────
install:
cd kanban && npm install
cd kanban/backend && bun install
cd kanban/frontend && pnpm install

Expand Down
53 changes: 49 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,60 @@
# SuperCrew

SuperCrew combines two things: a structured AI development workflow (powered by superpowers skills) and a kanban-style team management app built with those same workflows.
SuperCrew combines two things: a structured AI development workflow (powered by superpowers skills) and a kanban-style feature management app built with those same workflows.

## What's Inside

### `plugins/supercrew/` — The Claude Code Plugin

AI-driven feature lifecycle management. Track features from idea to done using structured `.supercrew/features/` directories in your repo.

**Install in Claude Code:**

```bash
# 1. Add the local marketplace (use absolute path)
/plugin marketplace add /path/to/supercrew/plugins/supercrew

# 2. Install the plugin
/plugin install supercrew@supercrew-dev

# 3. Verify
/plugin list
```

**Commands:**

| Command | Description |
|---|---|
| `/supercrew:new-feature` | Create a new feature with meta.yaml, design.md, plan.md, log.md |
| `/supercrew:feature-status` | Show all features status table |
| `/supercrew:work-on` | Switch active feature for this session |

**Feature lifecycle:**

```
planning → designing → ready → active → blocked → done
```

Each feature lives in `.supercrew/features/<id>/` with four files:

| File | Purpose |
|---|---|
| `meta.yaml` | ID, title, status, priority, owner, dates |
| `design.md` | Requirements, architecture, constraints |
| `plan.md` | Task breakdown with checklist & progress |
| `log.md` | Chronological progress entries |

The plugin's SessionStart hook auto-detects `feature/<id>` branches and loads context.

### `kanban/` — The Crew App

A lightweight team kanban board with GitHub integration. Features:
A read-only kanban board that visualizes features from `.supercrew/features/`. Connect a GitHub repo and see your feature lifecycle at a glance.

Features:
- GitHub OAuth login
- Connect a GitHub repo to your project
- Board, People, Knowledge, Decisions pages
- Connect a GitHub repo with `.supercrew/features/`
- 6-column kanban board: Planning → Designing → Ready → Active → Blocked → Done
- Feature detail page with Overview / Design / Plan tabs
- i18n (English / Chinese)
- Dark mode

Expand Down
12 changes: 10 additions & 2 deletions kanban/backend/bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions kanban/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
"gray-matter": "^4.0.3",
"hono": "^4.12.3",
"jose": "^6.1.3",
"js-yaml": "^4.1.1",
"typescript": "^5.0.0"
},
"devDependencies": {
"@types/bun": "latest",
"@types/js-yaml": "^4.0.9",
"chokidar": "^4.0.3",
"vitest": "^3.2.4"
}
Expand Down
27 changes: 14 additions & 13 deletions kanban/backend/src/__tests__/github-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'
const mockFetch = vi.fn()
global.fetch = mockFetch as any

import { listTasksGH, readTaskGH } from '../store/github-store.js'
import { listFeaturesGH, getFeatureMetaGH } from '../store/github-store.js'

const TOKEN = 'ghp_test'
const OWNER = 'testowner'
Expand All @@ -12,37 +12,38 @@ const REPO = 'testrepo'
describe('github-store', () => {
beforeEach(() => { mockFetch.mockReset() })

it('listTasksGH returns empty array when directory missing', async () => {
it('listFeaturesGH returns empty array when directory missing', async () => {
mockFetch.mockResolvedValueOnce({ ok: false, status: 404 } as any)
const result = await listTasksGH(TOKEN, OWNER, REPO)
const result = await listFeaturesGH(TOKEN, OWNER, REPO)
expect(result).toEqual([])
})

it('listTasksGH skips template files', async () => {
it('listFeaturesGH lists feature directories and loads meta', async () => {
// First call: list features directory
mockFetch.mockResolvedValueOnce({
ok: true,
json: async () => [
{ name: '_template.md', type: 'file' },
{ name: 'ENG-001.md', type: 'file' },
{ name: 'feat-001', type: 'dir' },
{ name: 'README.md', type: 'file' }, // should be skipped
],
} as any)
// Second call: ghGet for feat-001/meta.yaml
mockFetch.mockResolvedValueOnce({
ok: true,
json: async () => ({
content: btoa('---\ntitle: Test Task\nstatus: backlog\npriority: P2\ncreated: 2026-01-01\nupdated: 2026-01-01\ntags: []\nblocks: []\nblocked_by: []\n---\nTask body'),
sha: 'abc123',
content: btoa('id: feat-001\ntitle: Test Feature\nstatus: planning\nowner: alice\npriority: P1\ncreated: "2026-01-01"\nupdated: "2026-01-01"\n'),
}),
} as any)

const result = await listTasksGH(TOKEN, OWNER, REPO)
const result = await listFeaturesGH(TOKEN, OWNER, REPO)
expect(result).toHaveLength(1)
expect(result[0].id).toBe('ENG-001')
expect(result[0].title).toBe('Test Task')
expect(result[0].id).toBe('feat-001')
expect(result[0].title).toBe('Test Feature')
})

it('readTaskGH returns null when file missing', async () => {
it('getFeatureMetaGH returns null when file missing', async () => {
mockFetch.mockResolvedValueOnce({ ok: false, status: 404 } as any)
const result = await readTaskGH(TOKEN, OWNER, REPO, 'ENG-999')
const result = await getFeatureMetaGH(TOKEN, OWNER, REPO, 'missing-feat')
expect(result).toBeNull()
})
})
4 changes: 2 additions & 2 deletions kanban/backend/src/__tests__/routes-tasks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ process.env.JWT_SECRET = 'test-jwt-secret'

const { app } = await import('../index.js')

describe('GET /api/tasks', () => {
describe('GET /api/features', () => {
it('returns 401 without auth', async () => {
const res = await app.request('/api/tasks')
const res = await app.request('/api/features')
expect(res.status).toBe(401)
})
})
Expand Down
Loading