@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.
Files changed (108) hide show
  1. package/README.md +11 -7
  2. package/agents/coder.md +150 -25
  3. package/bin/install.cjs +171 -16
  4. package/commands/breakdown.md +47 -10
  5. package/commands/dashboard.md +29 -0
  6. package/commands/flux.md +92 -12
  7. package/commands/implement.md +166 -17
  8. package/commands/linear.md +6 -5
  9. package/commands/prd.md +996 -82
  10. package/manifest.json +2 -1
  11. package/package.json +9 -11
  12. package/skills/flux-orchestrator/SKILL.md +11 -3
  13. package/skills/prd-writer/SKILL.md +761 -0
  14. package/skills/ux-ui-design/SKILL.md +346 -0
  15. package/skills/ux-ui-design/references/design-tokens.md +359 -0
  16. package/src/__tests__/version.test.ts +37 -0
  17. package/src/adapters/local/.gitkeep +0 -0
  18. package/src/dashboard/__tests__/api.test.ts +211 -0
  19. package/src/dashboard/browser.ts +35 -0
  20. package/src/dashboard/public/app.js +869 -0
  21. package/src/dashboard/public/index.html +90 -0
  22. package/src/dashboard/public/styles.css +807 -0
  23. package/src/dashboard/public/vendor/highlight.css +10 -0
  24. package/src/dashboard/public/vendor/highlight.min.js +8422 -0
  25. package/src/dashboard/public/vendor/marked.min.js +2210 -0
  26. package/src/dashboard/server.ts +296 -0
  27. package/src/dashboard/watchers.ts +83 -0
  28. package/src/server/__tests__/config.test.ts +163 -0
  29. package/src/server/adapters/__tests__/a-client-linear.test.ts +197 -0
  30. package/src/server/adapters/__tests__/adapter-factory.test.ts +230 -0
  31. package/src/server/adapters/__tests__/dependency-ops.test.ts +429 -0
  32. package/src/server/adapters/__tests__/document-ops.test.ts +306 -0
  33. package/src/server/adapters/__tests__/linear-adapter.test.ts +91 -0
  34. package/src/server/adapters/__tests__/linear-config.test.ts +425 -0
  35. package/src/server/adapters/__tests__/linear-criteria-parser.test.ts +287 -0
  36. package/src/server/adapters/__tests__/linear-description-test.ts +238 -0
  37. package/src/server/adapters/__tests__/linear-epic-crud.test.ts +496 -0
  38. package/src/server/adapters/__tests__/linear-mappers-description.test.ts +276 -0
  39. package/src/server/adapters/__tests__/linear-mappers-epic.test.ts +294 -0
  40. package/src/server/adapters/__tests__/linear-mappers-prd.test.ts +300 -0
  41. package/src/server/adapters/__tests__/linear-mappers-task.test.ts +197 -0
  42. package/src/server/adapters/__tests__/linear-prd-crud.test.ts +620 -0
  43. package/src/server/adapters/__tests__/linear-stats.test.ts +450 -0
  44. package/src/server/adapters/__tests__/linear-task-crud.test.ts +534 -0
  45. package/src/server/adapters/__tests__/linear-types.test.ts +243 -0
  46. package/src/server/adapters/__tests__/status-ops.test.ts +441 -0
  47. package/src/server/adapters/factory.ts +90 -0
  48. package/src/server/adapters/index.ts +9 -0
  49. package/src/server/adapters/linear/adapter.ts +1141 -0
  50. package/src/server/adapters/linear/client.ts +169 -0
  51. package/src/server/adapters/linear/config.ts +152 -0
  52. package/src/server/adapters/linear/helpers/criteria-parser.ts +197 -0
  53. package/src/server/adapters/linear/helpers/index.ts +7 -0
  54. package/src/server/adapters/linear/index.ts +16 -0
  55. package/src/server/adapters/linear/mappers/description.ts +136 -0
  56. package/src/server/adapters/linear/mappers/epic.ts +81 -0
  57. package/src/server/adapters/linear/mappers/index.ts +27 -0
  58. package/src/server/adapters/linear/mappers/prd.ts +178 -0
  59. package/src/server/adapters/linear/mappers/task.ts +82 -0
  60. package/src/server/adapters/linear/types.ts +264 -0
  61. package/src/server/adapters/local-adapter.ts +1009 -0
  62. package/src/server/adapters/types.ts +293 -0
  63. package/src/server/config.ts +73 -0
  64. package/src/server/db/__tests__/queries.test.ts +473 -0
  65. package/src/server/db/ids.ts +17 -0
  66. package/src/server/db/index.ts +69 -0
  67. package/src/server/db/queries.ts +142 -0
  68. package/src/server/db/refs.ts +60 -0
  69. package/src/server/db/schema.ts +97 -0
  70. package/src/server/db/sqlite.ts +10 -0
  71. package/src/server/index.ts +81 -0
  72. package/src/server/tools/__tests__/crud.test.ts +411 -0
  73. package/src/server/tools/__tests__/get-version.test.ts +27 -0
  74. package/src/server/tools/__tests__/mcp-interface.test.ts +479 -0
  75. package/src/server/tools/__tests__/query.test.ts +405 -0
  76. package/src/server/tools/__tests__/z-configure-linear.test.ts +511 -0
  77. package/src/server/tools/__tests__/z-get-linear-url.test.ts +108 -0
  78. package/src/server/tools/configure-linear.ts +373 -0
  79. package/src/server/tools/create-epic.ts +44 -0
  80. package/src/server/tools/create-prd.ts +40 -0
  81. package/src/server/tools/create-task.ts +47 -0
  82. package/src/server/tools/criteria.ts +50 -0
  83. package/src/server/tools/delete-entity.ts +76 -0
  84. package/src/server/tools/dependencies.ts +55 -0
  85. package/src/server/tools/get-entity.ts +240 -0
  86. package/src/server/tools/get-linear-url.ts +28 -0
  87. package/src/server/tools/get-stats.ts +52 -0
  88. package/src/server/tools/get-version.ts +20 -0
  89. package/src/server/tools/index.ts +158 -0
  90. package/src/server/tools/init-project.ts +108 -0
  91. package/src/server/tools/query-entities.ts +167 -0
  92. package/src/server/tools/render-status.ts +219 -0
  93. package/src/server/tools/update-entity.ts +140 -0
  94. package/src/server/tools/update-status.ts +166 -0
  95. package/src/server/utils/__tests__/mcp-response.test.ts +331 -0
  96. package/src/server/utils/logger.ts +9 -0
  97. package/src/server/utils/mcp-response.ts +254 -0
  98. package/src/server/utils/status-transitions.ts +160 -0
  99. package/src/status-line/__tests__/status-line.test.ts +215 -0
  100. package/src/status-line/index.ts +147 -0
  101. package/src/utils/__tests__/chalk-import.test.ts +32 -0
  102. package/src/utils/__tests__/display.test.ts +97 -0
  103. package/src/utils/__tests__/status-renderer.test.ts +310 -0
  104. package/src/utils/display.ts +62 -0
  105. package/src/utils/status-renderer.ts +214 -0
  106. package/src/version.ts +5 -0
  107. package/dist/server/index.js +0 -87063
  108. 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
- npx @cliangdev/flux-plugin
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
- npx @cliangdev/flux-plugin --global # Install to ~/.claude (all projects)
20
- npx @cliangdev/flux-plugin --local # Install to ./.claude (current project)
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 product requirements through guided interview |
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
- npx @cliangdev/flux-plugin@latest --global
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-template
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-template
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: Detect Project Type & Apply Skill
23
+ ## Step 1: Apply Project Skill
24
24
 
25
- Before coding, detect the project type and apply the matching skill:
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
- # Check for project indicators
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 to Apply |
33
- |------------|--------------|----------------|
34
- | `pom.xml` | Java/Spring Boot | `springboot-patterns` |
35
- | `build.gradle` | Java/Gradle | `springboot-patterns` |
36
- | `tsconfig.json` | TypeScript | `typescript-patterns` |
37
- | `package.json` + `react` | React | `ui-patterns` |
38
- | `go.mod` | Go | Go idioms |
39
- | `Cargo.toml` | Rust | Rust idioms |
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
- **Apply the skill mentally** - follow its patterns for:
43
- - Code structure and organization
44
- - Error handling conventions
45
- - Testing patterns
46
- - Naming conventions
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
- - **DO** focus only on the assigned task
178
- - **DO** write tests before implementation
179
- - **DO** follow detected skill patterns
180
- - **DON'T** refactor unrelated code
181
- - **DON'T** add features not in acceptance criteria
182
- - **DON'T** skip tests for `[auto]` criteria
183
- - **DON'T** make commits without running tests
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
- import("../dist/server/index.js").catch((err) => {
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} npx @cliangdev/flux-plugin [options]
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
- npx @cliangdev/flux-plugin
96
+ bunx @cliangdev/flux-plugin
56
97
 
57
98
  ${dim}# Install globally (all projects)${reset}
58
- npx @cliangdev/flux-plugin --global
99
+ bunx @cliangdev/flux-plugin --global
59
100
 
60
101
  ${dim}# Install locally (current project only)${reset}
61
- npx @cliangdev/flux-plugin --local
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: "npx",
182
- args: ["-y", `@cliangdev/flux-plugin@${pkg.version}`, "serve"],
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
- if (hasGlobal && hasLocal) {
226
- console.error(` ${yellow}Cannot specify both --global and --local${reset}`);
227
- process.exit(1);
228
- } else if (hasGlobal) {
229
- install(true);
230
- } else if (hasLocal) {
231
- install(false);
232
- } else {
233
- promptLocation();
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
  }
@@ -16,12 +16,26 @@ Check if arguments were provided:
16
16
 
17
17
  ## Pre-checks
18
18
 
19
- 1. Call `get_project_context` to ensure Flux is initialized
20
- - If not initialized, tell user: "Run `/flux` first to initialize the project."
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 no ref provided, call `query_entities` with type=prd, status=APPROVED
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 which one
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
- - "This PRD already has {count} epics. Continue adding more or start fresh?"
83
- - Use AskUserQuestion with options: "Add more epics", "View existing", "Start fresh (delete existing)"
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
- - Create all epics (Recommended)
146
- - Modify structure first
147
- - Add/remove epics
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.