@cliangdev/flux-plugin 0.0.0-dev.b40f7c2 → 0.0.0-dev.cbdf207

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.
@@ -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
package/commands/prd.md CHANGED
@@ -1,4 +1,5 @@
1
1
  ---
2
+ name: flux:prd
2
3
  description: Create or refine PRDs through guided interview
3
4
  allowed-tools: mcp__flux__*, AskUserQuestion
4
5
  ---
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env node
2
1
  import { createRequire } from "node:module";
3
2
  var __create = Object.create;
4
3
  var __getProtoOf = Object.getPrototypeOf;
@@ -22587,7 +22586,8 @@ CREATE INDEX IF NOT EXISTS idx_criteria_parent ON acceptance_criteria(parent_typ
22587
22586
  var isBun = typeof process !== "undefined" && !!process.versions?.bun;
22588
22587
  var DatabaseImpl;
22589
22588
  if (isBun) {
22590
- const { Database: BunDatabase } = await import("bun:sqlite");
22589
+ const bunSqlite = "bun:" + "sqlite";
22590
+ const { Database: BunDatabase } = await import(bunSqlite);
22591
22591
  DatabaseImpl = BunDatabase;
22592
22592
  } else {
22593
22593
  const BetterSqlite3 = (await import("better-sqlite3")).default;
@@ -83910,6 +83910,18 @@ var PRD_STATUSES_WITH_LABELS = {
83910
83910
  COMPLETED: null,
83911
83911
  ARCHIVED: null
83912
83912
  };
83913
+ var FLUX_MILESTONE_LABEL_PREFIX = "flux:milestone:";
83914
+ function getMilestoneLabel(tag) {
83915
+ return `${FLUX_MILESTONE_LABEL_PREFIX}${tag}`;
83916
+ }
83917
+ function extractTagFromLabels(labels) {
83918
+ for (const label of labels) {
83919
+ if (label.startsWith(FLUX_MILESTONE_LABEL_PREFIX)) {
83920
+ return label.substring(FLUX_MILESTONE_LABEL_PREFIX.length);
83921
+ }
83922
+ }
83923
+ return;
83924
+ }
83913
83925
 
83914
83926
  // src/server/adapters/linear/mappers/epic.ts
83915
83927
  function toLinearEpicState(status) {
@@ -84061,6 +84073,7 @@ class LinearAdapter {
84061
84073
  title: issue2.title,
84062
84074
  description: issue2.description,
84063
84075
  status: toFluxPrdStatusFromIssue(issue2.stateName, issue2.labels),
84076
+ tag: extractTagFromLabels(issue2.labels),
84064
84077
  createdAt: issue2.createdAt.toISOString(),
84065
84078
  updatedAt: issue2.updatedAt.toISOString()
84066
84079
  };
@@ -84100,13 +84113,17 @@ class LinearAdapter {
84100
84113
  return issue2.labels.includes(this.config.defaultLabels.task);
84101
84114
  }
84102
84115
  async createPrd(input) {
84103
- const prdLabelId = await this.getLabelId(this.config.defaultLabels.prd);
84116
+ const labelIds = [await this.getLabelId(this.config.defaultLabels.prd)];
84117
+ if (input.tag) {
84118
+ const tagLabelId = await this.getOrCreateLabel(getMilestoneLabel(input.tag));
84119
+ labelIds.push(tagLabelId);
84120
+ }
84104
84121
  const createResult = await this.client.execute(() => this.client.client.createIssue({
84105
84122
  title: input.title,
84106
84123
  description: input.description,
84107
84124
  teamId: this.config.teamId,
84108
84125
  projectId: this.config.projectId,
84109
- labelIds: [prdLabelId]
84126
+ labelIds
84110
84127
  }));
84111
84128
  const rawIssue = await this.client.execute(() => createResult.issue);
84112
84129
  if (!rawIssue) {
@@ -84128,7 +84145,10 @@ class LinearAdapter {
84128
84145
  updatePayload.description = input.description;
84129
84146
  if (input.status !== undefined) {
84130
84147
  updatePayload.stateId = await this.getStateId(toLinearPrdIssueState(input.status));
84131
- updatePayload.labelIds = await this.buildPrdLabelIds(issue2.labels, input.status);
84148
+ }
84149
+ if (input.status !== undefined || input.tag !== undefined) {
84150
+ const currentStatus = toFluxPrdStatusFromIssue(issue2.stateName, issue2.labels);
84151
+ updatePayload.labelIds = await this.buildPrdLabelIdsWithTag(issue2.labels, input.status ?? currentStatus, input.tag !== undefined ? input.tag : extractTagFromLabels(issue2.labels));
84132
84152
  }
84133
84153
  await this.client.execute(() => issue2._raw.update(updatePayload));
84134
84154
  const updated = await this.fetchIssue(ref);
@@ -84150,6 +84170,23 @@ class LinearAdapter {
84150
84170
  }
84151
84171
  return labelIds;
84152
84172
  }
84173
+ async buildPrdLabelIdsWithTag(currentLabels, newStatus, newTag) {
84174
+ const statusLabels = getAllStatusLabels();
84175
+ const newStatusLabel = getStatusLabelForPrdStatus(newStatus);
84176
+ const labelsToKeep = currentLabels.filter((l) => !statusLabels.includes(l) && !l.startsWith(FLUX_MILESTONE_LABEL_PREFIX));
84177
+ if (newStatusLabel) {
84178
+ labelsToKeep.push(newStatusLabel);
84179
+ }
84180
+ if (newTag) {
84181
+ labelsToKeep.push(getMilestoneLabel(newTag));
84182
+ }
84183
+ const labelIds = [];
84184
+ for (const labelName of labelsToKeep) {
84185
+ const id = await this.getOrCreateLabel(labelName);
84186
+ labelIds.push(id);
84187
+ }
84188
+ return labelIds;
84189
+ }
84153
84190
  async getOrCreateLabel(labelName) {
84154
84191
  try {
84155
84192
  return await this.getLabelId(labelName);
@@ -84175,9 +84212,18 @@ class LinearAdapter {
84175
84212
  const limit = pagination?.limit ?? 50;
84176
84213
  const offset = pagination?.offset ?? 0;
84177
84214
  const linearFilter = {
84178
- labels: { name: { eq: this.config.defaultLabels.prd } },
84179
84215
  project: { id: { eq: this.config.projectId } }
84180
84216
  };
84217
+ if (filters?.tag) {
84218
+ linearFilter.labels = {
84219
+ and: [
84220
+ { name: { eq: this.config.defaultLabels.prd } },
84221
+ { name: { eq: getMilestoneLabel(filters.tag) } }
84222
+ ]
84223
+ };
84224
+ } else {
84225
+ linearFilter.labels = { name: { eq: this.config.defaultLabels.prd } };
84226
+ }
84181
84227
  if (filters?.status) {
84182
84228
  linearFilter.state = {
84183
84229
  name: { eq: toLinearPrdIssueState(filters.status) }
@@ -85467,14 +85513,20 @@ function clearAdapterCache() {
85467
85513
  var LINEAR_API_KEY_ENV = "LINEAR_API_KEY";
85468
85514
  var inputSchema = exports_external.object({
85469
85515
  apiKey: exports_external.string().optional(),
85470
- teamId: exports_external.string().min(1, "Team ID is required"),
85516
+ teamId: exports_external.string().optional(),
85471
85517
  projectName: exports_external.string().optional(),
85472
85518
  existingProjectId: exports_external.string().optional(),
85473
85519
  prdLabel: exports_external.string().optional().default("prd"),
85474
85520
  epicLabel: exports_external.string().optional().default("epic"),
85475
- taskLabel: exports_external.string().optional().default("task")
85476
- }).refine((data) => data.projectName || data.existingProjectId, {
85477
- message: "Either projectName or existingProjectId must be provided"
85521
+ taskLabel: exports_external.string().optional().default("task"),
85522
+ interactive: exports_external.boolean().optional().default(false)
85523
+ }).refine((data) => {
85524
+ if (data.interactive) {
85525
+ return true;
85526
+ }
85527
+ return data.teamId && data.teamId.length > 0 && (data.projectName || data.existingProjectId);
85528
+ }, {
85529
+ message: "teamId and either projectName or existingProjectId are required (unless interactive mode)"
85478
85530
  });
85479
85531
  function resolveApiKey(inputApiKey) {
85480
85532
  if (inputApiKey) {
@@ -85552,19 +85604,72 @@ async function createDefaultView(client, teamId, projectId, projectName, labels)
85552
85604
  };
85553
85605
  }
85554
85606
  }
85607
+ async function listTeams(client) {
85608
+ const teamsResult = await client.teams();
85609
+ return teamsResult.nodes.map((team) => ({
85610
+ id: team.id,
85611
+ name: team.name,
85612
+ key: team.key
85613
+ }));
85614
+ }
85615
+ async function listProjects(client, teamId) {
85616
+ const projectsResult = await client.projects({
85617
+ filter: {
85618
+ accessibleTeams: { some: { id: { eq: teamId } } },
85619
+ state: { nin: ["canceled"] }
85620
+ }
85621
+ });
85622
+ return projectsResult.nodes.map((project) => ({
85623
+ id: project.id,
85624
+ name: project.name,
85625
+ description: project.description ?? null,
85626
+ state: project.state,
85627
+ updatedAt: project.updatedAt.toISOString()
85628
+ }));
85629
+ }
85555
85630
  async function handler(input) {
85556
85631
  const parsed = inputSchema.parse(input);
85557
85632
  const { apiKey, source: apiKeySource } = resolveApiKey(parsed.apiKey);
85558
85633
  const client = new LinearClient({ apiKey });
85634
+ let viewerName;
85635
+ let viewerEmail;
85559
85636
  try {
85560
85637
  const viewer = await client.viewer;
85561
85638
  if (!viewer) {
85562
85639
  throw new Error("Invalid API key");
85563
85640
  }
85641
+ viewerName = viewer.name;
85642
+ viewerEmail = viewer.email;
85564
85643
  } catch (error48) {
85565
85644
  const message = error48 instanceof Error ? error48.message : String(error48);
85566
85645
  throw new Error(`Invalid Linear API key (source: ${apiKeySource}): ${message}`);
85567
85646
  }
85647
+ if (parsed.interactive && !parsed.teamId) {
85648
+ const teams = await listTeams(client);
85649
+ return {
85650
+ step: "select_team",
85651
+ user: { name: viewerName, email: viewerEmail },
85652
+ api_key_source: apiKeySource,
85653
+ teams
85654
+ };
85655
+ }
85656
+ if (parsed.interactive && parsed.teamId && !parsed.projectName && !parsed.existingProjectId) {
85657
+ const team2 = await client.team(parsed.teamId);
85658
+ if (!team2) {
85659
+ throw new Error(`Team ${parsed.teamId} not found`);
85660
+ }
85661
+ const projects = await listProjects(client, parsed.teamId);
85662
+ return {
85663
+ step: "select_project",
85664
+ user: { name: viewerName, email: viewerEmail },
85665
+ api_key_source: apiKeySource,
85666
+ team: { id: team2.id, name: team2.name, key: team2.key },
85667
+ projects
85668
+ };
85669
+ }
85670
+ if (!parsed.teamId) {
85671
+ throw new Error("teamId is required");
85672
+ }
85568
85673
  const team = await client.team(parsed.teamId);
85569
85674
  if (!team) {
85570
85675
  throw new Error(`Team ${parsed.teamId} not found`);
@@ -85641,7 +85746,7 @@ async function handler(input) {
85641
85746
  }
85642
85747
  var configureLinearTool = {
85643
85748
  name: "configure_linear",
85644
- description: "Configure Linear integration for the Flux project. " + "API Key Resolution (priority order): 1) LINEAR_API_KEY environment variable (recommended), 2) Existing .flux/linear-config.json, 3) apiKey parameter (not recommended - visible in logs). " + "Required: teamId (Linear team ID), and either projectName (to create new) or existingProjectId (to use existing). " + "Optional: apiKey, prdLabel (default 'prd'), epicLabel (default 'epic'), taskLabel (default 'task'). " + "Creates a Linear Project, ensures labels exist, creates 'Flux' custom view for new projects, saves config to .flux/linear-config.json. " + "Returns {success, message, api_key_source, team, project, labels, view}. " + "Setup: export LINEAR_API_KEY=lin_api_xxx before running.",
85749
+ description: "Configure Linear integration for the Flux project. " + "Use interactive mode for guided setup: " + "1) Call with {interactive: true} to get list of teams. " + "2) Call with {interactive: true, teamId: '...'} to get list of projects. " + "3) Call with full params to configure. " + "API Key Resolution (priority order): 1) LINEAR_API_KEY environment variable (recommended), 2) Existing .flux/linear-config.json, 3) apiKey parameter (not recommended - visible in logs). " + "Required (non-interactive): teamId (Linear team ID), and either projectName (to create new) or existingProjectId (to use existing). " + "Optional: apiKey, prdLabel (default 'prd'), epicLabel (default 'epic'), taskLabel (default 'task'). " + "Creates a Linear Project, ensures labels exist, creates 'Flux' custom view for new projects, saves config to .flux/linear-config.json. " + "Returns {success, message, api_key_source, team, project, labels, view} on final configuration. " + "Setup: export LINEAR_API_KEY=lin_api_xxx before running.",
85645
85750
  inputSchema,
85646
85751
  handler
85647
85752
  };
package/manifest.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "$schema": "./manifest.schema.json",
3
+ "version": "1.0.0",
4
+ "structure": {
5
+ "commands": ["flux.md", "prd.md", "breakdown.md", "implement.md"],
6
+ "skills": [
7
+ "agent-creator",
8
+ "epic-template",
9
+ "flux-orchestrator",
10
+ "prd-template"
11
+ ],
12
+ "agents": ["coder.md", "critic.md", "researcher.md", "verifier.md"],
13
+ "hooks": []
14
+ }
15
+ }
package/package.json CHANGED
@@ -1,16 +1,19 @@
1
1
  {
2
2
  "name": "@cliangdev/flux-plugin",
3
- "version": "0.0.0-dev.b40f7c2",
3
+ "version": "0.0.0-dev.cbdf207",
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
- "commands/"
14
+ "commands/",
15
+ "agents/",
16
+ "manifest.json"
14
17
  ],
15
18
  "scripts": {
16
19
  "dev": "bun run src/server/index.ts",
@@ -19,14 +22,17 @@
19
22
  "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
23
  "build:compile:server": "bun build --compile --outfile bin/flux-server src/server/index.ts",
21
24
  "build:compile:status": "bun build --compile --outfile bin/flux-status src/status-line/index.ts",
22
- "prepublishOnly": "bun run build",
25
+ "validate": "node scripts/validate-structure.cjs",
26
+ "test:integration": "bun test scripts/__tests__/integration.test.ts --timeout 120000",
27
+ "prepublishOnly": "bun run validate && bun run build && bun run test:integration",
23
28
  "test": "bun test",
24
29
  "test:linear-description": "bun run src/server/adapters/__tests__/linear-description-test.ts",
25
30
  "typecheck": "tsc --noEmit",
26
31
  "lint": "biome check .",
27
32
  "lint:fix": "biome check --write .",
28
33
  "format": "biome format --write .",
29
- "verify-release": "bun run scripts/verify-release.ts"
34
+ "verify-release": "bun run scripts/verify-release.ts",
35
+ "release": "./scripts/release.sh"
30
36
  },
31
37
  "repository": {
32
38
  "type": "git",
@@ -1,5 +1,7 @@
1
1
  ---
2
+ name: flux:agent-creator
2
3
  description: Guide for creating effective subagents. Use when users want to create a new agent that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.
4
+ user-invocable: false
3
5
  ---
4
6
 
5
7
  # Agent Creator Skill
@@ -1,5 +1,7 @@
1
1
  ---
2
+ name: flux:epic-template
2
3
  description: Epic and task structure patterns for Flux. Use when breaking PRDs into epics and tasks. Epics should be self-contained with clear acceptance criteria.
4
+ user-invocable: false
3
5
  ---
4
6
 
5
7
  # Epic Template Skill
@@ -1,21 +1,25 @@
1
1
  ---
2
+ name: flux:flux-orchestrator
2
3
  description: Orchestrates Flux workflows based on project context
4
+ user-invocable: false
3
5
  ---
4
6
 
5
7
  # Flux Orchestrator Skill
6
8
 
7
- This skill is automatically active when working in a Flux project. It provides context about available tools and workflow patterns.
9
+ This skill is automatically active when working in a Flux project. It provides context about available tools, workflow patterns, and integration options.
8
10
 
9
11
  ## Available MCP Tools
10
12
 
11
13
  ### Query Tools
12
- - `get_project_context` - Check if project initialized, get name/vision/prefix
14
+ - `get_project_context` - Check if project initialized, get name/vision/adapter type
13
15
  - `get_stats` - Get PRD/epic/task counts by status
14
16
  - `get_entity` - Fetch entity by ref with optional includes (criteria, tasks, dependencies)
15
17
  - `query_entities` - Search entities by type, status, parent ref
18
+ - `render_status` - Get formatted project status with progress bars
16
19
 
17
20
  ### Mutation Tools
18
21
  - `init_project` - Initialize new .flux/ directory with project.json and database
22
+ - `configure_linear` - Configure Linear integration (supports interactive mode)
19
23
  - `create_prd` - Create a new PRD
20
24
  - `create_epic` - Create an epic linked to a PRD
21
25
  - `create_task` - Create a task linked to an epic
@@ -29,102 +33,82 @@ This skill is automatically active when working in a Flux project. It provides c
29
33
  - `add_criteria` - Add acceptance criterion to epic or task
30
34
  - `mark_criteria_met` - Mark criterion as satisfied
31
35
 
32
- ## Workflow States
36
+ ## Available Commands
33
37
 
34
- The Flux project progresses through these states:
38
+ | Command | Purpose |
39
+ |---------|---------|
40
+ | `/flux` | Project init, status, and workflow routing |
41
+ | `/flux:linear` | Connect project to Linear (interactive setup) |
42
+ | `/flux:prd` | Create or refine PRDs |
43
+ | `/flux:breakdown` | Break approved PRD into epics and tasks |
44
+ | `/flux:implement` | Implement tasks with TDD workflow |
35
45
 
36
- 1. **Uninitialized** - No .flux/ directory
37
- - Action: Run `/flux` to initialize
46
+ ## Workflow States
38
47
 
39
- 2. **No PRDs** - Project initialized but empty
40
- - Action: Run `/flux:prd` to create first PRD
48
+ ```
49
+ Uninitialized Initialized PRD Draft Pending Review → Reviewed → Approved → Breakdown Ready → In Progress → Complete
50
+
51
+ (optional) Linear Connected
52
+ ```
41
53
 
42
- 3. **PRD Draft** - PRD created but needs review
43
- - Action: Review and submit for approval or refine
54
+ ### PRD Status Transitions
55
+ ```
56
+ DRAFT → PENDING_REVIEW → REVIEWED → APPROVED → BREAKDOWN_READY → COMPLETED
57
+ ↓ ↓
58
+ DRAFT (revise) DRAFT (revise)
59
+ ```
44
60
 
45
- 4. **PRD Pending Review** - PRD submitted for review
46
- - Action: Run critique agent, then approve or revise
61
+ ### Epic/Task Statuses
62
+ - `PENDING` `IN_PROGRESS` `COMPLETED`
63
+
64
+ ## Backend Adapters
47
65
 
48
- 5. **PRD Reviewed** - Critique complete
49
- - Action: Address feedback, then approve or revise to DRAFT
66
+ Flux supports multiple backends via the adapter pattern:
50
67
 
51
- 6. **PRD Approved** - Ready for epic breakdown
52
- - Action: Run `/flux:breakdown` to create epics
68
+ | Adapter | Storage | Use Case |
69
+ |---------|---------|----------|
70
+ | `local` | SQLite (.flux/flux.db) | Default, offline-first |
71
+ | `linear` | Linear API | Team collaboration, issue tracking |
53
72
 
54
- 7. **Breakdown Ready** - Epics and tasks created
55
- - Action: Run `/flux:implement` to start coding
73
+ ### Switching to Linear
56
74
 
57
- 8. **Implementation In Progress** - Tasks IN_PROGRESS
58
- - Action: Continue implementing current task
75
+ Use `configure_linear` with interactive mode:
76
+ 1. `{interactive: true}` returns teams list
77
+ 2. `{interactive: true, teamId: "..."}` → returns projects list
78
+ 3. `{teamId: "...", projectName/existingProjectId: "..."}` → configures
59
79
 
60
- 9. **Complete** - All tasks COMPLETED
61
- - Action: Review and create PR
80
+ Or run `/flux:linear` for guided setup.
62
81
 
63
82
  ## Entity References
64
83
 
65
- All entities have a reference format: `{PREFIX}-{TYPE}{NUMBER}`
84
+ Format: `{PREFIX}-{TYPE}{NUMBER}`
66
85
 
67
86
  - PRD: `MSA-P1`, `MSA-P2`
68
87
  - Epic: `MSA-E1`, `MSA-E2`
69
88
  - Task: `MSA-T1`, `MSA-T2`
70
89
 
71
- The prefix is generated from the project name during initialization.
72
-
73
- ## Status Values
74
-
75
- ### PRD Statuses (6-stage workflow)
76
- - `DRAFT` - Initial state, being created/refined
77
- - `PENDING_REVIEW` - Submitted for critique
78
- - `REVIEWED` - Critique complete, awaiting approval
79
- - `APPROVED` - Ready for epic breakdown
80
- - `BREAKDOWN_READY` - Epics and tasks created
81
- - `COMPLETED` - All epics done
82
-
83
- ### Valid PRD Transitions
84
- ```
85
- DRAFT → PENDING_REVIEW
86
- PENDING_REVIEW → REVIEWED | DRAFT (revise)
87
- REVIEWED → APPROVED | DRAFT (revise)
88
- APPROVED → BREAKDOWN_READY
89
- BREAKDOWN_READY → COMPLETED
90
- ```
91
-
92
- ### Epic/Task Statuses
93
- - `PENDING` - Not started
94
- - `IN_PROGRESS` - Currently being worked on
95
- - `COMPLETED` - Done
96
-
97
- ## Confidence-Based Autonomy
98
-
99
- The orchestrator uses confidence levels to determine autonomy:
100
-
101
- | Confidence | Behavior | Example |
102
- |------------|----------|---------|
103
- | > 80% | Auto-execute, inform user | "I'm creating the epic structure..." |
104
- | 50-80% | Suggest action, wait for confirmation | "Ready to break down into tasks. Proceed?" |
105
- | < 50% | Ask clarifying question | "Should we research this technology first?" |
106
-
107
- ### Confidence Indicators
108
- - **High confidence (>80%)**: Clear next step, no ambiguity, user has been responsive
109
- - **Medium confidence (50-80%)**: Reasonable next step, some uncertainty
110
- - **Low confidence (<50%)**: Multiple valid paths, unclear requirements, unfamiliar tech
111
-
112
90
  ## Available Subagents
113
91
 
114
- ### Research Agent
115
- - **Trigger**: Unfamiliar technology mentioned, confidence < 70%
116
- - **Purpose**: Gather information about libraries, frameworks, APIs
92
+ ### Research Agent (`flux:flux-researcher`)
93
+ - **Trigger**: Unfamiliar technology, confidence < 70%
117
94
  - **Tools**: Context7, WebSearch, WebFetch
118
95
 
119
- ### Critique Agent
120
- - **Trigger**: PRD status becomes PENDING_REVIEW
121
- - **Purpose**: Analyze feasibility, scope, risks
122
- - **Output**: Structured critique with recommendations
96
+ ### Critique Agent (`flux:flux-critic`)
97
+ - **Trigger**: PRD submitted for review
98
+ - **Output**: Feasibility analysis, risks, recommendations
99
+
100
+ ### Coder Agent (`flux:flux-coder`)
101
+ - **Trigger**: Task implementation
102
+ - **Workflow**: TDD - write tests, implement, verify
103
+
104
+ ### Verifier Agent (`flux:flux-verifier`)
105
+ - **Trigger**: After implementation
106
+ - **Purpose**: Verify acceptance criteria coverage
123
107
 
124
108
  ## Best Practices
125
109
 
126
- 1. **Check context first** - Always call `get_project_context` before taking actions
127
- 2. **Use refs, not IDs** - Tools accept human-readable refs like `MSA-E1`
128
- 3. **Validate status transitions** - Use `update_status` which enforces valid transitions
129
- 4. **Include related data** - Use `include` parameter to fetch nested entities in one call
130
- 5. **Handle errors gracefully** - Tools return errors with codes, display user-friendly messages
110
+ 1. **Check context first** - Call `get_project_context` before actions
111
+ 2. **Use refs, not IDs** - Tools accept `MSA-E1` format
112
+ 3. **Use render_status** - For visual project overview
113
+ 4. **Validate transitions** - `update_status` enforces valid transitions
114
+ 5. **Include related data** - Use `include` param to fetch nested entities
@@ -1,5 +1,7 @@
1
1
  ---
2
+ name: flux:prd-template
2
3
  description: PRD structure and patterns for Flux. Use when creating or refining product requirement documents. PRDs should be concise for humans but detailed enough for AI agents to implement.
4
+ user-invocable: false
3
5
  ---
4
6
 
5
7
  # PRD Template Skill