@cliangdev/flux-plugin 0.0.0-dev.cf5e864 → 0.0.0-dev.df9bd53
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 +12 -12
- package/agents/coder.md +192 -0
- package/agents/critic.md +174 -0
- package/agents/researcher.md +146 -0
- package/agents/verifier.md +149 -0
- package/bin/install.cjs +191 -65
- package/commands/breakdown.md +1 -0
- package/commands/flux.md +128 -84
- package/commands/implement.md +1 -0
- package/commands/linear.md +171 -0
- package/commands/prd.md +1 -0
- package/manifest.json +15 -0
- package/package.json +14 -13
- package/skills/agent-creator/SKILL.md +2 -0
- package/skills/epic-template/SKILL.md +2 -0
- package/skills/flux-orchestrator/SKILL.md +60 -76
- package/skills/prd-template/SKILL.md +2 -0
- 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,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
|
package/bin/install.cjs
CHANGED
|
@@ -4,31 +4,47 @@ 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
|
-
// Parse args first to check for serve command
|
|
9
9
|
const args = process.argv.slice(2);
|
|
10
10
|
|
|
11
|
-
// Check for "serve" subcommand - starts MCP server
|
|
12
11
|
if (args[0] === "serve") {
|
|
13
|
-
|
|
14
|
-
|
|
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) => {
|
|
15
20
|
console.error("Failed to start Flux MCP server:", err.message);
|
|
16
21
|
process.exit(1);
|
|
17
22
|
});
|
|
23
|
+
child.on("close", (code) => process.exit(code || 0));
|
|
18
24
|
} else {
|
|
19
|
-
// Run installer
|
|
20
25
|
runInstaller();
|
|
21
26
|
}
|
|
22
27
|
|
|
28
|
+
function getBunPath() {
|
|
29
|
+
const bunDir = path.join(os.homedir(), ".bun", "bin");
|
|
30
|
+
const bunBinary = process.platform === "win32" ? "bun.exe" : "bun";
|
|
31
|
+
const localBunPath = path.join(bunDir, bunBinary);
|
|
32
|
+
if (fs.existsSync(localBunPath)) return localBunPath;
|
|
33
|
+
try {
|
|
34
|
+
execSync("bun --version", { stdio: "ignore" });
|
|
35
|
+
return "bun";
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
23
41
|
function runInstaller() {
|
|
24
|
-
// Colors
|
|
25
42
|
const cyan = "\x1b[36m";
|
|
26
43
|
const green = "\x1b[32m";
|
|
27
44
|
const yellow = "\x1b[33m";
|
|
45
|
+
const red = "\x1b[31m";
|
|
28
46
|
const dim = "\x1b[2m";
|
|
29
47
|
const reset = "\x1b[0m";
|
|
30
|
-
|
|
31
|
-
// Get version from package.json
|
|
32
48
|
const pkg = require("../package.json");
|
|
33
49
|
|
|
34
50
|
const banner = `
|
|
@@ -49,9 +65,8 @@ ${cyan} ███████╗██╗ ██╗ ██╗██╗
|
|
|
49
65
|
|
|
50
66
|
console.log(banner);
|
|
51
67
|
|
|
52
|
-
// Show help
|
|
53
68
|
if (hasHelp) {
|
|
54
|
-
console.log(` ${yellow}Usage:${reset}
|
|
69
|
+
console.log(` ${yellow}Usage:${reset} bunx @cliangdev/flux-plugin [options]
|
|
55
70
|
|
|
56
71
|
${yellow}Options:${reset}
|
|
57
72
|
${cyan}-g, --global${reset} Install globally (to ~/.claude)
|
|
@@ -60,20 +75,123 @@ ${cyan} ███████╗██╗ ██╗ ██╗██╗
|
|
|
60
75
|
|
|
61
76
|
${yellow}Examples:${reset}
|
|
62
77
|
${dim}# Interactive installation${reset}
|
|
63
|
-
|
|
78
|
+
bunx @cliangdev/flux-plugin
|
|
64
79
|
|
|
65
80
|
${dim}# Install globally (all projects)${reset}
|
|
66
|
-
|
|
81
|
+
bunx @cliangdev/flux-plugin --global
|
|
67
82
|
|
|
68
83
|
${dim}# Install locally (current project only)${reset}
|
|
69
|
-
|
|
84
|
+
bunx @cliangdev/flux-plugin --local
|
|
85
|
+
|
|
86
|
+
${yellow}Note:${reset} This plugin requires Bun. Install from https://bun.sh
|
|
70
87
|
`);
|
|
71
88
|
process.exit(0);
|
|
72
89
|
}
|
|
73
90
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
91
|
+
function isBunInstalled() {
|
|
92
|
+
const bunDir = path.join(os.homedir(), ".bun", "bin");
|
|
93
|
+
const envPath = process.env.PATH || "";
|
|
94
|
+
const pathWithBun = envPath.includes(bunDir)
|
|
95
|
+
? envPath
|
|
96
|
+
: `${bunDir}${path.delimiter}${envPath}`;
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
execSync("bun --version", {
|
|
100
|
+
stdio: "ignore",
|
|
101
|
+
env: { ...process.env, PATH: pathWithBun },
|
|
102
|
+
});
|
|
103
|
+
return true;
|
|
104
|
+
} catch {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function installBun() {
|
|
110
|
+
return new Promise((resolve, reject) => {
|
|
111
|
+
const platform = os.platform();
|
|
112
|
+
const installCmd = platform === "win32" ? "powershell" : "/bin/sh";
|
|
113
|
+
const installArgs = platform === "win32"
|
|
114
|
+
? ["-c", "irm bun.sh/install.ps1 | iex"]
|
|
115
|
+
: ["-c", "curl -fsSL https://bun.sh/install | bash"];
|
|
116
|
+
|
|
117
|
+
console.log(`\n ${cyan}Installing Bun...${reset}\n`);
|
|
118
|
+
|
|
119
|
+
const child = spawn(installCmd, installArgs, {
|
|
120
|
+
stdio: "inherit",
|
|
121
|
+
shell: false,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
child.on("close", (code) => {
|
|
125
|
+
if (code === 0) {
|
|
126
|
+
console.log(`\n ${green}✓${reset} Bun installed successfully\n`);
|
|
127
|
+
resolve(true);
|
|
128
|
+
} else {
|
|
129
|
+
reject(new Error(`Installation exited with code ${code}`));
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
child.on("error", (err) => {
|
|
134
|
+
reject(err);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function showBunInstallInstructions() {
|
|
140
|
+
console.log(`
|
|
141
|
+
${yellow}Bun is required but not installed.${reset}
|
|
142
|
+
|
|
143
|
+
Install Bun manually:
|
|
144
|
+
|
|
145
|
+
${cyan}macOS/Linux:${reset}
|
|
146
|
+
curl -fsSL https://bun.sh/install | bash
|
|
147
|
+
|
|
148
|
+
${cyan}Windows:${reset}
|
|
149
|
+
powershell -c "irm bun.sh/install.ps1 | iex"
|
|
150
|
+
|
|
151
|
+
Then restart your terminal and run this installer again.
|
|
152
|
+
|
|
153
|
+
${dim}Learn more: https://bun.sh${reset}
|
|
154
|
+
`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function checkBunAndContinue(callback) {
|
|
158
|
+
if (isBunInstalled()) {
|
|
159
|
+
callback();
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const rl = readline.createInterface({
|
|
164
|
+
input: process.stdin,
|
|
165
|
+
output: process.stdout,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
console.log(` ${yellow}Bun is required but not installed.${reset}\n`);
|
|
169
|
+
|
|
170
|
+
rl.question(` Install Bun now? ${dim}[Y/n]${reset}: `, async (answer) => {
|
|
171
|
+
rl.close();
|
|
172
|
+
const shouldInstall = answer.trim().toLowerCase() !== "n";
|
|
173
|
+
|
|
174
|
+
if (shouldInstall) {
|
|
175
|
+
try {
|
|
176
|
+
await installBun();
|
|
177
|
+
if (isBunInstalled()) {
|
|
178
|
+
callback();
|
|
179
|
+
} else {
|
|
180
|
+
console.log(` ${yellow}Please restart your terminal to use Bun, then run the installer again.${reset}\n`);
|
|
181
|
+
process.exit(0);
|
|
182
|
+
}
|
|
183
|
+
} catch (err) {
|
|
184
|
+
console.log(`\n ${red}Failed to install Bun:${reset} ${err.message}\n`);
|
|
185
|
+
showBunInstallInstructions();
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
showBunInstallInstructions();
|
|
190
|
+
process.exit(1);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
77
195
|
function copyDir(src, dest) {
|
|
78
196
|
fs.mkdirSync(dest, { recursive: true });
|
|
79
197
|
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
@@ -90,9 +208,6 @@ ${cyan} ███████╗██╗ ██╗ ██╗██╗
|
|
|
90
208
|
}
|
|
91
209
|
}
|
|
92
210
|
|
|
93
|
-
/**
|
|
94
|
-
* Read JSON file, return empty object if doesn't exist
|
|
95
|
-
*/
|
|
96
211
|
function readJson(filePath) {
|
|
97
212
|
if (fs.existsSync(filePath)) {
|
|
98
213
|
try {
|
|
@@ -104,58 +219,53 @@ ${cyan} ███████╗██╗ ██╗ ██╗██╗
|
|
|
104
219
|
return {};
|
|
105
220
|
}
|
|
106
221
|
|
|
107
|
-
/**
|
|
108
|
-
* Write JSON file with formatting
|
|
109
|
-
*/
|
|
110
222
|
function writeJson(filePath, data) {
|
|
111
223
|
fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
|
|
112
224
|
}
|
|
113
225
|
|
|
114
|
-
/**
|
|
115
|
-
* Install to specified directory
|
|
116
|
-
*/
|
|
117
226
|
function install(isGlobal) {
|
|
118
227
|
const src = path.join(__dirname, "..");
|
|
119
228
|
const claudeDir = isGlobal
|
|
120
229
|
? path.join(os.homedir(), ".claude")
|
|
121
230
|
: path.join(process.cwd(), ".claude");
|
|
122
|
-
|
|
123
231
|
const locationLabel = isGlobal ? "~/.claude" : "./.claude";
|
|
124
232
|
|
|
125
233
|
console.log(` Installing to ${cyan}${locationLabel}${reset}\n`);
|
|
126
234
|
|
|
127
|
-
// Create directories
|
|
128
235
|
fs.mkdirSync(claudeDir, { recursive: true });
|
|
129
236
|
|
|
130
|
-
// Copy commands
|
|
131
237
|
const commandsSrc = path.join(src, "commands");
|
|
132
238
|
if (fs.existsSync(commandsSrc)) {
|
|
133
239
|
const commandsDest = path.join(claudeDir, "commands");
|
|
134
|
-
|
|
240
|
+
const fluxSubDir = path.join(commandsDest, "flux");
|
|
241
|
+
fs.mkdirSync(fluxSubDir, { recursive: true });
|
|
135
242
|
|
|
136
|
-
// Copy each command as a directory (flux.md -> commands/flux/COMMAND.md)
|
|
137
243
|
const commandFiles = fs.readdirSync(commandsSrc);
|
|
138
244
|
for (const file of commandFiles) {
|
|
139
245
|
if (file.endsWith(".md")) {
|
|
140
246
|
const name = file.replace(".md", "");
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
247
|
+
if (name === "flux") {
|
|
248
|
+
fs.copyFileSync(
|
|
249
|
+
path.join(commandsSrc, file),
|
|
250
|
+
path.join(commandsDest, file)
|
|
251
|
+
);
|
|
252
|
+
console.log(` ${green}✓${reset} Installed command: /flux`);
|
|
253
|
+
} else {
|
|
254
|
+
fs.copyFileSync(
|
|
255
|
+
path.join(commandsSrc, file),
|
|
256
|
+
path.join(fluxSubDir, file)
|
|
257
|
+
);
|
|
258
|
+
console.log(` ${green}✓${reset} Installed command: /flux:${name}`);
|
|
259
|
+
}
|
|
148
260
|
}
|
|
149
261
|
}
|
|
150
262
|
}
|
|
151
263
|
|
|
152
|
-
// Copy skills
|
|
153
264
|
const skillsSrc = path.join(src, "skills");
|
|
154
265
|
if (fs.existsSync(skillsSrc)) {
|
|
155
266
|
const skillsDest = path.join(claudeDir, "skills");
|
|
156
267
|
fs.mkdirSync(skillsDest, { recursive: true });
|
|
157
268
|
|
|
158
|
-
// Copy each skill directory
|
|
159
269
|
const skillDirs = fs.readdirSync(skillsSrc, { withFileTypes: true });
|
|
160
270
|
for (const dir of skillDirs) {
|
|
161
271
|
if (dir.isDirectory()) {
|
|
@@ -168,29 +278,45 @@ ${cyan} ███████╗██╗ ██╗ ██╗██╗
|
|
|
168
278
|
}
|
|
169
279
|
}
|
|
170
280
|
|
|
171
|
-
|
|
172
|
-
|
|
281
|
+
const agentsSrc = path.join(src, "agents");
|
|
282
|
+
if (fs.existsSync(agentsSrc)) {
|
|
283
|
+
const agentsDest = path.join(claudeDir, "agents");
|
|
284
|
+
fs.mkdirSync(agentsDest, { recursive: true });
|
|
285
|
+
|
|
286
|
+
const agentFiles = fs.readdirSync(agentsSrc);
|
|
287
|
+
for (const file of agentFiles) {
|
|
288
|
+
if (file.endsWith(".md")) {
|
|
289
|
+
fs.copyFileSync(
|
|
290
|
+
path.join(agentsSrc, file),
|
|
291
|
+
path.join(agentsDest, file)
|
|
292
|
+
);
|
|
293
|
+
const name = file.replace(".md", "");
|
|
294
|
+
console.log(` ${green}✓${reset} Installed agent: ${name}`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const mcpConfigPath = isGlobal
|
|
173
300
|
? path.join(os.homedir(), ".claude.json")
|
|
174
|
-
: path.join(process.cwd(), ".
|
|
301
|
+
: path.join(process.cwd(), ".mcp.json");
|
|
175
302
|
|
|
176
|
-
const
|
|
303
|
+
const mcpConfig = readJson(mcpConfigPath);
|
|
177
304
|
|
|
178
|
-
if (!
|
|
179
|
-
|
|
305
|
+
if (!mcpConfig.mcpServers) {
|
|
306
|
+
mcpConfig.mcpServers = {};
|
|
180
307
|
}
|
|
181
308
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
command: "
|
|
185
|
-
args: [
|
|
309
|
+
const versionTag = pkg.version.includes("-dev.") ? "latest" : pkg.version;
|
|
310
|
+
mcpConfig.mcpServers.flux = {
|
|
311
|
+
command: "bunx",
|
|
312
|
+
args: [`@cliangdev/flux-plugin@${versionTag}`, "serve"],
|
|
186
313
|
};
|
|
187
314
|
|
|
188
|
-
writeJson(
|
|
315
|
+
writeJson(mcpConfigPath, mcpConfig);
|
|
189
316
|
console.log(
|
|
190
|
-
` ${green}✓${reset} Configured MCP server in ${isGlobal ? "~/.claude.json" : "./.
|
|
317
|
+
` ${green}✓${reset} Configured MCP server in ${isGlobal ? "~/.claude.json" : "./.mcp.json"}`
|
|
191
318
|
);
|
|
192
319
|
|
|
193
|
-
// Write version file for update checking
|
|
194
320
|
const versionFile = path.join(claudeDir, "flux-version");
|
|
195
321
|
fs.writeFileSync(versionFile, pkg.version);
|
|
196
322
|
|
|
@@ -207,9 +333,6 @@ ${cyan} ███████╗██╗ ██╗ ██╗██╗
|
|
|
207
333
|
`);
|
|
208
334
|
}
|
|
209
335
|
|
|
210
|
-
/**
|
|
211
|
-
* Prompt for install location
|
|
212
|
-
*/
|
|
213
336
|
function promptLocation() {
|
|
214
337
|
const rl = readline.createInterface({
|
|
215
338
|
input: process.stdin,
|
|
@@ -229,15 +352,18 @@ ${cyan} ███████╗██╗ ██╗ ██╗██╗
|
|
|
229
352
|
});
|
|
230
353
|
}
|
|
231
354
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
355
|
+
function startInstallation() {
|
|
356
|
+
if (hasGlobal && hasLocal) {
|
|
357
|
+
console.error(` ${yellow}Cannot specify both --global and --local${reset}`);
|
|
358
|
+
process.exit(1);
|
|
359
|
+
} else if (hasGlobal) {
|
|
360
|
+
install(true);
|
|
361
|
+
} else if (hasLocal) {
|
|
362
|
+
install(false);
|
|
363
|
+
} else {
|
|
364
|
+
promptLocation();
|
|
365
|
+
}
|
|
242
366
|
}
|
|
367
|
+
|
|
368
|
+
checkBunAndContinue(startInstallation);
|
|
243
369
|
}
|