@cliangdev/flux-plugin 0.2.0-dev.dc5e2c4 → 0.2.0-dev.e34d43b
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 +3 -3
- package/commands/breakdown.md +3 -3
- package/commands/flux.md +92 -12
- package/commands/implement.md +1 -2
- package/commands/linear.md +6 -5
- package/commands/prd.md +821 -82
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/skills/flux-orchestrator/SKILL.md +11 -3
- package/skills/prd-writer/SKILL.md +577 -0
- package/src/server/index.ts +0 -2
- package/src/server/tools/__tests__/mcp-interface.test.ts +98 -8
- package/src/server/tools/__tests__/query.test.ts +0 -19
- package/src/server/tools/index.ts +53 -9
- package/src/server/tools/init-project.ts +1 -1
- package/skills/prd-template/SKILL.md +0 -242
- package/src/server/tools/get-project-context.ts +0 -33
|
@@ -10,7 +10,12 @@ process.env.FLUX_PROJECT_ROOT = TEST_DIR;
|
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
import { config } from "../../config.js";
|
|
12
12
|
import { closeDb, initDb } from "../../db/index.js";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
createError,
|
|
15
|
+
createProjectNotInitializedError,
|
|
16
|
+
registerTools,
|
|
17
|
+
type ToolDefinition,
|
|
18
|
+
} from "../index.js";
|
|
14
19
|
|
|
15
20
|
describe("MCP Interface", () => {
|
|
16
21
|
beforeEach(() => {
|
|
@@ -44,6 +49,42 @@ describe("MCP Interface", () => {
|
|
|
44
49
|
});
|
|
45
50
|
});
|
|
46
51
|
|
|
52
|
+
describe("createProjectNotInitializedError", () => {
|
|
53
|
+
test("creates error with setup instructions", () => {
|
|
54
|
+
const error = createProjectNotInitializedError("/test/cwd", "/test/root");
|
|
55
|
+
|
|
56
|
+
expect(error.error).toBe(true);
|
|
57
|
+
expect(error.code).toBe("PROJECT_NOT_INITIALIZED");
|
|
58
|
+
expect(error.message).toContain("/test/cwd");
|
|
59
|
+
expect(error.message).toContain("/test/root");
|
|
60
|
+
expect(error.setup.instructions).toBeDefined();
|
|
61
|
+
expect(error.setup.options).toHaveLength(2);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("includes command option for interactive setup", () => {
|
|
65
|
+
const error = createProjectNotInitializedError("/cwd", "/root");
|
|
66
|
+
const commandOption = error.setup.options.find(
|
|
67
|
+
(o) => o.method === "command",
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
expect(commandOption).toBeDefined();
|
|
71
|
+
expect(commandOption?.name).toBe("/flux");
|
|
72
|
+
expect(commandOption?.description).toContain("Interactive");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("includes tool option with required params", () => {
|
|
76
|
+
const error = createProjectNotInitializedError("/cwd", "/root");
|
|
77
|
+
const toolOption = error.setup.options.find((o) => o.method === "tool");
|
|
78
|
+
|
|
79
|
+
expect(toolOption).toBeDefined();
|
|
80
|
+
expect(toolOption?.name).toBe("init_project");
|
|
81
|
+
expect(toolOption?.params).toBeDefined();
|
|
82
|
+
expect(toolOption?.params?.name).toBeDefined();
|
|
83
|
+
expect(toolOption?.params?.vision).toBeDefined();
|
|
84
|
+
expect(toolOption?.params?.adapter).toBeDefined();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
47
88
|
describe("registerTools", () => {
|
|
48
89
|
test("sets up list tools handler", () => {
|
|
49
90
|
const handlers: Record<string, Function> = {};
|
|
@@ -287,7 +328,7 @@ describe("Project Validation", () => {
|
|
|
287
328
|
}
|
|
288
329
|
});
|
|
289
330
|
|
|
290
|
-
test("returns
|
|
331
|
+
test("returns PROJECT_NOT_INITIALIZED with setup instructions for tools requiring project", async () => {
|
|
291
332
|
const handlers: Record<string, Function> = {};
|
|
292
333
|
const mockServer = {
|
|
293
334
|
setRequestHandler: mock((schema: any, handler: Function) => {
|
|
@@ -317,7 +358,11 @@ describe("Project Validation", () => {
|
|
|
317
358
|
expect(result.isError).toBe(true);
|
|
318
359
|
const parsedError = JSON.parse(result.content[0].text);
|
|
319
360
|
expect(parsedError.error).toBe(true);
|
|
320
|
-
expect(parsedError.code).toBe("
|
|
361
|
+
expect(parsedError.code).toBe("PROJECT_NOT_INITIALIZED");
|
|
362
|
+
expect(parsedError.setup).toBeDefined();
|
|
363
|
+
expect(parsedError.setup.instructions).toBeDefined();
|
|
364
|
+
expect(parsedError.setup.options).toBeDefined();
|
|
365
|
+
expect(parsedError.setup.options.length).toBe(2);
|
|
321
366
|
});
|
|
322
367
|
|
|
323
368
|
test("allows init_project without existing project", async () => {
|
|
@@ -353,7 +398,7 @@ describe("Project Validation", () => {
|
|
|
353
398
|
expect(handlerMock).toHaveBeenCalled();
|
|
354
399
|
});
|
|
355
400
|
|
|
356
|
-
test("allows
|
|
401
|
+
test("allows get_version without existing project", async () => {
|
|
357
402
|
const handlers: Record<string, Function> = {};
|
|
358
403
|
const mockServer = {
|
|
359
404
|
setRequestHandler: mock((schema: any, handler: Function) => {
|
|
@@ -363,11 +408,11 @@ describe("Project Validation", () => {
|
|
|
363
408
|
}),
|
|
364
409
|
};
|
|
365
410
|
|
|
366
|
-
const handlerMock = mock(async () => ({
|
|
411
|
+
const handlerMock = mock(async () => ({ version: "1.0.0" }));
|
|
367
412
|
|
|
368
413
|
const testTool: ToolDefinition = {
|
|
369
|
-
name: "
|
|
370
|
-
description: "Get
|
|
414
|
+
name: "get_version",
|
|
415
|
+
description: "Get version",
|
|
371
416
|
inputSchema: z.object({}),
|
|
372
417
|
handler: handlerMock,
|
|
373
418
|
};
|
|
@@ -377,7 +422,7 @@ describe("Project Validation", () => {
|
|
|
377
422
|
const callHandler = handlers.call;
|
|
378
423
|
const result = await callHandler({
|
|
379
424
|
params: {
|
|
380
|
-
name: "
|
|
425
|
+
name: "get_version",
|
|
381
426
|
arguments: {},
|
|
382
427
|
},
|
|
383
428
|
});
|
|
@@ -385,4 +430,49 @@ describe("Project Validation", () => {
|
|
|
385
430
|
expect(result.isError).toBeUndefined();
|
|
386
431
|
expect(handlerMock).toHaveBeenCalled();
|
|
387
432
|
});
|
|
433
|
+
|
|
434
|
+
test("setup options have correct structure", async () => {
|
|
435
|
+
const handlers: Record<string, Function> = {};
|
|
436
|
+
const mockServer = {
|
|
437
|
+
setRequestHandler: mock((schema: any, handler: Function) => {
|
|
438
|
+
if (schema.shape?.method?.value === "tools/call") {
|
|
439
|
+
handlers.call = handler;
|
|
440
|
+
}
|
|
441
|
+
}),
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
const testTool: ToolDefinition = {
|
|
445
|
+
name: "query_entities",
|
|
446
|
+
description: "Query entities",
|
|
447
|
+
inputSchema: z.object({ type: z.string() }),
|
|
448
|
+
handler: async () => ({ items: [] }),
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
registerTools(mockServer as any, [testTool]);
|
|
452
|
+
|
|
453
|
+
const callHandler = handlers.call;
|
|
454
|
+
const result = await callHandler({
|
|
455
|
+
params: {
|
|
456
|
+
name: "query_entities",
|
|
457
|
+
arguments: { type: "prd" },
|
|
458
|
+
},
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
expect(result.isError).toBe(true);
|
|
462
|
+
const parsedError = JSON.parse(result.content[0].text);
|
|
463
|
+
|
|
464
|
+
const commandOption = parsedError.setup.options.find(
|
|
465
|
+
(o: any) => o.method === "command",
|
|
466
|
+
);
|
|
467
|
+
expect(commandOption.name).toBe("/flux");
|
|
468
|
+
expect(commandOption.description).toBeTruthy();
|
|
469
|
+
|
|
470
|
+
const toolOption = parsedError.setup.options.find(
|
|
471
|
+
(o: any) => o.method === "tool",
|
|
472
|
+
);
|
|
473
|
+
expect(toolOption.name).toBe("init_project");
|
|
474
|
+
expect(toolOption.params.name).toBeTruthy();
|
|
475
|
+
expect(toolOption.params.vision).toBeTruthy();
|
|
476
|
+
expect(toolOption.params.adapter).toBeTruthy();
|
|
477
|
+
});
|
|
388
478
|
});
|
|
@@ -16,7 +16,6 @@ import { createPrdTool } from "../create-prd.js";
|
|
|
16
16
|
import { createTaskTool } from "../create-task.js";
|
|
17
17
|
import { addDependencyTool } from "../dependencies.js";
|
|
18
18
|
import { getEntityTool } from "../get-entity.js";
|
|
19
|
-
import { getProjectContextTool } from "../get-project-context.js";
|
|
20
19
|
import { getStatsTool } from "../get-stats.js";
|
|
21
20
|
import { initProjectTool } from "../init-project.js";
|
|
22
21
|
import { queryEntitiesTool } from "../query-entities.js";
|
|
@@ -242,24 +241,6 @@ describe("Query MCP Tools", () => {
|
|
|
242
241
|
});
|
|
243
242
|
});
|
|
244
243
|
|
|
245
|
-
describe("get_project_context", () => {
|
|
246
|
-
test("returns project context when initialized", async () => {
|
|
247
|
-
const result = (await getProjectContextTool.handler({})) as any;
|
|
248
|
-
|
|
249
|
-
expect(result.initialized).toBe(true);
|
|
250
|
-
expect(result.name).toBe("test-project");
|
|
251
|
-
expect(result.ref_prefix).toBe("TEST");
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
test("returns initialized false when no project", async () => {
|
|
255
|
-
// Remove the project.json
|
|
256
|
-
rmSync(join(FLUX_DIR, "project.json"));
|
|
257
|
-
|
|
258
|
-
const result = (await getProjectContextTool.handler({})) as any;
|
|
259
|
-
expect(result.initialized).toBe(false);
|
|
260
|
-
});
|
|
261
|
-
});
|
|
262
|
-
|
|
263
244
|
describe("get_stats", () => {
|
|
264
245
|
test("returns zeroes for empty project", async () => {
|
|
265
246
|
const result = (await getStatsTool.handler({})) as any;
|
|
@@ -7,11 +7,7 @@ import { z } from "zod";
|
|
|
7
7
|
import { config } from "../config.js";
|
|
8
8
|
import { logger } from "../utils/logger.js";
|
|
9
9
|
|
|
10
|
-
const TOOLS_WITHOUT_PROJECT = [
|
|
11
|
-
"init_project",
|
|
12
|
-
"get_project_context",
|
|
13
|
-
"get_version",
|
|
14
|
-
];
|
|
10
|
+
const TOOLS_WITHOUT_PROJECT = ["init_project", "get_version"];
|
|
15
11
|
|
|
16
12
|
export interface ToolDefinition {
|
|
17
13
|
name: string;
|
|
@@ -26,10 +22,56 @@ export interface ToolError {
|
|
|
26
22
|
code: string;
|
|
27
23
|
}
|
|
28
24
|
|
|
25
|
+
export interface ProjectNotInitializedError extends ToolError {
|
|
26
|
+
code: "PROJECT_NOT_INITIALIZED";
|
|
27
|
+
setup: {
|
|
28
|
+
instructions: string;
|
|
29
|
+
options: Array<{
|
|
30
|
+
method: "command" | "tool";
|
|
31
|
+
name: string;
|
|
32
|
+
description: string;
|
|
33
|
+
params?: Record<string, string>;
|
|
34
|
+
}>;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
29
38
|
export function createError(message: string, code: string): ToolError {
|
|
30
39
|
return { error: true, message, code };
|
|
31
40
|
}
|
|
32
41
|
|
|
42
|
+
export function createProjectNotInitializedError(
|
|
43
|
+
cwd: string,
|
|
44
|
+
projectRoot: string,
|
|
45
|
+
): ProjectNotInitializedError {
|
|
46
|
+
return {
|
|
47
|
+
error: true,
|
|
48
|
+
code: "PROJECT_NOT_INITIALIZED",
|
|
49
|
+
message: `No Flux project found. Current directory: ${cwd}, resolved project root: ${projectRoot}`,
|
|
50
|
+
setup: {
|
|
51
|
+
instructions:
|
|
52
|
+
"Initialize a Flux project before using Flux tools. Use one of the following options:",
|
|
53
|
+
options: [
|
|
54
|
+
{
|
|
55
|
+
method: "command",
|
|
56
|
+
name: "/flux",
|
|
57
|
+
description:
|
|
58
|
+
"Interactive setup with guided prompts (recommended for first-time setup)",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
method: "tool",
|
|
62
|
+
name: "init_project",
|
|
63
|
+
description: "Direct initialization via MCP tool",
|
|
64
|
+
params: {
|
|
65
|
+
name: "Project name (required)",
|
|
66
|
+
vision: "Brief project description (required)",
|
|
67
|
+
adapter: "local | linear (default: local)",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
33
75
|
export function registerTools(server: Server, tools: ToolDefinition[]) {
|
|
34
76
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
35
77
|
tools: tools.map((t) => ({
|
|
@@ -60,13 +102,16 @@ export function registerTools(server: Server, tools: ToolDefinition[]) {
|
|
|
60
102
|
}
|
|
61
103
|
|
|
62
104
|
if (!TOOLS_WITHOUT_PROJECT.includes(toolName) && !config.projectExists) {
|
|
63
|
-
const
|
|
64
|
-
|
|
105
|
+
const error = createProjectNotInitializedError(
|
|
106
|
+
process.cwd(),
|
|
107
|
+
config.projectRoot,
|
|
108
|
+
);
|
|
109
|
+
logger.error(error.message);
|
|
65
110
|
return {
|
|
66
111
|
content: [
|
|
67
112
|
{
|
|
68
113
|
type: "text",
|
|
69
|
-
text: JSON.stringify(
|
|
114
|
+
text: JSON.stringify(error, null, 2),
|
|
70
115
|
},
|
|
71
116
|
],
|
|
72
117
|
isError: true,
|
|
@@ -104,7 +149,6 @@ export { deleteEntityTool } from "./delete-entity.js";
|
|
|
104
149
|
export { addDependencyTool, removeDependencyTool } from "./dependencies.js";
|
|
105
150
|
export { getEntityTool } from "./get-entity.js";
|
|
106
151
|
export { getLinearUrlTool } from "./get-linear-url.js";
|
|
107
|
-
export { getProjectContextTool } from "./get-project-context.js";
|
|
108
152
|
export { getStatsTool } from "./get-stats.js";
|
|
109
153
|
export { getVersionTool } from "./get-version.js";
|
|
110
154
|
export { initProjectTool } from "./init-project.js";
|
|
@@ -102,7 +102,7 @@ async function handler(input: unknown) {
|
|
|
102
102
|
export const initProjectTool: ToolDefinition = {
|
|
103
103
|
name: "init_project",
|
|
104
104
|
description:
|
|
105
|
-
"Initialize a new Flux project. Required: name, vision. Optional: adapter (local|specflux|linear|notion, default 'local'). Creates .flux/ directory with project.json and SQLite database. Returns {success, project, message}. Fails if .flux/ already exists.
|
|
105
|
+
"Initialize a new Flux project. Required: name, vision. Optional: adapter (local|specflux|linear|notion, default 'local'). Creates .flux/ directory with project.json and SQLite database. Returns {success, project, message}. Fails if .flux/ already exists.",
|
|
106
106
|
inputSchema,
|
|
107
107
|
handler,
|
|
108
108
|
};
|
|
@@ -1,242 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: flux:prd-template
|
|
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
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# PRD Template Skill
|
|
8
|
-
|
|
9
|
-
PRDs should be **concise for human review** but contain **enough detail for AI implementation**.
|
|
10
|
-
|
|
11
|
-
## PRD Structure (Required)
|
|
12
|
-
|
|
13
|
-
```markdown
|
|
14
|
-
# {Project Name}
|
|
15
|
-
|
|
16
|
-
## Table of Contents
|
|
17
|
-
- [Problem](#problem)
|
|
18
|
-
- [Users](#users)
|
|
19
|
-
- [Solution](#solution)
|
|
20
|
-
- [Features (MVP)](#features-mvp)
|
|
21
|
-
- [P0: Must Have](#p0-must-have)
|
|
22
|
-
- [P1: Should Have](#p1-should-have)
|
|
23
|
-
- [Out of Scope](#out-of-scope)
|
|
24
|
-
- [Constraints](#constraints)
|
|
25
|
-
- [Open Questions](#open-questions)
|
|
26
|
-
|
|
27
|
-
## Problem
|
|
28
|
-
{2-3 sentences: What problem are we solving? Why does it matter?}
|
|
29
|
-
|
|
30
|
-
## Users
|
|
31
|
-
{Who has this problem? 1-2 user segments.}
|
|
32
|
-
|
|
33
|
-
## Solution
|
|
34
|
-
{1 paragraph: What are we building to solve this?}
|
|
35
|
-
|
|
36
|
-
## Features (MVP)
|
|
37
|
-
|
|
38
|
-
### P0: Must Have
|
|
39
|
-
- **{Feature 1}**: {One sentence description}
|
|
40
|
-
- {Acceptance criterion 1}
|
|
41
|
-
- {Acceptance criterion 2}
|
|
42
|
-
- **{Feature 2}**: {One sentence description}
|
|
43
|
-
- {Acceptance criterion}
|
|
44
|
-
|
|
45
|
-
### P1: Should Have
|
|
46
|
-
- **{Feature}**: {Description}
|
|
47
|
-
|
|
48
|
-
### Out of Scope
|
|
49
|
-
- {What we're NOT building}
|
|
50
|
-
- {Future considerations}
|
|
51
|
-
|
|
52
|
-
## Constraints
|
|
53
|
-
- **Tech Stack**: {Required technologies, if any}
|
|
54
|
-
- **Timeline**: {Any deadlines}
|
|
55
|
-
- **Other**: {Budget, compliance, etc.}
|
|
56
|
-
|
|
57
|
-
## Open Questions
|
|
58
|
-
- {Unresolved decisions needing input}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### TOC Guidelines
|
|
62
|
-
|
|
63
|
-
**For Local Adapter** (files in `.flux/prds/`):
|
|
64
|
-
- Include TOC with anchor links (e.g., `[Problem](#problem)`)
|
|
65
|
-
- Use lowercase with hyphens for anchors
|
|
66
|
-
- Nest sub-sections under parent sections
|
|
67
|
-
|
|
68
|
-
**For External Adapters** (Linear, Notion):
|
|
69
|
-
- **Skip TOC** - External systems have their own navigation (Linear's outline, collapsible sections)
|
|
70
|
-
- Linear generates unique anchor suffixes making pre-built links impossible
|
|
71
|
-
|
|
72
|
-
## Guidelines
|
|
73
|
-
|
|
74
|
-
### Keep It Short
|
|
75
|
-
- Problem: 2-3 sentences max
|
|
76
|
-
- Features: 3-5 P0 features for MVP
|
|
77
|
-
- Each feature: 1 sentence + 2-4 acceptance criteria
|
|
78
|
-
- Total PRD: 1-2 pages
|
|
79
|
-
|
|
80
|
-
### Write for AI Implementation
|
|
81
|
-
- Acceptance criteria should be testable
|
|
82
|
-
- Use specific, unambiguous language
|
|
83
|
-
- Include edge cases in criteria when important
|
|
84
|
-
|
|
85
|
-
### Bad vs Good Examples
|
|
86
|
-
|
|
87
|
-
**Bad feature:**
|
|
88
|
-
> - User authentication
|
|
89
|
-
|
|
90
|
-
**Good feature:**
|
|
91
|
-
> - **User Authentication**: Users can sign up and log in with email/password
|
|
92
|
-
> - Sign up requires email, password (min 8 chars), and name
|
|
93
|
-
> - Login with email + password returns JWT token
|
|
94
|
-
> - Invalid credentials show error message
|
|
95
|
-
> - Forgot password sends reset email
|
|
96
|
-
|
|
97
|
-
## Supporting Documents (Optional)
|
|
98
|
-
|
|
99
|
-
Generate based on **agent confidence** and **user request**.
|
|
100
|
-
|
|
101
|
-
| Document | When to Generate |
|
|
102
|
-
|----------|------------------|
|
|
103
|
-
| `architecture.md` | Complex systems, multiple components, API integrations |
|
|
104
|
-
| `wireframes.md` | UI-heavy features, user specifically requests |
|
|
105
|
-
| `data-model.md` | Custom data storage, complex relationships |
|
|
106
|
-
| `user-flows.md` | Multi-step processes, complex user journeys |
|
|
107
|
-
|
|
108
|
-
### Asking About Supporting Docs
|
|
109
|
-
|
|
110
|
-
After PRD outline approval, ask:
|
|
111
|
-
|
|
112
|
-
```
|
|
113
|
-
PRD outline looks good. I can also generate:
|
|
114
|
-
- Architecture diagram (recommended for this project)
|
|
115
|
-
- UI wireframes
|
|
116
|
-
- Data model
|
|
117
|
-
|
|
118
|
-
Which would you like? Or should I proceed with just the PRD?
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
Recommend based on project type:
|
|
122
|
-
- **Web App**: architecture, wireframes
|
|
123
|
-
- **API/Backend**: architecture, data-model
|
|
124
|
-
- **CLI Tool**: usually just PRD
|
|
125
|
-
- **Mobile App**: architecture, wireframes
|
|
126
|
-
|
|
127
|
-
## Supporting Doc Templates
|
|
128
|
-
|
|
129
|
-
### architecture.md
|
|
130
|
-
|
|
131
|
-
```markdown
|
|
132
|
-
# Architecture
|
|
133
|
-
|
|
134
|
-
## Overview
|
|
135
|
-
{One paragraph system description}
|
|
136
|
-
|
|
137
|
-
## Components
|
|
138
|
-
|
|
139
|
-
```mermaid
|
|
140
|
-
graph TB
|
|
141
|
-
Client[Client] --> API[API Server]
|
|
142
|
-
API --> DB[(Database)]
|
|
143
|
-
API --> Cache[(Redis)]
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
## API Endpoints
|
|
147
|
-
|
|
148
|
-
| Method | Path | Description |
|
|
149
|
-
|--------|------|-------------|
|
|
150
|
-
| POST | /api/users | Create user |
|
|
151
|
-
| GET | /api/users/:id | Get user |
|
|
152
|
-
|
|
153
|
-
## Data Flow
|
|
154
|
-
{Describe key data flows}
|
|
155
|
-
|
|
156
|
-
## Tech Stack
|
|
157
|
-
- **Frontend**: {tech}
|
|
158
|
-
- **Backend**: {tech}
|
|
159
|
-
- **Database**: {tech}
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
### wireframes.md
|
|
163
|
-
|
|
164
|
-
```markdown
|
|
165
|
-
# Wireframes
|
|
166
|
-
|
|
167
|
-
## Screen: {Name}
|
|
168
|
-
|
|
169
|
-
```
|
|
170
|
-
+----------------------------------+
|
|
171
|
-
| Logo [Login] [Sign Up]
|
|
172
|
-
+----------------------------------+
|
|
173
|
-
| |
|
|
174
|
-
| Welcome to {App Name} |
|
|
175
|
-
| |
|
|
176
|
-
| [ Email ] |
|
|
177
|
-
| [Password ] |
|
|
178
|
-
| [ Login ] |
|
|
179
|
-
| |
|
|
180
|
-
| Forgot password? |
|
|
181
|
-
+----------------------------------+
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
**Elements:**
|
|
185
|
-
- Logo: links to home
|
|
186
|
-
- Login button: submits form
|
|
187
|
-
- Forgot password: opens reset flow
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### data-model.md
|
|
191
|
-
|
|
192
|
-
```markdown
|
|
193
|
-
# Data Model
|
|
194
|
-
|
|
195
|
-
## ERD
|
|
196
|
-
|
|
197
|
-
```mermaid
|
|
198
|
-
erDiagram
|
|
199
|
-
User ||--o{ Post : creates
|
|
200
|
-
Post ||--o{ Comment : has
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
## Tables
|
|
204
|
-
|
|
205
|
-
### users
|
|
206
|
-
| Column | Type | Constraints |
|
|
207
|
-
|--------|------|-------------|
|
|
208
|
-
| id | uuid | PK |
|
|
209
|
-
| email | varchar | unique, not null |
|
|
210
|
-
| created_at | timestamp | default now() |
|
|
211
|
-
|
|
212
|
-
### posts
|
|
213
|
-
| Column | Type | Constraints |
|
|
214
|
-
|--------|------|-------------|
|
|
215
|
-
| id | uuid | PK |
|
|
216
|
-
| user_id | uuid | FK users.id |
|
|
217
|
-
| content | text | not null |
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
## Workflow
|
|
221
|
-
|
|
222
|
-
Check adapter type via `get_project_context` to determine storage behavior.
|
|
223
|
-
|
|
224
|
-
### Local Adapter (`adapter.type === "local"`)
|
|
225
|
-
|
|
226
|
-
1. **Interview** → Collect answers via AskUserQuestion
|
|
227
|
-
2. **Outline** → Generate brief outline, ask for approval
|
|
228
|
-
3. **Create entity** → Call `create_prd` with title and short description
|
|
229
|
-
4. **Write PRD** → Write full PRD to `.flux/prds/{slug}/prd.md`
|
|
230
|
-
5. **Set folder_path** → Call `update_entity` with `folder_path`: `.flux/prds/{slug}`
|
|
231
|
-
6. **Ask about docs** → Offer relevant supporting docs
|
|
232
|
-
7. **Generate docs** → Write requested docs to same folder
|
|
233
|
-
|
|
234
|
-
### External Adapters (`adapter.type === "linear"`, `"notion"`, etc.)
|
|
235
|
-
|
|
236
|
-
1. **Interview** → Collect answers via AskUserQuestion
|
|
237
|
-
2. **Outline** → Generate brief outline, ask for approval
|
|
238
|
-
3. **Create entity** → Call `create_prd` with title and short description
|
|
239
|
-
4. **Sync content** → Call `update_entity` with `description`: Full PRD markdown content
|
|
240
|
-
5. **Ask about docs** → Offer relevant supporting docs (stored as attachments in external system)
|
|
241
|
-
|
|
242
|
-
**Note**: External adapters store all content in the external system. No local `.flux/prds/` files are created.
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { config } from "../config.js";
|
|
4
|
-
import type { ToolDefinition } from "./index.js";
|
|
5
|
-
|
|
6
|
-
const inputSchema = z.object({});
|
|
7
|
-
|
|
8
|
-
async function handler(_input: unknown) {
|
|
9
|
-
const projectJsonPath = config.projectJsonPath;
|
|
10
|
-
|
|
11
|
-
if (!existsSync(projectJsonPath)) {
|
|
12
|
-
return { initialized: false };
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
try {
|
|
16
|
-
const content = readFileSync(projectJsonPath, "utf-8");
|
|
17
|
-
const project = JSON.parse(content);
|
|
18
|
-
return { initialized: true, ...project };
|
|
19
|
-
} catch (_err) {
|
|
20
|
-
return {
|
|
21
|
-
initialized: false,
|
|
22
|
-
error: "Failed to read project.json",
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export const getProjectContextTool: ToolDefinition = {
|
|
28
|
-
name: "get_project_context",
|
|
29
|
-
description:
|
|
30
|
-
"Get project context from .flux/project.json. No parameters required. Returns {initialized: boolean, name?, vision?, ref_prefix?, adapter?: {type: 'local'|'specflux'|'linear'|'notion'}, created_at?}. Use this to check if a Flux project exists before other operations.",
|
|
31
|
-
inputSchema,
|
|
32
|
-
handler,
|
|
33
|
-
};
|