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

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 (167) hide show
  1. package/bin/doflow.js +2 -2
  2. package/core/.claude-plugin/plugin.json +1 -1
  3. package/core/.codex-plugin/plugin.json +1 -1
  4. package/core/.plugin/plugin.json +1 -1
  5. package/core/registry/storage.json +87 -83
  6. package/core/registry/verification.json +1 -1
  7. package/core/shared/scripts/doflow/bash/render-puml.sh +2 -2
  8. package/core/shared/skills/do-execute-plan/references/scaffold.md +2 -2
  9. package/package.json +4 -2
  10. package/src/adapters/antigravity/{index.ts → index.js} +60 -60
  11. package/src/adapters/claude/{index.ts → index.js} +48 -48
  12. package/src/adapters/claude/{mcp.ts → mcp.js} +16 -16
  13. package/src/adapters/codex/{agents.ts → agents.js} +24 -24
  14. package/src/adapters/codex/{config.ts → config.js} +49 -49
  15. package/src/adapters/codex/{hooks.ts → hooks.js} +23 -23
  16. package/src/adapters/codex/{index.ts → index.js} +98 -98
  17. package/src/adapters/codex/{mcp.ts → mcp.js} +38 -38
  18. package/src/adapters/codex/{native-projection.ts → native-projection.js} +5 -5
  19. package/src/adapters/copilot/{index.ts → index.js} +42 -42
  20. package/src/adapters/{copy-tree.ts → copy-tree.js} +70 -70
  21. package/src/adapters/gemini/{hooks.ts → hooks.js} +24 -24
  22. package/src/adapters/gemini/{index.ts → index.js} +41 -41
  23. package/src/adapters/{hook-commands.ts → hook-commands.js} +8 -8
  24. package/src/adapters/{index.ts → index.js} +13 -13
  25. package/src/adapters/kiro/{index.ts → index.js} +34 -34
  26. package/src/adapters/{mcp-entries.ts → mcp-entries.js} +55 -55
  27. package/src/adapters/opencode/{index.ts → index.js} +52 -52
  28. package/src/adapters/pi/{index.ts → index.js} +30 -30
  29. package/src/adapters/pi/{json-members.ts → json-members.js} +23 -23
  30. package/src/adapters/pi/{mcp.ts → mcp.js} +70 -70
  31. package/src/adapters/{resolve-source.ts → resolve-source.js} +1 -1
  32. package/src/cli/commands/{install.ts → install.js} +24 -24
  33. package/src/cli/commands/{list-backups.ts → list-backups.js} +4 -4
  34. package/src/cli/commands/{reconcile.ts → reconcile.js} +19 -19
  35. package/src/cli/commands/{remove.ts → remove.js} +10 -10
  36. package/src/cli/commands/{rollback.ts → rollback.js} +17 -17
  37. package/src/cli/commands/{self-update.ts → self-update.js} +3 -3
  38. package/src/cli/commands/{status.ts → status.js} +24 -24
  39. package/src/cli/commands/{tools.ts → tools.js} +16 -16
  40. package/src/cli/commands/{update.ts → update.js} +19 -19
  41. package/src/cli/{index.ts → index.js} +26 -26
  42. package/src/cli/{runtime-commands.ts → runtime-commands.js} +48 -48
  43. package/src/cli/{shared.ts → shared.js} +40 -40
  44. package/src/helper/{bash-helper.ts → bash-helper.js} +7 -7
  45. package/src/helper/{git.ts → git.js} +2 -2
  46. package/src/helper/{guardrails.ts → guardrails.js} +10 -10
  47. package/src/helper/{harness-paths.ts → harness-paths.js} +24 -24
  48. package/src/helper/{marker-merge.ts → marker-merge.js} +4 -4
  49. package/src/helper/{prompt.ts → prompt.js} +5 -5
  50. package/src/helper/{repo-root.ts → repo-root.js} +2 -2
  51. package/src/helper/{settings-merge.ts → settings-merge.js} +27 -27
  52. package/src/helper/{settings-scope.ts → settings-scope.js} +3 -3
  53. package/src/helper/{toml.ts → toml.js} +48 -48
  54. package/src/install/{backup-set.ts → backup-set.js} +16 -16
  55. package/src/install/{backup.ts → backup.js} +65 -65
  56. package/src/install/{context.ts → context.js} +15 -15
  57. package/src/install/{manifest.ts → manifest.js} +27 -27
  58. package/src/install/{mcp.ts → mcp.js} +30 -30
  59. package/src/install/{paths.ts → paths.js} +15 -15
  60. package/src/install/{targets.ts → targets.js} +10 -10
  61. package/src/install/{tool-lifecycle.ts → tool-lifecycle.js} +20 -20
  62. package/src/lifecycle/{bash-availability.ts → bash-availability.js} +1 -1
  63. package/src/lifecycle/{index.ts → index.js} +140 -140
  64. package/src/lifecycle/{mcp-index.ts → mcp-index.js} +1 -1
  65. package/src/lifecycle/{policies.ts → policies.js} +11 -11
  66. package/src/lifecycle/{view.ts → view.js} +28 -28
  67. package/src/registry/{index.ts → index.js} +60 -60
  68. package/src/release/build-package.js +91 -0
  69. package/src/release/changelog-section.js +70 -0
  70. package/src/release/{sync-plugin-versions.ts → sync-plugin-versions.js} +4 -4
  71. package/src/runtime/advice/{cli.ts → cli.js} +31 -31
  72. package/src/runtime/advice/{client.ts → client.js} +44 -44
  73. package/src/runtime/advice/{config.ts → config.js} +16 -16
  74. package/src/runtime/advice/{doctor.ts → doctor.js} +32 -32
  75. package/src/runtime/advice/{endpoint-guard.ts → endpoint-guard.js} +20 -20
  76. package/src/runtime/advice/{kinds.ts → kinds.js} +16 -16
  77. package/src/runtime/advice/providers/{ollama-systemone.ts → ollama-systemone.js} +21 -21
  78. package/src/runtime/advice/{record.ts → record.js} +19 -19
  79. package/src/runtime/advice/{registry.ts → registry.js} +16 -16
  80. package/src/runtime/advice/{strength.ts → strength.js} +7 -7
  81. package/src/runtime/{brief-tools.ts → brief-tools.js} +50 -50
  82. package/src/runtime/{c4-project.ts → c4-project.js} +27 -27
  83. package/src/runtime/{capability-router.ts → capability-router.js} +39 -39
  84. package/src/runtime/{checkouts.ts → checkouts.js} +21 -21
  85. package/src/runtime/{claims.ts → claims.js} +30 -30
  86. package/src/runtime/{cli-result.ts → cli-result.js} +4 -4
  87. package/src/runtime/{cli.ts → cli.js} +37 -37
  88. package/src/runtime/{command-detect.ts → command-detect.js} +47 -47
  89. package/src/runtime/{context-pack.ts → context-pack.js} +20 -20
  90. package/src/runtime/{decision-register.ts → decision-register.js} +67 -67
  91. package/src/runtime/{evidence-ledger.ts → evidence-ledger.js} +25 -25
  92. package/src/runtime/failure/{capture.ts → capture.js} +29 -29
  93. package/src/runtime/failure/{classifier.ts → classifier.js} +4 -4
  94. package/src/runtime/failure/{cli.ts → cli.js} +30 -30
  95. package/src/runtime/failure/{home.ts → home.js} +6 -6
  96. package/src/runtime/failure/{store.ts → store.js} +32 -32
  97. package/src/runtime/{feature-resolve.ts → feature-resolve.js} +6 -6
  98. package/src/runtime/{freshness.ts → freshness.js} +15 -15
  99. package/src/runtime/{health.ts → health.js} +72 -72
  100. package/src/runtime/{history-compactor.ts → history-compactor.js} +25 -25
  101. package/src/runtime/{implementation-gate.ts → implementation-gate.js} +30 -30
  102. package/src/runtime/inventory/{coverage.ts → coverage.js} +10 -10
  103. package/src/runtime/inventory/{identity.ts → identity.js} +2 -2
  104. package/src/runtime/inventory/{index.ts → index.js} +36 -36
  105. package/src/runtime/inventory/{precedence.ts → precedence.js} +2 -2
  106. package/src/runtime/inventory/{read-scopes.ts → read-scopes.js} +9 -9
  107. package/src/runtime/inventory/{siblings.ts → siblings.js} +15 -15
  108. package/src/runtime/knowledge/{chunker.ts → chunker.js} +12 -12
  109. package/src/runtime/knowledge/{hybrid.ts → hybrid.js} +17 -17
  110. package/src/runtime/knowledge/{index-store.ts → index-store.js} +23 -23
  111. package/src/runtime/knowledge/{retrieval.ts → retrieval.js} +27 -27
  112. package/src/runtime/{leak-scan.ts → leak-scan.js} +12 -12
  113. package/src/runtime/lifecycle/{cli.ts → cli.js} +32 -32
  114. package/src/runtime/lifecycle/{event-store.ts → event-store.js} +48 -48
  115. package/src/runtime/lifecycle/{fold.ts → fold.js} +41 -41
  116. package/src/runtime/lifecycle/{followup.ts → followup.js} +54 -54
  117. package/src/runtime/lifecycle/{goal.ts → goal.js} +50 -50
  118. package/src/runtime/lifecycle/{intent-writer.ts → intent-writer.js} +5 -5
  119. package/src/runtime/lifecycle/{overview.ts → overview.js} +47 -47
  120. package/src/runtime/lifecycle/{release.ts → release.js} +29 -29
  121. package/src/runtime/lifecycle/{report-store.ts → report-store.js} +25 -25
  122. package/src/runtime/lifecycle/{retention.ts → retention.js} +37 -37
  123. package/src/runtime/lifecycle/{root.ts → root.js} +9 -9
  124. package/src/runtime/lifecycle/{status.ts → status.js} +64 -64
  125. package/src/runtime/lifecycle/{store-upkeep.ts → store-upkeep.js} +46 -46
  126. package/src/runtime/{locator-resolve.ts → locator-resolve.js} +7 -7
  127. package/src/runtime/{mask.ts → mask.js} +22 -22
  128. package/src/runtime/{mcp-consultations.ts → mcp-consultations.js} +54 -54
  129. package/src/runtime/{mcp-reach.ts → mcp-reach.js} +24 -24
  130. package/src/runtime/{model-router.ts → model-router.js} +20 -20
  131. package/src/runtime/{outcome.ts → outcome.js} +27 -27
  132. package/src/runtime/{reach.ts → reach.js} +21 -21
  133. package/src/runtime/{readiness-record.ts → readiness-record.js} +12 -12
  134. package/src/runtime/{readiness.ts → readiness.js} +35 -35
  135. package/src/runtime/{recovery.ts → recovery.js} +15 -15
  136. package/src/runtime/{research-request.ts → research-request.js} +34 -34
  137. package/src/runtime/{retrieval-plan.ts → retrieval-plan.js} +40 -40
  138. package/src/runtime/scaffold/{artifacts.ts → artifacts.js} +19 -19
  139. package/src/runtime/scaffold/{fingerprint.ts → fingerprint.js} +3 -3
  140. package/src/runtime/scaffold/{generate.ts → generate.js} +109 -109
  141. package/src/runtime/scaffold/{languages.ts → languages.js} +34 -34
  142. package/src/runtime/storage/{cli.ts → cli.js} +9 -9
  143. package/src/runtime/storage/{engine.ts → engine.js} +37 -37
  144. package/src/runtime/storage/{registry.ts → registry.js} +19 -19
  145. package/src/runtime/storage/{roots.ts → roots.js} +8 -8
  146. package/src/runtime/storage/{rules.ts → rules.js} +26 -26
  147. package/src/runtime/storage/{upkeep.ts → upkeep.js} +35 -35
  148. package/src/runtime/storage/{work-status.ts → work-status.js} +39 -39
  149. package/src/runtime/{task-attempts.ts → task-attempts.js} +61 -61
  150. package/src/runtime/{task-classifier.ts → task-classifier.js} +34 -34
  151. package/src/runtime/{task-scope.ts → task-scope.js} +13 -13
  152. package/src/runtime/{task-state.ts → task-state.js} +15 -15
  153. package/src/runtime/trace/{indicators.ts → indicators.js} +22 -22
  154. package/src/runtime/trace/{ledger.ts → ledger.js} +42 -42
  155. package/src/runtime/trace/{render.ts → render.js} +7 -7
  156. package/src/runtime/trace/{views.ts → views.js} +40 -40
  157. package/src/runtime/verification/{contract-runner.ts → contract-runner.js} +23 -23
  158. package/src/runtime/verification/{engine.ts → engine.js} +143 -143
  159. package/src/runtime/verification/{registry.ts → registry.js} +6 -6
  160. package/src/runtime/verification/{scope-bound.ts → scope-bound.js} +50 -50
  161. package/src/runtime/verification/{test-selection.ts → test-selection.js} +28 -28
  162. package/src/runtime/{workflow-engine.ts → workflow-engine.js} +50 -50
  163. package/src/runtime/{workflow-orchestrator.ts → workflow-orchestrator.js} +91 -91
  164. package/src/runtime/{worktree.ts → worktree.js} +31 -31
  165. package/src/state/{index.ts → index.js} +26 -26
  166. package/src/state/{lockfile.ts → lockfile.js} +20 -20
  167. package/src/state/{run-lock.ts → run-lock.js} +44 -44
@@ -15,7 +15,7 @@
15
15
  //
16
16
  // Only SELECTED servers are imported, so the cost is proportional to what is installed: a
17
17
  // one-server install pays for one doc, and an install with none produces no file at all.
18
- function renderMcpIndex(selectedServers: any[] | null | undefined): string | null {
18
+ function renderMcpIndex(selectedServers ) {
19
19
  if (!selectedServers || selectedServers.length === 0) return null;
20
20
  const lines = ['# MCP short flags — this install'];
21
21
  for (const server of selectedServers) {
@@ -5,30 +5,30 @@
5
5
  // Registry lifecycle policies are logical intent. This projection reports what a
6
6
  // harness may render and any human prerequisite; native event serialization stays
7
7
  // in the owning harness adapter.
8
- const { harnessFor } = require('../registry/index.ts');
8
+ const { harnessFor } = require('../registry/index.js');
9
9
 
10
10
  const POLICY_STATUSES = new Set(['supported', 'different', 'unavailable', 'prerequisite']);
11
11
 
12
- function unique(values: any[]): any[] { return [...new Set(values.filter(Boolean))]; }
12
+ function unique(values ) { return [...new Set(values.filter(Boolean))]; }
13
13
 
14
- function policyFor(registry: any, id: string): any {
15
- const policy = registry?.lifecycle?.find((item: any) => item.id === id);
14
+ function policyFor(registry , id ) {
15
+ const policy = registry?.lifecycle?.find((item ) => item.id === id);
16
16
  if (!policy) throw new Error(`Unknown lifecycle policy '${id}'`);
17
17
  return policy;
18
18
  }
19
19
 
20
- function renderPolicy(policy: any, harness: any): any {
20
+ function renderPolicy(policy , harness ) {
21
21
  const mapping = policy?.mappings?.[harness.id];
22
22
  if (!mapping) throw new Error(`Lifecycle policy '${policy.id}' has no mapping for '${harness.id}'`);
23
- const requiredCapabilities = (policy.requires || []).map((capability: string): [string, any] => {
23
+ const requiredCapabilities = (policy.requires || []).map((capability ) => {
24
24
  const declaration = harness.capabilities?.[capability];
25
25
  if (!declaration) throw new Error(`Lifecycle policy '${policy.id}' requires unknown '${harness.id}' capability '${capability}'`);
26
26
  return [capability, declaration];
27
27
  });
28
- const unavailableCapability = requiredCapabilities.find(([, declaration]: [string, any]) => declaration.status === 'unavailable');
28
+ const unavailableCapability = requiredCapabilities.find(([, declaration] ) => declaration.status === 'unavailable');
29
29
  const prerequisites = unique([
30
30
  ...(mapping.prerequisites || []),
31
- ...requiredCapabilities.flatMap(([, declaration]: [string, any]) => declaration.prerequisites || []),
31
+ ...requiredCapabilities.flatMap(([, declaration] ) => declaration.prerequisites || []),
32
32
  ]);
33
33
  const capabilityStatus = unavailableCapability ? 'unavailable' : mapping.status;
34
34
  const unavailable = capabilityStatus === 'unavailable';
@@ -60,14 +60,14 @@ function renderPolicy(policy: any, harness: any): any {
60
60
  }
61
61
 
62
62
  /** Render policy status data for one harness without calling an adapter or writing state. */
63
- function renderPolicies(registry: any, { harness: harnessId, policyIds }: { harness?: string, policyIds?: string[] | null } = {}): any {
63
+ function renderPolicies(registry , { harness: harnessId, policyIds } = {}) {
64
64
  if (!harnessId) throw new Error('A harness id is required to render lifecycle policies');
65
65
  const harness = harnessFor(registry, harnessId);
66
66
  const policies = policyIds === undefined || policyIds === null
67
67
  ? registry.lifecycle
68
- : policyIds.map((id: string) => policyFor(registry, id));
68
+ : policyIds.map((id ) => policyFor(registry, id));
69
69
  if (!Array.isArray(policies)) throw new Error('Lifecycle policies must be an array');
70
- return Object.freeze(policies.map((policy: any) => renderPolicy(policy, harness)));
70
+ return Object.freeze(policies.map((policy ) => renderPolicy(policy, harness)));
71
71
  }
72
72
 
73
73
  module.exports = { POLICY_STATUSES, policyFor, renderPolicy, renderPolicies };
@@ -8,21 +8,21 @@
8
8
  const fs = require('node:fs');
9
9
  const os = require('node:os');
10
10
  const path = require('node:path');
11
- const { parseToml } = require('../helper/toml.ts');
12
- const { createAdapterRegistry } = require('../adapters/index.ts');
13
- const { declaredHarnessPaths } = require('../helper/harness-paths.ts');
14
- const claudeAdapter = require('../adapters/claude/index.ts');
15
- const codexAdapter = require('../adapters/codex/index.ts');
16
- const { createGeminiAdapter } = require('../adapters/gemini/index.ts');
17
- const { createOpenCodeAdapter } = require('../adapters/opencode/index.ts');
18
- const { createPiAdapter } = require('../adapters/pi/index.ts');
19
- const { createCopilotAdapter } = require('../adapters/copilot/index.ts');
20
- const { createKiroAdapter } = require('../adapters/kiro/index.ts');
21
- const { createAntigravityAdapter } = require('../adapters/antigravity/index.ts');
22
- const { planLifecycle } = require('./index.ts');
23
- const { stateRoot, readLedger, defaultLedger } = require('../state/index.ts');
24
- const { selectAssets } = require('../registry/index.ts');
25
- const { defaultLock, readLock, writeLock, diffLocks, removeLock, pinnedSelections } = require('../state/lockfile.ts');
11
+ const { parseToml } = require('../helper/toml.js');
12
+ const { createAdapterRegistry } = require('../adapters/index.js');
13
+ const { declaredHarnessPaths } = require('../helper/harness-paths.js');
14
+ const claudeAdapter = require('../adapters/claude/index.js');
15
+ const codexAdapter = require('../adapters/codex/index.js');
16
+ const { createGeminiAdapter } = require('../adapters/gemini/index.js');
17
+ const { createOpenCodeAdapter } = require('../adapters/opencode/index.js');
18
+ const { createPiAdapter } = require('../adapters/pi/index.js');
19
+ const { createCopilotAdapter } = require('../adapters/copilot/index.js');
20
+ const { createKiroAdapter } = require('../adapters/kiro/index.js');
21
+ const { createAntigravityAdapter } = require('../adapters/antigravity/index.js');
22
+ const { planLifecycle } = require('./index.js');
23
+ const { stateRoot, readLedger, defaultLedger } = require('../state/index.js');
24
+ const { selectAssets } = require('../registry/index.js');
25
+ const { defaultLock, readLock, writeLock, diffLocks, removeLock, pinnedSelections } = require('../state/lockfile.js');
26
26
  // Tolerant because the projected runtime under `.doflow/runtime/` ships bin/, src/ and
27
27
  // core/registry/ but no package.json — see the `runtime.*` assets in core/registry/assets.json.
28
28
  // A hard require here would make every Node-backed verb fail in an install, which is the exact
@@ -32,9 +32,9 @@ function loadPkg() {
32
32
  }
33
33
  const pkg = loadPkg();
34
34
 
35
- function codexScope(scope: any) { return scope.global ? 'global' : 'project'; }
35
+ function codexScope(scope ) { return scope.global ? 'global' : 'project'; }
36
36
 
37
- function codexConfigResources(repoRoot: any, fsImpl: any) {
37
+ function codexConfigResources(repoRoot , fsImpl ) {
38
38
  const configSrc = path.join(repoRoot, 'core', 'harnesses', 'codex', 'config', 'config.toml');
39
39
  const parsed = parseToml(fsImpl.readFileSync(configSrc, 'utf8'));
40
40
  return [...parsed.entries.entries()].map(([identity, entry]) => ({
@@ -48,7 +48,7 @@ function codexConfigResources(repoRoot: any, fsImpl: any) {
48
48
  * mutation path until every native adapter has CLI-level parity.
49
49
  * `registry` is loaded once per command (by the caller) and threaded through here rather than
50
50
  * reloaded — the same registry also resolves every harness's MCP selection for that command. */
51
- function registryLifecycleView({ registry, scope, targets, mcpSelections = {}, mcpAdoptable = {}, retainedMcpIds = [], operation, repoRoot, force = false, adopt = false, permissions = false, statusline = false, fsImpl = fs }: any) {
51
+ function registryLifecycleView({ registry, scope, targets, mcpSelections = {}, mcpAdoptable = {}, retainedMcpIds = [], operation, repoRoot, force = false, adopt = false, permissions = false, statusline = false, fsImpl = fs } ) {
52
52
  const lifecycleScope = codexScope(scope);
53
53
  const scopeRoot = scope.global ? os.homedir() : path.resolve(scope.projectRoot);
54
54
  const neutralStateRoot = stateRoot({ scope: lifecycleScope, projectRoot: scopeRoot, homeDir: scopeRoot });
@@ -79,11 +79,11 @@ function registryLifecycleView({ registry, scope, targets, mcpSelections = {}, m
79
79
  return { registry, stateRoot: neutralStateRoot, ledger, plan, adapters };
80
80
  }
81
81
 
82
- function printRegistryLifecycle(view: any, prefix = '[PLAN]') {
82
+ function printRegistryLifecycle(view , prefix = '[PLAN]') {
83
83
  console.log(`${prefix} Registry lifecycle: ${view.plan.changes.length} native change(s), ${view.plan.conflicts.length} conflict(s), ${view.plan.prerequisites.length} prerequisite(s)`);
84
84
  for (const target of view.plan.targets) {
85
85
  console.log(`${prefix} ${target.harness}: ${target.changes.length} change(s)${target.conflicts.length ? `; conflicts: ${target.conflicts.join('; ')}` : ''}`);
86
- const hookChange = target.changes.find((change: any) => change.nativeComponent === 'hooks');
86
+ const hookChange = target.changes.find((change ) => change.nativeComponent === 'hooks');
87
87
  if (hookChange?.nativePlan?.trust?.required) {
88
88
  console.log(`${prefix} ${target.harness} hooks trust: ${hookChange.nativePlan.trust.status} (review required in ${target.harness})`);
89
89
  }
@@ -94,7 +94,7 @@ function printRegistryLifecycle(view: any, prefix = '[PLAN]') {
94
94
 
95
95
  /** Prints the plan's notices after a real run. Independent of whether anything changed: a no-op
96
96
  * reinstall of a harness that has a gap must still say so. */
97
- function printPlanNotices(view: any) {
97
+ function printPlanNotices(view ) {
98
98
  for (const { harness, notice } of view.plan.notices) console.log(`[INFO] ${harness}: ${notice}`);
99
99
  }
100
100
 
@@ -103,7 +103,7 @@ function printPlanNotices(view: any) {
103
103
  * src/targets.js — so a future non-lifecycle target doesn't silently gain lifecycle behavior. */
104
104
  const LIFECYCLE_HARNESSES = ['claude', 'codex', 'gemini', 'opencode', 'pi', 'copilot', 'kiro', 'antigravity'];
105
105
 
106
- function assertSafeRegistryPlan(view: any) {
106
+ function assertSafeRegistryPlan(view ) {
107
107
  if (view.plan.safe) return;
108
108
  for (const conflict of view.plan.conflicts) console.error(`[ERROR] ${conflict.harness} lifecycle refused: ${conflict.reason}`);
109
109
  for (const prerequisite of view.plan.prerequisites) console.error(`[ERROR] ${prerequisite.harness} lifecycle prerequisite: ${prerequisite.prerequisite}`);
@@ -119,9 +119,9 @@ function assertSafeRegistryPlan(view: any) {
119
119
  * selections carry over for pinned harnesses and are replaced by the rows this run resolved, `[]`
120
120
  * included. Assets are recomputed from the registry for every pinned harness. `null` when no harness
121
121
  * stays pinned. */
122
- function lockDocument({ registry, scope, scopeRoot, previous = null, ledger, plannedTargets = [], mcpSelections = {}, sourceVersion = pkg.version, now = new Date() }: any) {
123
- const held = new Set((ledger?.resources ?? []).map((resource: any) => resource?.harness));
124
- const pinned = [...new Set([...(previous?.targets ?? []).map((entry: any) => entry.harness), ...plannedTargets])]
122
+ function lockDocument({ registry, scope, scopeRoot, previous = null, ledger, plannedTargets = [], mcpSelections = {}, sourceVersion = pkg.version, now = new Date() } ) {
123
+ const held = new Set((ledger?.resources ?? []).map((resource ) => resource?.harness));
124
+ const pinned = [...new Set([...(previous?.targets ?? []).map((entry ) => entry.harness), ...plannedTargets])]
125
125
  .filter((harness) => held.has(harness)).sort();
126
126
  if (!pinned.length) return null;
127
127
  const assets = [];
@@ -145,13 +145,13 @@ function lockDocument({ registry, scope, scopeRoot, previous = null, ledger, pla
145
145
  /** Describe the reviewable delta between the pinned lock and the next one: "created" for a first
146
146
  * pin, "unchanged" when nothing pinned differs, "cleared" when nothing remains pinned, else a count
147
147
  * of the changed rows. */
148
- function lockDelta(previous: any, next: any) {
148
+ function lockDelta(previous , next ) {
149
149
  if (!next) return previous ? { changed: true, summary: 'cleared' } : { changed: false, summary: 'unchanged' };
150
150
  if (!previous) return { changed: true, summary: 'created' };
151
151
  const diff = diffLocks(previous, next);
152
152
  if (diff.clean) return { changed: false, summary: 'unchanged' };
153
153
  const count = ['targets', 'assets']
154
- .flatMap((section) => Object.values<any>(diff[section]).map((list: any) => list.length))
154
+ .flatMap((section) => Object.values (diff[section]).map((list ) => list.length))
155
155
  .reduce((sum, n) => sum + n, 0)
156
156
  + diff.mcpSelections.changed.length + (diff.meta.sourceVersion ? 1 : 0);
157
157
  return { changed: true, summary: `${count} change(s)` };
@@ -160,7 +160,7 @@ function lockDelta(previous: any, next: any) {
160
160
  /** Persist the lock for a completed command and return its delta against what was pinned before.
161
161
  * An unchanged lock is left byte for byte, so routine re-runs neither rewrite the file nor
162
162
  * manufacture noise; a `null` lock removes the file. */
163
- function recordLock(scopeArgs: any, next: any, { fsImpl = fs }: any = {}) {
163
+ function recordLock(scopeArgs , next , { fsImpl = fs } = {}) {
164
164
  const delta = lockDelta(readLock(scopeArgs, { fsImpl }), next);
165
165
  if (delta.changed) {
166
166
  if (next) writeLock(scopeArgs, next, { fsImpl });
@@ -5,7 +5,7 @@
5
5
  // Registry loader and validator. Registry files are plain JSON (core/registry/*.json).
6
6
  const fs = require('node:fs');
7
7
  const path = require('node:path');
8
- const { validatePathsSection } = require('../helper/harness-paths.ts');
8
+ const { validatePathsSection } = require('../helper/harness-paths.js');
9
9
 
10
10
  const REGISTRY_FILES = Object.freeze({
11
11
  harnesses: 'harnesses.json',
@@ -42,9 +42,9 @@ const MCP_NAME = /^[A-Za-z0-9_.-]+$/;
42
42
  * artifact that can lag the source (`index`). Required on every provider (068). */
43
43
  const PROVIDER_SOURCE_MODES = Object.freeze(['live', 'index']);
44
44
 
45
- function issue(errors: string[], location: string, message: string): void { errors.push(`${location}: ${message}`); }
46
- function object(value: any): boolean { return value !== null && typeof value === 'object' && !Array.isArray(value); }
47
- function idsUnique(items: any, label: string, errors: string[]): void {
45
+ function issue(errors , location , message ) { errors.push(`${location}: ${message}`); }
46
+ function object(value ) { return value !== null && typeof value === 'object' && !Array.isArray(value); }
47
+ function idsUnique(items , label , errors ) {
48
48
  const seen = new Set();
49
49
  for (const item of items || []) {
50
50
  if (!object(item) || typeof item.id !== 'string' || !item.id) continue;
@@ -53,22 +53,22 @@ function idsUnique(items: any, label: string, errors: string[]): void {
53
53
  }
54
54
  }
55
55
 
56
- function parseRegistryFile(file: string, fsImpl: any = fs): any {
56
+ function parseRegistryFile(file , fsImpl = fs) {
57
57
  let text;
58
- try { text = fsImpl.readFileSync(file, 'utf8'); } catch (error: any) {
58
+ try { text = fsImpl.readFileSync(file, 'utf8'); } catch (error ) {
59
59
  throw new Error(`Could not read registry file '${file}': ${error.message}`);
60
60
  }
61
- try { return JSON.parse(text); } catch (error: any) {
61
+ try { return JSON.parse(text); } catch (error ) {
62
62
  throw new Error(`Registry file '${file}' is not valid JSON: ${error.message}`);
63
63
  }
64
64
  }
65
65
 
66
- function registryDir(repoRoot: string): string { return path.join(path.resolve(repoRoot), 'core', 'registry'); }
66
+ function registryDir(repoRoot ) { return path.join(path.resolve(repoRoot), 'core', 'registry'); }
67
67
 
68
- function loadRegistry({ repoRoot, dir, fsImpl = fs }: { repoRoot?: string, dir?: string, fsImpl?: any } = {}): any {
68
+ function loadRegistry({ repoRoot, dir, fsImpl = fs } = {}) {
69
69
  if (!repoRoot && !dir) throw new Error('loadRegistry requires repoRoot or dir');
70
- const base = dir ? path.resolve(dir) : registryDir(repoRoot as string);
71
- const loaded: any = {};
70
+ const base = dir ? path.resolve(dir) : registryDir(repoRoot );
71
+ const loaded = {};
72
72
  for (const [name, fileName] of Object.entries(REGISTRY_FILES)) {
73
73
  loaded[name] = parseRegistryFile(path.join(base, fileName), fsImpl);
74
74
  }
@@ -90,11 +90,11 @@ function loadRegistry({ repoRoot, dir, fsImpl = fs }: { repoRoot?: string, dir?:
90
90
  retrievalSlots: Array.isArray(loaded.models.slots) ? loaded.models.slots : [],
91
91
  dispatchTiers: loaded.models.dispatchTiers,
92
92
  capabilities,
93
- versions: Object.fromEntries((Object.entries(loaded) as [string, any][]).map(([name, value]) => [name, value.version])),
93
+ versions: Object.fromEntries((Object.entries(loaded) ).map(([name, value]) => [name, value.version])),
94
94
  };
95
95
  const validation = validateRegistry(registry, { repoRoot: registry.repoRoot, fsImpl });
96
96
  if (!validation.ok) {
97
- const error: any = new Error(`Invalid DoFlow registry:\n- ${validation.errors.join('\n- ')}`);
97
+ const error = new Error(`Invalid DoFlow registry:\n- ${validation.errors.join('\n- ')}`);
98
98
  error.validation = validation;
99
99
  throw error;
100
100
  }
@@ -109,7 +109,7 @@ const CONTRACT_COMPLETENESS = new Set(['verified', 'lower-bound']);
109
109
  *
110
110
  * `evidence` is required for the same reason capabilities require it: a contract claim with no
111
111
  * citation is folklore, and these lists are copied from vendor documentation that moves. */
112
- function validateContract(value: any, location: string, errors: string[], harnessIds: any): void {
112
+ function validateContract(value , location , errors , harnessIds ) {
113
113
  if (!object(value)) { issue(errors, location, 'must be an object'); return; }
114
114
  if (typeof value.harness !== 'string' || !harnessIds.has(value.harness)) {
115
115
  issue(errors, location, `harness must be one of: ${[...harnessIds].join(', ')}`);
@@ -147,28 +147,28 @@ function validateContract(value: any, location: string, errors: string[], harnes
147
147
  issue(errors, `${location}.hookEvents`, 'must not contain duplicates');
148
148
  }
149
149
  if (!Array.isArray(value.evidence) || value.evidence.length === 0
150
- || value.evidence.some((url: any) => typeof url !== 'string' || !/^https:\/\//.test(url))) {
150
+ || value.evidence.some((url ) => typeof url !== 'string' || !/^https:\/\//.test(url))) {
151
151
  issue(errors, location, 'must include one or more HTTPS evidence URLs');
152
152
  }
153
153
  }
154
154
 
155
155
  /** A harness MCP tool-name template: contains {server} and {tool}, and no other brace text. */
156
- function validToolNameTemplate(value: any): boolean {
156
+ function validToolNameTemplate(value ) {
157
157
  return typeof value === 'string' && value.includes('{server}') && value.includes('{tool}')
158
158
  && !value.replace(/\{server\}|\{tool\}/g, '').match(/[{}]/);
159
159
  }
160
160
 
161
- function validateCapability(value: any, location: string, errors: string[]): void {
161
+ function validateCapability(value , location , errors ) {
162
162
  if (!object(value)) { issue(errors, location, 'must be an object'); return; }
163
163
  if (!CAPABILITY_STATUS.has(value.status)) issue(errors, location, 'status must be supported, different, or unavailable');
164
- if (!Array.isArray(value.evidence) || value.evidence.length === 0 || value.evidence.some((url: any) => typeof url !== 'string' || !/^https:\/\//.test(url))) {
164
+ if (!Array.isArray(value.evidence) || value.evidence.length === 0 || value.evidence.some((url ) => typeof url !== 'string' || !/^https:\/\//.test(url))) {
165
165
  issue(errors, location, 'must include one or more HTTPS evidence URLs');
166
166
  }
167
167
  if (typeof value.verification !== 'string' || !value.verification.trim()) issue(errors, location, 'requires a verification instruction');
168
168
  if (value.toolName !== undefined && !validToolNameTemplate(value.toolName)) {
169
169
  issue(errors, location, 'toolName must be a string with exactly the placeholders {server} and {tool} and no other braces');
170
170
  }
171
- if (value.prerequisites !== undefined && (!Array.isArray(value.prerequisites) || value.prerequisites.some((item: any) => typeof item !== 'string' || !item))) {
171
+ if (value.prerequisites !== undefined && (!Array.isArray(value.prerequisites) || value.prerequisites.some((item ) => typeof item !== 'string' || !item))) {
172
172
  issue(errors, location, 'prerequisites must be an array of non-empty strings');
173
173
  }
174
174
  // Optional per-event detail, reusing the same three-value status vocabulary. Additive on
@@ -178,7 +178,7 @@ function validateCapability(value: any, location: string, errors: string[]): voi
178
178
  if (!object(value.events) || Object.keys(value.events).length === 0) {
179
179
  issue(errors, `${location}.events`, 'must be a non-empty object when present');
180
180
  } else {
181
- for (const [event, detail] of Object.entries(value.events) as [string, any][]) {
181
+ for (const [event, detail] of Object.entries(value.events) ) {
182
182
  if (!/^[A-Z][A-Za-z]*$/.test(event)) issue(errors, `${location}.events`, `invalid event name '${event}'`);
183
183
  if (!object(detail) || !CAPABILITY_STATUS.has(detail.status)) {
184
184
  issue(errors, `${location}.events.${event}`, 'status must be supported, different, or unavailable');
@@ -191,7 +191,7 @@ function validateCapability(value: any, location: string, errors: string[]): voi
191
191
  }
192
192
  }
193
193
 
194
- function validateSource(repoRoot: any, source: any, location: string, errors: string[], fsImpl: any, expected: string = 'any'): void {
194
+ function validateSource(repoRoot , source , location , errors , fsImpl , expected = 'any') {
195
195
  if (typeof source !== 'string' || !source.trim()) { issue(errors, location, 'requires a repository-relative source'); return; }
196
196
  const root = path.resolve(repoRoot);
197
197
  const resolved = path.resolve(root, source);
@@ -200,7 +200,7 @@ function validateSource(repoRoot: any, source: any, location: string, errors: st
200
200
  if (expected === 'directory' && !fsImpl.statSync(resolved).isDirectory()) issue(errors, location, `source must be a directory: ${source}`);
201
201
  }
202
202
 
203
- function validateNativeProjection(projection: any, harness: any, { repoRoot, fsImpl }: { repoRoot: any, fsImpl: any }, errors: string[]): void {
203
+ function validateNativeProjection(projection , harness , { repoRoot, fsImpl } , errors ) {
204
204
  const at = `harness '${harness.id}' nativeProjection`;
205
205
  if (!object(projection)) { issue(errors, at, 'must be an object'); return; }
206
206
  const config = projection.config;
@@ -228,14 +228,14 @@ function validateNativeProjection(projection: any, harness: any, { repoRoot, fsI
228
228
  }
229
229
  }
230
230
 
231
- function validateCommand(command: any, location: string, errors: string[]): void {
231
+ function validateCommand(command , location , errors ) {
232
232
  if (!Array.isArray(command) || command.length === 0
233
233
  || command.some((argument) => typeof argument !== 'string' || !argument.trim())) {
234
234
  issue(errors, location, 'command must be a non-empty array of non-empty strings');
235
235
  }
236
236
  }
237
237
 
238
- function validateExternalTool(value: any, location: string, errors: string[]): void {
238
+ function validateExternalTool(value , location , errors ) {
239
239
  if (!object(value)) { issue(errors, location, 'must be an object'); return; }
240
240
  const allowed = new Set(['id', 'displayName', 'officialEvidence', 'status', 'install', 'update', 'uninstall', 'prerequisites']);
241
241
  for (const key of Object.keys(value)) {
@@ -246,11 +246,11 @@ function validateExternalTool(value: any, location: string, errors: string[]): v
246
246
  }
247
247
  if (typeof value.displayName !== 'string' || !value.displayName.trim()) issue(errors, location, 'requires displayName');
248
248
  if (!Array.isArray(value.officialEvidence) || value.officialEvidence.length === 0
249
- || value.officialEvidence.some((url: any) => typeof url !== 'string' || !/^https:\/\//.test(url))) {
249
+ || value.officialEvidence.some((url ) => typeof url !== 'string' || !/^https:\/\//.test(url))) {
250
250
  issue(errors, location, 'must include one or more HTTPS officialEvidence URLs');
251
251
  }
252
252
  if (value.prerequisites !== undefined && (!Array.isArray(value.prerequisites)
253
- || value.prerequisites.some((item: any) => typeof item !== 'string' || !item.trim())
253
+ || value.prerequisites.some((item ) => typeof item !== 'string' || !item.trim())
254
254
  || new Set(value.prerequisites).size !== value.prerequisites.length)) {
255
255
  issue(errors, location, 'prerequisites must be an array of unique non-empty strings when present');
256
256
  }
@@ -261,7 +261,7 @@ function validateExternalTool(value: any, location: string, errors: string[]): v
261
261
  for (const key of Object.keys(value.status)) {
262
262
  if (!allowedStatus.has(key)) issue(errors, `${location}.status`, `unsupported field '${key}'`);
263
263
  }
264
- value.status.commands.forEach((command: any, index: number) => validateCommand(command, `${location}.status.commands[${index}]`, errors));
264
+ value.status.commands.forEach((command , index ) => validateCommand(command, `${location}.status.commands[${index}]`, errors));
265
265
  }
266
266
  for (const action of EXTERNAL_TOOL_ACTIONS) {
267
267
  if (value[action] === undefined) continue;
@@ -277,18 +277,18 @@ function validateExternalTool(value: any, location: string, errors: string[]): v
277
277
  * router will consume. Shell scope: providers are declared with identity, kind, and
278
278
  * evidence (a capability claim without a citation is folklore); roles name routing preferences
279
279
  * without binding to concrete model IDs, which stay runtime/user choices resolved at run time. */
280
- function validateModelProvider(value: any, location: string, errors: string[]): void {
280
+ function validateModelProvider(value , location , errors ) {
281
281
  if (!object(value)) { issue(errors, location, 'must be an object'); return; }
282
282
  if (typeof value.id !== 'string' || !/^[a-z][a-z0-9-]*$/.test(value.id)) issue(errors, location, 'id must be a lowercase identifier');
283
283
  if (typeof value.displayName !== 'string' || !value.displayName.trim()) issue(errors, location, 'requires displayName');
284
284
  if (!MODEL_KINDS.has(value.kind)) issue(errors, location, `kind must be one of: ${[...MODEL_KINDS].join(', ')}`);
285
285
  if (!Array.isArray(value.evidence) || value.evidence.length === 0
286
- || value.evidence.some((url: any) => typeof url !== 'string' || !/^https:\/\//.test(url))) {
286
+ || value.evidence.some((url ) => typeof url !== 'string' || !/^https:\/\//.test(url))) {
287
287
  issue(errors, location, 'must include one or more HTTPS evidence URLs');
288
288
  }
289
289
  }
290
290
 
291
- function validateModelRole(value: any, location: string, errors: string[]): void {
291
+ function validateModelRole(value , location , errors ) {
292
292
  if (!object(value)) { issue(errors, location, 'must be an object'); return; }
293
293
  if (typeof value.id !== 'string' || !/^[a-z][a-z0-9-]*$/.test(value.id)) issue(errors, location, 'id must be a lowercase identifier');
294
294
  for (const field of ['prefer', 'fallback', 'require']) {
@@ -304,7 +304,7 @@ function validateModelRole(value: any, location: string, errors: string[]): void
304
304
  * must fail the load loudly, because a typo reading as "no dense provider" would silently skip
305
305
  * the feature instead of surfacing it. `enabled` is therefore required rather than defaulted:
306
306
  * a misspelled flag defaulting to off is the same PASS-over-no-evidence defect in miniature. */
307
- function validateModelSlot(value: any, location: string, errors: string[], providerIds: any): void {
307
+ function validateModelSlot(value , location , errors , providerIds ) {
308
308
  if (!object(value)) { issue(errors, location, 'must be an object'); return; }
309
309
  const allowed = new Set(['id', 'provider', 'model', 'enabled', 'evidence']);
310
310
  for (const key of Object.keys(value)) {
@@ -316,20 +316,20 @@ function validateModelSlot(value: any, location: string, errors: string[], provi
316
316
  if (typeof value.model !== 'string' || !value.model.trim()) issue(errors, location, 'requires model');
317
317
  if (typeof value.enabled !== 'boolean') issue(errors, location, 'enabled must be a boolean');
318
318
  if (value.evidence !== undefined && (!Array.isArray(value.evidence) || value.evidence.length === 0
319
- || value.evidence.some((url: any) => typeof url !== 'string' || !/^https:\/\//.test(url)))) {
319
+ || value.evidence.some((url ) => typeof url !== 'string' || !/^https:\/\//.test(url)))) {
320
320
  issue(errors, location, 'must include one or more HTTPS evidence URLs when evidence is present');
321
321
  }
322
322
  }
323
323
 
324
324
  /** `trigger` and `steps` on an MCP server (062): when a stage should reach for the server, and
325
325
  * which skill files name it. Registry-only; adapters pick fields and never render these keys. */
326
- function validateMcpStageUse(server: any, at: string, errors: string[]): void {
326
+ function validateMcpStageUse(server , at , errors ) {
327
327
  if (typeof server.trigger !== 'string' || server.trigger.length < 20 || server.trigger.length > 300 || /[\r\n]/.test(server.trigger)) {
328
328
  issue(errors, at, 'trigger must be one line of 20 to 300 characters');
329
329
  }
330
330
  if (!Array.isArray(server.steps)) { issue(errors, at, 'steps must be an array'); return; }
331
331
  const seen = new Set();
332
- server.steps.forEach((step: any, index: number) => {
332
+ server.steps.forEach((step , index ) => {
333
333
  const where = `steps[${index}]`;
334
334
  if (!object(step)) { issue(errors, at, `${where} must be an object`); return; }
335
335
  for (const key of Object.keys(step)) if (!MCP_STEP_KEYS.has(key)) issue(errors, at, `${where} has unknown key '${key}'`);
@@ -345,10 +345,10 @@ function validateMcpStageUse(server: any, at: string, errors: string[]): void {
345
345
  }
346
346
 
347
347
  /** Provider checks for capabilities.json: required `sourceMode` (068) and optional `mcp: {server, tool}` (062). */
348
- function validateCapabilityProviders(capabilities: any, errors: string[]): void {
348
+ function validateCapabilityProviders(capabilities , errors ) {
349
349
  if (capabilities === undefined) return;
350
350
  if (!object(capabilities)) { issue(errors, 'capabilities', 'must be an object'); return; }
351
- for (const [capabilityId, capability] of Object.entries(capabilities) as [string, any][]) {
351
+ for (const [capabilityId, capability] of Object.entries(capabilities) ) {
352
352
  for (const provider of object(capability) && Array.isArray(capability.providers) ? capability.providers : []) {
353
353
  if (!object(provider)) continue;
354
354
  const at = `capability '${capabilityId}' provider '${provider.id ?? '?'}'`;
@@ -365,11 +365,11 @@ function validateCapabilityProviders(capabilities: any, errors: string[]): void
365
365
  }
366
366
 
367
367
  /** `dispatchTiers` in models.json (062): the tier vocabulary and the per-owner default a brief names. */
368
- function validateDispatchTiers(value: any, errors: string[]): void {
368
+ function validateDispatchTiers(value , errors ) {
369
369
  if (value === undefined) return;
370
370
  const at = 'models.dispatchTiers';
371
371
  if (!object(value)) { issue(errors, at, 'must be an object'); return; }
372
- const tiers = Array.isArray(value.tiers) && value.tiers.length > 0 && value.tiers.every((tier: any) => typeof tier === 'string' && tier) ? value.tiers : null;
372
+ const tiers = Array.isArray(value.tiers) && value.tiers.length > 0 && value.tiers.every((tier ) => typeof tier === 'string' && tier) ? value.tiers : null;
373
373
  if (!tiers) { issue(errors, at, 'tiers must be a non-empty array of strings'); return; }
374
374
  if (new Set(tiers).size !== tiers.length) issue(errors, at, 'tiers must be unique');
375
375
  if (!tiers.includes(value.default)) issue(errors, at, `default must be one of: ${tiers.join(', ')}`);
@@ -382,7 +382,7 @@ function validateDispatchTiers(value: any, errors: string[]): void {
382
382
  /** Validates one harness's `orchestration` block (074 IC-026): the five subagent primitives a
383
383
  * harness exposes (spawn, wait, status, cancel, resume), each with evidence and a re-check
384
384
  * instruction. It reads no other harness and no model name; a value is declared, never inferred. */
385
- function validateOrchestration(value: any, location: string, errors: string[]): void {
385
+ function validateOrchestration(value , location , errors ) {
386
386
  if (!object(value)) { issue(errors, location, 'orchestration block is required and must be an object'); return; }
387
387
  for (const op of ORCHESTRATION_OPS) {
388
388
  if (!Object.hasOwn(value, op)) issue(errors, location, `orchestration is missing operation '${op}'`);
@@ -396,7 +396,7 @@ function validateOrchestration(value: any, location: string, errors: string[]):
396
396
  const entry = value[op];
397
397
  if (!object(entry)) { issue(errors, at, 'must be an object'); continue; }
398
398
  if (!ORCHESTRATION_STATUS.has(entry.status)) issue(errors, at, 'status must be supported, unsupported, or unknown');
399
- if (!Array.isArray(entry.evidence) || entry.evidence.some((item: any) => typeof item !== 'string')) {
399
+ if (!Array.isArray(entry.evidence) || entry.evidence.some((item ) => typeof item !== 'string')) {
400
400
  issue(errors, at, 'evidence must be an array of strings');
401
401
  } else if (entry.evidence.length === 0 && (entry.status === 'supported' || entry.status === 'unsupported')) {
402
402
  issue(errors, at, `evidence must not be empty when status is ${entry.status}`);
@@ -412,8 +412,8 @@ function validateOrchestration(value: any, location: string, errors: string[]):
412
412
  }
413
413
  }
414
414
 
415
- function validateRegistry(registry: any, { repoRoot, fsImpl = fs }: { repoRoot?: string, fsImpl?: any } = {}): { ok: boolean, errors: string[] } {
416
- const errors: string[] = [];
415
+ function validateRegistry(registry , { repoRoot, fsImpl = fs } = {}) {
416
+ const errors = [];
417
417
  const harnesses = Array.isArray(registry?.harnesses) ? registry.harnesses : null;
418
418
  const assets = Array.isArray(registry?.assets) ? registry.assets : null;
419
419
  const mcp = Array.isArray(registry?.mcp) ? registry.mcp : null;
@@ -430,14 +430,14 @@ function validateRegistry(registry: any, { repoRoot, fsImpl = fs }: { repoRoot?:
430
430
  idsUnique(mcp, 'mcp', errors); idsUnique(lifecycle, 'lifecycle', errors);
431
431
  idsUnique(externalTools, 'externalTools', errors);
432
432
 
433
- const harnessIds = new Set((harnesses || []).filter(object).map((item: any) => item.id));
433
+ const harnessIds = new Set((harnesses || []).filter(object).map((item ) => item.id));
434
434
  for (const harness of harnesses || []) {
435
435
  const at = `harness '${harness?.id ?? '?'}'`;
436
436
  if (!object(harness)) { issue(errors, 'harnesses', 'entries must be objects'); continue; }
437
437
  if (typeof harness.id !== 'string' || !/^[a-z][a-z0-9-]*$/.test(harness.id)) issue(errors, at, 'id must be a lowercase identifier');
438
438
  if (typeof harness.displayName !== 'string' || !harness.displayName.trim()) issue(errors, at, 'requires displayName');
439
439
  if (typeof harness.adapter !== 'string' || !harness.adapter.trim()) issue(errors, at, 'requires adapter');
440
- if (!Array.isArray(harness.scopes) || harness.scopes.length === 0 || harness.scopes.some((scope: any) => !SCOPES.has(scope))) issue(errors, at, 'scopes must contain project and/or user');
440
+ if (!Array.isArray(harness.scopes) || harness.scopes.length === 0 || harness.scopes.some((scope ) => !SCOPES.has(scope))) issue(errors, at, 'scopes must contain project and/or user');
441
441
  if (!object(harness.nativeTargets)) issue(errors, at, 'nativeTargets must be an object');
442
442
  // Declared native paths (Stage 3): every surface a harness declares must parse into the
443
443
  // minimal {base, segments} shape — unknown keys or malformed rules fail the load loudly so a
@@ -484,7 +484,7 @@ function validateRegistry(registry: any, { repoRoot, fsImpl = fs }: { repoRoot?:
484
484
  if (!object(projection) || typeof projection.renderer !== 'string' || !projection.renderer.trim() || typeof projection.capability !== 'string' || !projection.capability.trim()) {
485
485
  issue(errors, at, `requires renderer and capability projection for '${harnessId}'`); continue;
486
486
  }
487
- const harness = (harnesses || []).find((item: any) => item?.id === harnessId);
487
+ const harness = (harnesses || []).find((item ) => item?.id === harnessId);
488
488
  const capability = harness?.capabilities?.[projection.capability];
489
489
  if (!capability) issue(errors, at, `projection '${harnessId}' references unknown capability '${projection.capability}'`);
490
490
  else if (capability.status === 'unavailable') issue(errors, at, `projection '${harnessId}' cannot render unavailable capability '${projection.capability}'`);
@@ -497,7 +497,7 @@ function validateRegistry(registry: any, { repoRoot, fsImpl = fs }: { repoRoot?:
497
497
  if (typeof server.id !== 'string' || !/^[a-z][a-z0-9-]*$/.test(server.id)) issue(errors, at, 'id must be a lowercase identifier');
498
498
  if (!MCP_TRANSPORTS.has(server.transport)) issue(errors, at, 'transport must be stdio, http, or sse');
499
499
  if (server.transport === 'stdio' && (typeof server.command !== 'string' || !server.command.trim())) issue(errors, at, 'stdio transport requires command');
500
- if (server.args !== undefined && (!Array.isArray(server.args) || server.args.some((arg: any) => typeof arg !== 'string'))) issue(errors, at, 'args must be an array of strings');
500
+ if (server.args !== undefined && (!Array.isArray(server.args) || server.args.some((arg ) => typeof arg !== 'string'))) issue(errors, at, 'args must be an array of strings');
501
501
  if (!['optional', 'required'].includes(server.selection)) issue(errors, at, 'selection must be optional or required');
502
502
  validateMcpStageUse(server, at, errors);
503
503
  }
@@ -511,9 +511,9 @@ function validateRegistry(registry: any, { repoRoot, fsImpl = fs }: { repoRoot?:
511
511
  if (typeof policy.id !== 'string' || !/^[a-z][a-z0-9-]*$/.test(policy.id)) issue(errors, at, 'id must be a lowercase identifier');
512
512
  if (typeof policy.intent !== 'string' || !policy.intent.trim()) issue(errors, at, 'requires intent');
513
513
  if (policy.tier !== undefined && !POLICY_TIERS.has(policy.tier)) issue(errors, at, `tier must be one of ${[...POLICY_TIERS].join(', ')}`);
514
- if (!Array.isArray(policy.requires) || policy.requires.some((capability: any) => typeof capability !== 'string')) issue(errors, at, 'requires must be an array of capability names');
514
+ if (!Array.isArray(policy.requires) || policy.requires.some((capability ) => typeof capability !== 'string')) issue(errors, at, 'requires must be an array of capability names');
515
515
  if (!object(policy.mappings)) { issue(errors, at, 'mappings must be an object'); continue; }
516
- for (const [harnessId, mapping] of Object.entries(policy.mappings) as [string, any][]) {
516
+ for (const [harnessId, mapping] of Object.entries(policy.mappings) ) {
517
517
  if (!harnessIds.has(harnessId)) { issue(errors, at, `mapping references unknown harness '${harnessId}'`); continue; }
518
518
  if (!object(mapping) || !CAPABILITY_STATUS.has(mapping.status)) { issue(errors, at, `mapping '${harnessId}' needs a valid status`); continue; }
519
519
  if (mapping.status !== 'unavailable' && (typeof mapping.event !== 'string' || !mapping.event.trim())) issue(errors, at, `mapping '${harnessId}' requires event for a renderable status`);
@@ -539,21 +539,21 @@ function validateRegistry(registry: any, { repoRoot, fsImpl = fs }: { repoRoot?:
539
539
  issue(errors, 'models.slots', 'must be an array when present');
540
540
  } else {
541
541
  idsUnique(modelSlots, 'models.slots', errors);
542
- const providerIds = new Set((modelProviders || []).filter(object).map((item: any) => item.id));
542
+ const providerIds = new Set((modelProviders || []).filter(object).map((item ) => item.id));
543
543
  for (const slot of modelSlots) validateModelSlot(slot, `model slot '${slot?.id ?? '?'}'`, errors, providerIds);
544
544
  }
545
545
  return { ok: errors.length === 0, errors };
546
546
  }
547
547
 
548
- function harnessFor(registry: any, id: string): any {
549
- const harness = registry.harnesses.find((item: any) => item.id === id);
548
+ function harnessFor(registry , id ) {
549
+ const harness = registry.harnesses.find((item ) => item.id === id);
550
550
  if (!harness) throw new Error(`Unknown registry harness '${id}'`);
551
551
  return harness;
552
552
  }
553
553
 
554
- function selectAssets(registry: any, { harness, capability, kind }: { harness?: string, capability?: string, kind?: string } = {}): any[] {
554
+ function selectAssets(registry , { harness, capability, kind } = {}) {
555
555
  const declaration = harness ? harnessFor(registry, harness) : null;
556
- return registry.assets.filter((asset: any) => {
556
+ return registry.assets.filter((asset ) => {
557
557
  if (kind && asset.kind !== kind) return false;
558
558
  if (!harness) return true;
559
559
  if (!asset.appliesTo.includes(harness)) return false;
@@ -562,18 +562,18 @@ function selectAssets(registry: any, { harness, capability, kind }: { harness?:
562
562
  });
563
563
  }
564
564
 
565
- function selectMcpServers(registry: any, ids?: string[] | null): any[] {
565
+ function selectMcpServers(registry , ids ) {
566
566
  if (ids === undefined || ids === null) return [...registry.mcp];
567
567
  if (!Array.isArray(ids)) throw new Error('MCP selection must be an array of server ids');
568
568
  const wanted = new Set(ids);
569
- const unknown = [...wanted].filter((id) => !registry.mcp.some((server: any) => server.id === id));
569
+ const unknown = [...wanted].filter((id) => !registry.mcp.some((server ) => server.id === id));
570
570
  if (unknown.length) throw new Error(`Unknown registry MCP server(s): ${unknown.join(', ')}`);
571
- return registry.mcp.filter((server: any) => wanted.has(server.id));
571
+ return registry.mcp.filter((server ) => wanted.has(server.id));
572
572
  }
573
573
 
574
574
  /** True when the harness takes servers from the shared MCP catalog; the lifecycle and the selection
575
575
  * resolver read this one predicate. */
576
- function mcpCapable(registry: any, harnessId: string): boolean {
576
+ function mcpCapable(registry , harnessId ) {
577
577
  return harnessFor(registry, harnessId).capabilities.mcp?.status === 'supported';
578
578
  }
579
579
 
@@ -581,7 +581,7 @@ function mcpCapable(registry: any, harnessId: string): boolean {
581
581
  * harness's native MCP reconciliation (Codex's config.toml writer, Claude's ~/.claude.json/
582
582
  * .mcp.json writer) expects — the one place this transform is derived, reused rather than
583
583
  * re-implemented per consumer. */
584
- function nativeMcpCatalog(servers: any[] = []): { allServers: string[], serverDefs: any } {
584
+ function nativeMcpCatalog(servers = []) {
585
585
  const allServers = servers.map((server) => server.id);
586
586
  const serverDefs = Object.fromEntries(servers.map((server) => [server.id, {
587
587
  command: server.command,
@@ -591,8 +591,8 @@ function nativeMcpCatalog(servers: any[] = []): { allServers: string[], serverDe
591
591
  return { allServers, serverDefs };
592
592
  }
593
593
 
594
- function capabilityMapData(registry: any): any[] {
595
- return registry.harnesses.flatMap((harness: any) => (Object.entries(harness.capabilities) as [string, any][]).map(([capability, declaration]) => ({
594
+ function capabilityMapData(registry ) {
595
+ return registry.harnesses.flatMap((harness ) => (Object.entries(harness.capabilities) ).map(([capability, declaration]) => ({
596
596
  harness: harness.id,
597
597
  displayName: harness.displayName,
598
598
  capability,