@cliangdev/flux-plugin 0.2.0 → 0.3.0
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 +11 -7
- package/agents/coder.md +150 -25
- package/bin/install.cjs +171 -16
- package/commands/breakdown.md +47 -10
- package/commands/dashboard.md +29 -0
- package/commands/flux.md +92 -12
- package/commands/implement.md +166 -17
- package/commands/linear.md +6 -5
- package/commands/prd.md +996 -82
- package/manifest.json +2 -1
- package/package.json +9 -11
- package/skills/flux-orchestrator/SKILL.md +11 -3
- package/skills/prd-writer/SKILL.md +761 -0
- package/skills/ux-ui-design/SKILL.md +346 -0
- package/skills/ux-ui-design/references/design-tokens.md +359 -0
- package/src/__tests__/version.test.ts +37 -0
- package/src/adapters/local/.gitkeep +0 -0
- package/src/dashboard/__tests__/api.test.ts +211 -0
- package/src/dashboard/browser.ts +35 -0
- package/src/dashboard/public/app.js +869 -0
- package/src/dashboard/public/index.html +90 -0
- package/src/dashboard/public/styles.css +807 -0
- package/src/dashboard/public/vendor/highlight.css +10 -0
- package/src/dashboard/public/vendor/highlight.min.js +8422 -0
- package/src/dashboard/public/vendor/marked.min.js +2210 -0
- package/src/dashboard/server.ts +296 -0
- package/src/dashboard/watchers.ts +83 -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 +429 -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 +1141 -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 +1009 -0
- package/src/server/adapters/types.ts +293 -0
- package/src/server/config.ts +73 -0
- package/src/server/db/__tests__/queries.test.ts +473 -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 +97 -0
- package/src/server/db/sqlite.ts +10 -0
- package/src/server/index.ts +81 -0
- package/src/server/tools/__tests__/crud.test.ts +411 -0
- package/src/server/tools/__tests__/get-version.test.ts +27 -0
- package/src/server/tools/__tests__/mcp-interface.test.ts +479 -0
- package/src/server/tools/__tests__/query.test.ts +405 -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 +44 -0
- package/src/server/tools/create-prd.ts +40 -0
- package/src/server/tools/create-task.ts +47 -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 +240 -0
- package/src/server/tools/get-linear-url.ts +28 -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 +158 -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 +219 -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 +214 -0
- package/src/version.ts +5 -0
- package/dist/server/index.js +0 -87063
- package/skills/prd-template/SKILL.md +0 -242
package/README.md
CHANGED
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Agent-orchestrated, spec-driven workflow for Claude Code.
|
|
4
4
|
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
This plugin requires [Bun](https://bun.sh). If you don't have Bun installed, the installer will offer to install it for you.
|
|
8
|
+
|
|
5
9
|
## Installation
|
|
6
10
|
|
|
7
11
|
```bash
|
|
8
|
-
|
|
12
|
+
bunx @cliangdev/flux-plugin
|
|
9
13
|
```
|
|
10
14
|
|
|
11
15
|
This installs:
|
|
@@ -16,8 +20,8 @@ This installs:
|
|
|
16
20
|
### Options
|
|
17
21
|
|
|
18
22
|
```bash
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
bunx @cliangdev/flux-plugin --global # Install to ~/.claude (all projects)
|
|
24
|
+
bunx @cliangdev/flux-plugin --local # Install to ./.claude (current project)
|
|
21
25
|
```
|
|
22
26
|
|
|
23
27
|
### What Gets Configured
|
|
@@ -32,7 +36,7 @@ The installer automatically:
|
|
|
32
36
|
| Command | Purpose |
|
|
33
37
|
|---------|---------|
|
|
34
38
|
| `/flux` | Smart entry point - shows status and guides you to the next step |
|
|
35
|
-
| `/flux:prd` | Create
|
|
39
|
+
| `/flux:prd` | Create PRDs through discovery, research, and guided writing |
|
|
36
40
|
| `/flux:breakdown` | Break PRDs into epics and tasks with acceptance criteria |
|
|
37
41
|
| `/flux:implement` | Implement tasks with TDD workflow |
|
|
38
42
|
|
|
@@ -121,7 +125,7 @@ your-project/
|
|
|
121
125
|
To update to the latest version, simply re-run the installer:
|
|
122
126
|
|
|
123
127
|
```bash
|
|
124
|
-
|
|
128
|
+
bunx @cliangdev/flux-plugin@latest --global
|
|
125
129
|
```
|
|
126
130
|
|
|
127
131
|
This will:
|
|
@@ -139,7 +143,7 @@ Check your current version:
|
|
|
139
143
|
|
|
140
144
|
```bash
|
|
141
145
|
rm -rf ~/.claude/commands/flux.md ~/.claude/commands/flux
|
|
142
|
-
rm -rf ~/.claude/skills/agent-creator ~/.claude/skills/epic-template ~/.claude/skills/flux-orchestrator ~/.claude/skills/prd-
|
|
146
|
+
rm -rf ~/.claude/skills/agent-creator ~/.claude/skills/epic-template ~/.claude/skills/flux-orchestrator ~/.claude/skills/prd-writer
|
|
143
147
|
rm -f ~/.claude/flux-version
|
|
144
148
|
# Edit ~/.claude.json and remove the "flux" entry from "mcpServers"
|
|
145
149
|
```
|
|
@@ -148,7 +152,7 @@ rm -f ~/.claude/flux-version
|
|
|
148
152
|
|
|
149
153
|
```bash
|
|
150
154
|
rm -rf .claude/commands/flux.md .claude/commands/flux
|
|
151
|
-
rm -rf .claude/skills/agent-creator .claude/skills/epic-template .claude/skills/flux-orchestrator .claude/skills/prd-
|
|
155
|
+
rm -rf .claude/skills/agent-creator .claude/skills/epic-template .claude/skills/flux-orchestrator .claude/skills/prd-writer
|
|
152
156
|
rm -f .claude/flux-version
|
|
153
157
|
# Edit .claude.json and remove the "flux" entry from "mcpServers"
|
|
154
158
|
```
|
package/agents/coder.md
CHANGED
|
@@ -20,30 +20,41 @@ You receive only:
|
|
|
20
20
|
|
|
21
21
|
This keeps your context clean for high-quality code output.
|
|
22
22
|
|
|
23
|
-
## Step 1:
|
|
23
|
+
## Step 1: Apply Project Skill
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
The orchestrator should provide a **Project Skill** section in your prompt with patterns to follow. If provided, strictly follow those patterns for:
|
|
26
|
+
- Code structure and organization
|
|
27
|
+
- Error handling conventions
|
|
28
|
+
- Testing patterns
|
|
29
|
+
- Naming conventions
|
|
30
|
+
|
|
31
|
+
### Fallback: Discover Skill Yourself
|
|
32
|
+
|
|
33
|
+
If no skill was provided in your prompt, discover and load it:
|
|
26
34
|
|
|
27
35
|
```bash
|
|
28
|
-
#
|
|
36
|
+
# 1. Detect project type
|
|
29
37
|
ls -la | head -20
|
|
38
|
+
|
|
39
|
+
# 2. Check for matching skill in .claude/skills/
|
|
40
|
+
ls .claude/skills/ 2>/dev/null
|
|
30
41
|
```
|
|
31
42
|
|
|
32
|
-
| File Found | Project Type | Skill
|
|
33
|
-
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
37
|
-
| `package.json` +
|
|
38
|
-
| `
|
|
39
|
-
| `
|
|
40
|
-
| `requirements.txt` / `pyproject.toml` | Python | Python idioms |
|
|
43
|
+
| File Found | Project Type | Skill Search Pattern |
|
|
44
|
+
|------------|--------------|---------------------|
|
|
45
|
+
| `go.mod` | Go | `golang*`, `go-*` |
|
|
46
|
+
| `tsconfig.json` | TypeScript | `typescript*`, `ts-*` |
|
|
47
|
+
| `pom.xml` / `build.gradle` | Java/Spring | `java*`, `spring*` |
|
|
48
|
+
| `package.json` + react | React | `react*`, `ui-*` |
|
|
49
|
+
| `Cargo.toml` | Rust | `rust*` |
|
|
50
|
+
| `requirements.txt` | Python | `python*`, `py-*` |
|
|
41
51
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
-
|
|
45
|
-
|
|
46
|
-
|
|
52
|
+
```bash
|
|
53
|
+
# 3. Read matching skill
|
|
54
|
+
cat .claude/skills/{matched-skill}/SKILL.md
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Apply the loaded skill patterns** - if no matching skill exists, use general best practices for that language.
|
|
47
58
|
|
|
48
59
|
## Step 2: Understand the Task
|
|
49
60
|
|
|
@@ -61,10 +72,11 @@ Acceptance Criteria:
|
|
|
61
72
|
|
|
62
73
|
## Step 3: Write Tests First (TDD)
|
|
63
74
|
|
|
75
|
+
### 3.1 Tests for Acceptance Criteria
|
|
76
|
+
|
|
64
77
|
For each `[auto]` criterion, write a failing test:
|
|
65
78
|
|
|
66
79
|
```typescript
|
|
67
|
-
// Example for TypeScript
|
|
68
80
|
describe('Login API', () => {
|
|
69
81
|
it('returns 401 for invalid credentials', async () => {
|
|
70
82
|
const response = await api.post('/login', {
|
|
@@ -82,6 +94,34 @@ describe('Login API', () => {
|
|
|
82
94
|
});
|
|
83
95
|
```
|
|
84
96
|
|
|
97
|
+
### 3.2 Tests for Critical Components
|
|
98
|
+
|
|
99
|
+
Beyond acceptance criteria, also test:
|
|
100
|
+
|
|
101
|
+
- **Core business logic**: Functions that make important decisions
|
|
102
|
+
- **Data transformations**: Parsing, validation, mapping functions
|
|
103
|
+
- **Error paths**: Edge cases and failure scenarios
|
|
104
|
+
- **Integration points**: API handlers, database operations
|
|
105
|
+
- **Utilities used in multiple places**: Shared helpers and utils
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
describe('SessionManager', () => {
|
|
109
|
+
it('expires sessions after TTL', async () => {
|
|
110
|
+
const session = await sessionManager.create(userId);
|
|
111
|
+
await advanceTime(SESSION_TTL + 1000);
|
|
112
|
+
expect(await sessionManager.isValid(session.id)).toBe(false);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('handles concurrent session creation', async () => {
|
|
116
|
+
const results = await Promise.all([
|
|
117
|
+
sessionManager.create(userId),
|
|
118
|
+
sessionManager.create(userId),
|
|
119
|
+
]);
|
|
120
|
+
expect(results[0].id).not.toBe(results[1].id);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
85
125
|
Run tests to confirm they fail:
|
|
86
126
|
```bash
|
|
87
127
|
bun test login.test.ts
|
|
@@ -103,6 +143,81 @@ Write minimal code to make tests pass:
|
|
|
103
143
|
bun test
|
|
104
144
|
```
|
|
105
145
|
|
|
146
|
+
## Code Quality Standards
|
|
147
|
+
|
|
148
|
+
### Write Clean, Modular, Testable Code
|
|
149
|
+
|
|
150
|
+
**Modularity**
|
|
151
|
+
- Small, focused functions that do one thing well
|
|
152
|
+
- Clear separation of concerns (business logic vs I/O vs presentation)
|
|
153
|
+
- Dependencies injected, not hardcoded - makes testing easy
|
|
154
|
+
- Extract reusable logic into well-named helper functions
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// ✅ Good: Modular, testable
|
|
158
|
+
function validateCredentials(email: string, password: string): ValidationResult {
|
|
159
|
+
if (!isValidEmail(email)) return { valid: false, error: 'Invalid email' };
|
|
160
|
+
if (password.length < 8) return { valid: false, error: 'Password too short' };
|
|
161
|
+
return { valid: true };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function login(credentials: Credentials, userRepo: UserRepository) {
|
|
165
|
+
const validation = validateCredentials(credentials.email, credentials.password);
|
|
166
|
+
if (!validation.valid) throw new ValidationError(validation.error);
|
|
167
|
+
return userRepo.findByEmail(credentials.email);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ❌ Bad: Monolithic, hard to test
|
|
171
|
+
async function login(email: string, password: string) {
|
|
172
|
+
// validation mixed with business logic mixed with database calls
|
|
173
|
+
const db = getDatabase();
|
|
174
|
+
if (!email.includes('@')) throw new Error('bad email');
|
|
175
|
+
const user = await db.query('SELECT * FROM users WHERE email = ?', [email]);
|
|
176
|
+
// ... more mixed concerns
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Testability**
|
|
181
|
+
- Pure functions where possible (same input → same output)
|
|
182
|
+
- Side effects isolated and explicit
|
|
183
|
+
- Avoid global state
|
|
184
|
+
- Use interfaces/types for dependencies
|
|
185
|
+
|
|
186
|
+
### No Unnecessary Comments
|
|
187
|
+
|
|
188
|
+
Let code be self-documenting. Comments should explain **why**, not **what**.
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// ❌ Bad: Comments that restate the code
|
|
192
|
+
// Check if user is admin
|
|
193
|
+
if (user.role === 'admin') {
|
|
194
|
+
// Grant admin access
|
|
195
|
+
grantAdminAccess(user);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// ✅ Good: Code explains itself
|
|
199
|
+
if (user.role === 'admin') {
|
|
200
|
+
grantAdminAccess(user);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ✅ Good: Comment explains non-obvious "why"
|
|
204
|
+
// Rate limit bypass for internal services to prevent cascade failures
|
|
205
|
+
if (request.source === 'internal') {
|
|
206
|
+
skipRateLimit = true;
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**When to comment:**
|
|
211
|
+
- Non-obvious business rules or edge cases
|
|
212
|
+
- Workarounds for external bugs/limitations
|
|
213
|
+
- Performance optimizations that sacrifice readability
|
|
214
|
+
- Public API documentation (JSDoc for library interfaces)
|
|
215
|
+
|
|
216
|
+
**Never comment:**
|
|
217
|
+
- What the code does (let naming convey this)
|
|
218
|
+
- Obvious operations
|
|
219
|
+
- Commented-out code (delete it, git has history)
|
|
220
|
+
|
|
106
221
|
## Step 5: Handle Manual Criteria
|
|
107
222
|
|
|
108
223
|
For `[manual]` criteria, add a comment or doc noting verification steps:
|
|
@@ -174,13 +289,23 @@ Needs:
|
|
|
174
289
|
|
|
175
290
|
## Boundaries
|
|
176
291
|
|
|
177
|
-
|
|
178
|
-
-
|
|
179
|
-
-
|
|
180
|
-
-
|
|
181
|
-
-
|
|
182
|
-
-
|
|
183
|
-
-
|
|
292
|
+
**DO:**
|
|
293
|
+
- Focus only on the assigned task
|
|
294
|
+
- Apply project skill patterns from your prompt (or discover them from `.claude/skills/`)
|
|
295
|
+
- Write tests before implementation
|
|
296
|
+
- Test critical components beyond just acceptance criteria
|
|
297
|
+
- Write clean, modular, testable code
|
|
298
|
+
- Use clear naming that makes code self-documenting
|
|
299
|
+
|
|
300
|
+
**DON'T:**
|
|
301
|
+
- Ignore provided skill patterns - they contain project-specific conventions
|
|
302
|
+
- Refactor unrelated code
|
|
303
|
+
- Add features not in acceptance criteria
|
|
304
|
+
- Skip tests for `[auto]` criteria
|
|
305
|
+
- Make commits without running tests
|
|
306
|
+
- Add comments that restate what code does
|
|
307
|
+
- Add unnecessary JSDoc/docstrings for simple functions
|
|
308
|
+
- Leave commented-out code
|
|
184
309
|
|
|
185
310
|
## Context Escalation
|
|
186
311
|
|
package/bin/install.cjs
CHANGED
|
@@ -4,22 +4,58 @@ const fs = require("fs");
|
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const os = require("os");
|
|
6
6
|
const readline = require("readline");
|
|
7
|
+
const { execSync, spawn } = require("child_process");
|
|
7
8
|
|
|
8
9
|
const args = process.argv.slice(2);
|
|
9
10
|
|
|
10
11
|
if (args[0] === "serve") {
|
|
11
|
-
|
|
12
|
+
const serverSrc = path.join(__dirname, "..", "src", "server", "index.ts");
|
|
13
|
+
const bunPath = getBunPath();
|
|
14
|
+
if (!bunPath) {
|
|
15
|
+
console.error("Failed to start Flux MCP server: Bun is required but not found");
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
const child = spawn(bunPath, ["run", serverSrc], { stdio: "inherit" });
|
|
19
|
+
child.on("error", (err) => {
|
|
12
20
|
console.error("Failed to start Flux MCP server:", err.message);
|
|
13
21
|
process.exit(1);
|
|
14
22
|
});
|
|
23
|
+
child.on("close", (code) => process.exit(code || 0));
|
|
24
|
+
} else if (args[0] === "dashboard") {
|
|
25
|
+
const dashboardSrc = path.join(__dirname, "..", "src", "dashboard", "server.ts");
|
|
26
|
+
const bunPath = getBunPath();
|
|
27
|
+
if (!bunPath) {
|
|
28
|
+
console.error("Failed to start Flux Dashboard: Bun is required but not found");
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
const child = spawn(bunPath, ["run", dashboardSrc], { stdio: "inherit" });
|
|
32
|
+
child.on("error", (err) => {
|
|
33
|
+
console.error("Failed to start Flux Dashboard:", err.message);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
});
|
|
36
|
+
child.on("close", (code) => process.exit(code || 0));
|
|
15
37
|
} else {
|
|
16
38
|
runInstaller();
|
|
17
39
|
}
|
|
18
40
|
|
|
41
|
+
function getBunPath() {
|
|
42
|
+
const bunDir = path.join(os.homedir(), ".bun", "bin");
|
|
43
|
+
const bunBinary = process.platform === "win32" ? "bun.exe" : "bun";
|
|
44
|
+
const localBunPath = path.join(bunDir, bunBinary);
|
|
45
|
+
if (fs.existsSync(localBunPath)) return localBunPath;
|
|
46
|
+
try {
|
|
47
|
+
execSync("bun --version", { stdio: "ignore" });
|
|
48
|
+
return "bun";
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
19
54
|
function runInstaller() {
|
|
20
55
|
const cyan = "\x1b[36m";
|
|
21
56
|
const green = "\x1b[32m";
|
|
22
57
|
const yellow = "\x1b[33m";
|
|
58
|
+
const red = "\x1b[31m";
|
|
23
59
|
const dim = "\x1b[2m";
|
|
24
60
|
const reset = "\x1b[0m";
|
|
25
61
|
const pkg = require("../package.json");
|
|
@@ -43,7 +79,12 @@ ${cyan} ███████╗██╗ ██╗ ██╗██╗
|
|
|
43
79
|
console.log(banner);
|
|
44
80
|
|
|
45
81
|
if (hasHelp) {
|
|
46
|
-
console.log(` ${yellow}Usage:${reset}
|
|
82
|
+
console.log(` ${yellow}Usage:${reset} bunx @cliangdev/flux-plugin [command] [options]
|
|
83
|
+
|
|
84
|
+
${yellow}Commands:${reset}
|
|
85
|
+
${cyan}(none)${reset} Run the installer (default)
|
|
86
|
+
${cyan}serve${reset} Start the MCP server
|
|
87
|
+
${cyan}dashboard${reset} Open the Flux Dashboard in browser
|
|
47
88
|
|
|
48
89
|
${yellow}Options:${reset}
|
|
49
90
|
${cyan}-g, --global${reset} Install globally (to ~/.claude)
|
|
@@ -52,17 +93,126 @@ ${cyan} ███████╗██╗ ██╗ ██╗██╗
|
|
|
52
93
|
|
|
53
94
|
${yellow}Examples:${reset}
|
|
54
95
|
${dim}# Interactive installation${reset}
|
|
55
|
-
|
|
96
|
+
bunx @cliangdev/flux-plugin
|
|
56
97
|
|
|
57
98
|
${dim}# Install globally (all projects)${reset}
|
|
58
|
-
|
|
99
|
+
bunx @cliangdev/flux-plugin --global
|
|
59
100
|
|
|
60
101
|
${dim}# Install locally (current project only)${reset}
|
|
61
|
-
|
|
102
|
+
bunx @cliangdev/flux-plugin --local
|
|
103
|
+
|
|
104
|
+
${dim}# Open the dashboard${reset}
|
|
105
|
+
bunx @cliangdev/flux-plugin dashboard
|
|
106
|
+
|
|
107
|
+
${yellow}Note:${reset} This plugin requires Bun. Install from https://bun.sh
|
|
62
108
|
`);
|
|
63
109
|
process.exit(0);
|
|
64
110
|
}
|
|
65
111
|
|
|
112
|
+
function isBunInstalled() {
|
|
113
|
+
const bunDir = path.join(os.homedir(), ".bun", "bin");
|
|
114
|
+
const envPath = process.env.PATH || "";
|
|
115
|
+
const pathWithBun = envPath.includes(bunDir)
|
|
116
|
+
? envPath
|
|
117
|
+
: `${bunDir}${path.delimiter}${envPath}`;
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
execSync("bun --version", {
|
|
121
|
+
stdio: "ignore",
|
|
122
|
+
env: { ...process.env, PATH: pathWithBun },
|
|
123
|
+
});
|
|
124
|
+
return true;
|
|
125
|
+
} catch {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function installBun() {
|
|
131
|
+
return new Promise((resolve, reject) => {
|
|
132
|
+
const platform = os.platform();
|
|
133
|
+
const installCmd = platform === "win32" ? "powershell" : "/bin/sh";
|
|
134
|
+
const installArgs = platform === "win32"
|
|
135
|
+
? ["-c", "irm bun.sh/install.ps1 | iex"]
|
|
136
|
+
: ["-c", "curl -fsSL https://bun.sh/install | bash"];
|
|
137
|
+
|
|
138
|
+
console.log(`\n ${cyan}Installing Bun...${reset}\n`);
|
|
139
|
+
|
|
140
|
+
const child = spawn(installCmd, installArgs, {
|
|
141
|
+
stdio: "inherit",
|
|
142
|
+
shell: false,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
child.on("close", (code) => {
|
|
146
|
+
if (code === 0) {
|
|
147
|
+
console.log(`\n ${green}✓${reset} Bun installed successfully\n`);
|
|
148
|
+
resolve(true);
|
|
149
|
+
} else {
|
|
150
|
+
reject(new Error(`Installation exited with code ${code}`));
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
child.on("error", (err) => {
|
|
155
|
+
reject(err);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function showBunInstallInstructions() {
|
|
161
|
+
console.log(`
|
|
162
|
+
${yellow}Bun is required but not installed.${reset}
|
|
163
|
+
|
|
164
|
+
Install Bun manually:
|
|
165
|
+
|
|
166
|
+
${cyan}macOS/Linux:${reset}
|
|
167
|
+
curl -fsSL https://bun.sh/install | bash
|
|
168
|
+
|
|
169
|
+
${cyan}Windows:${reset}
|
|
170
|
+
powershell -c "irm bun.sh/install.ps1 | iex"
|
|
171
|
+
|
|
172
|
+
Then restart your terminal and run this installer again.
|
|
173
|
+
|
|
174
|
+
${dim}Learn more: https://bun.sh${reset}
|
|
175
|
+
`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function checkBunAndContinue(callback) {
|
|
179
|
+
if (isBunInstalled()) {
|
|
180
|
+
callback();
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const rl = readline.createInterface({
|
|
185
|
+
input: process.stdin,
|
|
186
|
+
output: process.stdout,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
console.log(` ${yellow}Bun is required but not installed.${reset}\n`);
|
|
190
|
+
|
|
191
|
+
rl.question(` Install Bun now? ${dim}[Y/n]${reset}: `, async (answer) => {
|
|
192
|
+
rl.close();
|
|
193
|
+
const shouldInstall = answer.trim().toLowerCase() !== "n";
|
|
194
|
+
|
|
195
|
+
if (shouldInstall) {
|
|
196
|
+
try {
|
|
197
|
+
await installBun();
|
|
198
|
+
if (isBunInstalled()) {
|
|
199
|
+
callback();
|
|
200
|
+
} else {
|
|
201
|
+
console.log(` ${yellow}Please restart your terminal to use Bun, then run the installer again.${reset}\n`);
|
|
202
|
+
process.exit(0);
|
|
203
|
+
}
|
|
204
|
+
} catch (err) {
|
|
205
|
+
console.log(`\n ${red}Failed to install Bun:${reset} ${err.message}\n`);
|
|
206
|
+
showBunInstallInstructions();
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
showBunInstallInstructions();
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
66
216
|
function copyDir(src, dest) {
|
|
67
217
|
fs.mkdirSync(dest, { recursive: true });
|
|
68
218
|
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
@@ -177,9 +327,10 @@ ${cyan} ███████╗██╗ ██╗ ██╗██╗
|
|
|
177
327
|
mcpConfig.mcpServers = {};
|
|
178
328
|
}
|
|
179
329
|
|
|
330
|
+
const versionTag = pkg.version.includes("-dev.") ? "latest" : pkg.version;
|
|
180
331
|
mcpConfig.mcpServers.flux = {
|
|
181
|
-
command: "
|
|
182
|
-
args: [
|
|
332
|
+
command: "bunx",
|
|
333
|
+
args: [`@cliangdev/flux-plugin@${versionTag}`, "serve"],
|
|
183
334
|
};
|
|
184
335
|
|
|
185
336
|
writeJson(mcpConfigPath, mcpConfig);
|
|
@@ -222,14 +373,18 @@ ${cyan} ███████╗██╗ ██╗ ██╗██╗
|
|
|
222
373
|
});
|
|
223
374
|
}
|
|
224
375
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
376
|
+
function startInstallation() {
|
|
377
|
+
if (hasGlobal && hasLocal) {
|
|
378
|
+
console.error(` ${yellow}Cannot specify both --global and --local${reset}`);
|
|
379
|
+
process.exit(1);
|
|
380
|
+
} else if (hasGlobal) {
|
|
381
|
+
install(true);
|
|
382
|
+
} else if (hasLocal) {
|
|
383
|
+
install(false);
|
|
384
|
+
} else {
|
|
385
|
+
promptLocation();
|
|
386
|
+
}
|
|
234
387
|
}
|
|
388
|
+
|
|
389
|
+
checkBunAndContinue(startInstallation);
|
|
235
390
|
}
|
package/commands/breakdown.md
CHANGED
|
@@ -16,12 +16,26 @@ Check if arguments were provided:
|
|
|
16
16
|
|
|
17
17
|
## Pre-checks
|
|
18
18
|
|
|
19
|
-
1.
|
|
20
|
-
- If
|
|
19
|
+
1. If no ref provided, call `query_entities` with type=prd, status=APPROVED
|
|
20
|
+
- If error with `code: "PROJECT_NOT_INITIALIZED"`, tell user: "Run `/flux` first to initialize the project." and exit.
|
|
21
21
|
|
|
22
|
-
2. If
|
|
22
|
+
2. If query successful but no approved PRDs found:
|
|
23
23
|
- If no approved PRDs, tell user: "No approved PRDs found. Approve a PRD first or run `/flux:prd` to create one."
|
|
24
|
-
- If multiple approved PRDs, use AskUserQuestion to let user select
|
|
24
|
+
- If multiple approved PRDs, use AskUserQuestion to let user select:
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"questions": [{
|
|
28
|
+
"question": "Which PRD would you like to break down?",
|
|
29
|
+
"header": "Select PRD",
|
|
30
|
+
"options": [
|
|
31
|
+
{"label": "{PRD-1 title}", "description": "{ref} - {brief description}"},
|
|
32
|
+
{"label": "{PRD-2 title}", "description": "{ref} - {brief description}"}
|
|
33
|
+
],
|
|
34
|
+
"multiSelect": false
|
|
35
|
+
}]
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
Note: Dynamically populate options from the query results.
|
|
25
39
|
|
|
26
40
|
3. If ref provided, call `get_entity` with the ref
|
|
27
41
|
- Verify status is APPROVED
|
|
@@ -78,9 +92,21 @@ Which approach do you prefer?
|
|
|
78
92
|
|
|
79
93
|
1. Get PRD entity with `get_entity` including `include: ['epics']`
|
|
80
94
|
2. Read full PRD content from `folder_path + '/prd.md'` using Read tool
|
|
81
|
-
3. If PRD already has epics, inform user:
|
|
82
|
-
|
|
83
|
-
|
|
95
|
+
3. If PRD already has epics, inform user and use AskUserQuestion:
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"questions": [{
|
|
99
|
+
"question": "This PRD already has {count} epics. What would you like to do?",
|
|
100
|
+
"header": "Epics",
|
|
101
|
+
"options": [
|
|
102
|
+
{"label": "Add more epics", "description": "Keep existing epics and add new ones"},
|
|
103
|
+
{"label": "View existing", "description": "See current epic structure before deciding"},
|
|
104
|
+
{"label": "Start fresh", "description": "Delete existing epics and recreate from scratch"}
|
|
105
|
+
],
|
|
106
|
+
"multiSelect": false
|
|
107
|
+
}]
|
|
108
|
+
}
|
|
109
|
+
```
|
|
84
110
|
|
|
85
111
|
### Step 2: Analyze & Identify Epics
|
|
86
112
|
|
|
@@ -142,9 +168,20 @@ Ready to create these epics?
|
|
|
142
168
|
```
|
|
143
169
|
|
|
144
170
|
Use AskUserQuestion only when uncertain:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"questions": [{
|
|
174
|
+
"question": "Ready to create these epics?",
|
|
175
|
+
"header": "Confirm",
|
|
176
|
+
"options": [
|
|
177
|
+
{"label": "Create all epics (Recommended)", "description": "Proceed with the proposed structure"},
|
|
178
|
+
{"label": "Modify structure first", "description": "Adjust epic boundaries or dependencies"},
|
|
179
|
+
{"label": "Add/remove epics", "description": "Change the number of epics"}
|
|
180
|
+
],
|
|
181
|
+
"multiSelect": false
|
|
182
|
+
}]
|
|
183
|
+
}
|
|
184
|
+
```
|
|
148
185
|
|
|
149
186
|
### Step 4: Create Epics
|
|
150
187
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flux:dashboard
|
|
3
|
+
description: Open the Flux Dashboard to visualize PRDs, epics, and tasks
|
|
4
|
+
allowed-tools: Bash
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Flux Dashboard
|
|
8
|
+
|
|
9
|
+
Launch the Flux Dashboard web interface to visualize project status.
|
|
10
|
+
|
|
11
|
+
## Instructions
|
|
12
|
+
|
|
13
|
+
Run the dashboard server:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bunx @cliangdev/flux-plugin dashboard
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This will:
|
|
20
|
+
1. Start the dashboard server on port 3333 (or next available)
|
|
21
|
+
2. Open the dashboard in the default browser
|
|
22
|
+
|
|
23
|
+
The dashboard shows:
|
|
24
|
+
- All PRDs with their epics and tasks
|
|
25
|
+
- Status indicators and progress
|
|
26
|
+
- Dependency graph visualization
|
|
27
|
+
- Detailed views for each entity
|
|
28
|
+
|
|
29
|
+
Tell the user the dashboard is starting and they can close it with Ctrl+C when done.
|