@michaelxwb/code-flow 0.8.1 → 1.0.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 (268) hide show
  1. package/README.md +73 -152
  2. package/bin/code-flow.js +12 -0
  3. package/docs/README.md +19 -0
  4. package/docs/USAGE.md +156 -1417
  5. package/docs/v1-design.md +1858 -0
  6. package/docs/v1-implementation.md +186 -0
  7. package/lib/adapters.js +126 -0
  8. package/lib/cli.js +258 -0
  9. package/lib/config.js +85 -0
  10. package/lib/doctor.js +283 -0
  11. package/lib/frontmatter.js +42 -0
  12. package/lib/fs.js +141 -0
  13. package/lib/glob.js +23 -0
  14. package/lib/hook.js +99 -0
  15. package/lib/init.js +132 -0
  16. package/lib/learn.js +170 -0
  17. package/lib/locks.js +52 -0
  18. package/lib/markdown.js +13 -0
  19. package/lib/process.js +116 -0
  20. package/lib/render.js +115 -0
  21. package/lib/repo.js +48 -0
  22. package/lib/runs.js +155 -0
  23. package/lib/session.js +40 -0
  24. package/lib/spec.js +80 -0
  25. package/lib/task.js +258 -0
  26. package/lib/taskdoc.js +308 -0
  27. package/lib/toml.js +140 -0
  28. package/lib/verify.js +168 -0
  29. package/lib/worktree.js +930 -0
  30. package/package.json +18 -21
  31. package/templates/adapters/opencode.mjs +65 -0
  32. package/templates/config.json +39 -0
  33. package/templates/instructions.md +13 -0
  34. package/templates/launcher/bridge.cjs +295 -0
  35. package/templates/launcher/hook.cmd +15 -0
  36. package/templates/launcher/hook.ps1 +10 -0
  37. package/templates/launcher/hook.sh +20 -0
  38. package/templates/skills/cf-check.md +19 -0
  39. package/templates/skills/cf-init.md +16 -0
  40. package/templates/skills/cf-spec.md +20 -0
  41. package/templates/skills/cf-task-align.md +19 -0
  42. package/templates/skills/cf-task-archive.md +20 -0
  43. package/templates/skills/cf-task-plan.md +31 -0
  44. package/templates/skills/cf-task-prd.md +14 -0
  45. package/templates/skills/cf-task-start.md +41 -0
  46. package/{src/core/code-flow/specs/shared/design → templates/workflow}/design-frontend.md +12 -12
  47. package/{src/core/code-flow/specs/shared/design → templates/workflow}/design-full.md +17 -19
  48. package/{src/core/code-flow/specs/shared/design → templates/workflow}/design-lite.md +12 -12
  49. package/{src/core/code-flow/specs/shared/prd-template.md → templates/workflow/prd.md} +1 -1
  50. package/templates/workflow/project-guidelines.md +48 -0
  51. package/templates/workflow/scenarios.md +67 -0
  52. package/templates/workflow/task.md +56 -0
  53. package/docs/acceptance-contract.md +0 -217
  54. package/docs/ci/acceptance-evidence.md +0 -77
  55. package/docs/migrations/runtime-0.7.0.md +0 -41
  56. package/docs/migrations/runtime-0.8.0.md +0 -154
  57. package/docs/migrations/runtime-0.9.0.md +0 -40
  58. package/docs/migrations/spec-workflow-0.6.0.md +0 -42
  59. package/docs/spec-injection-hardening.md +0 -54
  60. package/docs/spec-injection-performance.json +0 -73
  61. package/docs/spec-optimization.md +0 -12
  62. package/src/adapters/claude/CLAUDE.md +0 -45
  63. package/src/adapters/claude/commands/cf-init.md +0 -262
  64. package/src/adapters/claude/commands/cf-learn.md +0 -291
  65. package/src/adapters/claude/commands/cf-spec.md +0 -87
  66. package/src/adapters/claude/commands/cf-stats.md +0 -96
  67. package/src/adapters/claude/commands/cf-sync.md +0 -39
  68. package/src/adapters/claude/commands/cf-task/align.md +0 -331
  69. package/src/adapters/claude/commands/cf-task/archive.md +0 -155
  70. package/src/adapters/claude/commands/cf-task/block.md +0 -49
  71. package/src/adapters/claude/commands/cf-task/graph.md +0 -92
  72. package/src/adapters/claude/commands/cf-task/note.md +0 -248
  73. package/src/adapters/claude/commands/cf-task/plan.md +0 -477
  74. package/src/adapters/claude/commands/cf-task/prd.md +0 -173
  75. package/src/adapters/claude/commands/cf-task/start.md +0 -317
  76. package/src/adapters/claude/commands/cf-task/status.md +0 -71
  77. package/src/adapters/claude/commands/cf-task/verify-e2e.md +0 -148
  78. package/src/adapters/claude/commands/cf-validate.md +0 -100
  79. package/src/adapters/claude/settings.local.json +0 -50
  80. package/src/adapters/codex/AGENTS.md +0 -45
  81. package/src/adapters/codex/config.toml +0 -3
  82. package/src/adapters/codex/hooks.json +0 -54
  83. package/src/adapters/codex/skills/cf-init/SKILL.md +0 -273
  84. package/src/adapters/codex/skills/cf-learn/SKILL.md +0 -296
  85. package/src/adapters/codex/skills/cf-spec/SKILL.md +0 -88
  86. package/src/adapters/codex/skills/cf-stats/SKILL.md +0 -97
  87. package/src/adapters/codex/skills/cf-sync/SKILL.md +0 -45
  88. package/src/adapters/codex/skills/cf-task-align/SKILL.md +0 -332
  89. package/src/adapters/codex/skills/cf-task-archive/SKILL.md +0 -156
  90. package/src/adapters/codex/skills/cf-task-block/SKILL.md +0 -50
  91. package/src/adapters/codex/skills/cf-task-graph/SKILL.md +0 -93
  92. package/src/adapters/codex/skills/cf-task-note/SKILL.md +0 -245
  93. package/src/adapters/codex/skills/cf-task-plan/SKILL.md +0 -478
  94. package/src/adapters/codex/skills/cf-task-prd/SKILL.md +0 -174
  95. package/src/adapters/codex/skills/cf-task-start/SKILL.md +0 -318
  96. package/src/adapters/codex/skills/cf-task-status/SKILL.md +0 -72
  97. package/src/adapters/codex/skills/cf-task-verify-e2e/SKILL.md +0 -145
  98. package/src/adapters/codex/skills/cf-validate/SKILL.md +0 -102
  99. package/src/adapters/commands.json +0 -115
  100. package/src/adapters/costrict/CLAUDE.md +0 -45
  101. package/src/adapters/costrict/commands/cf-init.md +0 -262
  102. package/src/adapters/costrict/commands/cf-learn.md +0 -291
  103. package/src/adapters/costrict/commands/cf-spec.md +0 -87
  104. package/src/adapters/costrict/commands/cf-stats.md +0 -96
  105. package/src/adapters/costrict/commands/cf-sync.md +0 -39
  106. package/src/adapters/costrict/commands/cf-task/align.md +0 -331
  107. package/src/adapters/costrict/commands/cf-task/archive.md +0 -155
  108. package/src/adapters/costrict/commands/cf-task/block.md +0 -49
  109. package/src/adapters/costrict/commands/cf-task/graph.md +0 -92
  110. package/src/adapters/costrict/commands/cf-task/note.md +0 -248
  111. package/src/adapters/costrict/commands/cf-task/plan.md +0 -477
  112. package/src/adapters/costrict/commands/cf-task/prd.md +0 -173
  113. package/src/adapters/costrict/commands/cf-task/start.md +0 -317
  114. package/src/adapters/costrict/commands/cf-task/status.md +0 -71
  115. package/src/adapters/costrict/commands/cf-task/verify-e2e.md +0 -148
  116. package/src/adapters/costrict/commands/cf-validate.md +0 -100
  117. package/src/adapters/costrict/settings.local.json +0 -50
  118. package/src/adapters/install-plan.json +0 -100
  119. package/src/adapters/opencode/AGENTS.md +0 -45
  120. package/src/adapters/opencode/commands/cf-init.md +0 -281
  121. package/src/adapters/opencode/commands/cf-learn.md +0 -295
  122. package/src/adapters/opencode/commands/cf-spec.md +0 -91
  123. package/src/adapters/opencode/commands/cf-stats.md +0 -100
  124. package/src/adapters/opencode/commands/cf-sync.md +0 -44
  125. package/src/adapters/opencode/commands/cf-task/align.md +0 -331
  126. package/src/adapters/opencode/commands/cf-task/archive.md +0 -160
  127. package/src/adapters/opencode/commands/cf-task/block.md +0 -54
  128. package/src/adapters/opencode/commands/cf-task/graph.md +0 -97
  129. package/src/adapters/opencode/commands/cf-task/note.md +0 -253
  130. package/src/adapters/opencode/commands/cf-task/plan.md +0 -477
  131. package/src/adapters/opencode/commands/cf-task/prd.md +0 -178
  132. package/src/adapters/opencode/commands/cf-task/start.md +0 -317
  133. package/src/adapters/opencode/commands/cf-task/status.md +0 -76
  134. package/src/adapters/opencode/commands/cf-task/verify-e2e.md +0 -148
  135. package/src/adapters/opencode/commands/cf-validate.md +0 -105
  136. package/src/adapters/opencode/opencode.json +0 -3
  137. package/src/adapters/opencode/plugins/code-flow/index.js +0 -162
  138. package/src/adapters/opencode/plugins/code-flow/package.json +0 -6
  139. package/src/adapters/platform-bindings.json +0 -370
  140. package/src/cli.js +0 -1076
  141. package/src/core/code-flow/config.yml +0 -161
  142. package/src/core/code-flow/runtime-commands.json +0 -259
  143. package/src/core/code-flow/scripts/cf_acceptance_manifest.py +0 -10
  144. package/src/core/code-flow/scripts/cf_acceptance_runner.py +0 -10
  145. package/src/core/code-flow/scripts/cf_audit.py +0 -10
  146. package/src/core/code-flow/scripts/cf_codex_hook.py +0 -10
  147. package/src/core/code-flow/scripts/cf_design_lint.py +0 -10
  148. package/src/core/code-flow/scripts/cf_evidence_import.py +0 -10
  149. package/src/core/code-flow/scripts/cf_feedback.py +0 -10
  150. package/src/core/code-flow/scripts/cf_opencode_event.py +0 -10
  151. package/src/core/code-flow/scripts/cf_post_hook.py +0 -11
  152. package/src/core/code-flow/scripts/cf_pre_tool_hook.py +0 -11
  153. package/src/core/code-flow/scripts/cf_scenarios.py +0 -10
  154. package/src/core/code-flow/scripts/cf_spec_context.py +0 -10
  155. package/src/core/code-flow/scripts/cf_spec_gate.py +0 -10
  156. package/src/core/code-flow/scripts/cf_spec_session.py +0 -10
  157. package/src/core/code-flow/scripts/cf_stats.py +0 -10
  158. package/src/core/code-flow/scripts/cf_stop_hook.py +0 -11
  159. package/src/core/code-flow/scripts/cf_sync.py +0 -10
  160. package/src/core/code-flow/scripts/cf_task_index.py +0 -10
  161. package/src/core/code-flow/scripts/cf_task_parallel.py +0 -10
  162. package/src/core/code-flow/scripts/cf_task_workflow.py +0 -10
  163. package/src/core/code-flow/scripts/cf_user_prompt_hook.py +0 -11
  164. package/src/core/code-flow/scripts/cf_validation.py +0 -10
  165. package/src/core/code-flow/scripts/codeflow/__init__.py +0 -0
  166. package/src/core/code-flow/scripts/codeflow/acceptance/__init__.py +0 -0
  167. package/src/core/code-flow/scripts/codeflow/acceptance/cf_acceptance_evidence.py +0 -347
  168. package/src/core/code-flow/scripts/codeflow/acceptance/cf_acceptance_manifest_core.py +0 -660
  169. package/src/core/code-flow/scripts/codeflow/acceptance/cf_acceptance_plan.py +0 -202
  170. package/src/core/code-flow/scripts/codeflow/acceptance/cf_acceptance_registration.py +0 -60
  171. package/src/core/code-flow/scripts/codeflow/acceptance/cf_acceptance_risk.py +0 -73
  172. package/src/core/code-flow/scripts/codeflow/acceptance/cf_acceptance_schema.py +0 -282
  173. package/src/core/code-flow/scripts/codeflow/acceptance/cf_acceptance_selection.py +0 -266
  174. package/src/core/code-flow/scripts/codeflow/acceptance/cf_check_inputs.py +0 -172
  175. package/src/core/code-flow/scripts/codeflow/acceptance/cf_check_registration.py +0 -326
  176. package/src/core/code-flow/scripts/codeflow/acceptance/cf_checks.py +0 -333
  177. package/src/core/code-flow/scripts/codeflow/acceptance/cf_plan_quality.py +0 -197
  178. package/src/core/code-flow/scripts/codeflow/acceptance/cf_scenario_quality.py +0 -174
  179. package/src/core/code-flow/scripts/codeflow/acceptance/cf_scenario_source.py +0 -300
  180. package/src/core/code-flow/scripts/codeflow/acceptance/cf_traceability.py +0 -502
  181. package/src/core/code-flow/scripts/codeflow/cli/__init__.py +0 -0
  182. package/src/core/code-flow/scripts/codeflow/cli/cf_acceptance_manifest.py +0 -347
  183. package/src/core/code-flow/scripts/codeflow/cli/cf_acceptance_runner.py +0 -1490
  184. package/src/core/code-flow/scripts/codeflow/cli/cf_audit.py +0 -533
  185. package/src/core/code-flow/scripts/codeflow/cli/cf_design_lint.py +0 -172
  186. package/src/core/code-flow/scripts/codeflow/cli/cf_evidence_import.py +0 -607
  187. package/src/core/code-flow/scripts/codeflow/cli/cf_feedback.py +0 -53
  188. package/src/core/code-flow/scripts/codeflow/cli/cf_scenarios.py +0 -58
  189. package/src/core/code-flow/scripts/codeflow/cli/cf_spec_context.py +0 -1175
  190. package/src/core/code-flow/scripts/codeflow/cli/cf_spec_gate.py +0 -60
  191. package/src/core/code-flow/scripts/codeflow/cli/cf_spec_session.py +0 -46
  192. package/src/core/code-flow/scripts/codeflow/cli/cf_stats.py +0 -540
  193. package/src/core/code-flow/scripts/codeflow/cli/cf_sync.py +0 -144
  194. package/src/core/code-flow/scripts/codeflow/cli/cf_task_index.py +0 -177
  195. package/src/core/code-flow/scripts/codeflow/cli/cf_task_parallel.py +0 -1142
  196. package/src/core/code-flow/scripts/codeflow/cli/cf_task_workflow.py +0 -742
  197. package/src/core/code-flow/scripts/codeflow/cli/cf_validation.py +0 -31
  198. package/src/core/code-flow/scripts/codeflow/core/__init__.py +0 -0
  199. package/src/core/code-flow/scripts/codeflow/core/cf_context_cache.py +0 -67
  200. package/src/core/code-flow/scripts/codeflow/core/cf_core.py +0 -609
  201. package/src/core/code-flow/scripts/codeflow/core/cf_exec_base.py +0 -325
  202. package/src/core/code-flow/scripts/codeflow/core/cf_fs.py +0 -57
  203. package/src/core/code-flow/scripts/codeflow/core/cf_git.py +0 -92
  204. package/src/core/code-flow/scripts/codeflow/core/cf_log.py +0 -160
  205. package/src/core/code-flow/scripts/codeflow/core/cf_markdown.py +0 -77
  206. package/src/core/code-flow/scripts/codeflow/core/cf_native_paths.py +0 -26
  207. package/src/core/code-flow/scripts/codeflow/core/cf_patch.py +0 -33
  208. package/src/core/code-flow/scripts/codeflow/core/cf_runtime_install.py +0 -32
  209. package/src/core/code-flow/scripts/codeflow/core/cf_session_state.py +0 -37
  210. package/src/core/code-flow/scripts/codeflow/core/cf_stop_guard.py +0 -53
  211. package/src/core/code-flow/scripts/codeflow/core/cf_task_state.py +0 -9
  212. package/src/core/code-flow/scripts/codeflow/core/cf_validators.py +0 -408
  213. package/src/core/code-flow/scripts/codeflow/core/cf_worktree_env.py +0 -253
  214. package/src/core/code-flow/scripts/codeflow/evidence/__init__.py +0 -0
  215. package/src/core/code-flow/scripts/codeflow/evidence/cf_evidence_store.py +0 -527
  216. package/src/core/code-flow/scripts/codeflow/hooks/__init__.py +0 -0
  217. package/src/core/code-flow/scripts/codeflow/hooks/cf_codex_hook.py +0 -95
  218. package/src/core/code-flow/scripts/codeflow/hooks/cf_opencode_event.py +0 -98
  219. package/src/core/code-flow/scripts/codeflow/hooks/cf_post_hook.py +0 -30
  220. package/src/core/code-flow/scripts/codeflow/hooks/cf_pre_tool_hook.py +0 -41
  221. package/src/core/code-flow/scripts/codeflow/hooks/cf_stop_hook.py +0 -292
  222. package/src/core/code-flow/scripts/codeflow/hooks/cf_user_prompt_hook.py +0 -161
  223. package/src/core/code-flow/scripts/codeflow/spec/__init__.py +0 -0
  224. package/src/core/code-flow/scripts/codeflow/spec/cf_active_task.py +0 -412
  225. package/src/core/code-flow/scripts/codeflow/spec/cf_context_refresh.py +0 -301
  226. package/src/core/code-flow/scripts/codeflow/spec/cf_edit_service.py +0 -149
  227. package/src/core/code-flow/scripts/codeflow/spec/cf_injection.py +0 -111
  228. package/src/core/code-flow/scripts/codeflow/spec/cf_rule_readiness.py +0 -114
  229. package/src/core/code-flow/scripts/codeflow/spec/cf_scan.py +0 -367
  230. package/src/core/code-flow/scripts/codeflow/spec/cf_spec_budget.py +0 -55
  231. package/src/core/code-flow/scripts/codeflow/spec/cf_spec_context_model.py +0 -477
  232. package/src/core/code-flow/scripts/codeflow/spec/cf_spec_impact.py +0 -119
  233. package/src/core/code-flow/scripts/codeflow/spec/cf_spec_metadata.py +0 -387
  234. package/src/core/code-flow/scripts/codeflow/spec/cf_spec_migrate.py +0 -529
  235. package/src/core/code-flow/scripts/codeflow/spec/cf_spec_optimize.py +0 -193
  236. package/src/core/code-flow/scripts/codeflow/spec/cf_spec_optimize_apply.py +0 -187
  237. package/src/core/code-flow/scripts/codeflow/spec/cf_spec_resolver.py +0 -369
  238. package/src/core/code-flow/scripts/codeflow/spec/cf_spec_router.py +0 -304
  239. package/src/core/code-flow/scripts/codeflow/spec/cf_spec_verify.py +0 -687
  240. package/src/core/code-flow/scripts/codeflow/spec/cf_stage_gate.py +0 -330
  241. package/src/core/code-flow/scripts/codeflow/spec/cf_task_projection.py +0 -83
  242. package/src/core/code-flow/scripts/codeflow/spec/cf_workflow_service.py +0 -755
  243. package/src/core/code-flow/scripts/codeflow/spec/cf_workflow_transaction.py +0 -110
  244. package/src/core/code-flow/scripts/codeflow/task/__init__.py +0 -0
  245. package/src/core/code-flow/scripts/codeflow/task/cf_requirement_verification.py +0 -867
  246. package/src/core/code-flow/scripts/codeflow/task/cf_task_doc.py +0 -178
  247. package/src/core/code-flow/scripts/codeflow/task/cf_task_merge.py +0 -635
  248. package/src/core/code-flow/scripts/codeflow/task/cf_task_plan.py +0 -251
  249. package/src/core/code-flow/scripts/codeflow/task/cf_task_runtime.py +0 -570
  250. package/src/core/code-flow/scripts/codeflow/task/cf_validation_service.py +0 -73
  251. package/src/core/code-flow/scripts/runtime-resources.json +0 -29
  252. package/src/core/code-flow/specs/backend/_map.md +0 -56
  253. package/src/core/code-flow/specs/backend/code-quality-performance.md +0 -59
  254. package/src/core/code-flow/specs/backend/database.md +0 -51
  255. package/src/core/code-flow/specs/backend/directory-structure.md +0 -54
  256. package/src/core/code-flow/specs/backend/logging.md +0 -50
  257. package/src/core/code-flow/specs/backend/platform-rules.md +0 -55
  258. package/src/core/code-flow/specs/frontend/_map.md +0 -52
  259. package/src/core/code-flow/specs/frontend/component-specs.md +0 -68
  260. package/src/core/code-flow/specs/frontend/directory-structure.md +0 -53
  261. package/src/core/code-flow/specs/frontend/quality-standards.md +0 -58
  262. package/src/core/code-flow/specs/shared/_map.md +0 -57
  263. package/src/core/code-flow/specs/shared/scenarios-template.md +0 -55
  264. package/src/core/code-flow/tasks/.gitkeep +0 -0
  265. package/src/core/code-flow/validation.yml +0 -59
  266. package/src/migrate/runtime.js +0 -213
  267. package/src/migrate/spec-workflow.js +0 -253
  268. package/src/runtime.js +0 -219
package/src/runtime.js DELETED
@@ -1,219 +0,0 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
- const crypto = require('crypto');
6
- const { spawnSync } = require('child_process');
7
- const pkg = require('../package.json');
8
-
9
- function projectRoot(cwd) {
10
- let current = path.resolve(cwd);
11
- while (true) {
12
- if (fs.existsSync(path.join(current, '.code-flow/config.yml'))) return current;
13
- const parent = path.dirname(current);
14
- if (parent === current) throw new Error('Run code-flow inside an initialized project.');
15
- current = parent;
16
- }
17
- }
18
-
19
- function commands(root) {
20
- const registry = JSON.parse(fs.readFileSync(path.join(root, '.code-flow/runtime-commands.json'), 'utf8'));
21
- if (registry.schema_version !== 1) throw new Error('Unsupported runtime command contract.');
22
- return registry.commands;
23
- }
24
-
25
- function projectCommandFamily(name) {
26
- if (!name || name.startsWith('-')) return false;
27
- let current = path.resolve(process.cwd());
28
- while (true) {
29
- if (fs.existsSync(path.join(current, '.code-flow/runtime-commands.json'))) {
30
- try {
31
- return Object.keys(commands(current)).some(command => command.split(' ')[0] === name);
32
- } catch (error) {
33
- process.stderr.write(`code-flow contract: ${error.message}\n`);
34
- return false;
35
- }
36
- }
37
- const parent = path.dirname(current);
38
- if (parent === current) return false;
39
- current = parent;
40
- }
41
- }
42
-
43
- function versionRepair(root) {
44
- const file = path.join(root, '.code-flow/.version');
45
- if (fs.existsSync(file)) {
46
- const version = fs.readFileSync(file, 'utf8').trim();
47
- if (/^\d+\.\d+\.\d+$/.test(version)) {
48
- const installed = version.split('.').map(Number);
49
- const current = pkg.version.split('.').map(Number);
50
- const index = installed.findIndex((value, position) => value !== current[position]);
51
- if (index >= 0 && installed[index] > current[index]) {
52
- return `CLI ${pkg.version} is older than project runtime ${version}. Upgrade the CLI: npm install --global ${pkg.name}@${version}`;
53
- }
54
- }
55
- }
56
- return 'code-flow migrate --runtime --dry-run';
57
- }
58
-
59
- function digest(file) {
60
- return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
61
- }
62
-
63
- function artifactDigest(relative, source) {
64
- if (relative !== '.opencode/plugins/code-flow/package.json') return digest(source);
65
- const data = JSON.parse(fs.readFileSync(source, 'utf8'));
66
- data.version = pkg.version;
67
- return crypto.createHash('sha256').update(JSON.stringify(data, null, 2) + '\n').digest('hex');
68
- }
69
-
70
- function filesUnder(directory, prefix) {
71
- if (!fs.existsSync(directory)) return [];
72
- return fs.readdirSync(directory, { withFileTypes: true }).flatMap(entry => {
73
- if (entry.name === '__pycache__' || entry.name === '.DS_Store') return [];
74
- const relative = path.posix.join(prefix, entry.name);
75
- return entry.isDirectory() ? filesUnder(path.join(directory, entry.name), relative) : [relative];
76
- });
77
- }
78
-
79
- function artifactSources(platforms) {
80
- const base = path.join(__dirname, 'core/code-flow');
81
- const sources = { '.code-flow/runtime-commands.json': path.join(base, 'runtime-commands.json') };
82
- for (const relative of filesUnder(path.join(base, 'scripts'), 'scripts')) {
83
- sources[`.code-flow/${relative}`] = path.join(base, relative);
84
- }
85
- for (const platform of platforms) {
86
- const adapter = path.join(__dirname, 'adapters', platform);
87
- const area = platform === 'codex' ? 'skills' : 'commands';
88
- const destination = platform === 'codex' ? '.agents' : `.${platform}`;
89
- for (const relative of filesUnder(path.join(adapter, area), area)) {
90
- sources[`${destination}/${relative}`] = path.join(adapter, relative);
91
- }
92
- if (platform === 'opencode') {
93
- for (const relative of filesUnder(path.join(adapter, 'plugins'), 'plugins')) {
94
- sources[`.opencode/${relative}`] = path.join(adapter, relative);
95
- }
96
- }
97
- }
98
- return sources;
99
- }
100
-
101
- function hookContracts(platforms) {
102
- const contracts = {};
103
- for (const platform of platforms.filter(value => value !== 'opencode')) {
104
- const name = platform === 'codex' ? 'hooks.json' : 'settings.local.json';
105
- const source = path.join(__dirname, 'adapters', platform, name);
106
- contracts[`.${platform}/${name}`] = JSON.parse(fs.readFileSync(source, 'utf8')).hooks;
107
- }
108
- return contracts;
109
- }
110
-
111
- function writeInstall(root, platforms) {
112
- const files = Object.fromEntries(Object.entries(artifactSources(platforms))
113
- .map(([relative, source]) => [relative, artifactDigest(relative, source)]));
114
- const manifest = { schema_version: 1, version: pkg.version, platforms,
115
- files, hook_contracts: hookContracts(platforms) };
116
- const target = path.join(root, '.code-flow/.runtime-install.json');
117
- fs.writeFileSync(`${target}.tmp`, JSON.stringify(manifest, null, 2) + '\n');
118
- fs.renameSync(`${target}.tmp`, target);
119
- }
120
-
121
- function verifyHooks(root, contracts) {
122
- for (const [relative, expected] of Object.entries(contracts)) {
123
- const actual = JSON.parse(fs.readFileSync(path.join(root, relative), 'utf8')).hooks;
124
- for (const [event, groups] of Object.entries(expected)) {
125
- for (const group of groups) {
126
- const candidates = (actual[event] || []).filter(item => (item.matcher || '') === (group.matcher || ''));
127
- for (const hook of group.hooks) {
128
- if (!candidates.some(item => item.hooks.some(value => JSON.stringify(value) === JSON.stringify(hook)))) {
129
- throw new Error(`Managed hook has drifted: ${relative}:${event}`);
130
- }
131
- }
132
- }
133
- }
134
- }
135
- }
136
-
137
- function verify(root, duringMigration = false) {
138
- if (!duringMigration && fs.existsSync(path.join(root, '.code-flow/.runtime-migration.lock'))) {
139
- throw new Error('Runtime migration is in progress or needs recovery.');
140
- }
141
- const manifest = JSON.parse(fs.readFileSync(path.join(root, '.code-flow/.runtime-install.json'), 'utf8'));
142
- const version = fs.readFileSync(path.join(root, '.code-flow/.version'), 'utf8').trim();
143
- if (manifest.schema_version !== 1 || manifest.version !== version || version !== pkg.version) {
144
- throw new Error(`Runtime ${version} and CLI ${pkg.version} versions differ.`);
145
- }
146
- if (!manifest.files || Object.keys(manifest.files).length === 0 || !manifest.hook_contracts) {
147
- throw new Error('Runtime installation manifest is incomplete.');
148
- }
149
- const expected = artifactSources(manifest.platforms);
150
- if (Object.keys(expected).length !== Object.keys(manifest.files).length) {
151
- throw new Error('Installed artifact set differs from the CLI contract.');
152
- }
153
- for (const [relative, source] of Object.entries(expected)) {
154
- if (manifest.files[relative] !== artifactDigest(relative, source)) {
155
- throw new Error(`Installed contract differs from the CLI package: ${relative}`);
156
- }
157
- }
158
- for (const [relative, hash] of Object.entries(manifest.files)) {
159
- const target = path.resolve(root, relative);
160
- if (!target.startsWith(root + path.sep) || digest(target) !== hash) {
161
- throw new Error(`Managed runtime artifact has drifted: ${relative}`);
162
- }
163
- }
164
- verifyHooks(root, manifest.hook_contracts);
165
- }
166
-
167
- function execute(root, contract, values, scripts = path.join(root, '.code-flow/scripts')) {
168
- try {
169
- if (values.length === 1 || (values.length === 2 && ['--help', '-h'].includes(values[1]))) {
170
- const family = Object.keys(contract).filter(command => command.startsWith(values[0] + ' '));
171
- if (family.length) {
172
- process.stdout.write(`Usage:\n${family.map(command => ` code-flow ${command} [options]`).join('\n')}\nUse <command> --help for its actual parameters.\n`);
173
- return 0;
174
- }
175
- }
176
- const key = Object.keys(contract).sort((a, b) => b.length - a.length)
177
- .find(command => command.split(' ').every((part, index) => values[index] === part));
178
- if (!key) throw new Error(`Unknown runtime command. Available: ${Object.keys(contract).join(', ')}`);
179
- const entry = contract[key];
180
- if (!/^cf_[a-z_]+$/.test(entry.module)) throw new Error('Invalid runtime module in command contract.');
181
- const script = path.join(scripts, `${entry.module}.py`);
182
- const args = [...entry.prefix, ...values.slice(key.split(' ').length)];
183
- const publicName = entry.prefix.length ? key.split(' ').slice(0, -entry.prefix.length).join(' ') : key;
184
- const result = spawnSync('python3', [script, ...args], {
185
- cwd: root, stdio: 'inherit', env: { ...process.env,
186
- CF_RUNTIME_COMMAND: `code-flow ${publicName}`, CF_RUNTIME_ACTION: entry.prefix.join(' '),
187
- CF_CLI_VERSION: pkg.version },
188
- });
189
- if (result.error) throw result.error;
190
- return result.status === null ? 2 : result.status;
191
- } catch (error) {
192
- process.stderr.write(`code-flow runtime: ${error.message}\n`);
193
- return 2;
194
- }
195
- }
196
-
197
- function run(values) {
198
- let root;
199
- try {
200
- root = projectRoot(process.cwd());
201
- } catch (error) {
202
- process.stderr.write(`code-flow runtime: ${error.message}\n`);
203
- return 3;
204
- }
205
- try {
206
- if (values[0] === 'spec' && values[1] === 'doctor') {
207
- const base = path.join(__dirname, 'core/code-flow');
208
- const contract = JSON.parse(fs.readFileSync(path.join(base, 'runtime-commands.json'), 'utf8')).commands;
209
- return execute(root, { 'spec doctor': contract['spec doctor'] }, values, path.join(base, 'scripts'));
210
- }
211
- verify(root);
212
- return execute(root, commands(root), values);
213
- } catch (error) {
214
- process.stderr.write(`code-flow installation: ${error.message}\nRepair: ${versionRepair(root)}\n`);
215
- return 3;
216
- }
217
- }
218
-
219
- module.exports = { artifactSources, commands, digest, hookContracts, projectCommandFamily, projectRoot, run, verify, writeInstall };