@cliangdev/flux-plugin 0.0.0-dev.df9c61f → 0.1.0-dev.588ae42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -4
- package/bin/install.cjs +158 -24
- package/commands/flux.md +127 -84
- package/commands/linear.md +171 -0
- package/package.json +10 -12
- package/skills/flux-orchestrator/SKILL.md +58 -76
- package/src/__tests__/version.test.ts +37 -0
- package/src/adapters/local/.gitkeep +0 -0
- package/src/server/__tests__/config.test.ts +163 -0
- package/src/server/adapters/__tests__/a-client-linear.test.ts +197 -0
- package/src/server/adapters/__tests__/adapter-factory.test.ts +230 -0
- package/src/server/adapters/__tests__/dependency-ops.test.ts +395 -0
- package/src/server/adapters/__tests__/document-ops.test.ts +306 -0
- package/src/server/adapters/__tests__/linear-adapter.test.ts +91 -0
- package/src/server/adapters/__tests__/linear-config.test.ts +425 -0
- package/src/server/adapters/__tests__/linear-criteria-parser.test.ts +287 -0
- package/src/server/adapters/__tests__/linear-description-test.ts +238 -0
- package/src/server/adapters/__tests__/linear-epic-crud.test.ts +496 -0
- package/src/server/adapters/__tests__/linear-mappers-description.test.ts +276 -0
- package/src/server/adapters/__tests__/linear-mappers-epic.test.ts +294 -0
- package/src/server/adapters/__tests__/linear-mappers-prd.test.ts +300 -0
- package/src/server/adapters/__tests__/linear-mappers-task.test.ts +197 -0
- package/src/server/adapters/__tests__/linear-prd-crud.test.ts +620 -0
- package/src/server/adapters/__tests__/linear-stats.test.ts +450 -0
- package/src/server/adapters/__tests__/linear-task-crud.test.ts +534 -0
- package/src/server/adapters/__tests__/linear-types.test.ts +243 -0
- package/src/server/adapters/__tests__/status-ops.test.ts +441 -0
- package/src/server/adapters/factory.ts +90 -0
- package/src/server/adapters/index.ts +9 -0
- package/src/server/adapters/linear/adapter.ts +1136 -0
- package/src/server/adapters/linear/client.ts +169 -0
- package/src/server/adapters/linear/config.ts +152 -0
- package/src/server/adapters/linear/helpers/criteria-parser.ts +197 -0
- package/src/server/adapters/linear/helpers/index.ts +7 -0
- package/src/server/adapters/linear/index.ts +16 -0
- package/src/server/adapters/linear/mappers/description.ts +136 -0
- package/src/server/adapters/linear/mappers/epic.ts +81 -0
- package/src/server/adapters/linear/mappers/index.ts +27 -0
- package/src/server/adapters/linear/mappers/prd.ts +178 -0
- package/src/server/adapters/linear/mappers/task.ts +82 -0
- package/src/server/adapters/linear/types.ts +264 -0
- package/src/server/adapters/local-adapter.ts +968 -0
- package/src/server/adapters/types.ts +293 -0
- package/src/server/config.ts +73 -0
- package/src/server/db/__tests__/queries.test.ts +472 -0
- package/src/server/db/ids.ts +17 -0
- package/src/server/db/index.ts +69 -0
- package/src/server/db/queries.ts +142 -0
- package/src/server/db/refs.ts +60 -0
- package/src/server/db/schema.ts +88 -0
- package/src/server/db/sqlite.ts +10 -0
- package/src/server/index.ts +83 -0
- package/src/server/tools/__tests__/crud.test.ts +301 -0
- package/src/server/tools/__tests__/get-version.test.ts +27 -0
- package/src/server/tools/__tests__/mcp-interface.test.ts +388 -0
- package/src/server/tools/__tests__/query.test.ts +353 -0
- package/src/server/tools/__tests__/z-configure-linear.test.ts +511 -0
- package/src/server/tools/__tests__/z-get-linear-url.test.ts +108 -0
- package/src/server/tools/configure-linear.ts +373 -0
- package/src/server/tools/create-epic.ts +35 -0
- package/src/server/tools/create-prd.ts +31 -0
- package/src/server/tools/create-task.ts +38 -0
- package/src/server/tools/criteria.ts +50 -0
- package/src/server/tools/delete-entity.ts +76 -0
- package/src/server/tools/dependencies.ts +55 -0
- package/src/server/tools/get-entity.ts +238 -0
- package/src/server/tools/get-linear-url.ts +28 -0
- package/src/server/tools/get-project-context.ts +33 -0
- package/src/server/tools/get-stats.ts +52 -0
- package/src/server/tools/get-version.ts +20 -0
- package/src/server/tools/index.ts +114 -0
- package/src/server/tools/init-project.ts +108 -0
- package/src/server/tools/query-entities.ts +167 -0
- package/src/server/tools/render-status.ts +201 -0
- package/src/server/tools/update-entity.ts +140 -0
- package/src/server/tools/update-status.ts +166 -0
- package/src/server/utils/__tests__/mcp-response.test.ts +331 -0
- package/src/server/utils/logger.ts +9 -0
- package/src/server/utils/mcp-response.ts +254 -0
- package/src/server/utils/status-transitions.ts +160 -0
- package/src/status-line/__tests__/status-line.test.ts +215 -0
- package/src/status-line/index.ts +147 -0
- package/src/utils/__tests__/chalk-import.test.ts +32 -0
- package/src/utils/__tests__/display.test.ts +97 -0
- package/src/utils/__tests__/status-renderer.test.ts +310 -0
- package/src/utils/display.ts +62 -0
- package/src/utils/status-renderer.ts +188 -0
- package/src/version.ts +5 -0
- package/dist/server/index.js +0 -86958
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flux:linear
|
|
3
|
+
description: Connect Flux project to Linear for issue tracking
|
|
4
|
+
allowed-tools: mcp__plugin_flux_flux__*, AskUserQuestion
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Linear Integration Setup
|
|
8
|
+
|
|
9
|
+
Connect a Flux project to Linear using the interactive configuration flow.
|
|
10
|
+
|
|
11
|
+
## How Interactive Mode Works
|
|
12
|
+
|
|
13
|
+
The `configure_linear` tool supports progressive discovery:
|
|
14
|
+
|
|
15
|
+
| Call | Response |
|
|
16
|
+
|------|----------|
|
|
17
|
+
| `{interactive: true}` | Returns `{step: "select_team", teams: [...], user: {...}}` |
|
|
18
|
+
| `{interactive: true, teamId: "xxx"}` | Returns `{step: "select_project", projects: [...], team: {...}}` |
|
|
19
|
+
| `{teamId: "xxx", projectName: "..."}` | Creates new project and configures |
|
|
20
|
+
| `{teamId: "xxx", existingProjectId: "..."}` | Uses existing project and configures |
|
|
21
|
+
|
|
22
|
+
## Flow
|
|
23
|
+
|
|
24
|
+
### Step 1: Verify Project
|
|
25
|
+
|
|
26
|
+
Call `get_project_context`.
|
|
27
|
+
|
|
28
|
+
- If `initialized: false` → Tell user to run `/flux` first, then exit.
|
|
29
|
+
- If `adapter.type === "linear"` and config exists → Already configured, show info and exit.
|
|
30
|
+
- Otherwise → Continue to Step 2.
|
|
31
|
+
|
|
32
|
+
### Step 2: Fetch Teams
|
|
33
|
+
|
|
34
|
+
**IMPORTANT**: Call `configure_linear` with ONLY `interactive: true`. Do NOT pass teamId, projectName, or any other params.
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{"interactive": true}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This is the ONLY parameter needed for the first call. The tool will return available teams.
|
|
41
|
+
|
|
42
|
+
**If error** (e.g., "Linear API key not found"):
|
|
43
|
+
```
|
|
44
|
+
Linear API key required.
|
|
45
|
+
|
|
46
|
+
1. Get your key: Linear → Settings → API → Personal API keys
|
|
47
|
+
2. Set it: export LINEAR_API_KEY=lin_api_xxx
|
|
48
|
+
3. Run /flux:linear again
|
|
49
|
+
```
|
|
50
|
+
Then exit.
|
|
51
|
+
|
|
52
|
+
**If success**, response will be:
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"step": "select_team",
|
|
56
|
+
"user": {"name": "...", "email": "..."},
|
|
57
|
+
"teams": [
|
|
58
|
+
{"id": "team-abc", "name": "Engineering", "key": "ENG"},
|
|
59
|
+
{"id": "team-def", "name": "Product", "key": "PROD"}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Display: "Connected as {user.name} ({user.email})"
|
|
65
|
+
|
|
66
|
+
Use AskUserQuestion to let user select a team:
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"questions": [{
|
|
70
|
+
"question": "Which Linear team should Flux use?",
|
|
71
|
+
"header": "Team",
|
|
72
|
+
"options": [
|
|
73
|
+
{"label": "Engineering (ENG)", "description": "team-abc"},
|
|
74
|
+
{"label": "Product (PROD)", "description": "team-def"}
|
|
75
|
+
],
|
|
76
|
+
"multiSelect": false
|
|
77
|
+
}]
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Note: Put the team ID in the description field so you can retrieve it from the response.
|
|
82
|
+
|
|
83
|
+
### Step 3: Fetch Projects
|
|
84
|
+
|
|
85
|
+
Call `configure_linear` with:
|
|
86
|
+
```json
|
|
87
|
+
{"interactive": true, "teamId": "<selected_team_id>"}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Response will be:
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"step": "select_project",
|
|
94
|
+
"team": {"id": "...", "name": "...", "key": "..."},
|
|
95
|
+
"projects": [
|
|
96
|
+
{"id": "proj-123", "name": "Q1 Sprint", "state": "started"},
|
|
97
|
+
{"id": "proj-456", "name": "Backlog", "state": "planned"}
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Use AskUserQuestion:
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"questions": [{
|
|
106
|
+
"question": "Which project should Flux sync to?",
|
|
107
|
+
"header": "Project",
|
|
108
|
+
"options": [
|
|
109
|
+
{"label": "Create New Project", "description": "new"},
|
|
110
|
+
{"label": "Q1 Sprint", "description": "proj-123"},
|
|
111
|
+
{"label": "Backlog", "description": "proj-456"}
|
|
112
|
+
],
|
|
113
|
+
"multiSelect": false
|
|
114
|
+
}]
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Step 4: Configure
|
|
119
|
+
|
|
120
|
+
**If user selected "Create New Project":**
|
|
121
|
+
|
|
122
|
+
Ask for project name, then call:
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"teamId": "<team_id>",
|
|
126
|
+
"projectName": "<user_provided_name>"
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**If user selected existing project:**
|
|
131
|
+
|
|
132
|
+
Call:
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"teamId": "<team_id>",
|
|
136
|
+
"existingProjectId": "<project_id>"
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Step 5: Success
|
|
141
|
+
|
|
142
|
+
Response will include:
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"success": true,
|
|
146
|
+
"team": "Engineering",
|
|
147
|
+
"project": {"id": "...", "name": "..."},
|
|
148
|
+
"labels": {...},
|
|
149
|
+
"view": {"created": "...", "setup_hint": "..."}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Display:
|
|
154
|
+
```
|
|
155
|
+
Linear connected!
|
|
156
|
+
|
|
157
|
+
Team: {team}
|
|
158
|
+
Project: {project.name}
|
|
159
|
+
|
|
160
|
+
All PRDs, epics, and tasks will sync to Linear.
|
|
161
|
+
Run /flux:prd to create your first PRD.
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
If `view.setup_hint` exists, show it as a tip.
|
|
165
|
+
|
|
166
|
+
## Key Points
|
|
167
|
+
|
|
168
|
+
- Always use `{"interactive": true}` (boolean) not a string
|
|
169
|
+
- The response `step` field tells you what stage you're at
|
|
170
|
+
- Use AskUserQuestion for team/project selection
|
|
171
|
+
- Store the selected IDs from previous responses to use in next calls
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cliangdev/flux-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.0-dev.588ae42",
|
|
4
4
|
"description": "Claude Code plugin for AI-first workflow orchestration with MCP server",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/server/index.js",
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"flux-plugin": "./bin/install.cjs"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
-
"bin/",
|
|
12
|
-
"
|
|
11
|
+
"bin/install.cjs",
|
|
12
|
+
"src/",
|
|
13
13
|
"skills/",
|
|
14
14
|
"commands/",
|
|
15
15
|
"agents/",
|
|
@@ -17,20 +17,20 @@
|
|
|
17
17
|
],
|
|
18
18
|
"scripts": {
|
|
19
19
|
"dev": "bun run src/server/index.ts",
|
|
20
|
-
"build": "bun build src/server/index.ts
|
|
21
|
-
"
|
|
22
|
-
"build:
|
|
23
|
-
"build:compile:server": "bun build --compile --outfile bin/flux-server src/server/index.ts",
|
|
24
|
-
"build:compile:status": "bun build --compile --outfile bin/flux-status src/status-line/index.ts",
|
|
20
|
+
"build": "bun build --compile --outfile bin/flux-server src/server/index.ts && bun build --compile --outfile bin/flux-status src/status-line/index.ts",
|
|
21
|
+
"build:server": "bun build --compile --outfile bin/flux-server src/server/index.ts",
|
|
22
|
+
"build:status": "bun build --compile --outfile bin/flux-status src/status-line/index.ts",
|
|
25
23
|
"validate": "node scripts/validate-structure.cjs",
|
|
26
|
-
"
|
|
24
|
+
"test:integration": "bun test scripts/__tests__/integration.test.ts --timeout 120000",
|
|
25
|
+
"prepublishOnly": "bun run validate && bun run test:integration",
|
|
27
26
|
"test": "bun test",
|
|
28
27
|
"test:linear-description": "bun run src/server/adapters/__tests__/linear-description-test.ts",
|
|
29
28
|
"typecheck": "tsc --noEmit",
|
|
30
29
|
"lint": "biome check .",
|
|
31
30
|
"lint:fix": "biome check --write .",
|
|
32
31
|
"format": "biome format --write .",
|
|
33
|
-
"verify-release": "bun run scripts/verify-release.ts"
|
|
32
|
+
"verify-release": "bun run scripts/verify-release.ts",
|
|
33
|
+
"release": "./scripts/release.sh"
|
|
34
34
|
},
|
|
35
35
|
"repository": {
|
|
36
36
|
"type": "git",
|
|
@@ -51,14 +51,12 @@
|
|
|
51
51
|
"license": "MIT",
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"@biomejs/biome": "^2.3.11",
|
|
54
|
-
"@types/better-sqlite3": "^7.6.13",
|
|
55
54
|
"@types/bun": "^1.3.6",
|
|
56
55
|
"typescript": "^5.0.0"
|
|
57
56
|
},
|
|
58
57
|
"dependencies": {
|
|
59
58
|
"@linear/sdk": "^70.0.0",
|
|
60
59
|
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
61
|
-
"better-sqlite3": "^12.6.2",
|
|
62
60
|
"chalk": "^5.4.1",
|
|
63
61
|
"zod": "^4.3.5"
|
|
64
62
|
},
|
|
@@ -6,18 +6,20 @@ user-invocable: false
|
|
|
6
6
|
|
|
7
7
|
# Flux Orchestrator Skill
|
|
8
8
|
|
|
9
|
-
This skill is automatically active when working in a Flux project. It provides context about available tools and
|
|
9
|
+
This skill is automatically active when working in a Flux project. It provides context about available tools, workflow patterns, and integration options.
|
|
10
10
|
|
|
11
11
|
## Available MCP Tools
|
|
12
12
|
|
|
13
13
|
### Query Tools
|
|
14
|
-
- `get_project_context` - Check if project initialized, get name/vision/
|
|
14
|
+
- `get_project_context` - Check if project initialized, get name/vision/adapter type
|
|
15
15
|
- `get_stats` - Get PRD/epic/task counts by status
|
|
16
16
|
- `get_entity` - Fetch entity by ref with optional includes (criteria, tasks, dependencies)
|
|
17
17
|
- `query_entities` - Search entities by type, status, parent ref
|
|
18
|
+
- `render_status` - Get formatted project status with progress bars
|
|
18
19
|
|
|
19
20
|
### Mutation Tools
|
|
20
21
|
- `init_project` - Initialize new .flux/ directory with project.json and database
|
|
22
|
+
- `configure_linear` - Configure Linear integration (supports interactive mode)
|
|
21
23
|
- `create_prd` - Create a new PRD
|
|
22
24
|
- `create_epic` - Create an epic linked to a PRD
|
|
23
25
|
- `create_task` - Create a task linked to an epic
|
|
@@ -31,102 +33,82 @@ This skill is automatically active when working in a Flux project. It provides c
|
|
|
31
33
|
- `add_criteria` - Add acceptance criterion to epic or task
|
|
32
34
|
- `mark_criteria_met` - Mark criterion as satisfied
|
|
33
35
|
|
|
34
|
-
##
|
|
36
|
+
## Available Commands
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
| Command | Purpose |
|
|
39
|
+
|---------|---------|
|
|
40
|
+
| `/flux` | Project init, status, and workflow routing |
|
|
41
|
+
| `/flux:linear` | Connect project to Linear (interactive setup) |
|
|
42
|
+
| `/flux:prd` | Create or refine PRDs |
|
|
43
|
+
| `/flux:breakdown` | Break approved PRD into epics and tasks |
|
|
44
|
+
| `/flux:implement` | Implement tasks with TDD workflow |
|
|
37
45
|
|
|
38
|
-
|
|
39
|
-
- Action: Run `/flux` to initialize
|
|
46
|
+
## Workflow States
|
|
40
47
|
|
|
41
|
-
|
|
42
|
-
|
|
48
|
+
```
|
|
49
|
+
Uninitialized → Initialized → PRD Draft → Pending Review → Reviewed → Approved → Breakdown Ready → In Progress → Complete
|
|
50
|
+
↓
|
|
51
|
+
(optional) Linear Connected
|
|
52
|
+
```
|
|
43
53
|
|
|
44
|
-
|
|
45
|
-
|
|
54
|
+
### PRD Status Transitions
|
|
55
|
+
```
|
|
56
|
+
DRAFT → PENDING_REVIEW → REVIEWED → APPROVED → BREAKDOWN_READY → COMPLETED
|
|
57
|
+
↓ ↓
|
|
58
|
+
DRAFT (revise) DRAFT (revise)
|
|
59
|
+
```
|
|
46
60
|
|
|
47
|
-
|
|
48
|
-
|
|
61
|
+
### Epic/Task Statuses
|
|
62
|
+
- `PENDING` → `IN_PROGRESS` → `COMPLETED`
|
|
63
|
+
|
|
64
|
+
## Backend Adapters
|
|
49
65
|
|
|
50
|
-
|
|
51
|
-
- Action: Address feedback, then approve or revise to DRAFT
|
|
66
|
+
Flux supports multiple backends via the adapter pattern:
|
|
52
67
|
|
|
53
|
-
|
|
54
|
-
|
|
68
|
+
| Adapter | Storage | Use Case |
|
|
69
|
+
|---------|---------|----------|
|
|
70
|
+
| `local` | SQLite (.flux/flux.db) | Default, offline-first |
|
|
71
|
+
| `linear` | Linear API | Team collaboration, issue tracking |
|
|
55
72
|
|
|
56
|
-
|
|
57
|
-
- Action: Run `/flux:implement` to start coding
|
|
73
|
+
### Switching to Linear
|
|
58
74
|
|
|
59
|
-
|
|
60
|
-
|
|
75
|
+
Use `configure_linear` with interactive mode:
|
|
76
|
+
1. `{interactive: true}` → returns teams list
|
|
77
|
+
2. `{interactive: true, teamId: "..."}` → returns projects list
|
|
78
|
+
3. `{teamId: "...", projectName/existingProjectId: "..."}` → configures
|
|
61
79
|
|
|
62
|
-
|
|
63
|
-
- Action: Review and create PR
|
|
80
|
+
Or run `/flux:linear` for guided setup.
|
|
64
81
|
|
|
65
82
|
## Entity References
|
|
66
83
|
|
|
67
|
-
|
|
84
|
+
Format: `{PREFIX}-{TYPE}{NUMBER}`
|
|
68
85
|
|
|
69
86
|
- PRD: `MSA-P1`, `MSA-P2`
|
|
70
87
|
- Epic: `MSA-E1`, `MSA-E2`
|
|
71
88
|
- Task: `MSA-T1`, `MSA-T2`
|
|
72
89
|
|
|
73
|
-
The prefix is generated from the project name during initialization.
|
|
74
|
-
|
|
75
|
-
## Status Values
|
|
76
|
-
|
|
77
|
-
### PRD Statuses (6-stage workflow)
|
|
78
|
-
- `DRAFT` - Initial state, being created/refined
|
|
79
|
-
- `PENDING_REVIEW` - Submitted for critique
|
|
80
|
-
- `REVIEWED` - Critique complete, awaiting approval
|
|
81
|
-
- `APPROVED` - Ready for epic breakdown
|
|
82
|
-
- `BREAKDOWN_READY` - Epics and tasks created
|
|
83
|
-
- `COMPLETED` - All epics done
|
|
84
|
-
|
|
85
|
-
### Valid PRD Transitions
|
|
86
|
-
```
|
|
87
|
-
DRAFT → PENDING_REVIEW
|
|
88
|
-
PENDING_REVIEW → REVIEWED | DRAFT (revise)
|
|
89
|
-
REVIEWED → APPROVED | DRAFT (revise)
|
|
90
|
-
APPROVED → BREAKDOWN_READY
|
|
91
|
-
BREAKDOWN_READY → COMPLETED
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### Epic/Task Statuses
|
|
95
|
-
- `PENDING` - Not started
|
|
96
|
-
- `IN_PROGRESS` - Currently being worked on
|
|
97
|
-
- `COMPLETED` - Done
|
|
98
|
-
|
|
99
|
-
## Confidence-Based Autonomy
|
|
100
|
-
|
|
101
|
-
The orchestrator uses confidence levels to determine autonomy:
|
|
102
|
-
|
|
103
|
-
| Confidence | Behavior | Example |
|
|
104
|
-
|------------|----------|---------|
|
|
105
|
-
| > 80% | Auto-execute, inform user | "I'm creating the epic structure..." |
|
|
106
|
-
| 50-80% | Suggest action, wait for confirmation | "Ready to break down into tasks. Proceed?" |
|
|
107
|
-
| < 50% | Ask clarifying question | "Should we research this technology first?" |
|
|
108
|
-
|
|
109
|
-
### Confidence Indicators
|
|
110
|
-
- **High confidence (>80%)**: Clear next step, no ambiguity, user has been responsive
|
|
111
|
-
- **Medium confidence (50-80%)**: Reasonable next step, some uncertainty
|
|
112
|
-
- **Low confidence (<50%)**: Multiple valid paths, unclear requirements, unfamiliar tech
|
|
113
|
-
|
|
114
90
|
## Available Subagents
|
|
115
91
|
|
|
116
|
-
### Research Agent
|
|
117
|
-
- **Trigger**: Unfamiliar technology
|
|
118
|
-
- **Purpose**: Gather information about libraries, frameworks, APIs
|
|
92
|
+
### Research Agent (`flux:flux-researcher`)
|
|
93
|
+
- **Trigger**: Unfamiliar technology, confidence < 70%
|
|
119
94
|
- **Tools**: Context7, WebSearch, WebFetch
|
|
120
95
|
|
|
121
|
-
### Critique Agent
|
|
122
|
-
- **Trigger**: PRD
|
|
123
|
-
- **
|
|
124
|
-
|
|
96
|
+
### Critique Agent (`flux:flux-critic`)
|
|
97
|
+
- **Trigger**: PRD submitted for review
|
|
98
|
+
- **Output**: Feasibility analysis, risks, recommendations
|
|
99
|
+
|
|
100
|
+
### Coder Agent (`flux:flux-coder`)
|
|
101
|
+
- **Trigger**: Task implementation
|
|
102
|
+
- **Workflow**: TDD - write tests, implement, verify
|
|
103
|
+
|
|
104
|
+
### Verifier Agent (`flux:flux-verifier`)
|
|
105
|
+
- **Trigger**: After implementation
|
|
106
|
+
- **Purpose**: Verify acceptance criteria coverage
|
|
125
107
|
|
|
126
108
|
## Best Practices
|
|
127
109
|
|
|
128
|
-
1. **Check context first** -
|
|
129
|
-
2. **Use refs, not IDs** - Tools accept
|
|
130
|
-
3. **
|
|
131
|
-
4. **
|
|
132
|
-
5. **
|
|
110
|
+
1. **Check context first** - Call `get_project_context` before actions
|
|
111
|
+
2. **Use refs, not IDs** - Tools accept `MSA-E1` format
|
|
112
|
+
3. **Use render_status** - For visual project overview
|
|
113
|
+
4. **Validate transitions** - `update_status` enforces valid transitions
|
|
114
|
+
5. **Include related data** - Use `include` param to fetch nested entities
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
describe("version module", () => {
|
|
6
|
+
test("VERSION is exported from src/version.ts", async () => {
|
|
7
|
+
// Dynamic import to get the VERSION constant
|
|
8
|
+
const versionModule = await import("../version.js");
|
|
9
|
+
|
|
10
|
+
expect(versionModule.VERSION).toBeDefined();
|
|
11
|
+
expect(typeof versionModule.VERSION).toBe("string");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("VERSION equals package.json version", async () => {
|
|
15
|
+
// Read package.json version
|
|
16
|
+
const packageJsonPath = join(process.cwd(), "package.json");
|
|
17
|
+
expect(existsSync(packageJsonPath)).toBe(true);
|
|
18
|
+
|
|
19
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
20
|
+
const packageVersion = packageJson.version;
|
|
21
|
+
|
|
22
|
+
expect(packageVersion).toBeDefined();
|
|
23
|
+
expect(typeof packageVersion).toBe("string");
|
|
24
|
+
|
|
25
|
+
// Import VERSION and compare
|
|
26
|
+
const versionModule = await import("../version.js");
|
|
27
|
+
expect(versionModule.VERSION).toBe(packageVersion);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("VERSION is a valid semver format", async () => {
|
|
31
|
+
const versionModule = await import("../version.js");
|
|
32
|
+
|
|
33
|
+
// Basic semver format check (e.g., "0.1.0", "1.2.3-beta.1")
|
|
34
|
+
const semverPattern = /^\d+\.\d+\.\d+(-[\w.]+)?$/;
|
|
35
|
+
expect(semverPattern.test(versionModule.VERSION)).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
File without changes
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
realpathSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
|
|
11
|
+
describe("config", () => {
|
|
12
|
+
const originalEnv = process.env.FLUX_PROJECT_ROOT;
|
|
13
|
+
const originalCwd = process.cwd();
|
|
14
|
+
// Use os.tmpdir() to get the real temp path (handles /tmp -> /private/tmp on macOS)
|
|
15
|
+
const TEST_DIR = `${realpathSync(tmpdir())}/flux-config-test-${Date.now()}`;
|
|
16
|
+
const NESTED_DIR = `${TEST_DIR}/subdir/nested`;
|
|
17
|
+
|
|
18
|
+
beforeEach(async () => {
|
|
19
|
+
// Clean up any previous test directory
|
|
20
|
+
if (existsSync(TEST_DIR)) {
|
|
21
|
+
rmSync(TEST_DIR, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Create test directory structure
|
|
25
|
+
mkdirSync(NESTED_DIR, { recursive: true });
|
|
26
|
+
|
|
27
|
+
// Clear the config cache before each test
|
|
28
|
+
const { config } = await import("../config.js");
|
|
29
|
+
config.clearCache();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(async () => {
|
|
33
|
+
// Restore original env
|
|
34
|
+
if (originalEnv !== undefined) {
|
|
35
|
+
process.env.FLUX_PROJECT_ROOT = originalEnv;
|
|
36
|
+
} else {
|
|
37
|
+
delete process.env.FLUX_PROJECT_ROOT;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Restore original cwd
|
|
41
|
+
process.chdir(originalCwd);
|
|
42
|
+
|
|
43
|
+
// Clear the config cache
|
|
44
|
+
const { config } = await import("../config.js");
|
|
45
|
+
config.clearCache();
|
|
46
|
+
|
|
47
|
+
// Clean up test directory
|
|
48
|
+
if (existsSync(TEST_DIR)) {
|
|
49
|
+
rmSync(TEST_DIR, { recursive: true });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("uses env var when properly set", async () => {
|
|
54
|
+
process.env.FLUX_PROJECT_ROOT = "/some/valid/path";
|
|
55
|
+
|
|
56
|
+
const { config } = await import("../config.js");
|
|
57
|
+
config.clearCache();
|
|
58
|
+
|
|
59
|
+
expect(config.projectRoot).toBe("/some/valid/path");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("ignores unresolved template variable and walks up directories", async () => {
|
|
63
|
+
// Create a .flux folder at TEST_DIR
|
|
64
|
+
mkdirSync(`${TEST_DIR}/.flux`, { recursive: true });
|
|
65
|
+
writeFileSync(
|
|
66
|
+
`${TEST_DIR}/.flux/project.json`,
|
|
67
|
+
JSON.stringify({ name: "test" }),
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Simulate Claude Code passing unresolved template variable
|
|
71
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: intentionally testing literal template string
|
|
72
|
+
process.env.FLUX_PROJECT_ROOT = "${CLAUDE_PROJECT_DIR}";
|
|
73
|
+
|
|
74
|
+
// Change to nested directory
|
|
75
|
+
process.chdir(NESTED_DIR);
|
|
76
|
+
|
|
77
|
+
const { config } = await import("../config.js");
|
|
78
|
+
config.clearCache();
|
|
79
|
+
|
|
80
|
+
// Should walk up and find TEST_DIR, not use the literal "${CLAUDE_PROJECT_DIR}"
|
|
81
|
+
expect(config.projectRoot).not.toContain("${");
|
|
82
|
+
expect(config.projectRoot).toBe(TEST_DIR);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("walks up directories to find .flux folder", async () => {
|
|
86
|
+
// Create a .flux folder at TEST_DIR (parent of NESTED_DIR)
|
|
87
|
+
mkdirSync(`${TEST_DIR}/.flux`, { recursive: true });
|
|
88
|
+
writeFileSync(
|
|
89
|
+
`${TEST_DIR}/.flux/project.json`,
|
|
90
|
+
JSON.stringify({ name: "test" }),
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
// No env var set
|
|
94
|
+
delete process.env.FLUX_PROJECT_ROOT;
|
|
95
|
+
|
|
96
|
+
// Change to nested directory
|
|
97
|
+
process.chdir(NESTED_DIR);
|
|
98
|
+
|
|
99
|
+
const { config } = await import("../config.js");
|
|
100
|
+
config.clearCache();
|
|
101
|
+
|
|
102
|
+
// Should walk up and find TEST_DIR
|
|
103
|
+
expect(config.projectRoot).toBe(TEST_DIR);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("falls back to cwd when no .flux folder found", async () => {
|
|
107
|
+
// No .flux folder anywhere in TEST_DIR hierarchy
|
|
108
|
+
// No env var set
|
|
109
|
+
delete process.env.FLUX_PROJECT_ROOT;
|
|
110
|
+
|
|
111
|
+
// Change to test directory (which has no .flux)
|
|
112
|
+
process.chdir(TEST_DIR);
|
|
113
|
+
|
|
114
|
+
const { config } = await import("../config.js");
|
|
115
|
+
config.clearCache();
|
|
116
|
+
|
|
117
|
+
// Should fall back to cwd
|
|
118
|
+
expect(config.projectRoot).toBe(TEST_DIR);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("projectExists returns true when project.json exists", async () => {
|
|
122
|
+
mkdirSync(`${TEST_DIR}/.flux`, { recursive: true });
|
|
123
|
+
writeFileSync(
|
|
124
|
+
`${TEST_DIR}/.flux/project.json`,
|
|
125
|
+
JSON.stringify({ name: "test" }),
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
process.env.FLUX_PROJECT_ROOT = TEST_DIR;
|
|
129
|
+
|
|
130
|
+
const { config } = await import("../config.js");
|
|
131
|
+
config.clearCache();
|
|
132
|
+
|
|
133
|
+
expect(config.projectExists).toBe(true);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("projectExists returns false when project.json does not exist", async () => {
|
|
137
|
+
// No .flux folder
|
|
138
|
+
process.env.FLUX_PROJECT_ROOT = TEST_DIR;
|
|
139
|
+
|
|
140
|
+
const { config } = await import("../config.js");
|
|
141
|
+
config.clearCache();
|
|
142
|
+
|
|
143
|
+
expect(config.projectExists).toBe(false);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("caches project root for performance", async () => {
|
|
147
|
+
process.env.FLUX_PROJECT_ROOT = "/first/path";
|
|
148
|
+
|
|
149
|
+
const { config } = await import("../config.js");
|
|
150
|
+
config.clearCache();
|
|
151
|
+
|
|
152
|
+
// First call caches the value
|
|
153
|
+
expect(config.projectRoot).toBe("/first/path");
|
|
154
|
+
|
|
155
|
+
// Changing env var should not affect cached value
|
|
156
|
+
process.env.FLUX_PROJECT_ROOT = "/second/path";
|
|
157
|
+
expect(config.projectRoot).toBe("/first/path");
|
|
158
|
+
|
|
159
|
+
// After clearing cache, should use new env var
|
|
160
|
+
config.clearCache();
|
|
161
|
+
expect(config.projectRoot).toBe("/second/path");
|
|
162
|
+
});
|
|
163
|
+
});
|