@hassangameryt/freeflow 0.5.0 → 0.7.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 (301) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/.codex-plugin/plugin.json +4 -5
  4. package/README.md +141 -163
  5. package/capabilities/cognitive-routing/SKILL.md +387 -0
  6. package/capabilities/context-virtualization/SKILL.md +103 -0
  7. package/capabilities/conversation-history/SKILL.md +53 -0
  8. package/command-surface.json +6 -37
  9. package/hooks/freeflow-runtime-context.mjs +50 -541
  10. package/hooks/hooks.json +1 -26
  11. package/hooks/tests/check-runtime-context-hook.sh +114 -308
  12. package/package.json +27 -17
  13. package/pi-extension/dist/cognitive-routing/commands.js +96 -0
  14. package/pi-extension/dist/cognitive-routing/config.js +232 -0
  15. package/pi-extension/dist/cognitive-routing/controller.js +1292 -0
  16. package/pi-extension/dist/cognitive-routing/history.js +451 -0
  17. package/pi-extension/dist/cognitive-routing/host.js +188 -0
  18. package/pi-extension/dist/cognitive-routing/pi-routing-session.js +414 -0
  19. package/pi-extension/dist/cognitive-routing/pi-session-control.js +320 -0
  20. package/pi-extension/dist/cognitive-routing/runtime.js +43 -0
  21. package/pi-extension/dist/cognitive-routing/tool.js +212 -0
  22. package/pi-extension/dist/cognitive-routing/types.js +6 -0
  23. package/pi-extension/dist/context-virtualization/commands.js +89 -0
  24. package/pi-extension/dist/context-virtualization/projector.js +42 -0
  25. package/pi-extension/dist/context-virtualization/runtime.js +356 -0
  26. package/pi-extension/dist/context-virtualization/state.js +57 -0
  27. package/pi-extension/dist/context-virtualization/types.js +10 -0
  28. package/pi-extension/dist/conversation-history/passages.js +66 -0
  29. package/pi-extension/dist/conversation-history/ranking.js +105 -0
  30. package/pi-extension/dist/conversation-history/runtime.js +226 -0
  31. package/pi-extension/dist/conversation-history/search.js +56 -0
  32. package/pi-extension/dist/conversation-history/types.js +1 -0
  33. package/pi-extension/dist/freeflow-context/resolver.js +120 -0
  34. package/pi-extension/dist/freeflow-context/runtime.js +80 -0
  35. package/pi-extension/dist/freeflow-context/source-projector.js +95 -0
  36. package/pi-extension/dist/freeflow-context/tool.js +308 -0
  37. package/pi-extension/dist/freeflow-context/types.js +12 -0
  38. package/pi-extension/dist/freeflow-context/visibility.js +28 -0
  39. package/pi-extension/dist/index.js +564 -129
  40. package/pi-extension/dist/runtime/runtime-context.js +357 -470
  41. package/pi-extension/dist/runtime/runtime-identity.js +5 -0
  42. package/pi-extension/dist/settings/settings-tui.js +78 -0
  43. package/pi-extension/dist/settings/settings-ui.js +572 -826
  44. package/pi-extension/dist/ui/text-component.js +57 -0
  45. package/pi-extension/src/cognitive-routing/commands.ts +129 -0
  46. package/pi-extension/src/cognitive-routing/config.ts +303 -0
  47. package/pi-extension/src/cognitive-routing/controller.ts +1628 -0
  48. package/pi-extension/src/cognitive-routing/history.ts +650 -0
  49. package/pi-extension/src/cognitive-routing/host.ts +266 -0
  50. package/pi-extension/src/cognitive-routing/pi-routing-session.ts +538 -0
  51. package/pi-extension/src/cognitive-routing/pi-session-control.ts +444 -0
  52. package/pi-extension/src/cognitive-routing/runtime.ts +57 -0
  53. package/pi-extension/src/cognitive-routing/tool.ts +273 -0
  54. package/pi-extension/src/cognitive-routing/types.ts +89 -0
  55. package/pi-extension/src/context-virtualization/commands.ts +111 -0
  56. package/pi-extension/src/context-virtualization/projector.ts +62 -0
  57. package/pi-extension/src/context-virtualization/runtime.ts +406 -0
  58. package/pi-extension/src/context-virtualization/state.ts +79 -0
  59. package/pi-extension/src/context-virtualization/types.ts +92 -0
  60. package/pi-extension/src/conversation-history/passages.ts +81 -0
  61. package/pi-extension/src/conversation-history/ranking.ts +155 -0
  62. package/pi-extension/src/conversation-history/runtime.ts +250 -0
  63. package/pi-extension/src/conversation-history/search.ts +79 -0
  64. package/pi-extension/src/conversation-history/types.ts +31 -0
  65. package/pi-extension/src/freeflow-context/resolver.ts +139 -0
  66. package/pi-extension/src/freeflow-context/runtime.ts +100 -0
  67. package/pi-extension/src/freeflow-context/source-projector.ts +106 -0
  68. package/pi-extension/src/freeflow-context/tool.ts +360 -0
  69. package/pi-extension/src/freeflow-context/types.ts +70 -0
  70. package/pi-extension/src/freeflow-context/visibility.ts +53 -0
  71. package/pi-extension/src/index.ts +597 -135
  72. package/pi-extension/src/runtime/runtime-context.ts +373 -491
  73. package/pi-extension/src/runtime/runtime-identity.ts +8 -0
  74. package/pi-extension/src/settings/settings-tui.ts +93 -0
  75. package/pi-extension/src/settings/settings-ui.ts +679 -910
  76. package/pi-extension/src/ui/text-component.ts +66 -0
  77. package/pi-extension/tests/cognitive-routing/commands.test.js +134 -0
  78. package/pi-extension/tests/cognitive-routing/controller.test.js +1128 -0
  79. package/pi-extension/tests/cognitive-routing/extension-lifecycle.test.js +1142 -0
  80. package/pi-extension/tests/cognitive-routing/history.test.js +274 -0
  81. package/pi-extension/tests/cognitive-routing/host-fixture.js +4 -0
  82. package/pi-extension/tests/cognitive-routing/host-identity.test.js +63 -0
  83. package/pi-extension/tests/cognitive-routing/pi-normal-lifecycle.test.js +434 -0
  84. package/pi-extension/tests/cognitive-routing/pi-session-control.test.js +174 -0
  85. package/pi-extension/tests/cognitive-routing/pi-session-history.test.js +91 -0
  86. package/pi-extension/tests/cognitive-routing/profile-resolution.test.js +279 -0
  87. package/pi-extension/tests/cognitive-routing/tool.test.js +230 -0
  88. package/pi-extension/tests/context-virtualization/extension.test.js +749 -0
  89. package/pi-extension/tests/context-virtualization/projector.test.js +110 -0
  90. package/pi-extension/tests/context-virtualization/runtime.test.js +216 -0
  91. package/pi-extension/tests/conversation-history/runtime.test.js +98 -0
  92. package/pi-extension/tests/conversation-history/search.test.js +97 -0
  93. package/pi-extension/tests/integration/mode-free-core.test.js +218 -0
  94. package/pi-extension/tests/integration/pi-extension.test.js +532 -3701
  95. package/pi-extension/tests/integration/prompt-architecture.test.js +350 -0
  96. package/pi-extension/tests/integration/unaffected-regressions.test.js +411 -0
  97. package/runtime/prompts/cognitive-routing.md +10 -0
  98. package/runtime/prompts/context-virtualization.md +3 -0
  99. package/runtime/prompts/conversation-history.md +3 -0
  100. package/runtime/prompts/core.md +84 -0
  101. package/runtime/prompts/interaction-contract.md +11 -0
  102. package/skills/action-selection/SKILL.md +140 -0
  103. package/skills/action-selection/references/trajectory-stalls.md +71 -0
  104. package/skills/bypass/SKILL.md +109 -38
  105. package/skills/commit-work/SKILL.md +94 -55
  106. package/skills/commit-work/references/git-state-edges.md +94 -0
  107. package/skills/commit-work/references/simple-push.md +92 -0
  108. package/skills/commit-work/references/staging-decisions.md +79 -46
  109. package/skills/decision-gate/SKILL.md +80 -22
  110. package/skills/design-for-depth/SKILL.md +206 -60
  111. package/skills/design-for-depth/references/design-pressure-signals.md +108 -240
  112. package/skills/design-for-depth/references/interface-design-loop.md +77 -97
  113. package/skills/design-for-depth/references/module-and-dependency-design.md +147 -0
  114. package/skills/design-for-depth/references/software-design-philosophy.md +128 -174
  115. package/skills/design-for-depth/references/state-and-failure-boundaries.md +136 -0
  116. package/skills/diagnose-failure/SKILL.md +200 -48
  117. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +18 -6
  118. package/skills/diagnose-failure/references/{flaky-and-performance.md → flaky-and-performance-diagnosis.md} +13 -5
  119. package/skills/discuss/SKILL.md +119 -65
  120. package/skills/discuss/references/discussion-continuity.md +123 -0
  121. package/skills/evaluate-skill/SKILL.md +166 -52
  122. package/skills/evaluate-skill/references/{definition-schema.md → evaluation-definition-schema.md} +43 -3
  123. package/skills/evaluate-skill/references/evaluation-design.md +56 -30
  124. package/skills/evaluate-skill/references/execution-and-evidence.md +8 -3
  125. package/skills/evaluate-skill/references/review-and-revision.md +62 -21
  126. package/skills/evaluate-skill/scripts/lib/definitions.mjs +114 -3
  127. package/skills/evaluate-skill/scripts/lib/grade-interaction.mjs +156 -1
  128. package/skills/evaluate-skill/scripts/lib/grade.mjs +9 -1
  129. package/skills/evaluate-skill/scripts/lib/pi.mjs +147 -32
  130. package/skills/evaluate-skill/scripts/lib/rpc.mjs +4 -4
  131. package/skills/evaluate-skill/scripts/lib/runner.mjs +36 -14
  132. package/skills/evaluate-skill/scripts/lib/sandbox.mjs +92 -0
  133. package/skills/evaluate-skill/scripts/pi-guard.mjs +1 -1
  134. package/skills/evaluate-skill/scripts/pi-observer.mjs +66 -0
  135. package/skills/evaluate-skill/scripts/skill-eval.mjs +6 -5
  136. package/skills/execute-work/SKILL.md +99 -57
  137. package/skills/execute-work/references/code-practices.md +4 -4
  138. package/skills/execute-work/references/execute-work-edges.md +62 -0
  139. package/skills/finish-branch/SKILL.md +2 -2
  140. package/skills/finish-branch/references/integration-options.md +1 -1
  141. package/skills/handoff/SKILL.md +3 -3
  142. package/skills/launch-work/SKILL.md +1 -1
  143. package/skills/launch-work/references/launch-readiness.md +2 -2
  144. package/skills/migration-work/SKILL.md +2 -2
  145. package/skills/migration-work/references/migration-lifecycle.md +1 -1
  146. package/skills/release-work/SKILL.md +1 -1
  147. package/skills/release-work/references/release-evidence.md +1 -1
  148. package/skills/review-artifact/SKILL.md +129 -66
  149. package/skills/review-artifact/references/adjudicate-artifact-review.md +101 -0
  150. package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +159 -0
  151. package/skills/review-work/SKILL.md +113 -68
  152. package/skills/review-work/references/adjudicate-work-review.md +97 -0
  153. package/skills/review-work/references/independent-work-reviewer-contract.md +139 -0
  154. package/skills/review-work/references/security-risk-lens.md +18 -16
  155. package/skills/setup-freeflow/SKILL.md +16 -17
  156. package/skills/setup-freeflow/references/activation-contract.md +24 -30
  157. package/skills/setup-freeflow/references/host-setup.md +6 -7
  158. package/skills/simplify-code/SKILL.md +97 -39
  159. package/skills/simplify-code/references/simplification-patterns.md +1 -1
  160. package/skills/tdd/SKILL.md +100 -53
  161. package/skills/tdd/references/test-design.md +3 -3
  162. package/skills/track-work/SKILL.md +195 -58
  163. package/skills/track-work/references/working-record-format.md +515 -0
  164. package/skills/track-work/scripts/lib/document.mjs +713 -0
  165. package/skills/track-work/scripts/lib/format.mjs +166 -0
  166. package/skills/track-work/scripts/lib/store.mjs +233 -0
  167. package/skills/track-work/scripts/lib/transitions.mjs +790 -0
  168. package/skills/track-work/scripts/lib/views.mjs +40 -0
  169. package/skills/track-work/scripts/working-record.mjs +316 -0
  170. package/skills/verify-work/SKILL.md +57 -19
  171. package/skills/verify-work/references/browser-runtime-evidence.md +2 -2
  172. package/skills/verify-work/references/integration-evidence.md +1 -7
  173. package/skills/verify-work/references/performance-evidence.md +2 -2
  174. package/skills/workflow/SKILL.md +137 -55
  175. package/skills/workflow/references/domain-skill-composition.md +26 -40
  176. package/skills/write-plan/SKILL.md +1 -1
  177. package/skills/write-skill/SKILL.md +218 -61
  178. package/skills/write-skill/references/activation-boundaries.md +63 -21
  179. package/skills/write-skill/references/agent-first-instructions.md +61 -27
  180. package/skills/write-skill/references/progressive-disclosure.md +97 -33
  181. package/skills/write-skill/references/skill-dependency-graphs.md +190 -0
  182. package/capabilities/output-router/SKILL.md +0 -206
  183. package/capabilities/output-router/references/safety-policy.md +0 -40
  184. package/capabilities/output-router/references/setup.md +0 -110
  185. package/pi-extension/dist/output-router/host-producer-identification.js +0 -232
  186. package/pi-extension/dist/output-router/native-safety-net.js +0 -216
  187. package/pi-extension/dist/output-router/observed-tool-routing.js +0 -122
  188. package/pi-extension/dist/output-router/renderers.js +0 -1023
  189. package/pi-extension/dist/output-router/router-tools.js +0 -554
  190. package/pi-extension/dist/output-router/schemas.js +0 -383
  191. package/pi-extension/dist/output-router/status.js +0 -744
  192. package/pi-extension/dist/output-router/utils.js +0 -502
  193. package/pi-extension/src/output-router/host-producer-identification.ts +0 -256
  194. package/pi-extension/src/output-router/native-safety-net.ts +0 -248
  195. package/pi-extension/src/output-router/observed-tool-routing.ts +0 -134
  196. package/pi-extension/src/output-router/renderers.ts +0 -1108
  197. package/pi-extension/src/output-router/router-tools.ts +0 -579
  198. package/pi-extension/src/output-router/schemas.ts +0 -387
  199. package/pi-extension/src/output-router/status.ts +0 -791
  200. package/pi-extension/src/output-router/utils.ts +0 -554
  201. package/pi-extension/tests/output-router/observed-producers.test.js +0 -132
  202. package/pi-extension/tests/output-router/search.test.js +0 -722
  203. package/router/dist/benchmarks/benchmark-harness.d.ts +0 -35
  204. package/router/dist/benchmarks/benchmark-harness.js +0 -92
  205. package/router/dist/benchmarks/benchmarks.d.ts +0 -109
  206. package/router/dist/benchmarks/benchmarks.js +0 -979
  207. package/router/dist/benchmarks/codex-qa-benchmarks.d.ts +0 -95
  208. package/router/dist/benchmarks/codex-qa-benchmarks.js +0 -500
  209. package/router/dist/benchmarks/command-benchmarks.d.ts +0 -143
  210. package/router/dist/benchmarks/command-benchmarks.js +0 -698
  211. package/router/dist/benchmarks/context-mode-normalized-benchmarks.d.ts +0 -82
  212. package/router/dist/benchmarks/context-mode-normalized-benchmarks.js +0 -773
  213. package/router/dist/benchmarks/context-mode-real-deep-benchmark.d.ts +0 -132
  214. package/router/dist/benchmarks/context-mode-real-deep-benchmark.js +0 -1380
  215. package/router/dist/benchmarks/context-saving-baseline.d.ts +0 -60
  216. package/router/dist/benchmarks/context-saving-baseline.js +0 -470
  217. package/router/dist/benchmarks/index-benchmarks.d.ts +0 -103
  218. package/router/dist/benchmarks/index-benchmarks.js +0 -862
  219. package/router/dist/benchmarks/storage-policy-benchmarks.d.ts +0 -101
  220. package/router/dist/benchmarks/storage-policy-benchmarks.js +0 -618
  221. package/router/dist/benchmarks/vault-index-benchmarks.d.ts +0 -56
  222. package/router/dist/benchmarks/vault-index-benchmarks.js +0 -322
  223. package/router/dist/config/config.d.ts +0 -103
  224. package/router/dist/config/config.js +0 -632
  225. package/router/dist/config/router-contract.d.ts +0 -10
  226. package/router/dist/config/router-contract.js +0 -54
  227. package/router/dist/config/schema.d.ts +0 -27
  228. package/router/dist/config/schema.js +0 -648
  229. package/router/dist/config/types.d.ts +0 -573
  230. package/router/dist/config/types.js +0 -41
  231. package/router/dist/evidence/bounded-evidence.d.ts +0 -40
  232. package/router/dist/evidence/bounded-evidence.js +0 -323
  233. package/router/dist/evidence/evidence-range-selector.d.ts +0 -24
  234. package/router/dist/evidence/evidence-range-selector.js +0 -232
  235. package/router/dist/evidence/evidence-search.d.ts +0 -27
  236. package/router/dist/evidence/evidence-search.js +0 -490
  237. package/router/dist/evidence/evidence.d.ts +0 -38
  238. package/router/dist/evidence/evidence.js +0 -200
  239. package/router/dist/evidence/failure-contracts.d.ts +0 -37
  240. package/router/dist/evidence/failure-contracts.js +0 -166
  241. package/router/dist/evidence/line-ranges.d.ts +0 -24
  242. package/router/dist/evidence/line-ranges.js +0 -22
  243. package/router/dist/experiments/local-index.d.ts +0 -64
  244. package/router/dist/experiments/local-index.js +0 -581
  245. package/router/dist/index.d.ts +0 -30
  246. package/router/dist/index.js +0 -32
  247. package/router/dist/local/local-traversal.d.ts +0 -18
  248. package/router/dist/local/local-traversal.js +0 -334
  249. package/router/dist/processing/engine.d.ts +0 -132
  250. package/router/dist/processing/engine.js +0 -533
  251. package/router/dist/processing/reducers.d.ts +0 -280
  252. package/router/dist/processing/reducers.js +0 -1673
  253. package/router/dist/processing/renderers.d.ts +0 -31
  254. package/router/dist/processing/renderers.js +0 -195
  255. package/router/dist/processing/scripts.d.ts +0 -91
  256. package/router/dist/processing/scripts.js +0 -386
  257. package/router/dist/repo/repo-traversal.d.ts +0 -18
  258. package/router/dist/repo/repo-traversal.js +0 -245
  259. package/router/dist/routing/observed-reducers.d.ts +0 -41
  260. package/router/dist/routing/observed-reducers.js +0 -661
  261. package/router/dist/routing/observed-routing.d.ts +0 -26
  262. package/router/dist/routing/observed-routing.js +0 -378
  263. package/router/dist/routing/parsers.d.ts +0 -16
  264. package/router/dist/routing/parsers.js +0 -521
  265. package/router/dist/routing/run-filters.d.ts +0 -60
  266. package/router/dist/routing/run-filters.js +0 -238
  267. package/router/dist/routing/run-reducers.d.ts +0 -49
  268. package/router/dist/routing/run-reducers.js +0 -122
  269. package/router/dist/sandbox/adapter-roots.d.ts +0 -46
  270. package/router/dist/sandbox/adapter-roots.js +0 -66
  271. package/router/dist/sandbox/eryx-python-adapter.d.ts +0 -13
  272. package/router/dist/sandbox/eryx-python-adapter.js +0 -868
  273. package/router/dist/sandbox/jq-wasm-adapter.d.ts +0 -9
  274. package/router/dist/sandbox/jq-wasm-adapter.js +0 -429
  275. package/router/dist/sandbox/quickjs-wasi-adapter.d.ts +0 -13
  276. package/router/dist/sandbox/quickjs-wasi-adapter.js +0 -384
  277. package/router/dist/sandbox/script-sandbox.d.ts +0 -134
  278. package/router/dist/sandbox/script-sandbox.js +0 -337
  279. package/router/dist/setup/script-transform-adapters.d.ts +0 -32
  280. package/router/dist/setup/script-transform-adapters.js +0 -278
  281. package/router/dist/tools/batch.d.ts +0 -33
  282. package/router/dist/tools/batch.js +0 -586
  283. package/router/dist/tools/run.d.ts +0 -62
  284. package/router/dist/tools/run.js +0 -1304
  285. package/router/dist/tools/search.d.ts +0 -66
  286. package/router/dist/tools/search.js +0 -1364
  287. package/router/dist/transform/engine.d.ts +0 -168
  288. package/router/dist/transform/engine.js +0 -2072
  289. package/router/dist/vault/vault-index.d.ts +0 -125
  290. package/router/dist/vault/vault-index.js +0 -416
  291. package/router/dist/vault/vault.d.ts +0 -138
  292. package/router/dist/vault/vault.js +0 -630
  293. package/runtime/interaction-contract.md +0 -16
  294. package/skills/discuss/references/checkpoints.md +0 -66
  295. package/skills/execute-work/references/execution-loop.md +0 -77
  296. package/skills/mode-contract/SKILL.md +0 -104
  297. package/skills/review-artifact/references/reviewer-prompt.md +0 -148
  298. package/skills/review-work/references/reviewer-prompt.md +0 -134
  299. package/skills/track-work/references/working-record-schema.md +0 -378
  300. package/skills/workflow/references/workflow-loop.md +0 -92
  301. /package/skills/handoff/references/{templates.md → handoff-templates.md} +0 -0
@@ -0,0 +1,166 @@
1
+ export const SCHEMA_VERSION = 4;
2
+
3
+ export const TOP_LEVEL_SECTIONS = ["Current Context", "Current Work", "Future Work", "History", "Notes"];
4
+ export const CONTEXT_HEADINGS = [
5
+ "Goal",
6
+ "What defines this task",
7
+ "Settled",
8
+ "Tentative",
9
+ "Open",
10
+ "Current direction",
11
+ "Boundaries",
12
+ ];
13
+ export const HISTORY_HEADINGS = ["Decisions", "Checkpoints", "Slices"];
14
+
15
+ export const TASK_STATES = new Set(["active", "paused", "completed", "abandoned"]);
16
+ export const SLICE_TYPES = new Set(["learning", "delivery", "deepening"]);
17
+ export const CURRENT_SLICE_STATES = new Set(["in_progress", "paused"]);
18
+ export const HISTORICAL_SLICE_STATES = new Set(["completed", "blocked", "abandoned"]);
19
+ export const CHECKPOINT_TYPES = new Set(["independent_review", "local_commit", "user_decision", "continuity"]);
20
+ export const FUTURE_CHECKPOINT_STATES = new Set(["proposed", "pending", "deferred"]);
21
+ export const HISTORICAL_CHECKPOINT_STATES = new Set(["completed", "cancelled", "replaced"]);
22
+ export const DECISION_STATES = new Set(["active", "superseded", "retired"]);
23
+
24
+ export const ACTIVE_SLICE_FIELDS = new Set([
25
+ "State",
26
+ "Type",
27
+ "Intended result",
28
+ "Authority source",
29
+ "Scope",
30
+ "Expected evidence",
31
+ "Stop condition",
32
+ "Starting state",
33
+ "Dependencies",
34
+ "Reopened from",
35
+ ]);
36
+ export const PROPOSED_SLICE_FIELDS = new Set(["State", "Type", "Intended result", "Expected evidence", "Dependencies"]);
37
+ export const FUTURE_CHECKPOINT_FIELDS = new Set(["State", "Type", "Condition", "Applies to"]);
38
+ export const HISTORICAL_CHECKPOINT_FIELDS = new Set([
39
+ "State",
40
+ "Type",
41
+ "Condition",
42
+ "Applies to",
43
+ "Result",
44
+ "Evidence",
45
+ "Task effect",
46
+ "Reason",
47
+ "Replaced by",
48
+ ]);
49
+ export const DECISION_FIELDS = new Set([
50
+ "State",
51
+ "Decision",
52
+ "Established by",
53
+ "Rationale",
54
+ "Source references",
55
+ "Consequences",
56
+ "Revisit when",
57
+ "Superseded by",
58
+ "Retired because",
59
+ ]);
60
+ export const HISTORICAL_SLICE_FIELDS = new Set([
61
+ "State",
62
+ "Type",
63
+ "Intended result",
64
+ "Authority source",
65
+ "Result",
66
+ "Evidence and limits",
67
+ "Task effect",
68
+ "Reopened from",
69
+ "Resume when",
70
+ "Reason",
71
+ "Residual effects",
72
+ ]);
73
+
74
+ export const IDS = {
75
+ slice: /^S-(\d{3,})$/,
76
+ checkpoint: /^C-(\d{3,})$/,
77
+ decision: /^D-(\d{3,})$/,
78
+ };
79
+
80
+ export function heading(level, title) {
81
+ return `${"#".repeat(level)} ${title}`;
82
+ }
83
+
84
+ export function canonicalLines(name, state = "active", lastUpdated = new Date().toISOString()) {
85
+ return [
86
+ `# Working Record: ${name}`,
87
+ "",
88
+ "Schema: 4",
89
+ `State: ${state}`,
90
+ `Last updated: ${lastUpdated}`,
91
+ "",
92
+ "## Current Context",
93
+ "",
94
+ "### Goal",
95
+ "",
96
+ "### What defines this task",
97
+ "",
98
+ "### Settled",
99
+ "",
100
+ "### Tentative",
101
+ "",
102
+ "### Open",
103
+ "",
104
+ "### Current direction",
105
+ "",
106
+ "### Boundaries",
107
+ "",
108
+ "## Current Work",
109
+ "",
110
+ "### Current Slice",
111
+ "",
112
+ "None",
113
+ "",
114
+ "### Next useful action",
115
+ "",
116
+ "## Future Work",
117
+ "",
118
+ "## History",
119
+ "",
120
+ "### Decisions",
121
+ "",
122
+ "### Checkpoints",
123
+ "",
124
+ "### Slices",
125
+ "",
126
+ "## Notes",
127
+ "",
128
+ ];
129
+ }
130
+
131
+ export function normalizeNewline(text) {
132
+ return text.includes("\r\n") ? "\r\n" : "\n";
133
+ }
134
+
135
+ export function splitLines(text) {
136
+ const newline = normalizeNewline(text);
137
+ return { lines: text.split(/\r?\n/), newline };
138
+ }
139
+
140
+ export function joinLines(lines, newline) {
141
+ return lines.join(newline);
142
+ }
143
+
144
+ export function parseId(id, kind) {
145
+ const pattern = IDS[kind];
146
+ return typeof id === "string" && pattern ? pattern.exec(id) : null;
147
+ }
148
+
149
+ export function nextId(usedIds, kind) {
150
+ let next = 1;
151
+ for (const id of usedIds) {
152
+ const match = parseId(id, kind);
153
+ if (match) next = Math.max(next, Number(match[1]) + 1);
154
+ }
155
+ return `${kind === "slice" ? "S" : kind === "checkpoint" ? "C" : "D"}-${String(next).padStart(3, "0")}`;
156
+ }
157
+
158
+ export function slugify(value) {
159
+ const slug = String(value)
160
+ .trim()
161
+ .toLowerCase()
162
+ .replace(/[^a-z0-9]+/g, "-")
163
+ .replace(/^-+|-+$/g, "");
164
+ if (!slug) throw new Error("Task name must contain at least one letter or digit");
165
+ return slug;
166
+ }
@@ -0,0 +1,233 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { execFile as execFileCallback } from "node:child_process";
3
+ import { lstat, mkdir, readdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
4
+ import { realpathSync } from "node:fs";
5
+ import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
6
+ import { promisify } from "node:util";
7
+
8
+ const execFile = promisify(execFileCallback);
9
+
10
+ export class StoreError extends Error {
11
+ constructor(code, message, details = {}, exitCode = 1) {
12
+ super(message);
13
+ this.name = "StoreError";
14
+ this.code = code;
15
+ this.details = details;
16
+ this.exitCode = exitCode;
17
+ }
18
+ }
19
+
20
+ function fail(code, message, details = {}, exitCode = 1) {
21
+ throw new StoreError(code, message, details, exitCode);
22
+ }
23
+
24
+ async function gitCommand(root, args) {
25
+ try {
26
+ const result = await execFile("git", ["-C", root, ...args], { maxBuffer: 1024 * 1024 });
27
+ return { ok: true, stdout: result.stdout, stderr: result.stderr };
28
+ } catch (error) {
29
+ return { ok: false, code: error?.code, stdout: error?.stdout ?? "", stderr: error?.stderr ?? "" };
30
+ }
31
+ }
32
+
33
+ async function assertGitSafePath(root, path) {
34
+ const repository = await gitCommand(root, ["rev-parse", "--show-toplevel"]);
35
+ if (!repository.ok) return;
36
+ const repositoryRoot = canonicalPath(repository.stdout.trim());
37
+ const relativePath = relative(repositoryRoot, canonicalPath(path)).split(sep).join("/");
38
+ const ignored = await gitCommand(repositoryRoot, ["check-ignore", "--no-index", "-q", "--", relativePath]);
39
+ if (!ignored.ok) fail("unignored-path", `Record path is not ignored by Git: ${relativePath}`);
40
+ const tracked = await gitCommand(repositoryRoot, ["ls-files", "--error-unmatch", "--", relativePath]);
41
+ if (tracked.ok) fail("tracked-path", `Record path is already tracked by Git: ${relativePath}`);
42
+ }
43
+
44
+ function inside(root, candidate) {
45
+ const rootPath = resolve(root);
46
+ const candidatePath = resolve(candidate);
47
+ const relativePath = relative(rootPath, candidatePath);
48
+ return (
49
+ relativePath === "" || (!relativePath.startsWith(`..${sep}`) && relativePath !== ".." && !isAbsolute(relativePath))
50
+ );
51
+ }
52
+
53
+ async function assertNoSymlinkPath(target, stopAt) {
54
+ let current = resolve(target);
55
+ const boundary = resolve(stopAt);
56
+ while (inside(boundary, current)) {
57
+ try {
58
+ if ((await lstat(current)).isSymbolicLink())
59
+ fail("unsafe-symlink", `Mutable path contains a symlink: ${current}`);
60
+ } catch (error) {
61
+ if (error?.code !== "ENOENT") throw error;
62
+ }
63
+ if (current === boundary) break;
64
+ const parent = dirname(current);
65
+ if (parent === current) break;
66
+ current = parent;
67
+ }
68
+ }
69
+
70
+ export function taskRoot(root) {
71
+ return resolve(root, ".freeflow", "tasks");
72
+ }
73
+
74
+ function canonicalPath(path) {
75
+ const candidate = resolve(path);
76
+ try {
77
+ return realpathSync(candidate);
78
+ } catch {
79
+ const parent = dirname(candidate);
80
+ if (parent === candidate) return candidate;
81
+ try {
82
+ return join(realpathSync(parent), basename(candidate));
83
+ } catch {
84
+ return candidate;
85
+ }
86
+ }
87
+ }
88
+
89
+ export function resolveRecordPath(root, supplied) {
90
+ if (!supplied) fail("missing-record", "A record path is required");
91
+ const rootPath = canonicalPath(root);
92
+ const tasks = canonicalPath(taskRoot(rootPath));
93
+ const candidate = isAbsolute(supplied) ? resolve(supplied) : resolve(rootPath, supplied);
94
+ const recordPath = basename(candidate) === "record.md" ? candidate : join(candidate, "record.md");
95
+ const canonicalRecord = canonicalPath(recordPath);
96
+ if (!inside(tasks, canonicalRecord) || basename(canonicalRecord) !== "record.md")
97
+ fail("unsafe-path", `Record must be a record.md below ${tasks}`);
98
+ const taskDirectory = basename(dirname(canonicalRecord));
99
+ if (!/^task-\d{3}-[a-z0-9][a-z0-9-]*$/.test(taskDirectory))
100
+ fail("unsafe-path", `Invalid task directory: ${taskDirectory}`);
101
+ return canonicalRecord;
102
+ }
103
+
104
+ export async function assertRecordPath(root, recordPath) {
105
+ const path = resolveRecordPath(root, recordPath);
106
+ await assertNoSymlinkPath(path, canonicalPath(taskRoot(root)));
107
+ await assertGitSafePath(root, path);
108
+ return path;
109
+ }
110
+
111
+ export async function readRecord(root, recordPath) {
112
+ const path = await assertRecordPath(root, recordPath);
113
+ try {
114
+ return { path, text: await readFile(path, "utf8") };
115
+ } catch (error) {
116
+ if (error?.code === "ENOENT") fail("missing-record", `Record does not exist: ${path}`);
117
+ fail("record-read-failure", `Could not read record: ${path}`);
118
+ }
119
+ }
120
+
121
+ export async function readInput(inputPath, cwd = process.cwd()) {
122
+ if (!inputPath) return "";
123
+ if (inputPath === "-")
124
+ return new Promise((resolveInput, reject) => {
125
+ let text = "";
126
+ process.stdin.setEncoding("utf8");
127
+ process.stdin.on("data", (chunk) => {
128
+ text += chunk;
129
+ });
130
+ process.stdin.on("end", () => resolveInput(text));
131
+ process.stdin.on("error", reject);
132
+ });
133
+ try {
134
+ return await readFile(isAbsolute(inputPath) ? inputPath : resolve(cwd, inputPath), "utf8");
135
+ } catch {
136
+ fail("input-read-failure", `Could not read input: ${inputPath}`);
137
+ }
138
+ }
139
+
140
+ async function nextTaskNumber(tasks) {
141
+ let entries = [];
142
+ try {
143
+ entries = await readdir(tasks, { withFileTypes: true });
144
+ } catch (error) {
145
+ if (error?.code !== "ENOENT") throw error;
146
+ }
147
+ const used = new Set();
148
+ for (const entry of entries) {
149
+ const match = /^task-(\d{3})-/.exec(entry.name);
150
+ if (entry.isDirectory() && match) used.add(Number(match[1]));
151
+ }
152
+ let number = 1;
153
+ while (used.has(number)) number += 1;
154
+ return number;
155
+ }
156
+
157
+ export async function createRecordPath(root, taskName, slug) {
158
+ const tasks = taskRoot(root);
159
+ await assertGitSafePath(root, tasks);
160
+ await mkdir(tasks, { recursive: true });
161
+ await assertNoSymlinkPath(tasks, canonicalPath(root));
162
+ const number = await nextTaskNumber(tasks);
163
+ const directory = join(tasks, `task-${String(number).padStart(3, "0")}-${slug}`);
164
+ const path = join(directory, "record.md");
165
+ await assertGitSafePath(root, path);
166
+ try {
167
+ await mkdir(directory, { recursive: false });
168
+ } catch (error) {
169
+ if (error?.code === "EEXIST") fail("task-already-exists", `Task directory already exists: ${directory}`);
170
+ throw error;
171
+ }
172
+ return { path, taskName };
173
+ }
174
+
175
+ export async function writeInitialAtomically(root, recordPath, text) {
176
+ const path = await assertRecordPath(root, recordPath);
177
+ const temporaryPath = join(dirname(path), `.${basename(path)}.${process.pid}.${randomUUID()}.tmp`);
178
+ let published = false;
179
+ try {
180
+ await writeFile(temporaryPath, text, { encoding: "utf8", flag: "wx" });
181
+ await rename(temporaryPath, path);
182
+ published = true;
183
+ const confirmedText = await readFile(path, "utf8");
184
+ if (confirmedText !== text) fail("publication-confirmation", "Published record differs from the candidate", {}, 2);
185
+ } catch (error) {
186
+ if (!published) await unlink(temporaryPath).catch(() => undefined);
187
+ if (error instanceof StoreError) throw error;
188
+ fail("publication-failure", `Could not publish record: ${error?.code ?? "write"}`, {}, published ? 2 : 1);
189
+ }
190
+ return { path, changed: true };
191
+ }
192
+
193
+ export async function writeAtomically(root, recordPath, sourceText, candidateText) {
194
+ const path = await assertRecordPath(root, recordPath);
195
+ let currentText;
196
+ try {
197
+ currentText = await readFile(path, "utf8");
198
+ } catch (error) {
199
+ if (error?.code === "ENOENT") fail("missing-record", `Record does not exist: ${path}`);
200
+ throw error;
201
+ }
202
+ if (currentText !== sourceText)
203
+ fail("stale-source", "Record changed while the operation was preparing; nothing was written");
204
+ if (candidateText === sourceText) return { path, changed: false };
205
+
206
+ const temporaryPath = join(dirname(path), `.${basename(path)}.${process.pid}.${randomUUID()}.tmp`);
207
+ let published = false;
208
+ try {
209
+ await writeFile(temporaryPath, candidateText, { encoding: "utf8", flag: "wx" });
210
+ const beforeRename = await readFile(path, "utf8");
211
+ if (beforeRename !== sourceText) fail("stale-source", "Record changed before publication; nothing was written");
212
+ await rename(temporaryPath, path);
213
+ published = true;
214
+ let confirmedText;
215
+ try {
216
+ confirmedText = await readFile(path, "utf8");
217
+ } catch (error) {
218
+ fail("publication-confirmation", `Could not confirm published record: ${error?.code ?? "read"}`, {}, 2);
219
+ }
220
+ if (confirmedText !== candidateText)
221
+ fail("publication-confirmation", "Published record differs from the candidate", {}, 2);
222
+ } catch (error) {
223
+ if (!published) await unlink(temporaryPath).catch(() => undefined);
224
+ if (error instanceof StoreError) throw error;
225
+ fail("publication-failure", `Could not publish record: ${error?.code ?? "write"}`, {}, published ? 2 : 1);
226
+ }
227
+ return { path, changed: true };
228
+ }
229
+
230
+ export function displayPath(root, path) {
231
+ const relativePath = relative(resolve(root), resolve(path));
232
+ return relativePath && !relativePath.startsWith(`..${sep}`) && relativePath !== ".." ? relativePath : path;
233
+ }