@the-open-engine/zeroshot 6.38.0 → 6.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (381) hide show
  1. package/cli/event-copy.js +8 -7
  2. package/cli/event-copy.ts +12 -0
  3. package/cli/index.js +7 -31
  4. package/cli/json-export.js +61 -64
  5. package/cli/json-export.ts +112 -0
  6. package/cli/message-formatter-utils.js +68 -56
  7. package/cli/message-formatter-utils.ts +121 -0
  8. package/cli/message-formatters-normal.js +124 -259
  9. package/cli/message-formatters-normal.ts +250 -0
  10. package/cli/message-formatters-watch.js +81 -84
  11. package/cli/message-formatters-watch.ts +189 -0
  12. package/lib/agent-cli-provider/adapters/codex.d.ts.map +1 -1
  13. package/lib/agent-cli-provider/adapters/codex.js +3 -1
  14. package/lib/agent-cli-provider/adapters/codex.js.map +1 -1
  15. package/lib/cluster/generated/protocol-schema.cjs +1 -1
  16. package/lib/cluster/generated/protocol-schema.mjs +1 -1
  17. package/lib/cluster/generated/protocol.d.cts +8 -0
  18. package/lib/cluster/generated/protocol.d.mts +8 -0
  19. package/lib/cluster/generated/protocol.d.ts +8 -0
  20. package/lib/clusters-registry.d.ts +23 -0
  21. package/lib/clusters-registry.js +17 -29
  22. package/lib/completion.d.ts +12 -0
  23. package/lib/completion.js +84 -94
  24. package/lib/compose-utils.d.ts +32 -0
  25. package/lib/compose-utils.js +57 -72
  26. package/lib/detached-startup.d.ts +83 -0
  27. package/lib/detached-startup.js +231 -302
  28. package/lib/docker-config.d.ts +43 -0
  29. package/lib/docker-config.js +150 -325
  30. package/lib/docker-env-auth.d.ts +17 -0
  31. package/lib/docker-env-auth.js +75 -0
  32. package/lib/git-remote-utils.d.ts +48 -0
  33. package/lib/git-remote-utils.js +147 -213
  34. package/lib/id-detector.d.ts +15 -0
  35. package/lib/id-detector.js +37 -41
  36. package/lib/path-check.d.ts +30 -0
  37. package/lib/path-check.js +42 -45
  38. package/lib/process-liveness.d.ts +16 -0
  39. package/lib/process-liveness.js +19 -12
  40. package/lib/provider-credential-path.d.ts +16 -0
  41. package/lib/provider-credential-path.js +31 -29
  42. package/lib/provider-defaults.d.ts +30 -0
  43. package/lib/provider-defaults.js +47 -42
  44. package/lib/provider-detection.d.ts +11 -0
  45. package/lib/provider-detection.js +91 -54
  46. package/lib/provider-names.d.ts +38 -0
  47. package/lib/provider-names.js +56 -64
  48. package/lib/repo-settings.d.ts +28 -0
  49. package/lib/repo-settings.js +83 -59
  50. package/lib/run-mode.d.ts +21 -0
  51. package/lib/run-mode.js +21 -20
  52. package/lib/run-plan.d.ts +28 -0
  53. package/lib/run-plan.js +17 -16
  54. package/lib/settings/claude-auth.d.ts +35 -0
  55. package/lib/settings/claude-auth.js +30 -47
  56. package/lib/settings-defaults.d.ts +20 -0
  57. package/lib/settings-defaults.js +149 -0
  58. package/lib/settings-error.d.ts +7 -0
  59. package/lib/settings-error.js +8 -0
  60. package/lib/settings-issue-providers.d.ts +10 -0
  61. package/lib/settings-issue-providers.js +12 -0
  62. package/lib/settings-models.d.ts +22 -0
  63. package/lib/settings-models.js +93 -0
  64. package/lib/settings-storage.d.ts +23 -0
  65. package/lib/settings-storage.js +205 -0
  66. package/lib/settings-validation.d.ts +7 -0
  67. package/lib/settings-validation.js +182 -0
  68. package/lib/settings.d.ts +29 -0
  69. package/lib/settings.js +41 -736
  70. package/lib/setup-apply-types.d.ts +66 -0
  71. package/lib/setup-apply-types.js +2 -0
  72. package/lib/setup-apply.d.ts +39 -0
  73. package/lib/setup-apply.js +267 -264
  74. package/lib/setup-journal.d.ts +41 -0
  75. package/lib/setup-journal.js +119 -76
  76. package/lib/setup-plan-types.d.ts +102 -0
  77. package/lib/setup-plan-types.js +2 -0
  78. package/lib/setup-plan.d.ts +30 -0
  79. package/lib/setup-plan.js +300 -324
  80. package/lib/setup-undo.d.ts +44 -0
  81. package/lib/setup-undo.js +61 -74
  82. package/lib/start-cluster-config.d.ts +26 -0
  83. package/lib/start-cluster-config.js +56 -0
  84. package/lib/start-cluster-environment.d.ts +40 -0
  85. package/lib/start-cluster-environment.js +134 -0
  86. package/lib/start-cluster-run-options.d.ts +53 -0
  87. package/lib/start-cluster-run-options.js +147 -0
  88. package/lib/start-cluster.d.ts +64 -0
  89. package/lib/start-cluster.js +98 -454
  90. package/lib/stream-json-parser.d.ts +17 -0
  91. package/lib/stream-json-parser.js +51 -52
  92. package/npm-shrinkwrap.json +2 -2
  93. package/package.json +12 -6
  94. package/scripts/postinstall.js +27 -0
  95. package/scripts/rust-distribution.js +23 -14
  96. package/src/agent/agent-command-proofs-context.js +35 -45
  97. package/src/agent/agent-command-proofs-context.ts +87 -0
  98. package/src/agent/agent-config-settings.js +19 -0
  99. package/src/agent/agent-config-settings.ts +41 -0
  100. package/src/agent/agent-config.js +122 -231
  101. package/src/agent/agent-config.ts +285 -0
  102. package/src/agent/agent-context-builder.js +38 -273
  103. package/src/agent/agent-context-builder.ts +51 -0
  104. package/src/agent/agent-context-environment-sections.js +105 -0
  105. package/src/agent/agent-context-environment-sections.ts +134 -0
  106. package/src/agent/agent-context-packs.js +165 -0
  107. package/src/agent/agent-context-packs.ts +296 -0
  108. package/src/agent/agent-context-prompt-sections.js +100 -0
  109. package/src/agent/agent-context-prompt-sections.ts +148 -0
  110. package/src/agent/agent-context-sections.js +25 -334
  111. package/src/agent/agent-context-sections.ts +36 -0
  112. package/src/agent/agent-context-sources.js +96 -149
  113. package/src/agent/agent-context-sources.ts +253 -0
  114. package/src/agent/agent-context-types.js +2 -0
  115. package/src/agent/agent-context-types.ts +132 -0
  116. package/src/agent/agent-context-validation-sections.js +73 -0
  117. package/src/agent/agent-context-validation-sections.ts +129 -0
  118. package/src/agent/agent-input-injector.js +99 -118
  119. package/src/agent/agent-input-injector.ts +195 -0
  120. package/src/agent/agent-liveness-poll.js +74 -78
  121. package/src/agent/agent-liveness-poll.ts +163 -0
  122. package/src/agent/agent-quality-gate-schema.js +77 -86
  123. package/src/agent/agent-quality-gate-schema.ts +135 -0
  124. package/src/agent/agent-quality-gates-context.js +44 -50
  125. package/src/agent/agent-quality-gates-context.ts +91 -0
  126. package/src/agent/agent-stuck-detector.js +171 -239
  127. package/src/agent/agent-stuck-detector.ts +288 -0
  128. package/src/agent/agent-trigger-evaluator.js +29 -46
  129. package/src/agent/agent-trigger-evaluator.ts +98 -0
  130. package/src/agent/context-metrics.js +110 -141
  131. package/src/agent/context-metrics.ts +261 -0
  132. package/src/agent/context-pack-builder.js +72 -358
  133. package/src/agent/context-pack-builder.ts +105 -0
  134. package/src/agent/context-pack-core.js +70 -0
  135. package/src/agent/context-pack-core.ts +96 -0
  136. package/src/agent/context-pack-selection.js +119 -0
  137. package/src/agent/context-pack-selection.ts +187 -0
  138. package/src/agent/context-pack-truncation.js +139 -0
  139. package/src/agent/context-pack-truncation.ts +208 -0
  140. package/src/agent/context-pack-types.js +2 -0
  141. package/src/agent/context-pack-types.ts +98 -0
  142. package/src/agent/context-replay-policy.js +30 -39
  143. package/src/agent/context-replay-policy.ts +71 -0
  144. package/src/agent/critical-agent-policy.js +6 -10
  145. package/src/agent/critical-agent-policy.ts +20 -0
  146. package/src/agent/guidance-queue.js +74 -87
  147. package/src/agent/guidance-queue.ts +166 -0
  148. package/src/agent/output-extraction-error-detail.js +45 -0
  149. package/src/agent/output-extraction-error-detail.ts +49 -0
  150. package/src/agent/output-extraction-failures.js +125 -0
  151. package/src/agent/output-extraction-failures.ts +149 -0
  152. package/src/agent/output-extraction-json.js +234 -0
  153. package/src/agent/output-extraction-json.ts +235 -0
  154. package/src/agent/output-extraction-pi.js +124 -0
  155. package/src/agent/output-extraction-pi.ts +137 -0
  156. package/src/agent/output-extraction-types.js +2 -0
  157. package/src/agent/output-extraction-types.ts +49 -0
  158. package/src/agent/output-extraction.js +17 -617
  159. package/src/agent/output-extraction.ts +30 -0
  160. package/src/agent/output-reformatter-errors.js +83 -0
  161. package/src/agent/output-reformatter-errors.ts +106 -0
  162. package/src/agent/output-reformatter-types.js +2 -0
  163. package/src/agent/output-reformatter-types.ts +86 -0
  164. package/src/agent/output-reformatter.js +140 -185
  165. package/src/agent/output-reformatter.ts +238 -0
  166. package/src/agent/pi-terminal-lifecycle-types.js +2 -0
  167. package/src/agent/pi-terminal-lifecycle-types.ts +41 -0
  168. package/src/agent/pi-terminal-lifecycle.js +186 -206
  169. package/src/agent/pi-terminal-lifecycle.ts +297 -0
  170. package/src/agent/provider-control-plane.js +22 -21
  171. package/src/agent/provider-control-plane.ts +52 -0
  172. package/src/agent/provider-terminal-failure-types.js +2 -0
  173. package/src/agent/provider-terminal-failure-types.ts +60 -0
  174. package/src/agent/provider-terminal-failure.js +194 -191
  175. package/src/agent/provider-terminal-failure.ts +262 -0
  176. package/src/agent/rate-limit-backoff.js +31 -74
  177. package/src/agent/rate-limit-backoff.ts +71 -0
  178. package/src/agent/schema-utils.js +85 -125
  179. package/src/agent/schema-utils.ts +148 -0
  180. package/src/agent/structured-output-error.js +34 -33
  181. package/src/agent/structured-output-error.ts +105 -0
  182. package/src/agent/validation-platform.js +27 -27
  183. package/src/agent/validation-platform.ts +49 -0
  184. package/src/agent-cli-provider/adapters/codex.ts +3 -1
  185. package/src/attach/attach-client.js +232 -405
  186. package/src/attach/attach-client.ts +300 -0
  187. package/src/attach/attach-server-cleanup.js +43 -0
  188. package/src/attach/attach-server-cleanup.ts +39 -0
  189. package/src/attach/attach-server-clients.js +117 -0
  190. package/src/attach/attach-server-clients.ts +122 -0
  191. package/src/attach/attach-server-events.js +38 -0
  192. package/src/attach/attach-server-events.ts +36 -0
  193. package/src/attach/attach-server-pty.js +45 -0
  194. package/src/attach/attach-server-pty.ts +64 -0
  195. package/src/attach/attach-server-runtime.js +73 -0
  196. package/src/attach/attach-server-runtime.ts +62 -0
  197. package/src/attach/attach-server-socket.js +28 -0
  198. package/src/attach/attach-server-socket.ts +23 -0
  199. package/src/attach/attach-server-types.js +2 -0
  200. package/src/attach/attach-server-types.ts +82 -0
  201. package/src/attach/attach-server.js +106 -523
  202. package/src/attach/attach-server.ts +158 -0
  203. package/src/attach/index.js +18 -35
  204. package/src/attach/index.ts +18 -0
  205. package/src/attach/protocol.js +95 -184
  206. package/src/attach/protocol.ts +181 -0
  207. package/src/attach/ring-buffer.js +76 -102
  208. package/src/attach/ring-buffer.ts +109 -0
  209. package/src/attach/send-input.js +64 -75
  210. package/src/attach/send-input.ts +94 -0
  211. package/src/attach/socket-discovery.js +164 -218
  212. package/src/attach/socket-discovery.ts +230 -0
  213. package/src/attach/socket-paths.js +42 -61
  214. package/src/attach/socket-paths.ts +81 -0
  215. package/src/claude-credentials.js +51 -54
  216. package/src/claude-credentials.ts +89 -0
  217. package/src/cluster/generated/protocol-schema.ts +1 -1
  218. package/src/cluster/generated/protocol.ts +2 -1
  219. package/src/command-proofs.js +110 -141
  220. package/src/command-proofs.ts +216 -0
  221. package/src/config-router.js +81 -73
  222. package/src/config-router.ts +131 -0
  223. package/src/copy-worker.js +53 -29
  224. package/src/copy-worker.ts +75 -0
  225. package/src/darwin-keychain-boundary.js +80 -111
  226. package/src/darwin-keychain-boundary.ts +148 -0
  227. package/src/guidance-topics.js +4 -5
  228. package/src/guidance-topics.ts +10 -0
  229. package/src/input-helpers.js +33 -54
  230. package/src/input-helpers.ts +58 -0
  231. package/src/ledger-sequence.js +45 -48
  232. package/src/ledger-sequence.ts +63 -0
  233. package/src/legacy-lib/clusters-registry.ts +40 -0
  234. package/src/legacy-lib/completion.ts +148 -0
  235. package/src/legacy-lib/compose-utils.ts +102 -0
  236. package/src/legacy-lib/detached-startup.ts +449 -0
  237. package/src/legacy-lib/docker-config.ts +282 -0
  238. package/src/legacy-lib/docker-env-auth.ts +116 -0
  239. package/src/legacy-lib/git-remote-utils.ts +244 -0
  240. package/src/legacy-lib/id-detector.ts +57 -0
  241. package/src/legacy-lib/js-yaml.d.ts +3 -0
  242. package/src/legacy-lib/omelette.d.ts +15 -0
  243. package/src/legacy-lib/path-check.ts +89 -0
  244. package/src/legacy-lib/process-liveness.ts +36 -0
  245. package/src/legacy-lib/provider-credential-path.ts +57 -0
  246. package/src/legacy-lib/provider-defaults.ts +114 -0
  247. package/src/legacy-lib/provider-detection.ts +67 -0
  248. package/src/legacy-lib/provider-names.ts +126 -0
  249. package/src/legacy-lib/repo-settings.ts +88 -0
  250. package/src/legacy-lib/run-mode.ts +58 -0
  251. package/src/legacy-lib/run-plan.ts +49 -0
  252. package/src/legacy-lib/settings/claude-auth.ts +80 -0
  253. package/src/legacy-lib/settings-defaults.ts +212 -0
  254. package/src/legacy-lib/settings-error.ts +8 -0
  255. package/src/legacy-lib/settings-issue-providers.ts +18 -0
  256. package/src/legacy-lib/settings-models.ts +124 -0
  257. package/src/legacy-lib/settings-storage.ts +277 -0
  258. package/src/legacy-lib/settings-validation.ts +220 -0
  259. package/src/legacy-lib/settings.ts +87 -0
  260. package/src/legacy-lib/setup-apply-types.ts +70 -0
  261. package/src/legacy-lib/setup-apply.ts +456 -0
  262. package/src/legacy-lib/setup-journal.ts +185 -0
  263. package/src/legacy-lib/setup-plan-types.ts +96 -0
  264. package/src/legacy-lib/setup-plan.ts +500 -0
  265. package/src/legacy-lib/setup-undo.ts +142 -0
  266. package/src/legacy-lib/start-cluster-config.ts +124 -0
  267. package/src/legacy-lib/start-cluster-environment.ts +165 -0
  268. package/src/legacy-lib/start-cluster-run-options.ts +279 -0
  269. package/src/legacy-lib/start-cluster.ts +237 -0
  270. package/src/legacy-lib/stream-json-parser.ts +88 -0
  271. package/src/message-buffer.js +55 -71
  272. package/src/message-buffer.ts +107 -0
  273. package/src/message-bus-bridge.js +92 -133
  274. package/src/message-bus-bridge.ts +149 -0
  275. package/src/name-generator-shared.js +220 -0
  276. package/src/name-generator-shared.ts +231 -0
  277. package/src/name-generator.js +8 -217
  278. package/src/name-generator.ts +27 -0
  279. package/src/omp-blob-root.js +83 -64
  280. package/src/omp-blob-root.ts +116 -0
  281. package/src/omp-config-overlay.js +65 -38
  282. package/src/omp-config-overlay.ts +110 -0
  283. package/src/omp-execution-fingerprint.js +38 -43
  284. package/src/omp-execution-fingerprint.ts +92 -0
  285. package/src/omp-session-limits.js +10 -15
  286. package/src/omp-session-limits.ts +53 -0
  287. package/src/omp-session-partition.js +2 -2
  288. package/src/omp-session-verifier.js +1 -1
  289. package/src/orchestrator.js +19 -4
  290. package/src/providers/anthropic/index.js +3 -1
  291. package/src/providers/anthropic/index.ts +13 -0
  292. package/src/providers/capabilities.js +19 -27
  293. package/src/providers/capabilities.ts +44 -0
  294. package/src/providers/google/index.js +3 -1
  295. package/src/providers/google/index.ts +13 -0
  296. package/src/providers/openai/index.js +3 -1
  297. package/src/providers/openai/index.ts +13 -0
  298. package/src/providers/opencode/index.js +3 -1
  299. package/src/providers/opencode/index.ts +13 -0
  300. package/src/quality-gates.js +67 -127
  301. package/src/quality-gates.ts +149 -0
  302. package/src/repo-settings-access.js +40 -0
  303. package/src/repo-settings-access.ts +64 -0
  304. package/src/schemas/sub-cluster.js +127 -183
  305. package/src/schemas/sub-cluster.ts +238 -0
  306. package/src/state-snapshot-normalization.js +224 -0
  307. package/src/state-snapshot-normalization.ts +222 -0
  308. package/src/state-snapshot.js +192 -359
  309. package/src/state-snapshot.ts +291 -0
  310. package/src/state-snapshotter.js +118 -130
  311. package/src/state-snapshotter.ts +167 -0
  312. package/src/sub-cluster-wrapper.js +4 -13
  313. package/src/task-run-model-args.js +87 -124
  314. package/src/task-run-model-args.ts +204 -0
  315. package/src/task-runner.js +5 -25
  316. package/src/task-runner.ts +28 -0
  317. package/src/task-startup-error.js +73 -58
  318. package/src/task-startup-error.ts +126 -0
  319. package/src/template-validation/consensus-gate-contracts.js +2 -0
  320. package/src/template-validation/consensus-gate-contracts.ts +70 -0
  321. package/src/template-validation/consensus-gate-scenarios.js +76 -0
  322. package/src/template-validation/consensus-gate-scenarios.ts +130 -0
  323. package/src/template-validation/consensus-gate-stage.js +34 -0
  324. package/src/template-validation/consensus-gate-stage.ts +56 -0
  325. package/src/template-validation/random-topology-contracts.js +2 -0
  326. package/src/template-validation/random-topology-contracts.ts +54 -0
  327. package/src/template-validation/random-topology-dispatch.js +25 -0
  328. package/src/template-validation/random-topology-dispatch.ts +33 -0
  329. package/src/template-validation/random-topology-message-loop.js +53 -0
  330. package/src/template-validation/random-topology-message-loop.ts +73 -0
  331. package/src/template-validation/random-topology-operations.js +79 -0
  332. package/src/template-validation/random-topology-operations.ts +101 -0
  333. package/src/template-validation/random-topology-runtime.js +25 -0
  334. package/src/template-validation/random-topology-runtime.ts +43 -0
  335. package/src/template-validation/random-topology-scenario.js +52 -0
  336. package/src/template-validation/random-topology-scenario.ts +57 -0
  337. package/src/template-validation/random-topology-schema.js +159 -0
  338. package/src/template-validation/random-topology-schema.ts +190 -0
  339. package/src/template-validation/random-topology-state.js +28 -0
  340. package/src/template-validation/random-topology-state.ts +30 -0
  341. package/src/template-validation/random-topology-trigger-evaluation.js +45 -0
  342. package/src/template-validation/random-topology-trigger-evaluation.ts +65 -0
  343. package/src/template-validation/random-topology-trigger.js +75 -0
  344. package/src/template-validation/random-topology-trigger.ts +88 -0
  345. package/src/template-validation/report-formatter.js +40 -46
  346. package/src/template-validation/report-formatter.ts +101 -0
  347. package/src/template-validation/simulate-consensus-gates.js +111 -302
  348. package/src/template-validation/simulate-consensus-gates.ts +196 -0
  349. package/src/template-validation/simulate-random-topology.js +26 -519
  350. package/src/template-validation/simulate-random-topology.ts +57 -0
  351. package/src/template-validation/simulate-two-stage-validation.js +41 -265
  352. package/src/template-validation/simulate-two-stage-validation.ts +55 -0
  353. package/src/template-validation/simulation-agent-runtime.js +25 -0
  354. package/src/template-validation/simulation-agent-runtime.ts +40 -0
  355. package/src/template-validation/simulation-agent.js +29 -0
  356. package/src/template-validation/simulation-agent.ts +73 -0
  357. package/src/template-validation/simulation-runtime.js +34 -0
  358. package/src/template-validation/simulation-runtime.ts +72 -0
  359. package/src/template-validation/two-stage-contracts.js +2 -0
  360. package/src/template-validation/two-stage-contracts.ts +41 -0
  361. package/src/template-validation/two-stage-inputs.js +21 -0
  362. package/src/template-validation/two-stage-inputs.ts +29 -0
  363. package/src/template-validation/two-stage-results.js +30 -0
  364. package/src/template-validation/two-stage-results.ts +40 -0
  365. package/src/template-validation/two-stage-scenario.js +86 -0
  366. package/src/template-validation/two-stage-scenario.ts +120 -0
  367. package/src/worktree-claude-config.js +182 -220
  368. package/src/worktree-claude-config.ts +272 -0
  369. package/src/worktree-tooling-env.js +103 -125
  370. package/src/worktree-tooling-env.ts +142 -0
  371. package/task-lib/completion.js +2 -3
  372. package/task-lib/completion.ts +9 -0
  373. package/task-lib/config.js +1 -4
  374. package/task-lib/config.ts +11 -0
  375. package/task-lib/name-generator.js +4 -226
  376. package/task-lib/name-generator.ts +11 -0
  377. package/task-lib/omp-session-cleanup.js +1 -1
  378. package/task-lib/omp-storage-root.js +8 -12
  379. package/task-lib/omp-storage-root.ts +62 -0
  380. package/task-lib/process-termination.js +143 -166
  381. package/task-lib/process-termination.ts +268 -0
package/lib/setup-plan.js CHANGED
@@ -1,3 +1,4 @@
1
+ "use strict";
1
2
  /**
2
3
  * Setup plan — read-only facts collector + the pinned, versioned setup contract.
3
4
  *
@@ -8,379 +9,354 @@
8
9
  * decisionId registry below are a stable contract — do not rename IDs or add a
9
10
  * parallel run-mode field without updating every consumer.
10
11
  */
11
-
12
- const path = require('path');
13
-
12
+ const path = require("path");
14
13
  const SCHEMA_VERSION = 2;
15
-
16
14
  // Canonical settings keys consumed by resolveEffectiveRunPlan. Never introduce
17
15
  // a parallel setting or translate a decision into a differently named key.
18
16
  const DECISION_PATHS = {
19
- defaultProvider: { scope: 'global', path: 'defaultProvider' },
20
- defaultIsolation: { scope: 'global', path: 'defaultIsolation' },
21
- allowLocalNoIsolation: { scope: 'global', path: 'allowLocalNoIsolation' },
22
- defaultDelivery: { scope: 'global', path: 'defaultDelivery' },
23
- defaultIssueSource: { scope: 'global', path: 'defaultIssueSource' },
24
- prBase: { scope: 'repo', path: 'prBase' },
25
- dockerMounts: { scope: 'global', path: 'dockerMounts' },
26
- dockerEnvPassthrough: { scope: 'global', path: 'dockerEnvPassthrough' },
27
- updatePolicy: { scope: 'global', path: 'updatePolicy' },
17
+ defaultProvider: { scope: 'global', path: 'defaultProvider' },
18
+ defaultIsolation: { scope: 'global', path: 'defaultIsolation' },
19
+ allowLocalNoIsolation: { scope: 'global', path: 'allowLocalNoIsolation' },
20
+ defaultDelivery: { scope: 'global', path: 'defaultDelivery' },
21
+ defaultIssueSource: { scope: 'global', path: 'defaultIssueSource' },
22
+ prBase: { scope: 'repo', path: 'prBase' },
23
+ dockerMounts: { scope: 'global', path: 'dockerMounts' },
24
+ dockerEnvPassthrough: { scope: 'global', path: 'dockerEnvPassthrough' },
25
+ updatePolicy: { scope: 'global', path: 'updatePolicy' },
28
26
  };
29
-
30
27
  function providerLevelDecisionId(providerName) {
31
- return `providerLevel.${providerName}`;
28
+ return `providerLevel.${providerName}`;
32
29
  }
33
-
34
30
  // Settings keys consumed by runtime resolvers. Shared by buildProposedWrites
35
31
  // and setup apply so neither surface can advertise dead configuration.
36
32
  const CONSUMED_PATHS = new Set([
37
- 'global:defaultProvider',
38
- 'global:defaultIsolation',
39
- 'global:defaultDelivery',
40
- 'global:defaultIssueSource',
41
- 'global:dockerMounts',
42
- 'global:dockerEnvPassthrough',
43
- // No resolver consumes this yet; consumption is deferred to a future
44
- // update-policy issue, but issue #606 explicitly sanctions writing it now.
45
- 'global:updatePolicy',
33
+ 'global:defaultProvider',
34
+ 'global:defaultIsolation',
35
+ 'global:defaultDelivery',
36
+ 'global:defaultIssueSource',
37
+ 'global:dockerMounts',
38
+ 'global:dockerEnvPassthrough',
39
+ // No resolver consumes this yet; consumption is deferred to a future
40
+ // update-policy issue, but issue #606 explicitly sanctions writing it now.
41
+ 'global:updatePolicy',
46
42
  ]);
47
-
48
43
  function isConsumedPath(scope, targetPath) {
49
- if (scope === 'global' && targetPath.startsWith('providerSettings.')) return true;
50
- return CONSUMED_PATHS.has(`${scope}:${targetPath}`);
44
+ if (scope === 'global' && targetPath.startsWith('providerSettings.'))
45
+ return true;
46
+ return CONSUMED_PATHS.has(`${scope}:${targetPath}`);
51
47
  }
52
-
53
48
  // providerLevel.<provider> decisionIds map to a per-provider settings key
54
49
  // (providerSettings.<provider>) not present in DECISION_PATHS above, since
55
50
  // the provider name is only known at runtime.
56
51
  function resolveDecisionPath(decisionId) {
57
- if (decisionId.startsWith('providerLevel.')) {
58
- const providerName = decisionId.slice('providerLevel.'.length);
59
- return { scope: 'global', path: `providerSettings.${providerName}` };
60
- }
61
- return DECISION_PATHS[decisionId] || null;
52
+ if (decisionId.startsWith('providerLevel.')) {
53
+ const providerName = decisionId.slice('providerLevel.'.length);
54
+ return { scope: 'global', path: `providerSettings.${providerName}` };
55
+ }
56
+ return DECISION_PATHS[decisionId] || null;
62
57
  }
63
-
64
58
  function getNestedValue(source, pathStr) {
65
- return pathStr
66
- .split('.')
67
- .reduce((acc, key) => (acc === null || acc === undefined ? undefined : acc[key]), source);
59
+ return pathStr
60
+ .split('.')
61
+ .reduce((acc, key) => {
62
+ if (acc === null || acc === undefined)
63
+ return undefined;
64
+ // Property access intentionally retains JavaScript's primitive boxing behavior.
65
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
66
+ return acc[key];
67
+ }, source);
68
68
  }
69
-
70
69
  function defaultDeps() {
71
- const { commandExists, getCommandPath } = require('./provider-detection');
72
- const { checkDocker, checkGhAuth } = require('../src/preflight');
73
- const { execSync } = require('../src/lib/safe-exec');
74
- const { listProviders, getProvider } = require('../src/providers');
75
- const { getProviderDefaults } = require('./provider-defaults');
76
- const { getDefaultProviderId, getProviderMetadata } = require('./provider-names');
77
- const packageJson = require('../package.json');
78
-
79
- return {
80
- commandExists,
81
- getCommandPath,
82
- checkDocker,
83
- checkGhAuth,
84
- execSync,
85
- listProviders,
86
- getProvider,
87
- getProviderDefaults,
88
- getDefaultProviderId,
89
- getProviderMetadata,
90
- getNodeVersion: () => process.version,
91
- getPackageVersion: () => packageJson.version,
92
- };
70
+ // These paths intentionally resolve beside the emitted module in lib/.
71
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
72
+ const { commandExists, getCommandPath } = require('./provider-detection');
73
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
74
+ const { checkDocker, checkGhAuth } = require('../src/preflight');
75
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
76
+ const { execSync } = require('../src/lib/safe-exec');
77
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
78
+ const { listProviders, getProvider } = require('../src/providers');
79
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
80
+ const { getProviderDefaults } = require('./provider-defaults');
81
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
82
+ const { getDefaultProviderId, getProviderMetadata } = require('./provider-names');
83
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
84
+ const packageJson = require('../package.json');
85
+ return {
86
+ commandExists,
87
+ getCommandPath,
88
+ checkDocker,
89
+ checkGhAuth,
90
+ execSync,
91
+ listProviders,
92
+ getProvider,
93
+ getProviderDefaults,
94
+ getDefaultProviderId,
95
+ getProviderMetadata,
96
+ getNodeVersion: () => process.version,
97
+ getPackageVersion: () => packageJson.version,
98
+ };
93
99
  }
94
-
95
100
  function detectInstallSource(cwd, env) {
96
- if (env.npm_config_global === 'true') return 'npm-global';
97
- if (env.npm_execpath && /_npx|npx/.test(env.npm_execpath)) return 'npx';
98
- const ownNodeModules = path.join(__dirname, '..', 'node_modules');
99
- if (cwd && (cwd === path.join(__dirname, '..') || cwd.startsWith(ownNodeModules))) return 'local';
100
- return 'unknown';
101
+ if (env.npm_config_global === 'true')
102
+ return 'npm-global';
103
+ if (env.npm_execpath && /_npx|npx/.test(env.npm_execpath))
104
+ return 'npx';
105
+ const ownNodeModules = path.join(__dirname, '..', 'node_modules');
106
+ if (cwd && (cwd === path.join(__dirname, '..') || cwd.startsWith(ownNodeModules)))
107
+ return 'local';
108
+ return 'unknown';
101
109
  }
102
-
103
- function buildNodeFacts({ cwd, env, deps }) {
104
- return {
105
- version: deps.getNodeVersion(),
106
- packageVersion: deps.getPackageVersion(),
107
- installSource: detectInstallSource(cwd, env),
108
- };
110
+ function buildNodeFacts({ cwd, env, deps, }) {
111
+ return {
112
+ version: deps.getNodeVersion(),
113
+ packageVersion: deps.getPackageVersion(),
114
+ installSource: detectInstallSource(cwd, env),
115
+ };
109
116
  }
110
-
111
117
  function buildProviderFacts(deps) {
112
- const providers = {};
113
- for (const name of deps.listProviders()) {
114
- const metadata = deps.getProviderMetadata(name);
115
- const provider = deps.getProvider(name);
116
- let available = false;
117
- try {
118
- available = provider.isAvailable() === true;
119
- } catch {
120
- available = false;
118
+ const providers = {};
119
+ for (const name of deps.listProviders()) {
120
+ const metadata = deps.getProviderMetadata(name);
121
+ const provider = deps.getProvider(name);
122
+ let available = false;
123
+ try {
124
+ available = provider.isAvailable() === true;
125
+ }
126
+ catch {
127
+ available = false;
128
+ }
129
+ const cliCommand = provider.cliCommand || metadata.binary;
130
+ providers[name] = {
131
+ available,
132
+ displayName: metadata.displayName,
133
+ installInstructions: metadata.installInstructions,
134
+ path: available && deps.commandExists(cliCommand) ? deps.getCommandPath(cliCommand) : null,
135
+ };
121
136
  }
122
- const cliCommand = provider.cliCommand || metadata.binary;
123
- providers[name] = {
124
- available,
125
- displayName: metadata.displayName,
126
- installInstructions: metadata.installInstructions,
127
- path: available && deps.commandExists(cliCommand) ? deps.getCommandPath(cliCommand) : null,
128
- };
129
- }
130
- return providers;
137
+ return providers;
131
138
  }
132
-
133
139
  function safeExecTrim(deps, command, cwd) {
134
- try {
135
- return deps.execSync(command, { cwd, stdio: 'pipe', encoding: 'utf8' }).trim();
136
- } catch {
137
- return null;
138
- }
140
+ try {
141
+ return deps.execSync(command, { cwd, stdio: 'pipe', encoding: 'utf8' }).trim();
142
+ }
143
+ catch {
144
+ return null;
145
+ }
139
146
  }
140
-
141
- function buildGitFacts({ cwd, deps }) {
142
- const isRepo = safeExecTrim(deps, 'git rev-parse --is-inside-work-tree', cwd) === 'true';
143
- const ghAvailable = deps.commandExists('gh');
144
-
145
- if (!isRepo) {
146
- return { isRepo: false, branch: null, remote: null, ghAvailable, ghAuthed: null };
147
- }
148
-
149
- const branch = safeExecTrim(deps, 'git rev-parse --abbrev-ref HEAD', cwd);
150
- const remote = safeExecTrim(deps, 'git remote get-url origin', cwd);
151
- const ghAuthed = ghAvailable ? !!deps.checkGhAuth().authenticated : null;
152
-
153
- return { isRepo: true, branch, remote, ghAvailable, ghAuthed };
147
+ function buildGitFacts({ cwd, deps, }) {
148
+ const isRepo = safeExecTrim(deps, 'git rev-parse --is-inside-work-tree', cwd) === 'true';
149
+ const ghAvailable = deps.commandExists('gh');
150
+ if (!isRepo) {
151
+ return { isRepo: false, branch: null, remote: null, ghAvailable, ghAuthed: null };
152
+ }
153
+ const branch = safeExecTrim(deps, 'git rev-parse --abbrev-ref HEAD', cwd);
154
+ const remote = safeExecTrim(deps, 'git remote get-url origin', cwd);
155
+ const ghAuthed = ghAvailable ? !!deps.checkGhAuth().authenticated : null;
156
+ return { isRepo: true, branch, remote, ghAvailable, ghAuthed };
154
157
  }
155
-
156
- function buildFacts({ cwd, settings, repoSettings, env, deps }) {
157
- return {
158
- node: buildNodeFacts({ cwd, env, deps }),
159
- providers: buildProviderFacts(deps),
160
- git: buildGitFacts({ cwd, deps }),
161
- docker: { available: !!deps.checkDocker().available },
162
- settings: {
163
- hasGlobal: settings?.__meta ? !!settings.__meta.fileExists : true,
164
- hasRepo: repoSettings !== null && repoSettings !== undefined,
165
- },
166
- };
158
+ function buildFacts({ cwd, settings, repoSettings, env, deps, }) {
159
+ return {
160
+ node: buildNodeFacts({ cwd, env, deps }),
161
+ providers: buildProviderFacts(deps),
162
+ git: buildGitFacts({ cwd, deps }),
163
+ docker: { available: !!deps.checkDocker().available },
164
+ settings: {
165
+ hasGlobal: settings.__meta ? Boolean(getNestedValue(settings.__meta, 'fileExists')) : true,
166
+ hasRepo: repoSettings !== null && repoSettings !== undefined,
167
+ },
168
+ };
167
169
  }
168
-
169
170
  function inferIssueSource(facts) {
170
- const { parseGitRemoteUrl } = require('./git-remote-utils');
171
- const parsed = parseGitRemoteUrl(facts.git.remote);
172
- return parsed?.provider || null;
171
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
172
+ const { parseGitRemoteUrl } = require('./git-remote-utils');
173
+ const parsed = parseGitRemoteUrl(facts.git.remote);
174
+ return parsed?.provider || null;
173
175
  }
174
-
175
176
  function inferPrBase(cwd, deps) {
176
- const branch = safeExecTrim(deps, 'git rev-parse --abbrev-ref origin/HEAD', cwd);
177
- if (!branch) return null;
178
- return branch.replace(/^origin\//, '');
177
+ const branch = safeExecTrim(deps, 'git rev-parse --abbrev-ref origin/HEAD', cwd);
178
+ if (!branch)
179
+ return null;
180
+ return branch.replace(/^origin\//, '');
179
181
  }
180
-
181
182
  function buildProviderLevelRecommendation(name, deps) {
182
- const providerDefaults = deps.getProviderDefaults()[name] || {};
183
- return {
184
- minLevel: providerDefaults.minLevel || 'level1',
185
- defaultLevel: providerDefaults.defaultLevel || 'level2',
186
- maxLevel: providerDefaults.maxLevel || 'level3',
187
- };
183
+ const providerDefaults = deps.getProviderDefaults()[name] || {};
184
+ return {
185
+ minLevel: providerDefaults.minLevel || 'level1',
186
+ defaultLevel: providerDefaults.defaultLevel || 'level2',
187
+ maxLevel: providerDefaults.maxLevel || 'level3',
188
+ };
188
189
  }
189
-
190
- function buildRecommendedAndRisk({ cwd, facts, env, deps }) {
191
- const recommended = {};
192
- const risk = {};
193
- const registryDefault = deps.getDefaultProviderId();
194
- recommended.defaultProvider = registryDefault;
195
- risk.defaultProvider = facts.providers[registryDefault]?.available ? 'low' : 'medium';
196
-
197
- for (const name of Object.keys(facts.providers)) {
198
- recommended[providerLevelDecisionId(name)] = buildProviderLevelRecommendation(name, deps);
199
- risk[providerLevelDecisionId(name)] = 'low';
200
- }
201
-
202
- if (facts.git.isRepo) {
203
- recommended.defaultIsolation = 'worktree';
204
- risk.defaultIsolation = 'low';
205
- } else if (facts.docker.available) {
206
- recommended.defaultIsolation = 'docker';
207
- risk.defaultIsolation = 'low';
208
- } else {
209
- recommended.defaultIsolation = 'none';
210
- risk.defaultIsolation = 'high';
211
- }
212
-
213
- recommended.allowLocalNoIsolation = false;
214
- risk.allowLocalNoIsolation = 'low';
215
- recommended.defaultDelivery = 'none';
216
- risk.defaultDelivery = 'low';
217
-
218
- const inferredIssueSource = inferIssueSource(facts);
219
- recommended.defaultIssueSource = inferredIssueSource || 'github';
220
- risk.defaultIssueSource = inferredIssueSource ? 'low' : 'medium';
221
-
222
- const inferredPrBase = inferPrBase(cwd, deps);
223
- recommended.prBase = inferredPrBase || 'main';
224
- risk.prBase = inferredPrBase ? 'low' : 'medium';
225
- recommended.dockerMounts = ['gh', 'git', 'ssh'];
226
- risk.dockerMounts = 'low';
227
- recommended.dockerEnvPassthrough = [];
228
- risk.dockerEnvPassthrough = 'low';
229
-
230
- const nonInteractive = env.CI === 'true' || env.__isTTY === false;
231
- recommended.updatePolicy = nonInteractive ? 'off' : 'notify';
232
- risk.updatePolicy = 'low';
233
-
234
- return { recommended, risk, inferredIssueSource, inferredPrBase };
190
+ function buildRecommendedAndRisk({ cwd, facts, env, deps, }) {
191
+ const recommended = {};
192
+ const risk = {};
193
+ const registryDefault = deps.getDefaultProviderId();
194
+ recommended.defaultProvider = registryDefault;
195
+ risk.defaultProvider = facts.providers[registryDefault]?.available ? 'low' : 'medium';
196
+ for (const name of Object.keys(facts.providers)) {
197
+ recommended[providerLevelDecisionId(name)] = buildProviderLevelRecommendation(name, deps);
198
+ risk[providerLevelDecisionId(name)] = 'low';
199
+ }
200
+ if (facts.git.isRepo) {
201
+ recommended.defaultIsolation = 'worktree';
202
+ risk.defaultIsolation = 'low';
203
+ }
204
+ else if (facts.docker.available) {
205
+ recommended.defaultIsolation = 'docker';
206
+ risk.defaultIsolation = 'low';
207
+ }
208
+ else {
209
+ recommended.defaultIsolation = 'none';
210
+ risk.defaultIsolation = 'high';
211
+ }
212
+ recommended.allowLocalNoIsolation = false;
213
+ risk.allowLocalNoIsolation = 'low';
214
+ recommended.defaultDelivery = 'none';
215
+ risk.defaultDelivery = 'low';
216
+ const inferredIssueSource = inferIssueSource(facts);
217
+ recommended.defaultIssueSource = inferredIssueSource || 'github';
218
+ risk.defaultIssueSource = inferredIssueSource ? 'low' : 'medium';
219
+ const inferredPrBase = inferPrBase(cwd, deps);
220
+ recommended.prBase = inferredPrBase || 'main';
221
+ risk.prBase = inferredPrBase ? 'low' : 'medium';
222
+ recommended.dockerMounts = ['gh', 'git', 'ssh'];
223
+ risk.dockerMounts = 'low';
224
+ recommended.dockerEnvPassthrough = [];
225
+ risk.dockerEnvPassthrough = 'low';
226
+ const nonInteractive = env.CI === 'true' || env.__isTTY === false;
227
+ recommended.updatePolicy = nonInteractive ? 'off' : 'notify';
228
+ risk.updatePolicy = 'low';
229
+ return { recommended, risk, inferredIssueSource, inferredPrBase };
235
230
  }
236
-
237
231
  function currentValueFor(decisionId, settings, repoSettings) {
238
- const target = resolveDecisionPath(decisionId);
239
- if (!target) return null;
240
- const source = target.scope === 'repo' ? repoSettings || {} : settings || {};
241
- const value = getNestedValue(source, target.path);
242
- return value === undefined ? null : value;
232
+ const target = resolveDecisionPath(decisionId);
233
+ if (!target)
234
+ return null;
235
+ const source = target.scope === 'repo' ? repoSettings || {} : settings || {};
236
+ const value = getNestedValue(source, target.path);
237
+ return value === undefined ? null : value;
243
238
  }
244
-
245
- function buildDecisions({ facts, settings, repoSettings, inferredIssueSource, inferredPrBase }) {
246
- const decisions = [];
247
- const hasGlobal = facts.settings.hasGlobal;
248
- const hasRepo = facts.settings.hasRepo;
249
-
250
- const globalDecisionIds = [
251
- 'defaultProvider',
252
- ...Object.keys(facts.providers).map(providerLevelDecisionId),
253
- 'defaultIsolation',
254
- 'allowLocalNoIsolation',
255
- 'defaultDelivery',
256
- 'defaultIssueSource',
257
- 'dockerMounts',
258
- 'dockerEnvPassthrough',
259
- 'updatePolicy',
260
- ];
261
-
262
- for (const decisionId of globalDecisionIds) {
263
- const shouldInclude =
264
- !hasGlobal || (decisionId === 'defaultIssueSource' && !inferredIssueSource);
265
- if (!shouldInclude) continue;
266
- decisions.push({
267
- decisionId,
268
- domain: domainFor(decisionId),
269
- currentValue: currentValueFor(decisionId, settings, repoSettings),
270
- });
271
- }
272
-
273
- if (!hasRepo || !inferredPrBase) {
274
- decisions.push({
275
- decisionId: 'prBase',
276
- domain: domainFor('prBase'),
277
- currentValue: currentValueFor('prBase', settings, repoSettings),
278
- });
279
- }
280
-
281
- return decisions;
239
+ function buildDecisions({ facts, settings, repoSettings, inferredIssueSource, inferredPrBase, }) {
240
+ const decisions = [];
241
+ const hasGlobal = facts.settings.hasGlobal;
242
+ const hasRepo = facts.settings.hasRepo;
243
+ const globalDecisionIds = [
244
+ 'defaultProvider',
245
+ ...Object.keys(facts.providers).map(providerLevelDecisionId),
246
+ 'defaultIsolation',
247
+ 'allowLocalNoIsolation',
248
+ 'defaultDelivery',
249
+ 'defaultIssueSource',
250
+ 'dockerMounts',
251
+ 'dockerEnvPassthrough',
252
+ 'updatePolicy',
253
+ ];
254
+ for (const decisionId of globalDecisionIds) {
255
+ const shouldInclude = !hasGlobal || (decisionId === 'defaultIssueSource' && !inferredIssueSource);
256
+ if (!shouldInclude)
257
+ continue;
258
+ decisions.push({
259
+ decisionId,
260
+ domain: domainFor(decisionId),
261
+ currentValue: currentValueFor(decisionId, settings, repoSettings),
262
+ });
263
+ }
264
+ if (!hasRepo || !inferredPrBase) {
265
+ decisions.push({
266
+ decisionId: 'prBase',
267
+ domain: domainFor('prBase'),
268
+ currentValue: currentValueFor('prBase', settings, repoSettings),
269
+ });
270
+ }
271
+ return decisions;
282
272
  }
283
-
284
273
  function domainFor(decisionId) {
285
- if (decisionId.startsWith('providerLevel.')) {
286
- return '{ minLevel, defaultLevel, maxLevel } of level1|level2|level3';
287
- }
288
- const domains = {
289
- defaultProvider: 'registry provider id',
290
- defaultIsolation: 'worktree | docker | none',
291
- allowLocalNoIsolation: 'boolean',
292
- defaultDelivery: 'none | pr | ship',
293
- defaultIssueSource: 'github | gitlab | jira | azure-devops | linear',
294
- prBase: 'string (branch)',
295
- dockerMounts: 'array of presets/objects',
296
- dockerEnvPassthrough: 'string[]',
297
- updatePolicy: 'off | notify | auto',
298
- };
299
- return domains[decisionId] || 'unknown';
274
+ if (decisionId.startsWith('providerLevel.')) {
275
+ return '{ minLevel, defaultLevel, maxLevel } of level1|level2|level3';
276
+ }
277
+ const domains = {
278
+ defaultProvider: 'registry provider id',
279
+ defaultIsolation: 'worktree | docker | none',
280
+ allowLocalNoIsolation: 'boolean',
281
+ defaultDelivery: 'none | pr | ship',
282
+ defaultIssueSource: 'github | gitlab | jira | azure-devops | linear',
283
+ prBase: 'string (branch)',
284
+ dockerMounts: 'array of presets/objects',
285
+ dockerEnvPassthrough: 'string[]',
286
+ updatePolicy: 'off | notify | auto',
287
+ };
288
+ return domains[decisionId] || 'unknown';
300
289
  }
301
-
302
- function buildProposedWrites({ decisions, recommended }) {
303
- const writes = [];
304
- for (const decision of decisions) {
305
- const target = resolveDecisionPath(decision.decisionId);
306
- if (!target) continue;
307
- // A decision can still be surfaced for the user to make (e.g. a future
308
- // wizard asking about prBase), but proposing a *write* for a settings key
309
- // no resolver reads would advertise a write that apply will always skip —
310
- // dead config. Only propose writes apply will actually perform.
311
- if (!isConsumedPath(target.scope, target.path)) continue;
312
- const to = recommended[decision.decisionId];
313
- if (to === decision.currentValue) continue;
314
- writes.push({
315
- scope: target.scope,
316
- path: target.path,
317
- from: decision.currentValue ?? null,
318
- to,
319
- decisionId: decision.decisionId,
320
- });
321
- }
322
- return writes;
290
+ function buildProposedWrites({ decisions, recommended, }) {
291
+ const writes = [];
292
+ for (const decision of decisions) {
293
+ const target = resolveDecisionPath(decision.decisionId);
294
+ if (!target)
295
+ continue;
296
+ // A decision can still be surfaced for the user to make (e.g. a future
297
+ // wizard asking about prBase), but proposing a *write* for a settings key
298
+ // no resolver reads would advertise a write that apply will always skip —
299
+ // dead config. Only propose writes apply will actually perform.
300
+ if (!isConsumedPath(target.scope, target.path))
301
+ continue;
302
+ const to = recommended[decision.decisionId];
303
+ if (to === decision.currentValue)
304
+ continue;
305
+ writes.push({
306
+ scope: target.scope,
307
+ path: target.path,
308
+ from: decision.currentValue ?? null,
309
+ to,
310
+ decisionId: decision.decisionId,
311
+ });
312
+ }
313
+ return writes;
323
314
  }
324
-
325
315
  /**
326
316
  * Build the pinned, versioned setup contract. Pure over injected inputs —
327
317
  * performs only cheap, read-only detection (no writes, no prompts).
328
- *
329
- * @param {Object} params
330
- * @param {string} params.cwd
331
- * @param {Object} params.settings - loaded global settings (optionally with __meta.fileExists)
332
- * @param {Object|null} params.repoSettings - repo-local settings object, or null
333
- * @param {Object} params.env - caller-provided env (e.g. process.env), plus optional __isTTY
334
- * @param {Object} [params.deps] - injected dependencies (for testing)
335
- * @returns {Object} plan
336
318
  */
337
- function buildSetupPlan({ cwd, settings, repoSettings, env, deps } = {}) {
338
- const resolvedDeps = { ...defaultDeps(), ...(deps || {}) };
339
- const resolvedEnv = env || {};
340
- const resolvedCwd = cwd || '.';
341
- const resolvedSettings = settings || {};
342
-
343
- const facts = buildFacts({
344
- cwd: resolvedCwd,
345
- settings: resolvedSettings,
346
- repoSettings,
347
- env: resolvedEnv,
348
- deps: resolvedDeps,
349
- });
350
-
351
- const { recommended, risk, inferredIssueSource, inferredPrBase } = buildRecommendedAndRisk({
352
- cwd: resolvedCwd,
353
- facts,
354
- env: resolvedEnv,
355
- deps: resolvedDeps,
356
- });
357
-
358
- const decisions = buildDecisions({
359
- facts,
360
- settings: resolvedSettings,
361
- repoSettings,
362
- inferredIssueSource,
363
- inferredPrBase,
364
- });
365
-
366
- const proposedWrites = buildProposedWrites({ decisions, recommended });
367
-
368
- return {
369
- schemaVersion: SCHEMA_VERSION,
370
- facts,
371
- decisions,
372
- recommended,
373
- risk,
374
- proposedWrites,
375
- };
319
+ function buildSetupPlan({ cwd, settings, repoSettings, env, deps, } = {}) {
320
+ const resolvedDeps = { ...defaultDeps(), ...(deps || {}) };
321
+ const resolvedEnv = env || {};
322
+ const resolvedCwd = cwd || '.';
323
+ const resolvedSettings = settings || {};
324
+ const facts = buildFacts({
325
+ cwd: resolvedCwd,
326
+ settings: resolvedSettings,
327
+ repoSettings,
328
+ env: resolvedEnv,
329
+ deps: resolvedDeps,
330
+ });
331
+ const { recommended, risk, inferredIssueSource, inferredPrBase } = buildRecommendedAndRisk({
332
+ cwd: resolvedCwd,
333
+ facts,
334
+ env: resolvedEnv,
335
+ deps: resolvedDeps,
336
+ });
337
+ const decisions = buildDecisions({
338
+ facts,
339
+ settings: resolvedSettings,
340
+ repoSettings,
341
+ inferredIssueSource,
342
+ inferredPrBase,
343
+ });
344
+ const proposedWrites = buildProposedWrites({ decisions, recommended });
345
+ return {
346
+ schemaVersion: SCHEMA_VERSION,
347
+ facts,
348
+ decisions,
349
+ recommended,
350
+ risk,
351
+ proposedWrites,
352
+ };
376
353
  }
377
-
378
354
  module.exports = {
379
- buildSetupPlan,
380
- resolveDecisionPath,
381
- domainFor,
382
- DECISION_PATHS,
383
- getNestedValue,
384
- isConsumedPath,
385
- CONSUMED_PATHS,
355
+ buildSetupPlan,
356
+ resolveDecisionPath,
357
+ domainFor,
358
+ DECISION_PATHS,
359
+ getNestedValue,
360
+ isConsumedPath,
361
+ CONSUMED_PATHS,
386
362
  };