@caiqueoak/flow 0.6.0 → 0.8.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 (173) hide show
  1. package/README.md +65 -43
  2. package/dist/artifacts/backlog.d.mts +16 -0
  3. package/dist/artifacts/backlog.d.mts.map +1 -0
  4. package/dist/artifacts/backlog.mjs +193 -0
  5. package/dist/artifacts/backlog.mjs.map +1 -0
  6. package/dist/artifacts/document.d.mts +6 -0
  7. package/dist/artifacts/document.d.mts.map +1 -0
  8. package/dist/artifacts/document.mjs +39 -0
  9. package/dist/artifacts/document.mjs.map +1 -0
  10. package/dist/artifacts/engineering.d.mts +5 -0
  11. package/dist/artifacts/engineering.d.mts.map +1 -0
  12. package/dist/artifacts/engineering.mjs +27 -0
  13. package/dist/artifacts/engineering.mjs.map +1 -0
  14. package/dist/artifacts/gates.d.mts +7 -0
  15. package/dist/artifacts/gates.d.mts.map +1 -0
  16. package/dist/artifacts/gates.mjs +42 -0
  17. package/dist/artifacts/gates.mjs.map +1 -0
  18. package/dist/artifacts/implementation-plan.d.mts +11 -0
  19. package/dist/artifacts/implementation-plan.d.mts.map +1 -0
  20. package/dist/artifacts/implementation-plan.mjs +30 -0
  21. package/dist/artifacts/implementation-plan.mjs.map +1 -0
  22. package/dist/artifacts/prd.d.mts +4 -0
  23. package/dist/artifacts/prd.d.mts.map +1 -0
  24. package/dist/artifacts/prd.mjs +13 -0
  25. package/dist/artifacts/prd.mjs.map +1 -0
  26. package/dist/artifacts/review.d.mts +11 -0
  27. package/dist/artifacts/review.d.mts.map +1 -0
  28. package/dist/artifacts/review.mjs +27 -0
  29. package/dist/artifacts/review.mjs.map +1 -0
  30. package/dist/artifacts/spec.d.mts +14 -0
  31. package/dist/artifacts/spec.d.mts.map +1 -0
  32. package/dist/artifacts/spec.mjs +50 -0
  33. package/dist/artifacts/spec.mjs.map +1 -0
  34. package/dist/artifacts/state.d.mts +44 -0
  35. package/dist/artifacts/state.d.mts.map +1 -0
  36. package/dist/artifacts/state.mjs +60 -0
  37. package/dist/artifacts/state.mjs.map +1 -0
  38. package/dist/artifacts/tasks.d.mts +10 -0
  39. package/dist/artifacts/tasks.d.mts.map +1 -0
  40. package/dist/artifacts/tasks.mjs +107 -0
  41. package/dist/artifacts/tasks.mjs.map +1 -0
  42. package/dist/artifacts/work-items.d.mts +21 -0
  43. package/dist/artifacts/work-items.d.mts.map +1 -0
  44. package/dist/artifacts/work-items.mjs +81 -0
  45. package/dist/artifacts/work-items.mjs.map +1 -0
  46. package/dist/cli/command-registry.d.mts +498 -0
  47. package/dist/cli/command-registry.d.mts.map +1 -0
  48. package/dist/cli/command-registry.mjs +230 -0
  49. package/dist/cli/command-registry.mjs.map +1 -0
  50. package/dist/cli.d.mts +3 -0
  51. package/dist/cli.d.mts.map +1 -0
  52. package/dist/cli.mjs +48 -0
  53. package/dist/cli.mjs.map +1 -0
  54. package/dist/commands/doctor.d.mts +21 -0
  55. package/dist/commands/doctor.d.mts.map +1 -0
  56. package/dist/commands/doctor.mjs +79 -0
  57. package/dist/commands/doctor.mjs.map +1 -0
  58. package/dist/commands/gates.d.mts +14 -0
  59. package/dist/commands/gates.d.mts.map +1 -0
  60. package/dist/commands/gates.mjs +136 -0
  61. package/dist/commands/gates.mjs.map +1 -0
  62. package/dist/commands/graph.d.mts +11 -0
  63. package/dist/commands/graph.d.mts.map +1 -0
  64. package/dist/commands/graph.mjs +78 -0
  65. package/dist/commands/graph.mjs.map +1 -0
  66. package/dist/commands/init.d.mts +6 -0
  67. package/dist/commands/init.d.mts.map +1 -0
  68. package/dist/commands/init.mjs +126 -0
  69. package/dist/commands/init.mjs.map +1 -0
  70. package/dist/commands/migrate.d.mts +27 -0
  71. package/dist/commands/migrate.d.mts.map +1 -0
  72. package/dist/commands/migrate.mjs +379 -0
  73. package/dist/commands/migrate.mjs.map +1 -0
  74. package/dist/commands/operations.d.mts +13 -0
  75. package/dist/commands/operations.d.mts.map +1 -0
  76. package/dist/commands/operations.mjs +340 -0
  77. package/dist/commands/operations.mjs.map +1 -0
  78. package/dist/commands/route.d.mts +12 -0
  79. package/dist/commands/route.d.mts.map +1 -0
  80. package/dist/commands/route.mjs +45 -0
  81. package/dist/commands/route.mjs.map +1 -0
  82. package/dist/commands/schemas.d.mts +5 -0
  83. package/dist/commands/schemas.d.mts.map +1 -0
  84. package/dist/commands/schemas.mjs +14 -0
  85. package/dist/commands/schemas.mjs.map +1 -0
  86. package/dist/commands/status.d.mts +4 -0
  87. package/dist/commands/status.d.mts.map +1 -0
  88. package/dist/commands/status.mjs +18 -0
  89. package/dist/commands/status.mjs.map +1 -0
  90. package/dist/commands/sync.d.mts +18 -0
  91. package/dist/commands/sync.d.mts.map +1 -0
  92. package/dist/commands/sync.mjs +29 -0
  93. package/dist/commands/sync.mjs.map +1 -0
  94. package/dist/commands/trace.d.mts +30 -0
  95. package/dist/commands/trace.d.mts.map +1 -0
  96. package/dist/commands/trace.mjs +111 -0
  97. package/dist/commands/trace.mjs.map +1 -0
  98. package/dist/commands/validate.d.mts +8 -0
  99. package/dist/commands/validate.d.mts.map +1 -0
  100. package/dist/commands/validate.mjs +82 -0
  101. package/dist/commands/validate.mjs.map +1 -0
  102. package/dist/domain/contracts.d.mts +203 -0
  103. package/dist/domain/contracts.d.mts.map +1 -0
  104. package/dist/domain/contracts.mjs +126 -0
  105. package/dist/domain/contracts.mjs.map +1 -0
  106. package/dist/domain/types.d.ts +45 -0
  107. package/dist/domain/types.d.ts.map +1 -0
  108. package/dist/domain/types.js +2 -0
  109. package/dist/domain/types.js.map +1 -0
  110. package/dist/entry.d.ts +3 -0
  111. package/dist/entry.d.ts.map +1 -0
  112. package/dist/entry.js +4 -0
  113. package/dist/entry.js.map +1 -0
  114. package/dist/shared/cli-io.d.mts +15 -0
  115. package/dist/shared/cli-io.d.mts.map +1 -0
  116. package/dist/shared/cli-io.mjs +83 -0
  117. package/dist/shared/cli-io.mjs.map +1 -0
  118. package/dist/shared/profiles.d.mts +17 -0
  119. package/dist/shared/profiles.d.mts.map +1 -0
  120. package/dist/shared/profiles.mjs +25 -0
  121. package/dist/shared/profiles.mjs.map +1 -0
  122. package/dist/shared/project-config.d.mts +21 -0
  123. package/dist/shared/project-config.d.mts.map +1 -0
  124. package/dist/shared/project-config.mjs +41 -0
  125. package/dist/shared/project-config.mjs.map +1 -0
  126. package/dist/shared/project-path.d.mts +3 -0
  127. package/dist/shared/project-path.d.mts.map +1 -0
  128. package/dist/shared/project-path.mjs +9 -0
  129. package/dist/shared/project-path.mjs.map +1 -0
  130. package/dist/shared/skill-installer.d.mts +2 -0
  131. package/dist/shared/skill-installer.d.mts.map +1 -0
  132. package/dist/shared/skill-installer.mjs +33 -0
  133. package/dist/shared/skill-installer.mjs.map +1 -0
  134. package/package.json +16 -8
  135. package/schemas/backlog.schema.json +85 -0
  136. package/schemas/config.schema.json +29 -0
  137. package/schemas/gates.schema.json +17 -0
  138. package/schemas/review.schema.json +28 -0
  139. package/schemas/tasks.schema.json +66 -0
  140. package/skills/flow/SKILL.md +10 -11
  141. package/skills/flow/build/step-01-execute-task.md +3 -3
  142. package/skills/flow/discovery/bmad-comparison.md +5 -0
  143. package/skills/flow/discovery/step-01-project.md +8 -3
  144. package/skills/flow/engineering/profiles/readability-first.md +1 -0
  145. package/skills/flow/engineering/step-02-synthesize.md +2 -2
  146. package/skills/flow/invariants.md +4 -4
  147. package/skills/flow/planning/step-01-create-tasks.md +3 -0
  148. package/skills/flow/planning/step-01-plan-work-item.md +2 -2
  149. package/skills/flow/planning/step-02-prepare-plan.md +1 -1
  150. package/skills/flow/review/step-01-review-work-item.md +2 -2
  151. package/skills/flow/specification/step-01-deepen-spec.md +7 -0
  152. package/src/artifacts/backlog.mjs +0 -157
  153. package/src/artifacts/document.mjs +0 -30
  154. package/src/artifacts/engineering.mjs +0 -26
  155. package/src/artifacts/gates.mjs +0 -28
  156. package/src/artifacts/implementation-plan.mjs +0 -27
  157. package/src/artifacts/prd.mjs +0 -12
  158. package/src/artifacts/state.mjs +0 -63
  159. package/src/artifacts/tasks.mjs +0 -90
  160. package/src/cli.mjs +0 -62
  161. package/src/commands/gates.mjs +0 -71
  162. package/src/commands/graph.mjs +0 -92
  163. package/src/commands/init.mjs +0 -111
  164. package/src/commands/migrate.mjs +0 -224
  165. package/src/commands/route.mjs +0 -135
  166. package/src/commands/status.mjs +0 -35
  167. package/src/commands/trace.mjs +0 -58
  168. package/src/commands/validate.mjs +0 -197
  169. package/src/shared/cli-io.mjs +0 -86
  170. package/src/shared/profiles.mjs +0 -27
  171. package/src/shared/project-config.mjs +0 -39
  172. package/src/shared/project-path.mjs +0 -10
  173. package/src/shared/skill-installer.mjs +0 -34
@@ -0,0 +1,78 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { info } from '../shared/cli-io.mjs';
4
+ import { projectRoot } from '../shared/project-path.mjs';
5
+ import { ArtifactValidationError, deriveExecutionStatus, parseBacklog, topologicalOrder } from '../artifacts/backlog.mjs';
6
+ export { ArtifactValidationError as GraphValidationError };
7
+ function escapeMermaid(text) {
8
+ return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
9
+ }
10
+ const CLASSES = [
11
+ ' classDef completed fill:#dcfce7,stroke:#16a34a,color:#14532d;',
12
+ ' classDef in_progress fill:#dbeafe,stroke:#2563eb,color:#1e3a8a;',
13
+ ' classDef eligible fill:#fef3c7,stroke:#d97706,color:#78350f;',
14
+ ' classDef blocked fill:#fee2e2,stroke:#dc2626,color:#7f1d1d;'
15
+ ];
16
+ const LINK_STYLES = {
17
+ completed: 'stroke:#16a34a,stroke-width:2px',
18
+ in_progress: 'stroke:#2563eb,stroke-width:3px',
19
+ eligible: 'stroke:#d97706,stroke-width:2px,stroke-dasharray:6 4',
20
+ blocked: 'stroke:#dc2626,stroke-width:2px,stroke-dasharray:2 3'
21
+ };
22
+ export function generateGraphMarkdown(backlogText) {
23
+ const { work_items: items } = typeof backlogText === 'string' ? parseBacklog(backlogText) : backlogText;
24
+ const ordered = topologicalOrder(items);
25
+ const byId = new Map(items.map((item) => [item.id, item]));
26
+ const statuses = new Map(items.map((item) => [item.id, item.state ?? deriveExecutionStatus(item, byId).status]));
27
+ const edges = ordered.flatMap((source) => ordered
28
+ .filter((target) => target.depends_on.includes(source.id))
29
+ .map((target) => ({ source: source.id, target: target.id })));
30
+ const lines = [
31
+ '# Work-item dependency graph',
32
+ '',
33
+ '> Derived from canonical work-items. Do not edit this file manually.',
34
+ '',
35
+ '## Status',
36
+ '',
37
+ '- 🟩 **Completed** — accepted work.',
38
+ '- 🟦 **In Progress** — currently executing.',
39
+ '- 🟨 **Eligible** — pending work with dependencies and blockers satisfied.',
40
+ '- 🟥 **Blocked** — pending work with an incomplete dependency or unresolved blocker.',
41
+ '',
42
+ '```mermaid',
43
+ "%%{init: {'flowchart': {'curve': 'linear', 'nodeSpacing': 32, 'rankSpacing': 54}} }%%",
44
+ 'flowchart TD'
45
+ ];
46
+ for (const item of ordered)
47
+ lines.push(` ${item.id}["${escapeMermaid(item.id)}<br/>${escapeMermaid(item.title)}"]`);
48
+ lines.push('');
49
+ for (const edge of edges)
50
+ lines.push(` ${edge.source} --> ${edge.target}`);
51
+ lines.push('');
52
+ edges.forEach((edge, index) => lines.push(` linkStyle ${index} ${LINK_STYLES[statuses.get(edge.source)]};`));
53
+ lines.push('', ...CLASSES);
54
+ for (const status of ['completed', 'in_progress', 'eligible', 'blocked']) {
55
+ const ids = ordered.filter((item) => statuses.get(item.id) === status).map((item) => item.id);
56
+ if (ids.length)
57
+ lines.push(` class ${ids.join(',')} ${status};`);
58
+ }
59
+ lines.push('```', '', 'Dependency arrows are authoritative for execution ordering. Flow executes one mutating task at a time; independent read-only research or review may run in parallel.', '');
60
+ return lines.join('\n');
61
+ }
62
+ export function writeGraph(root) {
63
+ const backlogPath = path.join(root, '_flow', 'backlog.yaml');
64
+ if (!fs.existsSync(backlogPath))
65
+ throw new ArtifactValidationError(`Backlog not found: ${backlogPath}`);
66
+ const graphPath = path.join(root, '_flow', 'docs', 'graph.md');
67
+ fs.mkdirSync(path.dirname(graphPath), { recursive: true });
68
+ const markdown = generateGraphMarkdown(fs.readFileSync(backlogPath, 'utf8'));
69
+ const temporaryPath = `${graphPath}.${process.pid}.${Date.now()}.tmp`;
70
+ fs.writeFileSync(temporaryPath, markdown, 'utf8');
71
+ fs.renameSync(temporaryPath, graphPath);
72
+ return { graphPath, markdown };
73
+ }
74
+ export function runGraph({ args }) {
75
+ const result = writeGraph(projectRoot(args));
76
+ info(`Generated ${result.graphPath}`);
77
+ }
78
+ //# sourceMappingURL=graph.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph.mjs","sourceRoot":"","sources":["../../src/commands/graph.mjs"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export declare function runInit({ args, packageRoot, version }: {
2
+ args: any;
3
+ packageRoot: any;
4
+ version: any;
5
+ }): Promise<void>;
6
+ //# sourceMappingURL=init.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.mts","sourceRoot":"","sources":["../../src/commands/init.mjs"],"names":[],"mappings":"AA4EA,wBAAsB,OAAO,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE;;;;CAAA,iBA8C3D"}
@@ -0,0 +1,126 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fail, info, promptMultiSelect, promptSelect, promptText } from '../shared/cli-io.mjs';
4
+ import { defaultConfig, readConfig, writeConfig } from '../shared/project-config.mjs';
5
+ import { projectRoot, valueAfter } from '../shared/project-path.mjs';
6
+ import { installRuntimeSkill } from '../shared/skill-installer.mjs';
7
+ import { BROWNFIELD_POLICIES, ENGINEERING_PROFILES } from '../shared/profiles.mjs';
8
+ import { FLOW_SCHEMA_VERSION, GATES_SCHEMA_VERSION } from '../domain/contracts.mjs';
9
+ const RUNTIME_DEFINITIONS = {
10
+ codex: { label: 'Codex', skillsPath: '.codex/skills' },
11
+ claude: { label: 'Claude Code', skillsPath: '.claude/skills' }
12
+ };
13
+ function parseRuntimeFlag(args) {
14
+ const raw = valueAfter(args, '--runtime');
15
+ return raw
16
+ ? raw
17
+ .split(',')
18
+ .map((v) => v.trim().toLowerCase())
19
+ .filter(Boolean)
20
+ : null;
21
+ }
22
+ function missingBuiltinRuntimes(existing) {
23
+ const types = new Set(existing.map((r) => r.type));
24
+ return Object.keys(RUNTIME_DEFINITIONS).filter((type) => !types.has(type));
25
+ }
26
+ async function selectRuntimes(existing) {
27
+ const types = new Set(existing.map((r) => r.type));
28
+ const options = Object.entries(RUNTIME_DEFINITIONS)
29
+ .filter(([type]) => !types.has(type))
30
+ .map(([value, d]) => ({ value, label: d.label }));
31
+ options.push({ value: 'custom', label: 'Custom coding agent / skills path' });
32
+ return promptMultiSelect({
33
+ title: existing.length ? 'Select coding agents to add:' : 'Select coding agents:',
34
+ options
35
+ });
36
+ }
37
+ async function resolveRuntime(type, existing) {
38
+ if (RUNTIME_DEFINITIONS[type])
39
+ return { type, skills_path: RUNTIME_DEFINITIONS[type].skillsPath };
40
+ if (type !== 'custom')
41
+ fail(`unsupported runtime '${type}'. Use codex, claude, or custom.`);
42
+ const fallback = `custom-${existing.filter((r) => r.type.startsWith('custom')).length + 1}`;
43
+ const name = await promptText('Custom coding agent id', fallback);
44
+ while (true) {
45
+ const skillsPath = await promptText('Project-local skills directory', `.${name}/skills`);
46
+ if (!path.isAbsolute(skillsPath) && !skillsPath.split(/[\\/]/).includes('..'))
47
+ return { type: name, skills_path: skillsPath };
48
+ info('Skills path must be relative and remain inside the project.');
49
+ }
50
+ }
51
+ async function selectEngineering(args, root, config, existed) {
52
+ const profileFlag = valueAfter(args, '--profile');
53
+ const brownfieldFlag = valueAfter(args, '--existing-code');
54
+ if (args.includes('--brownfield'))
55
+ fail('Use --existing-code improve|preserve: improve recommends clearer structure; preserve keeps consistent conventions.');
56
+ if (existed && (profileFlag || brownfieldFlag))
57
+ fail('Change engineering through /flow and human approval, not init.');
58
+ if (profileFlag && !ENGINEERING_PROFILES[profileFlag])
59
+ fail(`unknown engineering profile '${profileFlag}'.`);
60
+ if (brownfieldFlag && !BROWNFIELD_POLICIES[brownfieldFlag])
61
+ fail(`unknown brownfield policy '${brownfieldFlag}'.`);
62
+ if (profileFlag)
63
+ config.engineering.profile = ENGINEERING_PROFILES[profileFlag].id;
64
+ const hasProjectFiles = fs
65
+ .readdirSync(root)
66
+ .some((name) => ['src', 'app', 'lib', 'packages'].includes(name) || /\.(m?[jt]sx?|py|java|go|rs|cs)$/.test(name));
67
+ if (brownfieldFlag)
68
+ config.engineering.existing_code_policy = brownfieldFlag;
69
+ else if (!existed && hasProjectFiles)
70
+ config.engineering.existing_code_policy = await promptSelect({
71
+ title: 'How should Flow treat existing code conventions?',
72
+ options: Object.entries(BROWNFIELD_POLICIES).map(([value, policy]) => ({
73
+ value,
74
+ label: policy.label,
75
+ description: policy.description
76
+ }))
77
+ });
78
+ }
79
+ export async function runInit({ args, packageRoot, version }) {
80
+ const root = projectRoot(args);
81
+ const flowDirectory = path.join(root, '_flow');
82
+ if (!fs.existsSync(flowDirectory) && fs.existsSync(path.join(root, '.flow')))
83
+ fail('Legacy .flow project requires flow migrate --plan and flow migrate --apply before init. No files were changed.');
84
+ const existed = fs.existsSync(flowDirectory);
85
+ const config = readConfig(root) || defaultConfig(version);
86
+ if (config.schema_version !== FLOW_SCHEMA_VERSION)
87
+ fail('Existing Flow project requires npx --no-install flow migrate before init. No files were changed.');
88
+ if (existed)
89
+ info('Flow project already exists. Canonical project artifacts will not be created or modified.');
90
+ await selectEngineering(args, root, config, existed);
91
+ const requested = parseRuntimeFlag(args);
92
+ if (existed && !requested && missingBuiltinRuntimes(config.runtimes).length === 0) {
93
+ writeConfig(root, config);
94
+ for (const runtime of config.runtimes)
95
+ installRuntimeSkill(root, runtime, packageRoot);
96
+ info('All built-in coding agents are already configured. Configuration is up to date.');
97
+ return;
98
+ }
99
+ const selected = requested || (await selectRuntimes(config.runtimes));
100
+ const known = new Set(config.runtimes.map((r) => r.type));
101
+ const added = [];
102
+ for (const type of selected) {
103
+ if (known.has(type))
104
+ continue;
105
+ const runtime = await resolveRuntime(type, config.runtimes);
106
+ if (known.has(runtime.type))
107
+ continue;
108
+ config.runtimes.push(runtime);
109
+ known.add(runtime.type);
110
+ added.push(runtime);
111
+ }
112
+ fs.mkdirSync(flowDirectory, { recursive: true });
113
+ config.flow_version = version;
114
+ writeConfig(root, config);
115
+ if (!existed) {
116
+ fs.writeFileSync(path.join(flowDirectory, 'gates.yaml'), `schema_version: ${GATES_SCHEMA_VERSION}\ngates: []\n`);
117
+ fs.mkdirSync(path.join(flowDirectory, 'work-items'), { recursive: true });
118
+ fs.mkdirSync(path.join(flowDirectory, 'generated'), { recursive: true });
119
+ fs.writeFileSync(path.join(flowDirectory, 'generated', '.gitignore'), '*\n!.gitignore\n');
120
+ }
121
+ for (const runtime of config.runtimes)
122
+ info(`✓ ${runtime.type}: ${path.relative(root, installRuntimeSkill(root, runtime, packageRoot))}`);
123
+ info(`Engineering profile: ${ENGINEERING_PROFILES[config.engineering.profile]?.label ?? config.engineering.profile}`);
124
+ info('Flow is ready. Invoke /flow; engineering bootstrap runs before implementation when no approved contract exists.');
125
+ }
126
+ //# sourceMappingURL=init.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.mjs","sourceRoot":"","sources":["../../src/commands/init.mjs"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ export declare function migrationPlan(root: any, targetVersion?: string): {
2
+ from_version: any;
3
+ to_version: string;
4
+ changes: string[];
5
+ files_affected: any[];
6
+ incompatibilities: string[];
7
+ invalidated_plans: string[];
8
+ affected_approvals: string[];
9
+ human_decisions: string[];
10
+ can_apply: boolean;
11
+ };
12
+ export declare function migrateProject(root: any, { targetVersion }?: {
13
+ targetVersion?: string | undefined;
14
+ }): {
15
+ unresolved: never[];
16
+ unchanged: boolean;
17
+ backup?: never;
18
+ } | {
19
+ unresolved: string[];
20
+ unchanged: boolean;
21
+ backup: any;
22
+ };
23
+ export declare function runMigrate({ args, version }: {
24
+ args: any;
25
+ version: any;
26
+ }): void;
27
+ //# sourceMappingURL=migrate.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate.d.mts","sourceRoot":"","sources":["../../src/commands/migrate.mjs"],"names":[],"mappings":"AA0NA,wBAAgB,aAAa,CAAC,IAAI,KAAA,EAAE,aAAa,SAAU;;;;;;;;;;EA6D1D;AAwCD,wBAAgB,cAAc,CAAC,IAAI,KAAA,EAAE,EAAE,aAAuB,EAAE;;CAAK;;;;;;;;EAwCpE;AACD,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE;;;CAAA,QAgB3C"}
@@ -0,0 +1,379 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { parse, stringify } from 'yaml';
4
+ import { info } from '../shared/cli-io.mjs';
5
+ import { projectRoot } from '../shared/project-path.mjs';
6
+ import { emptyState, stringifyState } from '../artifacts/state.mjs';
7
+ import { parseBacklog } from '../artifacts/backlog.mjs';
8
+ import { parseGates } from '../artifacts/gates.mjs';
9
+ import { syncProject } from './sync.mjs';
10
+ import { validateProject } from './validate.mjs';
11
+ import { readConfig, writeConfig, defaultConfig } from '../shared/project-config.mjs';
12
+ import { BACKLOG_SCHEMA_VERSION, FLOW_SCHEMA_VERSION, GATES_SCHEMA_VERSION, TASKS_SCHEMA_VERSION } from '../domain/contracts.mjs';
13
+ const STATES = {
14
+ done: 'completed',
15
+ complete: 'completed',
16
+ completed: 'completed',
17
+ in_progress: 'in_progress',
18
+ blocked: 'pending',
19
+ pending: 'pending',
20
+ todo: 'pending'
21
+ };
22
+ function lifecycle(value) {
23
+ if (!STATES[value])
24
+ throw new Error(`Unknown legacy state '${value}'.`);
25
+ return STATES[value];
26
+ }
27
+ function number(value) {
28
+ const match = String(value).match(/\d+/);
29
+ if (!match)
30
+ throw new Error(`Cannot normalize ID '${value}'.`);
31
+ return match[0].padStart(3, '0');
32
+ }
33
+ function temporarySibling(file) {
34
+ const directory = path.dirname(file);
35
+ const base = path.basename(file);
36
+ let attempt = 0;
37
+ let temporary;
38
+ do {
39
+ temporary = path.join(directory, `.${base}_flow-migration-${process.pid}-${Date.now()}-${attempt++}`);
40
+ } while (fs.existsSync(temporary));
41
+ return temporary;
42
+ }
43
+ function sameEntry(from, to) {
44
+ return fs.realpathSync.native(from) === fs.realpathSync.native(to);
45
+ }
46
+ function move(from, to) {
47
+ if (!fs.existsSync(from))
48
+ return;
49
+ if (path.resolve(from) === path.resolve(to))
50
+ return;
51
+ if (fs.existsSync(to)) {
52
+ if (!sameEntry(from, to))
53
+ throw new Error(`Migration destination already exists: ${to}`);
54
+ const temporary = temporarySibling(from);
55
+ fs.renameSync(from, temporary);
56
+ try {
57
+ fs.renameSync(temporary, to);
58
+ }
59
+ catch (error) {
60
+ try {
61
+ fs.renameSync(temporary, from);
62
+ }
63
+ catch (recoveryError) {
64
+ error.preserveRecoveryData = true;
65
+ error.recovery = { from, to, temporary, recoveryError };
66
+ }
67
+ throw error;
68
+ }
69
+ return;
70
+ }
71
+ fs.mkdirSync(path.dirname(to), { recursive: true });
72
+ fs.renameSync(from, to);
73
+ }
74
+ function createCanonicalShell(base, item) {
75
+ fs.mkdirSync(base, { recursive: true });
76
+ fs.writeFileSync(path.join(base, 'spec.md'), `---\n${stringify({
77
+ schema_version: 1,
78
+ work_item: item.id,
79
+ title: item.title,
80
+ kind: item.kind,
81
+ priority: item.priority,
82
+ depends_on: item.depends_on,
83
+ blockers: [],
84
+ maturity: 'outlined'
85
+ }).trimEnd()}\n---\n\n# Work Item Specification\n`);
86
+ fs.writeFileSync(path.join(base, 'tasks.yaml'), stringify({ schema_version: TASKS_SCHEMA_VERSION, work_item: item.id, tasks: [] }, { lineWidth: 0 }));
87
+ fs.writeFileSync(path.join(base, 'implementation-plan.md'), `---\nschema_version: 1\nwork_item: ${item.id}\nstatus: draft\n---\n\n# Implementation Plan\n`);
88
+ fs.writeFileSync(path.join(base, 'review.yaml'), `schema_version: 1\nwork_item: ${item.id}\nstatus: pending\n`);
89
+ }
90
+ function migrateStaged(root) {
91
+ const flow = path.join(root, '_flow');
92
+ const oldConfig = readConfig(root);
93
+ const oldFile = path.join(flow, fs.existsSync(path.join(flow, 'BACKLOG.yaml')) ? 'BACKLOG.yaml' : 'backlog.yaml');
94
+ const raw = parse(fs.readFileSync(oldFile, 'utf8'));
95
+ const mapping = new Map();
96
+ const ids = new Set();
97
+ for (const item of raw.work_items ?? []) {
98
+ const id = `W${number(item.id)}`;
99
+ if (ids.has(id))
100
+ throw new Error(`Work-item ID collision: ${id}`);
101
+ ids.add(id);
102
+ mapping.set(item.id, id);
103
+ }
104
+ const original = new Map();
105
+ const work_items = (raw.work_items ?? []).map((item) => {
106
+ const id = mapping.get(item.id);
107
+ const folder = `${id}-${String(item.folder ?? item.title)
108
+ .replace(new RegExp(`^${id}-`, 'i'), '')
109
+ .toLowerCase()
110
+ .replace(/[^a-z0-9]+/g, '-')
111
+ .replace(/^-|-$/g, '') || 'work-item'}`;
112
+ original.set(id, item.folder);
113
+ return {
114
+ id,
115
+ folder,
116
+ title: item.title,
117
+ kind: item.kind,
118
+ state: lifecycle(item.state ?? item.status ?? 'pending'),
119
+ priority: item.priority ?? 1,
120
+ spec_maturity: 'ready',
121
+ depends_on: (item.depends_on ?? []).map((dep) => {
122
+ if (!mapping.has(dep))
123
+ throw new Error(`Unknown work-item dependency '${dep}'.`);
124
+ return mapping.get(dep);
125
+ }),
126
+ blockers: []
127
+ };
128
+ });
129
+ const text = stringify({ schema_version: BACKLOG_SCHEMA_VERSION, work_items }, { lineWidth: 0 });
130
+ parseBacklog(text);
131
+ const workRoot = path.join(flow, 'work-items');
132
+ for (const item of work_items) {
133
+ const previous = path.join(workRoot, original.get(item.id) ?? item.folder);
134
+ const folder = path.join(workRoot, item.folder);
135
+ if (previous !== folder && fs.existsSync(previous))
136
+ move(previous, folder);
137
+ const archive = path.join(flow, 'docs', 'legacy-work-items', item.folder);
138
+ if (fs.existsSync(folder)) {
139
+ fs.mkdirSync(path.dirname(archive), { recursive: true });
140
+ fs.cpSync(folder, archive, { recursive: true });
141
+ fs.rmSync(folder, { recursive: true, force: true });
142
+ }
143
+ createCanonicalShell(folder, item);
144
+ }
145
+ fs.mkdirSync(path.join(flow, 'docs'), { recursive: true });
146
+ for (const [old, next] of [
147
+ ['PRD.md', 'prd.md'],
148
+ ['ENGINEERING.md', 'legacy-engineering.md'],
149
+ ['STATE.md', 'legacy-state.md'],
150
+ ['DECISIONS.md', 'legacy-decisions.md'],
151
+ ['SUMMARY.md', 'legacy-summary.md']
152
+ ])
153
+ move(path.join(flow, old), path.join(flow, 'docs', next));
154
+ if (fs.existsSync(path.join(flow, 'GRAPH.md')))
155
+ fs.unlinkSync(path.join(flow, 'GRAPH.md'));
156
+ if (fs.existsSync(oldFile))
157
+ move(oldFile, path.join(flow, 'docs', 'legacy-backlog.yaml'));
158
+ const state = emptyState();
159
+ state.execution.phase = 'reconcile';
160
+ state.execution.step = 'resolve_conflicts';
161
+ state.migration.status = 'pending_reconciliation';
162
+ fs.writeFileSync(path.join(flow, 'state.yaml'), stringifyState(state));
163
+ if (!fs.existsSync(path.join(flow, 'gates.yaml')))
164
+ fs.writeFileSync(path.join(flow, 'gates.yaml'), `schema_version: ${GATES_SCHEMA_VERSION}\ngates: []\n`);
165
+ const config = defaultConfig();
166
+ config.runtimes = oldConfig?.runtimes ?? [];
167
+ config.engineering.existing_code_policy = 'improve';
168
+ writeConfig(root, config);
169
+ }
170
+ function hasLegacyBacklog(flow) {
171
+ return fs.existsSync(path.join(flow, 'BACKLOG.yaml')) || fs.existsSync(path.join(flow, 'backlog.yaml'));
172
+ }
173
+ function inspectCurrent(root, targetVersion) {
174
+ const flow = path.join(root, '_flow');
175
+ const changes = [];
176
+ try {
177
+ const config = readConfig(root);
178
+ if (!config || config.schema_version !== FLOW_SCHEMA_VERSION || config.flow_version !== targetVersion)
179
+ changes.push('upgrade config.yaml');
180
+ }
181
+ catch {
182
+ changes.push('upgrade config.yaml');
183
+ }
184
+ const workItems = path.join(flow, 'work-items');
185
+ if (!fs.existsSync(workItems))
186
+ changes.push('restore work-items directory');
187
+ else
188
+ for (const entry of fs.readdirSync(workItems, { withFileTypes: true })) {
189
+ if (!entry.isDirectory())
190
+ continue;
191
+ const tasks = path.join(workItems, entry.name, 'tasks.yaml');
192
+ if (!fs.existsSync(tasks)) {
193
+ changes.push(`restore work-items/${entry.name}/tasks.yaml`);
194
+ continue;
195
+ }
196
+ try {
197
+ const value = parse(fs.readFileSync(tasks, 'utf8')) ?? {};
198
+ if (value.schema_version !== TASKS_SCHEMA_VERSION ||
199
+ (value.tasks ?? []).some((task) => ['traceability', 'implementation', 'commit_sha'].some((field) => Object.hasOwn(task, field))))
200
+ changes.push(`upgrade work-items/${entry.name}/tasks.yaml`);
201
+ }
202
+ catch {
203
+ changes.push(`upgrade work-items/${entry.name}/tasks.yaml`);
204
+ }
205
+ }
206
+ try {
207
+ parseGates(fs.readFileSync(path.join(flow, 'gates.yaml'), 'utf8'));
208
+ }
209
+ catch {
210
+ changes.push('upgrade gates.yaml');
211
+ }
212
+ return [...new Set(changes)];
213
+ }
214
+ export function migrationPlan(root, targetVersion = '0.6.0') {
215
+ const currentFlow = path.join(root, '_flow');
216
+ const legacyFlow = path.join(root, '.flow');
217
+ if (fs.existsSync(currentFlow) && fs.existsSync(legacyFlow))
218
+ return {
219
+ from_version: 'ambiguous',
220
+ to_version: targetVersion,
221
+ changes: [],
222
+ files_affected: [],
223
+ incompatibilities: ['Both .flow and _flow exist; reconcile the authoritative directory first.'],
224
+ invalidated_plans: [],
225
+ affected_approvals: [],
226
+ human_decisions: ['Choose the authoritative Flow directory.'],
227
+ can_apply: false
228
+ };
229
+ const usesLegacyDirectory = !fs.existsSync(currentFlow) && fs.existsSync(legacyFlow);
230
+ const flow = usesLegacyDirectory ? legacyFlow : currentFlow;
231
+ if (!fs.existsSync(flow))
232
+ throw new Error('_flow does not exist.');
233
+ const rawConfigFile = path.join(flow, 'config.yaml');
234
+ const rawConfig = fs.existsSync(rawConfigFile) ? (parse(fs.readFileSync(rawConfigFile, 'utf8')) ?? {}) : {};
235
+ const config = usesLegacyDirectory
236
+ ? { flow_version: rawConfig.flow_version ?? rawConfig.framework?.version ?? null }
237
+ : readConfig(root);
238
+ const files = fs.readdirSync(flow);
239
+ const legacy = files.filter((name) => ['BACKLOG.yaml', 'PRD.md', 'ENGINEERING.md', 'STATE.md', 'DECISIONS.md', 'SUMMARY.md', 'GRAPH.md'].includes(name));
240
+ const changes = [];
241
+ const invalidatedPlans = [];
242
+ if (usesLegacyDirectory)
243
+ changes.push('rename the canonical project directory from .flow to _flow');
244
+ if (!config?.flow_version || config.flow_version !== targetVersion)
245
+ changes.push('record executed Flow package version');
246
+ if (legacy.length)
247
+ changes.push('normalize legacy artifact names and IDs');
248
+ if (!usesLegacyDirectory)
249
+ changes.push(...inspectCurrent(root, targetVersion));
250
+ const backlogFile = path.join(flow, 'backlog.yaml');
251
+ if (usesLegacyDirectory && fs.existsSync(backlogFile)) {
252
+ const backlog = parse(fs.readFileSync(backlogFile, 'utf8'));
253
+ backlog.schema_version = BACKLOG_SCHEMA_VERSION;
254
+ if ((backlog.work_items ?? []).some((item) => !item.spec_maturity))
255
+ changes.push('derive initial spec_maturity from existing artifacts');
256
+ for (const item of backlog.work_items ?? []) {
257
+ const planFile = path.join(flow, 'work-items', item.folder ?? '', 'implementation-plan.md');
258
+ if ((item.state ?? item.status) !== 'completed' && fs.existsSync(planFile))
259
+ invalidatedPlans.push(`${item.id}: work-items/${item.folder}/implementation-plan.md`);
260
+ }
261
+ }
262
+ const source = config?.flow_version ?? 'legacy';
263
+ const knownSource = source === 'legacy' || source === targetVersion || /^0\.[45]\./.test(source);
264
+ return {
265
+ from_version: source,
266
+ to_version: targetVersion,
267
+ changes,
268
+ files_affected: [...legacy, 'config.yaml', 'backlog.yaml', 'state.yaml', 'gates.yaml'],
269
+ incompatibilities: knownSource ? [] : [`Unknown source version '${source}'.`],
270
+ invalidated_plans: invalidatedPlans,
271
+ affected_approvals: invalidatedPlans,
272
+ human_decisions: changes.length
273
+ ? ['Reconcile preserved product, engineering, spec and traceability semantics before implementation.']
274
+ : [],
275
+ can_apply: knownSource
276
+ };
277
+ }
278
+ function upgradeCanonicalStaged(root, targetVersion) {
279
+ const flow = path.join(root, '_flow');
280
+ const workItems = path.join(flow, 'work-items');
281
+ if (fs.existsSync(workItems))
282
+ for (const entry of fs.readdirSync(workItems, { withFileTypes: true })) {
283
+ if (!entry.isDirectory())
284
+ continue;
285
+ const taskPath = path.join(workItems, entry.name, 'tasks.yaml');
286
+ if (!fs.existsSync(taskPath))
287
+ continue;
288
+ const value = parse(fs.readFileSync(taskPath, 'utf8')) ?? {};
289
+ value.schema_version = TASKS_SCHEMA_VERSION;
290
+ for (const task of value.tasks ?? []) {
291
+ if (task.state === 'completed' && task.commit_sha) {
292
+ task.legacy_commit ??= task.commit_sha;
293
+ task.provenance = 'legacy_migration';
294
+ }
295
+ delete task.traceability;
296
+ delete task.implementation;
297
+ delete task.commit_sha;
298
+ }
299
+ fs.writeFileSync(taskPath, stringify(value, { lineWidth: 0 }));
300
+ }
301
+ const config = readConfig(root) ?? defaultConfig(targetVersion);
302
+ config.flow_version = targetVersion;
303
+ writeConfig(root, config);
304
+ const gatesFile = path.join(flow, 'gates.yaml');
305
+ if (!fs.existsSync(gatesFile))
306
+ fs.writeFileSync(gatesFile, `schema_version: ${GATES_SCHEMA_VERSION}\ngates: []\n`);
307
+ else {
308
+ const gates = parse(fs.readFileSync(gatesFile, 'utf8'));
309
+ gates.schema_version = GATES_SCHEMA_VERSION;
310
+ for (const gate of gates.gates ?? []) {
311
+ gate.stage ??= 'full';
312
+ gate.cost ??= 'medium';
313
+ gate.scope ??= {};
314
+ }
315
+ fs.writeFileSync(gatesFile, stringify(gates, { lineWidth: 0 }));
316
+ }
317
+ }
318
+ export function migrateProject(root, { targetVersion = '0.6.0' } = {}) {
319
+ const plan = migrationPlan(root, targetVersion);
320
+ if (!plan.can_apply)
321
+ throw new Error(plan.incompatibilities.join(' '));
322
+ const targetFlow = path.join(root, '_flow');
323
+ const legacyFlow = path.join(root, '.flow');
324
+ const sourceFlow = fs.existsSync(targetFlow) ? targetFlow : legacyFlow;
325
+ const usesLegacyDirectory = sourceFlow === legacyFlow;
326
+ if (!usesLegacyDirectory && plan.changes.length === 0)
327
+ return { unresolved: [], unchanged: true };
328
+ const staging = fs.mkdtempSync(path.join(root, '_flow-migration-'));
329
+ const backup = path.join(staging, 'backup');
330
+ const staged = path.join(staging, '_flow');
331
+ let preserveStaging = false;
332
+ try {
333
+ fs.cpSync(sourceFlow, staged, { recursive: true });
334
+ if (usesLegacyDirectory || hasLegacyBacklog(staged))
335
+ migrateStaged(staging);
336
+ else
337
+ upgradeCanonicalStaged(staging, targetVersion);
338
+ syncProject(staging);
339
+ const findings = validateProject(staging);
340
+ if (findings.length)
341
+ throw new Error(`Migration staging validation failed: ${findings.map((x) => x.code).join(', ')}.`);
342
+ fs.renameSync(sourceFlow, backup);
343
+ try {
344
+ fs.renameSync(staged, targetFlow);
345
+ }
346
+ catch (error) {
347
+ fs.renameSync(backup, sourceFlow);
348
+ throw error;
349
+ }
350
+ const backupRoot = path.join(root, '_flow-backups');
351
+ fs.mkdirSync(backupRoot, { recursive: true });
352
+ const backupTarget = path.join(backupRoot, `migration-${Date.now()}`);
353
+ fs.renameSync(backup, backupTarget);
354
+ return { unresolved: ['semantic reconciliation'], unchanged: false, backup: backupTarget };
355
+ }
356
+ catch (error) {
357
+ preserveStaging = Boolean(error?.preserveRecoveryData);
358
+ throw error;
359
+ }
360
+ finally {
361
+ // Retain the original backup if even rollback failed; never delete the only copy.
362
+ if (!preserveStaging && (!fs.existsSync(backup) || fs.existsSync(targetFlow) || fs.existsSync(sourceFlow)))
363
+ fs.rmSync(staging, { recursive: true, force: true });
364
+ }
365
+ }
366
+ export function runMigrate({ args, version }) {
367
+ const root = projectRoot(args);
368
+ if (args.includes('--plan')) {
369
+ const plan = migrationPlan(root, version);
370
+ return info(args.includes('--json')
371
+ ? JSON.stringify(plan, null, 2)
372
+ : `Migration ${plan.from_version} -> ${plan.to_version}\n${plan.changes.length ? plan.changes.map((change) => `- ${change}`).join('\n') : 'No structural changes required.'}`);
373
+ }
374
+ const result = migrateProject(root, { targetVersion: version });
375
+ info(result.unchanged
376
+ ? 'Already migrated; no files changed.'
377
+ : 'Structural migration complete. Invoke /flow for semantic reconciliation before implementation.');
378
+ }
379
+ //# sourceMappingURL=migrate.mjs.map