@oisincoveney/pipeline 3.24.2 → 3.24.3

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 (326) hide show
  1. package/README.md +2 -2
  2. package/defaults/pipeline.yaml +23 -5
  3. package/defaults/profiles.yaml +104 -21
  4. package/dist/argo-graph.js +88 -88
  5. package/dist/argo-submit.d.ts +109 -137
  6. package/dist/argo-submit.js +337 -360
  7. package/dist/argo-workflow.d.ts +156 -176
  8. package/dist/argo-workflow.js +96 -138
  9. package/dist/backlog.js +5 -4
  10. package/dist/bench/eval-report.js +21 -12
  11. package/dist/claude-settings-config.js +23 -31
  12. package/dist/claude-user-config.js +6 -16
  13. package/dist/cli/bootstrap-commands.js +3 -3
  14. package/dist/cli/doctor.d.ts +1 -1
  15. package/dist/cli/doctor.js +172 -169
  16. package/dist/cli/factory-commands.js +12 -11
  17. package/dist/cli/format.js +92 -86
  18. package/dist/cli/loop-commands.js +21 -21
  19. package/dist/cli/mcp-gateway-commands.js +12 -12
  20. package/dist/cli/plan-commands.js +85 -116
  21. package/dist/cli/program.d.ts +2 -2
  22. package/dist/cli/program.js +35 -43
  23. package/dist/cli/run-command.js +25 -25
  24. package/dist/cli/run-commands.js +25 -25
  25. package/dist/cli/run-resolver.d.ts +1 -1
  26. package/dist/cli/run-resolver.js +30 -32
  27. package/dist/cli/run-service.d.ts +2 -2
  28. package/dist/cli/run-service.js +226 -239
  29. package/dist/cli/submit-options.js +78 -82
  30. package/dist/cluster-doctor.js +147 -175
  31. package/dist/codex-config.js +24 -30
  32. package/dist/commands/bench-command.js +9 -7
  33. package/dist/commands/pipeline-command.js +9 -5
  34. package/dist/commands/runner-command-command.js +28 -18
  35. package/dist/commands/ticket/complete.js +29 -33
  36. package/dist/commands/ticket/create.js +82 -82
  37. package/dist/commands/ticket/graph-check.js +11 -11
  38. package/dist/commands/ticket/next.js +36 -36
  39. package/dist/commands/ticket/sequence.js +10 -12
  40. package/dist/commands/ticket/shared.d.ts +1 -1
  41. package/dist/commands/ticket/shared.js +63 -66
  42. package/dist/commands/ticket/start.js +58 -67
  43. package/dist/commands/ticket-command.js +2 -2
  44. package/dist/config/defaults.d.ts +139 -139
  45. package/dist/config/defaults.js +81 -80
  46. package/dist/config/lint.js +52 -64
  47. package/dist/config/load.d.ts +4 -4
  48. package/dist/config/load.js +62 -74
  49. package/dist/config/schema/catalog.js +1 -1
  50. package/dist/config/schema/mcp.js +46 -52
  51. package/dist/config/schemas.d.ts +375 -525
  52. package/dist/config/schemas.js +397 -397
  53. package/dist/config/validate.d.ts +1 -1
  54. package/dist/config/validate.js +236 -201
  55. package/dist/context/repo-map.js +102 -119
  56. package/dist/credentials/broker.d.ts +6 -8
  57. package/dist/credentials/broker.js +17 -16
  58. package/dist/credentials/codex-config.js +15 -19
  59. package/dist/credentials/file-targets.js +9 -15
  60. package/dist/credentials/local-codex-auth-sync.js +55 -58
  61. package/dist/credentials/opencode-config.js +28 -32
  62. package/dist/credentials/runner.js +13 -13
  63. package/dist/effect-sync-errors.js +28 -0
  64. package/dist/factory/create-experiment.js +38 -42
  65. package/dist/factory/exec.js +10 -12
  66. package/dist/factory/git-credentials.js +4 -4
  67. package/dist/factory/stamp-answers.js +10 -11
  68. package/dist/factory/template-update.js +65 -44
  69. package/dist/factory-lane.d.ts +26 -40
  70. package/dist/factory-lane.js +26 -25
  71. package/dist/gates.js +3 -3
  72. package/dist/git-remote-url.js +17 -19
  73. package/dist/hooks.d.ts +26 -25
  74. package/dist/hooks.js +20 -22
  75. package/dist/index.d.ts +1 -1
  76. package/dist/index.js +21 -35
  77. package/dist/install-commands/claude-code.js +96 -92
  78. package/dist/install-commands/host-selection.js +3 -6
  79. package/dist/install-commands/opencode.js +263 -259
  80. package/dist/install-commands/planner.js +159 -180
  81. package/dist/install-commands/result-format.js +1 -3
  82. package/dist/install-commands/shared.js +25 -42
  83. package/dist/install-commands/writer.js +22 -25
  84. package/dist/install-commands.js +8 -8
  85. package/dist/jscpd-output.js +29 -0
  86. package/dist/json-config-merge.js +14 -20
  87. package/dist/json-line-values.js +17 -6
  88. package/dist/loop/argo-poll.js +52 -71
  89. package/dist/loop/backlog-records.js +1 -3
  90. package/dist/loop/controller-deps.js +146 -159
  91. package/dist/loop/controller.js +162 -186
  92. package/dist/loop/gh-checks.js +62 -61
  93. package/dist/loop/gh-runner.js +22 -25
  94. package/dist/loop/loop-command.js +66 -58
  95. package/dist/loop/loop-controller-entrypoint.js +28 -30
  96. package/dist/loop/merge.js +36 -40
  97. package/dist/mcp/gateway-config.js +25 -29
  98. package/dist/mcp/gateway-doctor.js +104 -116
  99. package/dist/mcp/gateway-reconcile.js +17 -22
  100. package/dist/mcp/gateway-runtime.js +1 -3
  101. package/dist/mcp/host-config.js +37 -40
  102. package/dist/mcp/host-renderers.js +10 -18
  103. package/dist/mcp/repo-local-backends.js +50 -54
  104. package/dist/mcp/toolhive-vmcp.js +39 -39
  105. package/dist/model-resolver.js +29 -33
  106. package/dist/moka-global-config.d.ts +78 -54
  107. package/dist/moka-global-config.js +90 -92
  108. package/dist/moka-submit.d.ts +2163 -864
  109. package/dist/moka-submit.js +97 -99
  110. package/dist/opencode-project-config.js +34 -40
  111. package/dist/package-assets.js +1 -3
  112. package/dist/path-refs.js +2 -2
  113. package/dist/pipeline-init.js +25 -25
  114. package/dist/pipeline-runtime.d.ts +11 -11
  115. package/dist/pipeline-runtime.js +49 -50
  116. package/dist/planned-node.js +6 -3
  117. package/dist/planning/compile.d.ts +3 -3
  118. package/dist/planning/compile.js +140 -139
  119. package/dist/planning/dependency-refs.js +5 -5
  120. package/dist/planning/generate.d.ts +1293 -1293
  121. package/dist/planning/generate.js +305 -314
  122. package/dist/planning/graph.js +131 -149
  123. package/dist/remote/argo/model.d.ts +103 -121
  124. package/dist/remote/argo/model.js +205 -197
  125. package/dist/remote/argo/policy.js +39 -47
  126. package/dist/remote/argo/storage.js +81 -85
  127. package/dist/remote/submit/argo-submission.js +53 -58
  128. package/dist/remote/submit/compilation.js +46 -49
  129. package/dist/remote/submit/event-boundary.js +62 -76
  130. package/dist/remote/submit/hook-events.d.ts +5 -0
  131. package/dist/remote/submit/io.js +40 -43
  132. package/dist/remote/submit/options.d.ts +50 -0
  133. package/dist/remote/submit/options.js +27 -0
  134. package/dist/remote/submit/service.js +45 -49
  135. package/dist/run-control/command-context.js +6 -8
  136. package/dist/run-control/commands.js +10 -10
  137. package/dist/run-control/contracts.d.ts +40 -100
  138. package/dist/run-control/contracts.js +56 -69
  139. package/dist/run-control/detach.js +66 -72
  140. package/dist/run-control/file-errors.js +1 -3
  141. package/dist/run-control/logical-segment.js +3 -3
  142. package/dist/run-control/next-node.js +66 -78
  143. package/dist/run-control/postgres/postgres-run-control-store.js +195 -224
  144. package/dist/run-control/resume-command.js +33 -41
  145. package/dist/run-control/run-artifacts-command.js +83 -105
  146. package/dist/run-control/run-command-domain.js +7 -11
  147. package/dist/run-control/run-control-store.js +9 -9
  148. package/dist/run-control/run-query-command.js +73 -100
  149. package/dist/run-control/run-record.js +2 -2
  150. package/dist/run-control/run-state-lock.js +5 -5
  151. package/dist/run-control/runtime-event-projection.js +38 -50
  152. package/dist/run-control/runtime-reporter.js +84 -102
  153. package/dist/run-control/stop-command.js +42 -53
  154. package/dist/run-control/store-fs-effects.js +3 -1
  155. package/dist/run-control/store-manifest.js +38 -13
  156. package/dist/run-control/store-paths.js +3 -3
  157. package/dist/run-control/store.js +1 -1
  158. package/dist/run-control/submit-result.js +21 -21
  159. package/dist/run-control/supervisor.js +77 -68
  160. package/dist/run-control/workspace.js +2 -2
  161. package/dist/run-state/git-refs.js +257 -307
  162. package/dist/runner/opencode-excludes.js +4 -4
  163. package/dist/runner/subprocess-result.js +28 -37
  164. package/dist/runner/subprocess.js +41 -41
  165. package/dist/runner/timeouts.js +14 -13
  166. package/dist/runner-command/dynamic-command.js +46 -39
  167. package/dist/runner-command/finalize.js +174 -153
  168. package/dist/runner-command/lifecycle-context.js +112 -130
  169. package/dist/runner-command/lifecycle.js +77 -71
  170. package/dist/runner-command/pre-schedule.js +201 -222
  171. package/dist/runner-command/run.js +432 -447
  172. package/dist/runner-command/schedule-source-options.js +8 -10
  173. package/dist/runner-command/select-ready-wave.js +38 -29
  174. package/dist/runner-command/task-descriptor.js +11 -17
  175. package/dist/runner-command-contract.d.ts +147 -156
  176. package/dist/runner-command-contract.js +401 -410
  177. package/dist/runner-event-schema.d.ts +364 -504
  178. package/dist/runner-event-schema.js +187 -175
  179. package/dist/runner-event-sink.js +37 -43
  180. package/dist/runner-output.js +6 -6
  181. package/dist/runner.d.ts +56 -36
  182. package/dist/runner.js +175 -127
  183. package/dist/runtime/actor-ids.js +2 -2
  184. package/dist/runtime/agent-node/agent-node.js +99 -111
  185. package/dist/runtime/agent-node/handoff-finalization.js +23 -24
  186. package/dist/runtime/agent-node/model-selection.js +19 -22
  187. package/dist/runtime/agent-node/output-finalization.js +174 -205
  188. package/dist/runtime/agent-node/prompt-rendering.js +207 -251
  189. package/dist/runtime/agent-node/session-execution.js +34 -38
  190. package/dist/runtime/builtins/builtins.js +212 -284
  191. package/dist/runtime/changed-files/changed-files.js +26 -34
  192. package/dist/runtime/command-executor/command-executor.js +31 -23
  193. package/dist/runtime/config-error.js +1 -3
  194. package/dist/runtime/context/context.js +35 -37
  195. package/dist/runtime/contracts/index.d.ts +1 -1
  196. package/dist/runtime/detached-race.js +11 -14
  197. package/dist/runtime/drain-merge/drain-merge.js +137 -163
  198. package/dist/runtime/durable-store/acquisition.js +5 -3
  199. package/dist/runtime/durable-store/durable-store.d.ts +1 -1
  200. package/dist/runtime/durable-store/postgres/migrate-substrate.js +36 -33
  201. package/dist/runtime/durable-store/postgres/postgres-store.js +17 -18
  202. package/dist/runtime/events/events.js +255 -323
  203. package/dist/runtime/gates/adjudication/llm-judge.js +21 -24
  204. package/dist/runtime/gates/adjudication/structured-claim.js +15 -16
  205. package/dist/runtime/gates/adjudicator/adjudicator.js +27 -27
  206. package/dist/runtime/gates/adjudicator/index.js +2 -2
  207. package/dist/runtime/gates/contract.js +5 -9
  208. package/dist/runtime/gates/gates.js +9 -8
  209. package/dist/runtime/gates/kinds/acceptance/acceptance.js +66 -70
  210. package/dist/runtime/gates/kinds/acceptance/index.js +2 -2
  211. package/dist/runtime/gates/kinds/artifact/artifact.js +4 -4
  212. package/dist/runtime/gates/kinds/artifact/index.js +2 -2
  213. package/dist/runtime/gates/kinds/builtin/builtin.js +4 -4
  214. package/dist/runtime/gates/kinds/builtin/index.js +2 -2
  215. package/dist/runtime/gates/kinds/changed-files/changed-files.js +31 -35
  216. package/dist/runtime/gates/kinds/changed-files/index.js +2 -2
  217. package/dist/runtime/gates/kinds/command/command.js +3 -3
  218. package/dist/runtime/gates/kinds/command/index.js +2 -2
  219. package/dist/runtime/gates/kinds/json-schema/index.js +2 -2
  220. package/dist/runtime/gates/kinds/json-schema/json-schema.js +7 -6
  221. package/dist/runtime/gates/kinds/verdict/index.js +2 -2
  222. package/dist/runtime/gates/kinds/verdict/verdict.js +3 -3
  223. package/dist/runtime/gates/orchestrator.js +72 -86
  224. package/dist/runtime/gates/registry.js +1 -3
  225. package/dist/runtime/handoff.d.ts +1 -1
  226. package/dist/runtime/handoff.js +43 -46
  227. package/dist/runtime/hooks/command-hook.js +57 -71
  228. package/dist/runtime/hooks/context.js +21 -25
  229. package/dist/runtime/hooks/events.js +84 -100
  230. package/dist/runtime/hooks/execution.js +6 -12
  231. package/dist/runtime/hooks/hooks.js +34 -23
  232. package/dist/runtime/hooks/invocation.js +23 -29
  233. package/dist/runtime/hooks/module-hook.js +35 -35
  234. package/dist/runtime/hooks/policy.js +32 -29
  235. package/dist/runtime/hooks/results.js +56 -66
  236. package/dist/runtime/journal-acquisition.d.ts +1 -1
  237. package/dist/runtime/journal-acquisition.js +14 -14
  238. package/dist/runtime/json-validation/json-validation.js +50 -72
  239. package/dist/runtime/local-scheduler.js +46 -48
  240. package/dist/runtime/node-execution.js +375 -446
  241. package/dist/runtime/node-protocol/node-protocol.js +36 -79
  242. package/dist/runtime/node-state-store.d.ts +1 -1
  243. package/dist/runtime/node-state-store.js +13 -57
  244. package/dist/runtime/node-state-tracker.js +46 -54
  245. package/dist/runtime/open-pull-request/open-pull-request.js +143 -187
  246. package/dist/runtime/opencode-adapter.js +23 -24
  247. package/dist/runtime/opencode-agent-name.js +6 -6
  248. package/dist/runtime/opencode-runtime.js +92 -92
  249. package/dist/runtime/opencode-server.js +39 -53
  250. package/dist/runtime/opencode-session-executor.js +315 -334
  251. package/dist/runtime/parallel-node/parallel-node.js +122 -134
  252. package/dist/runtime/parallel-worktrees/parallel-worktrees.js +90 -73
  253. package/dist/runtime/protected-paths/protected-paths.js +70 -71
  254. package/dist/runtime/remediation/remediation.js +202 -231
  255. package/dist/runtime/retry.js +8 -10
  256. package/dist/runtime/run-journal.js +7 -7
  257. package/dist/runtime/runtime-results.js +54 -69
  258. package/dist/runtime/scheduled-dependencies.d.ts +1 -1
  259. package/dist/runtime/scheduled-dependencies.js +59 -74
  260. package/dist/runtime/scheduler.js +177 -202
  261. package/dist/runtime/services/agent-node-runtime-service.js +3 -3
  262. package/dist/runtime/services/backlog-service.js +8 -14
  263. package/dist/runtime/services/command-executor-service.js +1 -1
  264. package/dist/runtime/services/config-io-service.js +24 -21
  265. package/dist/runtime/services/drain-merge-git-service.js +1 -1
  266. package/dist/runtime/services/file-system-service.js +9 -7
  267. package/dist/runtime/services/git-porcelain-service.js +6 -6
  268. package/dist/runtime/services/kubernetes-argo-service.d.ts +10 -2
  269. package/dist/runtime/services/kubernetes-argo-service.js +90 -84
  270. package/dist/runtime/services/mcp-gateway-service.js +82 -81
  271. package/dist/runtime/services/open-pull-request-git-service.js +1 -3
  272. package/dist/runtime/services/opencode-runtime-server-service.js +10 -12
  273. package/dist/runtime/services/opencode-sdk-service.js +7 -7
  274. package/dist/runtime/services/repo-io-service.js +10 -12
  275. package/dist/runtime/services/run-journal-file-service.js +2 -2
  276. package/dist/runtime/services/runner-command-io-service.js +48 -52
  277. package/dist/runtime/services/runner-event-sink-http-service.js +31 -39
  278. package/dist/runtime/step/step-node.js +15 -15
  279. package/dist/runtime/workflow-execution.js +55 -57
  280. package/dist/runtime/workflow-lifecycle.js +81 -101
  281. package/dist/safe-json.js +16 -16
  282. package/dist/schedule/backlog-context.js +120 -116
  283. package/dist/schedule/baseline.js +249 -255
  284. package/dist/schedule/passes/coverage.js +63 -68
  285. package/dist/schedule/passes/drain-merge.js +34 -40
  286. package/dist/schedule/passes/ids.js +15 -17
  287. package/dist/schedule/passes/models.js +40 -37
  288. package/dist/schedule/passes/open-pull-request.js +12 -18
  289. package/dist/schedule/passes/references.js +18 -22
  290. package/dist/schedule/prompts.js +134 -141
  291. package/dist/schedule/scheduling-roles.js +8 -14
  292. package/dist/schema-boundary.d.ts +1 -0
  293. package/dist/schema-boundary.js +145 -0
  294. package/dist/serialized-write-queue.js +2 -2
  295. package/dist/standard-output-schemas.js +61 -59
  296. package/dist/strings.js +12 -12
  297. package/dist/task-ref.js +5 -5
  298. package/dist/tickets/apply-ticket-plan.js +51 -61
  299. package/dist/tickets/backlog-task-store.d.ts +13 -22
  300. package/dist/tickets/backlog-task-store.js +80 -94
  301. package/dist/tickets/completion/complete-ticket.js +53 -59
  302. package/dist/tickets/ticket-graph-dto.d.ts +24 -47
  303. package/dist/tickets/ticket-graph-dto.js +40 -40
  304. package/dist/tickets/ticket-graph.d.ts +7 -1
  305. package/dist/tickets/ticket-graph.js +36 -50
  306. package/dist/tickets/ticket-plan-command-args.js +6 -10
  307. package/dist/tickets/ticket-plan-render.js +10 -16
  308. package/dist/tickets/ticket-plan.d.ts +31 -40
  309. package/dist/tickets/ticket-plan.js +97 -77
  310. package/dist/tickets/ticket-selection.d.ts +1 -1
  311. package/dist/tickets/ticket-selection.js +37 -52
  312. package/dist/tickets/ticket-task-index.js +13 -17
  313. package/dist/tickets/validation-error-format.js +3 -10
  314. package/dist/token-estimator.js +12 -7
  315. package/dist/unknown-fields.js +21 -0
  316. package/dist/workflow-submit-contract.d.ts +9 -9
  317. package/dist/workflow-submit-contract.js +10 -9
  318. package/docs/adr-opencode-first-goal-loop-runtime.md +11 -11
  319. package/docs/config-architecture.md +13 -13
  320. package/docs/mcp-host-isolation.md +1 -0
  321. package/docs/moka-orchestrator-design.md +3 -3
  322. package/docs/operator-guide.md +1 -1
  323. package/docs/runtime-actor-model.md +18 -18
  324. package/docs/slash-command-adapter-contract.md +3 -3
  325. package/package.json +73 -76
  326. package/dist/config/schema/reference-validation.js +0 -83
@@ -1,12 +1,14 @@
1
+ import { booleanValue, mutableArray, numberValue, parseJsonWithSchema, requiredString, struct } from "../schema-boundary.js";
1
2
  //#region src/bench/eval-report.ts
2
- function buildEvalReport(results) {
3
- const variants = [...new Set(results.map((r) => r.variant))].sort();
4
- return {
5
- tasks: new Set(results.map((r) => r.task)).size,
6
- variants: variants.map((variant) => summarizeVariant(variant, results.filter((r) => r.variant === variant)))
7
- };
8
- }
9
- function summarizeVariant(variant, runs) {
3
+ const evalRunResultsSchema = mutableArray(struct({
4
+ costTokens: numberValue(),
5
+ resolved: booleanValue(),
6
+ task: requiredString,
7
+ variant: requiredString,
8
+ wallMs: numberValue()
9
+ }));
10
+ const parseEvalRunResultsJson = (source) => parseJsonWithSchema(evalRunResultsSchema, source);
11
+ const summarizeVariant = (variant, runs) => {
10
12
  const resolved = runs.filter((r) => r.resolved).length;
11
13
  const totalWall = runs.reduce((sum, r) => sum + r.wallMs, 0);
12
14
  return {
@@ -17,11 +19,18 @@ function summarizeVariant(variant, runs) {
17
19
  totalCostTokens: runs.reduce((sum, r) => sum + r.costTokens, 0),
18
20
  variant
19
21
  };
20
- }
21
- function renderEvalReport(report) {
22
+ };
23
+ const buildEvalReport = (results) => {
24
+ const variants = [...new Set(results.map((r) => r.variant))].toSorted();
25
+ return {
26
+ tasks: new Set(results.map((r) => r.task)).size,
27
+ variants: variants.map((variant) => summarizeVariant(variant, results.filter((r) => r.variant === variant)))
28
+ };
29
+ };
30
+ const renderEvalReport = (report) => {
22
31
  const lines = [`Eval over ${report.tasks} task(s):`, "variant | resolved | rate | tokens | avg ms"];
23
32
  for (const v of report.variants) lines.push(`${v.variant} | ${v.resolved}/${v.count} | ${(v.resolutionRate * 100).toFixed(0)}% | ${v.totalCostTokens} | ${v.avgWallMs}`);
24
33
  return lines.join("\n");
25
- }
34
+ };
26
35
  //#endregion
27
- export { buildEvalReport, renderEvalReport };
36
+ export { buildEvalReport, parseEvalRunResultsJson, renderEvalReport };
@@ -1,7 +1,27 @@
1
1
  import { applyJsonEdit, ensureTrailingNewline, formatJson, isRecord, parseJsonRecord, setIfMissing } from "./json-config-merge.js";
2
2
  //#region src/claude-settings-config.ts
3
- function mergeClaudeSettings(currentText, projection) {
4
- if (currentText === void 0) return {
3
+ const applyMcpServersProjection = (content, parsed, projection) => Object.entries(projection.mcpServers ?? {}).reduce((nextContent, [name, server]) => setIfMissing(nextContent, parsed, ["mcpServers", name], server), content);
4
+ const projectedAllowList = (projection) => projection.permissions?.allow ?? [];
5
+ const unionPreservingOrder = (existing, extra) => {
6
+ const merged = [...existing];
7
+ for (const entry of extra) if (!merged.includes(entry)) merged.push(entry);
8
+ return merged;
9
+ };
10
+ const sameOrderedList = (a, b) => a.length === b.length && a.every((value, index) => value === b[index]);
11
+ const permissionAllowList = (parsed) => {
12
+ const { permissions } = parsed;
13
+ if (!isRecord(permissions)) return [];
14
+ const { allow } = permissions;
15
+ return Array.isArray(allow) ? allow.filter((entry) => typeof entry === "string") : [];
16
+ };
17
+ const applyPermissionsAllow = (content, parsed, projection) => {
18
+ const existing = permissionAllowList(parsed);
19
+ const merged = unionPreservingOrder(existing, projectedAllowList(projection));
20
+ return sameOrderedList(existing, merged) ? content : applyJsonEdit(content, ["permissions", "allow"], merged);
21
+ };
22
+ const applyClaudeProjection = (currentText, parsed, projection) => applyPermissionsAllow(applyMcpServersProjection(currentText, parsed, projection), parsed, projection);
23
+ const mergeClaudeSettings = (currentText = "", projection) => {
24
+ if (currentText === "") return {
5
25
  content: formatJson(projection),
6
26
  ok: true
7
27
  };
@@ -11,34 +31,6 @@ function mergeClaudeSettings(currentText, projection) {
11
31
  content: ensureTrailingNewline(applyClaudeProjection(currentText, parsed.value, projection)),
12
32
  ok: true
13
33
  };
14
- }
15
- function applyClaudeProjection(currentText, parsed, projection) {
16
- return applyPermissionsAllow(applyMcpServersProjection(currentText, parsed, projection), parsed, projection);
17
- }
18
- function applyMcpServersProjection(content, parsed, projection) {
19
- return Object.entries(projection.mcpServers ?? {}).reduce((nextContent, [name, server]) => setIfMissing(nextContent, parsed, ["mcpServers", name], server), content);
20
- }
21
- function applyPermissionsAllow(content, parsed, projection) {
22
- const existing = permissionAllowList(parsed);
23
- const merged = unionPreservingOrder(existing, projectedAllowList(projection));
24
- return sameOrderedList(existing, merged) ? content : applyJsonEdit(content, ["permissions", "allow"], merged);
25
- }
26
- function projectedAllowList(projection) {
27
- return projection.permissions?.allow ?? [];
28
- }
29
- function unionPreservingOrder(existing, extra) {
30
- const merged = [...existing];
31
- for (const entry of extra) if (!merged.includes(entry)) merged.push(entry);
32
- return merged;
33
- }
34
- function sameOrderedList(a, b) {
35
- return a.length === b.length && a.every((value, index) => value === b[index]);
36
- }
37
- function permissionAllowList(parsed) {
38
- const permissions = parsed.permissions;
39
- if (!isRecord(permissions)) return [];
40
- const allow = permissions.allow;
41
- return Array.isArray(allow) ? allow.filter((entry) => typeof entry === "string") : [];
42
- }
34
+ };
43
35
  //#endregion
44
36
  export { mergeClaudeSettings };
@@ -1,28 +1,18 @@
1
1
  import { applyJsonEdit, ensureTrailingNewline, formatJson, parseJsonRecord, setIfMissing } from "./json-config-merge.js";
2
2
  //#region src/claude-user-config.ts
3
- function mergeClaudeUserConfig(currentText, projection) {
4
- if (currentText === void 0) return {
3
+ const mergeClaudeUserConfigWith = (projection, project, currentText = "") => {
4
+ if (currentText === "") return {
5
5
  content: formatJson(projection),
6
6
  ok: true
7
7
  };
8
8
  const parsed = parseJsonRecord(currentText);
9
9
  if (!parsed.ok) return parsed;
10
10
  return {
11
- content: ensureTrailingNewline(Object.entries(projection.mcpServers ?? {}).reduce((nextContent, [name, server]) => setIfMissing(nextContent, parsed.value, ["mcpServers", name], server), currentText)),
11
+ content: ensureTrailingNewline(project(currentText, parsed.value, projection)),
12
12
  ok: true
13
13
  };
14
- }
15
- function replaceClaudeUserMcpServers(currentText, projection) {
16
- if (currentText === void 0) return {
17
- content: formatJson(projection),
18
- ok: true
19
- };
20
- const parsed = parseJsonRecord(currentText);
21
- if (!parsed.ok) return parsed;
22
- return {
23
- content: ensureTrailingNewline(applyJsonEdit(currentText, ["mcpServers"], projection.mcpServers ?? {})),
24
- ok: true
25
- };
26
- }
14
+ };
15
+ const mergeClaudeUserConfig = (projection, currentText = "") => mergeClaudeUserConfigWith(projection, (text, parsed, value) => Object.entries(value.mcpServers ?? {}).reduce((nextContent, [name, server]) => setIfMissing(nextContent, parsed, ["mcpServers", name], server), text), currentText);
16
+ const replaceClaudeUserMcpServers = (projection, currentText = "") => mergeClaudeUserConfigWith(projection, (text, _parsed, value) => applyJsonEdit(text, ["mcpServers"], value.mcpServers ?? {}), currentText);
27
17
  //#endregion
28
18
  export { mergeClaudeUserConfig, replaceClaudeUserMcpServers };
@@ -4,10 +4,10 @@ import { runDoctor } from "./doctor.js";
4
4
  import { formatDoctorResult } from "./format.js";
5
5
  import { resolve } from "node:path";
6
6
  //#region src/cli/bootstrap-commands.ts
7
- function registerBootstrapCommands(program) {
7
+ const registerBootstrapCommands = (program) => {
8
8
  program.command("doctor").description("Check local prerequisites for pipeline init and execution").option("--cluster [namespace]", "also check runner-job Kubernetes prerequisites").option("--json", "print machine-readable readiness results").option("--kube-context <context>", "kubectl context for cluster checks").option("--kubeconfig <path>", "kubeconfig path for cluster checks").action(async (flags) => {
9
9
  const result = await runDoctor(process.env.PIPELINE_TARGET_PATH ?? process.cwd(), flags);
10
- console.log(flags.json ? JSON.stringify(result) : formatDoctorResult(result));
10
+ console.log(flags.json === true ? JSON.stringify(result) : formatDoctorResult(result));
11
11
  if (!result.passed) throw new Error("Doctor checks failed.");
12
12
  });
13
13
  program.command("init").description([
@@ -34,6 +34,6 @@ function registerBootstrapCommands(program) {
34
34
  console.log(formatCodexAuthSyncResult(result));
35
35
  if (!result.ok) process.exitCode = 1;
36
36
  });
37
- }
37
+ };
38
38
  //#endregion
39
39
  export { registerBootstrapCommands };
@@ -16,6 +16,6 @@ interface DoctorFlags {
16
16
  kubeContext?: string;
17
17
  kubeconfig?: string;
18
18
  }
19
- declare function runDoctor(cwd: string, options?: DoctorFlags): Promise<DoctorResult>;
19
+ declare const runDoctor: (cwd: string, options?: DoctorFlags) => Promise<DoctorResult>;
20
20
  //#endregion
21
21
  export { DoctorCheck, DoctorFlags, DoctorResult, runDoctor };
@@ -2,6 +2,7 @@ import { PipelineConfigError } from "../config/schemas.js";
2
2
  import { loadPipelineConfig } from "../config/load.js";
3
3
  import "../config.js";
4
4
  import { loadMokaGlobalConfig } from "../moka-global-config.js";
5
+ import { isRecord } from "../safe-json.js";
5
6
  import { opencodeAgentName } from "../runtime/opencode-agent-name.js";
6
7
  import { defaultClusterDoctorNamespace, runClusterDoctor } from "../cluster-doctor.js";
7
8
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
@@ -23,50 +24,123 @@ const OPENCODE_AGENT_LIST_ARGS = [
23
24
  "list",
24
25
  "--json"
25
26
  ];
26
- const BULLET_PREFIX_RE = /^[-*]\s+/;
27
- const LINE_RE = /\r?\n/;
28
- async function runDoctor(cwd, options = {}) {
29
- const commandChecks = await Promise.all([
30
- checkCommand("npx", ["--version"], cwd),
31
- checkCommand("opencode", ["--version"], cwd),
32
- checkCommand("fallow", ["--version"], cwd)
33
- ]);
34
- const configCheck = checkPipelineConfig(cwd);
35
- const config = configCheck.passed ? loadPipelineConfig(cwd) : null;
36
- const [sdkCheck, agentVisibility] = await Promise.all([checkOpenCodeSdk(), config ? checkMokaAgents(cwd, config) : Promise.resolve({ check: {
37
- detail: "skipped because pipeline config is invalid",
27
+ const BULLET_PREFIX_RE = /^[-*]\s+/u;
28
+ const LINE_RE = /\r?\n/u;
29
+ const clusterNamespace = (value, configuredNamespace) => typeof value === "string" && value.length > 0 ? value : configuredNamespace ?? defaultClusterDoctorNamespace();
30
+ const checkPipelineConfig = (cwd) => {
31
+ try {
32
+ loadPipelineConfig(cwd);
33
+ return {
34
+ detail: "valid",
35
+ name: "pipeline-config",
36
+ passed: true
37
+ };
38
+ } catch (error) {
39
+ let message = "invalid";
40
+ if (error instanceof PipelineConfigError) message = error.issues.map((issue) => issue.message).join("; ");
41
+ else if (error instanceof Error) message = error.message;
42
+ return {
43
+ detail: message || "missing or invalid",
44
+ name: "pipeline-config",
45
+ passed: false
46
+ };
47
+ }
48
+ };
49
+ const skippedAgentVisibility = (detail) => ({
50
+ check: {
51
+ detail: "skipped because OpenCode agent listing is unavailable",
38
52
  name: "moka-agents",
39
53
  passed: true
40
- } })]);
41
- const globalConfig = loadMokaGlobalConfig();
42
- const clusterResult = options.cluster ? await runClusterDoctor({
43
- kubeContext: options.kubeContext,
44
- kubeconfigPath: options.kubeconfig ?? globalConfig?.momokaya.kubernetes.kubeconfig,
45
- namespace: clusterNamespace(options.cluster, globalConfig?.momokaya.kubernetes.namespace)
46
- }) : { checks: [] };
47
- const checks = [
48
- ...commandChecks,
49
- configCheck,
50
- sdkCheck,
51
- agentVisibility.check,
52
- ...clusterResult.checks
53
- ];
54
- const warnings = [...agentVisibility.warning ? [agentVisibility.warning] : [], ...headlessPermissionWarnings(cwd)];
55
- const blockers = checks.filter((check) => !check.passed);
54
+ },
55
+ warning: {
56
+ detail,
57
+ name: "moka-agents",
58
+ passed: true
59
+ }
60
+ });
61
+ const expectedRunAgentNames = (config) => {
62
+ const profiles = /* @__PURE__ */ new Set();
63
+ for (const catalog of Object.values(config.scheduler.node_catalogs)) for (const node of Object.values(catalog.nodes)) if (RUN_READINESS_CATEGORIES.has(node.category)) profiles.add(node.profile);
64
+ return [...profiles].filter((profileId) => config.profiles[profileId]?.runner === "opencode").map(opencodeAgentName).toSorted((a, b) => a.localeCompare(b));
65
+ };
66
+ const collectAgentNames = (value, names, inAgentList) => {
67
+ if (typeof value === "string") {
68
+ if (inAgentList) names.add(value);
69
+ return inAgentList;
70
+ }
71
+ if (Array.isArray(value)) {
72
+ let recognized = inAgentList;
73
+ for (const item of value) recognized = collectAgentNames(item, names, inAgentList) || recognized;
74
+ return recognized;
75
+ }
76
+ if (!isRecord(value)) return false;
77
+ let recognized = false;
78
+ for (const key of [
79
+ "agent",
80
+ "id",
81
+ "name",
82
+ "subagent_type",
83
+ "title"
84
+ ]) if (typeof value[key] === "string") {
85
+ names.add(value[key]);
86
+ recognized = true;
87
+ }
88
+ for (const key of [
89
+ "agents",
90
+ "data",
91
+ "items",
92
+ "result"
93
+ ]) {
94
+ const item = value[key];
95
+ if (Array.isArray(item) || typeof item === "object" && item !== null) recognized = collectAgentNames(item, names, true) || recognized;
96
+ }
97
+ return recognized;
98
+ };
99
+ const visibleAgentNames = (stdout) => {
100
+ const names = /* @__PURE__ */ new Set();
101
+ try {
102
+ const parsed = JSON.parse(stdout);
103
+ return {
104
+ ambiguous: false,
105
+ names,
106
+ recognized: collectAgentNames(parsed, names, Array.isArray(parsed))
107
+ };
108
+ } catch {
109
+ for (const line of stdout.split(LINE_RE)) {
110
+ const name = line.trim().replace(BULLET_PREFIX_RE, "");
111
+ if (name !== "") names.add(name);
112
+ }
113
+ }
56
114
  return {
57
- blockers,
58
- checks,
59
- passed: blockers.length === 0,
60
- warnings
115
+ ambiguous: true,
116
+ names,
117
+ recognized: names.size > 0
61
118
  };
62
- }
63
- function clusterNamespace(value, configuredNamespace) {
64
- return typeof value === "string" && value.length > 0 ? value : configuredNamespace ?? defaultClusterDoctorNamespace();
65
- }
66
- function checkCommand(name, args, cwd) {
67
- return checkCommandWithRunner(name, name, args, cwd);
68
- }
69
- async function checkCommandWithRunner(name, command, args, cwd) {
119
+ };
120
+ const interactivePermissionPaths = (value, path = ["permission"]) => {
121
+ if (typeof value === "string") return HEADLESS_AGENT_PERMISSION_VALUES.has(value.toLowerCase()) ? [path.join(".")] : [];
122
+ if (Array.isArray(value)) return value.flatMap((item, index) => interactivePermissionPaths(item, [...path, String(index)]));
123
+ if (!isRecord(value)) return [];
124
+ return Object.entries(value).flatMap(([key, item]) => interactivePermissionPaths(item, [...path, key]));
125
+ };
126
+ const isHeadless = () => {
127
+ const ci = process.env.CI?.toLowerCase();
128
+ return ci !== void 0 && ci !== "" && ci !== "0" && ci !== "false" || !process.stdin.isTTY;
129
+ };
130
+ const stringField = (value, field) => {
131
+ if (typeof value !== "object" || value === null) return "";
132
+ const candidate = Reflect.get(value, field);
133
+ return typeof candidate === "string" ? candidate : "";
134
+ };
135
+ const commandErrorDetail = (err) => {
136
+ return ([
137
+ stringField(err, "shortMessage"),
138
+ stringField(err, "stderr"),
139
+ stringField(err, "message"),
140
+ String(err)
141
+ ].find((candidate) => candidate.trim() !== "") ?? "not available").trim();
142
+ };
143
+ const checkCommandWithRunner = async (name, command, args, cwd) => {
70
144
  try {
71
145
  await execa(command, args, {
72
146
  cwd,
@@ -77,34 +151,16 @@ async function checkCommandWithRunner(name, command, args, cwd) {
77
151
  name,
78
152
  passed: true
79
153
  };
80
- } catch (err) {
154
+ } catch (error) {
81
155
  return {
82
- detail: commandErrorDetail(err),
156
+ detail: commandErrorDetail(error),
83
157
  name,
84
158
  passed: false
85
159
  };
86
160
  }
87
- }
88
- function checkPipelineConfig(cwd) {
89
- try {
90
- loadPipelineConfig(cwd);
91
- return {
92
- detail: "valid",
93
- name: "pipeline-config",
94
- passed: true
95
- };
96
- } catch (err) {
97
- let message = "invalid";
98
- if (err instanceof PipelineConfigError) message = err.issues.map((issue) => issue.message).join("; ");
99
- else if (err instanceof Error) message = err.message;
100
- return {
101
- detail: message || "missing or invalid",
102
- name: "pipeline-config",
103
- passed: false
104
- };
105
- }
106
- }
107
- async function checkOpenCodeSdk() {
161
+ };
162
+ const checkCommand = async (name, args, cwd) => await checkCommandWithRunner(name, name, args, cwd);
163
+ const checkOpenCodeSdk = async () => {
108
164
  try {
109
165
  if (typeof (await import("@opencode-ai/sdk")).createOpencodeClient !== "function") return {
110
166
  detail: "@opencode-ai/sdk does not export createOpencodeClient",
@@ -116,15 +172,15 @@ async function checkOpenCodeSdk() {
116
172
  name: "opencode-sdk",
117
173
  passed: true
118
174
  };
119
- } catch (err) {
175
+ } catch (error) {
120
176
  return {
121
- detail: commandErrorDetail(err),
177
+ detail: commandErrorDetail(error),
122
178
  name: "opencode-sdk",
123
179
  passed: false
124
180
  };
125
181
  }
126
- }
127
- async function checkMokaAgents(cwd, config) {
182
+ };
183
+ const checkMokaAgents = async (cwd, config) => {
128
184
  const expected = expectedRunAgentNames(config);
129
185
  if (expected.length === 0) return { check: {
130
186
  detail: "no configured MoKa run agents",
@@ -148,121 +204,68 @@ async function checkMokaAgents(cwd, config) {
148
204
  name: "moka-agents",
149
205
  passed: true
150
206
  } };
151
- } catch (err) {
152
- return skippedAgentVisibility(`Could not cheaply list OpenCode agents: ${commandErrorDetail(err)}`);
207
+ } catch (error) {
208
+ return skippedAgentVisibility(`Could not cheaply list OpenCode agents: ${commandErrorDetail(error)}`);
153
209
  }
154
- }
155
- function skippedAgentVisibility(detail) {
156
- return {
157
- check: {
158
- detail: "skipped because OpenCode agent listing is unavailable",
159
- name: "moka-agents",
160
- passed: true
161
- },
162
- warning: {
163
- detail,
164
- name: "moka-agents",
165
- passed: true
166
- }
167
- };
168
- }
169
- function expectedRunAgentNames(config) {
170
- const profiles = /* @__PURE__ */ new Set();
171
- for (const catalog of Object.values(config.scheduler.node_catalogs)) for (const node of Object.values(catalog.nodes)) if (RUN_READINESS_CATEGORIES.has(node.category)) profiles.add(node.profile);
172
- return [...profiles].filter((profileId) => config.profiles[profileId]?.runner === "opencode").map(opencodeAgentName).sort((a, b) => a.localeCompare(b));
173
- }
174
- function visibleAgentNames(stdout) {
175
- const names = /* @__PURE__ */ new Set();
176
- try {
177
- const parsed = JSON.parse(stdout);
178
- return {
179
- ambiguous: false,
180
- names,
181
- recognized: collectAgentNames(parsed, names, Array.isArray(parsed))
182
- };
183
- } catch {
184
- for (const line of stdout.split(LINE_RE)) {
185
- const name = line.trim().replace(BULLET_PREFIX_RE, "");
186
- if (name) names.add(name);
187
- }
188
- }
189
- return {
190
- ambiguous: true,
191
- names,
192
- recognized: names.size > 0
193
- };
194
- }
195
- function collectAgentNames(value, names, inAgentList) {
196
- if (typeof value === "string") {
197
- if (inAgentList) names.add(value);
198
- return inAgentList;
199
- }
200
- if (Array.isArray(value)) {
201
- let recognized = inAgentList;
202
- for (const item of value) recognized = collectAgentNames(item, names, inAgentList) || recognized;
203
- return recognized;
204
- }
205
- if (!(value && typeof value === "object")) return false;
206
- const record = value;
207
- let recognized = false;
208
- for (const key of [
209
- "agent",
210
- "id",
211
- "name",
212
- "subagent_type",
213
- "title"
214
- ]) if (typeof record[key] === "string") {
215
- names.add(record[key]);
216
- recognized = true;
217
- }
218
- for (const key of [
219
- "agents",
220
- "data",
221
- "items",
222
- "result"
223
- ]) {
224
- const item = record[key];
225
- if (Array.isArray(item) || item && typeof item === "object") recognized = collectAgentNames(item, names, true) || recognized;
226
- }
227
- return recognized;
228
- }
229
- function headlessPermissionWarnings(cwd) {
230
- if (!isHeadless()) return [];
231
- const agentDir = join(cwd, ".opencode", "agents");
232
- if (!existsSync(agentDir)) return [];
233
- return readdirSync(agentDir).filter((entry) => entry.endsWith(".md")).flatMap((entry) => headlessPermissionWarning(join(agentDir, entry), entry));
234
- }
235
- function headlessPermissionWarning(path, entry) {
210
+ };
211
+ const headlessPermissionWarning = (path, entry) => {
236
212
  try {
237
213
  if (!statSync(path).isFile()) return [];
238
- const risky = interactivePermissionPaths(matter(readFileSync(path, "utf8")).data.permission);
214
+ const risky = interactivePermissionPaths(matter(readFileSync(path, "utf-8")).data.permission);
239
215
  if (risky.length === 0) return [];
240
216
  return [{
241
217
  detail: `${entry} requires interactive permission prompts at ${risky.join(", ")}; headless MoKa runs may block.`,
242
218
  name: "headless-permissions",
243
219
  passed: true
244
220
  }];
245
- } catch (err) {
221
+ } catch (error) {
246
222
  return [{
247
- detail: `Could not inspect ${entry} for headless permission risks: ${commandErrorDetail(err)}`,
223
+ detail: `Could not inspect ${entry} for headless permission risks: ${commandErrorDetail(error)}`,
248
224
  name: "headless-permissions",
249
225
  passed: true
250
226
  }];
251
227
  }
252
- }
253
- function interactivePermissionPaths(value, path = ["permission"]) {
254
- if (typeof value === "string") return HEADLESS_AGENT_PERMISSION_VALUES.has(value.toLowerCase()) ? [path.join(".")] : [];
255
- if (Array.isArray(value)) return value.flatMap((item, index) => interactivePermissionPaths(item, [...path, String(index)]));
256
- if (!(value && typeof value === "object")) return [];
257
- return Object.entries(value).flatMap(([key, item]) => interactivePermissionPaths(item, [...path, key]));
258
- }
259
- function isHeadless() {
260
- const ci = process.env.CI?.toLowerCase();
261
- return ci !== void 0 && ci !== "" && ci !== "0" && ci !== "false" || !process.stdin.isTTY;
262
- }
263
- function commandErrorDetail(err) {
264
- const error = err;
265
- return (error.shortMessage || error.stderr || error.message || String(err)).trim() || "not available";
266
- }
228
+ };
229
+ const headlessPermissionWarnings = (cwd) => {
230
+ if (!isHeadless()) return [];
231
+ const agentDir = join(cwd, ".opencode", "agents");
232
+ if (!existsSync(agentDir)) return [];
233
+ return readdirSync(agentDir).filter((entry) => entry.endsWith(".md")).flatMap((entry) => headlessPermissionWarning(join(agentDir, entry), entry));
234
+ };
235
+ const runDoctor = async (cwd, options = {}) => {
236
+ const commandChecks = await Promise.all([
237
+ checkCommand("npx", ["--version"], cwd),
238
+ checkCommand("opencode", ["--version"], cwd),
239
+ checkCommand("fallow", ["--version"], cwd)
240
+ ]);
241
+ const configCheck = checkPipelineConfig(cwd);
242
+ const config = configCheck.passed ? loadPipelineConfig(cwd) : null;
243
+ const [sdkCheck, agentVisibility] = await Promise.all([checkOpenCodeSdk(), config ? checkMokaAgents(cwd, config) : Promise.resolve({ check: {
244
+ detail: "skipped because pipeline config is invalid",
245
+ name: "moka-agents",
246
+ passed: true
247
+ } })]);
248
+ const globalConfig = loadMokaGlobalConfig();
249
+ const clusterResult = options.cluster !== void 0 && options.cluster !== false ? await runClusterDoctor({
250
+ kubeContext: options.kubeContext,
251
+ kubeconfigPath: options.kubeconfig ?? (globalConfig === null ? void 0 : globalConfig.momokaya.kubernetes.kubeconfig),
252
+ namespace: clusterNamespace(options.cluster, globalConfig === null ? void 0 : globalConfig.momokaya.kubernetes.namespace)
253
+ }) : { checks: [] };
254
+ const checks = [
255
+ ...commandChecks,
256
+ configCheck,
257
+ sdkCheck,
258
+ agentVisibility.check,
259
+ ...clusterResult.checks
260
+ ];
261
+ const warnings = [...agentVisibility.warning ? [agentVisibility.warning] : [], ...headlessPermissionWarnings(cwd)];
262
+ const blockers = checks.filter((check) => !check.passed);
263
+ return {
264
+ blockers,
265
+ checks,
266
+ passed: blockers.length === 0,
267
+ warnings
268
+ };
269
+ };
267
270
  //#endregion
268
271
  export { runDoctor };
@@ -2,32 +2,33 @@ import { runCreateExperiment } from "../factory/create-experiment.js";
2
2
  import { runTemplateUpdate, summarizeTemplateUpdate } from "../factory/template-update.js";
3
3
  import { Option } from "commander";
4
4
  //#region src/cli/factory-commands.ts
5
- function registerFactoryCommands(program) {
5
+ const hasText = (value) => typeof value === "string" && value !== "";
6
+ const registerFactoryCommands = (program) => {
6
7
  program.command("create-experiment").description("Birth a fleet experiment: copier-stamp momokaya-template, create+push the org repo, register it in infra's fleet registry").requiredOption("--name <name>", "app name (kebab-case)").addOption(new Option("--flavor <flavor>", "app flavor").choices(["web", "expo-web"]).default("web")).option("--no-db", "skip the database surface").option("--no-previews", "skip per-PR preview environments").option("--org <org>", "GitHub org for the new repo").option("--template-src <source>", "copier template source").option("--template-ref <ref>", "template tag/ref (default: latest tag)").option("--infra-repo-url <url>", "infra repo the registry entry lands in").action(async (flags) => {
7
8
  const result = await runCreateExperiment({
8
9
  db: flags.db,
9
10
  flavor: flags.flavor,
10
- ...flags.infraRepoUrl ? { infraRepoUrl: flags.infraRepoUrl } : {},
11
+ ...hasText(flags.infraRepoUrl) ? { infraRepoUrl: flags.infraRepoUrl } : {},
11
12
  name: flags.name,
12
- ...flags.org ? { org: flags.org } : {},
13
+ ...hasText(flags.org) ? { org: flags.org } : {},
13
14
  previews: flags.previews,
14
- ...flags.templateRef ? { templateRef: flags.templateRef } : {},
15
- ...flags.templateSrc ? { templateSource: flags.templateSrc } : {}
15
+ ...hasText(flags.templateRef) ? { templateRef: flags.templateRef } : {},
16
+ ...hasText(flags.templateSrc) ? { templateSource: flags.templateSrc } : {}
16
17
  });
17
18
  console.log(`Experiment born: ${result.repoUrl} (registry ${result.registryPath} @ infra ${result.infraCommitSha})`);
18
19
  });
19
20
  program.command("template-update").description("Fan copier-update PRs out across repos stamped from momokaya-template").option("--repos <repos>", "comma-separated repo list (skips fleet-registry discovery)").option("--org <org>", "GitHub org the stamped repos live in").option("--template-match <substring>", "answers-file _src_path filter for stamp detection").option("--template-ref <ref>", "template tag/ref (default: latest tag)").option("--infra-repo-url <url>", "infra repo used for discovery").action(async (flags) => {
20
21
  const { results } = await runTemplateUpdate({
21
- ...flags.infraRepoUrl ? { infraRepoUrl: flags.infraRepoUrl } : {},
22
- ...flags.org ? { org: flags.org } : {},
23
- ...flags.repos ? { repos: flags.repos.split(",").map((repo) => repo.trim()).filter((repo) => repo.length > 0) } : {},
24
- ...flags.templateMatch ? { templateMatch: flags.templateMatch } : {},
25
- ...flags.templateRef ? { templateRef: flags.templateRef } : {}
22
+ ...hasText(flags.infraRepoUrl) ? { infraRepoUrl: flags.infraRepoUrl } : {},
23
+ ...hasText(flags.org) ? { org: flags.org } : {},
24
+ ...hasText(flags.repos) ? { repos: flags.repos.split(",").map((repo) => repo.trim()).filter((repo) => repo.length > 0) } : {},
25
+ ...hasText(flags.templateMatch) ? { templateMatch: flags.templateMatch } : {},
26
+ ...hasText(flags.templateRef) ? { templateRef: flags.templateRef } : {}
26
27
  });
27
28
  const { failed, opened } = summarizeTemplateUpdate(results);
28
29
  console.log(`template-update: ${opened} PR(s) opened, ${failed} error(s)`);
29
30
  if (failed > 0) process.exitCode = 1;
30
31
  });
31
- }
32
+ };
32
33
  //#endregion
33
34
  export { registerFactoryCommands };