@oisincoveney/pipeline 3.15.6 → 3.17.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 (59) hide show
  1. package/dist/argo-submit.d.ts +9 -5
  2. package/dist/argo-submit.js +5 -2
  3. package/dist/cli/program.js +1 -1
  4. package/dist/cli/run-commands.js +1 -1
  5. package/dist/cli/run-service.js +22 -21
  6. package/dist/cli/submit-options.js +1 -1
  7. package/dist/config/defaults.d.ts +5 -5
  8. package/dist/config/schemas.d.ts +30 -30
  9. package/dist/install-commands/shared.js +1 -1
  10. package/dist/install-hooks.js +22 -8
  11. package/dist/install-rules.js +119 -40
  12. package/dist/moka-global-config.d.ts +11 -6
  13. package/dist/moka-global-config.js +25 -6
  14. package/dist/moka-submit.d.ts +62 -42
  15. package/dist/moka-submit.js +2 -0
  16. package/dist/pipeline-init.js +53 -6
  17. package/dist/pipeline-runtime.d.ts +40 -1
  18. package/dist/pipeline-runtime.js +60 -1
  19. package/dist/planning/generate.d.ts +283 -278
  20. package/dist/planning/generate.js +10 -3
  21. package/dist/remote/argo/model.d.ts +4 -0
  22. package/dist/remote/argo/model.js +12 -1
  23. package/dist/remote/argo/policy.js +8 -1
  24. package/dist/remote/submit/argo-submission.d.ts +4 -0
  25. package/dist/remote/submit/argo-submission.js +1 -0
  26. package/dist/remote/submit/compilation.d.ts +14 -3
  27. package/dist/remote/submit/compilation.js +3 -4
  28. package/dist/remote/submit/service.d.ts +12 -1
  29. package/dist/remote/submit/service.js +46 -6
  30. package/dist/run-control/contracts.d.ts +87 -2
  31. package/dist/run-control/logical-segment.js +2 -8
  32. package/dist/run-control/next-node.js +45 -49
  33. package/dist/run-control/postgres/postgres-run-control-store.js +4 -6
  34. package/dist/run-control/resume-command.js +42 -3
  35. package/dist/run-control/run-control-store.js +11 -35
  36. package/dist/run-control/run-record.js +35 -0
  37. package/dist/run-control/runtime-reporter.js +7 -4
  38. package/dist/run-control/store-fs-effects.js +3 -48
  39. package/dist/run-control/store-manifest.js +5 -72
  40. package/dist/run-control/store-paths.js +4 -28
  41. package/dist/run-control/store.js +9 -165
  42. package/dist/run-control/submit-result.js +6 -5
  43. package/dist/run-control/supervisor.js +1 -2
  44. package/dist/runner-command/lifecycle-context.js +7 -3
  45. package/dist/runner-command/lifecycle.js +56 -2
  46. package/dist/runner-command/run.js +165 -3
  47. package/dist/runner-event-schema.d.ts +24 -24
  48. package/dist/runtime/durable-store/acquisition.js +2 -3
  49. package/dist/runtime/durable-store/durable-store.d.ts +1 -0
  50. package/dist/runtime/durable-store/postgres/postgres-store.js +4 -14
  51. package/dist/runtime/journal-acquisition.js +1 -1
  52. package/dist/runtime/run-journal.js +23 -0
  53. package/dist/runtime/services/run-journal-file-service.js +20 -0
  54. package/dist/runtime/step/step-node.js +53 -0
  55. package/docs/moka-orchestrator-design.md +35 -4
  56. package/docs/operator-guide.md +50 -22
  57. package/docs/run-control.md +40 -0
  58. package/package.json +19 -6
  59. package/dist/runtime/durable-store/durable-store.js +0 -53
@@ -5,6 +5,15 @@ import { z } from "zod";
5
5
 
6
6
  //#region src/argo-submit.d.ts
7
7
  declare const submitRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
8
+ brokerAuth: z.ZodObject<{
9
+ secretKey: z.ZodDefault<z.ZodString>;
10
+ secretName: z.ZodString;
11
+ url: z.ZodDefault<z.ZodString>;
12
+ }, z.core.$strict>;
13
+ dbAuth: z.ZodOptional<z.ZodObject<{
14
+ secretKey: z.ZodDefault<z.ZodString>;
15
+ secretName: z.ZodString;
16
+ }, z.core.$strict>>;
8
17
  eventAuthSecretKey: z.ZodOptional<z.ZodString>;
9
18
  eventAuthSecretName: z.ZodOptional<z.ZodString>;
10
19
  generateName: z.ZodOptional<z.ZodString>;
@@ -20,11 +29,6 @@ declare const submitRunnerArgoWorkflowOptionsSchema: z.ZodObject<{
20
29
  kubeconfigPath: z.ZodOptional<z.ZodString>;
21
30
  name: z.ZodOptional<z.ZodString>;
22
31
  namespace: z.ZodString;
23
- brokerAuth: z.ZodObject<{
24
- secretKey: z.ZodDefault<z.ZodString>;
25
- secretName: z.ZodString;
26
- url: z.ZodDefault<z.ZodString>;
27
- }, z.core.$strict>;
28
32
  payloadJson: z.ZodString;
29
33
  scheduleYaml: z.ZodString;
30
34
  serviceAccountName: z.ZodOptional<z.ZodString>;
@@ -1,4 +1,5 @@
1
1
  import { ArgoGraphCompilerError, compileArgoExecutionGraph } from "./argo-graph.js";
2
+ import { dbAuthOptionSchema } from "./remote/argo/model.js";
2
3
  import { brokerAuthOptionSchema } from "./credentials/broker.js";
3
4
  import { compileScheduleArtifact, parseScheduleArtifact } from "./planning/generate.js";
4
5
  import { parseRunnerCommandPayload, runnerCommandPayloadSchema } from "./runner-command-contract.js";
@@ -24,6 +25,8 @@ const configMapSchema = z.object({
24
25
  }).strict()
25
26
  }).strict();
26
27
  const submitRunnerArgoWorkflowOptionsSchema = z.object({
28
+ brokerAuth: brokerAuthOptionSchema,
29
+ dbAuth: dbAuthOptionSchema.optional(),
27
30
  eventAuthSecretKey: z.string().min(1).optional(),
28
31
  eventAuthSecretName: z.string().min(1).optional(),
29
32
  generateName: z.string().min(1).optional(),
@@ -39,7 +42,6 @@ const submitRunnerArgoWorkflowOptionsSchema = z.object({
39
42
  kubeconfigPath: z.string().min(1).optional(),
40
43
  name: z.string().min(1).optional(),
41
44
  namespace: z.string().min(1),
42
- brokerAuth: brokerAuthOptionSchema,
43
45
  payloadJson: z.string().min(1),
44
46
  scheduleYaml: z.string().min(1),
45
47
  serviceAccountName: z.string().min(1).optional()
@@ -78,6 +80,8 @@ function submitRunnerArgoWorkflowEffect(rawOptions, dependencies) {
78
80
  "pipeline.oisin.dev/ticket-project": payload.run.project,
79
81
  "pipeline.oisin.dev/ticket-title": payload.task.title
80
82
  } : {},
83
+ brokerAuth: options.brokerAuth,
84
+ dbAuth: options.dbAuth,
81
85
  eventAuthSecretKey: options.eventAuthSecretKey,
82
86
  eventAuthSecretName: options.eventAuthSecretName,
83
87
  generateName: options.generateName,
@@ -89,7 +93,6 @@ function submitRunnerArgoWorkflowEffect(rawOptions, dependencies) {
89
93
  labels,
90
94
  name: options.name,
91
95
  namespace: options.namespace,
92
- brokerAuth: options.brokerAuth,
93
96
  payloadConfigMapName,
94
97
  plan: compiled.plan,
95
98
  scheduleConfigMapName: scheduleArtifactConfigMapName,
@@ -4,13 +4,13 @@ import { registerBenchCommand } from "../commands/bench-command.js";
4
4
  import { registerConfiguredEntrypointCommands } from "../commands/pipeline-command.js";
5
5
  import { registerRunnerCommandCommand } from "../commands/runner-command-command.js";
6
6
  import { registerTicketCommand } from "../commands/ticket-command.js";
7
+ import { addMokaSubmitOptions, runMokaSubmitFromCli } from "./submit-options.js";
7
8
  import { registerRunControlCommands } from "../run-control/commands.js";
8
9
  import { registerBootstrapCommands } from "./bootstrap-commands.js";
9
10
  import { registerLoopCommand } from "./loop-commands.js";
10
11
  import { registerMcpGatewayCommands } from "./mcp-gateway-commands.js";
11
12
  import { registerPlanCommands } from "./plan-commands.js";
12
13
  import { execute } from "./run-service.js";
13
- import { addMokaSubmitOptions, runMokaSubmitFromCli } from "./submit-options.js";
14
14
  import { printMokaSubmitResult, registerRunCommands } from "./run-commands.js";
15
15
  import { Effect } from "effect";
16
16
  import { readFileSync } from "node:fs";
@@ -1,7 +1,7 @@
1
1
  import { MOKA_RUN_EFFORTS, MOKA_RUN_TARGETS, resolveMokaRun } from "./run-resolver.js";
2
+ import { runMokaSubmitFromCli } from "./submit-options.js";
2
3
  import { dispatchMokaRunCommand } from "./run-command.js";
3
4
  import { execute, runDetachedResolvedTask, runLocalResolvedTask } from "./run-service.js";
4
- import { runMokaSubmitFromCli } from "./submit-options.js";
5
5
  import { Option } from "commander";
6
6
  //#region src/cli/run-commands.ts
7
7
  function registerRunCommands(program, options = {}) {
@@ -1,15 +1,15 @@
1
1
  import { flattenNodes } from "../planning/graph.js";
2
2
  import { loadPipelineConfig } from "../config/load.js";
3
3
  import "../config.js";
4
+ import { withRunControlStoreScoped } from "../run-control/run-control-store.js";
4
5
  import { compileWorkflowPlan } from "../planning/compile.js";
5
6
  import { generateRuntimeRunId, resolveWorkflowSelection } from "../runtime/context/context.js";
6
7
  import "../runtime/context/index.js";
7
- import { compileScheduleArtifact, generateScheduleArtifact, parseScheduleArtifact } from "../planning/generate.js";
8
- import { withRunControlStoreScoped } from "../run-control/run-control-store.js";
8
+ import { compileScheduleArtifact, generateScheduleArtifactInMemory, parseScheduleArtifact } from "../planning/generate.js";
9
9
  import { runPipelineFromConfig } from "../pipeline-runtime.js";
10
+ import { createRunStoreRuntimeReporter } from "../run-control/runtime-reporter.js";
10
11
  import { createTerminalRuntimeReporter, formatRuntimeFailure, formatRuntimeResult } from "./format.js";
11
12
  import { startDetachedRunController } from "../run-control/detach.js";
12
- import { createRunStoreRuntimeReporter } from "../run-control/runtime-reporter.js";
13
13
  import { createRunControlSupervisor } from "../run-control/supervisor.js";
14
14
  import { Effect } from "effect";
15
15
  import { readFileSync } from "node:fs";
@@ -82,14 +82,7 @@ function persistDetachedRunController(input) {
82
82
  yield* store.createRun(detachedRunRecord(input));
83
83
  const launch = yield* Effect.tryPromise({
84
84
  catch: (error) => error,
85
- try: () => startDetachedRunController({
86
- entrypoint: input.prepared.entrypoint,
87
- runId: input.runId,
88
- schedule: input.prepared.schedule,
89
- task: input.task,
90
- workflow: input.prepared.workflow,
91
- workspaceRoot: input.worktreePath
92
- })
85
+ try: () => startDetachedRunController(detachedRunControllerInput(input))
93
86
  });
94
87
  yield* store.updateRunController({
95
88
  controller: {
@@ -103,6 +96,16 @@ function persistDetachedRunController(input) {
103
96
  });
104
97
  })));
105
98
  }
99
+ function detachedRunControllerInput(input) {
100
+ return {
101
+ entrypoint: input.prepared.entrypoint,
102
+ runId: input.runId,
103
+ ...input.prepared.schedule ? { schedule: input.prepared.schedule } : {},
104
+ task: input.task,
105
+ workflow: input.prepared.workflow,
106
+ workspaceRoot: input.worktreePath
107
+ };
108
+ }
106
109
  function detachedRunRecord(input) {
107
110
  return {
108
111
  ...resolvedRunControlOptions(input.runControl),
@@ -149,16 +152,16 @@ async function runConfiguredPipeline(rawInputs) {
149
152
  });
150
153
  return;
151
154
  }
152
- const result = await generateScheduleArtifact({
155
+ const result = await generateScheduleArtifactInMemory({
153
156
  config,
154
157
  entrypointId: scheduledEntrypoint,
155
158
  runId: inputs.runId,
156
159
  task: inputs.task,
157
160
  worktreePath: inputs.worktreePath
158
161
  });
159
- console.log(`Schedule generated: ${result.path}`);
160
- const scheduleYaml = readFileSync(resolve(inputs.worktreePath, result.path), "utf8");
161
- const compiled = compileScheduleArtifact(config, parseScheduleArtifact(scheduleYaml, result.path), inputs.worktreePath);
162
+ console.log("Schedule generated in memory");
163
+ const scheduleYaml = result.yaml;
164
+ const compiled = compileScheduleArtifact(config, parseScheduleArtifact(scheduleYaml, "schedule.yaml"), inputs.worktreePath);
162
165
  await runAndPrintPipeline({
163
166
  ...inputs,
164
167
  config: compiled.config,
@@ -327,20 +330,18 @@ async function prepareDetachedRun(input) {
327
330
  entrypoint: input.execution.entrypoint,
328
331
  workflow: input.execution.workflow
329
332
  };
330
- const result = await generateScheduleArtifact({
333
+ const result = await generateScheduleArtifactInMemory({
331
334
  config: input.config,
332
335
  entrypointId: scheduledEntrypoint,
333
336
  runId: input.runId,
334
337
  task: input.task,
335
338
  worktreePath: input.worktreePath
336
339
  });
337
- console.log(`Schedule generated: ${result.path}`);
338
- const schedule = resolve(input.worktreePath, result.path);
339
- const scheduleYaml = readFileSync(schedule, "utf8");
340
- const compiled = compileScheduleArtifact(input.config, parseScheduleArtifact(scheduleYaml, result.path), input.worktreePath);
340
+ console.log("Schedule generated in memory");
341
+ const scheduleYaml = result.yaml;
342
+ const compiled = compileScheduleArtifact(input.config, parseScheduleArtifact(scheduleYaml, "schedule.yaml"), input.worktreePath);
341
343
  return {
342
344
  config: compiled.config,
343
- schedule,
344
345
  scheduleArtifact: scheduleYaml,
345
346
  workflow: compiled.workflowId
346
347
  };
@@ -93,4 +93,4 @@ function parseImagePullPolicy(value) {
93
93
  return "Always";
94
94
  }
95
95
  //#endregion
96
- export { addMokaSubmitOptions, runMokaSubmitFromCli };
96
+ export { addMokaSubmitOptions, buildMokaSubmitInputFromCli, runMokaSubmitFromCli };
@@ -80,8 +80,6 @@ declare const DEFAULT_OPENCODE_ECOSYSTEM_MANIFEST: {
80
80
  default_stack: true;
81
81
  id: string;
82
82
  name: string;
83
- role: string;
84
- source: string;
85
83
  package?: string | undefined;
86
84
  plugin?: {
87
85
  kind: "local";
@@ -91,6 +89,8 @@ declare const DEFAULT_OPENCODE_ECOSYSTEM_MANIFEST: {
91
89
  kind: "npm";
92
90
  package: string;
93
91
  } | undefined;
92
+ role: string;
93
+ source: string;
94
94
  }[];
95
95
  generated_by: "@oisincoveney/pipeline";
96
96
  host_capabilities: {
@@ -108,9 +108,9 @@ declare const DEFAULT_OPENCODE_ECOSYSTEM_MANIFEST: {
108
108
  credentials: string[];
109
109
  id: string;
110
110
  locality: string;
111
+ name?: string | undefined;
111
112
  required: boolean;
112
113
  role: string;
113
- name?: string | undefined;
114
114
  }[];
115
115
  official_dependencies: {
116
116
  dependency_scope: string;
@@ -121,9 +121,9 @@ declare const DEFAULT_OPENCODE_ECOSYSTEM_MANIFEST: {
121
121
  }[];
122
122
  prompts: {
123
123
  id: string;
124
- used_by: string[];
125
124
  path?: string | undefined;
126
125
  source?: string | undefined;
126
+ used_by: string[];
127
127
  }[];
128
128
  runtime: {
129
129
  compatibility_runners: string[];
@@ -133,9 +133,9 @@ declare const DEFAULT_OPENCODE_ECOSYSTEM_MANIFEST: {
133
133
  };
134
134
  skills: {
135
135
  id: string;
136
- used_by: string[];
137
136
  path?: string | undefined;
138
137
  source?: string | undefined;
138
+ used_by: string[];
139
139
  }[];
140
140
  sources: {
141
141
  label: string;
@@ -92,10 +92,10 @@ declare const workflowNodeBaseSchema: z.ZodObject<{
92
92
  models: z.ZodOptional<z.ZodArray<z.ZodString>>;
93
93
  needs: z.ZodOptional<z.ZodArray<z.ZodString>>;
94
94
  reasoning_effort: z.ZodOptional<z.ZodEnum<{
95
- none: "none";
95
+ high: "high";
96
96
  low: "low";
97
97
  medium: "medium";
98
- high: "high";
98
+ none: "none";
99
99
  xhigh: "xhigh";
100
100
  }>>;
101
101
  retries: z.ZodOptional<z.ZodObject<{
@@ -176,8 +176,8 @@ declare const configSchema: z.ZodObject<{
176
176
  "workspace-write": "workspace-write";
177
177
  }>>;
178
178
  network: z.ZodOptional<z.ZodEnum<{
179
- inherit: "inherit";
180
179
  disabled: "disabled";
180
+ inherit: "inherit";
181
181
  }>>;
182
182
  }, z.core.$strict>>;
183
183
  returns: z.ZodOptional<z.ZodObject<{
@@ -226,8 +226,8 @@ declare const configSchema: z.ZodObject<{
226
226
  policy: z.ZodOptional<z.ZodObject<{
227
227
  commands: z.ZodOptional<z.ZodEnum<{
228
228
  allow: "allow";
229
- "trusted-only": "trusted-only";
230
229
  deny: "deny";
230
+ "trusted-only": "trusted-only";
231
231
  }>>;
232
232
  modules: z.ZodOptional<z.ZodEnum<{
233
233
  allow: "allow";
@@ -251,8 +251,8 @@ declare const configSchema: z.ZodObject<{
251
251
  }, z.core.$strict>>>;
252
252
  default_profile: z.ZodOptional<z.ZodString>;
253
253
  host_scope: z.ZodDefault<z.ZodEnum<{
254
- project: "project";
255
254
  global: "global";
255
+ project: "project";
256
256
  }>>;
257
257
  mode: z.ZodEnum<{
258
258
  hosted: "hosted";
@@ -294,16 +294,16 @@ declare const configSchema: z.ZodObject<{
294
294
  model: z.ZodOptional<z.ZodString>;
295
295
  network: z.ZodOptional<z.ZodObject<{
296
296
  mode: z.ZodEnum<{
297
- inherit: "inherit";
298
297
  disabled: "disabled";
298
+ inherit: "inherit";
299
299
  }>;
300
300
  }, z.core.$strict>>;
301
301
  output: z.ZodOptional<z.ZodObject<{
302
302
  format: z.ZodEnum<{
303
- text: "text";
304
303
  json: "json";
305
- jsonl: "jsonl";
306
304
  json_schema: "json_schema";
305
+ jsonl: "jsonl";
306
+ text: "text";
307
307
  }>;
308
308
  repair: z.ZodOptional<z.ZodObject<{
309
309
  enabled: z.ZodOptional<z.ZodBoolean>;
@@ -313,10 +313,10 @@ declare const configSchema: z.ZodObject<{
313
313
  schema_path: z.ZodOptional<z.ZodString>;
314
314
  }, z.core.$strict>>;
315
315
  reasoning_effort: z.ZodOptional<z.ZodEnum<{
316
- none: "none";
316
+ high: "high";
317
317
  low: "low";
318
318
  medium: "medium";
319
- high: "high";
319
+ none: "none";
320
320
  xhigh: "xhigh";
321
321
  }>>;
322
322
  rules: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -328,13 +328,13 @@ declare const configSchema: z.ZodObject<{
328
328
  skills: z.ZodOptional<z.ZodArray<z.ZodString>>;
329
329
  timeout_ms: z.ZodOptional<z.ZodNumber>;
330
330
  tools: z.ZodOptional<z.ZodArray<z.ZodEnum<{
331
- task: "task";
332
- read: "read";
333
- list: "list";
334
- grep: "grep";
335
- glob: "glob";
336
331
  bash: "bash";
337
332
  edit: "edit";
333
+ glob: "glob";
334
+ grep: "grep";
335
+ list: "list";
336
+ read: "read";
337
+ task: "task";
338
338
  write: "write";
339
339
  }>>>;
340
340
  }, z.core.$strict>>>;
@@ -361,8 +361,8 @@ declare const configSchema: z.ZodObject<{
361
361
  rules: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
362
362
  path: z.ZodString;
363
363
  source_root: z.ZodDefault<z.ZodEnum<{
364
- project: "project";
365
364
  package: "package";
365
+ project: "project";
366
366
  }>>;
367
367
  }, z.core.$strict>>>;
368
368
  runners: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
@@ -375,25 +375,25 @@ declare const configSchema: z.ZodObject<{
375
375
  mcp_servers: z.ZodOptional<z.ZodBoolean>;
376
376
  native_subagents: z.ZodOptional<z.ZodBoolean>;
377
377
  network: z.ZodOptional<z.ZodArray<z.ZodEnum<{
378
- inherit: "inherit";
379
378
  disabled: "disabled";
379
+ inherit: "inherit";
380
380
  }>>>;
381
381
  output_formats: z.ZodOptional<z.ZodArray<z.ZodEnum<{
382
- text: "text";
383
382
  json: "json";
384
- jsonl: "jsonl";
385
383
  json_schema: "json_schema";
384
+ jsonl: "jsonl";
385
+ text: "text";
386
386
  }>>>;
387
387
  rules: z.ZodOptional<z.ZodBoolean>;
388
388
  skills: z.ZodOptional<z.ZodBoolean>;
389
389
  tools: z.ZodOptional<z.ZodArray<z.ZodEnum<{
390
- task: "task";
391
- read: "read";
392
- list: "list";
393
- grep: "grep";
394
- glob: "glob";
395
390
  bash: "bash";
396
391
  edit: "edit";
392
+ glob: "glob";
393
+ grep: "grep";
394
+ list: "list";
395
+ read: "read";
396
+ task: "task";
397
397
  write: "write";
398
398
  }>>>;
399
399
  }, z.core.$strict>;
@@ -401,10 +401,10 @@ declare const configSchema: z.ZodObject<{
401
401
  host_models: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
402
402
  model: z.ZodOptional<z.ZodString>;
403
403
  reasoning_effort: z.ZodOptional<z.ZodEnum<{
404
- none: "none";
404
+ high: "high";
405
405
  low: "low";
406
406
  medium: "medium";
407
- high: "high";
407
+ none: "none";
408
408
  xhigh: "xhigh";
409
409
  }>>;
410
410
  type: z.ZodEnum<{
@@ -490,10 +490,10 @@ declare const configSchema: z.ZodObject<{
490
490
  models: z.ZodArray<z.ZodString>;
491
491
  profile: z.ZodString;
492
492
  reasoning_effort: z.ZodOptional<z.ZodEnum<{
493
- none: "none";
493
+ high: "high";
494
494
  low: "low";
495
495
  medium: "medium";
496
- high: "high";
496
+ none: "none";
497
497
  xhigh: "xhigh";
498
498
  }>>;
499
499
  }, z.core.$strict>>>;
@@ -503,8 +503,8 @@ declare const configSchema: z.ZodObject<{
503
503
  schedules: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
504
504
  description: z.ZodOptional<z.ZodString>;
505
505
  baseline: z.ZodEnum<{
506
- quick: "quick";
507
506
  execute: "execute";
507
+ quick: "quick";
508
508
  }>;
509
509
  max_parallel_nodes: z.ZodOptional<z.ZodNumber>;
510
510
  node_catalog: z.ZodOptional<z.ZodString>;
@@ -516,8 +516,8 @@ declare const configSchema: z.ZodObject<{
516
516
  skills: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
517
517
  path: z.ZodString;
518
518
  source_root: z.ZodDefault<z.ZodEnum<{
519
- project: "project";
520
519
  package: "package";
520
+ project: "project";
521
521
  }>>;
522
522
  }, z.core.$strict>>>;
523
523
  task_context: z.ZodOptional<z.ZodObject<{
@@ -95,4 +95,4 @@ function commandIdForHost(host, entrypointId) {
95
95
  return entrypointId;
96
96
  }
97
97
  //#endregion
98
- export { AGENTS_MD_END, AGENTS_MD_START, CLAUDE_PROJECT_CONFIG_PATH, CLAUDE_USER_CONFIG_PATH, CODEX_CONFIG_PATH, COMMAND_HOSTS, ENTRYPOINT_PATH_PATTERNS, GENERATED_MARKER, GENERATED_TS_MARKER, GENERATED_YAML_MARKER, INSTALL_HOSTS, OPENCODE_PROJECT_CONFIG_PATH, OWNER_MARKER_PREFIX, OWNER_TS_MARKER_PREFIX, OWNER_YAML_MARKER_PREFIX, SINGLE_OPENCODE_PLUGIN_ARRAY_RE, commandIdForHost, compactLines, entrypointDescription, entrypointEntries, instructionsPointer, invocationForHost, profileEntries, resolveHarnessTarget };
98
+ export { AGENTS_MD_END, AGENTS_MD_START, CLAUDE_PROJECT_CONFIG_PATH, CLAUDE_USER_CONFIG_PATH, CODEX_CONFIG_PATH, COMMAND_HOSTS, ENTRYPOINT_PATH_PATTERNS, GENERATED_MARKER, GENERATED_TS_MARKER, GENERATED_YAML_MARKER, INSTALL_HOSTS, OPENCODE_PROJECT_CONFIG_PATH, OWNER_MARKER_PREFIX, OWNER_TS_MARKER_PREFIX, OWNER_YAML_MARKER_PREFIX, SINGLE_OPENCODE_PLUGIN_ARRAY_RE, claudeGlobalConfigDir, codexGlobalConfigDir, commandIdForHost, compactLines, entrypointDescription, entrypointEntries, instructionsPointer, invocationForHost, opencodeGlobalConfigDir, profileEntries, resolveHarnessTarget };
@@ -5,8 +5,8 @@ import { existsSync, readFileSync, statSync } from "node:fs";
5
5
  import { execa } from "execa";
6
6
  import { tmpdir } from "node:os";
7
7
  import { dirname, join, relative } from "node:path";
8
- import { createHash } from "node:crypto";
9
8
  import { mkdir, mkdtemp, readdir, rm, writeFile } from "node:fs/promises";
9
+ import { createHash } from "node:crypto";
10
10
  //#region src/install-hooks.ts
11
11
  const DEFAULT_HOOK_INSTALL_SOURCE = AGENT_ASSET_SOURCE;
12
12
  const HOOK_HOSTS = [
@@ -24,8 +24,12 @@ const NON_HOOK_OWNED_TARGETS = new Set([".opencode/opencode.json"]);
24
24
  function hashContent(content) {
25
25
  return createHash("sha256").update(content).digest("hex");
26
26
  }
27
- const MERGE_MANAGED = { ".claude/settings.json": ["hooks"] };
28
- function mergeKeyFor(path) {
27
+ const MERGE_MANAGED = { ".claude/settings.json": [
28
+ ["hooks"],
29
+ ["skillListingBudgetFraction"],
30
+ ["skillOverrides"]
31
+ ] };
32
+ function mergeKeysFor(path) {
29
33
  return MERGE_MANAGED[path];
30
34
  }
31
35
  function canonicalize(value) {
@@ -51,8 +55,12 @@ function managedSubtree(text, keyPath) {
51
55
  return cursor;
52
56
  }
53
57
  function targetIdentityHash(path, content) {
54
- const mergeKey = mergeKeyFor(path);
55
- return mergeKey ? hashJson(managedSubtree(content.toString("utf8"), mergeKey)) : hashContent(content);
58
+ const mergeKeys = mergeKeysFor(path);
59
+ if (!mergeKeys) return hashContent(content);
60
+ const text = content.toString("utf8");
61
+ const subtrees = {};
62
+ for (const keyPath of mergeKeys) subtrees[keyPath.join(".")] = managedSubtree(text, keyPath);
63
+ return hashJson(subtrees);
56
64
  }
57
65
  async function cloneHookRepository(targetDir) {
58
66
  await execa("gh", [
@@ -178,9 +186,15 @@ async function writePlannedFile(file) {
178
186
  if (file.action === "conflict" || file.action === "unchanged") return;
179
187
  const target = targetPath(file.path);
180
188
  await mkdir(dirname(target), { recursive: true });
181
- const mergeKey = mergeKeyFor(file.path);
182
- if (mergeKey && existsSync(target)) {
183
- await writeFile(target, ensureTrailingNewline(applyJsonEdit(readFileSync(target, "utf8"), mergeKey, managedSubtree(file.content.toString("utf8"), mergeKey))));
189
+ const mergeKeys = mergeKeysFor(file.path);
190
+ if (mergeKeys && existsSync(target)) {
191
+ const sourceText = file.content.toString("utf8");
192
+ let merged = readFileSync(target, "utf8");
193
+ for (const keyPath of mergeKeys) {
194
+ const desired = managedSubtree(sourceText, keyPath);
195
+ if (desired !== void 0) merged = applyJsonEdit(merged, keyPath, desired);
196
+ }
197
+ await writeFile(target, ensureTrailingNewline(merged));
184
198
  return;
185
199
  }
186
200
  await writeFile(target, file.content);
@@ -1,7 +1,8 @@
1
+ import { resolveHarnessTarget } from "./install-commands/shared.js";
1
2
  import { AGENT_ASSET_SOURCE, AGENT_RULES_DIR } from "./agent-assets.js";
2
3
  import { execa } from "execa";
3
- import { homedir, tmpdir } from "node:os";
4
- import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+ import { dirname, join } from "node:path";
5
6
  import { mkdir, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises";
6
7
  //#region src/install-rules.ts
7
8
  const DEFAULT_RULES_INSTALL_SOURCE = AGENT_ASSET_SOURCE;
@@ -12,6 +13,24 @@ const RULESYNC_TARGETS = [
12
13
  "geminicli",
13
14
  "opencode"
14
15
  ];
16
+ const RULE_OUTPUTS = [
17
+ {
18
+ generatedPath: ".claude/CLAUDE.md",
19
+ targetPath: ".claude/CLAUDE.md"
20
+ },
21
+ {
22
+ generatedPath: ".codex/AGENTS.md",
23
+ targetPath: ".codex/AGENTS.md"
24
+ },
25
+ {
26
+ generatedPath: ".gemini/GEMINI.md",
27
+ targetPath: ".gemini/GEMINI.md"
28
+ },
29
+ {
30
+ generatedPath: ".config/opencode/AGENTS.md",
31
+ targetPath: ".opencode/AGENTS.md"
32
+ }
33
+ ];
15
34
  async function cloneRulesRepository(targetDir) {
16
35
  await execa("gh", [
17
36
  "repo",
@@ -71,50 +90,110 @@ ${(await Promise.all(entries.map(async (name) => {
71
90
  await mkdir(rulesyncRulesDir, { recursive: true });
72
91
  await writeFile(join(rulesyncRulesDir, "_root.md"), rootContent);
73
92
  }
93
+ async function withRulesyncHome(useHome) {
94
+ const home = await mkdtemp(join(tmpdir(), "moka-rules-home-"));
95
+ try {
96
+ return await useHome(home);
97
+ } finally {
98
+ await rm(home, {
99
+ force: true,
100
+ recursive: true
101
+ });
102
+ }
103
+ }
104
+ function rulesyncArgs(options) {
105
+ const args = [
106
+ "generate",
107
+ "-t",
108
+ RULESYNC_TARGETS.join(","),
109
+ "-f",
110
+ "rules",
111
+ "--delete",
112
+ "--global"
113
+ ];
114
+ if (options.dryRun) args.push("--dry-run");
115
+ if (options.silent) args.push("--silent");
116
+ return args;
117
+ }
118
+ async function runRulesyncGenerate(input) {
119
+ await input.runner(rulesyncArgs(input), {
120
+ cwd: input.source,
121
+ env: {
122
+ ...process.env,
123
+ HOME_DIR: input.home
124
+ }
125
+ });
126
+ }
127
+ function ruleItems(action) {
128
+ return RULE_OUTPUTS.map((output) => ({
129
+ action,
130
+ path: resolveHarnessTarget(output.targetPath)
131
+ }));
132
+ }
133
+ function readGeneratedRuleFiles(home) {
134
+ return Promise.all(RULE_OUTPUTS.map(async (output) => ({
135
+ content: await readFile(join(home, output.generatedPath), "utf8"),
136
+ path: resolveHarnessTarget(output.targetPath)
137
+ })));
138
+ }
139
+ async function writeGeneratedRuleFiles(files) {
140
+ for (const file of files) {
141
+ await mkdir(dirname(file.path), { recursive: true });
142
+ await writeFile(file.path, file.content);
143
+ }
144
+ }
145
+ async function installedRuleAction(file) {
146
+ try {
147
+ return await readFile(file.path, "utf8") === file.content ? "unchanged" : "update";
148
+ } catch {
149
+ return "create";
150
+ }
151
+ }
152
+ async function assertInstalledRulesCurrent(files) {
153
+ const changed = (await Promise.all(files.map(async (file) => ({
154
+ action: await installedRuleAction(file),
155
+ path: file.path
156
+ })))).filter((item) => item.action !== "unchanged");
157
+ if (changed.length === 0) return;
158
+ throw new Error(["Installed rule files are not up to date.", ...changed.map((item) => `- ${item.path}: ${item.action}`)].join("\n"));
159
+ }
74
160
  function installRules(options = {}) {
75
161
  const runner = options.rulesyncRunner ?? defaultRulesyncRunner;
76
162
  return withRulesSource(options.sourceOverride, async (source) => {
77
163
  await buildRootRule(source);
78
- const home = process.env.HOME_DIR ?? homedir();
79
- const args = [
80
- "generate",
81
- "-t",
82
- RULESYNC_TARGETS.join(","),
83
- "-f",
84
- "rules",
85
- "--delete"
86
- ];
87
- if (options.dryRun) args.push("--dry-run");
88
- if (options.check) args.push("--check");
89
- await runner(args, {
90
- cwd: source,
91
- env: {
92
- ...process.env,
93
- HOME_DIR: home
164
+ return withRulesyncHome(async (home) => {
165
+ if (options.dryRun) {
166
+ await runRulesyncGenerate({
167
+ dryRun: true,
168
+ home,
169
+ runner,
170
+ source
171
+ });
172
+ return {
173
+ items: ruleItems("skip"),
174
+ source: DEFAULT_RULES_INSTALL_SOURCE
175
+ };
176
+ }
177
+ await runRulesyncGenerate({
178
+ home,
179
+ runner,
180
+ silent: options.check,
181
+ source
182
+ });
183
+ const files = await readGeneratedRuleFiles(home);
184
+ if (options.check) {
185
+ await assertInstalledRulesCurrent(files);
186
+ return {
187
+ items: ruleItems("skip"),
188
+ source: DEFAULT_RULES_INSTALL_SOURCE
189
+ };
94
190
  }
191
+ await writeGeneratedRuleFiles(files);
192
+ return {
193
+ items: ruleItems("generate"),
194
+ source: DEFAULT_RULES_INSTALL_SOURCE
195
+ };
95
196
  });
96
- const action = options.dryRun ?? options.check ? "skip" : "generate";
97
- return {
98
- items: [
99
- {
100
- action,
101
- path: join(home, ".claude/CLAUDE.md")
102
- },
103
- {
104
- action,
105
- path: join(home, ".codex/AGENTS.md")
106
- },
107
- {
108
- action,
109
- path: join(home, ".gemini/GEMINI.md")
110
- },
111
- {
112
- action,
113
- path: join(home, ".config/opencode/AGENTS.md")
114
- }
115
- ],
116
- source: DEFAULT_RULES_INSTALL_SOURCE
117
- };
118
197
  });
119
198
  }
120
199
  //#endregion