flowspec-mcp 5.4.0 → 5.5.1
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/dist/analysis/analysisUtils.d.ts +36 -0
- package/dist/analysis/analysisUtils.js +284 -0
- package/dist/analysis/analysisUtils.js.map +1 -0
- package/dist/db.js +19 -3
- package/dist/db.js.map +1 -1
- package/dist/export/yamlExporter.d.ts +6 -0
- package/dist/export/yamlExporter.js +155 -0
- package/dist/export/yamlExporter.js.map +1 -0
- package/dist/import/yamlImporter.d.ts +22 -0
- package/dist/import/yamlImporter.js +227 -0
- package/dist/import/yamlImporter.js.map +1 -0
- package/dist/index.js +0 -0
- package/dist/layout/semanticLayout.d.ts +24 -0
- package/dist/layout/semanticLayout.js +233 -0
- package/dist/layout/semanticLayout.js.map +1 -0
- package/dist/resources/selection.d.ts +5 -0
- package/dist/resources/selection.js +88 -0
- package/dist/resources/selection.js.map +1 -0
- package/dist/server.js +29 -24
- package/dist/server.js.map +1 -1
- package/dist/tools/captureScreen.d.ts +48 -0
- package/dist/tools/captureScreen.js +135 -0
- package/dist/tools/captureScreen.js.map +1 -0
- package/dist/tools/createNode.d.ts +2 -2
- package/dist/tools/createSubview.d.ts +50 -0
- package/dist/tools/createSubview.js +29 -0
- package/dist/tools/createSubview.js.map +1 -0
- package/dist/tools/deleteSubview.d.ts +24 -0
- package/dist/tools/deleteSubview.js +19 -0
- package/dist/tools/deleteSubview.js.map +1 -0
- package/dist/tools/generateSpec.d.ts +26 -0
- package/dist/tools/generateSpec.js +336 -0
- package/dist/tools/generateSpec.js.map +1 -0
- package/dist/tools/getYaml.d.ts +21 -0
- package/dist/tools/getYaml.js +23 -0
- package/dist/tools/getYaml.js.map +1 -0
- package/dist/tools/healthCheck.d.ts +8 -0
- package/dist/tools/healthCheck.js +16 -0
- package/dist/tools/healthCheck.js.map +1 -0
- package/dist/tools/importYaml.d.ts +33 -0
- package/dist/tools/importYaml.js +97 -0
- package/dist/tools/importYaml.js.map +1 -0
- package/dist/tools/ingestCodebase.d.ts +27 -0
- package/dist/tools/ingestCodebase.js +516 -0
- package/dist/tools/ingestCodebase.js.map +1 -0
- package/dist/tools/listSubviews.d.ts +21 -0
- package/dist/tools/listSubviews.js +34 -0
- package/dist/tools/listSubviews.js.map +1 -0
- package/dist/tools/smartLayout.d.ts +30 -0
- package/dist/tools/smartLayout.js +74 -0
- package/dist/tools/smartLayout.js.map +1 -0
- package/dist/tools/updateSubview.d.ts +53 -0
- package/dist/tools/updateSubview.js +33 -0
- package/dist/tools/updateSubview.js.map +1 -0
- package/dist/utils/selectionHelper.d.ts +61 -0
- package/dist/utils/selectionHelper.js +111 -0
- package/dist/utils/selectionHelper.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { uploadImageViaApi } from '../db.js';
|
|
3
|
+
import { detectImageDimensions } from '../image/dimensions.js';
|
|
4
|
+
export const captureScreenSchema = z.object({
|
|
5
|
+
url: z.string().describe('URL to screenshot (http:// or https://)'),
|
|
6
|
+
selector: z.string().optional().describe('CSS selector for specific element (full page if omitted)'),
|
|
7
|
+
viewport: z
|
|
8
|
+
.object({
|
|
9
|
+
width: z.number(),
|
|
10
|
+
height: z.number()
|
|
11
|
+
})
|
|
12
|
+
.optional()
|
|
13
|
+
.describe('Viewport size (default: 1920x1080)'),
|
|
14
|
+
waitFor: z
|
|
15
|
+
.string()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe('CSS selector to wait for before screenshot, or timeout in ms (e.g., "3000")'),
|
|
18
|
+
fullPage: z.boolean().optional().default(false).describe('Capture full page height (default: false)')
|
|
19
|
+
});
|
|
20
|
+
export async function handleCaptureScreen(args) {
|
|
21
|
+
// Validate URL
|
|
22
|
+
if (!args.url.startsWith('http://') && !args.url.startsWith('https://')) {
|
|
23
|
+
return {
|
|
24
|
+
content: [{ type: 'text', text: `Invalid URL. Must start with http:// or https://` }],
|
|
25
|
+
isError: true
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
let browser;
|
|
29
|
+
try {
|
|
30
|
+
// Dynamically import playwright (cloud-only dependency)
|
|
31
|
+
const { chromium } = await import('playwright');
|
|
32
|
+
// Launch browser
|
|
33
|
+
browser = await chromium.launch({ headless: true });
|
|
34
|
+
const context = await browser.newContext({
|
|
35
|
+
viewport: args.viewport ?? { width: 1920, height: 1080 }
|
|
36
|
+
});
|
|
37
|
+
const page = await context.newPage();
|
|
38
|
+
// Navigate to URL
|
|
39
|
+
await page.goto(args.url, { waitUntil: 'networkidle' });
|
|
40
|
+
// Wait for selector or timeout if specified
|
|
41
|
+
if (args.waitFor) {
|
|
42
|
+
const timeoutMs = parseInt(args.waitFor, 10);
|
|
43
|
+
if (!isNaN(timeoutMs)) {
|
|
44
|
+
// Wait for timeout
|
|
45
|
+
await page.waitForTimeout(timeoutMs);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// Wait for selector
|
|
49
|
+
try {
|
|
50
|
+
await page.waitForSelector(args.waitFor, { timeout: 10000 });
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
return {
|
|
54
|
+
content: [
|
|
55
|
+
{
|
|
56
|
+
type: 'text',
|
|
57
|
+
text: `Selector "${args.waitFor}" not found within 10s. Screenshot may be incomplete.`
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
isError: true
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Take screenshot
|
|
66
|
+
let screenshotBuffer;
|
|
67
|
+
if (args.selector) {
|
|
68
|
+
// Screenshot specific element
|
|
69
|
+
const element = await page.$(args.selector);
|
|
70
|
+
if (!element) {
|
|
71
|
+
return {
|
|
72
|
+
content: [
|
|
73
|
+
{
|
|
74
|
+
type: 'text',
|
|
75
|
+
text: `Element not found: ${args.selector}`
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
isError: true
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
screenshotBuffer = await element.screenshot({ type: 'png' });
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// Screenshot full page or viewport
|
|
85
|
+
screenshotBuffer = await page.screenshot({
|
|
86
|
+
type: 'png',
|
|
87
|
+
fullPage: args.fullPage
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
await browser.close();
|
|
91
|
+
// Detect dimensions from screenshot
|
|
92
|
+
const detected = detectImageDimensions(new Uint8Array(screenshotBuffer));
|
|
93
|
+
const width = detected?.width ?? args.viewport?.width ?? 1920;
|
|
94
|
+
const height = detected?.height ?? args.viewport?.height ?? 1080;
|
|
95
|
+
// Upload to Vercel Blob (via API)
|
|
96
|
+
const base64Data = screenshotBuffer.toString('base64');
|
|
97
|
+
const filename = `screenshot_${Date.now()}.png`;
|
|
98
|
+
const result = await uploadImageViaApi(base64Data, filename, 'image/png');
|
|
99
|
+
if (!result) {
|
|
100
|
+
return {
|
|
101
|
+
content: [
|
|
102
|
+
{
|
|
103
|
+
type: 'text',
|
|
104
|
+
text: `Failed to upload screenshot. In cloud mode, image upload is not yet supported.`
|
|
105
|
+
}
|
|
106
|
+
],
|
|
107
|
+
isError: true
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
// Return result in same format as uploadImage
|
|
111
|
+
return {
|
|
112
|
+
content: [
|
|
113
|
+
{
|
|
114
|
+
type: 'text',
|
|
115
|
+
text: JSON.stringify({ url: result.url, width, height, filename }, null, 2)
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
if (browser) {
|
|
122
|
+
await browser.close();
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
content: [
|
|
126
|
+
{
|
|
127
|
+
type: 'text',
|
|
128
|
+
text: `Screenshot capture failed: ${error.message}`
|
|
129
|
+
}
|
|
130
|
+
],
|
|
131
|
+
isError: true
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=captureScreen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"captureScreen.js","sourceRoot":"","sources":["../../src/tools/captureScreen.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,7 +14,7 @@ export declare const createNodeSchema: z.ZodObject<{
|
|
|
14
14
|
}>;
|
|
15
15
|
data: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
16
16
|
}, "strip", z.ZodTypeAny, {
|
|
17
|
-
type: "datapoint" | "
|
|
17
|
+
type: "datapoint" | "table" | "component" | "transform";
|
|
18
18
|
position: {
|
|
19
19
|
x: number;
|
|
20
20
|
y: number;
|
|
@@ -22,7 +22,7 @@ export declare const createNodeSchema: z.ZodObject<{
|
|
|
22
22
|
data: Record<string, unknown>;
|
|
23
23
|
projectId: string;
|
|
24
24
|
}, {
|
|
25
|
-
type: "datapoint" | "
|
|
25
|
+
type: "datapoint" | "table" | "component" | "transform";
|
|
26
26
|
position: {
|
|
27
27
|
x: number;
|
|
28
28
|
y: number;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const createSubviewSchema: z.ZodObject<{
|
|
3
|
+
projectId: z.ZodString;
|
|
4
|
+
name: z.ZodString;
|
|
5
|
+
description: z.ZodOptional<z.ZodString>;
|
|
6
|
+
nodePositions: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
7
|
+
nodeId: z.ZodString;
|
|
8
|
+
x: z.ZodNumber;
|
|
9
|
+
y: z.ZodNumber;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
nodeId: string;
|
|
14
|
+
}, {
|
|
15
|
+
x: number;
|
|
16
|
+
y: number;
|
|
17
|
+
nodeId: string;
|
|
18
|
+
}>, "many">>;
|
|
19
|
+
}, "strip", z.ZodTypeAny, {
|
|
20
|
+
name: string;
|
|
21
|
+
projectId: string;
|
|
22
|
+
description?: string | undefined;
|
|
23
|
+
nodePositions?: {
|
|
24
|
+
x: number;
|
|
25
|
+
y: number;
|
|
26
|
+
nodeId: string;
|
|
27
|
+
}[] | undefined;
|
|
28
|
+
}, {
|
|
29
|
+
name: string;
|
|
30
|
+
projectId: string;
|
|
31
|
+
description?: string | undefined;
|
|
32
|
+
nodePositions?: {
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
nodeId: string;
|
|
36
|
+
}[] | undefined;
|
|
37
|
+
}>;
|
|
38
|
+
export declare function handleCreateSubview(args: z.infer<typeof createSubviewSchema>): Promise<{
|
|
39
|
+
content: {
|
|
40
|
+
type: "text";
|
|
41
|
+
text: string;
|
|
42
|
+
}[];
|
|
43
|
+
isError: boolean;
|
|
44
|
+
} | {
|
|
45
|
+
content: {
|
|
46
|
+
type: "text";
|
|
47
|
+
text: string;
|
|
48
|
+
}[];
|
|
49
|
+
isError?: undefined;
|
|
50
|
+
}>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { createSubviewViaApi } from '../db.js';
|
|
3
|
+
export const createSubviewSchema = z.object({
|
|
4
|
+
projectId: z.string().describe('UUID of the project'),
|
|
5
|
+
name: z.string().describe('Subview name'),
|
|
6
|
+
description: z.string().optional().describe('Optional subview description'),
|
|
7
|
+
nodePositions: z.array(z.object({
|
|
8
|
+
nodeId: z.string().describe('Node ID from the main canvas'),
|
|
9
|
+
x: z.number().describe('X position in subview canvas'),
|
|
10
|
+
y: z.number().describe('Y position in subview canvas'),
|
|
11
|
+
})).optional().describe('Initial nodes and their positions in the subview'),
|
|
12
|
+
});
|
|
13
|
+
export async function handleCreateSubview(args) {
|
|
14
|
+
const subview = await createSubviewViaApi(args.projectId, args.name, args.description, args.nodePositions ?? []);
|
|
15
|
+
if (!subview) {
|
|
16
|
+
return {
|
|
17
|
+
content: [{ type: 'text', text: `Failed to create subview — project not found: ${args.projectId}` }],
|
|
18
|
+
isError: true,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const nodeCount = args.nodePositions?.length ?? 0;
|
|
22
|
+
return {
|
|
23
|
+
content: [{
|
|
24
|
+
type: 'text',
|
|
25
|
+
text: `Created subview **${args.name}** (id: ${subview.id}) with ${nodeCount} node${nodeCount === 1 ? '' : 's'}`,
|
|
26
|
+
}],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=createSubview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createSubview.js","sourceRoot":"","sources":["../../src/tools/createSubview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/C,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;IACrD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC;IACzC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;IAC3E,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QAC9B,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;QAC3D,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;QACtD,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;KACvD,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,kDAAkD,CAAC;CAC5E,CAAC,CAAC;AAEH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAAyC;IACjF,MAAM,OAAO,GAAG,MAAM,mBAAmB,CACvC,IAAI,CAAC,SAAS,EACd,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,aAAa,IAAI,EAAE,CACzB,CAAC;IAEF,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,iDAAiD,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;YAC7G,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,aAAa,EAAE,MAAM,IAAI,CAAC,CAAC;IAClD,OAAO;QACL,OAAO,EAAE,CAAC;gBACR,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE,qBAAqB,IAAI,CAAC,IAAI,WAAW,OAAO,CAAC,EAAE,UAAU,SAAS,QAAQ,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE;aACjH,CAAC;KACH,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const deleteSubviewSchema: z.ZodObject<{
|
|
3
|
+
projectId: z.ZodString;
|
|
4
|
+
subviewId: z.ZodString;
|
|
5
|
+
}, "strip", z.ZodTypeAny, {
|
|
6
|
+
projectId: string;
|
|
7
|
+
subviewId: string;
|
|
8
|
+
}, {
|
|
9
|
+
projectId: string;
|
|
10
|
+
subviewId: string;
|
|
11
|
+
}>;
|
|
12
|
+
export declare function handleDeleteSubview(args: z.infer<typeof deleteSubviewSchema>): Promise<{
|
|
13
|
+
content: {
|
|
14
|
+
type: "text";
|
|
15
|
+
text: string;
|
|
16
|
+
}[];
|
|
17
|
+
isError: boolean;
|
|
18
|
+
} | {
|
|
19
|
+
content: {
|
|
20
|
+
type: "text";
|
|
21
|
+
text: string;
|
|
22
|
+
}[];
|
|
23
|
+
isError?: undefined;
|
|
24
|
+
}>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { deleteSubviewViaApi } from '../db.js';
|
|
3
|
+
export const deleteSubviewSchema = z.object({
|
|
4
|
+
projectId: z.string().describe('UUID of the project'),
|
|
5
|
+
subviewId: z.string().describe('UUID of the subview to delete'),
|
|
6
|
+
});
|
|
7
|
+
export async function handleDeleteSubview(args) {
|
|
8
|
+
const ok = await deleteSubviewViaApi(args.projectId, args.subviewId);
|
|
9
|
+
if (!ok) {
|
|
10
|
+
return {
|
|
11
|
+
content: [{ type: 'text', text: `Subview or project not found: ${args.subviewId}` }],
|
|
12
|
+
isError: true,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
content: [{ type: 'text', text: `Deleted subview ${args.subviewId}` }],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=deleteSubview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deleteSubview.js","sourceRoot":"","sources":["../../src/tools/deleteSubview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/C,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,qBAAqB,CAAC;IACrD,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,+BAA+B,CAAC;CAChE,CAAC,CAAC;AAEH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,IAAyC;IACjF,MAAM,EAAE,GAAG,MAAM,mBAAmB,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;IAErE,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,iCAAiC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;YAC7F,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,mBAAmB,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC;KAChF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const generateSpecSchema: z.ZodObject<{
|
|
3
|
+
projectId: z.ZodString;
|
|
4
|
+
format: z.ZodDefault<z.ZodEnum<["markdown", "json"]>>;
|
|
5
|
+
sections: z.ZodOptional<z.ZodArray<z.ZodEnum<["db_schema", "api_contracts", "component_tree", "validation_rules", "all"]>, "many">>;
|
|
6
|
+
}, "strip", z.ZodTypeAny, {
|
|
7
|
+
projectId: string;
|
|
8
|
+
format: "markdown" | "json";
|
|
9
|
+
sections?: ("db_schema" | "api_contracts" | "component_tree" | "validation_rules" | "all")[] | undefined;
|
|
10
|
+
}, {
|
|
11
|
+
projectId: string;
|
|
12
|
+
format?: "markdown" | "json" | undefined;
|
|
13
|
+
sections?: ("db_schema" | "api_contracts" | "component_tree" | "validation_rules" | "all")[] | undefined;
|
|
14
|
+
}>;
|
|
15
|
+
export declare function handleGenerateSpec(args: z.infer<typeof generateSpecSchema>): Promise<{
|
|
16
|
+
content: {
|
|
17
|
+
type: "text";
|
|
18
|
+
text: string;
|
|
19
|
+
}[];
|
|
20
|
+
isError: boolean;
|
|
21
|
+
} | {
|
|
22
|
+
content: {
|
|
23
|
+
type: "text";
|
|
24
|
+
text: string;
|
|
25
|
+
}[];
|
|
26
|
+
}>;
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { getProject } from '../db.js';
|
|
3
|
+
import { handleGetYaml } from './getYaml.js';
|
|
4
|
+
import YAML from 'yaml';
|
|
5
|
+
export const generateSpecSchema = z.object({
|
|
6
|
+
projectId: z.string().describe('UUID of the project to generate spec for'),
|
|
7
|
+
format: z.enum(['markdown', 'json']).default('markdown').describe('Output format'),
|
|
8
|
+
sections: z
|
|
9
|
+
.array(z.enum(['db_schema', 'api_contracts', 'component_tree', 'validation_rules', 'all']))
|
|
10
|
+
.optional()
|
|
11
|
+
.describe('Sections to include (default: all)')
|
|
12
|
+
});
|
|
13
|
+
export async function handleGenerateSpec(args) {
|
|
14
|
+
const project = await getProject(args.projectId);
|
|
15
|
+
if (!project) {
|
|
16
|
+
return {
|
|
17
|
+
content: [{ type: 'text', text: `Project not found: ${args.projectId}` }],
|
|
18
|
+
isError: true
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
// Get YAML via existing tool
|
|
22
|
+
const yamlResult = await handleGetYaml({ projectId: args.projectId });
|
|
23
|
+
if (yamlResult.isError) {
|
|
24
|
+
return yamlResult;
|
|
25
|
+
}
|
|
26
|
+
const yamlContent = yamlResult.content[0].text;
|
|
27
|
+
const parsed = YAML.parse(yamlContent);
|
|
28
|
+
// Determine which sections to include
|
|
29
|
+
const sections = args.sections ?? ['all'];
|
|
30
|
+
const includeAll = sections.includes('all');
|
|
31
|
+
// Generate spec
|
|
32
|
+
const spec = args.format === 'markdown'
|
|
33
|
+
? generateMarkdownSpec(project.name, parsed, {
|
|
34
|
+
db_schema: includeAll || sections.includes('db_schema'),
|
|
35
|
+
api_contracts: includeAll || sections.includes('api_contracts'),
|
|
36
|
+
component_tree: includeAll || sections.includes('component_tree'),
|
|
37
|
+
validation_rules: includeAll || sections.includes('validation_rules')
|
|
38
|
+
})
|
|
39
|
+
: generateJsonSpec(project.name, parsed);
|
|
40
|
+
return {
|
|
41
|
+
content: [{ type: 'text', text: spec }]
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// ─── Markdown Spec Generation ───────────────────────────────────
|
|
45
|
+
function generateMarkdownSpec(projectName, yaml, sections) {
|
|
46
|
+
const lines = [];
|
|
47
|
+
// Header
|
|
48
|
+
lines.push(`# ${projectName} - Technical Specification`);
|
|
49
|
+
lines.push(``);
|
|
50
|
+
lines.push(`**Generated:** ${new Date().toISOString().split('T')[0]}`);
|
|
51
|
+
lines.push(`**FlowSpec Version:** ${yaml.version || '1.2.0'}`);
|
|
52
|
+
lines.push(``);
|
|
53
|
+
lines.push(`---`);
|
|
54
|
+
lines.push(``);
|
|
55
|
+
// Overview
|
|
56
|
+
const dataPoints = yaml.dataPoints || [];
|
|
57
|
+
const components = yaml.components || [];
|
|
58
|
+
const transforms = yaml.transforms || [];
|
|
59
|
+
const tables = yaml.tables || [];
|
|
60
|
+
const screens = yaml.screens || [];
|
|
61
|
+
lines.push(`## Overview`);
|
|
62
|
+
lines.push(``);
|
|
63
|
+
lines.push(`This specification describes the data architecture for **${projectName}**.`);
|
|
64
|
+
lines.push(``);
|
|
65
|
+
lines.push(`- **DataPoints:** ${dataPoints.length} data elements`);
|
|
66
|
+
lines.push(`- **Components:** ${components.length} UI components`);
|
|
67
|
+
lines.push(`- **Transforms:** ${transforms.length} business logic functions`);
|
|
68
|
+
lines.push(`- **Tables:** ${tables.length} database tables/APIs`);
|
|
69
|
+
lines.push(`- **Screens:** ${screens.length} wireframe screens`);
|
|
70
|
+
lines.push(``);
|
|
71
|
+
lines.push(`---`);
|
|
72
|
+
lines.push(``);
|
|
73
|
+
// DataPoints
|
|
74
|
+
lines.push(`## DataPoints`);
|
|
75
|
+
lines.push(``);
|
|
76
|
+
if (dataPoints.length === 0) {
|
|
77
|
+
lines.push(`*No data points defined.*`);
|
|
78
|
+
lines.push(``);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
lines.push(`| Label | Type | Source | Constraints | Source Definition |`);
|
|
82
|
+
lines.push(`|-------|------|--------|-------------|-------------------|`);
|
|
83
|
+
for (const dp of dataPoints) {
|
|
84
|
+
const label = dp.label || dp.id;
|
|
85
|
+
const type = dp.type || 'string';
|
|
86
|
+
const source = dp.source || 'inferred';
|
|
87
|
+
const constraints = dp.constraints && dp.constraints.length > 0 ? dp.constraints.join(', ') : '-';
|
|
88
|
+
const sourceDef = (dp.sourceDefinition || '').replace(/\|/g, '\\|');
|
|
89
|
+
lines.push(`| ${label} | ${type} | ${source} | ${constraints} | ${sourceDef} |`);
|
|
90
|
+
}
|
|
91
|
+
lines.push(``);
|
|
92
|
+
// Group by source
|
|
93
|
+
const captured = dataPoints.filter((dp) => dp.source === 'captured');
|
|
94
|
+
const inferred = dataPoints.filter((dp) => dp.source === 'inferred');
|
|
95
|
+
lines.push(`**Captured DataPoints (${captured.length}):** User inputs, form fields`);
|
|
96
|
+
lines.push(`**Inferred DataPoints (${inferred.length}):** Computed values, API responses, database queries`);
|
|
97
|
+
lines.push(``);
|
|
98
|
+
}
|
|
99
|
+
lines.push(`---`);
|
|
100
|
+
lines.push(``);
|
|
101
|
+
// Components
|
|
102
|
+
lines.push(`## Components`);
|
|
103
|
+
lines.push(``);
|
|
104
|
+
if (components.length === 0) {
|
|
105
|
+
lines.push(`*No components defined.*`);
|
|
106
|
+
lines.push(``);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
lines.push(`| Label | Displays | Captures | Wireframe |`);
|
|
110
|
+
lines.push(`|-------|----------|----------|-----------|`);
|
|
111
|
+
for (const comp of components) {
|
|
112
|
+
const label = comp.label || comp.id;
|
|
113
|
+
const displays = comp.displays?.join(', ') || '-';
|
|
114
|
+
const captures = comp.captures?.join(', ') || '-';
|
|
115
|
+
const wireframe = comp.wireframeRef || '-';
|
|
116
|
+
lines.push(`| ${label} | ${displays} | ${captures} | ${wireframe} |`);
|
|
117
|
+
}
|
|
118
|
+
lines.push(``);
|
|
119
|
+
}
|
|
120
|
+
lines.push(`---`);
|
|
121
|
+
lines.push(``);
|
|
122
|
+
// Transforms
|
|
123
|
+
lines.push(`## Transforms`);
|
|
124
|
+
lines.push(``);
|
|
125
|
+
if (transforms.length === 0) {
|
|
126
|
+
lines.push(`*No transforms defined.*`);
|
|
127
|
+
lines.push(``);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
lines.push(`| Label | Type | Inputs | Outputs | Description |`);
|
|
131
|
+
lines.push(`|-------|------|--------|---------|-------------|`);
|
|
132
|
+
for (const tf of transforms) {
|
|
133
|
+
const label = tf.label || tf.id;
|
|
134
|
+
const type = tf.type || 'formula';
|
|
135
|
+
const inputs = tf.inputs?.join(', ') || '-';
|
|
136
|
+
const outputs = tf.outputs?.join(', ') || '-';
|
|
137
|
+
const description = (tf.description || '').replace(/\|/g, '\\|');
|
|
138
|
+
lines.push(`| ${label} | ${type} | ${inputs} | ${outputs} | ${description} |`);
|
|
139
|
+
}
|
|
140
|
+
lines.push(``);
|
|
141
|
+
// Group by type
|
|
142
|
+
const formulas = transforms.filter((tf) => tf.type === 'formula');
|
|
143
|
+
const validations = transforms.filter((tf) => tf.type === 'validation');
|
|
144
|
+
const workflows = transforms.filter((tf) => tf.type === 'workflow');
|
|
145
|
+
lines.push(`**Formula (${formulas.length}):** Calculations, aggregations`);
|
|
146
|
+
lines.push(`**Validation (${validations.length}):** Data checking, constraint enforcement`);
|
|
147
|
+
lines.push(`**Workflow (${workflows.length}):** Multi-step processes, integrations`);
|
|
148
|
+
lines.push(``);
|
|
149
|
+
}
|
|
150
|
+
lines.push(`---`);
|
|
151
|
+
lines.push(``);
|
|
152
|
+
// Database Schema (if requested)
|
|
153
|
+
if (sections.db_schema && tables.length > 0) {
|
|
154
|
+
lines.push(`## Database Schema`);
|
|
155
|
+
lines.push(``);
|
|
156
|
+
for (const table of tables) {
|
|
157
|
+
const label = table.label || table.id;
|
|
158
|
+
const sourceType = table.sourceType || 'database';
|
|
159
|
+
const endpoint = table.endpoint || '-';
|
|
160
|
+
lines.push(`### Table: \`${label}\``);
|
|
161
|
+
lines.push(``);
|
|
162
|
+
lines.push(`**Source:** ${sourceType} (${endpoint})`);
|
|
163
|
+
lines.push(``);
|
|
164
|
+
if (table.columns && table.columns.length > 0) {
|
|
165
|
+
lines.push(`| Column | Type |`);
|
|
166
|
+
lines.push(`|--------|------|`);
|
|
167
|
+
for (const col of table.columns) {
|
|
168
|
+
lines.push(`| ${col.name} | ${col.type} |`);
|
|
169
|
+
}
|
|
170
|
+
lines.push(``);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
lines.push(`---`);
|
|
174
|
+
lines.push(``);
|
|
175
|
+
}
|
|
176
|
+
// API Contracts (if requested)
|
|
177
|
+
if (sections.api_contracts) {
|
|
178
|
+
lines.push(`## API Contracts`);
|
|
179
|
+
lines.push(``);
|
|
180
|
+
// Infer API endpoints from inferred DataPoints
|
|
181
|
+
const apiDataPoints = dataPoints.filter((dp) => dp.source === 'inferred' && dp.sourceDefinition && dp.sourceDefinition.includes('API'));
|
|
182
|
+
if (apiDataPoints.length === 0) {
|
|
183
|
+
lines.push(`*No API endpoints identified.*`);
|
|
184
|
+
lines.push(``);
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
lines.push(`Suggested API endpoints based on inferred DataPoints:`);
|
|
188
|
+
lines.push(``);
|
|
189
|
+
for (const dp of apiDataPoints) {
|
|
190
|
+
const label = dp.label || dp.id;
|
|
191
|
+
const method = dp.sourceDefinition.includes('POST') ? 'POST' : 'GET';
|
|
192
|
+
const endpoint = `/api/${label.toLowerCase().replace(/\s+/g, '-')}`;
|
|
193
|
+
lines.push(`### ${method} ${endpoint}`);
|
|
194
|
+
lines.push(``);
|
|
195
|
+
lines.push(`**Returns:** ${label} (${dp.type})`);
|
|
196
|
+
lines.push(`**Source:** ${dp.sourceDefinition}`);
|
|
197
|
+
lines.push(``);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
lines.push(`---`);
|
|
201
|
+
lines.push(``);
|
|
202
|
+
}
|
|
203
|
+
// Component Tree (if requested)
|
|
204
|
+
if (sections.component_tree && components.length > 0) {
|
|
205
|
+
lines.push(`## Component Hierarchy`);
|
|
206
|
+
lines.push(``);
|
|
207
|
+
// Simple tree based on wireframe references
|
|
208
|
+
const screenGroups = new Map();
|
|
209
|
+
for (const comp of components) {
|
|
210
|
+
const screen = comp.wireframeRef || 'No Screen';
|
|
211
|
+
const group = screenGroups.get(screen) || [];
|
|
212
|
+
group.push(comp);
|
|
213
|
+
screenGroups.set(screen, group);
|
|
214
|
+
}
|
|
215
|
+
for (const [screen, comps] of screenGroups) {
|
|
216
|
+
lines.push(`### ${screen}`);
|
|
217
|
+
lines.push(``);
|
|
218
|
+
for (const comp of comps) {
|
|
219
|
+
const label = comp.label || comp.id;
|
|
220
|
+
lines.push(`- **${label}**`);
|
|
221
|
+
if (comp.displays && comp.displays.length > 0) {
|
|
222
|
+
lines.push(` - Displays: ${comp.displays.join(', ')}`);
|
|
223
|
+
}
|
|
224
|
+
if (comp.captures && comp.captures.length > 0) {
|
|
225
|
+
lines.push(` - Captures: ${comp.captures.join(', ')}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
lines.push(``);
|
|
229
|
+
}
|
|
230
|
+
lines.push(`---`);
|
|
231
|
+
lines.push(``);
|
|
232
|
+
}
|
|
233
|
+
// Validation Rules (if requested)
|
|
234
|
+
if (sections.validation_rules) {
|
|
235
|
+
lines.push(`## Validation Rules`);
|
|
236
|
+
lines.push(``);
|
|
237
|
+
// Extract constraints from DataPoints
|
|
238
|
+
const constrainedDataPoints = dataPoints.filter((dp) => dp.constraints && dp.constraints.length > 0);
|
|
239
|
+
if (constrainedDataPoints.length === 0) {
|
|
240
|
+
lines.push(`*No validation rules defined.*`);
|
|
241
|
+
lines.push(``);
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
lines.push(`| DataPoint | Constraints |`);
|
|
245
|
+
lines.push(`|-----------|-------------|`);
|
|
246
|
+
for (const dp of constrainedDataPoints) {
|
|
247
|
+
const label = dp.label || dp.id;
|
|
248
|
+
const constraints = dp.constraints.join(', ');
|
|
249
|
+
lines.push(`| ${label} | ${constraints} |`);
|
|
250
|
+
}
|
|
251
|
+
lines.push(``);
|
|
252
|
+
}
|
|
253
|
+
// Extract validation transforms
|
|
254
|
+
const validationTransforms = transforms.filter((tf) => tf.type === 'validation');
|
|
255
|
+
if (validationTransforms.length > 0) {
|
|
256
|
+
lines.push(`### Validation Transforms`);
|
|
257
|
+
lines.push(``);
|
|
258
|
+
for (const tf of validationTransforms) {
|
|
259
|
+
const label = tf.label || tf.id;
|
|
260
|
+
const inputs = tf.inputs?.join(', ') || '-';
|
|
261
|
+
const description = tf.description || 'No description';
|
|
262
|
+
lines.push(`- **${label}**`);
|
|
263
|
+
lines.push(` - Validates: ${inputs}`);
|
|
264
|
+
lines.push(` - Logic: ${description}`);
|
|
265
|
+
lines.push(``);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
lines.push(`---`);
|
|
269
|
+
lines.push(``);
|
|
270
|
+
}
|
|
271
|
+
// Implementation Recommendations
|
|
272
|
+
lines.push(`## Implementation Recommendations`);
|
|
273
|
+
lines.push(``);
|
|
274
|
+
lines.push(`1. **Database Schema**`);
|
|
275
|
+
lines.push(` - Create tables: ${tables.map((t) => t.label).join(', ')}`);
|
|
276
|
+
lines.push(` - Set up migrations and seed data`);
|
|
277
|
+
lines.push(``);
|
|
278
|
+
lines.push(`2. **API Contracts**`);
|
|
279
|
+
lines.push(` - Implement endpoints for inferred DataPoints (${dataPoints.filter((dp) => dp.source === 'inferred').length} endpoints)`);
|
|
280
|
+
lines.push(` - Add authentication and authorization`);
|
|
281
|
+
lines.push(``);
|
|
282
|
+
lines.push(`3. **Component Structure**`);
|
|
283
|
+
lines.push(` - Build ${components.length} UI components`);
|
|
284
|
+
lines.push(` - Connect data flows (displays/captures)`);
|
|
285
|
+
lines.push(``);
|
|
286
|
+
lines.push(`4. **Validation Rules**`);
|
|
287
|
+
lines.push(` - Apply constraints at both client and server`);
|
|
288
|
+
lines.push(` - Implement ${transforms.filter((tf) => tf.type === 'validation').length} validation transforms`);
|
|
289
|
+
lines.push(``);
|
|
290
|
+
lines.push(`5. **Business Logic**`);
|
|
291
|
+
lines.push(` - Implement ${transforms.length} transforms`);
|
|
292
|
+
lines.push(` - Test all data flows and edge cases`);
|
|
293
|
+
lines.push(``);
|
|
294
|
+
lines.push(`---`);
|
|
295
|
+
lines.push(``);
|
|
296
|
+
// Quality Metrics (from project analysis)
|
|
297
|
+
const nodes = project.canvas_state?.nodes || [];
|
|
298
|
+
const edges = project.canvas_state?.edges || [];
|
|
299
|
+
lines.push(`## Quality Metrics`);
|
|
300
|
+
lines.push(``);
|
|
301
|
+
lines.push(`- **Total nodes:** ${nodes.length}`);
|
|
302
|
+
lines.push(`- **Total edges:** ${edges.length}`);
|
|
303
|
+
lines.push(`- **Screens:** ${screens.length}`);
|
|
304
|
+
lines.push(`- **Captured DataPoints:** ${dataPoints.filter((dp) => dp.source === 'captured').length}`);
|
|
305
|
+
lines.push(`- **Inferred DataPoints:** ${dataPoints.filter((dp) => dp.source === 'inferred').length}`);
|
|
306
|
+
lines.push(``);
|
|
307
|
+
lines.push(`✅ **Ready for implementation in Claude Code via \`/spec\` skill**`);
|
|
308
|
+
lines.push(``);
|
|
309
|
+
// Footer
|
|
310
|
+
lines.push(`---`);
|
|
311
|
+
lines.push(``);
|
|
312
|
+
lines.push(`*Generated by FlowSpec MCP Server v3.0.0*`);
|
|
313
|
+
return lines.join('\n');
|
|
314
|
+
}
|
|
315
|
+
// ─── JSON Spec Generation ───────────────────────────────────────
|
|
316
|
+
function generateJsonSpec(projectName, yaml) {
|
|
317
|
+
const spec = {
|
|
318
|
+
projectName: projectName,
|
|
319
|
+
generated: new Date().toISOString(),
|
|
320
|
+
version: yaml.version || '1.2.0',
|
|
321
|
+
summary: {
|
|
322
|
+
dataPoints: yaml.dataPoints?.length || 0,
|
|
323
|
+
components: yaml.components?.length || 0,
|
|
324
|
+
transforms: yaml.transforms?.length || 0,
|
|
325
|
+
tables: yaml.tables?.length || 0,
|
|
326
|
+
screens: yaml.screens?.length || 0
|
|
327
|
+
},
|
|
328
|
+
dataPoints: yaml.dataPoints || [],
|
|
329
|
+
components: yaml.components || [],
|
|
330
|
+
transforms: yaml.transforms || [],
|
|
331
|
+
tables: yaml.tables || [],
|
|
332
|
+
screens: yaml.screens || []
|
|
333
|
+
};
|
|
334
|
+
return JSON.stringify(spec, null, 2);
|
|
335
|
+
}
|
|
336
|
+
//# sourceMappingURL=generateSpec.js.map
|