@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,37 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+
5
+ describe("version module", () => {
6
+ test("VERSION is exported from src/version.ts", async () => {
7
+ // Dynamic import to get the VERSION constant
8
+ const versionModule = await import("../version.js");
9
+
10
+ expect(versionModule.VERSION).toBeDefined();
11
+ expect(typeof versionModule.VERSION).toBe("string");
12
+ });
13
+
14
+ test("VERSION equals package.json version", async () => {
15
+ // Read package.json version
16
+ const packageJsonPath = join(process.cwd(), "package.json");
17
+ expect(existsSync(packageJsonPath)).toBe(true);
18
+
19
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
20
+ const packageVersion = packageJson.version;
21
+
22
+ expect(packageVersion).toBeDefined();
23
+ expect(typeof packageVersion).toBe("string");
24
+
25
+ // Import VERSION and compare
26
+ const versionModule = await import("../version.js");
27
+ expect(versionModule.VERSION).toBe(packageVersion);
28
+ });
29
+
30
+ test("VERSION is a valid semver format", async () => {
31
+ const versionModule = await import("../version.js");
32
+
33
+ // Basic semver format check (e.g., "0.1.0", "1.2.3-beta.1")
34
+ const semverPattern = /^\d+\.\d+\.\d+(-[\w.]+)?$/;
35
+ expect(semverPattern.test(versionModule.VERSION)).toBe(true);
36
+ });
37
+ });
File without changes
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Dashboard API endpoint tests
3
+ */
4
+
5
+ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
6
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
7
+
8
+ const TEST_DIR = `/tmp/flux-dashboard-test-${Date.now()}-${Math.random().toString(36).slice(2)}`;
9
+ process.env.FLUX_PROJECT_ROOT = TEST_DIR;
10
+
11
+ import { clearAdapterCache } from "../../server/adapters/factory.js";
12
+ import { getAdapter } from "../../server/adapters/index.js";
13
+ import { initDb } from "../../server/db/index.js";
14
+ import { startDashboard } from "../server.js";
15
+
16
+ let dashboardUrl: string;
17
+ let stopDashboard: () => void;
18
+
19
+ beforeAll(async () => {
20
+ mkdirSync(`${TEST_DIR}/.flux/prds`, { recursive: true });
21
+
22
+ writeFileSync(
23
+ `${TEST_DIR}/.flux/project.json`,
24
+ JSON.stringify({
25
+ name: "test-project",
26
+ vision: "Test vision",
27
+ ref_prefix: "TEST",
28
+ project_root: TEST_DIR,
29
+ created_at: new Date().toISOString(),
30
+ adapter: { type: "local" },
31
+ }),
32
+ );
33
+
34
+ initDb();
35
+ clearAdapterCache();
36
+
37
+ const adapter = getAdapter();
38
+ const prd = await adapter.createPrd({
39
+ title: "Test PRD",
40
+ description: "Test description",
41
+ tag: "test-tag",
42
+ });
43
+
44
+ const epic = await adapter.createEpic({
45
+ prdRef: prd.ref,
46
+ title: "Test Epic",
47
+ description: "Epic description",
48
+ });
49
+
50
+ await adapter.createTask({
51
+ epicRef: epic.ref,
52
+ title: "Test Task",
53
+ description: "Task description",
54
+ priority: "HIGH",
55
+ });
56
+
57
+ const { port, stop } = await startDashboard();
58
+ dashboardUrl = `http://localhost:${port}`;
59
+ stopDashboard = stop;
60
+ });
61
+
62
+ afterAll(() => {
63
+ stopDashboard();
64
+ clearAdapterCache();
65
+ rmSync(TEST_DIR, { recursive: true, force: true });
66
+ });
67
+
68
+ describe("Dashboard API", () => {
69
+ describe("GET /api/tree", () => {
70
+ test("returns hierarchical tree structure", async () => {
71
+ const response = await fetch(`${dashboardUrl}/api/tree`);
72
+ expect(response.status).toBe(200);
73
+
74
+ const tree = await response.json();
75
+ expect(Array.isArray(tree)).toBe(true);
76
+ expect(tree.length).toBeGreaterThan(0);
77
+
78
+ const prd = tree[0];
79
+ expect(prd.ref).toMatch(/^TEST-P\d+$/);
80
+ expect(prd.title).toBe("Test PRD");
81
+ expect(prd.epics).toBeDefined();
82
+ expect(prd.epics.length).toBe(1);
83
+
84
+ const epic = prd.epics[0];
85
+ expect(epic.ref).toMatch(/^TEST-E\d+$/);
86
+ expect(epic.tasks).toBeDefined();
87
+ expect(epic.tasks.length).toBe(1);
88
+ });
89
+ });
90
+
91
+ describe("GET /api/prd/:ref", () => {
92
+ test("returns PRD details", async () => {
93
+ const treeResponse = await fetch(`${dashboardUrl}/api/tree`);
94
+ const tree = await treeResponse.json();
95
+ const prdRef = tree[0].ref;
96
+
97
+ const response = await fetch(`${dashboardUrl}/api/prd/${prdRef}`);
98
+ expect(response.status).toBe(200);
99
+
100
+ const prd = await response.json();
101
+ expect(prd.ref).toBe(prdRef);
102
+ expect(prd.title).toBe("Test PRD");
103
+ expect(prd.description).toBe("Test description");
104
+ });
105
+
106
+ test("returns 404 for non-existent PRD", async () => {
107
+ const response = await fetch(`${dashboardUrl}/api/prd/TEST-P999`);
108
+ expect(response.status).toBe(404);
109
+ });
110
+ });
111
+
112
+ describe("GET /api/epic/:ref", () => {
113
+ test("returns epic details with criteria", async () => {
114
+ const treeResponse = await fetch(`${dashboardUrl}/api/tree`);
115
+ const tree = await treeResponse.json();
116
+ const epicRef = tree[0].epics[0].ref;
117
+
118
+ const response = await fetch(`${dashboardUrl}/api/epic/${epicRef}`);
119
+ expect(response.status).toBe(200);
120
+
121
+ const epic = await response.json();
122
+ expect(epic.ref).toBe(epicRef);
123
+ expect(epic.title).toBe("Test Epic");
124
+ expect(epic.criteria).toBeDefined();
125
+ });
126
+
127
+ test("returns 404 for non-existent epic", async () => {
128
+ const response = await fetch(`${dashboardUrl}/api/epic/TEST-E999`);
129
+ expect(response.status).toBe(404);
130
+ });
131
+ });
132
+
133
+ describe("GET /api/task/:ref", () => {
134
+ test("returns task details with criteria and dependencies", async () => {
135
+ const treeResponse = await fetch(`${dashboardUrl}/api/tree`);
136
+ const tree = await treeResponse.json();
137
+ const taskRef = tree[0].epics[0].tasks[0].ref;
138
+
139
+ const response = await fetch(`${dashboardUrl}/api/task/${taskRef}`);
140
+ expect(response.status).toBe(200);
141
+
142
+ const task = await response.json();
143
+ expect(task.ref).toBe(taskRef);
144
+ expect(task.title).toBe("Test Task");
145
+ expect(task.priority).toBe("HIGH");
146
+ expect(task.criteria).toBeDefined();
147
+ expect(task.dependencies).toBeDefined();
148
+ });
149
+
150
+ test("returns 404 for non-existent task", async () => {
151
+ const response = await fetch(`${dashboardUrl}/api/task/TEST-T999`);
152
+ expect(response.status).toBe(404);
153
+ });
154
+ });
155
+
156
+ describe("GET /api/tags", () => {
157
+ test("returns tag list with counts", async () => {
158
+ const response = await fetch(`${dashboardUrl}/api/tags`);
159
+ expect(response.status).toBe(200);
160
+
161
+ const tags = await response.json();
162
+ expect(Array.isArray(tags)).toBe(true);
163
+
164
+ const allTag = tags.find((t: { tag: string }) => t.tag === "All");
165
+ expect(allTag).toBeDefined();
166
+ expect(allTag.count).toBeGreaterThan(0);
167
+
168
+ const testTag = tags.find((t: { tag: string }) => t.tag === "test-tag");
169
+ expect(testTag).toBeDefined();
170
+ expect(testTag.count).toBe(1);
171
+ });
172
+ });
173
+
174
+ describe("GET /api/dependencies", () => {
175
+ test("returns dependency edges", async () => {
176
+ const response = await fetch(`${dashboardUrl}/api/dependencies`);
177
+ expect(response.status).toBe(200);
178
+
179
+ const data = await response.json();
180
+ expect(data.edges).toBeDefined();
181
+ expect(Array.isArray(data.edges)).toBe(true);
182
+ });
183
+ });
184
+
185
+ describe("Static files", () => {
186
+ test("serves index.html at root", async () => {
187
+ const response = await fetch(`${dashboardUrl}/`);
188
+ expect(response.status).toBe(200);
189
+ expect(response.headers.get("content-type")).toBe("text/html");
190
+ });
191
+
192
+ test("serves CSS files", async () => {
193
+ const response = await fetch(`${dashboardUrl}/styles.css`);
194
+ expect(response.status).toBe(200);
195
+ expect(response.headers.get("content-type")).toBe("text/css");
196
+ });
197
+
198
+ test("serves JS files", async () => {
199
+ const response = await fetch(`${dashboardUrl}/app.js`);
200
+ expect(response.status).toBe(200);
201
+ expect(response.headers.get("content-type")).toBe(
202
+ "application/javascript",
203
+ );
204
+ });
205
+
206
+ test("returns 404 for non-existent files", async () => {
207
+ const response = await fetch(`${dashboardUrl}/nonexistent.txt`);
208
+ expect(response.status).toBe(404);
209
+ });
210
+ });
211
+ });
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Cross-platform Browser Opening
3
+ *
4
+ * Opens the default browser to the dashboard URL on macOS, Linux, and Windows.
5
+ */
6
+
7
+ export async function openBrowser(url: string): Promise<void> {
8
+ const platform = process.platform;
9
+
10
+ let command: string[];
11
+
12
+ switch (platform) {
13
+ case "darwin":
14
+ command = ["open", url];
15
+ break;
16
+ case "win32":
17
+ command = ["cmd", "/c", "start", url];
18
+ break;
19
+ default:
20
+ // Linux and others
21
+ command = ["xdg-open", url];
22
+ break;
23
+ }
24
+
25
+ try {
26
+ const proc = Bun.spawn(command, {
27
+ stdout: "ignore",
28
+ stderr: "ignore",
29
+ });
30
+ await proc.exited;
31
+ } catch {
32
+ console.log(`Could not open browser automatically.`);
33
+ console.log(`Please open manually: ${url}`);
34
+ }
35
+ }