@khoavu882/doflow 2.0.0-beta.3 → 2.0.0-beta.5

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 (236) hide show
  1. package/bin/doflow.js +2 -2
  2. package/core/.antigravity-plugin/plugin.json +0 -1
  3. package/core/.claude-plugin/plugin.json +1 -1
  4. package/core/.codex-plugin/plugin.json +1 -1
  5. package/core/.plugin/plugin.json +1 -1
  6. package/core/harnesses/claude/hooks/mcp-tool-guard.sh +7 -17
  7. package/core/harnesses/claude/hooks/pre-bash-guard.sh +7 -17
  8. package/core/harnesses/claude/hooks/pre-implement-gate.sh +7 -18
  9. package/core/harnesses/claude/hooks/session-start.sh +5 -3
  10. package/core/harnesses/claude/hooks/stop-check.sh +5 -3
  11. package/core/harnesses/claude/hooks/subagent-audit.sh +5 -5
  12. package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -3
  13. package/core/harnesses/codex/hooks/mcp-tool-guard.sh +7 -15
  14. package/core/harnesses/codex/hooks/pre-bash-guard.sh +7 -15
  15. package/core/harnesses/codex/hooks/pre-implement-gate.sh +7 -16
  16. package/core/harnesses/codex/hooks/session-start.sh +5 -3
  17. package/core/harnesses/codex/hooks/stop-check.sh +6 -5
  18. package/core/harnesses/codex/hooks/subagent-audit.sh +17 -6
  19. package/core/harnesses/codex/hooks/user-prompt-submit.sh +5 -4
  20. package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +7 -18
  21. package/core/harnesses/gemini/hooks/pre-bash-guard.sh +7 -18
  22. package/core/harnesses/gemini/hooks/pre-implement-gate.sh +7 -19
  23. package/core/harnesses/gemini/hooks/session-start.sh +5 -3
  24. package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +20 -5
  25. package/core/harnesses/kiro/hooks/pre-bash-guard.sh +20 -5
  26. package/core/harnesses/kiro/hooks/pre-implement-gate.sh +20 -5
  27. package/core/harnesses/kiro/hooks/session-start.sh +11 -5
  28. package/core/harnesses/kiro/hooks/stop-check.sh +21 -30
  29. package/core/harnesses/pi/extensions/doflow-guard.js +159 -0
  30. package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +27 -11
  31. package/core/harnesses/shared/hooks/adapters/antigravity.js +14 -57
  32. package/core/harnesses/shared/hooks/bridge/bridge.sh +184 -0
  33. package/core/harnesses/shared/hooks/bridge/envelope.jq +60 -0
  34. package/core/harnesses/shared/hooks/bridge/respond.sh +62 -0
  35. package/core/harnesses/shared/hooks/policies/capture-failure.sh +4 -2
  36. package/core/harnesses/shared/hooks/policies/contract.sh +75 -0
  37. package/core/harnesses/shared/hooks/policies/deny-json.sh +39 -4
  38. package/core/harnesses/shared/hooks/policies/lib.sh +4 -2
  39. package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +46 -34
  40. package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +75 -25
  41. package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +67 -73
  42. package/core/harnesses/shared/hooks/policies/session-context.sh +37 -7
  43. package/core/harnesses/shared/hooks/policies/stop-check.sh +42 -15
  44. package/core/harnesses/shared/hooks/policies/subagent-audit.sh +35 -5
  45. package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +40 -7
  46. package/core/harnesses/shared/hooks/stream-hook-runner.js +69 -181
  47. package/core/registry/assets.json +19 -0
  48. package/core/registry/contracts.json +2 -2
  49. package/core/registry/harnesses.json +196 -10
  50. package/core/registry/lifecycle.json +2626 -7
  51. package/core/registry/storage.json +110 -83
  52. package/core/registry/verification.json +1 -1
  53. package/core/shared/scripts/doflow/bash/render-puml.sh +2 -2
  54. package/core/shared/scripts/doflow/bin/doflow-run +10 -6
  55. package/core/shared/skills/do-design/SKILL.md +6 -6
  56. package/core/shared/skills/do-execute-plan/SKILL.md +11 -10
  57. package/core/shared/skills/do-execute-plan/references/readiness_gate.md +2 -0
  58. package/core/shared/skills/do-execute-plan/references/scaffold.md +2 -2
  59. package/core/shared/skills/do-implement/SKILL.md +3 -2
  60. package/core/shared/skills/do-plan/SKILL.md +7 -8
  61. package/package.json +4 -2
  62. package/src/adapters/antigravity/{index.ts → index.js} +141 -105
  63. package/src/adapters/claude/{index.ts → index.js} +63 -49
  64. package/src/adapters/claude/{mcp.ts → mcp.js} +16 -16
  65. package/src/adapters/codex/{agents.ts → agents.js} +24 -24
  66. package/src/adapters/codex/{config.ts → config.js} +49 -49
  67. package/src/adapters/codex/{hooks.ts → hooks.js} +62 -27
  68. package/src/adapters/codex/{index.ts → index.js} +148 -105
  69. package/src/adapters/codex/{mcp.ts → mcp.js} +38 -38
  70. package/src/adapters/codex/{native-projection.ts → native-projection.js} +5 -5
  71. package/src/adapters/copilot/{index.ts → index.js} +42 -42
  72. package/src/adapters/{copy-tree.ts → copy-tree.js} +74 -74
  73. package/src/adapters/gemini/{hooks.ts → hooks.js} +78 -37
  74. package/src/adapters/gemini/{index.ts → index.js} +96 -57
  75. package/src/adapters/{hook-commands.ts → hook-commands.js} +8 -8
  76. package/src/adapters/{index.ts → index.js} +13 -13
  77. package/src/adapters/kiro/{index.ts → index.js} +49 -35
  78. package/src/adapters/{mcp-entries.ts → mcp-entries.js} +55 -55
  79. package/src/adapters/opencode/{index.ts → index.js} +52 -52
  80. package/src/adapters/pi/hooks.js +119 -0
  81. package/src/adapters/pi/{index.ts → index.js} +64 -39
  82. package/src/adapters/pi/{json-members.ts → json-members.js} +23 -23
  83. package/src/adapters/pi/{mcp.ts → mcp.js} +70 -70
  84. package/src/adapters/{resolve-source.ts → resolve-source.js} +1 -1
  85. package/src/cli/commands/{install.ts → install.js} +25 -25
  86. package/src/cli/commands/{list-backups.ts → list-backups.js} +4 -4
  87. package/src/cli/commands/{reconcile.ts → reconcile.js} +19 -19
  88. package/src/cli/commands/{remove.ts → remove.js} +10 -10
  89. package/src/cli/commands/{rollback.ts → rollback.js} +17 -17
  90. package/src/cli/commands/{self-update.ts → self-update.js} +3 -3
  91. package/src/cli/commands/{status.ts → status.js} +24 -24
  92. package/src/cli/commands/{tools.ts → tools.js} +16 -16
  93. package/src/cli/commands/{update.ts → update.js} +20 -20
  94. package/src/cli/{index.ts → index.js} +43 -31
  95. package/src/cli/{runtime-commands.ts → runtime-commands.js} +97 -58
  96. package/src/cli/{shared.ts → shared.js} +40 -40
  97. package/src/helper/{bash-helper.ts → bash-helper.js} +7 -7
  98. package/src/helper/{git.ts → git.js} +2 -2
  99. package/src/helper/{guardrails.ts → guardrails.js} +10 -10
  100. package/src/helper/{harness-paths.ts → harness-paths.js} +24 -24
  101. package/src/helper/{marker-merge.ts → marker-merge.js} +4 -4
  102. package/src/helper/{prompt.ts → prompt.js} +5 -5
  103. package/src/helper/{repo-root.ts → repo-root.js} +2 -2
  104. package/src/helper/{settings-merge.ts → settings-merge.js} +27 -27
  105. package/src/helper/{settings-scope.ts → settings-scope.js} +3 -3
  106. package/src/helper/{toml.ts → toml.js} +48 -48
  107. package/src/install/{backup-set.ts → backup-set.js} +16 -16
  108. package/src/install/{backup.ts → backup.js} +65 -65
  109. package/src/install/{context.ts → context.js} +15 -15
  110. package/src/install/{manifest.ts → manifest.js} +27 -27
  111. package/src/install/{mcp.ts → mcp.js} +30 -30
  112. package/src/install/{paths.ts → paths.js} +15 -15
  113. package/src/install/{targets.ts → targets.js} +10 -10
  114. package/src/install/{tool-lifecycle.ts → tool-lifecycle.js} +20 -20
  115. package/src/lifecycle/{bash-availability.ts → bash-availability.js} +1 -1
  116. package/src/lifecycle/coverage.js +57 -0
  117. package/src/lifecycle/hook-step.js +61 -0
  118. package/src/lifecycle/{index.ts → index.js} +160 -146
  119. package/src/lifecycle/{mcp-index.ts → mcp-index.js} +1 -1
  120. package/src/lifecycle/{policies.ts → policies.js} +11 -11
  121. package/src/lifecycle/{view.ts → view.js} +28 -28
  122. package/src/registry/{index.ts → index.js} +195 -66
  123. package/src/release/build-package.js +91 -0
  124. package/src/release/changelog-section.js +70 -0
  125. package/src/release/{sync-plugin-versions.ts → sync-plugin-versions.js} +4 -4
  126. package/src/runtime/advice/{cli.ts → cli.js} +31 -31
  127. package/src/runtime/advice/{client.ts → client.js} +44 -44
  128. package/src/runtime/advice/{config.ts → config.js} +16 -16
  129. package/src/runtime/advice/{doctor.ts → doctor.js} +32 -32
  130. package/src/runtime/advice/{endpoint-guard.ts → endpoint-guard.js} +20 -20
  131. package/src/runtime/advice/{kinds.ts → kinds.js} +16 -16
  132. package/src/runtime/advice/providers/{ollama-systemone.ts → ollama-systemone.js} +21 -21
  133. package/src/runtime/advice/{record.ts → record.js} +19 -19
  134. package/src/runtime/advice/{registry.ts → registry.js} +16 -16
  135. package/src/runtime/advice/{strength.ts → strength.js} +7 -7
  136. package/src/runtime/{brief-tools.ts → brief-tools.js} +50 -50
  137. package/src/runtime/{c4-project.ts → c4-project.js} +27 -27
  138. package/src/runtime/candidate.js +505 -0
  139. package/src/runtime/{capability-router.ts → capability-router.js} +39 -39
  140. package/src/runtime/{checkouts.ts → checkouts.js} +21 -21
  141. package/src/runtime/{claims.ts → claims.js} +44 -35
  142. package/src/runtime/{cli-result.ts → cli-result.js} +15 -8
  143. package/src/runtime/{cli.ts → cli.js} +59 -292
  144. package/src/runtime/{command-detect.ts → command-detect.js} +47 -47
  145. package/src/runtime/context/budget.js +203 -0
  146. package/src/runtime/context/compose.js +1367 -0
  147. package/src/runtime/context/delivery-store.js +102 -0
  148. package/src/runtime/context/mandatory.js +594 -0
  149. package/src/runtime/context/pointer.js +142 -0
  150. package/src/runtime/context-pack.js +1277 -0
  151. package/src/runtime/{decision-register.ts → decision-register.js} +67 -67
  152. package/src/runtime/evidence-fields.js +39 -0
  153. package/src/runtime/{evidence-ledger.ts → evidence-ledger.js} +32 -25
  154. package/src/runtime/failure/{capture.ts → capture.js} +29 -29
  155. package/src/runtime/failure/{classifier.ts → classifier.js} +4 -4
  156. package/src/runtime/failure/{cli.ts → cli.js} +30 -30
  157. package/src/runtime/failure/{home.ts → home.js} +6 -6
  158. package/src/runtime/failure/{store.ts → store.js} +32 -32
  159. package/src/runtime/{feature-resolve.ts → feature-resolve.js} +6 -6
  160. package/src/runtime/{freshness.ts → freshness.js} +59 -22
  161. package/src/runtime/{health.ts → health.js} +72 -72
  162. package/src/runtime/{history-compactor.ts → history-compactor.js} +25 -25
  163. package/src/runtime/{implementation-gate.ts → implementation-gate.js} +30 -30
  164. package/src/runtime/inventory/{coverage.ts → coverage.js} +10 -10
  165. package/src/runtime/inventory/{identity.ts → identity.js} +2 -2
  166. package/src/runtime/inventory/{index.ts → index.js} +36 -36
  167. package/src/runtime/inventory/{precedence.ts → precedence.js} +2 -2
  168. package/src/runtime/inventory/{read-scopes.ts → read-scopes.js} +9 -9
  169. package/src/runtime/inventory/{siblings.ts → siblings.js} +15 -15
  170. package/src/runtime/knowledge/{chunker.ts → chunker.js} +12 -12
  171. package/src/runtime/knowledge/{hybrid.ts → hybrid.js} +17 -17
  172. package/src/runtime/knowledge/{index-store.ts → index-store.js} +23 -23
  173. package/src/runtime/knowledge/{retrieval.ts → retrieval.js} +27 -27
  174. package/src/runtime/{leak-scan.ts → leak-scan.js} +12 -12
  175. package/src/runtime/lifecycle/{cli.ts → cli.js} +32 -32
  176. package/src/runtime/lifecycle/{event-store.ts → event-store.js} +48 -48
  177. package/src/runtime/lifecycle/{fold.ts → fold.js} +41 -41
  178. package/src/runtime/lifecycle/{followup.ts → followup.js} +54 -54
  179. package/src/runtime/lifecycle/{goal.ts → goal.js} +50 -50
  180. package/src/runtime/lifecycle/{intent-writer.ts → intent-writer.js} +5 -5
  181. package/src/runtime/lifecycle/{overview.ts → overview.js} +47 -47
  182. package/src/runtime/lifecycle/{release.ts → release.js} +29 -29
  183. package/src/runtime/lifecycle/{report-store.ts → report-store.js} +25 -25
  184. package/src/runtime/lifecycle/{retention.ts → retention.js} +37 -37
  185. package/src/runtime/lifecycle/{root.ts → root.js} +9 -9
  186. package/src/runtime/lifecycle/{status.ts → status.js} +64 -64
  187. package/src/runtime/lifecycle/{store-upkeep.ts → store-upkeep.js} +46 -46
  188. package/src/runtime/{locator-resolve.ts → locator-resolve.js} +7 -7
  189. package/src/runtime/{mask.ts → mask.js} +22 -22
  190. package/src/runtime/{mcp-consultations.ts → mcp-consultations.js} +54 -54
  191. package/src/runtime/{mcp-reach.ts → mcp-reach.js} +24 -24
  192. package/src/runtime/{model-router.ts → model-router.js} +20 -20
  193. package/src/runtime/{outcome.ts → outcome.js} +27 -27
  194. package/src/runtime/{reach.ts → reach.js} +21 -21
  195. package/src/runtime/{readiness-record.ts → readiness-record.js} +12 -12
  196. package/src/runtime/{readiness.ts → readiness.js} +113 -43
  197. package/src/runtime/{recovery.ts → recovery.js} +15 -15
  198. package/src/runtime/{research-request.ts → research-request.js} +34 -34
  199. package/src/runtime/{retrieval-plan.ts → retrieval-plan.js} +453 -104
  200. package/src/runtime/retrieval-record.js +223 -0
  201. package/src/runtime/scaffold/{artifacts.ts → artifacts.js} +22 -19
  202. package/src/runtime/scaffold/{fingerprint.ts → fingerprint.js} +3 -3
  203. package/src/runtime/scaffold/{generate.ts → generate.js} +109 -109
  204. package/src/runtime/scaffold/{languages.ts → languages.js} +34 -34
  205. package/src/runtime/source-version.js +289 -0
  206. package/src/runtime/storage/{cli.ts → cli.js} +9 -9
  207. package/src/runtime/storage/{engine.ts → engine.js} +37 -37
  208. package/src/runtime/storage/{registry.ts → registry.js} +19 -19
  209. package/src/runtime/storage/{roots.ts → roots.js} +8 -8
  210. package/src/runtime/storage/{rules.ts → rules.js} +26 -26
  211. package/src/runtime/storage/{upkeep.ts → upkeep.js} +35 -35
  212. package/src/runtime/storage/{work-status.ts → work-status.js} +39 -39
  213. package/src/runtime/{task-attempts.ts → task-attempts.js} +62 -62
  214. package/src/runtime/{task-classifier.ts → task-classifier.js} +34 -34
  215. package/src/runtime/{task-scope.ts → task-scope.js} +40 -15
  216. package/src/runtime/{task-state.ts → task-state.js} +86 -21
  217. package/src/runtime/trace/{indicators.ts → indicators.js} +22 -22
  218. package/src/runtime/trace/{ledger.ts → ledger.js} +42 -42
  219. package/src/runtime/trace/{render.ts → render.js} +7 -7
  220. package/src/runtime/trace/{views.ts → views.js} +40 -40
  221. package/src/runtime/verification/{contract-runner.ts → contract-runner.js} +23 -23
  222. package/src/runtime/verification/{engine.ts → engine.js} +143 -143
  223. package/src/runtime/verification/{registry.ts → registry.js} +6 -6
  224. package/src/runtime/verification/{scope-bound.ts → scope-bound.js} +50 -50
  225. package/src/runtime/verification/{test-selection.ts → test-selection.js} +28 -28
  226. package/src/runtime/{workflow-engine.ts → workflow-engine.js} +50 -50
  227. package/src/runtime/{workflow-orchestrator.ts → workflow-orchestrator.js} +91 -91
  228. package/src/runtime/{worktree.ts → worktree.js} +31 -31
  229. package/src/state/{index.ts → index.js} +26 -26
  230. package/src/state/{lockfile.ts → lockfile.js} +20 -20
  231. package/src/state/{run-lock.ts → run-lock.js} +44 -44
  232. package/core/.antigravity-plugin/hooks.json +0 -39
  233. package/core/harnesses/shared/hooks/adapters/arg-fields.js +0 -19
  234. package/core/harnesses/shared/hooks/adapters/gemini.js +0 -114
  235. package/core/harnesses/shared/hooks/hooks.json +0 -47
  236. package/src/runtime/context-pack.ts +0 -492
@@ -52,69 +52,69 @@ const SANDBOX_SKILLS_DIR = path.join('.claude', 'skills');
52
52
  /** Source of truth for skill content, relative to the repo root. */
53
53
  const SOURCE_SKILLS_DIR = path.join('core', 'shared', 'skills');
54
54
 
55
- function sha256File(file: string): string {
55
+ function sha256File(file ) {
56
56
  return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
57
57
  }
58
58
 
59
- function git(args: string[], cwd: string, { allowFailure = false }: { allowFailure?: boolean } = {}): { ok: boolean; stdout: string; stderr?: string } {
59
+ function git(args , cwd , { allowFailure = false } = {}) {
60
60
  try {
61
61
  return {
62
62
  ok: true,
63
63
  stdout: execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }),
64
64
  };
65
- } catch (err: any) {
65
+ } catch (err ) {
66
66
  if (!allowFailure) throw err;
67
67
  return { ok: false, stdout: '', stderr: err.stderr ? String(err.stderr) : String(err.message) };
68
68
  }
69
69
  }
70
70
 
71
71
  class WorktreeManager {
72
- repoRoot: string;
73
- worktreesDir: string;
72
+ repoRoot ;
73
+ worktreesDir ;
74
74
 
75
75
  /**
76
76
  * @param {string} repoRoot repository whose worktrees are managed
77
77
  * @param {string} [worktreesDir] override the sandbox root; defaults to `<repo>/.doflow/worktrees`,
78
78
  * which is gitignored so sandboxes never appear as untracked noise in the parent tree.
79
79
  */
80
- constructor(repoRoot: string, worktreesDir?: string) {
80
+ constructor(repoRoot , worktreesDir ) {
81
81
  this.repoRoot = repoRoot;
82
82
  this.worktreesDir = worktreesDir || path.join(repoRoot, '.doflow', 'worktrees');
83
83
  }
84
84
 
85
- static assertSafeId(taskId: any): void {
85
+ static assertSafeId(taskId ) {
86
86
  if (!taskId || !SAFE_ID.test(taskId)) {
87
87
  throw new Error(`unsafe worktree id ${JSON.stringify(taskId)} — allowed: letters, digits, dot, dash, underscore`);
88
88
  }
89
89
  }
90
90
 
91
- pathFor(taskId: string): string {
91
+ pathFor(taskId ) {
92
92
  WorktreeManager.assertSafeId(taskId);
93
93
  return path.join(this.worktreesDir, taskId);
94
94
  }
95
95
 
96
- branchFor(taskId: string): string {
96
+ branchFor(taskId ) {
97
97
  WorktreeManager.assertSafeId(taskId);
98
98
  return `task/${taskId}`;
99
99
  }
100
100
 
101
101
  /** Whether git itself considers this path a registered worktree — the directory existing on disk
102
102
  * is not the same question, which is the bug fixed here. */
103
- isRegistered(wtPath: string): boolean {
103
+ isRegistered(wtPath ) {
104
104
  const res = git(['worktree', 'list', '--porcelain'], this.repoRoot, { allowFailure: true });
105
105
  if (!res.ok) return false;
106
106
  const resolved = path.resolve(wtPath);
107
107
  return res.stdout
108
108
  .split('\n')
109
- .filter((l: string) => l.startsWith('worktree '))
110
- .some((l: string) => path.resolve(l.slice('worktree '.length).trim()) === resolved);
109
+ .filter((l ) => l.startsWith('worktree '))
110
+ .some((l ) => path.resolve(l.slice('worktree '.length).trim()) === resolved);
111
111
  }
112
112
 
113
113
  /**
114
114
  * Create an isolated worktree, reusing it only when git agrees it is registered. Returns the
115
115
  * absolute path.
116
116
  */
117
- create(taskId: string, baseRef: string = 'HEAD'): string {
117
+ create(taskId , baseRef = 'HEAD') {
118
118
  const wtPath = this.pathFor(taskId);
119
119
  const branch = this.branchFor(taskId);
120
120
 
@@ -142,7 +142,7 @@ class WorktreeManager {
142
142
  }
143
143
 
144
144
  /** The resolved base SHA recorded at creation, or null when unavailable. */
145
- baseOf(taskId: string): string | null {
145
+ baseOf(taskId ) {
146
146
  const file = path.join(this.pathFor(taskId), BASE_FILE);
147
147
  return fs.existsSync(file) ? fs.readFileSync(file, 'utf8').trim() : null;
148
148
  }
@@ -172,7 +172,7 @@ class WorktreeManager {
172
172
  *
173
173
  * @returns {object} the manifest also written to `<worktree>/.doflow-skill-source.json`
174
174
  */
175
- projectSkills(taskId: string, { harness = 'claude', cli = path.join('bin', 'doflow.js'), skillsRoot = SOURCE_SKILLS_DIR }: { harness?: string; cli?: string; skillsRoot?: string } = {}): any {
175
+ projectSkills(taskId , { harness = 'claude', cli = path.join('bin', 'doflow.js'), skillsRoot = SOURCE_SKILLS_DIR } = {}) {
176
176
  const wtPath = this.pathFor(taskId);
177
177
  if (!fs.existsSync(wtPath)) {
178
178
  throw new Error(`no worktree at ${wtPath} — create it before projecting skills`);
@@ -182,25 +182,25 @@ class WorktreeManager {
182
182
  throw new Error(`no skill source at ${sourceRoot}`);
183
183
  }
184
184
 
185
- let installOutput: string;
185
+ let installOutput ;
186
186
  try {
187
187
  installOutput = execFileSync(
188
188
  process.execPath,
189
189
  [path.join(this.repoRoot, cli), 'install', wtPath, '--force', '-t', harness],
190
190
  { cwd: this.repoRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] },
191
191
  );
192
- } catch (err: any) {
192
+ } catch (err ) {
193
193
  throw new Error(`projecting skills into ${wtPath} failed: ${err.stderr || err.message}`);
194
194
  }
195
195
 
196
196
  const names = fs
197
197
  .readdirSync(sourceRoot, { withFileTypes: true })
198
- .filter((e: any) => e.isDirectory() && fs.existsSync(path.join(sourceRoot, e.name, 'SKILL.md')))
199
- .map((e: any) => e.name)
198
+ .filter((e ) => e.isDirectory() && fs.existsSync(path.join(sourceRoot, e.name, 'SKILL.md')))
199
+ .map((e ) => e.name)
200
200
  .sort();
201
201
 
202
- const skills: Record<string, any> = {};
203
- const mismatches: string[] = [];
202
+ const skills = {};
203
+ const mismatches = [];
204
204
  for (const name of names) {
205
205
  const rel = path.join(SANDBOX_SKILLS_DIR, name, 'SKILL.md');
206
206
  const dst = path.join(wtPath, rel);
@@ -237,7 +237,7 @@ class WorktreeManager {
237
237
  }
238
238
 
239
239
  /** The skill-source manifest a sandbox recorded, or null when it was never projected. */
240
- skillSourceOf(taskId: string): any {
240
+ skillSourceOf(taskId ) {
241
241
  const file = path.join(this.pathFor(taskId), SKILL_SOURCE_FILE);
242
242
  if (!fs.existsSync(file)) return null;
243
243
  try {
@@ -249,7 +249,7 @@ class WorktreeManager {
249
249
 
250
250
  /** Repo HEAD, or null outside a repository. Recorded for readability; the per-skill hashes are
251
251
  * the actual provenance, and they stay correct on a dirty tree where a commit id does not. */
252
- headCommit(): string | null {
252
+ headCommit() {
253
253
  const res = git(['rev-parse', 'HEAD'], this.repoRoot, { allowFailure: true });
254
254
  return res.ok ? res.stdout.trim() : null;
255
255
  }
@@ -259,13 +259,13 @@ class WorktreeManager {
259
259
  * skills. Split from `create()` because non-evaluation callers want a bare worktree and should
260
260
  * not pay for a projection they will not read.
261
261
  */
262
- createSandbox(taskId: string, { baseRef = 'HEAD', ...opts }: { baseRef?: string; [key: string]: any } = {}): { path: string; manifest: any } {
262
+ createSandbox(taskId , { baseRef = 'HEAD', ...opts } = {}) {
263
263
  const wtPath = this.create(taskId, baseRef);
264
264
  return { path: wtPath, manifest: this.projectSkills(taskId, opts) };
265
265
  }
266
266
 
267
267
  /** Remove the worktree and its branch. Idempotent: removing something absent is success. */
268
- remove(taskId: string): void {
268
+ remove(taskId ) {
269
269
  const wtPath = this.pathFor(taskId);
270
270
  const branch = this.branchFor(taskId);
271
271
 
@@ -282,7 +282,7 @@ class WorktreeManager {
282
282
  * collapsing a failure into an empty diff, so a caller can tell "nothing changed" from
283
283
  * "could not tell", which the Python version could not.
284
284
  */
285
- collectDiff(taskId: string, baseRef?: string): { diff: string | null; untracked: string[]; error: string | null | undefined } {
285
+ collectDiff(taskId , baseRef ) {
286
286
  const wtPath = this.pathFor(taskId);
287
287
  if (!fs.existsSync(wtPath)) {
288
288
  return { diff: null, untracked: [], error: `no worktree at ${wtPath}` };
@@ -304,23 +304,23 @@ class WorktreeManager {
304
304
  // Harness bookkeeping is not task work: the base pointer and the skill-source manifest are
305
305
  // written by this module, not by the skill under test.
306
306
  untracked: untracked.ok
307
- ? untracked.stdout.split('\n').filter((f: string) => f && f !== BASE_FILE && f !== SKILL_SOURCE_FILE)
307
+ ? untracked.stdout.split('\n').filter((f ) => f && f !== BASE_FILE && f !== SKILL_SOURCE_FILE)
308
308
  : [],
309
309
  error: null,
310
310
  };
311
311
  }
312
312
 
313
313
  /** Every sandbox this manager knows about — used to clean up after an interrupted run. */
314
- list(): string[] {
314
+ list() {
315
315
  if (!fs.existsSync(this.worktreesDir)) return [];
316
316
  return fs
317
317
  .readdirSync(this.worktreesDir, { withFileTypes: true })
318
- .filter((e: any) => e.isDirectory())
319
- .map((e: any) => e.name);
318
+ .filter((e ) => e.isDirectory())
319
+ .map((e ) => e.name);
320
320
  }
321
321
 
322
322
  /** Run `fn(worktreePath)` in a fresh sandbox and always tear it down, even when `fn` throws. */
323
- withWorktree(taskId: string, fn: (wtPath: string) => any, baseRef: string = 'HEAD'): any {
323
+ withWorktree(taskId , fn , baseRef = 'HEAD') {
324
324
  const wtPath = this.create(taskId, baseRef);
325
325
  try {
326
326
  return fn(wtPath);
@@ -9,7 +9,7 @@ const fs = require('node:fs');
9
9
  const os = require('node:os');
10
10
  const path = require('node:path');
11
11
  const crypto = require('node:crypto');
12
- const { manifestPath, readManifest } = require('../install/manifest.ts');
12
+ const { manifestPath, readManifest } = require('../install/manifest.js');
13
13
 
14
14
  const STATE_VERSION = 1;
15
15
  // Ledger document version. v1 ledgers (no tombstones) remain readable forever; writers produce
@@ -18,32 +18,32 @@ const LEDGER_VERSION = 2;
18
18
  const LEDGER_FILE = 'ledger.json';
19
19
  const RECOVERY_DIR = 'recovery';
20
20
 
21
- function assertScope(scope: any): void {
21
+ function assertScope(scope ) {
22
22
  if (scope !== 'project' && scope !== 'global') throw new Error(`Invalid state scope: '${scope}'`);
23
23
  }
24
24
 
25
25
  /** Resolve DoFlow-owned state without deriving it from a harness directory. */
26
- function stateRoot({ scope, projectRoot = '.', homeDir = os.homedir() }: { scope: any, projectRoot?: string, homeDir?: string }): string {
26
+ function stateRoot({ scope, projectRoot = '.', homeDir = os.homedir() } ) {
27
27
  assertScope(scope);
28
28
  return scope === 'project'
29
29
  ? path.join(path.resolve(projectRoot), '.doflow', 'state')
30
30
  : path.join(path.resolve(homeDir), '.doflow', 'state');
31
31
  }
32
32
 
33
- function ledgerPath(root: string): string { return path.join(root, LEDGER_FILE); }
34
- function recoveryRoot(root: string): string { return path.join(root, RECOVERY_DIR); }
33
+ function ledgerPath(root ) { return path.join(root, LEDGER_FILE); }
34
+ function recoveryRoot(root ) { return path.join(root, RECOVERY_DIR); }
35
35
 
36
- function stable(value: any): any {
36
+ function stable(value ) {
37
37
  if (Array.isArray(value)) return value.map(stable);
38
38
  if (!value || typeof value !== 'object') return value;
39
39
  return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stable(value[key])]));
40
40
  }
41
41
 
42
- function fingerprint(value: any): string {
42
+ function fingerprint(value ) {
43
43
  return crypto.createHash('sha256').update(JSON.stringify(stable(value))).digest('hex');
44
44
  }
45
45
 
46
- function atomicJsonWrite(file: string, value: any, { fsImpl = fs }: { fsImpl?: any } = {}): string {
46
+ function atomicJsonWrite(file , value , { fsImpl = fs } = {}) {
47
47
  fsImpl.mkdirSync(path.dirname(file), { recursive: true });
48
48
  const tmp = path.join(path.dirname(file), `.${path.basename(file)}.${process.pid}.${Date.now()}.tmp`);
49
49
  try {
@@ -55,7 +55,7 @@ function atomicJsonWrite(file: string, value: any, { fsImpl = fs }: { fsImpl?: a
55
55
  return file;
56
56
  }
57
57
 
58
- function defaultLedger({ scope, scopeRoot }: { scope: any, scopeRoot: string }): any {
58
+ function defaultLedger({ scope, scopeRoot } ) {
59
59
  assertScope(scope);
60
60
  return {
61
61
  version: LEDGER_VERSION,
@@ -72,13 +72,13 @@ function defaultLedger({ scope, scopeRoot }: { scope: any, scopeRoot: string }):
72
72
  }
73
73
 
74
74
  /** Upgrade a v1 ledger in memory to the current shape without touching disk. Idempotent. */
75
- function upgradeLedger(ledger: any): any {
75
+ function upgradeLedger(ledger ) {
76
76
  if (!ledger || typeof ledger !== 'object') throw new Error('Invalid neutral ledger: expected object');
77
77
  if (ledger.version === LEDGER_VERSION) return ledger;
78
78
  return { ...ledger, version: LEDGER_VERSION, tombstones: Array.isArray(ledger.tombstones) ? ledger.tombstones : [] };
79
79
  }
80
80
 
81
- function validateLedger(value: any): any {
81
+ function validateLedger(value ) {
82
82
  if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Invalid neutral ledger: expected object');
83
83
  if (![STATE_VERSION, LEDGER_VERSION].includes(value.version)) throw new Error(`Unsupported neutral ledger version: '${value.version}'`);
84
84
  if (value.version >= LEDGER_VERSION && value.tombstones !== undefined && !Array.isArray(value.tombstones)) {
@@ -91,30 +91,30 @@ function validateLedger(value: any): any {
91
91
  return value;
92
92
  }
93
93
 
94
- function readLedger(root: string, { fsImpl = fs }: { fsImpl?: any } = {}): any {
94
+ function readLedger(root , { fsImpl = fs } = {}) {
95
95
  const file = ledgerPath(root);
96
96
  if (!fsImpl.existsSync(file)) return null;
97
97
  try { return validateLedger(JSON.parse(fsImpl.readFileSync(file, 'utf8'))); }
98
- catch (error: any) { throw new Error(`Cannot read neutral ledger '${file}': ${error.message}`); }
98
+ catch (error ) { throw new Error(`Cannot read neutral ledger '${file}': ${error.message}`); }
99
99
  }
100
100
 
101
- function writeLedger(root: string, ledger: any, options: { fsImpl?: any } = {}): string {
101
+ function writeLedger(root , ledger , options = {}) {
102
102
  return atomicJsonWrite(ledgerPath(root), validateLedger(ledger), options);
103
103
  }
104
104
 
105
- function recoveryId(now: Date = new Date()): string {
105
+ function recoveryId(now = new Date()) {
106
106
  return `recovery_${now.toISOString().replace(/[:.]/g, '-').replace(/Z$/, 'Z')}_${crypto.randomBytes(4).toString('hex')}`;
107
107
  }
108
108
 
109
- function assertSafeRecoveryId(id: any): void {
109
+ function assertSafeRecoveryId(id ) {
110
110
  if (typeof id !== 'string' || !/^recovery_[A-Za-z0-9_-]+$/.test(id)) throw new Error(`Invalid recovery id: '${id}'`);
111
111
  }
112
112
 
113
113
  /** Persist a journal record before native mutation; append-only records make interrupted runs observable. */
114
- function writeRecoveryRecord(root: string, record: any, { now = new Date(), fsImpl = fs }: { now?: Date, fsImpl?: any } = {}): { id: string, file: string, record: any } {
114
+ function writeRecoveryRecord(root , record , { now = new Date(), fsImpl = fs } = {}) {
115
115
  const id = record?.id ?? recoveryId(now);
116
116
  assertSafeRecoveryId(id);
117
- const value: any = {
117
+ const value = {
118
118
  id,
119
119
  version: STATE_VERSION,
120
120
  status: record?.status ?? 'pending',
@@ -126,19 +126,19 @@ function writeRecoveryRecord(root: string, record: any, { now = new Date(), fsIm
126
126
  return { id, file: atomicJsonWrite(path.join(recoveryRoot(root), `${id}.json`), value, { fsImpl }), record: value };
127
127
  }
128
128
 
129
- function readRecoveryRecord(root: string, id: any, { fsImpl = fs }: { fsImpl?: any } = {}): any {
129
+ function readRecoveryRecord(root , id , { fsImpl = fs } = {}) {
130
130
  assertSafeRecoveryId(id);
131
131
  const file = path.join(recoveryRoot(root), `${id}.json`);
132
132
  if (!fsImpl.existsSync(file)) return null;
133
133
  try { return JSON.parse(fsImpl.readFileSync(file, 'utf8')); }
134
- catch (error: any) { throw new Error(`Cannot read recovery record '${file}': ${error.message}`); }
134
+ catch (error ) { throw new Error(`Cannot read recovery record '${file}': ${error.message}`); }
135
135
  }
136
136
 
137
- function ownershipKey(record: any): string {
137
+ function ownershipKey(record ) {
138
138
  return [record.harness, record.scope, record.assetId, record.target, record.ownershipIdentity].join('\u0000');
139
139
  }
140
140
 
141
- function importLegacyResource(resource: any, scope: any): any {
141
+ function importLegacyResource(resource , scope ) {
142
142
  if (!resource || typeof resource !== 'object') return null;
143
143
  if (typeof resource.target !== 'string' || typeof resource.kind !== 'string' || typeof resource.identity !== 'string') return null;
144
144
  return {
@@ -159,24 +159,24 @@ function importLegacyResource(resource: any, scope: any): any {
159
159
  * Read-compatible, conservative legacy import. Only explicit `managed_resources` records are
160
160
  * converted to ownership claims; a legacy tool entry alone never authorizes adoption of files.
161
161
  */
162
- function migrateLegacyManifest({ root, scope, scopeRoot, claudeDir, now = new Date(), fsImpl = fs }: { root: string, scope: any, scopeRoot: string, claudeDir: string, now?: Date, fsImpl?: any }): any {
162
+ function migrateLegacyManifest({ root, scope, scopeRoot, claudeDir, now = new Date(), fsImpl = fs } ) {
163
163
  assertScope(scope);
164
164
  const legacyFile = manifestPath(claudeDir);
165
165
  const current = readLedger(root, { fsImpl }) ?? defaultLedger({ scope, scopeRoot });
166
166
  if (current.scope !== scope || current.scopeRoot !== path.resolve(scopeRoot)) throw new Error('Neutral ledger scope does not match requested migration scope');
167
- if (current.legacyImports.some((item: any) => item.path === legacyFile)) return { migrated: false, reason: 'already-imported', ledger: current, legacyFile };
167
+ if (current.legacyImports.some((item ) => item.path === legacyFile)) return { migrated: false, reason: 'already-imported', ledger: current, legacyFile };
168
168
  if (!fsImpl.existsSync(legacyFile)) return { migrated: false, reason: 'legacy-missing', ledger: current, legacyFile };
169
169
 
170
170
  const legacy = readManifest(claudeDir);
171
171
  if (!legacy || legacy.operation === 'error') return { migrated: false, reason: 'legacy-invalid', ledger: current, legacyFile };
172
- const resources = legacy.managedResources.map((resource: any) => importLegacyResource(resource, scope)).filter(Boolean);
172
+ const resources = legacy.managedResources.map((resource ) => importLegacyResource(resource, scope)).filter(Boolean);
173
173
  const existing = new Set(current.resources.map(ownershipKey));
174
174
  for (const resource of resources) if (!existing.has(ownershipKey(resource))) current.resources.push(resource);
175
175
 
176
176
  // The legacy manifest's MCP selection was owned by Claude's config flow. Do not infer an
177
177
  // equivalent selection for Codex or Gemini merely because they happened to share the old file.
178
178
  if (Array.isArray(legacy.mcpServers) && !Object.hasOwn(current.mcpSelections, 'claude')) current.mcpSelections.claude = [...legacy.mcpServers];
179
- for (const [harness, status] of Object.entries(legacy.tools) as [string, any][]) {
179
+ for (const [harness, status] of Object.entries(legacy.tools) ) {
180
180
  if (status?.installed && !current.targets[harness]) current.targets[harness] = { installed: true, lastUpdated: status.last_updated ?? null, imported: true };
181
181
  }
182
182
  current.legacyImports.push({ path: legacyFile, importedAt: now.toISOString(), sourceFingerprint: fingerprint({ operation: legacy.operation, lastRun: legacy.lastRun, backupId: legacy.backupId, resources: legacy.managedResources }), lastBackupId: legacy.backupId });
@@ -10,25 +10,25 @@
10
10
  const fs = require('node:fs');
11
11
  const os = require('node:os');
12
12
  const path = require('node:path');
13
- const { atomicJsonWrite } = require('./index.ts');
13
+ const { atomicJsonWrite } = require('./index.js');
14
14
 
15
15
  const LOCK_VERSION = 1;
16
16
  const LOCK_FILE = 'doflow.lock';
17
17
 
18
- function lockRoot({ scope, projectRoot = '.', homeDir = os.homedir() }: { scope: any, projectRoot?: string, homeDir?: string }): string {
18
+ function lockRoot({ scope, projectRoot = '.', homeDir = os.homedir() } ) {
19
19
  if (scope !== 'project' && scope !== 'global') throw new Error(`Invalid lock scope: '${scope}'`);
20
20
  return scope === 'project' ? path.resolve(projectRoot) : path.resolve(homeDir);
21
21
  }
22
22
 
23
- function lockPath(options: any): string { return path.join(lockRoot(options), '.doflow', LOCK_FILE); }
23
+ function lockPath(options ) { return path.join(lockRoot(options), '.doflow', LOCK_FILE); }
24
24
 
25
- function stable(value: any): any {
26
- if (Array.isArray(value)) return [...value].map(stable).sort((a: any, b: any) => JSON.stringify(a) < JSON.stringify(b) ? -1 : 1);
25
+ function stable(value ) {
26
+ if (Array.isArray(value)) return [...value].map(stable).sort((a , b ) => JSON.stringify(a) < JSON.stringify(b) ? -1 : 1);
27
27
  if (!value || typeof value !== 'object') return value;
28
28
  return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stable(value[key])]));
29
29
  }
30
30
 
31
- function defaultLock({ scope, scopeRoot }: { scope: any, scopeRoot: string }): any {
31
+ function defaultLock({ scope, scopeRoot } ) {
32
32
  if (scope !== 'project' && scope !== 'global') throw new Error(`Invalid lock scope: '${scope}'`);
33
33
  return {
34
34
  version: LOCK_VERSION,
@@ -42,7 +42,7 @@ function defaultLock({ scope, scopeRoot }: { scope: any, scopeRoot: string }): a
42
42
  };
43
43
  }
44
44
 
45
- function validateLock(value: any): any {
45
+ function validateLock(value ) {
46
46
  if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Invalid doflow.lock: expected object');
47
47
  if (value.version !== LOCK_VERSION) throw new Error(`Unsupported doflow.lock version: '${value.version}'`);
48
48
  if (value.scope !== 'project' && value.scope !== 'global') throw new Error(`Invalid doflow.lock scope: '${value.scope}'`);
@@ -54,21 +54,21 @@ function validateLock(value: any): any {
54
54
  throw new Error('Invalid doflow.lock: mcpSelections must be an object');
55
55
  }
56
56
  for (const [harness, ids] of Object.entries(value.mcpSelections)) {
57
- if (!Array.isArray(ids) || !ids.every((id: any) => typeof id === 'string')) {
57
+ if (!Array.isArray(ids) || !ids.every((id ) => typeof id === 'string')) {
58
58
  throw new Error(`Invalid doflow.lock: mcpSelections.${harness} must be an array of server ids`);
59
59
  }
60
60
  }
61
61
  return value;
62
62
  }
63
63
 
64
- function readLock(options: any, { fsImpl = fs }: { fsImpl?: any } = {}): any {
64
+ function readLock(options , { fsImpl = fs } = {}) {
65
65
  const file = lockPath(options);
66
66
  if (!fsImpl.existsSync(file)) return null;
67
67
  try { return validateLock(JSON.parse(fsImpl.readFileSync(file, 'utf8'))); }
68
- catch (error: any) { throw new Error(`Cannot read doflow.lock '${file}': ${error.message}`); }
68
+ catch (error ) { throw new Error(`Cannot read doflow.lock '${file}': ${error.message}`); }
69
69
  }
70
70
 
71
- function writeLock(options: any, lock: any, { fsImpl = fs }: { fsImpl?: any } = {}): any {
71
+ function writeLock(options , lock , { fsImpl = fs } = {}) {
72
72
  // Validate the caller's document first so a foreign version fails loudly here rather than
73
73
  // being silently normalized into something no future reader can distinguish.
74
74
  validateLock(lock);
@@ -77,23 +77,23 @@ function writeLock(options: any, lock: any, { fsImpl = fs }: { fsImpl?: any } =
77
77
 
78
78
  /** The lock's MCP rows for the harnesses it targets. A row naming a harness outside `targets` is
79
79
  * ignored by every reader; the next write drops it. */
80
- function pinnedSelections(lock: any): Record<string, any> {
81
- const targets = new Set((lock?.targets ?? []).map((entry: any) => entry.harness));
80
+ function pinnedSelections(lock ) {
81
+ const targets = new Set((lock?.targets ?? []).map((entry ) => entry.harness));
82
82
  return Object.fromEntries(Object.entries(lock?.mcpSelections ?? {}).filter(([harness]) => targets.has(harness)));
83
83
  }
84
84
 
85
85
  /** Compare two lock documents. Arrays are keyed by identity (assets by id, targets by harness,
86
86
  * fallback to full JSON), mcpSelections by harness, where an absent row (`null`) and a recorded
87
87
  * empty selection (`[]`) differ. Returns {added, removed, changed} per section plus a clean flag. */
88
- function diffLocks(previousLock: any, nextLock: any): any {
88
+ function diffLocks(previousLock , nextLock ) {
89
89
  // Compare in the order writeLock stores, so a document built in memory never differs from the
90
90
  // file it was written to.
91
91
  const previous = previousLock && stable(previousLock);
92
92
  const next = nextLock && stable(nextLock);
93
- const key = (entry: any) => entry?.id ?? entry?.harness ?? JSON.stringify(entry);
94
- const sectionDiff = (before: any, after: any) => {
95
- const beforeKeys = new Map<string, any>((before || []).map((entry: any): [string, any] => [key(entry), entry]));
96
- const afterKeys = new Map<string, any>((after || []).map((entry: any): [string, any] => [key(entry), entry]));
93
+ const key = (entry ) => entry?.id ?? entry?.harness ?? JSON.stringify(entry);
94
+ const sectionDiff = (before , after ) => {
95
+ const beforeKeys = new Map ((before || []).map((entry ) => [key(entry), entry]));
96
+ const afterKeys = new Map ((after || []).map((entry ) => [key(entry), entry]));
97
97
  const added = [...afterKeys].filter(([k]) => !beforeKeys.has(k)).map(([, v]) => v);
98
98
  const removed = [...beforeKeys].filter(([k]) => !afterKeys.has(k)).map(([, v]) => v);
99
99
  const changed = [...afterKeys]
@@ -107,7 +107,7 @@ function diffLocks(previousLock: any, nextLock: any): any {
107
107
  const mcpChanged = [...harnesses]
108
108
  .filter((harness) => JSON.stringify(selectionsBefore[harness] ?? null) !== JSON.stringify(selectionsAfter[harness] ?? null))
109
109
  .map((harness) => ({ harness, before: selectionsBefore[harness] ?? null, after: selectionsAfter[harness] ?? null }));
110
- const result: any = {
110
+ const result = {
111
111
  targets: sectionDiff(previous?.targets, next?.targets),
112
112
  assets: sectionDiff(previous?.assets, next?.assets),
113
113
  mcpSelections: { changed: mcpChanged },
@@ -124,7 +124,7 @@ function diffLocks(previousLock: any, nextLock: any): any {
124
124
  }
125
125
 
126
126
  /** Delete the lock when nothing remains pinned. Returns true when a file actually went away. */
127
- function removeLock(options: any, { fsImpl = fs }: { fsImpl?: any } = {}): boolean {
127
+ function removeLock(options , { fsImpl = fs } = {}) {
128
128
  const file = lockPath(options);
129
129
  if (!fsImpl.existsSync(file)) return false;
130
130
  fsImpl.rmSync(file);