@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
@@ -4,7 +4,7 @@
4
4
 
5
5
  // Harness adapters own native formats and paths. This module intentionally only
6
6
  // validates and resolves that boundary; it never reads or writes harness files.
7
- const { resolveRegistrySource } = require('./resolve-source.ts');
7
+ const { resolveRegistrySource } = require('./resolve-source.js');
8
8
 
9
9
  const ADAPTER_METHODS = Object.freeze(['discover', 'render', 'plan', 'apply', 'remove', 'verify']);
10
10
 
@@ -12,12 +12,12 @@ const ADAPTER_METHODS = Object.freeze(['discover', 'render', 'plan', 'apply', 'r
12
12
  * A3: the Codex shape used to be inlined here, putting per-harness knowledge on the shared side of
13
13
  * the boundary); this module only dispatches by adapter name. An adapter gaining a
14
14
  * nativeProjection declaration registers its normalizer here. */
15
- const NATIVE_PROJECTION_NORMALIZERS: Record<string, () => any> = Object.freeze({
16
- codex: () => require('./codex/native-projection.ts').normalizeNativeProjection,
15
+ const NATIVE_PROJECTION_NORMALIZERS = Object.freeze({
16
+ codex: () => require('./codex/native-projection.js').normalizeNativeProjection,
17
17
  });
18
18
 
19
19
  /** Dispatch a harness's nativeProjection declaration to its adapter's own normalizer. */
20
- function nativeProjectionFor(registry: any, harness: any, mcp: any, context: any) {
20
+ function nativeProjectionFor(registry , harness , mcp , context ) {
21
21
  if (!harness.nativeProjection) return {};
22
22
  const load = NATIVE_PROJECTION_NORMALIZERS[harness.adapter];
23
23
  if (!load) {
@@ -32,9 +32,9 @@ function nativeProjectionFor(registry: any, harness: any, mcp: any, context: any
32
32
  * adapter receives. Native serializers may add adapter-specific fields, but
33
33
  * cannot infer applicability from a missing projection.
34
34
  */
35
- function projectAdapterInput({ registry, harness, scope, scopeRoot, assets = [], mcp = [], mcpAdoptable = [], policies = [], context = {} }: any) {
35
+ function projectAdapterInput({ registry, harness, scope, scopeRoot, assets = [], mcp = [], mcpAdoptable = [], policies = [], context = {} } ) {
36
36
  if (!registry || !harness) throw new Error('registry and harness are required for adapter projection');
37
- const projectAsset = (asset: any) => {
37
+ const projectAsset = (asset ) => {
38
38
  const projection = asset?.projection?.[harness.id];
39
39
  if (!projection || typeof projection.renderer !== 'string' || typeof projection.capability !== 'string') {
40
40
  throw new Error(`Asset '${asset?.id ?? '?'}' lacks a projection for '${harness.id}'`);
@@ -62,16 +62,16 @@ function projectAdapterInput({ registry, harness, scope, scopeRoot, assets = [],
62
62
  return Object.freeze({
63
63
  harness, scope, scopeRoot, context,
64
64
  assets: Object.freeze(assets.map(projectAsset)),
65
- mcp: Object.freeze(mcp.map((server: any) => Object.freeze({ ...server }))),
65
+ mcp: Object.freeze(mcp.map((server ) => Object.freeze({ ...server }))),
66
66
  // Servers whose existing entries the adapter may take as its own while it holds no MCP row.
67
- mcpAdoptable: Object.freeze(mcpAdoptable.map((server: any) => Object.freeze({ ...server }))),
68
- policies: Object.freeze(policies.map((policy: any) => Object.freeze({ ...policy }))),
67
+ mcpAdoptable: Object.freeze(mcpAdoptable.map((server ) => Object.freeze({ ...server }))),
68
+ policies: Object.freeze(policies.map((policy ) => Object.freeze({ ...policy }))),
69
69
  nativeTargets: Object.freeze({ ...harness.nativeTargets }),
70
70
  projection: Object.freeze({ ...projection }),
71
71
  });
72
72
  }
73
73
 
74
- function assertAdapter(adapter: any, name = 'adapter') {
74
+ function assertAdapter(adapter , name = 'adapter') {
75
75
  if (!adapter || typeof adapter !== 'object') throw new Error(`${name} must be an object`);
76
76
  for (const method of ADAPTER_METHODS) {
77
77
  if (typeof adapter[method] !== 'function') throw new Error(`${name} must implement ${method}()`);
@@ -88,8 +88,8 @@ function createAdapterRegistry(adapters = {}) {
88
88
  }
89
89
  return Object.freeze({
90
90
  names: () => [...resolved.keys()],
91
- has: (name: any) => resolved.has(name),
92
- get(name: any) {
91
+ has: (name ) => resolved.has(name),
92
+ get(name ) {
93
93
  const adapter = resolved.get(name);
94
94
  if (!adapter) throw new Error(`No registered adapter '${name}'`);
95
95
  return adapter;
@@ -97,7 +97,7 @@ function createAdapterRegistry(adapters = {}) {
97
97
  });
98
98
  }
99
99
 
100
- function resolveAdapter(adapterRegistry: any, harness: any) {
100
+ function resolveAdapter(adapterRegistry , harness ) {
101
101
  if (!adapterRegistry || typeof adapterRegistry.get !== 'function') throw new Error('adapterRegistry must expose get(name)');
102
102
  if (!harness || typeof harness.adapter !== 'string') throw new Error('Harness must declare an adapter');
103
103
  return adapterRegistry.get(harness.adapter);
@@ -17,9 +17,9 @@
17
17
  // https://kiro.dev/docs/hooks/
18
18
  const fs = require('node:fs');
19
19
  const path = require('node:path');
20
- const { planTree, applyTree, removeTree, verifyTree, resolveTransform, copyTreeAssets, copyTreeDestDir, ledgerFileResources, ledgerSiblingFingerprints, siblingReplacedNotices, sourceDirFor } = require('../copy-tree.ts');
21
- const { readMcpFiles, planMcpEntries, verifyMcpEntries, ownedMcpIds, writeMcpEntries } = require('../mcp-entries.ts');
22
- const { declaredHarnessPaths, resolveHarnessPaths } = require('../../helper/harness-paths.ts');
20
+ const { planTree, applyTree, removeTree, verifyTree, resolveTransform, copyTreeAssets, copyTreeDestDir, ledgerFileResources, ledgerSiblingFingerprints, siblingReplacedNotices, sourceDirFor } = require('../copy-tree.js');
21
+ const { readMcpFiles, planMcpEntries, verifyMcpEntries, ownedMcpIds, writeMcpEntries } = require('../mcp-entries.js');
22
+ const { declaredHarnessPaths, resolveHarnessPaths } = require('../../helper/harness-paths.js');
23
23
 
24
24
  const HARNESS = 'kiro';
25
25
  const MCP_CONTAINER = 'mcpServers';
@@ -41,13 +41,13 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
41
41
  // Parity note: Kiro's historical nativePaths() derived even its user-scope root from scopeRoot
42
42
  // (the CLI passes $HOME there for -g installs), never from a separate homeDir argument — so the
43
43
  // resolver is fed no homeDir and "root" keeps resolving exactly as before.
44
- function nativePaths({ scope, scopeRoot }: any = {}) {
44
+ function nativePaths({ scope, scopeRoot } = {}) {
45
45
  return resolveHarnessPaths(declaredPaths, { scope, scopeRoot });
46
46
  }
47
47
 
48
48
  /** Discovery keeps the MCP files as plan read them (`mcpSnapshot`), which verify compares against,
49
49
  * and reports the servers DoFlow owns in Kiro's mcp.json now (`mcpOwned`). */
50
- function discover({ scope, scopeRoot, mcp = [], mcpAdoptable = [], ledger, fsImpl = fs }: any) {
50
+ function discover({ scope, scopeRoot, mcp = [], mcpAdoptable = [], ledger, fsImpl = fs } ) {
51
51
  const paths = nativePaths({ scope, scopeRoot });
52
52
  const entries = mcpEntriesInput({ paths, mcp, mcpAdoptable, ledger });
53
53
  const mcpSnapshot = readMcpFiles({ file: entries.file, ownRows: entries.ownRows, container: MCP_CONTAINER, fsImpl });
@@ -65,7 +65,7 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
65
65
  * `kiro-agents`, so nothing reaches this function. It is kept as a pure passthrough only to
66
66
  * satisfy the six-function adapter contract every harness must implement identically.
67
67
  */
68
- function render({ content = '' }: any = {}) {
68
+ function render({ content = '' } = {}) {
69
69
  return String(content);
70
70
  }
71
71
 
@@ -84,11 +84,11 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
84
84
  * `kiro-agent-json` layout and `kiro-agents` transform: kiro-cli discovers agents as
85
85
  * `.kiro/agents/<name>.json`, so each shared `<name>.md` spec is rendered, not mirrored.
86
86
  */
87
- function kiroTreeAssets(assets: any) {
88
- return [...copyTreeAssets(assets), ...(assets || []).filter((asset: any) => asset.renderer === 'kiro-agents')];
87
+ function kiroTreeAssets(assets ) {
88
+ return [...copyTreeAssets(assets), ...(assets || []).filter((asset ) => asset.renderer === 'kiro-agents')];
89
89
  }
90
90
 
91
- function planCopyTreeAssets({ assets, scope, scopeRoot, context, ledger, removing, fsImpl = fs }: any) {
91
+ function planCopyTreeAssets({ assets, scope, scopeRoot, context, ledger, removing, fsImpl = fs } ) {
92
92
  const paths = nativePaths({ scope, scopeRoot });
93
93
  const changes = [];
94
94
  const conflicts = [];
@@ -105,10 +105,10 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
105
105
  // is never deleted on removal, forced or not.
106
106
  force: !removing && context?.force === true, });
107
107
  treeResults.push(result);
108
- conflicts.push(...result.conflicts.map((reason: any) => `${asset.id}: ${reason}`));
108
+ conflicts.push(...result.conflicts.map((reason ) => `${asset.id}: ${reason}`));
109
109
  // A hand-edited file at a path DoFlow no longer writes is the user's: it stays, only its
110
110
  // ownership row is released.
111
- notices.push(...result.kept.map((item: any) => `kept hand-edited ${path.relative(paths.root, item.target)}; DoFlow no longer manages it`));
111
+ notices.push(...result.kept.map((item ) => `kept hand-edited ${path.relative(paths.root, item.target)}; DoFlow no longer manages it`));
112
112
  for (const change of result.changes) {
113
113
  changes.push({
114
114
  assetId: asset.id, target: change.target, source: change.source, operation: change.operation,
@@ -124,7 +124,7 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
124
124
  return { changes, conflicts, notices: [...siblingReplacedNotices(treeResults), ...notices] };
125
125
  }
126
126
 
127
- function applyCopyTreeAssets(changes: any, { fsImpl = fs }: any = {}) {
127
+ function applyCopyTreeAssets(changes , { fsImpl = fs } = {}) {
128
128
  let applied = 0;
129
129
  const grouped = new Map();
130
130
  for (const change of changes) {
@@ -139,13 +139,13 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
139
139
  return applied;
140
140
  }
141
141
 
142
- function removeCopyTreeAssets(changes: any, { fsImpl = fs }: any = {}) {
143
- const treeChanges = changes.filter((change: any) => change.kind === 'copy-tree-file' && change.operation === 'remove' && !change.retained)
144
- .map((change: any) => ({ relPath: change.identity, target: change.target, operation: 'remove', fingerprint: change.fingerprint, pruneGuard: change.pruneGuard }));
142
+ function removeCopyTreeAssets(changes , { fsImpl = fs } = {}) {
143
+ const treeChanges = changes.filter((change ) => change.kind === 'copy-tree-file' && change.operation === 'remove' && !change.retained)
144
+ .map((change ) => ({ relPath: change.identity, target: change.target, operation: 'remove', fingerprint: change.fingerprint, pruneGuard: change.pruneGuard }));
145
145
  return removeTree({ changes: treeChanges, fsImpl }).removed;
146
146
  }
147
147
 
148
- function verifyCopyTreeAssets({ assets, scope, scopeRoot, context, fsImpl = fs }: any) {
148
+ function verifyCopyTreeAssets({ assets, scope, scopeRoot, context, fsImpl = fs } ) {
149
149
  const paths = nativePaths({ scope, scopeRoot });
150
150
  const statuses = [];
151
151
  const resources = [];
@@ -154,7 +154,7 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
154
154
  const destDir = copyTreeDestDir(paths.configDir, asset);
155
155
  const sourceDir = sourceDirFor(asset, context, fsImpl, 'Kiro');
156
156
  const result = verifyTree({ sourceDir, destDir, fsImpl, layout: asset.layout, transform: asset.transform, plannedWrites: context?.plannedWrites });
157
- conflicts.push(...result.conflicts.map((reason: any) => `${asset.id}: ${reason}`));
157
+ conflicts.push(...result.conflicts.map((reason ) => `${asset.id}: ${reason}`));
158
158
  for (const resource of result.resources) {
159
159
  resources.push({
160
160
  assetId: asset.id, target: resource.target, ownershipIdentity: `doflow:${HARNESS}:copy-tree:${asset.id}:${resource.relPath}`,
@@ -177,15 +177,15 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
177
177
  * registry (core/registry/mcp.json) only ever declares stdio servers today, so only the fields it
178
178
  * actually carries are emitted — this never invents a field the registry doesn't supply.
179
179
  */
180
- function buildServerEntry(server: any) {
180
+ function buildServerEntry(server ) {
181
181
  if (server.url) return { url: server.url, ...(server.headers ? { headers: server.headers } : {}) };
182
182
  return { command: server.command, ...(server.args?.length ? { args: server.args } : {}) };
183
183
  }
184
184
 
185
- function ownershipIdentity(id: any) { return `doflow:${HARNESS}:mcp-server:${id}`; }
185
+ function ownershipIdentity(id ) { return `doflow:${HARNESS}:mcp-server:${id}`; }
186
186
 
187
- function mcpOwnRows(ledger: any) {
188
- return (ledger?.resources || []).filter((row: any) => row.harness === HARNESS).flatMap((row: any) => {
187
+ function mcpOwnRows(ledger ) {
188
+ return (ledger?.resources || []).filter((row ) => row.harness === HARNESS).flatMap((row ) => {
189
189
  if (row.kind === 'mcp-server') {
190
190
  return [{ identity: row.identity, target: row.target, fingerprint: row.fingerprint ?? null, ownershipIdentity: row.ownershipIdentity, legacy: false }];
191
191
  }
@@ -196,13 +196,13 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
196
196
  }
197
197
 
198
198
  /** Everything the shared entry-ownership rules (../mcp-entries.ts) need from this adapter. */
199
- function mcpEntriesInput({ paths, mcp = [], mcpAdoptable = [], ledger, assets = [] }: any) {
200
- const rendered = (servers: any) => servers.map((server: any) => ({ id: server.id, entry: buildServerEntry(server) }));
199
+ function mcpEntriesInput({ paths, mcp = [], mcpAdoptable = [], ledger, assets = [] } ) {
200
+ const rendered = (servers ) => servers.map((server ) => ({ id: server.id, entry: buildServerEntry(server) }));
201
201
  return {
202
202
  file: paths.mcp, selected: rendered(mcp), adoptable: rendered(mcpAdoptable),
203
203
  ownRows: mcpOwnRows(ledger),
204
- foreignRows: (ledger?.resources || []).filter((row: any) => row.kind === 'mcp-server' && row.harness !== HARNESS)
205
- .map((row: any) => ({ harness: row.harness, identity: row.identity, target: row.target })),
204
+ foreignRows: (ledger?.resources || []).filter((row ) => row.kind === 'mcp-server' && row.harness !== HARNESS)
205
+ .map((row ) => ({ harness: row.harness, identity: row.identity, target: row.target })),
206
206
  identityFor: ownershipIdentity, assetId: pseudoAssetId(assets), renderer: MCP_RENDERER, label: 'Kiro MCP',
207
207
  };
208
208
  }
@@ -210,9 +210,9 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
210
210
  /** Kiro has no hooks or mcp asset of its own in core/registry/assets.json (mcp servers come from
211
211
  * core/registry/mcp.json, not an asset), so an MCP entry change piggybacks on an asset id
212
212
  * this harness actually receives — the same "pseudo-component" technique
213
- * src/adapters/gemini/index.ts#hooksAssetId already uses for its own settings-only component. */
214
- function pseudoAssetId(assets: any) {
215
- return assets.find((asset: any) => asset.id === 'guidance.context-layer')?.id ?? assets[0]?.id;
213
+ * src/adapters/gemini/index.js#hooksAssetId already uses for its own settings-only component. */
214
+ function pseudoAssetId(assets ) {
215
+ return assets.find((asset ) => asset.id === 'guidance.context-layer')?.id ?? assets[0]?.id;
216
216
  }
217
217
 
218
218
  // ---- shared adapter contract ----
@@ -226,11 +226,11 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
226
226
  * `assets` array; per-file installed status for the hooks tree itself is reported through
227
227
  * `copyTree.statuses`/`copyTree.resources`, exactly like it is for skills and agents.
228
228
  */
229
- function hooksSurface(paths: any) {
229
+ function hooksSurface(paths ) {
230
230
  return { status: 'supported', target: paths.hooks };
231
231
  }
232
232
 
233
- function plan({ scope, scopeRoot, assets = [], mcp = [], mcpAdoptable = [], discovery, context = {}, ledger, fsImpl = fs }: any) {
233
+ function plan({ scope, scopeRoot, assets = [], mcp = [], mcpAdoptable = [], discovery, context = {}, ledger, fsImpl = fs } ) {
234
234
  const found = discovery || discover({ scope, scopeRoot, mcp, mcpAdoptable, ledger, fsImpl });
235
235
  const changes = [];
236
236
  const conflicts = [];
@@ -253,12 +253,25 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
253
253
  skills: { status: 'supported', target: found.paths.skills },
254
254
  agents: { status: 'supported', target: found.paths.agents },
255
255
  hooks: hooksSurface(found.paths),
256
- mcp: { status: found.mcpSnapshot[found.paths.mcp].ok ? 'supported' : 'blocked', target: found.paths.mcp, selected: mcp.map((item: any) => item.id) },
256
+ mcp: { status: found.mcpSnapshot[found.paths.mcp].ok ? 'supported' : 'blocked', target: found.paths.mcp, selected: mcp.map((item ) => item.id) },
257
257
  },
258
258
  };
259
259
  }
260
260
 
261
- function apply({ changes = [], fsImpl = fs }: any) {
261
+ /** The hook front doors (IC-037): the scripts and the agent that wires them. */
262
+ function hookDoorAssets(assets ) { return (assets || []).filter((asset ) => asset.id === 'kiro.hooks-scripts' || asset.id === 'kiro.doflow-agent'); }
263
+
264
+ function planHookDoors({ scope, scopeRoot, assets = [], context = {}, ledger, fsImpl = fs } ) {
265
+ const doors = planCopyTreeAssets({ assets: hookDoorAssets(assets), scope, scopeRoot, context, ledger, removing: false, fsImpl });
266
+ return { changes: doors.changes, conflicts: doors.conflicts, prerequisites: [], notices: doors.notices };
267
+ }
268
+
269
+ function verifyHookDoors({ scope, scopeRoot, assets = [], context = {}, fsImpl = fs } ) {
270
+ const doors = verifyCopyTreeAssets({ assets: hookDoorAssets(assets), scope, scopeRoot, context, fsImpl });
271
+ return { ok: doors.conflicts.length === 0, statuses: doors.statuses, resources: doors.resources, conflicts: doors.conflicts };
272
+ }
273
+
274
+ function apply({ changes = [], fsImpl = fs } ) {
262
275
  writeMcpEntries(changes, { container: MCP_CONTAINER, fsImpl });
263
276
  applyCopyTreeAssets(changes, { fsImpl });
264
277
  // An update that no longer ships a copy-tree file carries its rows as removals inside the
@@ -266,12 +279,12 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
266
279
  removeCopyTreeAssets(changes, { fsImpl });
267
280
  }
268
281
 
269
- function remove({ changes = [], fsImpl = fs }: any) {
282
+ function remove({ changes = [], fsImpl = fs } ) {
270
283
  writeMcpEntries(changes, { container: MCP_CONTAINER, fsImpl });
271
284
  removeCopyTreeAssets(changes, { fsImpl });
272
285
  }
273
286
 
274
- function verify({ scope, scopeRoot, assets = [], mcp = [], mcpAdoptable = [], discovery, ledger, operation, context = {}, fsImpl = fs }: any) {
287
+ function verify({ scope, scopeRoot, assets = [], mcp = [], mcpAdoptable = [], discovery, ledger, operation, context = {}, fsImpl = fs } ) {
275
288
  const paths = nativePaths({ scope, scopeRoot });
276
289
  const resources = [];
277
290
  const conflicts = [];
@@ -297,7 +310,7 @@ function createKiroAdapter({ declaredPaths = declaredHarnessPaths()[HARNESS] } =
297
310
  };
298
311
  }
299
312
 
300
- return { nativePaths, discover, render, plan, apply, remove, verify, buildServerEntry };
313
+ return { nativePaths, discover, render, plan, apply, remove, verify, planHookDoors, verifyHookDoors, buildServerEntry };
301
314
  }
302
315
 
303
316
  const singleton = createKiroAdapter();
@@ -305,6 +318,7 @@ const singleton = createKiroAdapter();
305
318
  module.exports = {
306
319
  nativePaths: singleton.nativePaths, discover: singleton.discover, render: singleton.render,
307
320
  plan: singleton.plan, apply: singleton.apply, remove: singleton.remove, verify: singleton.verify,
321
+ planHookDoors: singleton.planHookDoors, verifyHookDoors: singleton.verifyHookDoors,
308
322
  buildServerEntry: singleton.buildServerEntry,
309
323
  createKiroAdapter,
310
324
  };
@@ -14,7 +14,7 @@
14
14
  // the same reason a file DoFlow cannot read is described by a fixed reason, never parser output.
15
15
  const fs = require('node:fs');
16
16
  const path = require('node:path');
17
- const { fingerprint } = require('./copy-tree.ts');
17
+ const { fingerprint } = require('./copy-tree.js');
18
18
 
19
19
  const REASONS = Object.freeze({
20
20
  json: 'invalid JSON',
@@ -22,29 +22,29 @@ const REASONS = Object.freeze({
22
22
  container: 'server container is not an object',
23
23
  });
24
24
 
25
- function isObject(value: any): boolean { return Boolean(value) && typeof value === 'object' && !Array.isArray(value); }
25
+ function isObject(value ) { return Boolean(value) && typeof value === 'object' && !Array.isArray(value); }
26
26
 
27
- function entryFingerprint(value: any): string { return fingerprint(value); }
27
+ function entryFingerprint(value ) { return fingerprint(value); }
28
28
 
29
- function fingerprintOrNull(value: any): string | null { return value === undefined ? null : entryFingerprint(value); }
29
+ function fingerprintOrNull(value ) { return value === undefined ? null : entryFingerprint(value); }
30
30
 
31
31
  /** Deep equality that ignores key order, so a reformatted entry still equals DoFlow's rendering. */
32
- function sameValue(a: any, b: any): boolean { return a !== undefined && b !== undefined && entryFingerprint(a) === entryFingerprint(b); }
32
+ function sameValue(a , b ) { return a !== undefined && b !== undefined && entryFingerprint(a) === entryFingerprint(b); }
33
33
 
34
- function shortId(id: any): string { const text = String(id); return text.length > 40 ? `${text.slice(0, 37)}...` : text; }
34
+ function shortId(id ) { const text = String(id); return text.length > 40 ? `${text.slice(0, 37)}...` : text; }
35
35
 
36
- function shortPath(file: string): string { return file.length > 80 ? `...${file.slice(-77)}` : file; }
36
+ function shortPath(file ) { return file.length > 80 ? `...${file.slice(-77)}` : file; }
37
37
 
38
38
  const NOTICES = Object.freeze({
39
- collision: (id: any, file: string) => `MCP: kept your own entry '${shortId(id)}' in ${path.basename(file)} and did not register DoFlow's; rename or remove yours to let DoFlow manage it.`,
40
- released: (id: any, file: string) => `MCP: entry '${shortId(id)}' in ${path.basename(file)} was changed outside DoFlow; it is yours now and DoFlow no longer updates or removes it.`,
41
- untouched: (file: string) => `MCP: left ${shortPath(file)} untouched because it cannot be edited safely; DoFlow no longer manages the entries in it.`,
39
+ collision: (id , file ) => `MCP: kept your own entry '${shortId(id)}' in ${path.basename(file)} and did not register DoFlow's; rename or remove yours to let DoFlow manage it.`,
40
+ released: (id , file ) => `MCP: entry '${shortId(id)}' in ${path.basename(file)} was changed outside DoFlow; it is yours now and DoFlow no longer updates or removes it.`,
41
+ untouched: (file ) => `MCP: left ${shortPath(file)} untouched because it cannot be edited safely; DoFlow no longer manages the entries in it.`,
42
42
  });
43
43
 
44
- function refusal(label: string, file: string, reason: string): string { return `${label}: ${file}: ${reason}; DoFlow did not change the file`; }
44
+ function refusal(label , file , reason ) { return `${label}: ${file}: ${reason}; DoFlow did not change the file`; }
45
45
 
46
46
  /** Read one MCP file as a JSON object. `servers` is null when the file or its container is absent. */
47
- function readMcpFile(file: string, container: string, { fsImpl = fs }: { fsImpl?: any } = {}): any {
47
+ function readMcpFile(file , container , { fsImpl = fs } = {}) {
48
48
  if (!fsImpl.existsSync(file)) return { ok: true, doc: null, servers: null };
49
49
  let doc;
50
50
  try { doc = JSON.parse(fsImpl.readFileSync(file, 'utf8')); } catch { return { ok: false, reason: REASONS.json }; }
@@ -55,52 +55,52 @@ function readMcpFile(file: string, container: string, { fsImpl = fs }: { fsImpl?
55
55
  }
56
56
 
57
57
  /** The `files` map plan and verify take: the scope's MCP file and every file an own row names. */
58
- function readMcpFiles({ file, ownRows = [], container, fsImpl = fs }: any): any {
59
- const targets = new Set([file, ...ownRows.map((row: any) => row.target)]);
58
+ function readMcpFiles({ file, ownRows = [], container, fsImpl = fs } ) {
59
+ const targets = new Set([file, ...ownRows.map((row ) => row.target)]);
60
60
  return Object.fromEntries([...targets].map((target) => {
61
61
  const read = readMcpFile(target, container, { fsImpl });
62
62
  return [target, read.ok ? { ok: true, servers: read.servers } : read];
63
63
  }));
64
64
  }
65
65
 
66
- function renderedById(selected: any[], adoptable: any[]): Map<string, any> {
67
- const rendered = new Map<string, any>();
66
+ function renderedById(selected , adoptable ) {
67
+ const rendered = new Map ();
68
68
  for (const { id, entry } of [...selected, ...adoptable]) if (!rendered.has(id)) rendered.set(id, entry);
69
69
  return rendered;
70
70
  }
71
71
 
72
72
  /** Shared by plan, verify and ownedMcpIds, so the three read one entry the same way. */
73
- function ownershipView({ file, files, selected = [], adoptable = [], ownRows = [], foreignRows = [] }: any): any {
73
+ function ownershipView({ file, files, selected = [], adoptable = [], ownRows = [], foreignRows = [] } ) {
74
74
  const rendered = renderedById(selected, adoptable);
75
- const valueOf = (target: string, id: string) => (files[target]?.ok ? files[target].servers?.[id] : undefined);
76
- const matches = (row: any, value: any) => {
75
+ const valueOf = (target , id ) => (files[target]?.ok ? files[target].servers?.[id] : undefined);
76
+ const matches = (row , value ) => {
77
77
  if (value === undefined) return false;
78
78
  const recorded = row.fingerprint ?? null;
79
79
  return recorded !== null ? entryFingerprint(value) === recorded : sameValue(value, rendered.get(row.identity));
80
80
  };
81
- const rowsFor = (id: string) => ownRows.filter((row: any) => row.identity === id && row.target === file);
81
+ const rowsFor = (id ) => ownRows.filter((row ) => row.identity === id && row.target === file);
82
82
  // A legacy row is consumed alongside the current one, but never preferred over it.
83
- const rowFor = (id: string) => { const rows = rowsFor(id); return rows.find((row: any) => !row.legacy) ?? rows[0] ?? null; };
83
+ const rowFor = (id ) => { const rows = rowsFor(id); return rows.find((row ) => !row.legacy) ?? rows[0] ?? null; };
84
84
  const adoptionOpen = ownRows.length === 0;
85
- const adoptableIds = new Set(adoptable.map((server: any) => server.id));
86
- const selectedIds = new Set(selected.map((server: any) => server.id));
85
+ const adoptableIds = new Set(adoptable.map((server ) => server.id));
86
+ const selectedIds = new Set(selected.map((server ) => server.id));
87
87
  /** An equal entry no own row claims is DoFlow's when another harness's row claims it, or when it
88
88
  * may be adopted. */
89
- const claimable = (id: string, entry: any) => {
89
+ const claimable = (id , entry ) => {
90
90
  const value = valueOf(file, id);
91
- return sameValue(value, entry) && (foreignRows.some((row: any) => row.identity === id && row.target === file)
91
+ return sameValue(value, entry) && (foreignRows.some((row ) => row.identity === id && row.target === file)
92
92
  || (adoptionOpen && adoptableIds.has(id)));
93
93
  };
94
94
  /** Adoptable entries DoFlow takes out because nothing selects them any more. */
95
95
  const adoptedRemovals = () => (adoptionOpen && files[file]?.ok
96
- ? adoptable.filter(({ id, entry }: any) => !selectedIds.has(id) && sameValue(valueOf(file, id), entry)) : []);
96
+ ? adoptable.filter(({ id, entry } ) => !selectedIds.has(id) && sameValue(valueOf(file, id), entry)) : []);
97
97
  return { rendered, valueOf, matches, rowsFor, rowFor, claimable, adoptedRemovals };
98
98
  }
99
99
 
100
- function orderOf(selected: any[], adoptable: any[]): (a: any, b: any) => number {
100
+ function orderOf(selected , adoptable ) {
101
101
  const ids = [...renderedById(selected, adoptable).keys()];
102
- const rank = (id: string) => { const index = ids.indexOf(id); return index < 0 ? ids.length : index; };
103
- return (a: any, b: any) => (a.target < b.target ? -1 : a.target > b.target ? 1
102
+ const rank = (id ) => { const index = ids.indexOf(id); return index < 0 ? ids.length : index; };
103
+ return (a , b ) => (a.target < b.target ? -1 : a.target > b.target ? 1
104
104
  : rank(a.identity) - rank(b.identity) || (a.identity < b.identity ? -1 : a.identity > b.identity ? 1 : 0));
105
105
  }
106
106
 
@@ -110,17 +110,17 @@ function orderOf(selected: any[], adoptable: any[]): (a: any, b: any) => number
110
110
  * `foreignRows` other harnesses' rows; `identityFor` composes the adapter's ownership identity.
111
111
  */
112
112
  function planMcpEntries({ file, files, selected = [], adoptable = [], ownRows = [], foreignRows = [], removing = false,
113
- identityFor, assetId, renderer, label }: any): any {
113
+ identityFor, assetId, renderer, label } ) {
114
114
  const view = ownershipView({ file, files, selected, adoptable, ownRows, foreignRows });
115
- const changes: any[] = [];
116
- const conflicts: string[] = [];
117
- const notices: string[] = [];
118
- const base = (fields: any) => ({ assetId, kind: 'mcp-server', sourceVersion: 'registry-v1', projection: { renderer }, ...fields });
119
- const write = (operation: string, id: string, entry: any) => base({
115
+ const changes = [];
116
+ const conflicts = [];
117
+ const notices = [];
118
+ const base = (fields ) => ({ assetId, kind: 'mcp-server', sourceVersion: 'registry-v1', projection: { renderer }, ...fields });
119
+ const write = (operation , id , entry ) => base({
120
120
  target: file, operation, identity: id, ownershipIdentity: identityFor(id),
121
121
  before: fingerprintOrNull(view.valueOf(file, id)), entry, fingerprint: entryFingerprint(entry), afterFingerprint: entryFingerprint(entry),
122
122
  });
123
- const drop = (row: any, release: boolean) => base({
123
+ const drop = (row , release ) => base({
124
124
  target: row.target, operation: 'remove', identity: row.identity, ownershipIdentity: row.ownershipIdentity,
125
125
  before: fingerprintOrNull(view.valueOf(row.target, row.identity)), fingerprint: row.fingerprint ?? null,
126
126
  ...(release ? { release: true } : {}),
@@ -132,7 +132,7 @@ function planMcpEntries({ file, files, selected = [], adoptable = [], ownRows =
132
132
  else conflicts.push(refusal(label, target, files[target].reason));
133
133
  }
134
134
 
135
- const consumed = new Set<any>();
135
+ const consumed = new Set ();
136
136
  if (!removing && files[file]?.ok) {
137
137
  for (const { id, entry } of selected) {
138
138
  const rows = view.rowsFor(id);
@@ -184,7 +184,7 @@ function planMcpEntries({ file, files, selected = [], adoptable = [], ownRows =
184
184
  /** Apply one file's planned changes to the server map read now. Each change goes ahead only while
185
185
  * its entry still holds what plan saw, or already holds the change's end state, which makes two
186
186
  * harnesses' changes to one shared entry in one run idempotent. Release changes touch nothing. */
187
- function applyMcpEntries(servers: any, changes: any[]): any {
187
+ function applyMcpEntries(servers , changes ) {
188
188
  const next = { ...(servers || {}) };
189
189
  for (const change of changes) {
190
190
  if (change.release) continue;
@@ -205,13 +205,13 @@ function applyMcpEntries(servers: any, changes: any[]): any {
205
205
  * whatever it holds now.
206
206
  */
207
207
  function verifyMcpEntries({ file, files, snapshot, selected = [], adoptable = [], ownRows = [], foreignRows = [], removing = false,
208
- identityFor, assetId, renderer, label, harness, sourceVersion = 'unknown' }: any): any {
208
+ identityFor, assetId, renderer, label, harness, sourceVersion = 'unknown' } ) {
209
209
  const now = ownershipView({ file, files, selected, adoptable, ownRows, foreignRows });
210
210
  const then = ownershipView({ file, files: snapshot, selected, adoptable, ownRows, foreignRows });
211
- const statuses: any[] = [];
212
- const resources: any[] = [];
213
- const conflicts: string[] = [];
214
- const report = (id: string, target: string, ownershipIdentity: any, status: string) => statuses.push({
211
+ const statuses = [];
212
+ const resources = [];
213
+ const conflicts = [];
214
+ const report = (id , target , ownershipIdentity , status ) => statuses.push({
215
215
  harness, assetId, capability: 'mcp', identity: id, target, ownershipIdentity, status,
216
216
  });
217
217
 
@@ -219,7 +219,7 @@ function verifyMcpEntries({ file, files, snapshot, selected = [], adoptable = []
219
219
  for (const target of Object.keys(files).filter((item) => !files[item].ok).sort()) conflicts.push(refusal(label, target, files[target].reason));
220
220
  }
221
221
 
222
- const consumed = new Set<any>();
222
+ const consumed = new Set ();
223
223
  if (!removing) {
224
224
  for (const { id, entry } of selected) {
225
225
  for (const row of now.rowsFor(id)) consumed.add(row);
@@ -239,7 +239,7 @@ function verifyMcpEntries({ file, files, snapshot, selected = [], adoptable = []
239
239
  }
240
240
  }
241
241
 
242
- const leftover = (value: any, matching: boolean, target: string) => {
242
+ const leftover = (value , matching , target ) => {
243
243
  if (!files[target]?.ok) return 'not-managed';
244
244
  if (value === undefined) return 'absent';
245
245
  return matching ? 'retained' : 'not-managed';
@@ -261,29 +261,29 @@ function verifyMcpEntries({ file, files, snapshot, selected = [], adoptable = []
261
261
  /** The ids DoFlow owns in this harness's MCP file now, in rendering order: those an own row matches,
262
262
  * and, while the harness holds no row, adoptable entries equal to DoFlow's rendering. A file that
263
263
  * cannot be read contributes none. */
264
- function ownedMcpIds({ file, files, selected = [], adoptable = [], ownRows = [] }: any): string[] {
264
+ function ownedMcpIds({ file, files, selected = [], adoptable = [], ownRows = [] } ) {
265
265
  const view = ownershipView({ file, files, selected, adoptable, ownRows });
266
- const owned = new Set<string>(ownRows.filter((row: any) => view.matches(row, view.valueOf(row.target, row.identity))).map((row: any) => row.identity));
266
+ const owned = new Set (ownRows.filter((row ) => view.matches(row, view.valueOf(row.target, row.identity))).map((row ) => row.identity));
267
267
  if (!ownRows.length) {
268
268
  for (const { id, entry } of adoptable) if (sameValue(view.valueOf(file, id), entry)) owned.add(id);
269
269
  }
270
- const order: string[] = [...view.rendered.keys()];
270
+ const order = [...view.rendered.keys()];
271
271
  return [...order.filter((id) => owned.has(id)), ...[...owned].filter((id) => !order.includes(id)).sort()];
272
272
  }
273
273
 
274
- function isSymlink(file: string, fsImpl: any): boolean {
274
+ function isSymlink(file , fsImpl ) {
275
275
  try { return fsImpl.lstatSync(file).isSymbolicLink(); } catch { return false; }
276
276
  }
277
277
 
278
278
  /** A symlinked MCP file is written through to the file it points at, so the link stays a link. */
279
- function writePath(file: string, fsImpl: any): string {
279
+ function writePath(file , fsImpl ) {
280
280
  if (!isSymlink(file, fsImpl)) return file;
281
281
  try { return fsImpl.realpathSync(file); } catch { return path.resolve(path.dirname(file), fsImpl.readlinkSync(file)); }
282
282
  }
283
283
 
284
284
  /** Write a JSON document atomically, keeping the existing file's permission bits: an MCP file kept
285
285
  * private for its tokens stays so. Creates missing directories; never deletes a file. */
286
- function writeMcpJson(file: string, doc: any, { fsImpl = fs }: { fsImpl?: any } = {}): string {
286
+ function writeMcpJson(file , doc , { fsImpl = fs } = {}) {
287
287
  const dest = writePath(file, fsImpl);
288
288
  fsImpl.mkdirSync(path.dirname(dest), { recursive: true });
289
289
  const mode = fsImpl.existsSync(dest) ? fsImpl.statSync(dest).mode & 0o7777 : null;
@@ -303,14 +303,14 @@ function writeMcpJson(file: string, doc: any, { fsImpl = fs }: { fsImpl?: any }
303
303
  * every file's next document computed before the first write. An emptied container is kept as `{}`
304
304
  * or its member deleted, as the harness expects. Returns the number of files written.
305
305
  */
306
- function writeMcpEntries(changes: any[], { container, keepEmptyContainer = false, fsImpl = fs }: { container?: any, keepEmptyContainer?: boolean, fsImpl?: any } = {}): number {
307
- const byFile = new Map<string, any[]>();
306
+ function writeMcpEntries(changes , { container, keepEmptyContainer = false, fsImpl = fs } = {}) {
307
+ const byFile = new Map ();
308
308
  for (const change of changes) {
309
309
  if (change.kind !== 'mcp-server' || change.release) continue;
310
310
  if (!byFile.has(change.target)) byFile.set(change.target, []);
311
- byFile.get(change.target)!.push(change);
311
+ byFile.get(change.target) .push(change);
312
312
  }
313
- const writes: [string, any][] = [];
313
+ const writes = [];
314
314
  for (const [file, group] of byFile) {
315
315
  const read = readMcpFile(file, container, { fsImpl });
316
316
  if (!read.ok) throw new Error(`MCP: ${file} changed after planning; nothing was written, re-run the command`);