@deepstorm/cli 0.1.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 (148) hide show
  1. package/README.md +72 -0
  2. package/dist/agents/reef-inspect-figma.md +77 -0
  3. package/dist/agents/reef-review-backend.md.tmpl +112 -0
  4. package/dist/agents/reef-review-frontend.md.tmpl +78 -0
  5. package/dist/agents/reef-review-infra.md +47 -0
  6. package/dist/agents/reef-review-security.md.tmpl +80 -0
  7. package/dist/agents/reef-scope-analysis.md +64 -0
  8. package/dist/build-registry.js +375 -0
  9. package/dist/cli.js +8581 -0
  10. package/dist/config-schema.json +133 -0
  11. package/dist/env-examples/context7.env-example +19 -0
  12. package/dist/env-examples/feishu-wiki.env-example +16 -0
  13. package/dist/env-examples/figma.env-example +16 -0
  14. package/dist/env-examples/github.env-example +20 -0
  15. package/dist/env-examples/jira.env-example +20 -0
  16. package/dist/hooks/mcp-hook.sh +77 -0
  17. package/dist/hooks/reef-auto-format.sh.tmpl +72 -0
  18. package/dist/hooks/reef-block-dangerous.sh +70 -0
  19. package/dist/hooks/reef-hooks.json +72 -0
  20. package/dist/hooks/reef-intent-detect.sh +129 -0
  21. package/dist/hooks/reef-protect-files.sh +55 -0
  22. package/dist/hooks/reef-run-tests.sh +84 -0
  23. package/dist/hooks/reef-scope-check.sh +386 -0
  24. package/dist/hooks/reef-scope-ci.sh +28 -0
  25. package/dist/hooks/reef-scope-gate.sh +115 -0
  26. package/dist/hooks/reef-scope-pre-commit.sh.tmpl +28 -0
  27. package/dist/hooks/reef-scope-setup.sh +204 -0
  28. package/dist/hooks/reef-scope-split.sh +203 -0
  29. package/dist/hooks/sweep-hooks.json +14 -0
  30. package/dist/hooks/sweep-mcp-hook.sh +77 -0
  31. package/dist/hooks/tide-hooks.json +14 -0
  32. package/dist/hooks/tide-session-preload.sh +17 -0
  33. package/dist/mcp/code-hosting/github.json +20 -0
  34. package/dist/mcp/design-tools/figma.json +19 -0
  35. package/dist/mcp/docs-reference/context7.json +28 -0
  36. package/dist/mcp/e2e-testing/playwright.json +13 -0
  37. package/dist/mcp/knowledge-base/feishu-wiki.json +19 -0
  38. package/dist/mcp/project-management/jira.json +27 -0
  39. package/dist/mcp-skills/deepflow-mcp-feishu-wiki-read/SKILL.md +65 -0
  40. package/dist/mcp-skills/deepflow-mcp-feishu-wiki-write/SKILL.md +63 -0
  41. package/dist/mcp-skills/deepflow-mcp-figma-read/SKILL.md +98 -0
  42. package/dist/mcp-skills/deepflow-mcp-github-read/SKILL.md +62 -0
  43. package/dist/mcp-skills/deepflow-mcp-github-write/SKILL.md +63 -0
  44. package/dist/mcp-skills/deepflow-mcp-jira-read/SKILL.md +80 -0
  45. package/dist/mcp-skills/deepflow-mcp-jira-write/SKILL.md +74 -0
  46. package/dist/mcp-skills/deepflow-mcp-playwright-read/SKILL.md +79 -0
  47. package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-read/SKILL.md +65 -0
  48. package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-write/SKILL.md +63 -0
  49. package/dist/mcp-skills/deepstorm-mcp-figma-read/SKILL.md +98 -0
  50. package/dist/mcp-skills/deepstorm-mcp-github-read/SKILL.md +62 -0
  51. package/dist/mcp-skills/deepstorm-mcp-github-write/SKILL.md +63 -0
  52. package/dist/mcp-skills/deepstorm-mcp-jira-read/SKILL.md +80 -0
  53. package/dist/mcp-skills/deepstorm-mcp-jira-write/SKILL.md +74 -0
  54. package/dist/mcp-skills/deepstorm-mcp-playwright-read/SKILL.md +79 -0
  55. package/dist/registry.json +818 -0
  56. package/dist/skills/atoll-ops/SKILL.md +46 -0
  57. package/dist/skills/reef-commit/SKILL.md +127 -0
  58. package/dist/skills/reef-gen-backend/SKILL.md.tmpl +87 -0
  59. package/dist/skills/reef-gen-backend/variants/java/steps.md +28 -0
  60. package/dist/skills/reef-gen-backend/variants/python/steps.md +70 -0
  61. package/dist/skills/reef-gen-frontend/SKILL.md.tmpl +83 -0
  62. package/dist/skills/reef-gen-frontend/variants/angular/steps.md +30 -0
  63. package/dist/skills/reef-harden/EXAMPLES.md +89 -0
  64. package/dist/skills/reef-harden/SKILL.md +136 -0
  65. package/dist/skills/reef-pr/SKILL.md +97 -0
  66. package/dist/skills/reef-review/SKILL.md.tmpl +107 -0
  67. package/dist/skills/reef-scope/SKILL.md +134 -0
  68. package/dist/skills/reef-start/SKILL.md.tmpl +562 -0
  69. package/dist/skills/reef-start/references/jira-start-subagent.md +60 -0
  70. package/dist/skills/reef-style-backend/SKILL.md.tmpl +134 -0
  71. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/chat-client.md +96 -0
  72. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/rag.md +94 -0
  73. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/structured-output.md +62 -0
  74. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/tool-calling.md +68 -0
  75. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/quick-reference.md +220 -0
  76. package/dist/skills/reef-style-backend/fragments/java/api-spec/quick-reference.md +148 -0
  77. package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/examples/database-migration.md +131 -0
  78. package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/quick-reference.md +103 -0
  79. package/dist/skills/reef-style-backend/fragments/java/dependency-management/quick-reference.md +119 -0
  80. package/dist/skills/reef-style-backend/fragments/java/exception-handling/examples/error-code-enum.md +101 -0
  81. package/dist/skills/reef-style-backend/fragments/java/exception-handling/quick-reference.md +181 -0
  82. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/controller.md +95 -0
  83. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/dto-mapper.md +121 -0
  84. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/infrastructure.md +179 -0
  85. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/service-entity.md +202 -0
  86. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/testing.md +107 -0
  87. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/quick-reference.md +83 -0
  88. package/dist/skills/reef-style-backend/fragments/java/orm/hibernate/quick-reference.md +150 -0
  89. package/dist/skills/reef-style-backend/fragments/java/security-redlines/quick-reference.md +197 -0
  90. package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/examples/user-repository-test.md +69 -0
  91. package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/quick-reference.md +101 -0
  92. package/dist/skills/reef-style-backend/fragments/java/test/junit5/examples/user-service-test.md +61 -0
  93. package/dist/skills/reef-style-backend/fragments/java/test/junit5/quick-reference.md +100 -0
  94. package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/examples/user-controller-test.md +61 -0
  95. package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/quick-reference.md +85 -0
  96. package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/examples/user-service-integration-test.md +56 -0
  97. package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/quick-reference.md +83 -0
  98. package/dist/skills/reef-style-backend/fragments/python/alembic-migration/quick-reference.md +77 -0
  99. package/dist/skills/reef-style-backend/fragments/python/api-spec/quick-reference.md +164 -0
  100. package/dist/skills/reef-style-backend/fragments/python/dependency-management/quick-reference.md +139 -0
  101. package/dist/skills/reef-style-backend/fragments/python/exception-handling/quick-reference.md +177 -0
  102. package/dist/skills/reef-style-backend/fragments/python/fastapi-quick-reference/quick-reference.md +101 -0
  103. package/dist/skills/reef-style-backend/fragments/python/langchain/quick-reference.md +135 -0
  104. package/dist/skills/reef-style-backend/fragments/python/pytest-testing/quick-reference.md +111 -0
  105. package/dist/skills/reef-style-backend/fragments/python/ruff-mypy-toolchain/quick-reference.md +83 -0
  106. package/dist/skills/reef-style-backend/fragments/python/security-redlines/quick-reference.md +207 -0
  107. package/dist/skills/reef-style-backend/fragments/python/sqlalchemy-orm/quick-reference.md +91 -0
  108. package/dist/skills/reef-style-backend/variants/java/examples/code-wrapping.md +227 -0
  109. package/dist/skills/reef-style-backend/variants/java/examples/contributor-pattern.md +97 -0
  110. package/dist/skills/reef-style-backend/variants/java/quick-reference.md +117 -0
  111. package/dist/skills/reef-style-backend/variants/python/examples/crud-router.md +71 -0
  112. package/dist/skills/reef-style-backend/variants/python/examples/pydantic-schema.md +45 -0
  113. package/dist/skills/reef-style-backend/variants/python/examples/pytest-fixture.md +60 -0
  114. package/dist/skills/reef-style-backend/variants/python/examples/sqlalchemy-model.md +31 -0
  115. package/dist/skills/reef-style-backend/variants/python/quick-reference.md +111 -0
  116. package/dist/skills/reef-style-frontend/SKILL.md.tmpl +70 -0
  117. package/dist/skills/reef-style-frontend/fragments/css/tailwind/quick-reference.md +79 -0
  118. package/dist/skills/reef-style-frontend/fragments/test/vitest/examples/testing.md +150 -0
  119. package/dist/skills/reef-style-frontend/fragments/test/vitest/quick-reference.md +141 -0
  120. package/dist/skills/reef-style-frontend/fragments/ts-config/strict/quick-reference.md +112 -0
  121. package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/examples/ui-components.md +134 -0
  122. package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/quick-reference.md +66 -0
  123. package/dist/skills/reef-style-frontend/variants/angular/examples/code-wrapping.md +252 -0
  124. package/dist/skills/reef-style-frontend/variants/angular/examples/component-types-pipes.md +56 -0
  125. package/dist/skills/reef-style-frontend/variants/angular/examples/entity-types.md +100 -0
  126. package/dist/skills/reef-style-frontend/variants/angular/examples/forms-layer.md +119 -0
  127. package/dist/skills/reef-style-frontend/variants/angular/examples/service-routing.md +95 -0
  128. package/dist/skills/reef-style-frontend/variants/angular/quick-reference.md +100 -0
  129. package/dist/skills/reef-testcase/SKILL.md +138 -0
  130. package/dist/skills/reef-testcase/references/coverage-dimensions.md +99 -0
  131. package/dist/skills/reef-testcase/references/test-case-template.md +63 -0
  132. package/dist/skills/sweep-init/SKILL.md +286 -0
  133. package/dist/skills/sweep-init/scripts/flow-selector.mjs +305 -0
  134. package/dist/skills/sweep-plan/SKILL.md.tmpl +312 -0
  135. package/dist/skills/sweep-plan/references/test-flow-template.md +46 -0
  136. package/dist/skills/sweep-run/SKILL.md +437 -0
  137. package/dist/skills/sweep-run/scripts/env-manager.mjs +208 -0
  138. package/dist/skills/sweep-run/scripts/flow-parser.mjs +329 -0
  139. package/dist/skills/sweep-run/scripts/flow-selector.mjs +483 -0
  140. package/dist/skills/sweep-run/scripts/mcp-manager.mjs +208 -0
  141. package/dist/skills/sweep-run/scripts/spec-compiler.mjs +303 -0
  142. package/dist/skills/tide-discuss/SKILL.md.tmpl +449 -0
  143. package/dist/skills/tide-discuss/references/checklists.md +88 -0
  144. package/dist/skills/tide-discuss/references/data-format.md +237 -0
  145. package/dist/skills/tide-discuss/references/prd-template.md +134 -0
  146. package/dist/skills/tide-discuss/references/publish-flow.md +167 -0
  147. package/dist/skills/tide-discuss/references/role-prompts.md +105 -0
  148. package/package.json +38 -0
@@ -0,0 +1,329 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Flow Parser — 结构化解析 .flow.md 文件
5
+ *
6
+ * 读取 .flow.md 文件,提取 frontmatter、场景清单、Flow 步骤和验证点,
7
+ * 输出结构化 JSON 供下游编译器和执行器使用。
8
+ *
9
+ * Usage:
10
+ * node flow-parser.mjs <path-to-flow.md> # CLI: output JSON to stdout
11
+ * import { parseFlowMd } from './flow-parser.mjs' # API
12
+ */
13
+
14
+ import { readFileSync, existsSync } from 'node:fs';
15
+
16
+ // ── Regex patterns ────────────────────────────────────────────────
17
+ // Frontmatter
18
+ const FEATURE_NAME_RE = /^#\s+(?:E2E 测试流程[::]\s*)?(.+)$/m;
19
+ const SOURCE_RE = /\*\*来源[::]\*\*\s*(.+)/;
20
+ const CREATED_AT_RE = /\*\*创建时间[::]\*\*\s*(.+)/;
21
+
22
+ // Scenario table (Markdown table with | ID | 场景 | 类型 | 优先级 |)
23
+ // Matches rows after the header separator (|---|---|---)
24
+ const SCENARIO_TABLE_SECTION = /## 场景清单\s*\n(.*?)(?=\n## |\n*$)/s;
25
+ const SCENARIO_ROW_RE = /^\|\s*([A-Z0-9]+)\s*\|\s*(.+?)\s*\|\s*(.+?)\s*\|\s*(P[0-9])\s*\|/m;
26
+
27
+ // Flow sections
28
+ const FLOW_SECTION_RE = /## Flow:\s*(\S+)\s*-\s*(.+?)\s*\n/g;
29
+
30
+ // Sub-sections within a flow
31
+ const PRECONDITIONS_RE = /###\s*前置条件\s*\n([\s\S]*?)(?=\n###|\n*$)/;
32
+ const EXECUTION_STEPS_RE = /###\s*执行步骤\s*\n([\s\S]*?)(?=\n###|\n*$)/;
33
+ const ENV_REQUIREMENTS_RE = /###\s*环境要求\s*\n([\s\S]*?)(?=\n##|\n*$)/;
34
+
35
+ // Step lines: "N. description" with optional validation points
36
+ const STEP_LINE_RE = /^\s*(\d+)\.\s+(.+?)(?:\s*\n|$)/m;
37
+ const VALIDATION_RE = /✅\s*验证点[::]\s*(.+?)(?=\n|$)/g;
38
+
39
+ // ── Parsing functions ─────────────────────────────────────────────
40
+
41
+ /**
42
+ * Parse frontmatter from .flow.md content.
43
+ */
44
+ function parseFrontmatter(content) {
45
+ const featureName = (content.match(FEATURE_NAME_RE)?.[1] || '').trim();
46
+ const source = (content.match(SOURCE_RE)?.[1] || '').trim();
47
+ const createdAt = (content.match(CREATED_AT_RE)?.[1] || '').trim();
48
+ return { featureName, source, createdAt };
49
+ }
50
+
51
+ /**
52
+ * Parse the scenario table from .flow.md content.
53
+ */
54
+ function parseScenarios(content) {
55
+ const sectionMatch = content.match(SCENARIO_TABLE_SECTION);
56
+ if (!sectionMatch) return [];
57
+
58
+ const tableText = sectionMatch[1];
59
+ const scenarios = [];
60
+ const lines = tableText.split('\n');
61
+
62
+ let inBody = false;
63
+ for (const line of lines) {
64
+ // Skip header and separator
65
+ if (/^\|[-]+\|/.test(line) && line.includes('---')) { inBody = true; continue; }
66
+ if (!inBody) continue;
67
+
68
+ const m = line.match(SCENARIO_ROW_RE);
69
+ if (m) {
70
+ scenarios.push({
71
+ id: m[1],
72
+ scenario: m[2].trim(),
73
+ type: m[3].trim(),
74
+ priority: m[4],
75
+ });
76
+ }
77
+ }
78
+
79
+ return scenarios;
80
+ }
81
+
82
+ /**
83
+ * Parse validation points from a step block.
84
+ */
85
+ function parseValidations(text) {
86
+ const validations = [];
87
+ let m;
88
+ while ((m = VALIDATION_RE.exec(text)) !== null) {
89
+ validations.push(m[1].trim());
90
+ }
91
+ return validations;
92
+ }
93
+
94
+ /**
95
+ * Parse steps within an execution steps block.
96
+ */
97
+ function parseSteps(stepsContent) {
98
+ if (!stepsContent) return [];
99
+
100
+ const steps = [];
101
+ // Split by numbered lines
102
+ const lines = stepsContent.split('\n');
103
+ let currentOrder = null;
104
+ let currentDesc = '';
105
+ let currentBlock = '';
106
+
107
+ for (const line of lines) {
108
+ const stepMatch = line.match(STEP_LINE_RE);
109
+ if (stepMatch) {
110
+ // Save previous step
111
+ if (currentOrder !== null) {
112
+ steps.push({
113
+ order: currentOrder,
114
+ description: currentDesc.trim(),
115
+ validations: parseValidations(currentBlock),
116
+ });
117
+ }
118
+ currentOrder = parseInt(stepMatch[1], 10);
119
+ currentDesc = stepMatch[2].trim();
120
+ currentBlock = line;
121
+ } else if (currentOrder !== null) {
122
+ currentBlock += '\n' + line;
123
+ }
124
+ }
125
+
126
+ // Save last step
127
+ if (currentOrder !== null) {
128
+ steps.push({
129
+ order: currentOrder,
130
+ description: currentDesc.trim(),
131
+ validations: parseValidations(currentBlock),
132
+ });
133
+ }
134
+
135
+ return steps;
136
+ }
137
+
138
+ /**
139
+ * Parse environment requirements.
140
+ */
141
+ function parseEnvRequirements(envContent) {
142
+ if (!envContent) return undefined;
143
+
144
+ const lines = envContent.trim().split('\n');
145
+ const env = {};
146
+
147
+ for (const line of lines) {
148
+ const trimmed = line.replace(/^-\s*/, '').trim();
149
+ if (trimmed.startsWith('目标环境')) {
150
+ const [, value] = trimmed.split(/[::]/);
151
+ if (value) env.targetEnv = value.trim();
152
+ }
153
+ }
154
+
155
+ return Object.keys(env).length > 0 ? env : undefined;
156
+ }
157
+
158
+ /**
159
+ * Parse all flows from .flow.md content.
160
+ */
161
+ function parseFlows(content) {
162
+ const flows = [];
163
+
164
+ // Split around each "## Flow:" heading
165
+ const flowHeadings = [...content.matchAll(FLOW_SECTION_RE)];
166
+ if (flowHeadings.length === 0) return flows;
167
+
168
+ for (let i = 0; i < flowHeadings.length; i++) {
169
+ const heading = flowHeadings[i];
170
+ const id = heading[1];
171
+ const title = heading[2].trim();
172
+
173
+ // Get content between this flow heading and the next, or end of file
174
+ const startIdx = heading.index + heading[0].length;
175
+ const endIdx = i + 1 < flowHeadings.length
176
+ ? flowHeadings[i + 1].index
177
+ : content.length;
178
+ const flowContent = content.slice(startIdx, endIdx);
179
+
180
+ const preconditionsMatch = flowContent.match(PRECONDITIONS_RE);
181
+ const stepsMatch = flowContent.match(EXECUTION_STEPS_RE);
182
+ const envMatch = flowContent.match(ENV_REQUIREMENTS_RE);
183
+
184
+ const flow = { id, title };
185
+
186
+ if (preconditionsMatch) {
187
+ const precondLines = preconditionsMatch[1]
188
+ .split('\n')
189
+ .map(l => l.replace(/^-\s*/, '').trim())
190
+ .filter(l => l.length > 0);
191
+ flow.preconditions = precondLines.join('\n');
192
+ }
193
+
194
+ if (stepsMatch) {
195
+ flow.steps = parseSteps(stepsMatch[1]);
196
+ } else {
197
+ flow.steps = [];
198
+ }
199
+
200
+ if (envMatch) {
201
+ flow.envRequirements = parseEnvRequirements(envMatch[1]);
202
+ }
203
+
204
+ flows.push(flow);
205
+ }
206
+
207
+ return flows;
208
+ }
209
+
210
+ // ── Public API ────────────────────────────────────────────────────
211
+
212
+ /**
213
+ * JSON Schema for FlowParseResult.
214
+ * Describes the output contract of the parser.
215
+ */
216
+ export const FlowParseResultSchema = {
217
+ $schema: 'http://json-schema.org/draft-07/schema#',
218
+ title: 'FlowParseResult',
219
+ description: 'Parsed .flow.md document structure',
220
+ type: 'object',
221
+ required: ['featureName', 'source', 'createdAt', 'scenarios', 'flows'],
222
+ properties: {
223
+ featureName: { type: 'string', description: '功能名称(从 # 标题提取)' },
224
+ source: { type: 'string', description: '来源(Issue/PRD 链接)' },
225
+ createdAt: { type: 'string', description: '创建时间' },
226
+ scenarios: {
227
+ type: 'array',
228
+ description: '场景清单',
229
+ items: {
230
+ type: 'object',
231
+ required: ['id', 'scenario', 'type', 'priority'],
232
+ properties: {
233
+ id: { type: 'string', pattern: '^[A-Z0-9]+$', description: '场景 ID(如 L01)' },
234
+ scenario: { type: 'string', description: '场景描述' },
235
+ type: { type: 'string', description: '场景类型(正常流程/边界条件/异常场景)' },
236
+ priority: { type: 'string', pattern: '^P[0-9]$', description: '优先级(P0-P3)' },
237
+ },
238
+ },
239
+ },
240
+ flows: {
241
+ type: 'array',
242
+ description: 'Flow 列表',
243
+ items: {
244
+ type: 'object',
245
+ required: ['id', 'title', 'steps'],
246
+ properties: {
247
+ id: { type: 'string', description: 'Flow ID' },
248
+ title: { type: 'string', description: 'Flow 标题' },
249
+ preconditions: { type: 'string', description: '前置条件描述' },
250
+ steps: {
251
+ type: 'array',
252
+ description: '执行步骤',
253
+ items: {
254
+ type: 'object',
255
+ required: ['order', 'description', 'validations'],
256
+ properties: {
257
+ order: { type: 'integer', description: '步骤序号' },
258
+ description: { type: 'string', description: '步骤描述' },
259
+ validations: {
260
+ type: 'array',
261
+ items: { type: 'string' },
262
+ description: '验证点列表',
263
+ },
264
+ },
265
+ },
266
+ },
267
+ envRequirements: {
268
+ type: 'object',
269
+ description: '环境要求',
270
+ properties: {
271
+ targetEnv: { type: 'string', description: '目标环境(test/staging/prod)' },
272
+ },
273
+ },
274
+ },
275
+ },
276
+ },
277
+ },
278
+ };
279
+
280
+ /**
281
+ * Parse a .flow.md file and return structured result.
282
+ * @param {string} filePath - Path to .flow.md file
283
+ * @returns {import('./types').FlowParseResult}
284
+ */
285
+ export function parseFlowMd(filePath) {
286
+ if (!existsSync(filePath)) {
287
+ throw new Error(`File not found: ${filePath}`);
288
+ }
289
+
290
+ const content = readFileSync(filePath, 'utf-8');
291
+ return parse(content);
292
+ }
293
+
294
+ /**
295
+ * Parse raw .flow.md content (no file I/O).
296
+ * @param {string} content - Raw .flow.md content
297
+ * @returns {import('./types').FlowParseResult}
298
+ */
299
+ export function parse(content) {
300
+ const frontmatter = parseFrontmatter(content);
301
+ const scenarios = parseScenarios(content);
302
+ const flows = parseFlows(content);
303
+
304
+ return {
305
+ featureName: frontmatter.featureName,
306
+ source: frontmatter.source,
307
+ createdAt: frontmatter.createdAt,
308
+ scenarios,
309
+ flows,
310
+ };
311
+ }
312
+
313
+ // ── CLI entry point ───────────────────────────────────────────────
314
+
315
+ if (process.argv[1] === import.meta.filename) {
316
+ const filePath = process.argv[2];
317
+ if (!filePath) {
318
+ console.error('Usage: node flow-parser.mjs <path-to-flow.md>');
319
+ process.exit(1);
320
+ }
321
+
322
+ try {
323
+ const result = parseFlowMd(filePath);
324
+ console.log(JSON.stringify(result, null, 2));
325
+ } catch (err) {
326
+ console.error(`Error: ${err.message}`);
327
+ process.exit(1);
328
+ }
329
+ }