@cliangdev/flux-plugin 0.2.0 → 0.3.0

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.
Files changed (108) hide show
  1. package/README.md +11 -7
  2. package/agents/coder.md +150 -25
  3. package/bin/install.cjs +171 -16
  4. package/commands/breakdown.md +47 -10
  5. package/commands/dashboard.md +29 -0
  6. package/commands/flux.md +92 -12
  7. package/commands/implement.md +166 -17
  8. package/commands/linear.md +6 -5
  9. package/commands/prd.md +996 -82
  10. package/manifest.json +2 -1
  11. package/package.json +9 -11
  12. package/skills/flux-orchestrator/SKILL.md +11 -3
  13. package/skills/prd-writer/SKILL.md +761 -0
  14. package/skills/ux-ui-design/SKILL.md +346 -0
  15. package/skills/ux-ui-design/references/design-tokens.md +359 -0
  16. package/src/__tests__/version.test.ts +37 -0
  17. package/src/adapters/local/.gitkeep +0 -0
  18. package/src/dashboard/__tests__/api.test.ts +211 -0
  19. package/src/dashboard/browser.ts +35 -0
  20. package/src/dashboard/public/app.js +869 -0
  21. package/src/dashboard/public/index.html +90 -0
  22. package/src/dashboard/public/styles.css +807 -0
  23. package/src/dashboard/public/vendor/highlight.css +10 -0
  24. package/src/dashboard/public/vendor/highlight.min.js +8422 -0
  25. package/src/dashboard/public/vendor/marked.min.js +2210 -0
  26. package/src/dashboard/server.ts +296 -0
  27. package/src/dashboard/watchers.ts +83 -0
  28. package/src/server/__tests__/config.test.ts +163 -0
  29. package/src/server/adapters/__tests__/a-client-linear.test.ts +197 -0
  30. package/src/server/adapters/__tests__/adapter-factory.test.ts +230 -0
  31. package/src/server/adapters/__tests__/dependency-ops.test.ts +429 -0
  32. package/src/server/adapters/__tests__/document-ops.test.ts +306 -0
  33. package/src/server/adapters/__tests__/linear-adapter.test.ts +91 -0
  34. package/src/server/adapters/__tests__/linear-config.test.ts +425 -0
  35. package/src/server/adapters/__tests__/linear-criteria-parser.test.ts +287 -0
  36. package/src/server/adapters/__tests__/linear-description-test.ts +238 -0
  37. package/src/server/adapters/__tests__/linear-epic-crud.test.ts +496 -0
  38. package/src/server/adapters/__tests__/linear-mappers-description.test.ts +276 -0
  39. package/src/server/adapters/__tests__/linear-mappers-epic.test.ts +294 -0
  40. package/src/server/adapters/__tests__/linear-mappers-prd.test.ts +300 -0
  41. package/src/server/adapters/__tests__/linear-mappers-task.test.ts +197 -0
  42. package/src/server/adapters/__tests__/linear-prd-crud.test.ts +620 -0
  43. package/src/server/adapters/__tests__/linear-stats.test.ts +450 -0
  44. package/src/server/adapters/__tests__/linear-task-crud.test.ts +534 -0
  45. package/src/server/adapters/__tests__/linear-types.test.ts +243 -0
  46. package/src/server/adapters/__tests__/status-ops.test.ts +441 -0
  47. package/src/server/adapters/factory.ts +90 -0
  48. package/src/server/adapters/index.ts +9 -0
  49. package/src/server/adapters/linear/adapter.ts +1141 -0
  50. package/src/server/adapters/linear/client.ts +169 -0
  51. package/src/server/adapters/linear/config.ts +152 -0
  52. package/src/server/adapters/linear/helpers/criteria-parser.ts +197 -0
  53. package/src/server/adapters/linear/helpers/index.ts +7 -0
  54. package/src/server/adapters/linear/index.ts +16 -0
  55. package/src/server/adapters/linear/mappers/description.ts +136 -0
  56. package/src/server/adapters/linear/mappers/epic.ts +81 -0
  57. package/src/server/adapters/linear/mappers/index.ts +27 -0
  58. package/src/server/adapters/linear/mappers/prd.ts +178 -0
  59. package/src/server/adapters/linear/mappers/task.ts +82 -0
  60. package/src/server/adapters/linear/types.ts +264 -0
  61. package/src/server/adapters/local-adapter.ts +1009 -0
  62. package/src/server/adapters/types.ts +293 -0
  63. package/src/server/config.ts +73 -0
  64. package/src/server/db/__tests__/queries.test.ts +473 -0
  65. package/src/server/db/ids.ts +17 -0
  66. package/src/server/db/index.ts +69 -0
  67. package/src/server/db/queries.ts +142 -0
  68. package/src/server/db/refs.ts +60 -0
  69. package/src/server/db/schema.ts +97 -0
  70. package/src/server/db/sqlite.ts +10 -0
  71. package/src/server/index.ts +81 -0
  72. package/src/server/tools/__tests__/crud.test.ts +411 -0
  73. package/src/server/tools/__tests__/get-version.test.ts +27 -0
  74. package/src/server/tools/__tests__/mcp-interface.test.ts +479 -0
  75. package/src/server/tools/__tests__/query.test.ts +405 -0
  76. package/src/server/tools/__tests__/z-configure-linear.test.ts +511 -0
  77. package/src/server/tools/__tests__/z-get-linear-url.test.ts +108 -0
  78. package/src/server/tools/configure-linear.ts +373 -0
  79. package/src/server/tools/create-epic.ts +44 -0
  80. package/src/server/tools/create-prd.ts +40 -0
  81. package/src/server/tools/create-task.ts +47 -0
  82. package/src/server/tools/criteria.ts +50 -0
  83. package/src/server/tools/delete-entity.ts +76 -0
  84. package/src/server/tools/dependencies.ts +55 -0
  85. package/src/server/tools/get-entity.ts +240 -0
  86. package/src/server/tools/get-linear-url.ts +28 -0
  87. package/src/server/tools/get-stats.ts +52 -0
  88. package/src/server/tools/get-version.ts +20 -0
  89. package/src/server/tools/index.ts +158 -0
  90. package/src/server/tools/init-project.ts +108 -0
  91. package/src/server/tools/query-entities.ts +167 -0
  92. package/src/server/tools/render-status.ts +219 -0
  93. package/src/server/tools/update-entity.ts +140 -0
  94. package/src/server/tools/update-status.ts +166 -0
  95. package/src/server/utils/__tests__/mcp-response.test.ts +331 -0
  96. package/src/server/utils/logger.ts +9 -0
  97. package/src/server/utils/mcp-response.ts +254 -0
  98. package/src/server/utils/status-transitions.ts +160 -0
  99. package/src/status-line/__tests__/status-line.test.ts +215 -0
  100. package/src/status-line/index.ts +147 -0
  101. package/src/utils/__tests__/chalk-import.test.ts +32 -0
  102. package/src/utils/__tests__/display.test.ts +97 -0
  103. package/src/utils/__tests__/status-renderer.test.ts +310 -0
  104. package/src/utils/display.ts +62 -0
  105. package/src/utils/status-renderer.ts +214 -0
  106. package/src/version.ts +5 -0
  107. package/dist/server/index.js +0 -87063
  108. package/skills/prd-template/SKILL.md +0 -242
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Epic Mapper
3
+ *
4
+ * Transforms Linear Issues to Flux Epics.
5
+ * Epic issues are identified by the 'epic' label in Linear.
6
+ */
7
+
8
+ import type { Epic, EpicStatus } from "../../types.js";
9
+ import type { LinearIssue } from "../types.js";
10
+ import { LINEAR_EPIC_STATUS_MAP } from "../types.js";
11
+
12
+ /**
13
+ * Transform Linear Issue to Flux Epic.
14
+ * Epic issues have the 'epic' label.
15
+ *
16
+ * @param issue Linear issue with epic label
17
+ * @param prdId Flux PRD ID to associate epic with
18
+ * @returns Flux Epic
19
+ */
20
+ export function toEpic(issue: LinearIssue, prdId: string): Epic {
21
+ return {
22
+ id: issue.id,
23
+ prdId,
24
+ ref: issue.identifier,
25
+ title: issue.title,
26
+ description: issue.description,
27
+ status: toFluxEpicStatus(issue.state.name),
28
+ createdAt: issue.createdAt,
29
+ updatedAt: issue.updatedAt,
30
+ };
31
+ }
32
+
33
+ /**
34
+ * Check if a Linear issue is an epic (has epic label).
35
+ *
36
+ * @param issue Linear issue
37
+ * @param epicLabelName Name of the epic label (default: "epic")
38
+ * @returns true if issue has epic label
39
+ */
40
+ export function isEpicIssue(
41
+ issue: LinearIssue,
42
+ epicLabelName: string,
43
+ ): boolean {
44
+ if (!issue.labels || issue.labels.length === 0) {
45
+ return false;
46
+ }
47
+
48
+ return issue.labels.some((label) => label.name === epicLabelName);
49
+ }
50
+
51
+ /**
52
+ * Get Linear workflow state from Flux Epic status.
53
+ *
54
+ * @param status Flux Epic status
55
+ * @returns Linear workflow state name
56
+ */
57
+ export function toLinearEpicState(status: EpicStatus): string {
58
+ return LINEAR_EPIC_STATUS_MAP[status];
59
+ }
60
+
61
+ /**
62
+ * Get Flux Epic status from Linear workflow state name.
63
+ * Case-sensitive matching - Linear uses "Backlog", "Todo", "In Progress", "Done".
64
+ *
65
+ * @param stateName Linear workflow state name
66
+ * @returns Flux Epic status
67
+ */
68
+ export function toFluxEpicStatus(stateName: string): EpicStatus {
69
+ switch (stateName) {
70
+ case "Backlog":
71
+ case "Todo":
72
+ return "PENDING";
73
+ case "In Progress":
74
+ return "IN_PROGRESS";
75
+ case "Done":
76
+ return "COMPLETED";
77
+ default:
78
+ // Default to PENDING for unknown states
79
+ return "PENDING";
80
+ }
81
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Linear Mappers
3
+ *
4
+ * Export all mapper functions for transforming between Linear and Flux entities.
5
+ */
6
+
7
+ export {
8
+ type DescriptionInput,
9
+ formatDescription,
10
+ type ParsedDescription,
11
+ parseDescription,
12
+ updateAcCompletion,
13
+ } from "./description.js";
14
+ export {
15
+ isEpicIssue,
16
+ toEpic,
17
+ toFluxEpicStatus,
18
+ toLinearEpicState,
19
+ } from "./epic.js";
20
+ export { toFluxPrdStatus, toLinearProjectState, toPrd } from "./prd.js";
21
+ export {
22
+ toFluxPriority,
23
+ toFluxTaskStatus,
24
+ toLinearPriority,
25
+ toLinearTaskState,
26
+ toTask,
27
+ } from "./task.js";
@@ -0,0 +1,178 @@
1
+ /**
2
+ * PRD Mapper
3
+ *
4
+ * Transforms between Linear Issues and Flux Prd entities.
5
+ * PRDs are represented as Linear Issues with a 'prd' label within a configured project.
6
+ */
7
+
8
+ import type { Prd, PrdStatus } from "../../types.js";
9
+ import type { LinearIssue, LinearProject } from "../types.js";
10
+ import {
11
+ FLUX_STATUS_LABELS,
12
+ LINEAR_PRD_ISSUE_STATUS_MAP,
13
+ LINEAR_PRD_STATUS_MAP,
14
+ PRD_STATUSES_WITH_LABELS,
15
+ } from "../types.js";
16
+
17
+ // =============================================================================
18
+ // Issue-based PRD mapping (new approach)
19
+ // =============================================================================
20
+
21
+ /**
22
+ * Transform Linear Issue to Flux Prd.
23
+ * PRD issues have the 'prd' label and are in the configured project.
24
+ *
25
+ * @param issue - Linear Issue with prd label
26
+ * @param projectId - The Linear Project ID (container for all entities)
27
+ * @returns Flux Prd entity
28
+ */
29
+ export function toPrdFromIssue(issue: LinearIssue, projectId: string): Prd {
30
+ return {
31
+ id: issue.id,
32
+ projectId,
33
+ ref: issue.identifier,
34
+ title: issue.title,
35
+ description: issue.description,
36
+ status: toFluxPrdStatusFromIssue(issue.state.name),
37
+ createdAt: issue.createdAt,
38
+ updatedAt: issue.updatedAt,
39
+ };
40
+ }
41
+
42
+ /**
43
+ * Check if a Linear issue is a PRD (has prd label).
44
+ *
45
+ * @param issue - Linear issue
46
+ * @param prdLabelName - Name of the prd label (default: "prd")
47
+ * @returns true if issue has prd label
48
+ */
49
+ export function isPrdIssue(issue: LinearIssue, prdLabelName: string): boolean {
50
+ if (!issue.labels || issue.labels.length === 0) {
51
+ return false;
52
+ }
53
+
54
+ return issue.labels.some((label) => label.name === prdLabelName);
55
+ }
56
+
57
+ /**
58
+ * Get Flux PRD status from Linear workflow state name and labels.
59
+ * Uses status labels to disambiguate states that share the same Linear workflow state.
60
+ *
61
+ * Linear "Todo" → PENDING_REVIEW or REVIEWED (check for flux:reviewed label)
62
+ * Linear "In Progress" → APPROVED or BREAKDOWN_READY (check for flux:breakdown-ready label)
63
+ *
64
+ * @param stateName - Linear workflow state name
65
+ * @param labels - Array of label names on the issue (optional)
66
+ * @returns Flux PRD status
67
+ */
68
+ export function toFluxPrdStatusFromIssue(
69
+ stateName: string,
70
+ labels: string[] = [],
71
+ ): PrdStatus {
72
+ const hasReviewedLabel = labels.includes(FLUX_STATUS_LABELS.REVIEWED);
73
+ const hasBreakdownReadyLabel = labels.includes(
74
+ FLUX_STATUS_LABELS.BREAKDOWN_READY,
75
+ );
76
+
77
+ switch (stateName) {
78
+ case "Backlog":
79
+ return "DRAFT";
80
+ case "Todo":
81
+ return hasReviewedLabel ? "REVIEWED" : "PENDING_REVIEW";
82
+ case "In Progress":
83
+ return hasBreakdownReadyLabel ? "BREAKDOWN_READY" : "APPROVED";
84
+ case "Done":
85
+ return "COMPLETED";
86
+ case "Canceled":
87
+ return "ARCHIVED";
88
+ default:
89
+ return "DRAFT";
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Get the status label that should be applied for a given Flux PRD status.
95
+ * Returns null if no label is needed for disambiguation.
96
+ *
97
+ * @param status - Flux PRD status
98
+ * @returns Label name or null
99
+ */
100
+ export function getStatusLabelForPrdStatus(status: PrdStatus): string | null {
101
+ return PRD_STATUSES_WITH_LABELS[status];
102
+ }
103
+
104
+ /**
105
+ * Get all status labels that should be removed when changing status.
106
+ * This ensures we don't have stale status labels after a transition.
107
+ *
108
+ * @returns Array of all status label names
109
+ */
110
+ export function getAllStatusLabels(): string[] {
111
+ return Object.values(FLUX_STATUS_LABELS);
112
+ }
113
+
114
+ /**
115
+ * Get Linear workflow state name from Flux PRD status.
116
+ * Used for issue-based PRDs.
117
+ *
118
+ * @param status - Flux PRD status
119
+ * @returns Linear workflow state name
120
+ */
121
+ export function toLinearPrdIssueState(status: PrdStatus): string {
122
+ return LINEAR_PRD_ISSUE_STATUS_MAP[status];
123
+ }
124
+
125
+ // =============================================================================
126
+ // Project-based PRD mapping (deprecated - kept for backward compatibility)
127
+ // =============================================================================
128
+
129
+ /**
130
+ * Transform Linear Project to Flux Prd.
131
+ *
132
+ * @deprecated Use toPrdFromIssue for new issue-based PRD mapping
133
+ * @param project - Linear Project object
134
+ * @param projectId - Project ID (used as both id and projectId in Flux)
135
+ * @returns Flux Prd entity
136
+ */
137
+ export function toPrd(project: LinearProject, projectId: string): Prd {
138
+ return {
139
+ id: projectId,
140
+ projectId: projectId,
141
+ ref: project.id,
142
+ title: project.name,
143
+ description: project.description,
144
+ status: toFluxPrdStatus(project.state),
145
+ createdAt: project.createdAt,
146
+ updatedAt: project.updatedAt,
147
+ };
148
+ }
149
+
150
+ /**
151
+ * Get Linear project state from Flux PRD status.
152
+ *
153
+ * @deprecated Use toLinearPrdIssueState for new issue-based PRD mapping
154
+ * @param status - Flux PRD status
155
+ * @returns Linear project state (backlog, planned, started, completed, canceled)
156
+ */
157
+ export function toLinearProjectState(status: PrdStatus): string {
158
+ return LINEAR_PRD_STATUS_MAP[status];
159
+ }
160
+
161
+ /**
162
+ * Get Flux PRD status from Linear project state.
163
+ *
164
+ * @deprecated Use toFluxPrdStatusFromIssue for new issue-based PRD mapping
165
+ * @param state - Linear project state
166
+ * @returns Flux PRD status
167
+ */
168
+ export function toFluxPrdStatus(state: string): PrdStatus {
169
+ const stateMap: Record<string, PrdStatus> = {
170
+ backlog: "DRAFT",
171
+ planned: "PENDING_REVIEW",
172
+ started: "APPROVED",
173
+ completed: "COMPLETED",
174
+ canceled: "ARCHIVED",
175
+ };
176
+
177
+ return stateMap[state] || "DRAFT";
178
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Linear Task Mapper
3
+ *
4
+ * Transforms Linear Issues to/from Flux Tasks.
5
+ */
6
+
7
+ import type { Priority, Task, TaskStatus } from "../../types.js";
8
+ import type { LinearIssue } from "../types.js";
9
+ import {
10
+ FLUX_PRIORITY_MAP,
11
+ LINEAR_PRIORITY_MAP,
12
+ LINEAR_TASK_STATUS_MAP,
13
+ } from "../types.js";
14
+
15
+ /**
16
+ * Transform Linear Issue to Flux Task.
17
+ *
18
+ * @param issue - Linear Issue object
19
+ * @param epicId - Parent Epic ID in Flux
20
+ * @returns Flux Task object
21
+ */
22
+ export function toTask(issue: LinearIssue, epicId: string): Task {
23
+ return {
24
+ id: issue.id,
25
+ epicId,
26
+ ref: issue.identifier,
27
+ title: issue.title,
28
+ description: issue.description,
29
+ status: toFluxTaskStatus(issue.state.name),
30
+ priority: toFluxPriority(issue.priority),
31
+ createdAt: issue.createdAt,
32
+ updatedAt: issue.updatedAt,
33
+ };
34
+ }
35
+
36
+ /**
37
+ * Get Linear priority number from Flux priority.
38
+ *
39
+ * @param priority - Flux Priority (LOW, MEDIUM, HIGH)
40
+ * @returns Linear priority number (1-4)
41
+ */
42
+ export function toLinearPriority(priority: Priority): number {
43
+ return LINEAR_PRIORITY_MAP[priority];
44
+ }
45
+
46
+ /**
47
+ * Get Flux priority from Linear priority number.
48
+ *
49
+ * @param linearPriority - Linear priority (1=urgent, 2=high, 3=medium, 4=low)
50
+ * @returns Flux Priority
51
+ */
52
+ export function toFluxPriority(linearPriority: number): Priority {
53
+ return FLUX_PRIORITY_MAP[linearPriority] ?? "LOW";
54
+ }
55
+
56
+ /**
57
+ * Get Linear workflow state from Flux Task status.
58
+ *
59
+ * @param status - Flux Task status
60
+ * @returns Linear workflow state name
61
+ */
62
+ export function toLinearTaskState(status: TaskStatus): string {
63
+ return LINEAR_TASK_STATUS_MAP[status];
64
+ }
65
+
66
+ /**
67
+ * Get Flux Task status from Linear workflow state name.
68
+ *
69
+ * @param stateName - Linear workflow state name
70
+ * @returns Flux Task status
71
+ */
72
+ export function toFluxTaskStatus(stateName: string): TaskStatus {
73
+ // Map Linear states to Flux statuses
74
+ const stateMap: Record<string, TaskStatus> = {
75
+ Backlog: "PENDING",
76
+ Todo: "PENDING",
77
+ "In Progress": "IN_PROGRESS",
78
+ Done: "COMPLETED",
79
+ };
80
+
81
+ return stateMap[stateName] ?? "PENDING";
82
+ }
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Linear Adapter Types
3
+ *
4
+ * Defines Linear-specific types for API responses and configuration.
5
+ * These types are internal to the Linear adapter and not exposed to MCP tools.
6
+ */
7
+
8
+ import type { EpicStatus, PrdStatus, Priority, TaskStatus } from "../types.js";
9
+
10
+ // =============================================================================
11
+ // Configuration
12
+ // =============================================================================
13
+
14
+ /**
15
+ * Linear adapter configuration.
16
+ * Stored in project.json under adapter.config.
17
+ */
18
+ export interface LinearConfig {
19
+ /** Linear API key (lin_api_...) */
20
+ apiKey: string;
21
+ /** Linear team ID */
22
+ teamId: string;
23
+ /** Linear Project ID - container for all PRDs, Epics, and Tasks */
24
+ projectId: string;
25
+ /** Label names used to identify entity types */
26
+ defaultLabels: {
27
+ /** Label for PRD issues */
28
+ prd: string;
29
+ /** Label for epic issues */
30
+ epic: string;
31
+ /** Label for task issues */
32
+ task: string;
33
+ };
34
+ }
35
+
36
+ // =============================================================================
37
+ // Linear API Response Types
38
+ // =============================================================================
39
+
40
+ /**
41
+ * Linear Project represents a PRD in Flux.
42
+ * ref = project.id (e.g., "proj_abc123")
43
+ */
44
+ export interface LinearProject {
45
+ /** Project ID */
46
+ id: string;
47
+ /** Project name (maps to PRD title) */
48
+ name: string;
49
+ /** Project description (maps to PRD description) */
50
+ description?: string;
51
+ /** Project state (backlog, planned, started, completed, canceled) */
52
+ state: string;
53
+ /** Creation timestamp */
54
+ createdAt: string;
55
+ /** Last update timestamp */
56
+ updatedAt: string;
57
+ }
58
+
59
+ /**
60
+ * Linear Issue represents an Epic or Task in Flux.
61
+ * - Epic: Issue with "epic" label (ref = issue.identifier like "ENG-42")
62
+ * - Task: Issue child of epic (ref = issue.identifier like "ENG-43")
63
+ */
64
+ export interface LinearIssue {
65
+ /** Issue ID */
66
+ id: string;
67
+ /** Issue identifier (e.g., "ENG-42") - used as ref */
68
+ identifier: string;
69
+ /** Issue title */
70
+ title: string;
71
+ /** Issue description */
72
+ description?: string;
73
+ /** Workflow state */
74
+ state: LinearWorkflowState;
75
+ /** Priority (1=urgent, 2=high, 3=medium, 4=low/none) */
76
+ priority: number;
77
+ /** Parent issue ID (for tasks) */
78
+ parentId?: string;
79
+ /** Project ID (for PRD association) */
80
+ projectId?: string;
81
+ /** Labels for categorization */
82
+ labels?: LinearLabel[];
83
+ /** Creation timestamp */
84
+ createdAt: string;
85
+ /** Last update timestamp */
86
+ updatedAt: string;
87
+ }
88
+
89
+ /**
90
+ * Linear Workflow State.
91
+ * Maps to Epic/Task status in Flux.
92
+ */
93
+ export interface LinearWorkflowState {
94
+ /** State ID */
95
+ id: string;
96
+ /** State name (e.g., "Backlog", "In Progress", "Done") */
97
+ name: string;
98
+ /** State type (backlog, unstarted, started, completed, canceled) */
99
+ type: string;
100
+ }
101
+
102
+ /**
103
+ * Linear Label.
104
+ * Used to identify epic vs task issues.
105
+ */
106
+ export interface LinearLabel {
107
+ /** Label ID */
108
+ id: string;
109
+ /** Label name */
110
+ name: string;
111
+ }
112
+
113
+ /**
114
+ * Linear Document (future).
115
+ * Represents attachments or linked documents.
116
+ */
117
+ export interface LinearDocument {
118
+ /** Document ID */
119
+ id: string;
120
+ /** Document title */
121
+ title: string;
122
+ /** Document URL */
123
+ url: string;
124
+ /** Creation timestamp */
125
+ createdAt: string;
126
+ }
127
+
128
+ // =============================================================================
129
+ // Status and Priority Mapping
130
+ // =============================================================================
131
+
132
+ /**
133
+ * Maps Flux PRD status to Linear project state.
134
+ * @deprecated Use LINEAR_PRD_ISSUE_STATUS_MAP for issue-based PRDs
135
+ */
136
+ export const LINEAR_PRD_STATUS_MAP: Record<PrdStatus, string> = {
137
+ DRAFT: "backlog",
138
+ PENDING_REVIEW: "planned",
139
+ REVIEWED: "planned",
140
+ APPROVED: "started",
141
+ BREAKDOWN_READY: "started",
142
+ COMPLETED: "completed",
143
+ ARCHIVED: "canceled",
144
+ };
145
+
146
+ /**
147
+ * Maps Flux PRD status to Linear workflow state name.
148
+ * Used for issue-based PRDs (new mapping).
149
+ */
150
+ export const LINEAR_PRD_ISSUE_STATUS_MAP: Record<PrdStatus, string> = {
151
+ DRAFT: "Backlog",
152
+ PENDING_REVIEW: "Todo",
153
+ REVIEWED: "Todo",
154
+ APPROVED: "In Progress",
155
+ BREAKDOWN_READY: "In Progress",
156
+ COMPLETED: "Done",
157
+ ARCHIVED: "Canceled",
158
+ };
159
+
160
+ /**
161
+ * Maps Flux Epic status to Linear workflow state name.
162
+ */
163
+ export const LINEAR_EPIC_STATUS_MAP: Record<EpicStatus, string> = {
164
+ PENDING: "Todo",
165
+ IN_PROGRESS: "In Progress",
166
+ COMPLETED: "Done",
167
+ };
168
+
169
+ /**
170
+ * Maps Flux Task status to Linear workflow state name.
171
+ */
172
+ export const LINEAR_TASK_STATUS_MAP: Record<TaskStatus, string> = {
173
+ PENDING: "Todo",
174
+ IN_PROGRESS: "In Progress",
175
+ COMPLETED: "Done",
176
+ };
177
+
178
+ /**
179
+ * Maps Flux Priority to Linear priority number.
180
+ * Linear: 1=urgent, 2=high, 3=medium, 4=low/none
181
+ */
182
+ export const LINEAR_PRIORITY_MAP: Record<Priority, number> = {
183
+ LOW: 4,
184
+ MEDIUM: 3,
185
+ HIGH: 2,
186
+ };
187
+
188
+ /**
189
+ * Reverse map: Linear priority to Flux Priority.
190
+ */
191
+ export const FLUX_PRIORITY_MAP: Record<number, Priority> = {
192
+ 1: "HIGH", // urgent -> HIGH
193
+ 2: "HIGH",
194
+ 3: "MEDIUM",
195
+ 4: "LOW",
196
+ };
197
+
198
+ // =============================================================================
199
+ // Status Labels for Disambiguation
200
+ // =============================================================================
201
+
202
+ /**
203
+ * Status labels used to disambiguate Flux statuses that share the same
204
+ * Linear workflow state.
205
+ *
206
+ * Problem: Linear has limited workflow states, but Flux has more granular statuses.
207
+ * - "Todo" maps to both PENDING_REVIEW and REVIEWED
208
+ * - "In Progress" maps to both APPROVED and BREAKDOWN_READY
209
+ *
210
+ * Solution: Use labels to track the exact Flux status.
211
+ */
212
+ export const FLUX_STATUS_LABELS = {
213
+ /** Applied when PRD is in REVIEWED state (disambiguates from PENDING_REVIEW) */
214
+ REVIEWED: "flux:reviewed",
215
+ /** Applied when PRD is in BREAKDOWN_READY state (disambiguates from APPROVED) */
216
+ BREAKDOWN_READY: "flux:breakdown-ready",
217
+ } as const;
218
+
219
+ /**
220
+ * PRD statuses that require a status label for disambiguation.
221
+ */
222
+ export const PRD_STATUSES_WITH_LABELS: Record<PrdStatus, string | null> = {
223
+ DRAFT: null,
224
+ PENDING_REVIEW: null,
225
+ REVIEWED: FLUX_STATUS_LABELS.REVIEWED,
226
+ APPROVED: null,
227
+ BREAKDOWN_READY: FLUX_STATUS_LABELS.BREAKDOWN_READY,
228
+ COMPLETED: null,
229
+ ARCHIVED: null,
230
+ };
231
+
232
+ // =============================================================================
233
+ // Milestone/Tag Labels
234
+ // =============================================================================
235
+
236
+ /**
237
+ * Prefix for milestone labels that map to PRD tags.
238
+ * Format: flux:milestone:<tag>
239
+ */
240
+ export const FLUX_MILESTONE_LABEL_PREFIX = "flux:milestone:";
241
+
242
+ /**
243
+ * Get the milestone label name for a given tag.
244
+ * @param tag - The tag value (e.g., "mvp-phase-1")
245
+ * @returns Full label name (e.g., "flux:milestone:mvp-phase-1")
246
+ */
247
+ export function getMilestoneLabel(tag: string): string {
248
+ return `${FLUX_MILESTONE_LABEL_PREFIX}${tag}`;
249
+ }
250
+
251
+ /**
252
+ * Extract tag value from a list of labels.
253
+ * Finds the first label starting with the milestone prefix and extracts the tag.
254
+ * @param labels - Array of label names
255
+ * @returns The tag value or undefined if no milestone label found
256
+ */
257
+ export function extractTagFromLabels(labels: string[]): string | undefined {
258
+ for (const label of labels) {
259
+ if (label.startsWith(FLUX_MILESTONE_LABEL_PREFIX)) {
260
+ return label.substring(FLUX_MILESTONE_LABEL_PREFIX.length);
261
+ }
262
+ }
263
+ return undefined;
264
+ }