@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
@@ -11,25 +11,25 @@
11
11
  //
12
12
  // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): authored as `.ts`, run directly by Node
13
13
  // native type-stripping — no build step, no runtime dependency. Body stays CommonJS so a `.js`
14
- // caller's `require('../install/backup-set.ts')` keeps working while the tree is still `.js`. The
14
+ // caller's `require('../install/backup-set.js')` keeps working while the tree is still `.js`. The
15
15
  // `./backup` require carries `.ts` because backup migrated in the same slice.
16
16
  const fs = require('node:fs');
17
17
  const path = require('node:path');
18
- const { BackupError, scopeRelative } = require('./backup.ts');
18
+ const { BackupError, scopeRelative } = require('./backup.js');
19
19
 
20
20
  const REASON_ORDER = ['change', 'companion', 'relocated-from', 'tombstone', 'snapshot', 'record'];
21
21
 
22
- function byCodePoint(a: string, b: string): number {
22
+ function byCodePoint(a , b ) {
23
23
  if (a === b) return 0;
24
24
  return a < b ? -1 : 1;
25
25
  }
26
26
 
27
27
  /** What a set entry is on disk now, following links. */
28
- function statEntry(file: string, fsImpl: any): { kind: string; size: number | null; mode: number | null } {
28
+ function statEntry(file , fsImpl ) {
29
29
  let stat;
30
30
  try {
31
31
  stat = fsImpl.statSync(file);
32
- } catch (err: any) {
32
+ } catch (err ) {
33
33
  if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return { kind: 'absent', size: null, mode: null };
34
34
  throw new BackupError(`could not read ${file}: ${err.code || err.message}`);
35
35
  }
@@ -37,19 +37,19 @@ function statEntry(file: string, fsImpl: any): { kind: string; size: number | nu
37
37
  return { kind: 'other', size: null, mode: null };
38
38
  }
39
39
 
40
- function isAtOrUnder(file: string, root: string): boolean {
40
+ function isAtOrUnder(file , root ) {
41
41
  const resolved = path.resolve(root);
42
42
  return file === resolved || file.startsWith(resolved.endsWith(path.sep) ? resolved : resolved + path.sep);
43
43
  }
44
44
 
45
45
  /** Candidates (absolute path -> harnesses and reasons) into a BackupSet, after the exclusions.
46
46
  * A path in `records` is kept even under an excluded root. */
47
- function toBackupSet(candidates: Map<string, any>, { scope, scopeRoot, exclude, excluded, records = [], fsImpl }: any): any {
47
+ function toBackupSet(candidates , { scope, scopeRoot, exclude, excluded, records = [], fsImpl } ) {
48
48
  const root = path.resolve(scopeRoot);
49
- const kept = new Set(records.map((file: string) => path.resolve(file)));
49
+ const kept = new Set(records.map((file ) => path.resolve(file)));
50
50
  const entries = [];
51
51
  for (const file of [...candidates.keys()].sort(byCodePoint)) {
52
- if (!kept.has(file) && exclude.some((x: string) => isAtOrUnder(file, x))) {
52
+ if (!kept.has(file) && exclude.some((x ) => isAtOrUnder(file, x))) {
53
53
  excluded.push({ path: file, reason: 'doflow-state' });
54
54
  continue;
55
55
  }
@@ -67,15 +67,15 @@ function toBackupSet(candidates: Map<string, any>, { scope, scopeRoot, exclude,
67
67
  scope,
68
68
  scopeRoot: root,
69
69
  entries,
70
- excluded: excluded.sort((a: any, b: any) => byCodePoint(a.path, b.path)),
70
+ excluded: excluded.sort((a , b ) => byCodePoint(a.path, b.path)),
71
71
  count: entries.length,
72
72
  existing: files.length,
73
73
  absent: entries.filter((e) => e.kind === 'absent').length,
74
- bytes: files.reduce((sum, e) => sum + (e.size as number), 0),
74
+ bytes: files.reduce((sum, e) => sum + (e.size ), 0),
75
75
  };
76
76
  }
77
77
 
78
- function addCandidate(candidates: Map<string, any>, file: string, harness: string | null, reason: string): void {
78
+ function addCandidate(candidates , file , harness , reason ) {
79
79
  const key = path.resolve(file);
80
80
  if (!candidates.has(key)) candidates.set(key, { harnesses: new Set(), reasons: new Set() });
81
81
  const item = candidates.get(key);
@@ -92,13 +92,13 @@ function addCandidate(candidates: Map<string, any>, file: string, harness: strin
92
92
  * it writes back (backup.ts mergeRestoredLedger). They join only a set that holds some file already,
93
93
  * so a run that backs up nothing still backs up nothing.
94
94
  */
95
- function backupSetFromPlan({ plan, scope, scopeRoot, exclude = [], records = [], fsImpl = fs }: any): any {
95
+ function backupSetFromPlan({ plan, scope, scopeRoot, exclude = [], records = [], fsImpl = fs } ) {
96
96
  const candidates = new Map();
97
97
  const mcpOnly = new Map();
98
98
  const resources = plan.ledger?.resources ?? [];
99
99
  const tombstones = plan.ledger?.tombstones ?? [];
100
100
  // A retained or released change gives up a ledger row and touches no file.
101
- const changes = plan.changes.filter((change: any) => change.retained !== true && change.release !== true);
101
+ const changes = plan.changes.filter((change ) => change.retained !== true && change.release !== true);
102
102
 
103
103
  for (const change of changes) {
104
104
  const target = path.resolve(change.target);
@@ -121,7 +121,7 @@ function backupSetFromPlan({ plan, scope, scopeRoot, exclude = [], records = [],
121
121
  }
122
122
  }
123
123
 
124
- const owned = new Set(resources.filter((r: any) => typeof r.target === 'string').map((r: any) => path.resolve(r.target)));
124
+ const owned = new Set(resources.filter((r ) => typeof r.target === 'string').map((r ) => path.resolve(r.target)));
125
125
  for (const tombstone of tombstones) {
126
126
  if (tombstone.sweptAt || typeof tombstone.fromTarget !== 'string') continue;
127
127
  if (owned.has(path.resolve(tombstone.fromTarget)) || !fsImpl.existsSync(tombstone.fromTarget)) continue;
@@ -137,7 +137,7 @@ function backupSetFromPlan({ plan, scope, scopeRoot, exclude = [], records = [],
137
137
 
138
138
  /** A BackupSet of the given absolute paths, for the pre-rollback snapshot. `records` as for
139
139
  * backupSetFromPlan: a restored ledger is snapshotted too, so the snapshot rolls back as a whole. */
140
- function backupSetFromPaths({ items, scope, scopeRoot, exclude = [], records = [], fsImpl = fs }: any): any {
140
+ function backupSetFromPaths({ items, scope, scopeRoot, exclude = [], records = [], fsImpl = fs } ) {
141
141
  const candidates = new Map();
142
142
  for (const item of items) {
143
143
  if (item.harnesses.length === 0) addCandidate(candidates, item.path, null, 'snapshot');
@@ -13,18 +13,18 @@
13
13
  //
14
14
  // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): authored as `.ts`, run directly by Node
15
15
  // native type-stripping — no build step, no runtime dependency. Body stays CommonJS so a `.js`
16
- // caller's `require('../install/backup.ts')` keeps working while the tree is still `.js`. The
17
- // `./paths` require already carries `.ts` (paths migrated first); `../helper/git.ts` is `.ts` too.
16
+ // caller's `require('../install/backup.js')` keeps working while the tree is still `.js`. The
17
+ // `./paths` require already carries `.ts` (paths migrated first); `../helper/git.js` is `.ts` too.
18
18
  const fs = require('node:fs');
19
19
  const os = require('node:os');
20
20
  const path = require('node:path');
21
21
  const crypto = require('node:crypto');
22
22
  const { execFileSync } = require('node:child_process');
23
- const { sourceCommit: gitSourceCommit } = require('../helper/git.ts');
23
+ const { sourceCommit: gitSourceCommit } = require('../helper/git.js');
24
24
  const {
25
25
  legacyBackupReadRoot, isLegacyBackupReadRoot, scopeRootFromCanonicalBackupRoot,
26
- } = require('./paths.ts');
27
- const { validateLedger } = require('../state/index.ts');
26
+ } = require('./paths.js');
27
+ const { validateLedger } = require('../state/index.js');
28
28
 
29
29
  /** Where a listed/restorable backup was found. 'current' = the canonical `.doflow/backups` root the
30
30
  * installer writes to; 'legacy' = the pre-7de6d5f `.claude/backups` root, readable only. Callers
@@ -34,7 +34,7 @@ const BACKUP_ORIGIN_LEGACY = 'legacy';
34
34
 
35
35
  /** The roots a READ (list/restore) consults for a scope, canonical first, legacy second.
36
36
  * Order is the precedence rule too: an id present in both resolves to the canonical copy. */
37
- function backupReadRoots(backupRoot: string): Array<{ root: string; origin: string }> {
37
+ function backupReadRoots(backupRoot ) {
38
38
  const roots = [{ root: path.resolve(backupRoot), origin: BACKUP_ORIGIN_CURRENT }];
39
39
  const scopeRoot = scopeRootFromCanonicalBackupRoot(backupRoot);
40
40
  if (scopeRoot) {
@@ -47,16 +47,16 @@ function backupReadRoots(backupRoot: string): Array<{ root: string; origin: stri
47
47
  /** Writes and deletes are canonical-only. The legacy root holds restore points DoFlow no longer
48
48
  * manages and may be a user's sole recovery material, so a mutating call aimed at it is a bug to
49
49
  * surface, not a path to follow. */
50
- function assertMutableBackupRoot(backupRoot: string, operation: string): void {
50
+ function assertMutableBackupRoot(backupRoot , operation ) {
51
51
  if (isLegacyBackupReadRoot(backupRoot)) {
52
52
  throw new Error(`Refusing to ${operation} in the legacy backup root (read-only): ${backupRoot}`);
53
53
  }
54
54
  }
55
55
 
56
- function pad2(n: number): string { return String(n).padStart(2, '0'); }
56
+ function pad2(n ) { return String(n).padStart(2, '0'); }
57
57
 
58
58
  /** `<op>_YYYY-MM-DD_HH-MM-SS`, using a caller-supplied Date (never `new Date()` internally — keeps this testable/deterministic). */
59
- function backupId(op: string, date: Date): string {
59
+ function backupId(op , date ) {
60
60
  const y = date.getFullYear();
61
61
  const mo = pad2(date.getMonth() + 1);
62
62
  const d = pad2(date.getDate());
@@ -78,8 +78,8 @@ const DEFAULT_BACKUP_RETENTION = 3;
78
78
 
79
79
  /** A backup could not be written completely. The caller stops before changing anything. */
80
80
  class BackupError extends Error {
81
- declare code: string;
82
- constructor(message: string) {
81
+
82
+ constructor(message ) {
83
83
  super(message);
84
84
  this.name = 'BackupError';
85
85
  this.code = 'backup-failed';
@@ -87,14 +87,14 @@ class BackupError extends Error {
87
87
  }
88
88
 
89
89
  /** POSIX path of `p` relative to `scopeRoot`, or null when `p` is not strictly inside it. */
90
- function scopeRelative(scopeRoot: string, p: string): string | null {
90
+ function scopeRelative(scopeRoot , p ) {
91
91
  const rel = path.relative(scopeRoot, p);
92
92
  if (!rel || path.isAbsolute(rel) || rel.split(path.sep)[0] === '..') return null;
93
93
  return rel.split(path.sep).join('/');
94
94
  }
95
95
 
96
96
  /** Manifest JSON: two-space indentation, except one compact line per `files` and `excluded` element. */
97
- function serializeManifest(manifest: Record<string, any>): string {
97
+ function serializeManifest(manifest ) {
98
98
  const body = Object.entries(manifest).map(([key, value]) => {
99
99
  if ((key === 'files' || key === 'excluded') && Array.isArray(value) && value.length > 0) {
100
100
  return ` ${JSON.stringify(key)}: [\n${value.map((v) => ` ${JSON.stringify(v)}`).join(',\n')}\n ]`;
@@ -104,7 +104,7 @@ function serializeManifest(manifest: Record<string, any>): string {
104
104
  return `{\n${body.join(',\n')}\n}\n`;
105
105
  }
106
106
 
107
- function errorCode(err: any): string { return err.code || err.message; }
107
+ function errorCode(err ) { return err.code || err.message; }
108
108
 
109
109
  /**
110
110
  * Copy the files of a backup set into `<backupRoot>/<id>/` (format 2). Returns null for an empty
@@ -114,14 +114,14 @@ function errorCode(err: any): string { return err.code || err.message; }
114
114
  function createFileBackup({
115
115
  operation, set, backupRoot, repoRoot, sourceCommit, version, date, restores = null,
116
116
  fsImpl = fs, randomHex = () => crypto.randomBytes(4).toString('hex'),
117
- }: any): null | { id: string; dir: string; files: number; existed: number; absent: number; bytes: number } {
117
+ } ) {
118
118
  assertMutableBackupRoot(backupRoot, 'create a backup');
119
119
  if (set.entries.length === 0) return null;
120
120
 
121
121
  const base = backupId(operation, date);
122
122
  const tmpDir = path.join(backupRoot, `${TEMP_PREFIX}${base}-${process.pid}-${randomHex()}`);
123
123
  let tmpCreated = false;
124
- const writeFailure = (err: any) => new BackupError(`could not write ${backupRoot}: ${errorCode(err)}`);
124
+ const writeFailure = (err ) => new BackupError(`could not write ${backupRoot}: ${errorCode(err)}`);
125
125
 
126
126
  try {
127
127
  try {
@@ -137,11 +137,11 @@ function createFileBackup({
137
137
  const summary = { files: 0, existed: 0, absent: 0, bytes: 0 };
138
138
  let external = 0;
139
139
  for (const entry of set.entries) {
140
- const record: any = entry.rel !== null ? { path: entry.rel } : { path: entry.path, outside: true };
140
+ const record = entry.rel !== null ? { path: entry.rel } : { path: entry.path, outside: true };
141
141
  let stat = null;
142
142
  try {
143
143
  stat = fsImpl.statSync(entry.path);
144
- } catch (err: any) {
144
+ } catch (err ) {
145
145
  if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') {
146
146
  throw new BackupError(`could not read ${entry.path}: ${errorCode(err)}`);
147
147
  }
@@ -172,21 +172,21 @@ function createFileBackup({
172
172
  files.push(record);
173
173
  }
174
174
 
175
- const manifest: any = {
175
+ const manifest = {
176
176
  id: base,
177
177
  operation,
178
178
  timestamp: date.toISOString(),
179
179
  source_path: repoRoot,
180
180
  source_commit: sourceCommit ?? gitSourceCommit(repoRoot),
181
181
  type: 'files',
182
- tools_affected: [...new Set(set.entries.flatMap((e: any) => e.harnesses))].sort(),
182
+ tools_affected: [...new Set(set.entries.flatMap((e ) => e.harnesses))].sort(),
183
183
  format: 2,
184
184
  doflow_version: version,
185
185
  scope: set.scope,
186
186
  scope_root: set.scopeRoot,
187
187
  restores,
188
188
  summary,
189
- excluded: set.excluded.map((x: any) => {
189
+ excluded: set.excluded.map((x ) => {
190
190
  const rel = scopeRelative(set.scopeRoot, x.path);
191
191
  return rel !== null ? { path: rel, reason: x.reason } : { path: x.path, outside: true, reason: x.reason };
192
192
  }),
@@ -209,7 +209,7 @@ function createFileBackup({
209
209
  fsImpl.writeFileSync(manifestPath, serializeManifest(manifest));
210
210
  }
211
211
  fsImpl.renameSync(tmpDir, dir);
212
- } catch (err: any) {
212
+ } catch (err ) {
213
213
  if (RENAME_COLLISION_CODES.has(err.code) && fsImpl.existsSync(dir)) continue;
214
214
  throw writeFailure(err);
215
215
  }
@@ -224,17 +224,17 @@ function createFileBackup({
224
224
  }
225
225
  }
226
226
 
227
- function defaultIsAlive(pid: number): boolean {
227
+ function defaultIsAlive(pid ) {
228
228
  if (!Number.isInteger(pid) || pid <= 0) return false;
229
229
  try {
230
230
  process.kill(pid, 0);
231
231
  return true;
232
- } catch (err: any) {
232
+ } catch (err ) {
233
233
  return err.code === 'EPERM';
234
234
  }
235
235
  }
236
236
 
237
- function isStaleTemp(dir: string, { now, fsImpl, isAlive, hostname }: any): boolean {
237
+ function isStaleTemp(dir , { now, fsImpl, isAlive, hostname } ) {
238
238
  let owner = null;
239
239
  try { owner = JSON.parse(fsImpl.readFileSync(path.join(dir, OWNER_FILE), 'utf8')); } catch { /* no owner */ }
240
240
  if (!owner || typeof owner !== 'object') {
@@ -248,7 +248,7 @@ function isStaleTemp(dir: string, { now, fsImpl, isAlive, hostname }: any): bool
248
248
  * Remove `.tmp-*` directories left in `backupRoot` by runs that died: the owner process is gone on
249
249
  * this host, or the temp is older than 24 hours. Returns how many were removed.
250
250
  */
251
- function sweepStaleTemps(backupRoot: string, { now = Date.now(), fsImpl = fs, isAlive = defaultIsAlive, hostname = os.hostname() }: any = {}): number {
251
+ function sweepStaleTemps(backupRoot , { now = Date.now(), fsImpl = fs, isAlive = defaultIsAlive, hostname = os.hostname() } = {}) {
252
252
  assertMutableBackupRoot(backupRoot, 'clean up backups');
253
253
  if (!fsImpl.existsSync(backupRoot)) return 0;
254
254
  let removed = 0;
@@ -269,8 +269,8 @@ function sweepStaleTemps(backupRoot: string, { now = Date.now(), fsImpl = fs, is
269
269
  * not know), 'full' or 'partial' (1.19, typed the way restoreBackup reads them), or 'incomplete'
270
270
  * (no manifest, or one that does not parse). `manifest` is the parsed object, or null.
271
271
  */
272
- function classifyBackupDir(dir: string, { fsImpl = fs }: any = {}): { kind: string; manifest: any } {
273
- let manifest: any = null;
272
+ function classifyBackupDir(dir , { fsImpl = fs } = {}) {
273
+ let manifest = null;
274
274
  try { manifest = JSON.parse(fsImpl.readFileSync(path.join(dir, MANIFEST_FILE), 'utf8')); } catch { /* incomplete */ }
275
275
  if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest)) return { kind: 'incomplete', manifest: null };
276
276
  const { format } = manifest;
@@ -286,7 +286,7 @@ const ID_TIME = /^.+?_(\d{4})-(\d{2})-(\d{2})_(\d{2})-(\d{2})-(\d{2})(?:-(\d+))?
286
286
  * Ordering key of a backup directory: the manifest timestamp, else the time in its id (local
287
287
  * time, as backupId writes it), else its mtime. Retention and listing share it.
288
288
  */
289
- function sortKey(dir: string, { fsImpl = fs, manifest }: any = {}): { id: string; time: number; suffix: number } {
289
+ function sortKey(dir , { fsImpl = fs, manifest } = {}) {
290
290
  const id = path.basename(dir);
291
291
  const m = manifest === undefined ? classifyBackupDir(dir, { fsImpl }).manifest : manifest;
292
292
  const match = ID_TIME.exec(id);
@@ -303,7 +303,7 @@ function sortKey(dir: string, { fsImpl = fs, manifest }: any = {}): { id: string
303
303
  }
304
304
 
305
305
  /** Newest first; ties go to the larger `-n` suffix, then the id in descending order. */
306
- function compareSortKeys(a: { id: string; time: number; suffix: number }, b: { id: string; time: number; suffix: number }): number {
306
+ function compareSortKeys(a , b ) {
307
307
  if (a.time !== b.time) return b.time - a.time;
308
308
  if (a.suffix !== b.suffix) return b.suffix - a.suffix;
309
309
  if (a.id === b.id) return 0;
@@ -311,7 +311,7 @@ function compareSortKeys(a: { id: string; time: number; suffix: number }, b: { i
311
311
  }
312
312
 
313
313
  /** Bytes of the regular files under `dir`, never following a link; null when any read fails. */
314
- function backupSize(dir: string, { fsImpl = fs }: any = {}): number | null {
314
+ function backupSize(dir , { fsImpl = fs } = {}) {
315
315
  try {
316
316
  let total = 0;
317
317
  const pending = [dir];
@@ -333,7 +333,7 @@ function backupSize(dir: string, { fsImpl = fs }: any = {}): number | null {
333
333
  const BYTE_UNITS = ['KiB', 'MiB', 'GiB', 'TiB'];
334
334
 
335
335
  /** `?` for an unknown size, `<n> B` below 1 KiB, otherwise one decimal in 1024-based units. */
336
- function formatBytes(n: number | null | undefined): string {
336
+ function formatBytes(n ) {
337
337
  if (n === null || n === undefined) return '?';
338
338
  if (n < 1024) return `${n} B`;
339
339
  let value = n / 1024;
@@ -346,14 +346,14 @@ function formatBytes(n: number | null | undefined): string {
346
346
  }
347
347
 
348
348
  /** Security guard: a backup id is a directory *name*, never a path — reject traversal/absolute ids. */
349
- function assertSafeBackupId(bid: string): void {
349
+ function assertSafeBackupId(bid ) {
350
350
  if (!bid || bid.includes('/') || bid.includes('\\') || bid === '..' || bid.includes('..')) {
351
351
  throw new Error(`Invalid backup id: '${bid}'`);
352
352
  }
353
353
  }
354
354
 
355
355
  /** Restore a 1.19 backup id into the given tool dirs. dirs = {tool: absDstDir}. */
356
- function restoreBackup({ bid, backupRoot, dirs, dryRun = false }: any): void {
356
+ function restoreBackup({ bid, backupRoot, dirs, dryRun = false } ) {
357
357
  assertSafeBackupId(bid);
358
358
  // An id the user picked from `list-backups` may live in either root, so resolve it the same way
359
359
  // the listing found it. Restoring reads the backup and writes only into the tool dirs, so a
@@ -397,24 +397,24 @@ function restoreBackup({ bid, backupRoot, dirs, dryRun = false }: any): void {
397
397
 
398
398
  /** Raw member lines of a tar.gz archive. A whole-home archive can list far more than the default
399
399
  * 1 MiB of output, so the buffer is unbounded. */
400
- function defaultListTar(archive: string): string[] {
400
+ function defaultListTar(archive ) {
401
401
  return execFileSync('tar', ['-tzf', archive], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], maxBuffer: Infinity })
402
402
  .split(/\r?\n/);
403
403
  }
404
404
 
405
405
  /** File members of an archive listing, relative to the archive root, without any `..` member. */
406
- function archiveMembers(lines: string[]): string[] {
406
+ function archiveMembers(lines ) {
407
407
  return lines
408
408
  .map((line) => line.replace(/^\.\//, ''))
409
409
  .filter((m) => m && m !== '.' && !m.endsWith('/') && !m.split('/').includes('..'));
410
410
  }
411
411
 
412
412
  /** Paths, relative to `root`, of the regular files under it; links are not followed. */
413
- function regularFilesUnder(root: string, fsImpl: any): string[] {
414
- const files: string[] = [];
413
+ function regularFilesUnder(root , fsImpl ) {
414
+ const files = [];
415
415
  const pending = [''];
416
416
  while (pending.length > 0) {
417
- const rel = pending.pop() as string;
417
+ const rel = pending.pop() ;
418
418
  for (const e of fsImpl.readdirSync(path.join(root, rel), { withFileTypes: true })) {
419
419
  const child = path.join(rel, e.name);
420
420
  if (e.isDirectory()) pending.push(child);
@@ -425,7 +425,7 @@ function regularFilesUnder(root: string, fsImpl: any): string[] {
425
425
  }
426
426
 
427
427
  /** Why a format-2 entry cannot be restored, or null when it can. */
428
- function refusalOf(entry: any, target: string, { scope, scopeRoot, bkDir, fsImpl }: any): string | null {
428
+ function refusalOf(entry , target , { scope, scopeRoot, bkDir, fsImpl } ) {
429
429
  const raw = entry.path;
430
430
  if (entry.outside === true) {
431
431
  if (!path.isAbsolute(raw)) return 'unsafe path';
@@ -451,7 +451,7 @@ function refusalOf(entry: any, target: string, { scope, scopeRoot, bkDir, fsImpl
451
451
  * tool through restoreBackup; its snapshot lists the files that restore will overwrite.
452
452
  * Reads only.
453
453
  */
454
- function planRestore({ bid, backupRoot, scope, scopeRoot, targets, explicitTargets, dirs, ledgerFile = null, fsImpl = fs, listTar = defaultListTar }: any): any {
454
+ function planRestore({ bid, backupRoot, scope, scopeRoot, targets, explicitTargets, dirs, ledgerFile = null, fsImpl = fs, listTar = defaultListTar } ) {
455
455
  assertSafeBackupId(bid);
456
456
  const found = backupReadRoots(backupRoot).find((r) => fsImpl.existsSync(path.join(r.root, bid)));
457
457
  if (!found) throw new Error(`Backup not found: ${bid}`);
@@ -461,7 +461,7 @@ function planRestore({ bid, backupRoot, scope, scopeRoot, targets, explicitTarge
461
461
  throw new Error(`Backup ${bid} was made by a newer DoFlow (format ${manifest.format}); this version cannot restore it`);
462
462
  }
463
463
 
464
- const plan: any = {
464
+ const plan = {
465
465
  bid,
466
466
  origin: found.origin,
467
467
  bkDir,
@@ -484,7 +484,7 @@ function planRestore({ bid, backupRoot, scope, scopeRoot, targets, explicitTarge
484
484
  // writes, so --target narrows it with the files and never skips it.
485
485
  const isLedger = ledger !== null && entry.outside !== true && typeof entry.path === 'string' && entry.path !== ''
486
486
  && path.resolve(scopeRoot, entry.path) === ledger;
487
- if (!isLedger && explicitTargets && !harnesses.some((h: string) => wanted.has(h))) {
487
+ if (!isLedger && explicitTargets && !harnesses.some((h ) => wanted.has(h))) {
488
488
  plan.untargeted += 1;
489
489
  continue;
490
490
  }
@@ -505,12 +505,12 @@ function planRestore({ bid, backupRoot, scope, scopeRoot, targets, explicitTarge
505
505
  }
506
506
  plan.restore.push({ path: target, stored: path.resolve(bkDir, entry.stored), mode: entry.mode, size: entry.size, harnesses, ...(isLedger ? { ledger: true } : {}) });
507
507
  }
508
- plan.snapshot = plan.restore.map((item: any) => ({ path: item.path, harnesses: item.harnesses }));
508
+ plan.snapshot = plan.restore.map((item ) => ({ path: item.path, harnesses: item.harnesses }));
509
509
  return plan;
510
510
  }
511
511
 
512
512
  const tools = [];
513
- for (const tool of wanted as Set<string>) {
513
+ for (const tool of wanted ) {
514
514
  const dstDir = dirs[tool];
515
515
  if (!dstDir) continue;
516
516
  if (plan.type === 'full') {
@@ -536,21 +536,21 @@ function planRestore({ bid, backupRoot, scope, scopeRoot, targets, explicitTarge
536
536
  * up) stay as they are; restoring the whole old ledger would make DoFlow forget files and entries it
537
537
  * still has on disk. With no current ledger the backup's is taken whole.
538
538
  */
539
- function mergeRestoredLedger(backup: any, current: any, restored: Set<string>): any {
539
+ function mergeRestoredLedger(backup , current , restored ) {
540
540
  if (!current) return backup;
541
- const at = (target: any) => typeof target === 'string' && restored.has(path.resolve(target));
541
+ const at = (target ) => typeof target === 'string' && restored.has(path.resolve(target));
542
542
  return {
543
543
  ...current,
544
- resources: [...current.resources.filter((r: any) => !at(r.target)), ...backup.resources.filter((r: any) => at(r.target))],
544
+ resources: [...current.resources.filter((r ) => !at(r.target)), ...backup.resources.filter((r ) => at(r.target))],
545
545
  tombstones: [
546
- ...(current.tombstones ?? []).filter((t: any) => !at(t.fromTarget)),
547
- ...(backup.tombstones ?? []).filter((t: any) => at(t.fromTarget)),
546
+ ...(current.tombstones ?? []).filter((t ) => !at(t.fromTarget)),
547
+ ...(backup.tombstones ?? []).filter((t ) => at(t.fromTarget)),
548
548
  ],
549
549
  };
550
550
  }
551
551
 
552
552
  /** The bytes a ledger restore item writes: the backup's ledger merged onto the current one. */
553
- function restoredLedgerBytes(item: any, restored: Set<string>, fsImpl: any): Buffer {
553
+ function restoredLedgerBytes(item , restored , fsImpl ) {
554
554
  const backup = validateLedger(JSON.parse(fsImpl.readFileSync(item.stored, 'utf8')));
555
555
  const current = fsImpl.existsSync(item.path) ? validateLedger(JSON.parse(fsImpl.readFileSync(item.path, 'utf8'))) : null;
556
556
  return Buffer.from(`${JSON.stringify(validateLedger(mergeRestoredLedger(backup, current, restored)), null, 2)}\n`);
@@ -563,17 +563,17 @@ function restoredLedgerBytes(item: any, restored: Set<string>, fsImpl: any): Buf
563
563
  * files written back before it (mergeRestoredLedger). It never deletes a file and never writes
564
564
  * outside `plan.restore`. A 1.19 plan goes through restoreBackup unchanged.
565
565
  */
566
- function executeRestore(plan: any, { backupRoot, dryRun = false, fsImpl = fs }: any): any {
566
+ function executeRestore(plan , { backupRoot, dryRun = false, fsImpl = fs } ) {
567
567
  if (plan.format !== 2) {
568
- const toolDirs = Object.fromEntries(plan.v1.tools.map((t: any) => [t.tool, t.dstDir]));
568
+ const toolDirs = Object.fromEntries(plan.v1.tools.map((t ) => [t.tool, t.dstDir]));
569
569
  restoreBackup({ bid: plan.bid, backupRoot, dirs: toolDirs, dryRun });
570
- return { restored: plan.v1.tools.map((t: any) => t.tool), failed: [], absent: [], refused: [], untargeted: 0, legacy: true };
570
+ return { restored: plan.v1.tools.map((t ) => t.tool), failed: [], absent: [], refused: [], untargeted: 0, legacy: true };
571
571
  }
572
572
 
573
573
  const restored = [];
574
574
  const failed = [];
575
- const files = new Set<string>();
576
- const items = [...plan.restore.filter((item: any) => !item.ledger), ...plan.restore.filter((item: any) => item.ledger)];
575
+ const files = new Set ();
576
+ const items = [...plan.restore.filter((item ) => !item.ledger), ...plan.restore.filter((item ) => item.ledger)];
577
577
  for (const item of items) {
578
578
  if (dryRun) {
579
579
  restored.push(item.path);
@@ -587,7 +587,7 @@ function executeRestore(plan: any, { backupRoot, dryRun = false, fsImpl = fs }:
587
587
  let isLink = false;
588
588
  try {
589
589
  isLink = fsImpl.lstatSync(item.path).isSymbolicLink();
590
- } catch (err: any) {
590
+ } catch (err ) {
591
591
  if (err.code !== 'ENOENT') throw err;
592
592
  }
593
593
  if (isLink) {
@@ -613,11 +613,11 @@ function executeRestore(plan: any, { backupRoot, dryRun = false, fsImpl = fs }:
613
613
 
614
614
  /** One `{ row, key }` per backup dir under `root`, tagged with where it came from. Dot-named
615
615
  * directories (temps of a run in progress) and symlinks are not backups. */
616
- function readBackupRows(root: string, origin: string): Array<{ key: any; row: any }> {
616
+ function readBackupRows(root , origin ) {
617
617
  if (!fs.existsSync(root)) return [];
618
618
  return fs.readdirSync(root, { withFileTypes: true })
619
- .filter((e: any) => e.isDirectory() && !e.name.startsWith('.'))
620
- .map((e: any) => {
619
+ .filter((e ) => e.isDirectory() && !e.name.startsWith('.'))
620
+ .map((e ) => {
621
621
  const bkDir = path.join(root, e.name);
622
622
  const { kind, manifest: m } = classifyBackupDir(bkDir);
623
623
  const key = sortKey(bkDir, { manifest: m });
@@ -641,7 +641,7 @@ function readBackupRows(root: string, origin: string): Array<{ key: any; row: an
641
641
  * manifest is missing), `complete` and `bytes` (null when unreadable), and are ordered by the
642
642
  * same key retention uses.
643
643
  */
644
- function listBackups(backupRoot: string): any[] {
644
+ function listBackups(backupRoot ) {
645
645
  const found = [];
646
646
  const seen = new Set();
647
647
  for (const { root, origin } of backupReadRoots(backupRoot)) {
@@ -663,10 +663,10 @@ function listBackups(backupRoot: string): any[] {
663
663
  * nothing; `reserve` makes room for a backup the real run would add, and `kept` then counts it.
664
664
  * Never reaches the legacy `.claude/backups` root: those may be a user's only restore points.
665
665
  */
666
- function applyRetention({ backupRoot, keep, protect = [], reserve = 0, dryRun = false, now = Date.now(), fsImpl = fs }: any): any {
666
+ function applyRetention({ backupRoot, keep, protect = [], reserve = 0, dryRun = false, now = Date.now(), fsImpl = fs } ) {
667
667
  assertMutableBackupRoot(backupRoot, 'prune backups');
668
668
  if (!Number.isInteger(keep) || keep < 0) throw new Error(`Backup retention must be a non-negative whole number, got ${keep}`);
669
- const result: any = { keep, kept: 0, removed: [], wouldRemove: [], failed: [], sweptTemps: 0 };
669
+ const result = { keep, kept: 0, removed: [], wouldRemove: [], failed: [], sweptTemps: 0 };
670
670
 
671
671
  // Only DoFlow backups count: a directory with a readable manifest, or one named like a backup id
672
672
  // (a 1.19 or interrupted backup can lack a manifest). Anything else in the root is not ours to
@@ -701,7 +701,7 @@ function applyRetention({ backupRoot, keep, protect = [], reserve = 0, dryRun =
701
701
  try {
702
702
  fsImpl.rmSync(dir, { recursive: true, force: true });
703
703
  result.removed.push(id);
704
- } catch (err: any) {
704
+ } catch (err ) {
705
705
  result.failed.push({ id, error: err.message });
706
706
  }
707
707
  }
@@ -710,7 +710,7 @@ function applyRetention({ backupRoot, keep, protect = [], reserve = 0, dryRun =
710
710
  }
711
711
 
712
712
  /** The parenthesised end of a retention line: the `--prune` value given, or the default. */
713
- function retentionNote(prune: number | null | undefined, keep: number): string {
713
+ function retentionNote(prune , keep ) {
714
714
  if (prune === null || prune === undefined) return `keeping the newest ${keep}; --prune N changes this`;
715
715
  if (prune === 0) return '--prune 0 keeps every backup';
716
716
  return `--prune ${keep}`;
@@ -5,27 +5,27 @@
5
5
  //
6
6
  // TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): authored as `.ts`, run directly by Node
7
7
  // native type-stripping — no build step, no runtime dependency. Body stays CommonJS so a `.js`
8
- // caller's `require('../install/context.ts')` keeps working while the tree is still `.js`.
8
+ // caller's `require('../install/context.js')` keeps working while the tree is still `.js`.
9
9
  const path = require('node:path');
10
- const { sourceCommit: gitSourceCommit } = require('../helper/git.ts');
10
+ const { sourceCommit: gitSourceCommit } = require('../helper/git.js');
11
11
 
12
- interface ResolveContextInput {
13
- repoRoot: string;
14
- global: boolean;
15
- projectRoot: string;
16
- targets: string[];
17
- dirs: Record<string, string>;
18
- sourceCommit?: string;
19
- managedResources?: object[];
20
- }
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
21
 
22
22
  /**
23
23
  * `sourceCommit` lets a caller (bin/doflow.js) pass an already-resolved commit instead of this
24
24
  * module spawning its own `git rev-parse`; omit it to resolve here (e.g. tests calling this module
25
25
  * directly). Returns a plain properties object (used by both the text and --json renderers).
26
26
  */
27
- function resolveContext({ repoRoot, global, projectRoot, targets, dirs, sourceCommit, managedResources }: ResolveContextInput): any {
28
- const context: any = {
27
+ function resolveContext({ repoRoot, global, projectRoot, targets, dirs, sourceCommit, managedResources } ) {
28
+ const context = {
29
29
  scope: global ? 'global' : 'project',
30
30
  scopeRoot: global ? path.dirname(dirs.claude) : path.resolve(projectRoot),
31
31
  targets,
@@ -41,12 +41,12 @@ function resolveContext({ repoRoot, global, projectRoot, targets, dirs, sourceCo
41
41
  }
42
42
 
43
43
  /** Human-readable "Resolved context" block, printed to stderr (keeps stdout clean like sync.sh's log()). */
44
- function printContext(ctx: any): void {
44
+ function printContext(ctx ) {
45
45
  const lines = [
46
46
  '[CONTEXT] Resolved install context',
47
47
  ` scope : ${ctx.scope} (root: ${ctx.scopeRoot})`,
48
48
  ` targets : ${ctx.targets.join(', ')}`,
49
- ...ctx.targets.map((t: string) => ` ${t.padEnd(7)}-> ${ctx.dirs[t]}`),
49
+ ...ctx.targets.map((t ) => ` ${t.padEnd(7)}-> ${ctx.dirs[t]}`),
50
50
  ` repo root : ${ctx.repoRoot}`,
51
51
  ` source commit : ${ctx.sourceCommit}`,
52
52
  ` node : ${ctx.nodeVersion}`,