@cliangdev/flux-plugin 0.0.0-dev.8e9707e → 0.0.0-dev.cf5e864

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 CHANGED
@@ -4,30 +4,29 @@ Agent-orchestrated, spec-driven workflow for Claude Code.
4
4
 
5
5
  ## Installation
6
6
 
7
- ### Quick Install (Recommended)
8
-
9
7
  ```bash
10
- claude mcp add flux -- npx -y @cliangdev/flux-plugin
8
+ npx @cliangdev/flux-plugin
11
9
  ```
12
10
 
13
- ### Manual Configuration
14
-
15
- Add to your `.mcp.json`:
16
-
17
- ```json
18
- {
19
- "mcpServers": {
20
- "flux": {
21
- "command": "npx",
22
- "args": ["-y", "@cliangdev/flux-plugin"],
23
- "env": {
24
- "FLUX_PROJECT_ROOT": "${CLAUDE_PROJECT_DIR}"
25
- }
26
- }
27
- }
28
- }
11
+ This installs:
12
+ - Commands (`/flux`, `/flux:prd`, `/flux:breakdown`, `/flux:implement`)
13
+ - Skills for AI orchestration
14
+ - MCP server for project data
15
+
16
+ ### Options
17
+
18
+ ```bash
19
+ npx @cliangdev/flux-plugin --global # Install to ~/.claude (all projects)
20
+ npx @cliangdev/flux-plugin --local # Install to ./.claude (current project)
29
21
  ```
30
22
 
23
+ ### What Gets Configured
24
+
25
+ The installer automatically:
26
+ 1. Copies commands to `.claude/commands/`
27
+ 2. Copies skills to `.claude/skills/`
28
+ 3. Adds MCP server config to `.claude.json`
29
+
31
30
  ## Commands
32
31
 
33
32
  | Command | Purpose |
@@ -119,13 +118,47 @@ your-project/
119
118
 
120
119
  ## Updating
121
120
 
122
- The plugin auto-updates via npx. Restart Claude Code to get the latest version.
121
+ To update to the latest version, simply re-run the installer:
123
122
 
124
- Check your version:
123
+ ```bash
124
+ npx @cliangdev/flux-plugin@latest --global
125
+ ```
126
+
127
+ This will:
128
+ - Update commands and skills to the latest version
129
+ - Update the MCP server configuration
130
+
131
+ Check your current version:
125
132
  ```
126
133
  /flux version
127
134
  ```
128
135
 
136
+ ## Uninstall
137
+
138
+ ### Global Installation
139
+
140
+ ```bash
141
+ # Remove commands and skills
142
+ rm -rf ~/.claude/commands/flux ~/.claude/commands/prd ~/.claude/commands/breakdown ~/.claude/commands/implement
143
+ rm -rf ~/.claude/skills/agent-creator ~/.claude/skills/epic-template ~/.claude/skills/flux-orchestrator ~/.claude/skills/prd-template
144
+ rm -f ~/.claude/flux-version
145
+
146
+ # Remove MCP server config (edit ~/.claude.json and remove the "flux" entry from "mcpServers")
147
+ ```
148
+
149
+ ### Local Installation
150
+
151
+ ```bash
152
+ # Remove commands and skills
153
+ rm -rf .claude/commands/flux .claude/commands/prd .claude/commands/breakdown .claude/commands/implement
154
+ rm -rf .claude/skills/agent-creator .claude/skills/epic-template .claude/skills/flux-orchestrator .claude/skills/prd-template
155
+ rm -f .claude/flux-version
156
+
157
+ # Remove MCP server config (edit .claude.json and remove the "flux" entry from "mcpServers")
158
+ ```
159
+
160
+ Note: Your project data in `.flux/` is preserved. Delete it manually if you want to remove all Flux data.
161
+
129
162
  ## Support
130
163
 
131
164
  GitHub: [github.com/cliangdev/flux-plugin](https://github.com/cliangdev/flux-plugin)
@@ -0,0 +1,243 @@
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
+
8
+ // Parse args first to check for serve command
9
+ const args = process.argv.slice(2);
10
+
11
+ // Check for "serve" subcommand - starts MCP server
12
+ if (args[0] === "serve") {
13
+ // Dynamic import to load ESM server
14
+ import("../dist/server/index.js").catch((err) => {
15
+ console.error("Failed to start Flux MCP server:", err.message);
16
+ process.exit(1);
17
+ });
18
+ } else {
19
+ // Run installer
20
+ runInstaller();
21
+ }
22
+
23
+ function runInstaller() {
24
+ // Colors
25
+ const cyan = "\x1b[36m";
26
+ const green = "\x1b[32m";
27
+ const yellow = "\x1b[33m";
28
+ const dim = "\x1b[2m";
29
+ const reset = "\x1b[0m";
30
+
31
+ // Get version from package.json
32
+ const pkg = require("../package.json");
33
+
34
+ const banner = `
35
+ ${cyan} ███████╗██╗ ██╗ ██╗██╗ ██╗
36
+ ██╔════╝██║ ██║ ██║╚██╗██╔╝
37
+ █████╗ ██║ ██║ ██║ ╚███╔╝
38
+ ██╔══╝ ██║ ██║ ██║ ██╔██╗
39
+ ██║ ███████╗╚██████╔╝██╔╝ ██╗
40
+ ╚═╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝${reset}
41
+
42
+ Flux Plugin ${dim}v${pkg.version}${reset}
43
+ AI-first workflow orchestration for Claude Code
44
+ `;
45
+
46
+ const hasGlobal = args.includes("--global") || args.includes("-g");
47
+ const hasLocal = args.includes("--local") || args.includes("-l");
48
+ const hasHelp = args.includes("--help") || args.includes("-h");
49
+
50
+ console.log(banner);
51
+
52
+ // Show help
53
+ if (hasHelp) {
54
+ console.log(` ${yellow}Usage:${reset} npx @cliangdev/flux-plugin [options]
55
+
56
+ ${yellow}Options:${reset}
57
+ ${cyan}-g, --global${reset} Install globally (to ~/.claude)
58
+ ${cyan}-l, --local${reset} Install locally (to ./.claude in current directory)
59
+ ${cyan}-h, --help${reset} Show this help message
60
+
61
+ ${yellow}Examples:${reset}
62
+ ${dim}# Interactive installation${reset}
63
+ npx @cliangdev/flux-plugin
64
+
65
+ ${dim}# Install globally (all projects)${reset}
66
+ npx @cliangdev/flux-plugin --global
67
+
68
+ ${dim}# Install locally (current project only)${reset}
69
+ npx @cliangdev/flux-plugin --local
70
+ `);
71
+ process.exit(0);
72
+ }
73
+
74
+ /**
75
+ * Recursively copy directory
76
+ */
77
+ function copyDir(src, dest) {
78
+ fs.mkdirSync(dest, { recursive: true });
79
+ const entries = fs.readdirSync(src, { withFileTypes: true });
80
+
81
+ for (const entry of entries) {
82
+ const srcPath = path.join(src, entry.name);
83
+ const destPath = path.join(dest, entry.name);
84
+
85
+ if (entry.isDirectory()) {
86
+ copyDir(srcPath, destPath);
87
+ } else {
88
+ fs.copyFileSync(srcPath, destPath);
89
+ }
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Read JSON file, return empty object if doesn't exist
95
+ */
96
+ function readJson(filePath) {
97
+ if (fs.existsSync(filePath)) {
98
+ try {
99
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
100
+ } catch {
101
+ return {};
102
+ }
103
+ }
104
+ return {};
105
+ }
106
+
107
+ /**
108
+ * Write JSON file with formatting
109
+ */
110
+ function writeJson(filePath, data) {
111
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
112
+ }
113
+
114
+ /**
115
+ * Install to specified directory
116
+ */
117
+ function install(isGlobal) {
118
+ const src = path.join(__dirname, "..");
119
+ const claudeDir = isGlobal
120
+ ? path.join(os.homedir(), ".claude")
121
+ : path.join(process.cwd(), ".claude");
122
+
123
+ const locationLabel = isGlobal ? "~/.claude" : "./.claude";
124
+
125
+ console.log(` Installing to ${cyan}${locationLabel}${reset}\n`);
126
+
127
+ // Create directories
128
+ fs.mkdirSync(claudeDir, { recursive: true });
129
+
130
+ // Copy commands
131
+ const commandsSrc = path.join(src, "commands");
132
+ if (fs.existsSync(commandsSrc)) {
133
+ const commandsDest = path.join(claudeDir, "commands");
134
+ fs.mkdirSync(commandsDest, { recursive: true });
135
+
136
+ // Copy each command as a directory (flux.md -> commands/flux/COMMAND.md)
137
+ const commandFiles = fs.readdirSync(commandsSrc);
138
+ for (const file of commandFiles) {
139
+ if (file.endsWith(".md")) {
140
+ const name = file.replace(".md", "");
141
+ const destDir = path.join(commandsDest, name);
142
+ fs.mkdirSync(destDir, { recursive: true });
143
+ fs.copyFileSync(
144
+ path.join(commandsSrc, file),
145
+ path.join(destDir, "COMMAND.md")
146
+ );
147
+ console.log(` ${green}✓${reset} Installed command: /${name}`);
148
+ }
149
+ }
150
+ }
151
+
152
+ // Copy skills
153
+ const skillsSrc = path.join(src, "skills");
154
+ if (fs.existsSync(skillsSrc)) {
155
+ const skillsDest = path.join(claudeDir, "skills");
156
+ fs.mkdirSync(skillsDest, { recursive: true });
157
+
158
+ // Copy each skill directory
159
+ const skillDirs = fs.readdirSync(skillsSrc, { withFileTypes: true });
160
+ for (const dir of skillDirs) {
161
+ if (dir.isDirectory()) {
162
+ copyDir(
163
+ path.join(skillsSrc, dir.name),
164
+ path.join(skillsDest, dir.name)
165
+ );
166
+ console.log(` ${green}✓${reset} Installed skill: ${dir.name}`);
167
+ }
168
+ }
169
+ }
170
+
171
+ // Configure MCP server
172
+ const claudeJsonPath = isGlobal
173
+ ? path.join(os.homedir(), ".claude.json")
174
+ : path.join(process.cwd(), ".claude.json");
175
+
176
+ const claudeJson = readJson(claudeJsonPath);
177
+
178
+ if (!claudeJson.mcpServers) {
179
+ claudeJson.mcpServers = {};
180
+ }
181
+
182
+ // Add or update flux MCP server
183
+ claudeJson.mcpServers.flux = {
184
+ command: "npx",
185
+ args: ["-y", "@cliangdev/flux-plugin", "serve"],
186
+ };
187
+
188
+ writeJson(claudeJsonPath, claudeJson);
189
+ console.log(
190
+ ` ${green}✓${reset} Configured MCP server in ${isGlobal ? "~/.claude.json" : "./.claude.json"}`
191
+ );
192
+
193
+ // Write version file for update checking
194
+ const versionFile = path.join(claudeDir, "flux-version");
195
+ fs.writeFileSync(versionFile, pkg.version);
196
+
197
+ console.log(`
198
+ ${green}Done!${reset} Restart Claude Code and run ${cyan}/flux${reset} to get started.
199
+
200
+ ${dim}Commands available:${reset}
201
+ /flux - Project status and guidance
202
+ /flux:prd - Create or refine PRDs
203
+ /flux:breakdown - Break PRDs into epics and tasks
204
+ /flux:implement - Implement tasks with TDD
205
+
206
+ ${dim}Learn more:${reset} https://github.com/cliangdev/flux-plugin
207
+ `);
208
+ }
209
+
210
+ /**
211
+ * Prompt for install location
212
+ */
213
+ function promptLocation() {
214
+ const rl = readline.createInterface({
215
+ input: process.stdin,
216
+ output: process.stdout,
217
+ });
218
+
219
+ console.log(` ${yellow}Where would you like to install?${reset}
220
+
221
+ ${cyan}1${reset}) Global ${dim}(~/.claude)${reset} - available in all projects
222
+ ${cyan}2${reset}) Local ${dim}(./.claude)${reset} - this project only
223
+ `);
224
+
225
+ rl.question(` Choice ${dim}[1]${reset}: `, (answer) => {
226
+ rl.close();
227
+ const choice = answer.trim() || "1";
228
+ install(choice !== "2");
229
+ });
230
+ }
231
+
232
+ // Main
233
+ if (hasGlobal && hasLocal) {
234
+ console.error(` ${yellow}Cannot specify both --global and --local${reset}`);
235
+ process.exit(1);
236
+ } else if (hasGlobal) {
237
+ install(true);
238
+ } else if (hasLocal) {
239
+ install(false);
240
+ } else {
241
+ promptLocation();
242
+ }
243
+ }
@@ -22481,7 +22481,6 @@ var config2 = {
22481
22481
  };
22482
22482
 
22483
22483
  // src/server/db/index.ts
22484
- import { Database } from "bun:sqlite";
22485
22484
  import { existsSync as existsSync2, mkdirSync } from "node:fs";
22486
22485
 
22487
22486
  // src/server/utils/logger.ts
@@ -22584,6 +22583,43 @@ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
22584
22583
  CREATE INDEX IF NOT EXISTS idx_criteria_parent ON acceptance_criteria(parent_type, parent_id);
22585
22584
  `;
22586
22585
 
22586
+ // src/server/db/sqlite.ts
22587
+ var isBun = typeof process !== "undefined" && !!process.versions?.bun;
22588
+ var DatabaseImpl;
22589
+ if (isBun) {
22590
+ const { Database: BunDatabase } = await import("bun:sqlite");
22591
+ DatabaseImpl = BunDatabase;
22592
+ } else {
22593
+ const BetterSqlite3 = (await import("better-sqlite3")).default;
22594
+
22595
+ class BetterSqlite3Wrapper {
22596
+ db;
22597
+ constructor(path) {
22598
+ this.db = new BetterSqlite3(path);
22599
+ }
22600
+ query(sql) {
22601
+ const stmt = this.db.prepare(sql);
22602
+ return {
22603
+ get: (...params) => stmt.get(...params),
22604
+ all: (...params) => stmt.all(...params),
22605
+ run: (...params) => {
22606
+ stmt.run(...params);
22607
+ }
22608
+ };
22609
+ }
22610
+ exec(sql) {
22611
+ this.db.exec(sql);
22612
+ }
22613
+ run(sql) {
22614
+ this.db.exec(sql);
22615
+ }
22616
+ close() {
22617
+ this.db.close();
22618
+ }
22619
+ }
22620
+ DatabaseImpl = BetterSqlite3Wrapper;
22621
+ }
22622
+
22587
22623
  // src/server/db/ids.ts
22588
22624
  function generateId(prefix = "") {
22589
22625
  const timestamp = Date.now().toString(36);
@@ -22686,7 +22722,7 @@ function initDb() {
22686
22722
  mkdirSync(config2.fluxPath, { recursive: true });
22687
22723
  }
22688
22724
  logger.info(`Opening database: ${config2.dbPath}`);
22689
- db = new Database(config2.dbPath);
22725
+ db = new DatabaseImpl(config2.dbPath);
22690
22726
  db.run("PRAGMA foreign_keys = ON");
22691
22727
  db.exec(SCHEMA);
22692
22728
  logger.info("Database initialized successfully");
@@ -86184,14 +86220,7 @@ var getStatsTool = {
86184
86220
  handler: handler8
86185
86221
  };
86186
86222
  // src/version.ts
86187
- import { readFileSync as readFileSync6 } from "node:fs";
86188
- import { dirname as dirname2, join as join2 } from "node:path";
86189
- import { fileURLToPath } from "node:url";
86190
- var __filename2 = fileURLToPath(import.meta.url);
86191
- var __dirname2 = dirname2(__filename2);
86192
- var packageJsonPath = join2(__dirname2, "..", "package.json");
86193
- var packageJson = JSON.parse(readFileSync6(packageJsonPath, "utf-8"));
86194
- var VERSION = packageJson.version;
86223
+ var VERSION = "0.1.0";
86195
86224
 
86196
86225
  // src/server/tools/get-version.ts
86197
86226
  var inputSchema10 = exports_external.object({});
@@ -86208,15 +86237,15 @@ var getVersionTool = {
86208
86237
  handler: handler9
86209
86238
  };
86210
86239
  // src/server/tools/init-project.ts
86211
- import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
86212
- import { dirname as dirname3, join as join3 } from "node:path";
86240
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
86241
+ import { dirname as dirname2, join as join2 } from "node:path";
86213
86242
  function getStatusLineBinaryPath() {
86214
- const thisDir = dirname3(new URL(import.meta.url).pathname);
86215
- return join3(thisDir, "..", "..", "..", "bin", "flux-status");
86243
+ const thisDir = dirname2(new URL(import.meta.url).pathname);
86244
+ return join2(thisDir, "..", "..", "..", "bin", "flux-status");
86216
86245
  }
86217
86246
  function setupClaudeSettings(projectRoot) {
86218
- const claudeDir = join3(projectRoot, ".claude");
86219
- const settingsPath = join3(claudeDir, "settings.local.json");
86247
+ const claudeDir = join2(projectRoot, ".claude");
86248
+ const settingsPath = join2(claudeDir, "settings.local.json");
86220
86249
  const statusBinaryPath = getStatusLineBinaryPath();
86221
86250
  if (!existsSync8(statusBinaryPath)) {
86222
86251
  return;
@@ -86227,7 +86256,7 @@ function setupClaudeSettings(projectRoot) {
86227
86256
  let settings = {};
86228
86257
  if (existsSync8(settingsPath)) {
86229
86258
  try {
86230
- settings = JSON.parse(readFileSync7(settingsPath, "utf-8"));
86259
+ settings = JSON.parse(readFileSync6(settingsPath, "utf-8"));
86231
86260
  } catch {
86232
86261
  settings = {};
86233
86262
  }
@@ -86393,7 +86422,7 @@ var queryEntitiesTool = {
86393
86422
  handler: handler11
86394
86423
  };
86395
86424
  // src/server/tools/render-status.ts
86396
- import { existsSync as existsSync9, readFileSync as readFileSync8 } from "node:fs";
86425
+ import { existsSync as existsSync9, readFileSync as readFileSync7 } from "node:fs";
86397
86426
 
86398
86427
  // src/utils/display.ts
86399
86428
  function renderProgressBar(percentage, width) {
@@ -86506,7 +86535,7 @@ function getProjectInfo() {
86506
86535
  return null;
86507
86536
  }
86508
86537
  try {
86509
- const content = readFileSync8(config2.projectJsonPath, "utf-8");
86538
+ const content = readFileSync7(config2.projectJsonPath, "utf-8");
86510
86539
  const project = JSON.parse(content);
86511
86540
  return {
86512
86541
  name: project.name || "Unnamed Project",
package/package.json CHANGED
@@ -1,20 +1,21 @@
1
1
  {
2
2
  "name": "@cliangdev/flux-plugin",
3
- "version": "0.0.0-dev.8e9707e",
3
+ "version": "0.0.0-dev.cf5e864",
4
4
  "description": "Claude Code plugin for AI-first workflow orchestration with MCP server",
5
5
  "type": "module",
6
6
  "main": "./dist/server/index.js",
7
7
  "bin": {
8
- "flux-plugin": "./dist/server/index.js"
8
+ "flux-plugin": "./bin/install.cjs"
9
9
  },
10
10
  "files": [
11
+ "bin/",
11
12
  "dist/",
12
13
  "skills/",
13
14
  "commands/"
14
15
  ],
15
16
  "scripts": {
16
17
  "dev": "bun run src/server/index.ts",
17
- "build": "bun build src/server/index.ts --outdir dist/server --target node",
18
+ "build": "bun build src/server/index.ts --outdir dist/server --target node --external better-sqlite3",
18
19
  "postbuild": "node -e \"const fs=require('fs');const f='dist/server/index.js';const c=fs.readFileSync(f,'utf-8');if(!c.startsWith('#!/usr/bin/env node')){fs.writeFileSync(f,'#!/usr/bin/env node\\n'+c)}\"",
19
20
  "build:compile": "bun build --compile --outfile bin/flux-server src/server/index.ts && bun build --compile --outfile bin/flux-status src/status-line/index.ts",
20
21
  "build:compile:server": "bun build --compile --outfile bin/flux-server src/server/index.ts",
@@ -47,12 +48,14 @@
47
48
  "license": "MIT",
48
49
  "devDependencies": {
49
50
  "@biomejs/biome": "^2.3.11",
51
+ "@types/better-sqlite3": "^7.6.13",
50
52
  "@types/bun": "^1.3.6",
51
53
  "typescript": "^5.0.0"
52
54
  },
53
55
  "dependencies": {
54
56
  "@linear/sdk": "^70.0.0",
55
57
  "@modelcontextprotocol/sdk": "^1.25.2",
58
+ "better-sqlite3": "^12.6.2",
56
59
  "chalk": "^5.4.1",
57
60
  "zod": "^4.3.5"
58
61
  },