@cliangdev/flux-plugin 0.0.0-dev.991d9a8 → 0.0.0-dev.aedd289

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/bin/install.cjs CHANGED
@@ -5,30 +5,23 @@ const path = require("path");
5
5
  const os = require("os");
6
6
  const readline = require("readline");
7
7
 
8
- // Parse args first to check for serve command
9
8
  const args = process.argv.slice(2);
10
9
 
11
- // Check for "serve" subcommand - starts MCP server
12
10
  if (args[0] === "serve") {
13
- // Dynamic import to load ESM server
14
11
  import("../dist/server/index.js").catch((err) => {
15
12
  console.error("Failed to start Flux MCP server:", err.message);
16
13
  process.exit(1);
17
14
  });
18
15
  } else {
19
- // Run installer
20
16
  runInstaller();
21
17
  }
22
18
 
23
19
  function runInstaller() {
24
- // Colors
25
20
  const cyan = "\x1b[36m";
26
21
  const green = "\x1b[32m";
27
22
  const yellow = "\x1b[33m";
28
23
  const dim = "\x1b[2m";
29
24
  const reset = "\x1b[0m";
30
-
31
- // Get version from package.json
32
25
  const pkg = require("../package.json");
33
26
 
34
27
  const banner = `
@@ -49,7 +42,6 @@ ${cyan} ███████╗██╗ ██╗ ██╗██╗
49
42
 
50
43
  console.log(banner);
51
44
 
52
- // Show help
53
45
  if (hasHelp) {
54
46
  console.log(` ${yellow}Usage:${reset} npx @cliangdev/flux-plugin [options]
55
47
 
@@ -71,9 +63,6 @@ ${cyan} ███████╗██╗ ██╗ ██╗██╗
71
63
  process.exit(0);
72
64
  }
73
65
 
74
- /**
75
- * Recursively copy directory
76
- */
77
66
  function copyDir(src, dest) {
78
67
  fs.mkdirSync(dest, { recursive: true });
79
68
  const entries = fs.readdirSync(src, { withFileTypes: true });
@@ -90,9 +79,6 @@ ${cyan} ███████╗██╗ ██╗ ██╗██╗
90
79
  }
91
80
  }
92
81
 
93
- /**
94
- * Read JSON file, return empty object if doesn't exist
95
- */
96
82
  function readJson(filePath) {
97
83
  if (fs.existsSync(filePath)) {
98
84
  try {
@@ -104,58 +90,53 @@ ${cyan} ███████╗██╗ ██╗ ██╗██╗
104
90
  return {};
105
91
  }
106
92
 
107
- /**
108
- * Write JSON file with formatting
109
- */
110
93
  function writeJson(filePath, data) {
111
94
  fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
112
95
  }
113
96
 
114
- /**
115
- * Install to specified directory
116
- */
117
97
  function install(isGlobal) {
118
98
  const src = path.join(__dirname, "..");
119
99
  const claudeDir = isGlobal
120
100
  ? path.join(os.homedir(), ".claude")
121
101
  : path.join(process.cwd(), ".claude");
122
-
123
102
  const locationLabel = isGlobal ? "~/.claude" : "./.claude";
124
103
 
125
104
  console.log(` Installing to ${cyan}${locationLabel}${reset}\n`);
126
105
 
127
- // Create directories
128
106
  fs.mkdirSync(claudeDir, { recursive: true });
129
107
 
130
- // Copy commands
131
108
  const commandsSrc = path.join(src, "commands");
132
109
  if (fs.existsSync(commandsSrc)) {
133
110
  const commandsDest = path.join(claudeDir, "commands");
134
- fs.mkdirSync(commandsDest, { recursive: true });
111
+ const fluxSubDir = path.join(commandsDest, "flux");
112
+ fs.mkdirSync(fluxSubDir, { recursive: true });
135
113
 
136
- // Copy each command as a directory (flux.md -> commands/flux/COMMAND.md)
137
114
  const commandFiles = fs.readdirSync(commandsSrc);
138
115
  for (const file of commandFiles) {
139
116
  if (file.endsWith(".md")) {
140
117
  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}`);
118
+ if (name === "flux") {
119
+ fs.copyFileSync(
120
+ path.join(commandsSrc, file),
121
+ path.join(commandsDest, file)
122
+ );
123
+ console.log(` ${green}✓${reset} Installed command: /flux`);
124
+ } else {
125
+ fs.copyFileSync(
126
+ path.join(commandsSrc, file),
127
+ path.join(fluxSubDir, file)
128
+ );
129
+ console.log(` ${green}✓${reset} Installed command: /flux:${name}`);
130
+ }
148
131
  }
149
132
  }
150
133
  }
151
134
 
152
- // Copy skills
153
135
  const skillsSrc = path.join(src, "skills");
154
136
  if (fs.existsSync(skillsSrc)) {
155
137
  const skillsDest = path.join(claudeDir, "skills");
156
138
  fs.mkdirSync(skillsDest, { recursive: true });
157
139
 
158
- // Copy each skill directory
159
140
  const skillDirs = fs.readdirSync(skillsSrc, { withFileTypes: true });
160
141
  for (const dir of skillDirs) {
161
142
  if (dir.isDirectory()) {
@@ -168,29 +149,44 @@ ${cyan} ███████╗██╗ ██╗ ██╗██╗
168
149
  }
169
150
  }
170
151
 
171
- // Configure MCP server
172
- const claudeJsonPath = isGlobal
152
+ const agentsSrc = path.join(src, "agents");
153
+ if (fs.existsSync(agentsSrc)) {
154
+ const agentsDest = path.join(claudeDir, "agents");
155
+ fs.mkdirSync(agentsDest, { recursive: true });
156
+
157
+ const agentFiles = fs.readdirSync(agentsSrc);
158
+ for (const file of agentFiles) {
159
+ if (file.endsWith(".md")) {
160
+ fs.copyFileSync(
161
+ path.join(agentsSrc, file),
162
+ path.join(agentsDest, file)
163
+ );
164
+ const name = file.replace(".md", "");
165
+ console.log(` ${green}✓${reset} Installed agent: ${name}`);
166
+ }
167
+ }
168
+ }
169
+
170
+ const mcpConfigPath = isGlobal
173
171
  ? path.join(os.homedir(), ".claude.json")
174
- : path.join(process.cwd(), ".claude.json");
172
+ : path.join(process.cwd(), ".mcp.json");
175
173
 
176
- const claudeJson = readJson(claudeJsonPath);
174
+ const mcpConfig = readJson(mcpConfigPath);
177
175
 
178
- if (!claudeJson.mcpServers) {
179
- claudeJson.mcpServers = {};
176
+ if (!mcpConfig.mcpServers) {
177
+ mcpConfig.mcpServers = {};
180
178
  }
181
179
 
182
- // Add or update flux MCP server
183
- claudeJson.mcpServers.flux = {
180
+ mcpConfig.mcpServers.flux = {
184
181
  command: "npx",
185
- args: ["-y", "@cliangdev/flux-plugin", "serve"],
182
+ args: ["-y", `@cliangdev/flux-plugin@${pkg.version}`, "serve"],
186
183
  };
187
184
 
188
- writeJson(claudeJsonPath, claudeJson);
185
+ writeJson(mcpConfigPath, mcpConfig);
189
186
  console.log(
190
- ` ${green}✓${reset} Configured MCP server in ${isGlobal ? "~/.claude.json" : "./.claude.json"}`
187
+ ` ${green}✓${reset} Configured MCP server in ${isGlobal ? "~/.claude.json" : "./.mcp.json"}`
191
188
  );
192
189
 
193
- // Write version file for update checking
194
190
  const versionFile = path.join(claudeDir, "flux-version");
195
191
  fs.writeFileSync(versionFile, pkg.version);
196
192
 
@@ -207,9 +203,6 @@ ${cyan} ███████╗██╗ ██╗ ██╗██╗
207
203
  `);
208
204
  }
209
205
 
210
- /**
211
- * Prompt for install location
212
- */
213
206
  function promptLocation() {
214
207
  const rl = readline.createInterface({
215
208
  input: process.stdin,
@@ -229,7 +222,6 @@ ${cyan} ███████╗██╗ ██╗ ██╗██╗
229
222
  });
230
223
  }
231
224
 
232
- // Main
233
225
  if (hasGlobal && hasLocal) {
234
226
  console.error(` ${yellow}Cannot specify both --global and --local${reset}`);
235
227
  process.exit(1);
package/commands/flux.md CHANGED
@@ -1,113 +1,156 @@
1
1
  ---
2
2
  name: flux
3
3
  description: AI-first workflow orchestration for spec-driven development
4
- allowed-tools: mcp__flux__*
4
+ allowed-tools: mcp__plugin_flux_flux__*, AskUserQuestion, Read, Write
5
5
  ---
6
6
 
7
- # Flux Orchestrator
7
+ # Flux Command
8
+
9
+ You are the Flux orchestrator. Detect project state and guide the user to the appropriate next action.
10
+
11
+ ## Subcommands
8
12
 
9
- You are the Flux orchestrator. Your job is to detect the project state and guide the user through the appropriate workflow.
13
+ - `/flux version` - Show plugin version (call `get_version`)
14
+ - `/flux linear` - Connect to Linear (delegate to `/flux:linear`)
10
15
 
11
- ## Step 0: Check for Version Subcommand
16
+ ## Main Flow
17
+
18
+ ### Step 1: Get Project Context
12
19
 
13
- First, check if the user requested version information:
14
- - `/flux version` - Show version information
20
+ Call `get_project_context` to check project state.
15
21
 
16
- If the argument is "version":
22
+ ### Step 2: Route Based on State
17
23
 
18
- 1. Call the `get_version` MCP tool
19
- 2. Display the version information in a friendly format:
20
- ```
21
- Flux Plugin v{version}
22
- Package: @cliangdev/{name}
23
- ```
24
- 3. Exit (do not proceed to project state check)
24
+ **If `initialized: false`:**
25
+ Guide through initialization (see Initialization Flow below)
25
26
 
26
- ## Step 1: Check Project State
27
+ **If `initialized: true`:**
28
+ → Call `render_status` with `{view: "summary"}` to show current state
29
+ → Determine next action based on workflow state (see Workflow States)
27
30
 
28
- If no subcommand was provided, call the `get_project_context` MCP tool to check if this is a Flux project.
31
+ ## Initialization Flow
29
32
 
30
- ## Step 2: Handle Result
33
+ Use the `AskUserQuestion` tool for all questions during initialization.
31
34
 
32
- ### If `initialized: false` (New Project)
35
+ ### Step 1: Confirm Initialization
33
36
 
34
- This is a new project. Guide the user through initialization:
37
+ Use AskUserQuestion:
38
+ ```json
39
+ {
40
+ "questions": [{
41
+ "question": "No Flux project found. Would you like to initialize one?",
42
+ "header": "Initialize",
43
+ "options": [
44
+ {"label": "Yes", "description": "Create a new Flux project in this directory"},
45
+ {"label": "No", "description": "Cancel initialization"}
46
+ ],
47
+ "multiSelect": false
48
+ }]
49
+ }
50
+ ```
35
51
 
36
- 1. Ask: "No Flux project found in this directory. Would you like to initialize one?"
37
- - If no, end with: "Run `/flux` when you're ready to set up Flux."
52
+ If "No", exit with: "Run `/flux` when you're ready to set up Flux."
38
53
 
39
- 2. Ask: "What is the name of this project?"
40
- - Wait for response
54
+ ### Step 2: Collect Project Details
41
55
 
42
- 3. Ask: "Brief vision - what does this project do? (one sentence)"
43
- - Wait for response
56
+ Use AskUserQuestion with text input (user will select "Other" to type):
57
+ - Ask for project name
58
+ - Ask for project vision (brief description)
44
59
 
45
- 4. Call `init_project` tool with the name and vision
60
+ ### Step 3: Select Storage Backend
46
61
 
47
- 5. Display success message:
48
- ```
49
- Flux project initialized!
62
+ Use AskUserQuestion:
63
+ ```json
64
+ {
65
+ "questions": [{
66
+ "question": "Where should Flux store data?",
67
+ "header": "Storage",
68
+ "options": [
69
+ {"label": "Local (Recommended)", "description": "SQLite database in .flux/ - offline-first, no setup required"},
70
+ {"label": "Linear", "description": "Sync with Linear for team collaboration and issue tracking"}
71
+ ],
72
+ "multiSelect": false
73
+ }]
74
+ }
75
+ ```
50
76
 
51
- Project: {name}
52
- Vision: {vision}
53
- Reference prefix: {ref_prefix}
77
+ ### Step 4: Ask About Tool Permissions
54
78
 
55
- Created:
56
- - .flux/project.json
57
- - .flux/flux.db
79
+ Use AskUserQuestion:
80
+ ```json
81
+ {
82
+ "questions": [{
83
+ "question": "Add Flux tools to allow list? This prevents permission prompts for Flux operations.",
84
+ "header": "Permissions",
85
+ "options": [
86
+ {"label": "Yes (Recommended)", "description": "Allow all Flux MCP tools without prompting"},
87
+ {"label": "No", "description": "Ask for permission each time"}
88
+ ],
89
+ "multiSelect": false
90
+ }]
91
+ }
92
+ ```
58
93
 
59
- Next: Run /flux:prd to start planning your first PRD.
60
- ```
94
+ If "Yes", update the settings file:
61
95
 
62
- ### If Project Exists (Initialized)
96
+ 1. Read `.claude/settings.local.json` (create if doesn't exist)
97
+ 2. Parse JSON (or start with `{"permissions": {"allow": []}}` if empty/missing)
98
+ 3. Add `"mcp__plugin_flux_flux__*"` to `permissions.allow` array if not already present
99
+ 4. Write back to `.claude/settings.local.json`
63
100
 
64
- This is an existing Flux project. Show status and suggest next action:
101
+ Example result:
102
+ ```json
103
+ {
104
+ "permissions": {
105
+ "allow": ["mcp__plugin_flux_flux__*"]
106
+ }
107
+ }
108
+ ```
65
109
 
66
- 1. Call `get_stats` to get entity counts
67
-
68
- 2. Display status summary:
69
- ```
70
- Project: {name}
71
- Vision: {vision}
72
-
73
- Status:
74
- - PRDs: {total} ({draft} draft, {pending_review} in review, {approved} approved)
75
- - Epics: {total} ({pending} pending, {in_progress} active, {completed} done)
76
- - Tasks: {total} ({pending} pending, {in_progress} active, {completed} done)
77
- ```
78
-
79
- 3. Determine and suggest next action based on state:
80
-
81
- **If no PRDs exist:**
82
- > "No PRDs yet. Run `/flux:prd` to create your first product requirements document."
83
-
84
- **If PRDs exist with DRAFT status:**
85
- > "You have draft PRDs. Review them and run `/flux:prd refine` or submit for review."
86
-
87
- **If PRDs in PENDING_REVIEW:**
88
- > "PRDs pending review. The critique agent will analyze feasibility and risks."
89
-
90
- **If PRDs in REVIEWED status:**
91
- > "Critique complete. Review feedback, then approve or revise the PRD."
92
-
93
- **If PRDs APPROVED but no epics:**
94
- > "PRDs approved! Run `/flux:breakdown` to break them into epics and tasks."
95
-
96
- **If PRDs BREAKDOWN_READY with epics/tasks:**
97
- > "Ready to implement! Run `/flux:implement` to start working on tasks."
98
-
99
- **If tasks exist with PENDING status:**
100
- > "Tasks ready. Run `/flux:implement` to start working."
101
-
102
- **If tasks IN_PROGRESS:**
103
- > "Implementation in progress. Run `/flux:implement` to continue."
104
-
105
- **If all tasks COMPLETED:**
106
- > "All tasks complete! Review your work and create a PR."
110
+ Confirm to user: "Flux tools added to allow list. No more permission prompts for Flux operations."
111
+
112
+ ### Step 5: Initialize Project
113
+
114
+ Call `init_project` with collected values:
115
+ - `name`: project name
116
+ - `vision`: project vision
117
+ - `adapter`: "local" or "linear"
118
+
119
+ ### Step 6: Next Steps
120
+
121
+ Display success message, then:
122
+
123
+ - **If Local**: "Project initialized! Run `/flux:prd` to create your first PRD."
124
+ - **If Linear**: "Project initialized! Now run `/flux:linear` to connect to Linear."
125
+
126
+ ## Workflow States
127
+
128
+ Detect current state and suggest the appropriate next action:
129
+
130
+ | State | Detection | Next Action |
131
+ |-------|-----------|-------------|
132
+ | No PRDs | `prds.total == 0` | `/flux:prd` to create first PRD |
133
+ | Draft PRDs | PRDs in DRAFT | Review and refine or submit for review |
134
+ | Pending Review | PRDs in PENDING_REVIEW | Critique agent will analyze |
135
+ | Reviewed | PRDs in REVIEWED | Address feedback, approve or revise |
136
+ | Approved | PRDs in APPROVED, no epics | `/flux:breakdown` to create epics |
137
+ | Breakdown Ready | PRDs in BREAKDOWN_READY | `/flux:implement` to start coding |
138
+ | In Progress | Tasks IN_PROGRESS | Continue with `/flux:implement` |
139
+ | Complete | All tasks COMPLETED | Create PR |
140
+
141
+ ## Confidence-Based Autonomy
142
+
143
+ When determining actions:
144
+
145
+ | Confidence | Behavior |
146
+ |------------|----------|
147
+ | > 80% | Auto-execute, inform user |
148
+ | 50-80% | Suggest action, wait for confirmation |
149
+ | < 50% | Ask clarifying question |
107
150
 
108
151
  ## Guidelines
109
152
 
110
- - Be concise - users want quick status, not essays
111
- - One question at a time during initialization
112
- - Show actionable next steps
113
- - Use the MCP tools, don't read filesystem directly
153
+ - Use `AskUserQuestion` tool for all user choices during initialization
154
+ - Be concise - show status and one clear next action
155
+ - Use `render_status` for visual project overview
156
+ - Apply confidence-based autonomy for decisions
@@ -0,0 +1,171 @@
1
+ ---
2
+ name: flux:linear
3
+ description: Connect Flux project to Linear for issue tracking
4
+ allowed-tools: mcp__plugin_flux_flux__*, AskUserQuestion
5
+ ---
6
+
7
+ # Linear Integration Setup
8
+
9
+ Connect a Flux project to Linear using the interactive configuration flow.
10
+
11
+ ## How Interactive Mode Works
12
+
13
+ The `configure_linear` tool supports progressive discovery:
14
+
15
+ | Call | Response |
16
+ |------|----------|
17
+ | `{interactive: true}` | Returns `{step: "select_team", teams: [...], user: {...}}` |
18
+ | `{interactive: true, teamId: "xxx"}` | Returns `{step: "select_project", projects: [...], team: {...}}` |
19
+ | `{teamId: "xxx", projectName: "..."}` | Creates new project and configures |
20
+ | `{teamId: "xxx", existingProjectId: "..."}` | Uses existing project and configures |
21
+
22
+ ## Flow
23
+
24
+ ### Step 1: Verify Project
25
+
26
+ Call `get_project_context`.
27
+
28
+ - If `initialized: false` → Tell user to run `/flux` first, then exit.
29
+ - If `adapter.type === "linear"` and config exists → Already configured, show info and exit.
30
+ - Otherwise → Continue to Step 2.
31
+
32
+ ### Step 2: Fetch Teams
33
+
34
+ **IMPORTANT**: Call `configure_linear` with ONLY `interactive: true`. Do NOT pass teamId, projectName, or any other params.
35
+
36
+ ```json
37
+ {"interactive": true}
38
+ ```
39
+
40
+ This is the ONLY parameter needed for the first call. The tool will return available teams.
41
+
42
+ **If error** (e.g., "Linear API key not found"):
43
+ ```
44
+ Linear API key required.
45
+
46
+ 1. Get your key: Linear → Settings → API → Personal API keys
47
+ 2. Set it: export LINEAR_API_KEY=lin_api_xxx
48
+ 3. Run /flux:linear again
49
+ ```
50
+ Then exit.
51
+
52
+ **If success**, response will be:
53
+ ```json
54
+ {
55
+ "step": "select_team",
56
+ "user": {"name": "...", "email": "..."},
57
+ "teams": [
58
+ {"id": "team-abc", "name": "Engineering", "key": "ENG"},
59
+ {"id": "team-def", "name": "Product", "key": "PROD"}
60
+ ]
61
+ }
62
+ ```
63
+
64
+ Display: "Connected as {user.name} ({user.email})"
65
+
66
+ Use AskUserQuestion to let user select a team:
67
+ ```json
68
+ {
69
+ "questions": [{
70
+ "question": "Which Linear team should Flux use?",
71
+ "header": "Team",
72
+ "options": [
73
+ {"label": "Engineering (ENG)", "description": "team-abc"},
74
+ {"label": "Product (PROD)", "description": "team-def"}
75
+ ],
76
+ "multiSelect": false
77
+ }]
78
+ }
79
+ ```
80
+
81
+ Note: Put the team ID in the description field so you can retrieve it from the response.
82
+
83
+ ### Step 3: Fetch Projects
84
+
85
+ Call `configure_linear` with:
86
+ ```json
87
+ {"interactive": true, "teamId": "<selected_team_id>"}
88
+ ```
89
+
90
+ Response will be:
91
+ ```json
92
+ {
93
+ "step": "select_project",
94
+ "team": {"id": "...", "name": "...", "key": "..."},
95
+ "projects": [
96
+ {"id": "proj-123", "name": "Q1 Sprint", "state": "started"},
97
+ {"id": "proj-456", "name": "Backlog", "state": "planned"}
98
+ ]
99
+ }
100
+ ```
101
+
102
+ Use AskUserQuestion:
103
+ ```json
104
+ {
105
+ "questions": [{
106
+ "question": "Which project should Flux sync to?",
107
+ "header": "Project",
108
+ "options": [
109
+ {"label": "Create New Project", "description": "new"},
110
+ {"label": "Q1 Sprint", "description": "proj-123"},
111
+ {"label": "Backlog", "description": "proj-456"}
112
+ ],
113
+ "multiSelect": false
114
+ }]
115
+ }
116
+ ```
117
+
118
+ ### Step 4: Configure
119
+
120
+ **If user selected "Create New Project":**
121
+
122
+ Ask for project name, then call:
123
+ ```json
124
+ {
125
+ "teamId": "<team_id>",
126
+ "projectName": "<user_provided_name>"
127
+ }
128
+ ```
129
+
130
+ **If user selected existing project:**
131
+
132
+ Call:
133
+ ```json
134
+ {
135
+ "teamId": "<team_id>",
136
+ "existingProjectId": "<project_id>"
137
+ }
138
+ ```
139
+
140
+ ### Step 5: Success
141
+
142
+ Response will include:
143
+ ```json
144
+ {
145
+ "success": true,
146
+ "team": "Engineering",
147
+ "project": {"id": "...", "name": "..."},
148
+ "labels": {...},
149
+ "view": {"created": "...", "setup_hint": "..."}
150
+ }
151
+ ```
152
+
153
+ Display:
154
+ ```
155
+ Linear connected!
156
+
157
+ Team: {team}
158
+ Project: {project.name}
159
+
160
+ All PRDs, epics, and tasks will sync to Linear.
161
+ Run /flux:prd to create your first PRD.
162
+ ```
163
+
164
+ If `view.setup_hint` exists, show it as a tip.
165
+
166
+ ## Key Points
167
+
168
+ - Always use `{"interactive": true}` (boolean) not a string
169
+ - The response `step` field tells you what stage you're at
170
+ - Use AskUserQuestion for team/project selection
171
+ - Store the selected IDs from previous responses to use in next calls