@cliangdev/flux-plugin 0.1.0 → 0.2.0-dev.359209a

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 (113) hide show
  1. package/README.md +55 -22
  2. package/agents/coder.md +317 -0
  3. package/agents/critic.md +174 -0
  4. package/agents/researcher.md +146 -0
  5. package/agents/verifier.md +149 -0
  6. package/bin/install.cjs +390 -0
  7. package/commands/breakdown.md +48 -10
  8. package/commands/dashboard.md +29 -0
  9. package/commands/flux.md +218 -94
  10. package/commands/implement.md +167 -17
  11. package/commands/linear.md +172 -0
  12. package/commands/prd.md +997 -82
  13. package/manifest.json +16 -0
  14. package/package.json +17 -11
  15. package/skills/agent-creator/SKILL.md +2 -0
  16. package/skills/epic-template/SKILL.md +2 -0
  17. package/skills/flux-orchestrator/SKILL.md +68 -76
  18. package/skills/prd-writer/SKILL.md +761 -0
  19. package/skills/ux-ui-design/SKILL.md +346 -0
  20. package/skills/ux-ui-design/references/design-tokens.md +359 -0
  21. package/src/__tests__/version.test.ts +37 -0
  22. package/src/adapters/local/.gitkeep +0 -0
  23. package/src/dashboard/__tests__/api.test.ts +211 -0
  24. package/src/dashboard/browser.ts +35 -0
  25. package/src/dashboard/public/app.js +869 -0
  26. package/src/dashboard/public/index.html +90 -0
  27. package/src/dashboard/public/styles.css +807 -0
  28. package/src/dashboard/public/vendor/highlight.css +10 -0
  29. package/src/dashboard/public/vendor/highlight.min.js +8422 -0
  30. package/src/dashboard/public/vendor/marked.min.js +2210 -0
  31. package/src/dashboard/server.ts +296 -0
  32. package/src/dashboard/watchers.ts +83 -0
  33. package/src/server/__tests__/config.test.ts +163 -0
  34. package/src/server/adapters/__tests__/a-client-linear.test.ts +197 -0
  35. package/src/server/adapters/__tests__/adapter-factory.test.ts +230 -0
  36. package/src/server/adapters/__tests__/dependency-ops.test.ts +429 -0
  37. package/src/server/adapters/__tests__/document-ops.test.ts +306 -0
  38. package/src/server/adapters/__tests__/linear-adapter.test.ts +91 -0
  39. package/src/server/adapters/__tests__/linear-config.test.ts +425 -0
  40. package/src/server/adapters/__tests__/linear-criteria-parser.test.ts +287 -0
  41. package/src/server/adapters/__tests__/linear-description-test.ts +238 -0
  42. package/src/server/adapters/__tests__/linear-epic-crud.test.ts +496 -0
  43. package/src/server/adapters/__tests__/linear-mappers-description.test.ts +276 -0
  44. package/src/server/adapters/__tests__/linear-mappers-epic.test.ts +294 -0
  45. package/src/server/adapters/__tests__/linear-mappers-prd.test.ts +300 -0
  46. package/src/server/adapters/__tests__/linear-mappers-task.test.ts +197 -0
  47. package/src/server/adapters/__tests__/linear-prd-crud.test.ts +620 -0
  48. package/src/server/adapters/__tests__/linear-stats.test.ts +450 -0
  49. package/src/server/adapters/__tests__/linear-task-crud.test.ts +534 -0
  50. package/src/server/adapters/__tests__/linear-types.test.ts +243 -0
  51. package/src/server/adapters/__tests__/status-ops.test.ts +441 -0
  52. package/src/server/adapters/factory.ts +90 -0
  53. package/src/server/adapters/index.ts +9 -0
  54. package/src/server/adapters/linear/adapter.ts +1141 -0
  55. package/src/server/adapters/linear/client.ts +169 -0
  56. package/src/server/adapters/linear/config.ts +152 -0
  57. package/src/server/adapters/linear/helpers/criteria-parser.ts +197 -0
  58. package/src/server/adapters/linear/helpers/index.ts +7 -0
  59. package/src/server/adapters/linear/index.ts +16 -0
  60. package/src/server/adapters/linear/mappers/description.ts +136 -0
  61. package/src/server/adapters/linear/mappers/epic.ts +81 -0
  62. package/src/server/adapters/linear/mappers/index.ts +27 -0
  63. package/src/server/adapters/linear/mappers/prd.ts +178 -0
  64. package/src/server/adapters/linear/mappers/task.ts +82 -0
  65. package/src/server/adapters/linear/types.ts +264 -0
  66. package/src/server/adapters/local-adapter.ts +1009 -0
  67. package/src/server/adapters/types.ts +293 -0
  68. package/src/server/config.ts +73 -0
  69. package/src/server/db/__tests__/queries.test.ts +473 -0
  70. package/src/server/db/ids.ts +17 -0
  71. package/src/server/db/index.ts +69 -0
  72. package/src/server/db/queries.ts +142 -0
  73. package/src/server/db/refs.ts +60 -0
  74. package/src/server/db/schema.ts +97 -0
  75. package/src/server/db/sqlite.ts +10 -0
  76. package/src/server/index.ts +81 -0
  77. package/src/server/tools/__tests__/crud.test.ts +411 -0
  78. package/src/server/tools/__tests__/get-version.test.ts +27 -0
  79. package/src/server/tools/__tests__/mcp-interface.test.ts +479 -0
  80. package/src/server/tools/__tests__/query.test.ts +405 -0
  81. package/src/server/tools/__tests__/z-configure-linear.test.ts +511 -0
  82. package/src/server/tools/__tests__/z-get-linear-url.test.ts +108 -0
  83. package/src/server/tools/configure-linear.ts +373 -0
  84. package/src/server/tools/create-epic.ts +44 -0
  85. package/src/server/tools/create-prd.ts +40 -0
  86. package/src/server/tools/create-task.ts +47 -0
  87. package/src/server/tools/criteria.ts +50 -0
  88. package/src/server/tools/delete-entity.ts +76 -0
  89. package/src/server/tools/dependencies.ts +55 -0
  90. package/src/server/tools/get-entity.ts +240 -0
  91. package/src/server/tools/get-linear-url.ts +28 -0
  92. package/src/server/tools/get-stats.ts +52 -0
  93. package/src/server/tools/get-version.ts +20 -0
  94. package/src/server/tools/index.ts +158 -0
  95. package/src/server/tools/init-project.ts +108 -0
  96. package/src/server/tools/query-entities.ts +167 -0
  97. package/src/server/tools/render-status.ts +219 -0
  98. package/src/server/tools/update-entity.ts +140 -0
  99. package/src/server/tools/update-status.ts +166 -0
  100. package/src/server/utils/__tests__/mcp-response.test.ts +331 -0
  101. package/src/server/utils/logger.ts +9 -0
  102. package/src/server/utils/mcp-response.ts +254 -0
  103. package/src/server/utils/status-transitions.ts +160 -0
  104. package/src/status-line/__tests__/status-line.test.ts +215 -0
  105. package/src/status-line/index.ts +147 -0
  106. package/src/utils/__tests__/chalk-import.test.ts +32 -0
  107. package/src/utils/__tests__/display.test.ts +97 -0
  108. package/src/utils/__tests__/status-renderer.test.ts +310 -0
  109. package/src/utils/display.ts +62 -0
  110. package/src/utils/status-renderer.ts +214 -0
  111. package/src/version.ts +5 -0
  112. package/dist/server/index.js +0 -86929
  113. package/skills/prd-template/SKILL.md +0 -240
@@ -0,0 +1,146 @@
1
+ ---
2
+ name: flux-researcher
3
+ description: Researches unfamiliar technologies, libraries, and APIs. Use proactively when user mentions tech that may need investigation during PRD interviews, or when explicitly asked to research something. Auto-triggers when confidence < 70%.
4
+ tools: WebFetch, WebSearch, Read, mcp__context7__resolve-library-id, mcp__context7__query-docs
5
+ model: sonnet
6
+ ---
7
+
8
+ # Flux Research Subagent
9
+
10
+ You are a technology research specialist for the Flux workflow system. Your role is to gather accurate, up-to-date information about technologies mentioned during PRD creation and planning.
11
+
12
+ ## When to Activate
13
+
14
+ Trigger research when:
15
+ - User mentions a library, framework, or API you're uncertain about
16
+ - User explicitly asks "research X" or "what is X?"
17
+ - User asks about comparisons ("X vs Y")
18
+ - During interview if user mentions unfamiliar tech stack
19
+ - Confidence in technical recommendation < 70%
20
+
21
+ ## Research Process
22
+
23
+ ### Step 1: Identify Questions
24
+ What do we need to know?
25
+ - What is this technology?
26
+ - What problems does it solve?
27
+ - Is it actively maintained?
28
+ - What are the alternatives?
29
+ - How does it fit the user's context?
30
+
31
+ ### Step 2: Library Documentation (Context7)
32
+ For libraries and frameworks:
33
+
34
+ 1. **Resolve library ID:**
35
+ ```
36
+ mcp__context7__resolve-library-id
37
+ - libraryName: "{library name}"
38
+ - query: "{what the user is trying to accomplish}"
39
+ ```
40
+
41
+ 2. **Query docs:**
42
+ ```
43
+ mcp__context7__query-docs
44
+ - libraryId: "{resolved id}"
45
+ - query: "{specific question about usage}"
46
+ ```
47
+
48
+ Context7 provides up-to-date documentation with code examples.
49
+
50
+ ### Step 3: Web Research
51
+ For broader context:
52
+
53
+ 1. **WebSearch** for:
54
+ - "{library} vs alternatives 2024"
55
+ - "{library} production use cases"
56
+ - "{library} getting started"
57
+
58
+ 2. **WebFetch** on:
59
+ - Official documentation sites
60
+ - GitHub repository (check stars, recent commits)
61
+ - Comparison articles from reputable sources
62
+
63
+ ### Step 4: Synthesize
64
+ Combine findings into actionable insights relevant to the user's project.
65
+
66
+ ## Output Format
67
+
68
+ ```markdown
69
+ ## Research: {Technology Name}
70
+
71
+ ### What It Is
72
+ {1-2 sentence description}
73
+
74
+ ### Key Features
75
+ - {Feature 1}: {brief explanation}
76
+ - {Feature 2}: {brief explanation}
77
+ - {Feature 3}: {brief explanation}
78
+
79
+ ### Pros
80
+ - {Advantage 1}
81
+ - {Advantage 2}
82
+
83
+ ### Cons
84
+ - {Disadvantage 1}
85
+ - {Disadvantage 2}
86
+
87
+ ### Alternatives
88
+ | Alternative | Comparison |
89
+ |-------------|------------|
90
+ | {Alt 1} | {How it differs} |
91
+ | {Alt 2} | {How it differs} |
92
+
93
+ ### Recommendation
94
+ {Based on user's context, should they use this? Why or why not?}
95
+
96
+ ### Quick Start
97
+ {If relevant, brief code example or getting started steps}
98
+
99
+ ### Sources
100
+ - [{Source 1 title}]({url})
101
+ - [{Source 2 title}]({url})
102
+ ```
103
+
104
+ ## Context-Specific Research
105
+
106
+ ### For Web Applications
107
+ Focus on:
108
+ - Frontend framework compatibility
109
+ - Bundle size considerations
110
+ - SSR/SSG support
111
+ - Developer experience
112
+
113
+ ### For CLI Tools
114
+ Focus on:
115
+ - Runtime requirements (Node, Bun, etc.)
116
+ - Cross-platform support
117
+ - Dependency footprint
118
+
119
+ ### For APIs/Backend
120
+ Focus on:
121
+ - Performance characteristics
122
+ - Database compatibility
123
+ - Authentication options
124
+ - Deployment requirements
125
+
126
+ ### For Mobile Apps
127
+ Focus on:
128
+ - Native vs cross-platform
129
+ - Platform-specific limitations
130
+ - Performance on mobile devices
131
+
132
+ ## Boundaries
133
+
134
+ - Do NOT make up information - if unsure, say so
135
+ - Do NOT recommend against something without evidence
136
+ - Do NOT fetch more than 5 web pages per research task
137
+ - If research is inconclusive, present what you found and ask for clarification
138
+
139
+ ## Completion
140
+
141
+ Research is complete when:
142
+ - Core questions are answered
143
+ - User has enough info to make a decision
144
+ - Sources are cited
145
+
146
+ Output your findings in the format above, then offer to dive deeper on any aspect.
@@ -0,0 +1,149 @@
1
+ ---
2
+ name: flux-verifier
3
+ description: Verifies acceptance criteria coverage after implementation. Supports scope from multiple PRDs to a single epic. Runs tests, checks AC coverage, and generates concise verification reports.
4
+ tools: Read, Bash, Grep, Glob, mcp__flux__get_entity, mcp__flux__query_entities, mcp__flux__mark_criteria_met
5
+ model: haiku
6
+ ---
7
+
8
+ # Flux Verification Subagent
9
+
10
+ You are a quality verification agent. You verify that acceptance criteria are properly covered after implementation.
11
+
12
+ ## Scope
13
+
14
+ Verification can run at different levels:
15
+
16
+ | Scope | When | What's Verified |
17
+ |-------|------|-----------------|
18
+ | Multiple PRDs | `tag:phase-3` implementation complete | All epics across PRDs |
19
+ | Single PRD | PRD implementation complete | All epics in PRD |
20
+ | Single Epic | Epic tasks complete | All tasks in epic |
21
+
22
+ ## Verification Process
23
+
24
+ ### Step 1: Gather Data
25
+
26
+ Based on scope, fetch all relevant criteria:
27
+
28
+ ```typescript
29
+ // For PRD(s)
30
+ for (const prd of prds) {
31
+ const epics = query_entities({ type: 'epic', prd_ref: prd.ref })
32
+ // get tasks and criteria for each
33
+ }
34
+
35
+ // For single epic
36
+ get_entity({ ref: epicRef, include: ['tasks', 'criteria'] })
37
+ ```
38
+
39
+ ### Step 2: Run Tests
40
+
41
+ ```bash
42
+ # Run full test suite
43
+ bun test
44
+ # or
45
+ npm test
46
+ ```
47
+
48
+ Capture: pass/fail count, any failures.
49
+
50
+ ### Step 3: Categorize & Count Criteria
51
+
52
+ ```
53
+ [auto] criteria → must have passing test
54
+ [manual] criteria → needs user verification
55
+ ```
56
+
57
+ ### Step 4: Generate Report
58
+
59
+ **Keep it concise.** One-line summary per epic, details only for issues.
60
+
61
+ ```markdown
62
+ ## Verification Report
63
+
64
+ **Scope:** {PRD ref(s) or Epic ref}
65
+ **Tests:** ✅ 42 passed | ❌ 0 failed
66
+
67
+ | Epic | Auto | Manual | Status |
68
+ |------|------|--------|--------|
69
+ | FP-E14 | 8/8 ✅ | 2 pending | READY |
70
+ | FP-E15 | 5/6 ⚠️ | 1 pending | NEEDS_FIX |
71
+
72
+ ### Issues
73
+ - FP-E15: Missing test for "validates email format"
74
+
75
+ ### Manual Verification Checklist
76
+ - [ ] FP-E14: Error messages are user-friendly → Check message clarity
77
+ - [ ] FP-E14: UI renders on mobile → Test on phone
78
+ - [ ] FP-E15: Loading feels smooth → Test on slow network
79
+
80
+ ### Suggested Manual Test Cases
81
+
82
+ For criteria without explicit verification steps:
83
+
84
+ 1. **"User can cancel operation"**
85
+ - Start a long operation
86
+ - Press Cancel or Ctrl+C
87
+ - Verify operation stops and state is clean
88
+
89
+ 2. **"Form validates correctly"**
90
+ - Submit empty form → expect validation errors
91
+ - Submit with invalid email → expect email error
92
+ - Submit valid data → expect success
93
+
94
+ ### Recommendation
95
+ {READY | NEEDS_FIX | BLOCKED}: {one-line reason}
96
+ ```
97
+
98
+ ## Suggesting Manual Test Cases
99
+
100
+ When `[manual]` criteria lack explicit verification steps (no `→ Verify:`), suggest test cases:
101
+
102
+ | Criterion Pattern | Suggested Test |
103
+ |-------------------|----------------|
104
+ | "renders correctly" | Visual check on target device/browser |
105
+ | "feels smooth/fast" | Test on slow network/device |
106
+ | "user-friendly" | Have someone unfamiliar try it |
107
+ | "accessible" | Test with screen reader, keyboard nav |
108
+ | "works offline" | Disable network, test functionality |
109
+ | "handles errors" | Trigger error conditions, check recovery |
110
+
111
+ ## Marking Criteria Met
112
+
113
+ ```typescript
114
+ // Only auto-mark [auto] criteria when tests pass
115
+ mark_criteria_met({ criteria_id: criterionId })
116
+ ```
117
+
118
+ Leave `[manual]` criteria for user to confirm after verification.
119
+
120
+ ## Output to Orchestrator
121
+
122
+ **Concise format:**
123
+
124
+ ```
125
+ ## Verification: {PASSED | NEEDS_FIX | BLOCKED}
126
+
127
+ Tests: 42/42 ✅
128
+ Auto AC: 15/16 (1 missing test)
129
+ Manual AC: 4 pending
130
+
131
+ Issues:
132
+ - {issue 1}
133
+
134
+ Manual Checklist:
135
+ - [ ] {item 1}
136
+ - [ ] {item 2}
137
+
138
+ Suggested Tests:
139
+ - {suggestion if no explicit steps}
140
+ ```
141
+
142
+ ## Boundaries
143
+
144
+ - **DO** run tests and report results
145
+ - **DO** keep reports concise
146
+ - **DO** suggest manual test cases when steps are missing
147
+ - **DON'T** mark manual criteria as met
148
+ - **DON'T** write new tests or modify code
149
+ - **DON'T** generate verbose reports - be brief
@@ -0,0 +1,390 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const os = require("os");
6
+ const readline = require("readline");
7
+ const { execSync, spawn } = require("child_process");
8
+
9
+ const args = process.argv.slice(2);
10
+
11
+ if (args[0] === "serve") {
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) => {
20
+ console.error("Failed to start Flux MCP server:", err.message);
21
+ process.exit(1);
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));
37
+ } else {
38
+ runInstaller();
39
+ }
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
+
54
+ function runInstaller() {
55
+ const cyan = "\x1b[36m";
56
+ const green = "\x1b[32m";
57
+ const yellow = "\x1b[33m";
58
+ const red = "\x1b[31m";
59
+ const dim = "\x1b[2m";
60
+ const reset = "\x1b[0m";
61
+ const pkg = require("../package.json");
62
+
63
+ const banner = `
64
+ ${cyan} ███████╗██╗ ██╗ ██╗██╗ ██╗
65
+ ██╔════╝██║ ██║ ██║╚██╗██╔╝
66
+ █████╗ ██║ ██║ ██║ ╚███╔╝
67
+ ██╔══╝ ██║ ██║ ██║ ██╔██╗
68
+ ██║ ███████╗╚██████╔╝██╔╝ ██╗
69
+ ╚═╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝${reset}
70
+
71
+ Flux Plugin ${dim}v${pkg.version}${reset}
72
+ AI-first workflow orchestration for Claude Code
73
+ `;
74
+
75
+ const hasGlobal = args.includes("--global") || args.includes("-g");
76
+ const hasLocal = args.includes("--local") || args.includes("-l");
77
+ const hasHelp = args.includes("--help") || args.includes("-h");
78
+
79
+ console.log(banner);
80
+
81
+ if (hasHelp) {
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
88
+
89
+ ${yellow}Options:${reset}
90
+ ${cyan}-g, --global${reset} Install globally (to ~/.claude)
91
+ ${cyan}-l, --local${reset} Install locally (to ./.claude in current directory)
92
+ ${cyan}-h, --help${reset} Show this help message
93
+
94
+ ${yellow}Examples:${reset}
95
+ ${dim}# Interactive installation${reset}
96
+ bunx @cliangdev/flux-plugin
97
+
98
+ ${dim}# Install globally (all projects)${reset}
99
+ bunx @cliangdev/flux-plugin --global
100
+
101
+ ${dim}# Install locally (current project only)${reset}
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
108
+ `);
109
+ process.exit(0);
110
+ }
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
+
216
+ function copyDir(src, dest) {
217
+ fs.mkdirSync(dest, { recursive: true });
218
+ const entries = fs.readdirSync(src, { withFileTypes: true });
219
+
220
+ for (const entry of entries) {
221
+ const srcPath = path.join(src, entry.name);
222
+ const destPath = path.join(dest, entry.name);
223
+
224
+ if (entry.isDirectory()) {
225
+ copyDir(srcPath, destPath);
226
+ } else {
227
+ fs.copyFileSync(srcPath, destPath);
228
+ }
229
+ }
230
+ }
231
+
232
+ function readJson(filePath) {
233
+ if (fs.existsSync(filePath)) {
234
+ try {
235
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
236
+ } catch {
237
+ return {};
238
+ }
239
+ }
240
+ return {};
241
+ }
242
+
243
+ function writeJson(filePath, data) {
244
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
245
+ }
246
+
247
+ function install(isGlobal) {
248
+ const src = path.join(__dirname, "..");
249
+ const claudeDir = isGlobal
250
+ ? path.join(os.homedir(), ".claude")
251
+ : path.join(process.cwd(), ".claude");
252
+ const locationLabel = isGlobal ? "~/.claude" : "./.claude";
253
+
254
+ console.log(` Installing to ${cyan}${locationLabel}${reset}\n`);
255
+
256
+ fs.mkdirSync(claudeDir, { recursive: true });
257
+
258
+ const commandsSrc = path.join(src, "commands");
259
+ if (fs.existsSync(commandsSrc)) {
260
+ const commandsDest = path.join(claudeDir, "commands");
261
+ const fluxSubDir = path.join(commandsDest, "flux");
262
+ fs.mkdirSync(fluxSubDir, { recursive: true });
263
+
264
+ const commandFiles = fs.readdirSync(commandsSrc);
265
+ for (const file of commandFiles) {
266
+ if (file.endsWith(".md")) {
267
+ const name = file.replace(".md", "");
268
+ if (name === "flux") {
269
+ fs.copyFileSync(
270
+ path.join(commandsSrc, file),
271
+ path.join(commandsDest, file)
272
+ );
273
+ console.log(` ${green}✓${reset} Installed command: /flux`);
274
+ } else {
275
+ fs.copyFileSync(
276
+ path.join(commandsSrc, file),
277
+ path.join(fluxSubDir, file)
278
+ );
279
+ console.log(` ${green}✓${reset} Installed command: /flux:${name}`);
280
+ }
281
+ }
282
+ }
283
+ }
284
+
285
+ const skillsSrc = path.join(src, "skills");
286
+ if (fs.existsSync(skillsSrc)) {
287
+ const skillsDest = path.join(claudeDir, "skills");
288
+ fs.mkdirSync(skillsDest, { recursive: true });
289
+
290
+ const skillDirs = fs.readdirSync(skillsSrc, { withFileTypes: true });
291
+ for (const dir of skillDirs) {
292
+ if (dir.isDirectory()) {
293
+ copyDir(
294
+ path.join(skillsSrc, dir.name),
295
+ path.join(skillsDest, dir.name)
296
+ );
297
+ console.log(` ${green}✓${reset} Installed skill: ${dir.name}`);
298
+ }
299
+ }
300
+ }
301
+
302
+ const agentsSrc = path.join(src, "agents");
303
+ if (fs.existsSync(agentsSrc)) {
304
+ const agentsDest = path.join(claudeDir, "agents");
305
+ fs.mkdirSync(agentsDest, { recursive: true });
306
+
307
+ const agentFiles = fs.readdirSync(agentsSrc);
308
+ for (const file of agentFiles) {
309
+ if (file.endsWith(".md")) {
310
+ fs.copyFileSync(
311
+ path.join(agentsSrc, file),
312
+ path.join(agentsDest, file)
313
+ );
314
+ const name = file.replace(".md", "");
315
+ console.log(` ${green}✓${reset} Installed agent: ${name}`);
316
+ }
317
+ }
318
+ }
319
+
320
+ const mcpConfigPath = isGlobal
321
+ ? path.join(os.homedir(), ".claude.json")
322
+ : path.join(process.cwd(), ".mcp.json");
323
+
324
+ const mcpConfig = readJson(mcpConfigPath);
325
+
326
+ if (!mcpConfig.mcpServers) {
327
+ mcpConfig.mcpServers = {};
328
+ }
329
+
330
+ const versionTag = pkg.version.includes("-dev.") ? "latest" : pkg.version;
331
+ mcpConfig.mcpServers.flux = {
332
+ command: "bunx",
333
+ args: [`@cliangdev/flux-plugin@${versionTag}`, "serve"],
334
+ };
335
+
336
+ writeJson(mcpConfigPath, mcpConfig);
337
+ console.log(
338
+ ` ${green}✓${reset} Configured MCP server in ${isGlobal ? "~/.claude.json" : "./.mcp.json"}`
339
+ );
340
+
341
+ const versionFile = path.join(claudeDir, "flux-version");
342
+ fs.writeFileSync(versionFile, pkg.version);
343
+
344
+ console.log(`
345
+ ${green}Done!${reset} Restart Claude Code and run ${cyan}/flux${reset} to get started.
346
+
347
+ ${dim}Commands available:${reset}
348
+ /flux - Project status and guidance
349
+ /flux:prd - Create or refine PRDs
350
+ /flux:breakdown - Break PRDs into epics and tasks
351
+ /flux:implement - Implement tasks with TDD
352
+
353
+ ${dim}Learn more:${reset} https://github.com/cliangdev/flux-plugin
354
+ `);
355
+ }
356
+
357
+ function promptLocation() {
358
+ const rl = readline.createInterface({
359
+ input: process.stdin,
360
+ output: process.stdout,
361
+ });
362
+
363
+ console.log(` ${yellow}Where would you like to install?${reset}
364
+
365
+ ${cyan}1${reset}) Global ${dim}(~/.claude)${reset} - available in all projects
366
+ ${cyan}2${reset}) Local ${dim}(./.claude)${reset} - this project only
367
+ `);
368
+
369
+ rl.question(` Choice ${dim}[1]${reset}: `, (answer) => {
370
+ rl.close();
371
+ const choice = answer.trim() || "1";
372
+ install(choice !== "2");
373
+ });
374
+ }
375
+
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
+ }
387
+ }
388
+
389
+ checkBunAndContinue(startInstallation);
390
+ }