@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/README.md +4 -8
- 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 +42 -50
- package/commands/flux.md +127 -84
- package/commands/linear.md +171 -0
- package/dist/server/index.js +116 -11
- package/manifest.json +15 -0
- package/package.json +9 -4
- package/skills/flux-orchestrator/SKILL.md +58 -76
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
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
172
|
-
|
|
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(), ".
|
|
172
|
+
: path.join(process.cwd(), ".mcp.json");
|
|
175
173
|
|
|
176
|
-
const
|
|
174
|
+
const mcpConfig = readJson(mcpConfigPath);
|
|
177
175
|
|
|
178
|
-
if (!
|
|
179
|
-
|
|
176
|
+
if (!mcpConfig.mcpServers) {
|
|
177
|
+
mcpConfig.mcpServers = {};
|
|
180
178
|
}
|
|
181
179
|
|
|
182
|
-
|
|
183
|
-
claudeJson.mcpServers.flux = {
|
|
180
|
+
mcpConfig.mcpServers.flux = {
|
|
184
181
|
command: "npx",
|
|
185
|
-
args: ["-y",
|
|
182
|
+
args: ["-y", `@cliangdev/flux-plugin@${pkg.version}`, "serve"],
|
|
186
183
|
};
|
|
187
184
|
|
|
188
|
-
writeJson(
|
|
185
|
+
writeJson(mcpConfigPath, mcpConfig);
|
|
189
186
|
console.log(
|
|
190
|
-
` ${green}✓${reset} Configured MCP server in ${isGlobal ? "~/.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:
|
|
4
|
+
allowed-tools: mcp__plugin_flux_flux__*, AskUserQuestion, Read, Write
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
# Flux
|
|
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
|
-
|
|
13
|
+
- `/flux version` - Show plugin version (call `get_version`)
|
|
14
|
+
- `/flux linear` - Connect to Linear (delegate to `/flux:linear`)
|
|
10
15
|
|
|
11
|
-
##
|
|
16
|
+
## Main Flow
|
|
17
|
+
|
|
18
|
+
### Step 1: Get Project Context
|
|
12
19
|
|
|
13
|
-
|
|
14
|
-
- `/flux version` - Show version information
|
|
20
|
+
Call `get_project_context` to check project state.
|
|
15
21
|
|
|
16
|
-
|
|
22
|
+
### Step 2: Route Based on State
|
|
17
23
|
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
|
|
31
|
+
## Initialization Flow
|
|
29
32
|
|
|
30
|
-
|
|
33
|
+
Use the `AskUserQuestion` tool for all questions during initialization.
|
|
31
34
|
|
|
32
|
-
###
|
|
35
|
+
### Step 1: Confirm Initialization
|
|
33
36
|
|
|
34
|
-
|
|
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
|
-
|
|
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
|
|
40
|
-
- Wait for response
|
|
54
|
+
### Step 2: Collect Project Details
|
|
41
55
|
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
60
|
+
### Step 3: Select Storage Backend
|
|
46
61
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
52
|
-
Vision: {vision}
|
|
53
|
-
Reference prefix: {ref_prefix}
|
|
77
|
+
### Step 4: Ask About Tool Permissions
|
|
54
78
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
60
|
-
```
|
|
94
|
+
If "Yes", update the settings file:
|
|
61
95
|
|
|
62
|
-
|
|
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
|
-
|
|
101
|
+
Example result:
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"permissions": {
|
|
105
|
+
"allow": ["mcp__plugin_flux_flux__*"]
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
65
109
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
-
|
|
111
|
-
-
|
|
112
|
-
-
|
|
113
|
-
-
|
|
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
|