@kody-ade/kody-engine 0.4.350 → 0.4.352

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.
package/dist/bin/kody.js CHANGED
@@ -15,7 +15,7 @@ var init_package = __esm({
15
15
  "package.json"() {
16
16
  package_default = {
17
17
  name: "@kody-ade/kody-engine",
18
- version: "0.4.350",
18
+ version: "0.4.352",
19
19
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
20
20
  license: "MIT",
21
21
  type: "module",
@@ -1983,15 +1983,19 @@ function parseWorkflowStep(value) {
1983
1983
  if (!capability || !isSafeSlug(capability)) return null;
1984
1984
  const implementation = stringField(raw.implementation);
1985
1985
  const action = stringField(raw.action);
1986
+ const evidence = stringField(raw.evidence);
1986
1987
  const agent = stringField(raw.agent);
1987
1988
  const reason = stringField(raw.reason);
1988
1989
  const target = stringField(raw.target);
1990
+ const targetFact = stringField(raw.targetFact ?? raw.target_fact);
1989
1991
  const cliArgs = raw.cliArgs;
1990
1992
  return {
1991
1993
  capability,
1992
1994
  ...action && isSafeSlug(action) ? { action } : {},
1993
1995
  ...implementation && isSafeSlug(implementation) ? { implementation } : {},
1996
+ ...evidence ? { evidence } : {},
1994
1997
  ...target === "issue" || target === "pr" ? { target } : {},
1998
+ ...targetFact ? { targetFact } : {},
1995
1999
  ...agent && isSafeSlug(agent) ? { agent } : {},
1996
2000
  ...reason ? { reason } : {},
1997
2001
  ...cliArgs && typeof cliArgs === "object" && !Array.isArray(cliArgs) ? { cliArgs } : {},
@@ -4551,7 +4555,13 @@ function loadProfile(profilePath) {
4551
4555
  const preNames = new Set(profile.scripts.preflight.map((e) => e.script).filter(Boolean));
4552
4556
  const postNames = profile.scripts.postflight.map((e) => e.script).filter(Boolean);
4553
4557
  const needsState = postNames.includes("writeJobStateFile") || postNames.includes("parseJobStateFromAgentResult");
4554
- const STATE_LOADERS = ["loadCapabilityState", "loadJobFromFile", "runTickScript", "runScheduledImplementationTick"];
4558
+ const STATE_LOADERS = [
4559
+ "loadCapabilityState",
4560
+ "loadJobFromFile",
4561
+ "runTickScript",
4562
+ "runScheduledImplementationTick",
4563
+ "runScheduledExecutableTick"
4564
+ ];
4555
4565
  if (needsState && !STATE_LOADERS.some((s) => preNames.has(s))) {
4556
4566
  throw new ProfileError(
4557
4567
  profilePath,
@@ -5769,8 +5779,10 @@ async function runContainerLoop(profile, ctx, input) {
5769
5779
  const priorAction = priorState.implementations?.[child.implementation]?.lastAction;
5770
5780
  let actionType2;
5771
5781
  if (priorAction && /_COMPLETED$/i.test(priorAction.type)) {
5772
- process.stderr.write(`[kody container] skipping ${child.implementation}: already completed (${priorAction.type})
5773
- `);
5782
+ process.stderr.write(
5783
+ `[kody container] skipping ${child.implementation}: already completed (${priorAction.type})
5784
+ `
5785
+ );
5774
5786
  actionType2 = priorAction.type;
5775
5787
  } else {
5776
5788
  let cliArgs;
@@ -7482,6 +7494,7 @@ function managedGoalFromState(state) {
7482
7494
  destination: { outcome: destination.outcome, evidence },
7483
7495
  capabilities,
7484
7496
  route,
7497
+ ...extra.runWithoutApproval === true ? { runWithoutApproval: true } : {},
7485
7498
  workflowRef: asWorkflowRef(extra.workflowRef),
7486
7499
  schedule: typeof extra.schedule === "string" ? extra.schedule : void 0,
7487
7500
  preferredRunTime: asPreferredRunTime(extra.preferredRunTime),
@@ -7503,6 +7516,7 @@ function writeManagedGoalToState(state, goal) {
7503
7516
  destination: goal.destination,
7504
7517
  capabilities: goal.capabilities,
7505
7518
  route: goal.route,
7519
+ ...goal.runWithoutApproval === true ? { runWithoutApproval: true } : {},
7506
7520
  workflowRef: goal.workflowRef,
7507
7521
  stage: goal.stage,
7508
7522
  facts: goal.facts,
@@ -8078,15 +8092,7 @@ function serializeTodoGoalState(goalId, state, previousRaw) {
8078
8092
  const routeByEvidence = new Map(route.map((step) => [stringField3(step.evidence), step]));
8079
8093
  const previousItems = new Map(parseItemsFromAnyRaw(previousRaw ?? "").map((item) => [item.id, item]));
8080
8094
  const items = evidence.length > 0 ? evidence.map(
8081
- (key) => itemFromEvidence(
8082
- key,
8083
- routeByEvidence.get(key),
8084
- facts,
8085
- evidenceState,
8086
- createdAt,
8087
- now,
8088
- previousItems.get(key)
8089
- )
8095
+ (key) => itemFromEvidence(key, routeByEvidence.get(key), facts, evidenceState, createdAt, now, previousItems.get(key))
8090
8096
  ) : stringArray(raw.capabilities).map(
8091
8097
  (capability) => itemFromCapability(capability, createdAt, previousItems.get(capability))
8092
8098
  );
@@ -8612,6 +8618,102 @@ var init_typeDefinitions = __esm({
8612
8618
  }
8613
8619
  });
8614
8620
 
8621
+ // src/workflowDefinitions.ts
8622
+ import * as fs28 from "fs";
8623
+ import * as path26 from "path";
8624
+ function isWorkflowDefinitionId(value) {
8625
+ return WORKFLOW_ID_PATTERN.test(value);
8626
+ }
8627
+ function workflowDefinitionPath(id) {
8628
+ if (!isWorkflowDefinitionId(id)) {
8629
+ throw new Error(`Invalid workflow id "${id}"`);
8630
+ }
8631
+ return `workflows/${id}/workflow.json`;
8632
+ }
8633
+ function normalizeWorkflowDefinition(value) {
8634
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
8635
+ const raw = value;
8636
+ const name = typeof raw.name === "string" ? raw.name.trim() : "";
8637
+ const workflow = parseCapabilityWorkflow({ steps: raw.steps });
8638
+ const steps = workflow?.steps;
8639
+ const capabilities = steps ? steps.map((step) => step.capability) : normalizeWorkflowCapabilities(raw.capabilities);
8640
+ if (!name || capabilities.length === 0) return null;
8641
+ return {
8642
+ version: 1,
8643
+ name,
8644
+ capabilities,
8645
+ ...raw.runWithoutApproval === true ? { runWithoutApproval: true } : {},
8646
+ ...steps ? { steps } : {},
8647
+ ...typeof raw.createdAt === "string" ? { createdAt: raw.createdAt } : {},
8648
+ ...typeof raw.updatedAt === "string" ? { updatedAt: raw.updatedAt } : {}
8649
+ };
8650
+ }
8651
+ function readWorkflowDefinition(config, cwd, id) {
8652
+ const file = readStateText(config, cwd, workflowDefinitionPath(id));
8653
+ if (!file) return readCompanyStoreWorkflowDefinition(id);
8654
+ return parseWorkflowDefinition(file.content);
8655
+ }
8656
+ function workflowDefinitionToCapabilityFolder(id, workflow, source = workflowDefinitionPath(id)) {
8657
+ return {
8658
+ slug: id,
8659
+ dir: path26.dirname(source),
8660
+ profilePath: source,
8661
+ bodyPath: source,
8662
+ title: workflow.name,
8663
+ body: "",
8664
+ rawBody: "",
8665
+ rawProfile: { name: id, workflow },
8666
+ config: {
8667
+ action: id,
8668
+ workflow: workflowDefinitionToConfig(workflow),
8669
+ describe: workflow.name
8670
+ }
8671
+ };
8672
+ }
8673
+ function normalizeWorkflowCapabilities(value) {
8674
+ if (!Array.isArray(value)) return [];
8675
+ const seen = /* @__PURE__ */ new Set();
8676
+ const capabilities = [];
8677
+ for (const item of value) {
8678
+ if (typeof item !== "string") continue;
8679
+ const slug2 = item.trim();
8680
+ if (!CAPABILITY_ID_PATTERN.test(slug2) || seen.has(slug2)) continue;
8681
+ seen.add(slug2);
8682
+ capabilities.push(slug2);
8683
+ }
8684
+ return capabilities;
8685
+ }
8686
+ function workflowDefinitionToConfig(workflow) {
8687
+ return {
8688
+ steps: workflow.steps ?? workflow.capabilities.map((capability) => ({ capability }))
8689
+ };
8690
+ }
8691
+ function readCompanyStoreWorkflowDefinition(id) {
8692
+ const root = getCompanyStoreAssetRoot("workflows");
8693
+ if (!root) return null;
8694
+ const filePath = path26.join(root, id, "workflow.json");
8695
+ if (!fs28.existsSync(filePath)) return null;
8696
+ return parseWorkflowDefinition(fs28.readFileSync(filePath, "utf8"));
8697
+ }
8698
+ function parseWorkflowDefinition(content) {
8699
+ try {
8700
+ return normalizeWorkflowDefinition(JSON.parse(content));
8701
+ } catch {
8702
+ return null;
8703
+ }
8704
+ }
8705
+ var WORKFLOW_ID_PATTERN, CAPABILITY_ID_PATTERN;
8706
+ var init_workflowDefinitions = __esm({
8707
+ "src/workflowDefinitions.ts"() {
8708
+ "use strict";
8709
+ init_capabilityFolders();
8710
+ init_companyStore();
8711
+ init_stateRepo();
8712
+ WORKFLOW_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{0,79}$/;
8713
+ CAPABILITY_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,79}$/;
8714
+ }
8715
+ });
8716
+
8615
8717
  // src/scripts/issueStateComment.ts
8616
8718
  function isStateEnvelope(x) {
8617
8719
  if (x === null || typeof x !== "object") return false;
@@ -8796,8 +8898,8 @@ var init_contentsApiBackend = __esm({
8796
8898
  });
8797
8899
 
8798
8900
  // src/scripts/jobState/localFileBackend.ts
8799
- import * as fs28 from "fs";
8800
- import * as path26 from "path";
8901
+ import * as fs29 from "fs";
8902
+ import * as path27 from "path";
8801
8903
  function sanitizeKey(s) {
8802
8904
  return s.replace(/[^A-Za-z0-9._-]/g, "-");
8803
8905
  }
@@ -8853,7 +8955,7 @@ var init_localFileBackend = __esm({
8853
8955
  if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
8854
8956
  this.cwd = opts.cwd;
8855
8957
  this.jobsDir = opts.jobsDir;
8856
- this.absDir = path26.join(opts.cwd, opts.jobsDir);
8958
+ this.absDir = path27.join(opts.cwd, opts.jobsDir);
8857
8959
  this.owner = opts.owner;
8858
8960
  this.repo = opts.repo;
8859
8961
  this.cache = opts.cache ?? defaultCacheAdapter();
@@ -8868,7 +8970,7 @@ var init_localFileBackend = __esm({
8868
8970
  `);
8869
8971
  return;
8870
8972
  }
8871
- fs28.mkdirSync(this.absDir, { recursive: true });
8973
+ fs29.mkdirSync(this.absDir, { recursive: true });
8872
8974
  const prefix = this.cacheKeyPrefix();
8873
8975
  const probeKey = `${prefix}probe-${Date.now()}`;
8874
8976
  try {
@@ -8897,7 +8999,7 @@ var init_localFileBackend = __esm({
8897
8999
  `);
8898
9000
  return;
8899
9001
  }
8900
- if (!fs28.existsSync(this.absDir)) {
9002
+ if (!fs29.existsSync(this.absDir)) {
8901
9003
  return;
8902
9004
  }
8903
9005
  const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
@@ -8913,11 +9015,11 @@ var init_localFileBackend = __esm({
8913
9015
  }
8914
9016
  load(slug2) {
8915
9017
  const relPath = stateFilePath(this.jobsDir, slug2);
8916
- const absPath = path26.join(this.cwd, relPath);
8917
- if (!fs28.existsSync(absPath)) {
9018
+ const absPath = path27.join(this.cwd, relPath);
9019
+ if (!fs29.existsSync(absPath)) {
8918
9020
  return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
8919
9021
  }
8920
- const raw = fs28.readFileSync(absPath, "utf-8");
9022
+ const raw = fs29.readFileSync(absPath, "utf-8");
8921
9023
  let parsed;
8922
9024
  try {
8923
9025
  parsed = JSON.parse(raw);
@@ -8934,13 +9036,13 @@ var init_localFileBackend = __esm({
8934
9036
  if (!loaded.created && isStateUnchanged(loaded.state, next)) {
8935
9037
  return false;
8936
9038
  }
8937
- const absPath = path26.join(this.cwd, loaded.path);
8938
- fs28.mkdirSync(path26.dirname(absPath), { recursive: true });
9039
+ const absPath = path27.join(this.cwd, loaded.path);
9040
+ fs29.mkdirSync(path27.dirname(absPath), { recursive: true });
8939
9041
  const body = `${JSON.stringify(next, null, 2)}
8940
9042
  `;
8941
9043
  const tmpPath = `${absPath}.${process.pid}.tmp`;
8942
- fs28.writeFileSync(tmpPath, body, "utf-8");
8943
- fs28.renameSync(tmpPath, absPath);
9044
+ fs29.writeFileSync(tmpPath, body, "utf-8");
9045
+ fs29.renameSync(tmpPath, absPath);
8944
9046
  return true;
8945
9047
  }
8946
9048
  cacheKeyPrefix() {
@@ -8980,7 +9082,7 @@ var init_jobState = __esm({
8980
9082
  });
8981
9083
 
8982
9084
  // src/scripts/goalCapabilityScheduling.ts
8983
- import * as path27 from "path";
9085
+ import * as path28 from "path";
8984
9086
  function isCapabilityCadenceGoal(goal, extra) {
8985
9087
  return extra.scheduleMode === "agentLoop" || extra.scheduler === "agentLoop" || goal.type === "standing" && goal.capabilities.length > 0;
8986
9088
  }
@@ -9036,7 +9138,7 @@ function planTargetLoopSchedule(opts) {
9036
9138
  }
9037
9139
  async function planGoalCapabilitySchedule(opts) {
9038
9140
  const jobsDir = opts.jobsDir ?? ".kody/capabilities";
9039
- const jobsRoot = path27.join(opts.cwd, jobsDir);
9141
+ const jobsRoot = path28.join(opts.cwd, jobsDir);
9040
9142
  const now = opts.now ?? /* @__PURE__ */ new Date();
9041
9143
  const at = now.toISOString();
9042
9144
  const backend = resolveBackend({ config: opts.config, cwd: opts.cwd, jobsDir });
@@ -9226,6 +9328,50 @@ var init_goalCapabilityScheduling = __esm({
9226
9328
  });
9227
9329
 
9228
9330
  // src/scripts/advanceManagedGoal.ts
9331
+ function scalarFacts(facts) {
9332
+ return Object.fromEntries(
9333
+ Object.entries(facts).filter(
9334
+ ([key, value]) => key !== "pendingEvidence" && (typeof value === "string" || typeof value === "number" || typeof value === "boolean")
9335
+ )
9336
+ );
9337
+ }
9338
+ function autonomyBlockReason(ctx, goalId, goal, dispatch2) {
9339
+ if (ctx.data.jobForce === true) return null;
9340
+ if (goal.runWithoutApproval !== true) {
9341
+ return `Run without approval is off for ${managedModelKind(goal)} ${goalId}`;
9342
+ }
9343
+ if (dispatch2.workflow) {
9344
+ const workflow = readWorkflowDefinition(ctx.config, ctx.cwd, dispatch2.workflow);
9345
+ if (workflow && workflow.runWithoutApproval !== true) {
9346
+ return `Run without approval is off for workflow ${dispatch2.workflow}`;
9347
+ }
9348
+ }
9349
+ const targetGoal = dispatch2.action === "goal-manager" && typeof dispatch2.cliArgs.goal === "string" ? dispatch2.cliArgs.goal : null;
9350
+ if (targetGoal && targetGoal !== goalId) {
9351
+ const target = fetchGoalState(ctx.config, targetGoal, ctx.cwd);
9352
+ const targetManaged = target ? managedGoalFromState(expandManagedGoalState(target)) : null;
9353
+ if (targetManaged && targetManaged.runWithoutApproval !== true) {
9354
+ return `Run without approval is off for goal ${targetGoal}`;
9355
+ }
9356
+ }
9357
+ return null;
9358
+ }
9359
+ function managedModelKind(goal) {
9360
+ return goal.loopTarget || goal.schedule ? "Loop" : "Goal";
9361
+ }
9362
+ function stageAutonomyBlocked(data, goalId, goal, goalState, reason) {
9363
+ stageGoalRunLogEvent(data, goalId, {
9364
+ source: "goal-manager",
9365
+ event: "goal.tick.wait",
9366
+ goalType: goal.type,
9367
+ goalState,
9368
+ stage: goal.stage,
9369
+ status: "wait",
9370
+ reason,
9371
+ goal: goalRunLogSnapshot(goalId, goalState, goal),
9372
+ decision: { kind: "wait", reason }
9373
+ });
9374
+ }
9229
9375
  function stageManagedGoalDecision(data, goalId, goal, goalState, decision, details) {
9230
9376
  if (decision.kind === "dispatch" || decision.kind === "dispatchWorkflow") {
9231
9377
  stageGoalRunLogEvent(data, goalId, {
@@ -9400,9 +9546,11 @@ var init_advanceManagedGoal = __esm({
9400
9546
  init_manager();
9401
9547
  init_runLog();
9402
9548
  init_state2();
9549
+ init_stateStore();
9403
9550
  init_targetLoopResolution();
9404
9551
  init_typeDefinitions();
9405
9552
  init_issue();
9553
+ init_workflowDefinitions();
9406
9554
  init_goalCapabilityScheduling();
9407
9555
  advanceManagedGoal = async (ctx) => {
9408
9556
  ctx.skipAgent = true;
@@ -9497,6 +9645,12 @@ var init_advanceManagedGoal = __esm({
9497
9645
  goal.raw = writeManagedGoalToState({ ...goal.raw, state: goal.state }, managed);
9498
9646
  goal.raw.extra.scheduleState = decision2.scheduleState;
9499
9647
  ctx.data.managedGoalDecision = decision2;
9648
+ const autonomyBlock = decision2.kind === "dispatch" && decision2.dispatch ? autonomyBlockReason(ctx, goal.id, managed, decision2.dispatch) : null;
9649
+ if (autonomyBlock) {
9650
+ stageAutonomyBlocked(ctx.data, goal.id, managed, goal.state, autonomyBlock);
9651
+ ctx.output.reason = autonomyBlock;
9652
+ return;
9653
+ }
9500
9654
  if (decision2.kind === "dispatch" && decision2.dispatch) {
9501
9655
  ctx.output.nextDispatch = {
9502
9656
  ...decision2.dispatch.action ? { action: decision2.dispatch.action } : {},
@@ -9553,6 +9707,12 @@ var init_advanceManagedGoal = __esm({
9553
9707
  goal.raw = writeManagedGoalToState({ ...goal.raw, state: goal.state }, managed);
9554
9708
  goal.raw.extra.scheduleState = decision2.scheduleState;
9555
9709
  ctx.data.managedGoalDecision = decision2;
9710
+ const autonomyBlock = decision2.kind === "dispatch" && decision2.dispatch ? autonomyBlockReason(ctx, goal.id, managed, decision2.dispatch) : null;
9711
+ if (autonomyBlock) {
9712
+ stageAutonomyBlocked(ctx.data, goal.id, managed, goal.state, autonomyBlock);
9713
+ ctx.output.reason = autonomyBlock;
9714
+ return;
9715
+ }
9556
9716
  if (decision2.kind === "dispatch" && decision2.dispatch) {
9557
9717
  ctx.output.nextDispatch = {
9558
9718
  capability: decision2.dispatch.capability,
@@ -9606,6 +9766,19 @@ var init_advanceManagedGoal = __esm({
9606
9766
  }
9607
9767
  goal.raw = writeManagedGoalToState({ ...goal.raw, state: goal.state }, managed);
9608
9768
  const afterDecisionSnapshot = goalRunLogSnapshot(goal.id, goal.state, managed);
9769
+ if (decision.kind === "dispatch" || decision.kind === "dispatchWorkflow") {
9770
+ const planned = decision.kind === "dispatchWorkflow" ? { workflow: decision.workflow, cliArgs: decision.cliArgs } : {
9771
+ capability: decision.capability,
9772
+ ...decision.implementation ? { implementation: decision.implementation } : {},
9773
+ cliArgs: decision.cliArgs
9774
+ };
9775
+ const autonomyBlock = autonomyBlockReason(ctx, goal.id, managed, planned);
9776
+ if (autonomyBlock) {
9777
+ stageAutonomyBlocked(ctx.data, goal.id, managed, goal.state, autonomyBlock);
9778
+ ctx.output.reason = autonomyBlock;
9779
+ return;
9780
+ }
9781
+ }
9609
9782
  stageManagedGoalDecision(ctx.data, goal.id, managed, goal.state, decision, {
9610
9783
  goalSnapshot: afterDecisionSnapshot,
9611
9784
  inspection: {
@@ -9626,8 +9799,9 @@ var init_advanceManagedGoal = __esm({
9626
9799
  ctx.output.nextDispatch = {
9627
9800
  workflow: decision.workflow,
9628
9801
  cliArgs: decision.cliArgs,
9802
+ workflowFacts: scalarFacts(managed.facts),
9629
9803
  ...decision.saveReport === true ? { saveReport: true } : {},
9630
- resultTarget: { type: "goal", id: goal.id }
9804
+ resultTarget: { type: "goal", id: goal.id, evidence: decision.evidence }
9631
9805
  };
9632
9806
  ctx.output.reason = `dispatch workflow ${decision.workflow} for ${decision.evidence}`;
9633
9807
  return;
@@ -11145,8 +11319,8 @@ var init_classifyByLabel = __esm({
11145
11319
  });
11146
11320
 
11147
11321
  // src/scripts/commitAndPush.ts
11148
- import * as fs29 from "fs";
11149
- import * as path28 from "path";
11322
+ import * as fs30 from "fs";
11323
+ import * as path29 from "path";
11150
11324
  function sentinelPathForStage(cwd, profileName) {
11151
11325
  const runId = resolveRunId();
11152
11326
  return runtimeStatePath(cwd, "agent-runs", runId, `commit-${profileName}.lock`);
@@ -11167,9 +11341,9 @@ var init_commitAndPush = __esm({
11167
11341
  }
11168
11342
  const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
11169
11343
  const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
11170
- if (sentinel && fs29.existsSync(sentinel)) {
11344
+ if (sentinel && fs30.existsSync(sentinel)) {
11171
11345
  try {
11172
- const replay = JSON.parse(fs29.readFileSync(sentinel, "utf-8"));
11346
+ const replay = JSON.parse(fs30.readFileSync(sentinel, "utf-8"));
11173
11347
  ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
11174
11348
  if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
11175
11349
  if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
@@ -11222,8 +11396,8 @@ var init_commitAndPush = __esm({
11222
11396
  const result = ctx.data.commitResult;
11223
11397
  if (sentinel && result?.committed) {
11224
11398
  try {
11225
- fs29.mkdirSync(path28.dirname(sentinel), { recursive: true });
11226
- fs29.writeFileSync(
11399
+ fs30.mkdirSync(path29.dirname(sentinel), { recursive: true });
11400
+ fs30.writeFileSync(
11227
11401
  sentinel,
11228
11402
  JSON.stringify(
11229
11403
  {
@@ -11314,8 +11488,8 @@ var init_commitGoalState = __esm({
11314
11488
  });
11315
11489
 
11316
11490
  // src/scripts/composePrompt.ts
11317
- import * as fs30 from "fs";
11318
- import * as path29 from "path";
11491
+ import * as fs31 from "fs";
11492
+ import * as path30 from "path";
11319
11493
  function fenceUntrusted(value) {
11320
11494
  if (value.trim().length === 0) return value;
11321
11495
  const safe = value.replace(/-{3,}\s*END UNTRUSTED INPUT\s*-{3,}/gi, "[END UNTRUSTED INPUT]");
@@ -11436,10 +11610,10 @@ var init_composePrompt = __esm({
11436
11610
  const explicit = ctx.data.promptTemplate;
11437
11611
  const mode = ctx.args.mode;
11438
11612
  const candidates = [
11439
- explicit ? path29.join(profile.dir, explicit) : null,
11440
- mode ? path29.join(profile.dir, "prompts", `${mode}.md`) : null,
11441
- path29.join(profile.dir, "prompt.md"),
11442
- path29.join(profile.dir, "capability.md")
11613
+ explicit ? path30.join(profile.dir, explicit) : null,
11614
+ mode ? path30.join(profile.dir, "prompts", `${mode}.md`) : null,
11615
+ path30.join(profile.dir, "prompt.md"),
11616
+ path30.join(profile.dir, "capability.md")
11443
11617
  ].filter(Boolean);
11444
11618
  let templatePath = "";
11445
11619
  let template = "";
@@ -11452,7 +11626,7 @@ var init_composePrompt = __esm({
11452
11626
  break;
11453
11627
  }
11454
11628
  try {
11455
- template = fs30.readFileSync(c, "utf-8");
11629
+ template = fs31.readFileSync(c, "utf-8");
11456
11630
  templatePath = c;
11457
11631
  break;
11458
11632
  } catch (err) {
@@ -11463,7 +11637,7 @@ var init_composePrompt = __esm({
11463
11637
  if (!templatePath) {
11464
11638
  let dirState;
11465
11639
  try {
11466
- dirState = `dir contents: [${fs30.readdirSync(profile.dir).join(", ")}]`;
11640
+ dirState = `dir contents: [${fs31.readdirSync(profile.dir).join(", ")}]`;
11467
11641
  } catch (err) {
11468
11642
  dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
11469
11643
  }
@@ -12152,19 +12326,19 @@ var init_deriveQaScopeFromIssue = __esm({
12152
12326
 
12153
12327
  // src/scripts/diagMcp.ts
12154
12328
  import { execFileSync as execFileSync10 } from "child_process";
12155
- import * as fs31 from "fs";
12329
+ import * as fs32 from "fs";
12156
12330
  import * as os6 from "os";
12157
- import * as path30 from "path";
12331
+ import * as path31 from "path";
12158
12332
  var diagMcp;
12159
12333
  var init_diagMcp = __esm({
12160
12334
  "src/scripts/diagMcp.ts"() {
12161
12335
  "use strict";
12162
12336
  diagMcp = async (_ctx) => {
12163
12337
  const home = os6.homedir();
12164
- const cacheDir = path30.join(home, ".cache", "ms-playwright");
12338
+ const cacheDir = path31.join(home, ".cache", "ms-playwright");
12165
12339
  let entries = [];
12166
12340
  try {
12167
- entries = fs31.readdirSync(cacheDir);
12341
+ entries = fs32.readdirSync(cacheDir);
12168
12342
  } catch {
12169
12343
  }
12170
12344
  const hasChromium = entries.some((e) => e.startsWith("chromium"));
@@ -12192,13 +12366,13 @@ var init_diagMcp = __esm({
12192
12366
  });
12193
12367
 
12194
12368
  // src/scripts/frameworkDetectors.ts
12195
- import * as fs32 from "fs";
12196
- import * as path31 from "path";
12369
+ import * as fs33 from "fs";
12370
+ import * as path32 from "path";
12197
12371
  function detectFrameworks(cwd) {
12198
12372
  const out = [];
12199
12373
  let deps = {};
12200
12374
  try {
12201
- const pkg = JSON.parse(fs32.readFileSync(path31.join(cwd, "package.json"), "utf-8"));
12375
+ const pkg = JSON.parse(fs33.readFileSync(path32.join(cwd, "package.json"), "utf-8"));
12202
12376
  deps = { ...pkg.dependencies, ...pkg.devDependencies };
12203
12377
  } catch {
12204
12378
  return out;
@@ -12235,25 +12409,25 @@ function detectFrameworks(cwd) {
12235
12409
  }
12236
12410
  function findFile(cwd, candidates) {
12237
12411
  for (const c of candidates) {
12238
- if (fs32.existsSync(path31.join(cwd, c))) return c;
12412
+ if (fs33.existsSync(path32.join(cwd, c))) return c;
12239
12413
  }
12240
12414
  return null;
12241
12415
  }
12242
12416
  function discoverPayloadCollections(cwd) {
12243
12417
  const out = [];
12244
12418
  for (const dir of COLLECTION_DIRS) {
12245
- const full = path31.join(cwd, dir);
12246
- if (!fs32.existsSync(full)) continue;
12419
+ const full = path32.join(cwd, dir);
12420
+ if (!fs33.existsSync(full)) continue;
12247
12421
  let files;
12248
12422
  try {
12249
- files = fs32.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
12423
+ files = fs33.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
12250
12424
  } catch {
12251
12425
  continue;
12252
12426
  }
12253
12427
  for (const file of files) {
12254
12428
  try {
12255
- const filePath = path31.join(full, file);
12256
- const content = fs32.readFileSync(filePath, "utf-8").slice(0, 1e4);
12429
+ const filePath = path32.join(full, file);
12430
+ const content = fs33.readFileSync(filePath, "utf-8").slice(0, 1e4);
12257
12431
  const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
12258
12432
  if (!slugMatch) continue;
12259
12433
  const slug2 = slugMatch[1];
@@ -12267,7 +12441,7 @@ function discoverPayloadCollections(cwd) {
12267
12441
  out.push({
12268
12442
  name,
12269
12443
  slug: slug2,
12270
- filePath: path31.relative(cwd, filePath),
12444
+ filePath: path32.relative(cwd, filePath),
12271
12445
  fields: fields.slice(0, 20),
12272
12446
  hasAdmin
12273
12447
  });
@@ -12280,28 +12454,28 @@ function discoverPayloadCollections(cwd) {
12280
12454
  function discoverAdminComponents(cwd, collections) {
12281
12455
  const out = [];
12282
12456
  for (const dir of ADMIN_COMPONENT_DIRS) {
12283
- const full = path31.join(cwd, dir);
12284
- if (!fs32.existsSync(full)) continue;
12457
+ const full = path32.join(cwd, dir);
12458
+ if (!fs33.existsSync(full)) continue;
12285
12459
  let entries;
12286
12460
  try {
12287
- entries = fs32.readdirSync(full, { withFileTypes: true });
12461
+ entries = fs33.readdirSync(full, { withFileTypes: true });
12288
12462
  } catch {
12289
12463
  continue;
12290
12464
  }
12291
12465
  for (const entry of entries) {
12292
- const entryPath = path31.join(full, entry.name);
12466
+ const entryPath = path32.join(full, entry.name);
12293
12467
  let name;
12294
12468
  let filePath;
12295
12469
  if (entry.isDirectory()) {
12296
12470
  const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
12297
- (f) => fs32.existsSync(path31.join(entryPath, f))
12471
+ (f) => fs33.existsSync(path32.join(entryPath, f))
12298
12472
  );
12299
12473
  if (!indexFile) continue;
12300
12474
  name = entry.name;
12301
- filePath = path31.relative(cwd, path31.join(entryPath, indexFile));
12475
+ filePath = path32.relative(cwd, path32.join(entryPath, indexFile));
12302
12476
  } else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
12303
12477
  name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
12304
- filePath = path31.relative(cwd, entryPath);
12478
+ filePath = path32.relative(cwd, entryPath);
12305
12479
  } else {
12306
12480
  continue;
12307
12481
  }
@@ -12309,7 +12483,7 @@ function discoverAdminComponents(cwd, collections) {
12309
12483
  if (collections) {
12310
12484
  for (const col of collections) {
12311
12485
  try {
12312
- const colContent = fs32.readFileSync(path31.join(cwd, col.filePath), "utf-8");
12486
+ const colContent = fs33.readFileSync(path32.join(cwd, col.filePath), "utf-8");
12313
12487
  if (colContent.includes(name)) {
12314
12488
  usedInCollection = col.slug;
12315
12489
  break;
@@ -12327,8 +12501,8 @@ function scanApiRoutes(cwd) {
12327
12501
  const out = [];
12328
12502
  const appDirs = ["src/app", "app"];
12329
12503
  for (const appDir of appDirs) {
12330
- const apiDir = path31.join(cwd, appDir, "api");
12331
- if (!fs32.existsSync(apiDir)) continue;
12504
+ const apiDir = path32.join(cwd, appDir, "api");
12505
+ if (!fs33.existsSync(apiDir)) continue;
12332
12506
  walkApiRoutes(apiDir, "/api", cwd, out);
12333
12507
  break;
12334
12508
  }
@@ -12337,14 +12511,14 @@ function scanApiRoutes(cwd) {
12337
12511
  function walkApiRoutes(dir, prefix, cwd, out) {
12338
12512
  let entries;
12339
12513
  try {
12340
- entries = fs32.readdirSync(dir, { withFileTypes: true });
12514
+ entries = fs33.readdirSync(dir, { withFileTypes: true });
12341
12515
  } catch {
12342
12516
  return;
12343
12517
  }
12344
12518
  const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
12345
12519
  if (routeFile) {
12346
12520
  try {
12347
- const content = fs32.readFileSync(path31.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
12521
+ const content = fs33.readFileSync(path32.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
12348
12522
  const methods = HTTP_METHODS.filter(
12349
12523
  (m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
12350
12524
  );
@@ -12352,7 +12526,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
12352
12526
  out.push({
12353
12527
  path: prefix,
12354
12528
  methods,
12355
- filePath: path31.relative(cwd, path31.join(dir, routeFile.name))
12529
+ filePath: path32.relative(cwd, path32.join(dir, routeFile.name))
12356
12530
  });
12357
12531
  }
12358
12532
  } catch {
@@ -12363,7 +12537,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
12363
12537
  if (entry.name === "node_modules" || entry.name === ".next") continue;
12364
12538
  let segment = entry.name;
12365
12539
  if (segment.startsWith("(") && segment.endsWith(")")) {
12366
- walkApiRoutes(path31.join(dir, entry.name), prefix, cwd, out);
12540
+ walkApiRoutes(path32.join(dir, entry.name), prefix, cwd, out);
12367
12541
  continue;
12368
12542
  }
12369
12543
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -12371,16 +12545,16 @@ function walkApiRoutes(dir, prefix, cwd, out) {
12371
12545
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
12372
12546
  segment = `:${segment.slice(1, -1)}`;
12373
12547
  }
12374
- walkApiRoutes(path31.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
12548
+ walkApiRoutes(path32.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
12375
12549
  }
12376
12550
  }
12377
12551
  function scanEnvVars(cwd) {
12378
12552
  const candidates = [".env.example", ".env.local.example", ".env.template"];
12379
12553
  for (const envFile of candidates) {
12380
- const envPath = path31.join(cwd, envFile);
12381
- if (!fs32.existsSync(envPath)) continue;
12554
+ const envPath = path32.join(cwd, envFile);
12555
+ if (!fs33.existsSync(envPath)) continue;
12382
12556
  try {
12383
- const content = fs32.readFileSync(envPath, "utf-8");
12557
+ const content = fs33.readFileSync(envPath, "utf-8");
12384
12558
  const vars = [];
12385
12559
  for (const line of content.split("\n")) {
12386
12560
  const trimmed = line.trim();
@@ -12425,8 +12599,8 @@ var init_frameworkDetectors = __esm({
12425
12599
  });
12426
12600
 
12427
12601
  // src/scripts/discoverQaContext.ts
12428
- import * as fs33 from "fs";
12429
- import * as path32 from "path";
12602
+ import * as fs34 from "fs";
12603
+ import * as path33 from "path";
12430
12604
  function runQaDiscovery(cwd) {
12431
12605
  const out = {
12432
12606
  routes: [],
@@ -12457,9 +12631,9 @@ function runQaDiscovery(cwd) {
12457
12631
  }
12458
12632
  function detectDevServer(cwd, out) {
12459
12633
  try {
12460
- const pkg = JSON.parse(fs33.readFileSync(path32.join(cwd, "package.json"), "utf-8"));
12634
+ const pkg = JSON.parse(fs34.readFileSync(path33.join(cwd, "package.json"), "utf-8"));
12461
12635
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
12462
- const pm = fs33.existsSync(path32.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs33.existsSync(path32.join(cwd, "yarn.lock")) ? "yarn" : fs33.existsSync(path32.join(cwd, "bun.lockb")) ? "bun" : "npm";
12636
+ const pm = fs34.existsSync(path33.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs34.existsSync(path33.join(cwd, "yarn.lock")) ? "yarn" : fs34.existsSync(path33.join(cwd, "bun.lockb")) ? "bun" : "npm";
12463
12637
  if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
12464
12638
  if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
12465
12639
  else if (allDeps.vite) out.devPort = 5173;
@@ -12469,8 +12643,8 @@ function detectDevServer(cwd, out) {
12469
12643
  function scanFrontendRoutes(cwd, out) {
12470
12644
  const appDirs = ["src/app", "app"];
12471
12645
  for (const appDir of appDirs) {
12472
- const full = path32.join(cwd, appDir);
12473
- if (!fs33.existsSync(full)) continue;
12646
+ const full = path33.join(cwd, appDir);
12647
+ if (!fs34.existsSync(full)) continue;
12474
12648
  walkFrontendRoutes(full, "", out);
12475
12649
  break;
12476
12650
  }
@@ -12478,7 +12652,7 @@ function scanFrontendRoutes(cwd, out) {
12478
12652
  function walkFrontendRoutes(dir, prefix, out) {
12479
12653
  let entries;
12480
12654
  try {
12481
- entries = fs33.readdirSync(dir, { withFileTypes: true });
12655
+ entries = fs34.readdirSync(dir, { withFileTypes: true });
12482
12656
  } catch {
12483
12657
  return;
12484
12658
  }
@@ -12495,7 +12669,7 @@ function walkFrontendRoutes(dir, prefix, out) {
12495
12669
  if (entry.name === "node_modules" || entry.name === ".next") continue;
12496
12670
  let segment = entry.name;
12497
12671
  if (segment.startsWith("(") && segment.endsWith(")")) {
12498
- walkFrontendRoutes(path32.join(dir, entry.name), prefix, out);
12672
+ walkFrontendRoutes(path33.join(dir, entry.name), prefix, out);
12499
12673
  continue;
12500
12674
  }
12501
12675
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -12503,7 +12677,7 @@ function walkFrontendRoutes(dir, prefix, out) {
12503
12677
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
12504
12678
  segment = `:${segment.slice(1, -1)}`;
12505
12679
  }
12506
- walkFrontendRoutes(path32.join(dir, entry.name), `${prefix}/${segment}`, out);
12680
+ walkFrontendRoutes(path33.join(dir, entry.name), `${prefix}/${segment}`, out);
12507
12681
  }
12508
12682
  }
12509
12683
  function detectAuthFiles(cwd, out) {
@@ -12520,23 +12694,23 @@ function detectAuthFiles(cwd, out) {
12520
12694
  "src/app/api/oauth"
12521
12695
  ];
12522
12696
  for (const c of candidates) {
12523
- if (fs33.existsSync(path32.join(cwd, c))) out.authFiles.push(c);
12697
+ if (fs34.existsSync(path33.join(cwd, c))) out.authFiles.push(c);
12524
12698
  }
12525
12699
  }
12526
12700
  function detectRoles(cwd, out) {
12527
12701
  const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
12528
12702
  for (const rp of rolePaths) {
12529
- const dir = path32.join(cwd, rp);
12530
- if (!fs33.existsSync(dir)) continue;
12703
+ const dir = path33.join(cwd, rp);
12704
+ if (!fs34.existsSync(dir)) continue;
12531
12705
  let files;
12532
12706
  try {
12533
- files = fs33.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
12707
+ files = fs34.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
12534
12708
  } catch {
12535
12709
  continue;
12536
12710
  }
12537
12711
  for (const f of files) {
12538
12712
  try {
12539
- const content = fs33.readFileSync(path32.join(dir, f), "utf-8").slice(0, 5e3);
12713
+ const content = fs34.readFileSync(path33.join(dir, f), "utf-8").slice(0, 5e3);
12540
12714
  const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
12541
12715
  if (roleMatches) {
12542
12716
  for (const m of roleMatches) {
@@ -13781,12 +13955,12 @@ var init_fixFlow = __esm({
13781
13955
 
13782
13956
  // src/scripts/initFlow.ts
13783
13957
  import { execFileSync as execFileSync15 } from "child_process";
13784
- import * as fs34 from "fs";
13785
- import * as path33 from "path";
13958
+ import * as fs35 from "fs";
13959
+ import * as path34 from "path";
13786
13960
  function detectPackageManager(cwd) {
13787
- if (fs34.existsSync(path33.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
13788
- if (fs34.existsSync(path33.join(cwd, "yarn.lock"))) return "yarn";
13789
- if (fs34.existsSync(path33.join(cwd, "bun.lockb"))) return "bun";
13961
+ if (fs35.existsSync(path34.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
13962
+ if (fs35.existsSync(path34.join(cwd, "yarn.lock"))) return "yarn";
13963
+ if (fs35.existsSync(path34.join(cwd, "bun.lockb"))) return "bun";
13790
13964
  return "npm";
13791
13965
  }
13792
13966
  function qualityCommandsFor(pm) {
@@ -13858,22 +14032,22 @@ function performInit(cwd, force) {
13858
14032
  const pm = detectPackageManager(cwd);
13859
14033
  const ownerRepo = detectOwnerRepo(cwd);
13860
14034
  const defaultBranch = defaultBranchFromGit(cwd);
13861
- const configPath = path33.join(cwd, "kody.config.json");
13862
- if (fs34.existsSync(configPath) && !force) {
14035
+ const configPath = path34.join(cwd, "kody.config.json");
14036
+ if (fs35.existsSync(configPath) && !force) {
13863
14037
  skipped.push("kody.config.json");
13864
14038
  } else {
13865
14039
  const cfg = makeConfig(pm, ownerRepo, defaultBranch);
13866
- fs34.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
14040
+ fs35.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
13867
14041
  `);
13868
14042
  wrote.push("kody.config.json");
13869
14043
  }
13870
- const workflowDir = path33.join(cwd, ".github", "workflows");
13871
- const workflowPath = path33.join(workflowDir, "kody.yml");
13872
- if (fs34.existsSync(workflowPath) && !force) {
14044
+ const workflowDir = path34.join(cwd, ".github", "workflows");
14045
+ const workflowPath = path34.join(workflowDir, "kody.yml");
14046
+ if (fs35.existsSync(workflowPath) && !force) {
13873
14047
  skipped.push(".github/workflows/kody.yml");
13874
14048
  } else {
13875
- fs34.mkdirSync(workflowDir, { recursive: true });
13876
- fs34.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
14049
+ fs35.mkdirSync(workflowDir, { recursive: true });
14050
+ fs35.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
13877
14051
  wrote.push(".github/workflows/kody.yml");
13878
14052
  }
13879
14053
  for (const exe of listImplementations()) {
@@ -13884,12 +14058,12 @@ function performInit(cwd, force) {
13884
14058
  continue;
13885
14059
  }
13886
14060
  if (profile.kind !== "scheduled" || !profile.schedule) continue;
13887
- const target = path33.join(workflowDir, `kody-${exe.name}.yml`);
13888
- if (fs34.existsSync(target) && !force) {
14061
+ const target = path34.join(workflowDir, `kody-${exe.name}.yml`);
14062
+ if (fs35.existsSync(target) && !force) {
13889
14063
  skipped.push(`.github/workflows/kody-${exe.name}.yml`);
13890
14064
  continue;
13891
14065
  }
13892
- fs34.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
14066
+ fs35.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
13893
14067
  wrote.push(`.github/workflows/kody-${exe.name}.yml`);
13894
14068
  }
13895
14069
  let labels;
@@ -14037,7 +14211,7 @@ Nothing to do. All files already present. (Use --force to overwrite.)
14037
14211
  });
14038
14212
 
14039
14213
  // src/scripts/loadAgentAdhoc.ts
14040
- import * as fs35 from "fs";
14214
+ import * as fs36 from "fs";
14041
14215
  function resolveMessage(messageArg) {
14042
14216
  const fromComment = readCommentBody();
14043
14217
  if (fromComment) return stripDirective(fromComment);
@@ -14045,9 +14219,9 @@ function resolveMessage(messageArg) {
14045
14219
  }
14046
14220
  function readCommentBody() {
14047
14221
  const eventPath = process.env.GITHUB_EVENT_PATH;
14048
- if (!eventPath || !fs35.existsSync(eventPath)) return "";
14222
+ if (!eventPath || !fs36.existsSync(eventPath)) return "";
14049
14223
  try {
14050
- const event = JSON.parse(fs35.readFileSync(eventPath, "utf-8"));
14224
+ const event = JSON.parse(fs36.readFileSync(eventPath, "utf-8"));
14051
14225
  return String(event.comment?.body ?? "");
14052
14226
  } catch {
14053
14227
  return "";
@@ -14100,10 +14274,10 @@ var init_loadAgentAdhoc = __esm({
14100
14274
  throw new Error("loadAgentAdhoc: ctx.args.agent must be a non-empty slug");
14101
14275
  }
14102
14276
  const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
14103
- if (!fs35.existsSync(agentPath)) {
14277
+ if (!fs36.existsSync(agentPath)) {
14104
14278
  throw new Error(`loadAgentAdhoc: agent identity not found: ${agentPath}`);
14105
14279
  }
14106
- const { title, body } = parseAgentFile(fs35.readFileSync(agentPath, "utf-8"), agentSlug);
14280
+ const { title, body } = parseAgentFile(fs36.readFileSync(agentPath, "utf-8"), agentSlug);
14107
14281
  const message = resolveMessage(ctx.args.message);
14108
14282
  if (!message) {
14109
14283
  throw new Error(
@@ -14347,8 +14521,8 @@ var init_loadIssueStateComment = __esm({
14347
14521
  });
14348
14522
 
14349
14523
  // src/scripts/loadJobFromFile.ts
14350
- import * as fs36 from "fs";
14351
- import * as path34 from "path";
14524
+ import * as fs37 from "fs";
14525
+ import * as path35 from "path";
14352
14526
  function parseJobFile(raw, slug2) {
14353
14527
  let stripped = raw;
14354
14528
  if (stripped.startsWith("---\n")) {
@@ -14386,9 +14560,9 @@ var init_loadJobFromFile = __esm({
14386
14560
  if (!slug2) {
14387
14561
  throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
14388
14562
  }
14389
- const capability = resolveCapabilityFolder(slug2, path34.join(ctx.cwd, jobsDir));
14563
+ const capability = resolveCapabilityFolder(slug2, path35.join(ctx.cwd, jobsDir));
14390
14564
  if (!capability) {
14391
- throw new Error(`loadJobFromFile: capability folder not found or incomplete: ${path34.join(ctx.cwd, jobsDir, slug2)}`);
14565
+ throw new Error(`loadJobFromFile: capability folder not found or incomplete: ${path35.join(ctx.cwd, jobsDir, slug2)}`);
14392
14566
  }
14393
14567
  const { title, body, config } = capability;
14394
14568
  const mentions = (config.mentions ?? []).map((login) => `@${login}`).join(" ");
@@ -14397,12 +14571,12 @@ var init_loadJobFromFile = __esm({
14397
14571
  let agentIdentity = "";
14398
14572
  if (agentSlug) {
14399
14573
  const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
14400
- if (!fs36.existsSync(agentPath)) {
14574
+ if (!fs37.existsSync(agentPath)) {
14401
14575
  throw new Error(
14402
14576
  `loadJobFromFile: capability '${slug2}' declares agent '${agentSlug}' but ${agentPath} does not exist`
14403
14577
  );
14404
14578
  }
14405
- const agentRaw = fs36.readFileSync(agentPath, "utf-8");
14579
+ const agentRaw = fs37.readFileSync(agentPath, "utf-8");
14406
14580
  const parsed = parseJobFile(agentRaw, agentSlug);
14407
14581
  agentTitle = parsed.title;
14408
14582
  agentIdentity = parsed.body;
@@ -14482,13 +14656,13 @@ ${truncate(issue.body, FINDING_BODY_MAX_BYTES)}`;
14482
14656
  });
14483
14657
 
14484
14658
  // src/scripts/kodyVariables.ts
14485
- import * as fs37 from "fs";
14486
- import * as path35 from "path";
14659
+ import * as fs38 from "fs";
14660
+ import * as path36 from "path";
14487
14661
  function readKodyVariables(cwd) {
14488
- const full = path35.join(cwd, KODY_VARIABLES_REL_PATH);
14662
+ const full = path36.join(cwd, KODY_VARIABLES_REL_PATH);
14489
14663
  let raw;
14490
14664
  try {
14491
- raw = fs37.readFileSync(full, "utf-8");
14665
+ raw = fs38.readFileSync(full, "utf-8");
14492
14666
  } catch {
14493
14667
  return {};
14494
14668
  }
@@ -14556,8 +14730,8 @@ var init_keys = __esm({
14556
14730
  });
14557
14731
 
14558
14732
  // src/stateRepoGithub.ts
14559
- import * as fs38 from "fs";
14560
- import * as path36 from "path";
14733
+ import * as fs39 from "fs";
14734
+ import * as path37 from "path";
14561
14735
  function recordValue4(value) {
14562
14736
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
14563
14737
  }
@@ -14697,15 +14871,15 @@ function mergeJsonl(localText, remoteText) {
14697
14871
  async function syncJsonlFileFromGithubState(opts) {
14698
14872
  const remote = await readGithubStateTextWithConfig(opts);
14699
14873
  if (!remote) return;
14700
- const local = fs38.existsSync(opts.localPath) ? fs38.readFileSync(opts.localPath, "utf-8") : "";
14874
+ const local = fs39.existsSync(opts.localPath) ? fs39.readFileSync(opts.localPath, "utf-8") : "";
14701
14875
  const next = mergeJsonl(local, remote.content);
14702
14876
  if (next === local) return;
14703
- fs38.mkdirSync(path36.dirname(opts.localPath), { recursive: true });
14704
- fs38.writeFileSync(opts.localPath, next);
14877
+ fs39.mkdirSync(path37.dirname(opts.localPath), { recursive: true });
14878
+ fs39.writeFileSync(opts.localPath, next);
14705
14879
  }
14706
14880
  async function persistJsonlFileToGithubState(opts) {
14707
- if (!fs38.existsSync(opts.localPath)) return;
14708
- const localText = fs38.readFileSync(opts.localPath, "utf-8");
14881
+ if (!fs39.existsSync(opts.localPath)) return;
14882
+ const localText = fs39.readFileSync(opts.localPath, "utf-8");
14709
14883
  for (let attempt = 1; attempt <= 3; attempt += 1) {
14710
14884
  const remote = await readGithubStateTextWithConfig(opts);
14711
14885
  const body = mergeJsonl(localText, remote?.content ?? "");
@@ -14844,8 +15018,8 @@ var init_runtimeSecrets = __esm({
14844
15018
  });
14845
15019
 
14846
15020
  // src/scripts/loadQaContext.ts
14847
- import * as fs39 from "fs";
14848
- import * as path37 from "path";
15021
+ import * as fs40 from "fs";
15022
+ import * as path38 from "path";
14849
15023
  function parseSlugList(value) {
14850
15024
  const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
14851
15025
  return inner.split(",").map(
@@ -14874,18 +15048,18 @@ function readProfileAgents(raw) {
14874
15048
  return { agent: agent ?? legacy ?? ["kody"], body };
14875
15049
  }
14876
15050
  function readProfile(cwd) {
14877
- const dir = path37.join(cwd, CONTEXT_DIR_REL_PATH);
14878
- if (!fs39.existsSync(dir)) return "";
15051
+ const dir = path38.join(cwd, CONTEXT_DIR_REL_PATH);
15052
+ if (!fs40.existsSync(dir)) return "";
14879
15053
  let entries;
14880
15054
  try {
14881
- entries = fs39.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
15055
+ entries = fs40.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
14882
15056
  } catch {
14883
15057
  return "";
14884
15058
  }
14885
15059
  const blocks = [];
14886
15060
  for (const file of entries) {
14887
15061
  try {
14888
- const raw = fs39.readFileSync(path37.join(dir, file), "utf-8");
15062
+ const raw = fs40.readFileSync(path38.join(dir, file), "utf-8");
14889
15063
  const { agent, body } = readProfileAgents(raw);
14890
15064
  if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
14891
15065
  blocks.push(`## ${file}
@@ -14934,8 +15108,8 @@ var init_loadQaContext = __esm({
14934
15108
  });
14935
15109
 
14936
15110
  // src/taskContext.ts
14937
- import * as fs40 from "fs";
14938
- import * as path38 from "path";
15111
+ import * as fs41 from "fs";
15112
+ import * as path39 from "path";
14939
15113
  function buildTaskContext(args) {
14940
15114
  return {
14941
15115
  schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
@@ -14951,9 +15125,9 @@ function buildTaskContext(args) {
14951
15125
  function persistTaskContext(cwd, ctx) {
14952
15126
  try {
14953
15127
  const dir = runtimeStatePath(cwd, "agent-runs", ctx.runId);
14954
- fs40.mkdirSync(dir, { recursive: true });
14955
- const file = path38.join(dir, "task-context.json");
14956
- fs40.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
15128
+ fs41.mkdirSync(dir, { recursive: true });
15129
+ const file = path39.join(dir, "task-context.json");
15130
+ fs41.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
14957
15131
  `);
14958
15132
  return file;
14959
15133
  } catch (err) {
@@ -17463,12 +17637,12 @@ fi
17463
17637
 
17464
17638
  // src/scripts/runPreviewBuild.ts
17465
17639
  import { copyFile, writeFile } from "fs/promises";
17466
- import * as path39 from "path";
17640
+ import * as path40 from "path";
17467
17641
  import { fileURLToPath } from "url";
17468
17642
  function bundledDockerfilePath(mode) {
17469
- const here = path39.dirname(fileURLToPath(import.meta.url));
17643
+ const here = path40.dirname(fileURLToPath(import.meta.url));
17470
17644
  const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
17471
- return path39.join(here, "preview-build-templates", file);
17645
+ return path40.join(here, "preview-build-templates", file);
17472
17646
  }
17473
17647
  function required(name) {
17474
17648
  const v = (process.env[name] ?? "").trim();
@@ -17719,10 +17893,10 @@ var init_runPreviewBuild = __esm({
17719
17893
  console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
17720
17894
  if (Object.keys(buildEnv).length > 0) {
17721
17895
  const lines = Object.entries(buildEnv).map(([k, v]) => `${k}=${JSON.stringify(v)}`);
17722
- await writeFile(path39.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
17896
+ await writeFile(path40.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
17723
17897
  `, "utf8");
17724
17898
  }
17725
- const consumerDockerfile = path39.join(ctx.cwd, "Dockerfile.preview");
17899
+ const consumerDockerfile = path40.join(ctx.cwd, "Dockerfile.preview");
17726
17900
  const { stat } = await import("fs/promises");
17727
17901
  let hasConsumerDockerfile = false;
17728
17902
  try {
@@ -17906,8 +18080,8 @@ var init_tickShellRunner = __esm({
17906
18080
  });
17907
18081
 
17908
18082
  // src/scripts/runScheduledImplementationTick.ts
17909
- import * as fs41 from "fs";
17910
- import * as path40 from "path";
18083
+ import * as fs42 from "fs";
18084
+ import * as path41 from "path";
17911
18085
  var runScheduledImplementationTick;
17912
18086
  var init_runScheduledImplementationTick = __esm({
17913
18087
  "src/scripts/runScheduledImplementationTick.ts"() {
@@ -17927,14 +18101,14 @@ var init_runScheduledImplementationTick = __esm({
17927
18101
  ctx.output.reason = `runScheduledImplementationTick: args.slug or ctx.args.${slugArg} must be non-empty capability slug`;
17928
18102
  return;
17929
18103
  }
17930
- const capability = resolveCapabilityFolder(slug2, path40.join(ctx.cwd, jobsDir));
18104
+ const capability = resolveCapabilityFolder(slug2, path41.join(ctx.cwd, jobsDir));
17931
18105
  if (!capability) {
17932
18106
  ctx.output.exitCode = 99;
17933
18107
  ctx.output.reason = `runScheduledImplementationTick: capability folder not found or incomplete: ${slug2} (searched ${jobsDir} and company store)`;
17934
18108
  return;
17935
18109
  }
17936
- const shellPath = path40.join(profile.dir, shell);
17937
- if (!fs41.existsSync(shellPath)) {
18110
+ const shellPath = path41.join(profile.dir, shell);
18111
+ if (!fs42.existsSync(shellPath)) {
17938
18112
  ctx.output.exitCode = 99;
17939
18113
  ctx.output.reason = `runScheduledImplementationTick: shell not found: ${shell} (looked in ${profile.dir})`;
17940
18114
  return;
@@ -17965,8 +18139,8 @@ var init_runScheduledImplementationTick = __esm({
17965
18139
  });
17966
18140
 
17967
18141
  // src/scripts/runTickScript.ts
17968
- import * as fs42 from "fs";
17969
- import * as path41 from "path";
18142
+ import * as fs43 from "fs";
18143
+ import * as path42 from "path";
17970
18144
  var runTickScript;
17971
18145
  var init_runTickScript = __esm({
17972
18146
  "src/scripts/runTickScript.ts"() {
@@ -17985,10 +18159,10 @@ var init_runTickScript = __esm({
17985
18159
  ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
17986
18160
  return;
17987
18161
  }
17988
- const capability = readCapabilityFolder(path41.join(ctx.cwd, jobsDir), slug2);
18162
+ const capability = readCapabilityFolder(path42.join(ctx.cwd, jobsDir), slug2);
17989
18163
  if (!capability) {
17990
18164
  ctx.output.exitCode = 99;
17991
- ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path41.join(ctx.cwd, jobsDir, slug2)}`;
18165
+ ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path42.join(ctx.cwd, jobsDir, slug2)}`;
17992
18166
  return;
17993
18167
  }
17994
18168
  const tickScript = capability.config.tickScript;
@@ -17997,8 +18171,8 @@ var init_runTickScript = __esm({
17997
18171
  ctx.output.reason = `runTickScript: capability ${slug2} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
17998
18172
  return;
17999
18173
  }
18000
- const scriptPath = path41.isAbsolute(tickScript) ? tickScript : path41.join(ctx.cwd, tickScript);
18001
- if (!fs42.existsSync(scriptPath)) {
18174
+ const scriptPath = path42.isAbsolute(tickScript) ? tickScript : path42.join(ctx.cwd, tickScript);
18175
+ if (!fs43.existsSync(scriptPath)) {
18002
18176
  ctx.output.exitCode = 99;
18003
18177
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
18004
18178
  return;
@@ -19143,7 +19317,7 @@ var init_warmupMcp = __esm({
19143
19317
  });
19144
19318
 
19145
19319
  // src/scripts/writeAgentRunSummary.ts
19146
- import * as fs43 from "fs";
19320
+ import * as fs44 from "fs";
19147
19321
  var writeAgentRunSummary;
19148
19322
  var init_writeAgentRunSummary = __esm({
19149
19323
  "src/scripts/writeAgentRunSummary.ts"() {
@@ -19169,7 +19343,7 @@ var init_writeAgentRunSummary = __esm({
19169
19343
  if (reason) lines.push(`- **Reason:** ${reason}`);
19170
19344
  lines.push("");
19171
19345
  try {
19172
- fs43.appendFileSync(summaryPath, `${lines.join("\n")}
19346
+ fs44.appendFileSync(summaryPath, `${lines.join("\n")}
19173
19347
  `);
19174
19348
  } catch {
19175
19349
  }
@@ -19427,6 +19601,7 @@ var init_scripts = __esm({
19427
19601
  planTaskJobs,
19428
19602
  dispatchNextTaskJob,
19429
19603
  runScheduledImplementationTick,
19604
+ runScheduledExecutableTick: runScheduledImplementationTick,
19430
19605
  runTickScript,
19431
19606
  runPreviewBuild,
19432
19607
  advanceManagedGoal,
@@ -19495,53 +19670,53 @@ var init_scripts = __esm({
19495
19670
  // src/stateWorkspace.ts
19496
19671
  import { execFileSync as execFileSync24 } from "child_process";
19497
19672
  import * as crypto3 from "crypto";
19498
- import * as fs44 from "fs";
19673
+ import * as fs45 from "fs";
19499
19674
  import * as os7 from "os";
19500
- import * as path42 from "path";
19675
+ import * as path43 from "path";
19501
19676
  function writeLocalFile(cwd, relativePath, content) {
19502
- const fullPath = path42.join(cwd, relativePath);
19503
- fs44.mkdirSync(path42.dirname(fullPath), { recursive: true });
19504
- fs44.writeFileSync(fullPath, content);
19677
+ const fullPath = path43.join(cwd, relativePath);
19678
+ fs45.mkdirSync(path43.dirname(fullPath), { recursive: true });
19679
+ fs45.writeFileSync(fullPath, content);
19505
19680
  }
19506
19681
  function copyPath(source, target) {
19507
- const st = fs44.lstatSync(source);
19508
- fs44.rmSync(target, { recursive: true, force: true });
19682
+ const st = fs45.lstatSync(source);
19683
+ fs45.rmSync(target, { recursive: true, force: true });
19509
19684
  if (st.isSymbolicLink()) return;
19510
- fs44.mkdirSync(path42.dirname(target), { recursive: true });
19511
- fs44.cpSync(source, target, { recursive: true, force: true });
19685
+ fs45.mkdirSync(path43.dirname(target), { recursive: true });
19686
+ fs45.cpSync(source, target, { recursive: true, force: true });
19512
19687
  }
19513
19688
  function overlayDirectoryChildren(cwd, sourceDir, localDir) {
19514
- if (!fs44.existsSync(sourceDir)) return;
19515
- for (const entry of fs44.readdirSync(sourceDir, { withFileTypes: true })) {
19516
- const source = path42.join(sourceDir, entry.name);
19517
- const target = path42.join(cwd, localDir, entry.name);
19689
+ if (!fs45.existsSync(sourceDir)) return;
19690
+ for (const entry of fs45.readdirSync(sourceDir, { withFileTypes: true })) {
19691
+ const source = path43.join(sourceDir, entry.name);
19692
+ const target = path43.join(cwd, localDir, entry.name);
19518
19693
  copyPath(source, target);
19519
19694
  }
19520
19695
  }
19521
19696
  function hydrateStateWorkspace(config, cwd) {
19522
19697
  if (process.env.VITEST && process.env[TEST_FETCH_ENV] !== "1") return;
19523
19698
  const parsed = parseStateRepo(config);
19524
- const hydrateKey = `${path42.resolve(cwd)}|${parsed.owner}/${parsed.repo}|${parsed.basePath}|${parsed.branch}`;
19699
+ const hydrateKey = `${path43.resolve(cwd)}|${parsed.owner}/${parsed.repo}|${parsed.basePath}|${parsed.branch}`;
19525
19700
  if (hydratedWorkspaces.has(hydrateKey)) return;
19526
19701
  const snapshotRoot = fetchStateSnapshot(parsed);
19527
19702
  for (const mapping of DIR_MAPPINGS) {
19528
- overlayDirectoryChildren(cwd, path42.join(snapshotRoot, mapping.stateDir), mapping.localDir);
19703
+ overlayDirectoryChildren(cwd, path43.join(snapshotRoot, mapping.stateDir), mapping.localDir);
19529
19704
  }
19530
19705
  for (const mapping of FILE_MAPPINGS) {
19531
- const source = path42.join(snapshotRoot, mapping.statePath);
19532
- if (fs44.existsSync(source) && !fs44.lstatSync(source).isSymbolicLink() && fs44.statSync(source).isFile()) {
19533
- writeLocalFile(cwd, mapping.localPath, fs44.readFileSync(source, "utf-8"));
19706
+ const source = path43.join(snapshotRoot, mapping.statePath);
19707
+ if (fs45.existsSync(source) && !fs45.lstatSync(source).isSymbolicLink() && fs45.statSync(source).isFile()) {
19708
+ writeLocalFile(cwd, mapping.localPath, fs45.readFileSync(source, "utf-8"));
19534
19709
  }
19535
19710
  }
19536
19711
  hydratedWorkspaces.add(hydrateKey);
19537
19712
  }
19538
19713
  function fetchStateSnapshot(parsed) {
19539
- const cacheDir = path42.join(cacheRoot2(), cacheKey3(parsed));
19714
+ const cacheDir = path43.join(cacheRoot2(), cacheKey3(parsed));
19540
19715
  const url = `https://github.com/${parsed.owner}/${parsed.repo}.git`;
19541
19716
  try {
19542
- fs44.mkdirSync(path42.dirname(cacheDir), { recursive: true });
19543
- if (!fs44.existsSync(path42.join(cacheDir, ".git"))) {
19544
- fs44.rmSync(cacheDir, { recursive: true, force: true });
19717
+ fs45.mkdirSync(path43.dirname(cacheDir), { recursive: true });
19718
+ if (!fs45.existsSync(path43.join(cacheDir, ".git"))) {
19719
+ fs45.rmSync(cacheDir, { recursive: true, force: true });
19545
19720
  runGit3(["clone", "--no-checkout", "--filter=blob:none", url, cacheDir]);
19546
19721
  }
19547
19722
  runGit3(["-C", cacheDir, "remote", "set-url", "origin", url]);
@@ -19556,10 +19731,10 @@ function fetchStateSnapshot(parsed) {
19556
19731
  `stateWorkspace: failed to fetch ${parsed.owner}/${parsed.repo}:${parsed.basePath}@${parsed.branch}: ${msg}`
19557
19732
  );
19558
19733
  }
19559
- return path42.join(cacheDir, parsed.basePath);
19734
+ return path43.join(cacheDir, parsed.basePath);
19560
19735
  }
19561
19736
  function cacheRoot2() {
19562
- return process.env[CACHE_ENV2]?.trim() || path42.join(os7.homedir(), ".cache", "kody", "state-repo");
19737
+ return process.env[CACHE_ENV2]?.trim() || path43.join(os7.homedir(), ".cache", "kody", "state-repo");
19563
19738
  }
19564
19739
  function cacheKey3(parsed) {
19565
19740
  return crypto3.createHash("sha256").update(`${parsed.owner}/${parsed.repo}#${parsed.branch}#${parsed.basePath}`).digest("hex").slice(0, 24);
@@ -19599,15 +19774,15 @@ var init_stateWorkspace = __esm({
19599
19774
  "use strict";
19600
19775
  init_stateRepo();
19601
19776
  DIR_MAPPINGS = [
19602
- { stateDir: "capabilities", localDir: path42.join(".kody", "capabilities") },
19603
- { stateDir: "agents", localDir: path42.join(".kody", "agents") },
19604
- { stateDir: "context", localDir: path42.join(".kody", "context") },
19605
- { stateDir: "memory", localDir: path42.join(".kody", "memory") }
19777
+ { stateDir: "capabilities", localDir: path43.join(".kody", "capabilities") },
19778
+ { stateDir: "agents", localDir: path43.join(".kody", "agents") },
19779
+ { stateDir: "context", localDir: path43.join(".kody", "context") },
19780
+ { stateDir: "memory", localDir: path43.join(".kody", "memory") }
19606
19781
  ];
19607
19782
  FILE_MAPPINGS = [
19608
- { statePath: "instructions.md", localPath: path42.join(".kody", "instructions.md") },
19609
- { statePath: "variables.json", localPath: path42.join(".kody", "variables.json") },
19610
- { statePath: "secrets.enc", localPath: path42.join(".kody", "secrets.enc") }
19783
+ { statePath: "instructions.md", localPath: path43.join(".kody", "instructions.md") },
19784
+ { statePath: "variables.json", localPath: path43.join(".kody", "variables.json") },
19785
+ { statePath: "secrets.enc", localPath: path43.join(".kody", "secrets.enc") }
19611
19786
  ];
19612
19787
  CACHE_ENV2 = "KODY_STATE_REPO_CACHE";
19613
19788
  TEST_FETCH_ENV = "KODY_STATE_WORKSPACE_FETCH_FOR_TESTS";
@@ -19681,8 +19856,8 @@ var init_tools = __esm({
19681
19856
 
19682
19857
  // src/executor.ts
19683
19858
  import { spawn as spawn7 } from "child_process";
19684
- import * as fs45 from "fs";
19685
- import * as path43 from "path";
19859
+ import * as fs46 from "fs";
19860
+ import * as path44 from "path";
19686
19861
  function isMutatingPostflight(scriptName) {
19687
19862
  return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
19688
19863
  }
@@ -19727,9 +19902,7 @@ function jobReferenceBlock(profileName, profile, data) {
19727
19902
  const jobId = typeof data.jobId === "string" && data.jobId.length > 0 ? data.jobId : null;
19728
19903
  const flavor = typeof data.jobFlavor === "string" && data.jobFlavor.length > 0 ? data.jobFlavor : null;
19729
19904
  const schedule = typeof data.jobSchedule === "string" && data.jobSchedule.length > 0 ? data.jobSchedule : null;
19730
- const isJob2 = Boolean(
19731
- jobId || flavor || schedule || data.jobCapability || data.selectedImplementation || data.jobWhy
19732
- );
19905
+ const isJob2 = Boolean(jobId || flavor || schedule || data.jobCapability || data.selectedImplementation || data.jobWhy);
19733
19906
  if (!isJob2) return null;
19734
19907
  const capability = typeof data.jobCapability === "string" && data.jobCapability.length > 0 ? data.jobCapability : profile.implementation ? profile.name : null;
19735
19908
  const implementation = typeof profile.implementation === "string" && profile.implementation.length > 0 ? profile.implementation : typeof data.selectedImplementation === "string" && data.selectedImplementation.length > 0 ? data.selectedImplementation : profileName;
@@ -19907,7 +20080,7 @@ async function runImplementation(profileName, input) {
19907
20080
  const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
19908
20081
  const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
19909
20082
  const invokeAgent = async (prompt) => {
19910
- const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path43.isAbsolute(p) ? p : path43.resolve(profile.dir, p)).filter((p) => p.length > 0);
20083
+ const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path44.isAbsolute(p) ? p : path44.resolve(profile.dir, p)).filter((p) => p.length > 0);
19911
20084
  const syntheticPath = ctx.data.syntheticPluginPath;
19912
20085
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
19913
20086
  const agents = loadSubagents(profile);
@@ -20309,6 +20482,7 @@ function handoffToJob(handoff) {
20309
20482
  workflow: handoff.workflow,
20310
20483
  implementation: handoff.implementation,
20311
20484
  cliArgs: handoff.cliArgs,
20485
+ workflowFacts: handoff.workflowFacts,
20312
20486
  flavor: "instant",
20313
20487
  saveReport: handoff.saveReport === true,
20314
20488
  resultTarget: handoff.resultTarget
@@ -20333,17 +20507,17 @@ function clearStampedLifecycleLabels(profile, ctx) {
20333
20507
  function resolveProfilePath(profileName) {
20334
20508
  const found = resolveImplementation(profileName);
20335
20509
  if (found) return found;
20336
- const here = path43.dirname(new URL(import.meta.url).pathname);
20510
+ const here = path44.dirname(new URL(import.meta.url).pathname);
20337
20511
  const candidates = [
20338
- path43.join(here, "implementations", profileName, "profile.json"),
20512
+ path44.join(here, "implementations", profileName, "profile.json"),
20339
20513
  // same-dir sibling (dev)
20340
- path43.join(here, "..", "implementations", profileName, "profile.json"),
20514
+ path44.join(here, "..", "implementations", profileName, "profile.json"),
20341
20515
  // up one (prod: dist/bin → dist/implementations)
20342
- path43.join(here, "..", "src", "implementations", profileName, "profile.json")
20516
+ path44.join(here, "..", "src", "implementations", profileName, "profile.json")
20343
20517
  // fallback
20344
20518
  ];
20345
20519
  for (const c of candidates) {
20346
- if (fs45.existsSync(c)) return c;
20520
+ if (fs46.existsSync(c)) return c;
20347
20521
  }
20348
20522
  return candidates[0];
20349
20523
  }
@@ -20458,8 +20632,8 @@ function resolveShellTimeoutMs(entry) {
20458
20632
  }
20459
20633
  async function runShellEntry(entry, ctx, profile) {
20460
20634
  const shellName = entry.shell;
20461
- const shellPath = path43.join(profile.dir, shellName);
20462
- if (!fs45.existsSync(shellPath)) {
20635
+ const shellPath = path44.join(profile.dir, shellName);
20636
+ if (!fs46.existsSync(shellPath)) {
20463
20637
  ctx.skipAgent = true;
20464
20638
  ctx.output.exitCode = 99;
20465
20639
  ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
@@ -20610,101 +20784,6 @@ var init_executor = __esm({
20610
20784
  }
20611
20785
  });
20612
20786
 
20613
- // src/workflowDefinitions.ts
20614
- import * as fs46 from "fs";
20615
- import * as path44 from "path";
20616
- function isWorkflowDefinitionId(value) {
20617
- return WORKFLOW_ID_PATTERN.test(value);
20618
- }
20619
- function workflowDefinitionPath(id) {
20620
- if (!isWorkflowDefinitionId(id)) {
20621
- throw new Error(`Invalid workflow id "${id}"`);
20622
- }
20623
- return `workflows/${id}/workflow.json`;
20624
- }
20625
- function normalizeWorkflowDefinition(value) {
20626
- if (!value || typeof value !== "object" || Array.isArray(value)) return null;
20627
- const raw = value;
20628
- const name = typeof raw.name === "string" ? raw.name.trim() : "";
20629
- const workflow = parseCapabilityWorkflow({ steps: raw.steps });
20630
- const steps = workflow?.steps;
20631
- const capabilities = steps ? steps.map((step) => step.capability) : normalizeWorkflowCapabilities(raw.capabilities);
20632
- if (!name || capabilities.length === 0) return null;
20633
- return {
20634
- version: 1,
20635
- name,
20636
- capabilities,
20637
- ...steps ? { steps } : {},
20638
- ...typeof raw.createdAt === "string" ? { createdAt: raw.createdAt } : {},
20639
- ...typeof raw.updatedAt === "string" ? { updatedAt: raw.updatedAt } : {}
20640
- };
20641
- }
20642
- function readWorkflowDefinition(config, cwd, id) {
20643
- const file = readStateText(config, cwd, workflowDefinitionPath(id));
20644
- if (!file) return readCompanyStoreWorkflowDefinition(id);
20645
- return parseWorkflowDefinition(file.content);
20646
- }
20647
- function workflowDefinitionToCapabilityFolder(id, workflow, source = workflowDefinitionPath(id)) {
20648
- return {
20649
- slug: id,
20650
- dir: path44.dirname(source),
20651
- profilePath: source,
20652
- bodyPath: source,
20653
- title: workflow.name,
20654
- body: "",
20655
- rawBody: "",
20656
- rawProfile: { name: id, workflow },
20657
- config: {
20658
- action: id,
20659
- workflow: workflowDefinitionToConfig(workflow),
20660
- describe: workflow.name
20661
- }
20662
- };
20663
- }
20664
- function normalizeWorkflowCapabilities(value) {
20665
- if (!Array.isArray(value)) return [];
20666
- const seen = /* @__PURE__ */ new Set();
20667
- const capabilities = [];
20668
- for (const item of value) {
20669
- if (typeof item !== "string") continue;
20670
- const slug2 = item.trim();
20671
- if (!CAPABILITY_ID_PATTERN.test(slug2) || seen.has(slug2)) continue;
20672
- seen.add(slug2);
20673
- capabilities.push(slug2);
20674
- }
20675
- return capabilities;
20676
- }
20677
- function workflowDefinitionToConfig(workflow) {
20678
- return {
20679
- steps: workflow.steps ?? workflow.capabilities.map((capability) => ({ capability }))
20680
- };
20681
- }
20682
- function readCompanyStoreWorkflowDefinition(id) {
20683
- const root = getCompanyStoreAssetRoot("workflows");
20684
- if (!root) return null;
20685
- const filePath = path44.join(root, id, "workflow.json");
20686
- if (!fs46.existsSync(filePath)) return null;
20687
- return parseWorkflowDefinition(fs46.readFileSync(filePath, "utf8"));
20688
- }
20689
- function parseWorkflowDefinition(content) {
20690
- try {
20691
- return normalizeWorkflowDefinition(JSON.parse(content));
20692
- } catch {
20693
- return null;
20694
- }
20695
- }
20696
- var WORKFLOW_ID_PATTERN, CAPABILITY_ID_PATTERN;
20697
- var init_workflowDefinitions = __esm({
20698
- "src/workflowDefinitions.ts"() {
20699
- "use strict";
20700
- init_capabilityFolders();
20701
- init_companyStore();
20702
- init_stateRepo();
20703
- WORKFLOW_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{0,79}$/;
20704
- CAPABILITY_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,79}$/;
20705
- }
20706
- });
20707
-
20708
20787
  // src/job.ts
20709
20788
  var job_exports = {};
20710
20789
  __export(job_exports, {
@@ -20748,6 +20827,7 @@ function validateJob(input) {
20748
20827
  schedule: typeof j.schedule === "string" ? j.schedule : void 0,
20749
20828
  target: typeof j.target === "number" ? j.target : void 0,
20750
20829
  cliArgs: j.cliArgs ?? {},
20830
+ workflowFacts: j.workflowFacts && typeof j.workflowFacts === "object" && !Array.isArray(j.workflowFacts) ? j.workflowFacts : void 0,
20751
20831
  flavor: j.flavor,
20752
20832
  force: j.force === true,
20753
20833
  saveReport: j.saveReport === true,
@@ -20824,6 +20904,7 @@ async function runCapabilityImplementationStep(valid, profileName, capabilityIde
20824
20904
  preloadedData.selectedImplementation = profileName;
20825
20905
  if (valid.schedule !== void 0 && valid.schedule.length > 0) preloadedData.jobSchedule = valid.schedule;
20826
20906
  if (valid.saveReport === true) preloadedData.jobSaveReport = true;
20907
+ if (valid.force === true) preloadedData.jobForce = true;
20827
20908
  if (valid.resultTarget) preloadedData.capabilityResultTarget = valid.resultTarget;
20828
20909
  if (capabilityContext) {
20829
20910
  preloadedData.capabilitySlug = capabilityContext.slug;
@@ -20876,13 +20957,15 @@ async function runCapabilityWorkflow(parent, workflow, capability, base) {
20876
20957
  workflowTitle: capability.title,
20877
20958
  workflowStepCount: workflow.steps.length,
20878
20959
  workflowIssueNumber: workflowIssueNumber(parent),
20960
+ workflowFacts: parent.workflowFacts ?? {},
20879
20961
  workflowStack: [
20880
20962
  ...Array.isArray(base.preloadedData?.workflowStack) ? base.preloadedData.workflowStack.filter((entry) => typeof entry === "string") : [],
20881
20963
  capability.slug
20882
20964
  ]
20883
20965
  };
20884
20966
  let result = { exitCode: 0 };
20885
- for (let index = 0; index < workflow.steps.length; index++) {
20967
+ const startIndex = workflowResumeStartIndex(workflow.steps, parent.resultTarget?.evidence);
20968
+ for (let index = startIndex; index < workflow.steps.length; index++) {
20886
20969
  const step = workflow.steps[index];
20887
20970
  const label = step.action ?? step.capability;
20888
20971
  if (!shouldRunWorkflowStep(step, chainData)) {
@@ -21014,10 +21097,23 @@ function valueMatches(actual, expected) {
21014
21097
  return actual === expected;
21015
21098
  }
21016
21099
  function workflowStepTargetNumber(step, parent, chainData) {
21017
- if (step.target === "pr") return workflowPrNumber(chainData) ?? targetFromCliArgs(step.cliArgs ?? {});
21100
+ if (step.target === "pr")
21101
+ return workflowPrNumber(chainData) ?? workflowTargetFactNumber(step, chainData) ?? targetFromCliArgs(step.cliArgs ?? {});
21018
21102
  if (step.target === "issue") return workflowIssueNumber(parent);
21019
21103
  return typeof parent.target === "number" ? parent.target : targetFromCliArgs({ ...parent.cliArgs, ...step.cliArgs ?? {} });
21020
21104
  }
21105
+ function workflowResumeStartIndex(steps, evidence) {
21106
+ if (!evidence) return 0;
21107
+ const index = steps.findIndex((step) => step.evidence === evidence);
21108
+ return index >= 0 ? index : 0;
21109
+ }
21110
+ function workflowTargetFactNumber(step, data) {
21111
+ if (!step.targetFact) return void 0;
21112
+ const facts = data.workflowFacts;
21113
+ if (!facts || typeof facts !== "object" || Array.isArray(facts)) return void 0;
21114
+ const value = facts[step.targetFact];
21115
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
21116
+ }
21021
21117
  function workflowIssueNumber(parent) {
21022
21118
  return typeof parent.target === "number" ? parent.target : targetFromCliArgs(parent.cliArgs);
21023
21119
  }
@@ -23032,8 +23128,10 @@ ${CI_HELP}`);
23032
23128
  `);
23033
23129
  const buildOnly = dispatch2.implementation === "preview-build";
23034
23130
  if (args.skipInstall || buildOnly) {
23035
- process.stdout.write(`\u2192 kody: skipping dep install (${buildOnly ? "build-only implementation" : "--skip-install"})
23036
- `);
23131
+ process.stdout.write(
23132
+ `\u2192 kody: skipping dep install (${buildOnly ? "build-only implementation" : "--skip-install"})
23133
+ `
23134
+ );
23037
23135
  } else {
23038
23136
  const code = installDeps(pm, cwd);
23039
23137
  if (code !== 0) {
@@ -26206,7 +26304,8 @@ Usage:
26206
26304
  kody-engine release --issue <N> [--cwd <path>] [--verbose|--quiet]
26207
26305
  kody-engine init [--cwd <path>] [--verbose|--quiet]
26208
26306
  kody-engine <action> [--cwd <path>] [--verbose|--quiet]
26209
- kody-engine implementation <name> [--cwd <path>] [--verbose|--quiet]
26307
+ kody-engine exec <name> [--cwd <path>] [--verbose|--quiet]
26308
+ kody-engine implementation <name> [--cwd <path>] [--verbose|--quiet]
26210
26309
  kody-engine ci [preflight flags \u2014 see: kody-engine ci --help]
26211
26310
  kody-engine chat [chat flags \u2014 see: kody-engine chat --help]
26212
26311
  kody-engine stats [--since 7d|--run <id>|--json|--cwd <path>]
@@ -26214,7 +26313,7 @@ Usage:
26214
26313
  kody-engine version
26215
26314
 
26216
26315
  Top-level work commands are capabilities. A capability owns the public command name
26217
- and resolves the implementation that runs it. Use the implementation command only for
26316
+ and resolves the implementation that runs it. Use the exec command only for
26218
26317
  internal implementation profiles and scheduled helpers.
26219
26318
 
26220
26319
  Exit codes:
@@ -26250,10 +26349,10 @@ function parseArgs(argv) {
26250
26349
  if (cmd === "stats") {
26251
26350
  return { ...result, command: "stats", statsArgv: argv.slice(1) };
26252
26351
  }
26253
- if (cmd === "implementation") {
26352
+ if (cmd === "implementation" || cmd === "exec") {
26254
26353
  const implementationName = argv[1];
26255
26354
  if (!implementationName || implementationName.startsWith("-")) {
26256
- result.errors.push("implementation requires a name");
26355
+ result.errors.push(`${cmd} requires a name`);
26257
26356
  return result;
26258
26357
  }
26259
26358
  if (!resolveImplementation(implementationName)) {
@@ -26288,7 +26387,7 @@ function parseArgs(argv) {
26288
26387
  return result;
26289
26388
  }
26290
26389
  const discoveredActions = listCapabilityActions().map((e) => e.action);
26291
- const available = ["ci", "chat", "stats", "implementation", "help", "version", ...discoveredActions];
26390
+ const available = ["ci", "chat", "stats", "exec", "implementation", "help", "version", ...discoveredActions];
26292
26391
  result.errors.push(`unknown command: ${cmd} (available: ${available.join(", ")})`);
26293
26392
  return result;
26294
26393
  }