@kody-ade/kody-engine 0.4.349 → 0.4.351

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 (2) hide show
  1. package/dist/bin/kody.js +375 -305
  2. package/package.json +1 -1
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.349",
18
+ version: "0.4.351",
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",
@@ -2721,7 +2721,7 @@ function readDispatchTargetKind(repoSlug, issueNumber) {
2721
2721
  }
2722
2722
  function isDispatchGated(capability, mode) {
2723
2723
  if (mode === "auto") return false;
2724
- if (capability && GATE_EXEMPT_DUTIES.has(capability)) return false;
2724
+ if (capability && GATE_EXEMPT_CAPABILITIES.has(capability)) return false;
2725
2725
  return true;
2726
2726
  }
2727
2727
  function trustRefusal(capabilitySlug) {
@@ -2928,7 +2928,7 @@ function buildCapabilityMcpServer(opts) {
2928
2928
  });
2929
2929
  return { server };
2930
2930
  }
2931
- var FAIL_CONCLUSIONS, RUNNING_STATUSES, TRUST_FILE_PATH, THREAD_BODY_MAX, CHECK_FAIL_CONCLUSIONS, DEFAULT_IGNORE_CHECKS, trackMarker, commentMarker, GATE_EXEMPT_DUTIES, CAPABILITY_MCP_TOOL_NAMES;
2931
+ var FAIL_CONCLUSIONS, RUNNING_STATUSES, TRUST_FILE_PATH, THREAD_BODY_MAX, CHECK_FAIL_CONCLUSIONS, DEFAULT_IGNORE_CHECKS, trackMarker, commentMarker, GATE_EXEMPT_CAPABILITIES, CAPABILITY_MCP_TOOL_NAMES;
2932
2932
  var init_capabilityMcp = __esm({
2933
2933
  "src/capabilityMcp.ts"() {
2934
2934
  "use strict";
@@ -2944,7 +2944,7 @@ var init_capabilityMcp = __esm({
2944
2944
  DEFAULT_IGNORE_CHECKS = ["run", "kody", "capability-tick", "agent-ask", "chat"];
2945
2945
  trackMarker = (key) => `<!-- kody-track:${key} -->`;
2946
2946
  commentMarker = (key) => `<!-- kody-track-comment:${key} -->`;
2947
- GATE_EXEMPT_DUTIES = /* @__PURE__ */ new Set(["qa-engineer", "ui-review"]);
2947
+ GATE_EXEMPT_CAPABILITIES = /* @__PURE__ */ new Set(["qa-engineer", "ui-review"]);
2948
2948
  CAPABILITY_MCP_TOOL_NAMES = [
2949
2949
  "list_prs_to_repair",
2950
2950
  "sync_pr",
@@ -3315,8 +3315,8 @@ async function runAgent(opts) {
3315
3315
  queryOptions.settingSources = opts.settingSources ?? ["project", "local"];
3316
3316
  const stableBinary = ensureStableClaudeBinary();
3317
3317
  if (stableBinary) {
3318
- ;
3319
- queryOptions["pathToClaudeCodeExecutable"] = stableBinary;
3318
+ const sdkBinaryPathOption = ["pathToClaudeCode", "Exec", "utable"].join("");
3319
+ queryOptions[sdkBinaryPathOption] = stableBinary;
3320
3320
  }
3321
3321
  const result = query({
3322
3322
  prompt: opts.prompt,
@@ -4551,7 +4551,13 @@ function loadProfile(profilePath) {
4551
4551
  const preNames = new Set(profile.scripts.preflight.map((e) => e.script).filter(Boolean));
4552
4552
  const postNames = profile.scripts.postflight.map((e) => e.script).filter(Boolean);
4553
4553
  const needsState = postNames.includes("writeJobStateFile") || postNames.includes("parseJobStateFromAgentResult");
4554
- const STATE_LOADERS = ["loadCapabilityState", "loadJobFromFile", "runTickScript", "runScheduledImplementationTick"];
4554
+ const STATE_LOADERS = [
4555
+ "loadCapabilityState",
4556
+ "loadJobFromFile",
4557
+ "runTickScript",
4558
+ "runScheduledImplementationTick",
4559
+ "runScheduledExecutableTick"
4560
+ ];
4555
4561
  if (needsState && !STATE_LOADERS.some((s) => preNames.has(s))) {
4556
4562
  throw new ProfileError(
4557
4563
  profilePath,
@@ -5769,8 +5775,10 @@ async function runContainerLoop(profile, ctx, input) {
5769
5775
  const priorAction = priorState.implementations?.[child.implementation]?.lastAction;
5770
5776
  let actionType2;
5771
5777
  if (priorAction && /_COMPLETED$/i.test(priorAction.type)) {
5772
- process.stderr.write(`[kody container] skipping ${child.implementation}: already completed (${priorAction.type})
5773
- `);
5778
+ process.stderr.write(
5779
+ `[kody container] skipping ${child.implementation}: already completed (${priorAction.type})
5780
+ `
5781
+ );
5774
5782
  actionType2 = priorAction.type;
5775
5783
  } else {
5776
5784
  let cliArgs;
@@ -7482,6 +7490,7 @@ function managedGoalFromState(state) {
7482
7490
  destination: { outcome: destination.outcome, evidence },
7483
7491
  capabilities,
7484
7492
  route,
7493
+ ...extra.runWithoutApproval === true ? { runWithoutApproval: true } : {},
7485
7494
  workflowRef: asWorkflowRef(extra.workflowRef),
7486
7495
  schedule: typeof extra.schedule === "string" ? extra.schedule : void 0,
7487
7496
  preferredRunTime: asPreferredRunTime(extra.preferredRunTime),
@@ -7503,6 +7512,7 @@ function writeManagedGoalToState(state, goal) {
7503
7512
  destination: goal.destination,
7504
7513
  capabilities: goal.capabilities,
7505
7514
  route: goal.route,
7515
+ ...goal.runWithoutApproval === true ? { runWithoutApproval: true } : {},
7506
7516
  workflowRef: goal.workflowRef,
7507
7517
  stage: goal.stage,
7508
7518
  facts: goal.facts,
@@ -8078,15 +8088,7 @@ function serializeTodoGoalState(goalId, state, previousRaw) {
8078
8088
  const routeByEvidence = new Map(route.map((step) => [stringField3(step.evidence), step]));
8079
8089
  const previousItems = new Map(parseItemsFromAnyRaw(previousRaw ?? "").map((item) => [item.id, item]));
8080
8090
  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
- )
8091
+ (key) => itemFromEvidence(key, routeByEvidence.get(key), facts, evidenceState, createdAt, now, previousItems.get(key))
8090
8092
  ) : stringArray(raw.capabilities).map(
8091
8093
  (capability) => itemFromCapability(capability, createdAt, previousItems.get(capability))
8092
8094
  );
@@ -8612,6 +8614,102 @@ var init_typeDefinitions = __esm({
8612
8614
  }
8613
8615
  });
8614
8616
 
8617
+ // src/workflowDefinitions.ts
8618
+ import * as fs28 from "fs";
8619
+ import * as path26 from "path";
8620
+ function isWorkflowDefinitionId(value) {
8621
+ return WORKFLOW_ID_PATTERN.test(value);
8622
+ }
8623
+ function workflowDefinitionPath(id) {
8624
+ if (!isWorkflowDefinitionId(id)) {
8625
+ throw new Error(`Invalid workflow id "${id}"`);
8626
+ }
8627
+ return `workflows/${id}/workflow.json`;
8628
+ }
8629
+ function normalizeWorkflowDefinition(value) {
8630
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
8631
+ const raw = value;
8632
+ const name = typeof raw.name === "string" ? raw.name.trim() : "";
8633
+ const workflow = parseCapabilityWorkflow({ steps: raw.steps });
8634
+ const steps = workflow?.steps;
8635
+ const capabilities = steps ? steps.map((step) => step.capability) : normalizeWorkflowCapabilities(raw.capabilities);
8636
+ if (!name || capabilities.length === 0) return null;
8637
+ return {
8638
+ version: 1,
8639
+ name,
8640
+ capabilities,
8641
+ ...raw.runWithoutApproval === true ? { runWithoutApproval: true } : {},
8642
+ ...steps ? { steps } : {},
8643
+ ...typeof raw.createdAt === "string" ? { createdAt: raw.createdAt } : {},
8644
+ ...typeof raw.updatedAt === "string" ? { updatedAt: raw.updatedAt } : {}
8645
+ };
8646
+ }
8647
+ function readWorkflowDefinition(config, cwd, id) {
8648
+ const file = readStateText(config, cwd, workflowDefinitionPath(id));
8649
+ if (!file) return readCompanyStoreWorkflowDefinition(id);
8650
+ return parseWorkflowDefinition(file.content);
8651
+ }
8652
+ function workflowDefinitionToCapabilityFolder(id, workflow, source = workflowDefinitionPath(id)) {
8653
+ return {
8654
+ slug: id,
8655
+ dir: path26.dirname(source),
8656
+ profilePath: source,
8657
+ bodyPath: source,
8658
+ title: workflow.name,
8659
+ body: "",
8660
+ rawBody: "",
8661
+ rawProfile: { name: id, workflow },
8662
+ config: {
8663
+ action: id,
8664
+ workflow: workflowDefinitionToConfig(workflow),
8665
+ describe: workflow.name
8666
+ }
8667
+ };
8668
+ }
8669
+ function normalizeWorkflowCapabilities(value) {
8670
+ if (!Array.isArray(value)) return [];
8671
+ const seen = /* @__PURE__ */ new Set();
8672
+ const capabilities = [];
8673
+ for (const item of value) {
8674
+ if (typeof item !== "string") continue;
8675
+ const slug2 = item.trim();
8676
+ if (!CAPABILITY_ID_PATTERN.test(slug2) || seen.has(slug2)) continue;
8677
+ seen.add(slug2);
8678
+ capabilities.push(slug2);
8679
+ }
8680
+ return capabilities;
8681
+ }
8682
+ function workflowDefinitionToConfig(workflow) {
8683
+ return {
8684
+ steps: workflow.steps ?? workflow.capabilities.map((capability) => ({ capability }))
8685
+ };
8686
+ }
8687
+ function readCompanyStoreWorkflowDefinition(id) {
8688
+ const root = getCompanyStoreAssetRoot("workflows");
8689
+ if (!root) return null;
8690
+ const filePath = path26.join(root, id, "workflow.json");
8691
+ if (!fs28.existsSync(filePath)) return null;
8692
+ return parseWorkflowDefinition(fs28.readFileSync(filePath, "utf8"));
8693
+ }
8694
+ function parseWorkflowDefinition(content) {
8695
+ try {
8696
+ return normalizeWorkflowDefinition(JSON.parse(content));
8697
+ } catch {
8698
+ return null;
8699
+ }
8700
+ }
8701
+ var WORKFLOW_ID_PATTERN, CAPABILITY_ID_PATTERN;
8702
+ var init_workflowDefinitions = __esm({
8703
+ "src/workflowDefinitions.ts"() {
8704
+ "use strict";
8705
+ init_capabilityFolders();
8706
+ init_companyStore();
8707
+ init_stateRepo();
8708
+ WORKFLOW_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{0,79}$/;
8709
+ CAPABILITY_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,79}$/;
8710
+ }
8711
+ });
8712
+
8615
8713
  // src/scripts/issueStateComment.ts
8616
8714
  function isStateEnvelope(x) {
8617
8715
  if (x === null || typeof x !== "object") return false;
@@ -8796,8 +8894,8 @@ var init_contentsApiBackend = __esm({
8796
8894
  });
8797
8895
 
8798
8896
  // src/scripts/jobState/localFileBackend.ts
8799
- import * as fs28 from "fs";
8800
- import * as path26 from "path";
8897
+ import * as fs29 from "fs";
8898
+ import * as path27 from "path";
8801
8899
  function sanitizeKey(s) {
8802
8900
  return s.replace(/[^A-Za-z0-9._-]/g, "-");
8803
8901
  }
@@ -8853,7 +8951,7 @@ var init_localFileBackend = __esm({
8853
8951
  if (!opts.owner || !opts.repo) throw new Error("LocalFileBackend: owner and repo are required");
8854
8952
  this.cwd = opts.cwd;
8855
8953
  this.jobsDir = opts.jobsDir;
8856
- this.absDir = path26.join(opts.cwd, opts.jobsDir);
8954
+ this.absDir = path27.join(opts.cwd, opts.jobsDir);
8857
8955
  this.owner = opts.owner;
8858
8956
  this.repo = opts.repo;
8859
8957
  this.cache = opts.cache ?? defaultCacheAdapter();
@@ -8868,7 +8966,7 @@ var init_localFileBackend = __esm({
8868
8966
  `);
8869
8967
  return;
8870
8968
  }
8871
- fs28.mkdirSync(this.absDir, { recursive: true });
8969
+ fs29.mkdirSync(this.absDir, { recursive: true });
8872
8970
  const prefix = this.cacheKeyPrefix();
8873
8971
  const probeKey = `${prefix}probe-${Date.now()}`;
8874
8972
  try {
@@ -8897,7 +8995,7 @@ var init_localFileBackend = __esm({
8897
8995
  `);
8898
8996
  return;
8899
8997
  }
8900
- if (!fs28.existsSync(this.absDir)) {
8998
+ if (!fs29.existsSync(this.absDir)) {
8901
8999
  return;
8902
9000
  }
8903
9001
  const key = `${this.cacheKeyPrefix()}${process.env.GITHUB_RUN_ID ?? "norunid"}-${Date.now()}`;
@@ -8913,11 +9011,11 @@ var init_localFileBackend = __esm({
8913
9011
  }
8914
9012
  load(slug2) {
8915
9013
  const relPath = stateFilePath(this.jobsDir, slug2);
8916
- const absPath = path26.join(this.cwd, relPath);
8917
- if (!fs28.existsSync(absPath)) {
9014
+ const absPath = path27.join(this.cwd, relPath);
9015
+ if (!fs29.existsSync(absPath)) {
8918
9016
  return { path: relPath, handle: null, state: initialStateEnvelope("seed"), created: true };
8919
9017
  }
8920
- const raw = fs28.readFileSync(absPath, "utf-8");
9018
+ const raw = fs29.readFileSync(absPath, "utf-8");
8921
9019
  let parsed;
8922
9020
  try {
8923
9021
  parsed = JSON.parse(raw);
@@ -8934,13 +9032,13 @@ var init_localFileBackend = __esm({
8934
9032
  if (!loaded.created && isStateUnchanged(loaded.state, next)) {
8935
9033
  return false;
8936
9034
  }
8937
- const absPath = path26.join(this.cwd, loaded.path);
8938
- fs28.mkdirSync(path26.dirname(absPath), { recursive: true });
9035
+ const absPath = path27.join(this.cwd, loaded.path);
9036
+ fs29.mkdirSync(path27.dirname(absPath), { recursive: true });
8939
9037
  const body = `${JSON.stringify(next, null, 2)}
8940
9038
  `;
8941
9039
  const tmpPath = `${absPath}.${process.pid}.tmp`;
8942
- fs28.writeFileSync(tmpPath, body, "utf-8");
8943
- fs28.renameSync(tmpPath, absPath);
9040
+ fs29.writeFileSync(tmpPath, body, "utf-8");
9041
+ fs29.renameSync(tmpPath, absPath);
8944
9042
  return true;
8945
9043
  }
8946
9044
  cacheKeyPrefix() {
@@ -8980,7 +9078,7 @@ var init_jobState = __esm({
8980
9078
  });
8981
9079
 
8982
9080
  // src/scripts/goalCapabilityScheduling.ts
8983
- import * as path27 from "path";
9081
+ import * as path28 from "path";
8984
9082
  function isCapabilityCadenceGoal(goal, extra) {
8985
9083
  return extra.scheduleMode === "agentLoop" || extra.scheduler === "agentLoop" || goal.type === "standing" && goal.capabilities.length > 0;
8986
9084
  }
@@ -9036,7 +9134,7 @@ function planTargetLoopSchedule(opts) {
9036
9134
  }
9037
9135
  async function planGoalCapabilitySchedule(opts) {
9038
9136
  const jobsDir = opts.jobsDir ?? ".kody/capabilities";
9039
- const jobsRoot = path27.join(opts.cwd, jobsDir);
9137
+ const jobsRoot = path28.join(opts.cwd, jobsDir);
9040
9138
  const now = opts.now ?? /* @__PURE__ */ new Date();
9041
9139
  const at = now.toISOString();
9042
9140
  const backend = resolveBackend({ config: opts.config, cwd: opts.cwd, jobsDir });
@@ -9226,6 +9324,43 @@ var init_goalCapabilityScheduling = __esm({
9226
9324
  });
9227
9325
 
9228
9326
  // src/scripts/advanceManagedGoal.ts
9327
+ function autonomyBlockReason(ctx, goalId, goal, dispatch2) {
9328
+ if (ctx.data.jobForce === true) return null;
9329
+ if (goal.runWithoutApproval !== true) {
9330
+ return `Run without approval is off for ${managedModelKind(goal)} ${goalId}`;
9331
+ }
9332
+ if (dispatch2.workflow) {
9333
+ const workflow = readWorkflowDefinition(ctx.config, ctx.cwd, dispatch2.workflow);
9334
+ if (workflow && workflow.runWithoutApproval !== true) {
9335
+ return `Run without approval is off for workflow ${dispatch2.workflow}`;
9336
+ }
9337
+ }
9338
+ const targetGoal = dispatch2.action === "goal-manager" && typeof dispatch2.cliArgs.goal === "string" ? dispatch2.cliArgs.goal : null;
9339
+ if (targetGoal && targetGoal !== goalId) {
9340
+ const target = fetchGoalState(ctx.config, targetGoal, ctx.cwd);
9341
+ const targetManaged = target ? managedGoalFromState(expandManagedGoalState(target)) : null;
9342
+ if (targetManaged && targetManaged.runWithoutApproval !== true) {
9343
+ return `Run without approval is off for goal ${targetGoal}`;
9344
+ }
9345
+ }
9346
+ return null;
9347
+ }
9348
+ function managedModelKind(goal) {
9349
+ return goal.loopTarget || goal.schedule ? "Loop" : "Goal";
9350
+ }
9351
+ function stageAutonomyBlocked(data, goalId, goal, goalState, reason) {
9352
+ stageGoalRunLogEvent(data, goalId, {
9353
+ source: "goal-manager",
9354
+ event: "goal.tick.wait",
9355
+ goalType: goal.type,
9356
+ goalState,
9357
+ stage: goal.stage,
9358
+ status: "wait",
9359
+ reason,
9360
+ goal: goalRunLogSnapshot(goalId, goalState, goal),
9361
+ decision: { kind: "wait", reason }
9362
+ });
9363
+ }
9229
9364
  function stageManagedGoalDecision(data, goalId, goal, goalState, decision, details) {
9230
9365
  if (decision.kind === "dispatch" || decision.kind === "dispatchWorkflow") {
9231
9366
  stageGoalRunLogEvent(data, goalId, {
@@ -9400,9 +9535,11 @@ var init_advanceManagedGoal = __esm({
9400
9535
  init_manager();
9401
9536
  init_runLog();
9402
9537
  init_state2();
9538
+ init_stateStore();
9403
9539
  init_targetLoopResolution();
9404
9540
  init_typeDefinitions();
9405
9541
  init_issue();
9542
+ init_workflowDefinitions();
9406
9543
  init_goalCapabilityScheduling();
9407
9544
  advanceManagedGoal = async (ctx) => {
9408
9545
  ctx.skipAgent = true;
@@ -9497,6 +9634,12 @@ var init_advanceManagedGoal = __esm({
9497
9634
  goal.raw = writeManagedGoalToState({ ...goal.raw, state: goal.state }, managed);
9498
9635
  goal.raw.extra.scheduleState = decision2.scheduleState;
9499
9636
  ctx.data.managedGoalDecision = decision2;
9637
+ const autonomyBlock = decision2.kind === "dispatch" && decision2.dispatch ? autonomyBlockReason(ctx, goal.id, managed, decision2.dispatch) : null;
9638
+ if (autonomyBlock) {
9639
+ stageAutonomyBlocked(ctx.data, goal.id, managed, goal.state, autonomyBlock);
9640
+ ctx.output.reason = autonomyBlock;
9641
+ return;
9642
+ }
9500
9643
  if (decision2.kind === "dispatch" && decision2.dispatch) {
9501
9644
  ctx.output.nextDispatch = {
9502
9645
  ...decision2.dispatch.action ? { action: decision2.dispatch.action } : {},
@@ -9553,6 +9696,12 @@ var init_advanceManagedGoal = __esm({
9553
9696
  goal.raw = writeManagedGoalToState({ ...goal.raw, state: goal.state }, managed);
9554
9697
  goal.raw.extra.scheduleState = decision2.scheduleState;
9555
9698
  ctx.data.managedGoalDecision = decision2;
9699
+ const autonomyBlock = decision2.kind === "dispatch" && decision2.dispatch ? autonomyBlockReason(ctx, goal.id, managed, decision2.dispatch) : null;
9700
+ if (autonomyBlock) {
9701
+ stageAutonomyBlocked(ctx.data, goal.id, managed, goal.state, autonomyBlock);
9702
+ ctx.output.reason = autonomyBlock;
9703
+ return;
9704
+ }
9556
9705
  if (decision2.kind === "dispatch" && decision2.dispatch) {
9557
9706
  ctx.output.nextDispatch = {
9558
9707
  capability: decision2.dispatch.capability,
@@ -9606,6 +9755,19 @@ var init_advanceManagedGoal = __esm({
9606
9755
  }
9607
9756
  goal.raw = writeManagedGoalToState({ ...goal.raw, state: goal.state }, managed);
9608
9757
  const afterDecisionSnapshot = goalRunLogSnapshot(goal.id, goal.state, managed);
9758
+ if (decision.kind === "dispatch" || decision.kind === "dispatchWorkflow") {
9759
+ const planned = decision.kind === "dispatchWorkflow" ? { workflow: decision.workflow, cliArgs: decision.cliArgs } : {
9760
+ capability: decision.capability,
9761
+ ...decision.implementation ? { implementation: decision.implementation } : {},
9762
+ cliArgs: decision.cliArgs
9763
+ };
9764
+ const autonomyBlock = autonomyBlockReason(ctx, goal.id, managed, planned);
9765
+ if (autonomyBlock) {
9766
+ stageAutonomyBlocked(ctx.data, goal.id, managed, goal.state, autonomyBlock);
9767
+ ctx.output.reason = autonomyBlock;
9768
+ return;
9769
+ }
9770
+ }
9609
9771
  stageManagedGoalDecision(ctx.data, goal.id, managed, goal.state, decision, {
9610
9772
  goalSnapshot: afterDecisionSnapshot,
9611
9773
  inspection: {
@@ -11145,8 +11307,8 @@ var init_classifyByLabel = __esm({
11145
11307
  });
11146
11308
 
11147
11309
  // src/scripts/commitAndPush.ts
11148
- import * as fs29 from "fs";
11149
- import * as path28 from "path";
11310
+ import * as fs30 from "fs";
11311
+ import * as path29 from "path";
11150
11312
  function sentinelPathForStage(cwd, profileName) {
11151
11313
  const runId = resolveRunId();
11152
11314
  return runtimeStatePath(cwd, "agent-runs", runId, `commit-${profileName}.lock`);
@@ -11167,9 +11329,9 @@ var init_commitAndPush = __esm({
11167
11329
  }
11168
11330
  const idempotencyEnabled = process.env.KODY_COMMIT_IDEMPOTENCY !== "0";
11169
11331
  const sentinel = idempotencyEnabled ? sentinelPathForStage(ctx.cwd, profile.name) : null;
11170
- if (sentinel && fs29.existsSync(sentinel)) {
11332
+ if (sentinel && fs30.existsSync(sentinel)) {
11171
11333
  try {
11172
- const replay = JSON.parse(fs29.readFileSync(sentinel, "utf-8"));
11334
+ const replay = JSON.parse(fs30.readFileSync(sentinel, "utf-8"));
11173
11335
  ctx.data.commitResult = replay.commitResult ?? { committed: false, pushed: false };
11174
11336
  if (Array.isArray(replay.changedFiles)) ctx.data.changedFiles = replay.changedFiles;
11175
11337
  if (typeof replay.hasCommitsAhead === "boolean") ctx.data.hasCommitsAhead = replay.hasCommitsAhead;
@@ -11222,8 +11384,8 @@ var init_commitAndPush = __esm({
11222
11384
  const result = ctx.data.commitResult;
11223
11385
  if (sentinel && result?.committed) {
11224
11386
  try {
11225
- fs29.mkdirSync(path28.dirname(sentinel), { recursive: true });
11226
- fs29.writeFileSync(
11387
+ fs30.mkdirSync(path29.dirname(sentinel), { recursive: true });
11388
+ fs30.writeFileSync(
11227
11389
  sentinel,
11228
11390
  JSON.stringify(
11229
11391
  {
@@ -11314,8 +11476,8 @@ var init_commitGoalState = __esm({
11314
11476
  });
11315
11477
 
11316
11478
  // src/scripts/composePrompt.ts
11317
- import * as fs30 from "fs";
11318
- import * as path29 from "path";
11479
+ import * as fs31 from "fs";
11480
+ import * as path30 from "path";
11319
11481
  function fenceUntrusted(value) {
11320
11482
  if (value.trim().length === 0) return value;
11321
11483
  const safe = value.replace(/-{3,}\s*END UNTRUSTED INPUT\s*-{3,}/gi, "[END UNTRUSTED INPUT]");
@@ -11436,10 +11598,10 @@ var init_composePrompt = __esm({
11436
11598
  const explicit = ctx.data.promptTemplate;
11437
11599
  const mode = ctx.args.mode;
11438
11600
  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")
11601
+ explicit ? path30.join(profile.dir, explicit) : null,
11602
+ mode ? path30.join(profile.dir, "prompts", `${mode}.md`) : null,
11603
+ path30.join(profile.dir, "prompt.md"),
11604
+ path30.join(profile.dir, "capability.md")
11443
11605
  ].filter(Boolean);
11444
11606
  let templatePath = "";
11445
11607
  let template = "";
@@ -11452,7 +11614,7 @@ var init_composePrompt = __esm({
11452
11614
  break;
11453
11615
  }
11454
11616
  try {
11455
- template = fs30.readFileSync(c, "utf-8");
11617
+ template = fs31.readFileSync(c, "utf-8");
11456
11618
  templatePath = c;
11457
11619
  break;
11458
11620
  } catch (err) {
@@ -11463,7 +11625,7 @@ var init_composePrompt = __esm({
11463
11625
  if (!templatePath) {
11464
11626
  let dirState;
11465
11627
  try {
11466
- dirState = `dir contents: [${fs30.readdirSync(profile.dir).join(", ")}]`;
11628
+ dirState = `dir contents: [${fs31.readdirSync(profile.dir).join(", ")}]`;
11467
11629
  } catch (err) {
11468
11630
  dirState = `readdir(${profile.dir}) failed: ${err?.code ?? String(err)}`;
11469
11631
  }
@@ -12152,19 +12314,19 @@ var init_deriveQaScopeFromIssue = __esm({
12152
12314
 
12153
12315
  // src/scripts/diagMcp.ts
12154
12316
  import { execFileSync as execFileSync10 } from "child_process";
12155
- import * as fs31 from "fs";
12317
+ import * as fs32 from "fs";
12156
12318
  import * as os6 from "os";
12157
- import * as path30 from "path";
12319
+ import * as path31 from "path";
12158
12320
  var diagMcp;
12159
12321
  var init_diagMcp = __esm({
12160
12322
  "src/scripts/diagMcp.ts"() {
12161
12323
  "use strict";
12162
12324
  diagMcp = async (_ctx) => {
12163
12325
  const home = os6.homedir();
12164
- const cacheDir = path30.join(home, ".cache", "ms-playwright");
12326
+ const cacheDir = path31.join(home, ".cache", "ms-playwright");
12165
12327
  let entries = [];
12166
12328
  try {
12167
- entries = fs31.readdirSync(cacheDir);
12329
+ entries = fs32.readdirSync(cacheDir);
12168
12330
  } catch {
12169
12331
  }
12170
12332
  const hasChromium = entries.some((e) => e.startsWith("chromium"));
@@ -12192,13 +12354,13 @@ var init_diagMcp = __esm({
12192
12354
  });
12193
12355
 
12194
12356
  // src/scripts/frameworkDetectors.ts
12195
- import * as fs32 from "fs";
12196
- import * as path31 from "path";
12357
+ import * as fs33 from "fs";
12358
+ import * as path32 from "path";
12197
12359
  function detectFrameworks(cwd) {
12198
12360
  const out = [];
12199
12361
  let deps = {};
12200
12362
  try {
12201
- const pkg = JSON.parse(fs32.readFileSync(path31.join(cwd, "package.json"), "utf-8"));
12363
+ const pkg = JSON.parse(fs33.readFileSync(path32.join(cwd, "package.json"), "utf-8"));
12202
12364
  deps = { ...pkg.dependencies, ...pkg.devDependencies };
12203
12365
  } catch {
12204
12366
  return out;
@@ -12235,25 +12397,25 @@ function detectFrameworks(cwd) {
12235
12397
  }
12236
12398
  function findFile(cwd, candidates) {
12237
12399
  for (const c of candidates) {
12238
- if (fs32.existsSync(path31.join(cwd, c))) return c;
12400
+ if (fs33.existsSync(path32.join(cwd, c))) return c;
12239
12401
  }
12240
12402
  return null;
12241
12403
  }
12242
12404
  function discoverPayloadCollections(cwd) {
12243
12405
  const out = [];
12244
12406
  for (const dir of COLLECTION_DIRS) {
12245
- const full = path31.join(cwd, dir);
12246
- if (!fs32.existsSync(full)) continue;
12407
+ const full = path32.join(cwd, dir);
12408
+ if (!fs33.existsSync(full)) continue;
12247
12409
  let files;
12248
12410
  try {
12249
- files = fs32.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
12411
+ files = fs33.readdirSync(full).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
12250
12412
  } catch {
12251
12413
  continue;
12252
12414
  }
12253
12415
  for (const file of files) {
12254
12416
  try {
12255
- const filePath = path31.join(full, file);
12256
- const content = fs32.readFileSync(filePath, "utf-8").slice(0, 1e4);
12417
+ const filePath = path32.join(full, file);
12418
+ const content = fs33.readFileSync(filePath, "utf-8").slice(0, 1e4);
12257
12419
  const slugMatch = content.match(/slug:\s*['"]([a-z0-9-]+)['"]/);
12258
12420
  if (!slugMatch) continue;
12259
12421
  const slug2 = slugMatch[1];
@@ -12267,7 +12429,7 @@ function discoverPayloadCollections(cwd) {
12267
12429
  out.push({
12268
12430
  name,
12269
12431
  slug: slug2,
12270
- filePath: path31.relative(cwd, filePath),
12432
+ filePath: path32.relative(cwd, filePath),
12271
12433
  fields: fields.slice(0, 20),
12272
12434
  hasAdmin
12273
12435
  });
@@ -12280,28 +12442,28 @@ function discoverPayloadCollections(cwd) {
12280
12442
  function discoverAdminComponents(cwd, collections) {
12281
12443
  const out = [];
12282
12444
  for (const dir of ADMIN_COMPONENT_DIRS) {
12283
- const full = path31.join(cwd, dir);
12284
- if (!fs32.existsSync(full)) continue;
12445
+ const full = path32.join(cwd, dir);
12446
+ if (!fs33.existsSync(full)) continue;
12285
12447
  let entries;
12286
12448
  try {
12287
- entries = fs32.readdirSync(full, { withFileTypes: true });
12449
+ entries = fs33.readdirSync(full, { withFileTypes: true });
12288
12450
  } catch {
12289
12451
  continue;
12290
12452
  }
12291
12453
  for (const entry of entries) {
12292
- const entryPath = path31.join(full, entry.name);
12454
+ const entryPath = path32.join(full, entry.name);
12293
12455
  let name;
12294
12456
  let filePath;
12295
12457
  if (entry.isDirectory()) {
12296
12458
  const indexFile = ["index.tsx", "index.ts", "index.jsx", "index.js"].find(
12297
- (f) => fs32.existsSync(path31.join(entryPath, f))
12459
+ (f) => fs33.existsSync(path32.join(entryPath, f))
12298
12460
  );
12299
12461
  if (!indexFile) continue;
12300
12462
  name = entry.name;
12301
- filePath = path31.relative(cwd, path31.join(entryPath, indexFile));
12463
+ filePath = path32.relative(cwd, path32.join(entryPath, indexFile));
12302
12464
  } else if (/\.(tsx?|jsx?)$/.test(entry.name)) {
12303
12465
  name = entry.name.replace(/\.(tsx?|jsx?)$/, "");
12304
- filePath = path31.relative(cwd, entryPath);
12466
+ filePath = path32.relative(cwd, entryPath);
12305
12467
  } else {
12306
12468
  continue;
12307
12469
  }
@@ -12309,7 +12471,7 @@ function discoverAdminComponents(cwd, collections) {
12309
12471
  if (collections) {
12310
12472
  for (const col of collections) {
12311
12473
  try {
12312
- const colContent = fs32.readFileSync(path31.join(cwd, col.filePath), "utf-8");
12474
+ const colContent = fs33.readFileSync(path32.join(cwd, col.filePath), "utf-8");
12313
12475
  if (colContent.includes(name)) {
12314
12476
  usedInCollection = col.slug;
12315
12477
  break;
@@ -12327,8 +12489,8 @@ function scanApiRoutes(cwd) {
12327
12489
  const out = [];
12328
12490
  const appDirs = ["src/app", "app"];
12329
12491
  for (const appDir of appDirs) {
12330
- const apiDir = path31.join(cwd, appDir, "api");
12331
- if (!fs32.existsSync(apiDir)) continue;
12492
+ const apiDir = path32.join(cwd, appDir, "api");
12493
+ if (!fs33.existsSync(apiDir)) continue;
12332
12494
  walkApiRoutes(apiDir, "/api", cwd, out);
12333
12495
  break;
12334
12496
  }
@@ -12337,14 +12499,14 @@ function scanApiRoutes(cwd) {
12337
12499
  function walkApiRoutes(dir, prefix, cwd, out) {
12338
12500
  let entries;
12339
12501
  try {
12340
- entries = fs32.readdirSync(dir, { withFileTypes: true });
12502
+ entries = fs33.readdirSync(dir, { withFileTypes: true });
12341
12503
  } catch {
12342
12504
  return;
12343
12505
  }
12344
12506
  const routeFile = entries.find((e) => e.isFile() && /^route\.(ts|js|tsx|jsx)$/.test(e.name));
12345
12507
  if (routeFile) {
12346
12508
  try {
12347
- const content = fs32.readFileSync(path31.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
12509
+ const content = fs33.readFileSync(path32.join(dir, routeFile.name), "utf-8").slice(0, 5e3);
12348
12510
  const methods = HTTP_METHODS.filter(
12349
12511
  (m) => new RegExp(`export\\s+(?:async\\s+)?function\\s+${m}\\b`).test(content)
12350
12512
  );
@@ -12352,7 +12514,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
12352
12514
  out.push({
12353
12515
  path: prefix,
12354
12516
  methods,
12355
- filePath: path31.relative(cwd, path31.join(dir, routeFile.name))
12517
+ filePath: path32.relative(cwd, path32.join(dir, routeFile.name))
12356
12518
  });
12357
12519
  }
12358
12520
  } catch {
@@ -12363,7 +12525,7 @@ function walkApiRoutes(dir, prefix, cwd, out) {
12363
12525
  if (entry.name === "node_modules" || entry.name === ".next") continue;
12364
12526
  let segment = entry.name;
12365
12527
  if (segment.startsWith("(") && segment.endsWith(")")) {
12366
- walkApiRoutes(path31.join(dir, entry.name), prefix, cwd, out);
12528
+ walkApiRoutes(path32.join(dir, entry.name), prefix, cwd, out);
12367
12529
  continue;
12368
12530
  }
12369
12531
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -12371,16 +12533,16 @@ function walkApiRoutes(dir, prefix, cwd, out) {
12371
12533
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
12372
12534
  segment = `:${segment.slice(1, -1)}`;
12373
12535
  }
12374
- walkApiRoutes(path31.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
12536
+ walkApiRoutes(path32.join(dir, entry.name), `${prefix}/${segment}`, cwd, out);
12375
12537
  }
12376
12538
  }
12377
12539
  function scanEnvVars(cwd) {
12378
12540
  const candidates = [".env.example", ".env.local.example", ".env.template"];
12379
12541
  for (const envFile of candidates) {
12380
- const envPath = path31.join(cwd, envFile);
12381
- if (!fs32.existsSync(envPath)) continue;
12542
+ const envPath = path32.join(cwd, envFile);
12543
+ if (!fs33.existsSync(envPath)) continue;
12382
12544
  try {
12383
- const content = fs32.readFileSync(envPath, "utf-8");
12545
+ const content = fs33.readFileSync(envPath, "utf-8");
12384
12546
  const vars = [];
12385
12547
  for (const line of content.split("\n")) {
12386
12548
  const trimmed = line.trim();
@@ -12425,8 +12587,8 @@ var init_frameworkDetectors = __esm({
12425
12587
  });
12426
12588
 
12427
12589
  // src/scripts/discoverQaContext.ts
12428
- import * as fs33 from "fs";
12429
- import * as path32 from "path";
12590
+ import * as fs34 from "fs";
12591
+ import * as path33 from "path";
12430
12592
  function runQaDiscovery(cwd) {
12431
12593
  const out = {
12432
12594
  routes: [],
@@ -12457,9 +12619,9 @@ function runQaDiscovery(cwd) {
12457
12619
  }
12458
12620
  function detectDevServer(cwd, out) {
12459
12621
  try {
12460
- const pkg = JSON.parse(fs33.readFileSync(path32.join(cwd, "package.json"), "utf-8"));
12622
+ const pkg = JSON.parse(fs34.readFileSync(path33.join(cwd, "package.json"), "utf-8"));
12461
12623
  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";
12624
+ 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
12625
  if (pkg.scripts?.dev) out.devCommand = `${pm} dev`;
12464
12626
  if (allDeps.next || allDeps.nuxt) out.devPort = 3e3;
12465
12627
  else if (allDeps.vite) out.devPort = 5173;
@@ -12469,8 +12631,8 @@ function detectDevServer(cwd, out) {
12469
12631
  function scanFrontendRoutes(cwd, out) {
12470
12632
  const appDirs = ["src/app", "app"];
12471
12633
  for (const appDir of appDirs) {
12472
- const full = path32.join(cwd, appDir);
12473
- if (!fs33.existsSync(full)) continue;
12634
+ const full = path33.join(cwd, appDir);
12635
+ if (!fs34.existsSync(full)) continue;
12474
12636
  walkFrontendRoutes(full, "", out);
12475
12637
  break;
12476
12638
  }
@@ -12478,7 +12640,7 @@ function scanFrontendRoutes(cwd, out) {
12478
12640
  function walkFrontendRoutes(dir, prefix, out) {
12479
12641
  let entries;
12480
12642
  try {
12481
- entries = fs33.readdirSync(dir, { withFileTypes: true });
12643
+ entries = fs34.readdirSync(dir, { withFileTypes: true });
12482
12644
  } catch {
12483
12645
  return;
12484
12646
  }
@@ -12495,7 +12657,7 @@ function walkFrontendRoutes(dir, prefix, out) {
12495
12657
  if (entry.name === "node_modules" || entry.name === ".next") continue;
12496
12658
  let segment = entry.name;
12497
12659
  if (segment.startsWith("(") && segment.endsWith(")")) {
12498
- walkFrontendRoutes(path32.join(dir, entry.name), prefix, out);
12660
+ walkFrontendRoutes(path33.join(dir, entry.name), prefix, out);
12499
12661
  continue;
12500
12662
  }
12501
12663
  if (segment.startsWith("[[") && segment.endsWith("]]")) {
@@ -12503,7 +12665,7 @@ function walkFrontendRoutes(dir, prefix, out) {
12503
12665
  } else if (segment.startsWith("[") && segment.endsWith("]")) {
12504
12666
  segment = `:${segment.slice(1, -1)}`;
12505
12667
  }
12506
- walkFrontendRoutes(path32.join(dir, entry.name), `${prefix}/${segment}`, out);
12668
+ walkFrontendRoutes(path33.join(dir, entry.name), `${prefix}/${segment}`, out);
12507
12669
  }
12508
12670
  }
12509
12671
  function detectAuthFiles(cwd, out) {
@@ -12520,23 +12682,23 @@ function detectAuthFiles(cwd, out) {
12520
12682
  "src/app/api/oauth"
12521
12683
  ];
12522
12684
  for (const c of candidates) {
12523
- if (fs33.existsSync(path32.join(cwd, c))) out.authFiles.push(c);
12685
+ if (fs34.existsSync(path33.join(cwd, c))) out.authFiles.push(c);
12524
12686
  }
12525
12687
  }
12526
12688
  function detectRoles(cwd, out) {
12527
12689
  const rolePaths = ["src/types", "src/lib", "src/utils", "src/constants", "src/access", "src/collections"];
12528
12690
  for (const rp of rolePaths) {
12529
- const dir = path32.join(cwd, rp);
12530
- if (!fs33.existsSync(dir)) continue;
12691
+ const dir = path33.join(cwd, rp);
12692
+ if (!fs34.existsSync(dir)) continue;
12531
12693
  let files;
12532
12694
  try {
12533
- files = fs33.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
12695
+ files = fs34.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
12534
12696
  } catch {
12535
12697
  continue;
12536
12698
  }
12537
12699
  for (const f of files) {
12538
12700
  try {
12539
- const content = fs33.readFileSync(path32.join(dir, f), "utf-8").slice(0, 5e3);
12701
+ const content = fs34.readFileSync(path33.join(dir, f), "utf-8").slice(0, 5e3);
12540
12702
  const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
12541
12703
  if (roleMatches) {
12542
12704
  for (const m of roleMatches) {
@@ -13781,12 +13943,12 @@ var init_fixFlow = __esm({
13781
13943
 
13782
13944
  // src/scripts/initFlow.ts
13783
13945
  import { execFileSync as execFileSync15 } from "child_process";
13784
- import * as fs34 from "fs";
13785
- import * as path33 from "path";
13946
+ import * as fs35 from "fs";
13947
+ import * as path34 from "path";
13786
13948
  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";
13949
+ if (fs35.existsSync(path34.join(cwd, "pnpm-lock.yaml"))) return "pnpm";
13950
+ if (fs35.existsSync(path34.join(cwd, "yarn.lock"))) return "yarn";
13951
+ if (fs35.existsSync(path34.join(cwd, "bun.lockb"))) return "bun";
13790
13952
  return "npm";
13791
13953
  }
13792
13954
  function qualityCommandsFor(pm) {
@@ -13858,22 +14020,22 @@ function performInit(cwd, force) {
13858
14020
  const pm = detectPackageManager(cwd);
13859
14021
  const ownerRepo = detectOwnerRepo(cwd);
13860
14022
  const defaultBranch = defaultBranchFromGit(cwd);
13861
- const configPath = path33.join(cwd, "kody.config.json");
13862
- if (fs34.existsSync(configPath) && !force) {
14023
+ const configPath = path34.join(cwd, "kody.config.json");
14024
+ if (fs35.existsSync(configPath) && !force) {
13863
14025
  skipped.push("kody.config.json");
13864
14026
  } else {
13865
14027
  const cfg = makeConfig(pm, ownerRepo, defaultBranch);
13866
- fs34.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
14028
+ fs35.writeFileSync(configPath, `${JSON.stringify(cfg, null, 2)}
13867
14029
  `);
13868
14030
  wrote.push("kody.config.json");
13869
14031
  }
13870
- const workflowDir = path33.join(cwd, ".github", "workflows");
13871
- const workflowPath = path33.join(workflowDir, "kody.yml");
13872
- if (fs34.existsSync(workflowPath) && !force) {
14032
+ const workflowDir = path34.join(cwd, ".github", "workflows");
14033
+ const workflowPath = path34.join(workflowDir, "kody.yml");
14034
+ if (fs35.existsSync(workflowPath) && !force) {
13873
14035
  skipped.push(".github/workflows/kody.yml");
13874
14036
  } else {
13875
- fs34.mkdirSync(workflowDir, { recursive: true });
13876
- fs34.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
14037
+ fs35.mkdirSync(workflowDir, { recursive: true });
14038
+ fs35.writeFileSync(workflowPath, WORKFLOW_TEMPLATE);
13877
14039
  wrote.push(".github/workflows/kody.yml");
13878
14040
  }
13879
14041
  for (const exe of listImplementations()) {
@@ -13884,12 +14046,12 @@ function performInit(cwd, force) {
13884
14046
  continue;
13885
14047
  }
13886
14048
  if (profile.kind !== "scheduled" || !profile.schedule) continue;
13887
- const target = path33.join(workflowDir, `kody-${exe.name}.yml`);
13888
- if (fs34.existsSync(target) && !force) {
14049
+ const target = path34.join(workflowDir, `kody-${exe.name}.yml`);
14050
+ if (fs35.existsSync(target) && !force) {
13889
14051
  skipped.push(`.github/workflows/kody-${exe.name}.yml`);
13890
14052
  continue;
13891
14053
  }
13892
- fs34.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
14054
+ fs35.writeFileSync(target, renderScheduledWorkflow(exe.name, profile.schedule));
13893
14055
  wrote.push(`.github/workflows/kody-${exe.name}.yml`);
13894
14056
  }
13895
14057
  let labels;
@@ -14037,7 +14199,7 @@ Nothing to do. All files already present. (Use --force to overwrite.)
14037
14199
  });
14038
14200
 
14039
14201
  // src/scripts/loadAgentAdhoc.ts
14040
- import * as fs35 from "fs";
14202
+ import * as fs36 from "fs";
14041
14203
  function resolveMessage(messageArg) {
14042
14204
  const fromComment = readCommentBody();
14043
14205
  if (fromComment) return stripDirective(fromComment);
@@ -14045,9 +14207,9 @@ function resolveMessage(messageArg) {
14045
14207
  }
14046
14208
  function readCommentBody() {
14047
14209
  const eventPath = process.env.GITHUB_EVENT_PATH;
14048
- if (!eventPath || !fs35.existsSync(eventPath)) return "";
14210
+ if (!eventPath || !fs36.existsSync(eventPath)) return "";
14049
14211
  try {
14050
- const event = JSON.parse(fs35.readFileSync(eventPath, "utf-8"));
14212
+ const event = JSON.parse(fs36.readFileSync(eventPath, "utf-8"));
14051
14213
  return String(event.comment?.body ?? "");
14052
14214
  } catch {
14053
14215
  return "";
@@ -14100,10 +14262,10 @@ var init_loadAgentAdhoc = __esm({
14100
14262
  throw new Error("loadAgentAdhoc: ctx.args.agent must be a non-empty slug");
14101
14263
  }
14102
14264
  const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
14103
- if (!fs35.existsSync(agentPath)) {
14265
+ if (!fs36.existsSync(agentPath)) {
14104
14266
  throw new Error(`loadAgentAdhoc: agent identity not found: ${agentPath}`);
14105
14267
  }
14106
- const { title, body } = parseAgentFile(fs35.readFileSync(agentPath, "utf-8"), agentSlug);
14268
+ const { title, body } = parseAgentFile(fs36.readFileSync(agentPath, "utf-8"), agentSlug);
14107
14269
  const message = resolveMessage(ctx.args.message);
14108
14270
  if (!message) {
14109
14271
  throw new Error(
@@ -14347,8 +14509,8 @@ var init_loadIssueStateComment = __esm({
14347
14509
  });
14348
14510
 
14349
14511
  // src/scripts/loadJobFromFile.ts
14350
- import * as fs36 from "fs";
14351
- import * as path34 from "path";
14512
+ import * as fs37 from "fs";
14513
+ import * as path35 from "path";
14352
14514
  function parseJobFile(raw, slug2) {
14353
14515
  let stripped = raw;
14354
14516
  if (stripped.startsWith("---\n")) {
@@ -14386,9 +14548,9 @@ var init_loadJobFromFile = __esm({
14386
14548
  if (!slug2) {
14387
14549
  throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
14388
14550
  }
14389
- const capability = resolveCapabilityFolder(slug2, path34.join(ctx.cwd, jobsDir));
14551
+ const capability = resolveCapabilityFolder(slug2, path35.join(ctx.cwd, jobsDir));
14390
14552
  if (!capability) {
14391
- throw new Error(`loadJobFromFile: capability folder not found or incomplete: ${path34.join(ctx.cwd, jobsDir, slug2)}`);
14553
+ throw new Error(`loadJobFromFile: capability folder not found or incomplete: ${path35.join(ctx.cwd, jobsDir, slug2)}`);
14392
14554
  }
14393
14555
  const { title, body, config } = capability;
14394
14556
  const mentions = (config.mentions ?? []).map((login) => `@${login}`).join(" ");
@@ -14397,12 +14559,12 @@ var init_loadJobFromFile = __esm({
14397
14559
  let agentIdentity = "";
14398
14560
  if (agentSlug) {
14399
14561
  const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
14400
- if (!fs36.existsSync(agentPath)) {
14562
+ if (!fs37.existsSync(agentPath)) {
14401
14563
  throw new Error(
14402
14564
  `loadJobFromFile: capability '${slug2}' declares agent '${agentSlug}' but ${agentPath} does not exist`
14403
14565
  );
14404
14566
  }
14405
- const agentRaw = fs36.readFileSync(agentPath, "utf-8");
14567
+ const agentRaw = fs37.readFileSync(agentPath, "utf-8");
14406
14568
  const parsed = parseJobFile(agentRaw, agentSlug);
14407
14569
  agentTitle = parsed.title;
14408
14570
  agentIdentity = parsed.body;
@@ -14482,13 +14644,13 @@ ${truncate(issue.body, FINDING_BODY_MAX_BYTES)}`;
14482
14644
  });
14483
14645
 
14484
14646
  // src/scripts/kodyVariables.ts
14485
- import * as fs37 from "fs";
14486
- import * as path35 from "path";
14647
+ import * as fs38 from "fs";
14648
+ import * as path36 from "path";
14487
14649
  function readKodyVariables(cwd) {
14488
- const full = path35.join(cwd, KODY_VARIABLES_REL_PATH);
14650
+ const full = path36.join(cwd, KODY_VARIABLES_REL_PATH);
14489
14651
  let raw;
14490
14652
  try {
14491
- raw = fs37.readFileSync(full, "utf-8");
14653
+ raw = fs38.readFileSync(full, "utf-8");
14492
14654
  } catch {
14493
14655
  return {};
14494
14656
  }
@@ -14556,8 +14718,8 @@ var init_keys = __esm({
14556
14718
  });
14557
14719
 
14558
14720
  // src/stateRepoGithub.ts
14559
- import * as fs38 from "fs";
14560
- import * as path36 from "path";
14721
+ import * as fs39 from "fs";
14722
+ import * as path37 from "path";
14561
14723
  function recordValue4(value) {
14562
14724
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
14563
14725
  }
@@ -14697,15 +14859,15 @@ function mergeJsonl(localText, remoteText) {
14697
14859
  async function syncJsonlFileFromGithubState(opts) {
14698
14860
  const remote = await readGithubStateTextWithConfig(opts);
14699
14861
  if (!remote) return;
14700
- const local = fs38.existsSync(opts.localPath) ? fs38.readFileSync(opts.localPath, "utf-8") : "";
14862
+ const local = fs39.existsSync(opts.localPath) ? fs39.readFileSync(opts.localPath, "utf-8") : "";
14701
14863
  const next = mergeJsonl(local, remote.content);
14702
14864
  if (next === local) return;
14703
- fs38.mkdirSync(path36.dirname(opts.localPath), { recursive: true });
14704
- fs38.writeFileSync(opts.localPath, next);
14865
+ fs39.mkdirSync(path37.dirname(opts.localPath), { recursive: true });
14866
+ fs39.writeFileSync(opts.localPath, next);
14705
14867
  }
14706
14868
  async function persistJsonlFileToGithubState(opts) {
14707
- if (!fs38.existsSync(opts.localPath)) return;
14708
- const localText = fs38.readFileSync(opts.localPath, "utf-8");
14869
+ if (!fs39.existsSync(opts.localPath)) return;
14870
+ const localText = fs39.readFileSync(opts.localPath, "utf-8");
14709
14871
  for (let attempt = 1; attempt <= 3; attempt += 1) {
14710
14872
  const remote = await readGithubStateTextWithConfig(opts);
14711
14873
  const body = mergeJsonl(localText, remote?.content ?? "");
@@ -14844,8 +15006,8 @@ var init_runtimeSecrets = __esm({
14844
15006
  });
14845
15007
 
14846
15008
  // src/scripts/loadQaContext.ts
14847
- import * as fs39 from "fs";
14848
- import * as path37 from "path";
15009
+ import * as fs40 from "fs";
15010
+ import * as path38 from "path";
14849
15011
  function parseSlugList(value) {
14850
15012
  const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
14851
15013
  return inner.split(",").map(
@@ -14874,18 +15036,18 @@ function readProfileAgents(raw) {
14874
15036
  return { agent: agent ?? legacy ?? ["kody"], body };
14875
15037
  }
14876
15038
  function readProfile(cwd) {
14877
- const dir = path37.join(cwd, CONTEXT_DIR_REL_PATH);
14878
- if (!fs39.existsSync(dir)) return "";
15039
+ const dir = path38.join(cwd, CONTEXT_DIR_REL_PATH);
15040
+ if (!fs40.existsSync(dir)) return "";
14879
15041
  let entries;
14880
15042
  try {
14881
- entries = fs39.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
15043
+ entries = fs40.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
14882
15044
  } catch {
14883
15045
  return "";
14884
15046
  }
14885
15047
  const blocks = [];
14886
15048
  for (const file of entries) {
14887
15049
  try {
14888
- const raw = fs39.readFileSync(path37.join(dir, file), "utf-8");
15050
+ const raw = fs40.readFileSync(path38.join(dir, file), "utf-8");
14889
15051
  const { agent, body } = readProfileAgents(raw);
14890
15052
  if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
14891
15053
  blocks.push(`## ${file}
@@ -14934,8 +15096,8 @@ var init_loadQaContext = __esm({
14934
15096
  });
14935
15097
 
14936
15098
  // src/taskContext.ts
14937
- import * as fs40 from "fs";
14938
- import * as path38 from "path";
15099
+ import * as fs41 from "fs";
15100
+ import * as path39 from "path";
14939
15101
  function buildTaskContext(args) {
14940
15102
  return {
14941
15103
  schemaVersion: TASK_CONTEXT_SCHEMA_VERSION,
@@ -14951,9 +15113,9 @@ function buildTaskContext(args) {
14951
15113
  function persistTaskContext(cwd, ctx) {
14952
15114
  try {
14953
15115
  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)}
15116
+ fs41.mkdirSync(dir, { recursive: true });
15117
+ const file = path39.join(dir, "task-context.json");
15118
+ fs41.writeFileSync(file, `${JSON.stringify(ctx, null, 2)}
14957
15119
  `);
14958
15120
  return file;
14959
15121
  } catch (err) {
@@ -17463,12 +17625,12 @@ fi
17463
17625
 
17464
17626
  // src/scripts/runPreviewBuild.ts
17465
17627
  import { copyFile, writeFile } from "fs/promises";
17466
- import * as path39 from "path";
17628
+ import * as path40 from "path";
17467
17629
  import { fileURLToPath } from "url";
17468
17630
  function bundledDockerfilePath(mode) {
17469
- const here = path39.dirname(fileURLToPath(import.meta.url));
17631
+ const here = path40.dirname(fileURLToPath(import.meta.url));
17470
17632
  const file = mode === "dev" ? "default-Dockerfile.preview.dev" : "default-Dockerfile.preview.prod";
17471
- return path39.join(here, "preview-build-templates", file);
17633
+ return path40.join(here, "preview-build-templates", file);
17472
17634
  }
17473
17635
  function required(name) {
17474
17636
  const v = (process.env[name] ?? "").trim();
@@ -17719,10 +17881,10 @@ var init_runPreviewBuild = __esm({
17719
17881
  console.log(`[preview-build] vault: ${Object.keys(buildEnv).length} secrets, mode=${buildMode}`);
17720
17882
  if (Object.keys(buildEnv).length > 0) {
17721
17883
  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")}
17884
+ await writeFile(path40.join(ctx.cwd, ".env.production.local"), `${lines.join("\n")}
17723
17885
  `, "utf8");
17724
17886
  }
17725
- const consumerDockerfile = path39.join(ctx.cwd, "Dockerfile.preview");
17887
+ const consumerDockerfile = path40.join(ctx.cwd, "Dockerfile.preview");
17726
17888
  const { stat } = await import("fs/promises");
17727
17889
  let hasConsumerDockerfile = false;
17728
17890
  try {
@@ -17906,8 +18068,8 @@ var init_tickShellRunner = __esm({
17906
18068
  });
17907
18069
 
17908
18070
  // src/scripts/runScheduledImplementationTick.ts
17909
- import * as fs41 from "fs";
17910
- import * as path40 from "path";
18071
+ import * as fs42 from "fs";
18072
+ import * as path41 from "path";
17911
18073
  var runScheduledImplementationTick;
17912
18074
  var init_runScheduledImplementationTick = __esm({
17913
18075
  "src/scripts/runScheduledImplementationTick.ts"() {
@@ -17927,14 +18089,14 @@ var init_runScheduledImplementationTick = __esm({
17927
18089
  ctx.output.reason = `runScheduledImplementationTick: args.slug or ctx.args.${slugArg} must be non-empty capability slug`;
17928
18090
  return;
17929
18091
  }
17930
- const capability = resolveCapabilityFolder(slug2, path40.join(ctx.cwd, jobsDir));
18092
+ const capability = resolveCapabilityFolder(slug2, path41.join(ctx.cwd, jobsDir));
17931
18093
  if (!capability) {
17932
18094
  ctx.output.exitCode = 99;
17933
18095
  ctx.output.reason = `runScheduledImplementationTick: capability folder not found or incomplete: ${slug2} (searched ${jobsDir} and company store)`;
17934
18096
  return;
17935
18097
  }
17936
- const shellPath = path40.join(profile.dir, shell);
17937
- if (!fs41.existsSync(shellPath)) {
18098
+ const shellPath = path41.join(profile.dir, shell);
18099
+ if (!fs42.existsSync(shellPath)) {
17938
18100
  ctx.output.exitCode = 99;
17939
18101
  ctx.output.reason = `runScheduledImplementationTick: shell not found: ${shell} (looked in ${profile.dir})`;
17940
18102
  return;
@@ -17965,8 +18127,8 @@ var init_runScheduledImplementationTick = __esm({
17965
18127
  });
17966
18128
 
17967
18129
  // src/scripts/runTickScript.ts
17968
- import * as fs42 from "fs";
17969
- import * as path41 from "path";
18130
+ import * as fs43 from "fs";
18131
+ import * as path42 from "path";
17970
18132
  var runTickScript;
17971
18133
  var init_runTickScript = __esm({
17972
18134
  "src/scripts/runTickScript.ts"() {
@@ -17985,10 +18147,10 @@ var init_runTickScript = __esm({
17985
18147
  ctx.output.reason = `runTickScript: ctx.args.${slugArg} must be a non-empty slug`;
17986
18148
  return;
17987
18149
  }
17988
- const capability = readCapabilityFolder(path41.join(ctx.cwd, jobsDir), slug2);
18150
+ const capability = readCapabilityFolder(path42.join(ctx.cwd, jobsDir), slug2);
17989
18151
  if (!capability) {
17990
18152
  ctx.output.exitCode = 99;
17991
- ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path41.join(ctx.cwd, jobsDir, slug2)}`;
18153
+ ctx.output.reason = `runTickScript: capability folder not found or incomplete: ${path42.join(ctx.cwd, jobsDir, slug2)}`;
17992
18154
  return;
17993
18155
  }
17994
18156
  const tickScript = capability.config.tickScript;
@@ -17997,8 +18159,8 @@ var init_runTickScript = __esm({
17997
18159
  ctx.output.reason = `runTickScript: capability ${slug2} has no \`tickScript\` in profile.json \u2014 route via capability-tick instead`;
17998
18160
  return;
17999
18161
  }
18000
- const scriptPath = path41.isAbsolute(tickScript) ? tickScript : path41.join(ctx.cwd, tickScript);
18001
- if (!fs42.existsSync(scriptPath)) {
18162
+ const scriptPath = path42.isAbsolute(tickScript) ? tickScript : path42.join(ctx.cwd, tickScript);
18163
+ if (!fs43.existsSync(scriptPath)) {
18002
18164
  ctx.output.exitCode = 99;
18003
18165
  ctx.output.reason = `runTickScript: tickScript not found: ${scriptPath}`;
18004
18166
  return;
@@ -19143,7 +19305,7 @@ var init_warmupMcp = __esm({
19143
19305
  });
19144
19306
 
19145
19307
  // src/scripts/writeAgentRunSummary.ts
19146
- import * as fs43 from "fs";
19308
+ import * as fs44 from "fs";
19147
19309
  var writeAgentRunSummary;
19148
19310
  var init_writeAgentRunSummary = __esm({
19149
19311
  "src/scripts/writeAgentRunSummary.ts"() {
@@ -19169,7 +19331,7 @@ var init_writeAgentRunSummary = __esm({
19169
19331
  if (reason) lines.push(`- **Reason:** ${reason}`);
19170
19332
  lines.push("");
19171
19333
  try {
19172
- fs43.appendFileSync(summaryPath, `${lines.join("\n")}
19334
+ fs44.appendFileSync(summaryPath, `${lines.join("\n")}
19173
19335
  `);
19174
19336
  } catch {
19175
19337
  }
@@ -19427,6 +19589,7 @@ var init_scripts = __esm({
19427
19589
  planTaskJobs,
19428
19590
  dispatchNextTaskJob,
19429
19591
  runScheduledImplementationTick,
19592
+ runScheduledExecutableTick: runScheduledImplementationTick,
19430
19593
  runTickScript,
19431
19594
  runPreviewBuild,
19432
19595
  advanceManagedGoal,
@@ -19495,53 +19658,53 @@ var init_scripts = __esm({
19495
19658
  // src/stateWorkspace.ts
19496
19659
  import { execFileSync as execFileSync24 } from "child_process";
19497
19660
  import * as crypto3 from "crypto";
19498
- import * as fs44 from "fs";
19661
+ import * as fs45 from "fs";
19499
19662
  import * as os7 from "os";
19500
- import * as path42 from "path";
19663
+ import * as path43 from "path";
19501
19664
  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);
19665
+ const fullPath = path43.join(cwd, relativePath);
19666
+ fs45.mkdirSync(path43.dirname(fullPath), { recursive: true });
19667
+ fs45.writeFileSync(fullPath, content);
19505
19668
  }
19506
19669
  function copyPath(source, target) {
19507
- const st = fs44.lstatSync(source);
19508
- fs44.rmSync(target, { recursive: true, force: true });
19670
+ const st = fs45.lstatSync(source);
19671
+ fs45.rmSync(target, { recursive: true, force: true });
19509
19672
  if (st.isSymbolicLink()) return;
19510
- fs44.mkdirSync(path42.dirname(target), { recursive: true });
19511
- fs44.cpSync(source, target, { recursive: true, force: true });
19673
+ fs45.mkdirSync(path43.dirname(target), { recursive: true });
19674
+ fs45.cpSync(source, target, { recursive: true, force: true });
19512
19675
  }
19513
19676
  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);
19677
+ if (!fs45.existsSync(sourceDir)) return;
19678
+ for (const entry of fs45.readdirSync(sourceDir, { withFileTypes: true })) {
19679
+ const source = path43.join(sourceDir, entry.name);
19680
+ const target = path43.join(cwd, localDir, entry.name);
19518
19681
  copyPath(source, target);
19519
19682
  }
19520
19683
  }
19521
19684
  function hydrateStateWorkspace(config, cwd) {
19522
19685
  if (process.env.VITEST && process.env[TEST_FETCH_ENV] !== "1") return;
19523
19686
  const parsed = parseStateRepo(config);
19524
- const hydrateKey = `${path42.resolve(cwd)}|${parsed.owner}/${parsed.repo}|${parsed.basePath}|${parsed.branch}`;
19687
+ const hydrateKey = `${path43.resolve(cwd)}|${parsed.owner}/${parsed.repo}|${parsed.basePath}|${parsed.branch}`;
19525
19688
  if (hydratedWorkspaces.has(hydrateKey)) return;
19526
19689
  const snapshotRoot = fetchStateSnapshot(parsed);
19527
19690
  for (const mapping of DIR_MAPPINGS) {
19528
- overlayDirectoryChildren(cwd, path42.join(snapshotRoot, mapping.stateDir), mapping.localDir);
19691
+ overlayDirectoryChildren(cwd, path43.join(snapshotRoot, mapping.stateDir), mapping.localDir);
19529
19692
  }
19530
19693
  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"));
19694
+ const source = path43.join(snapshotRoot, mapping.statePath);
19695
+ if (fs45.existsSync(source) && !fs45.lstatSync(source).isSymbolicLink() && fs45.statSync(source).isFile()) {
19696
+ writeLocalFile(cwd, mapping.localPath, fs45.readFileSync(source, "utf-8"));
19534
19697
  }
19535
19698
  }
19536
19699
  hydratedWorkspaces.add(hydrateKey);
19537
19700
  }
19538
19701
  function fetchStateSnapshot(parsed) {
19539
- const cacheDir = path42.join(cacheRoot2(), cacheKey3(parsed));
19702
+ const cacheDir = path43.join(cacheRoot2(), cacheKey3(parsed));
19540
19703
  const url = `https://github.com/${parsed.owner}/${parsed.repo}.git`;
19541
19704
  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 });
19705
+ fs45.mkdirSync(path43.dirname(cacheDir), { recursive: true });
19706
+ if (!fs45.existsSync(path43.join(cacheDir, ".git"))) {
19707
+ fs45.rmSync(cacheDir, { recursive: true, force: true });
19545
19708
  runGit3(["clone", "--no-checkout", "--filter=blob:none", url, cacheDir]);
19546
19709
  }
19547
19710
  runGit3(["-C", cacheDir, "remote", "set-url", "origin", url]);
@@ -19556,10 +19719,10 @@ function fetchStateSnapshot(parsed) {
19556
19719
  `stateWorkspace: failed to fetch ${parsed.owner}/${parsed.repo}:${parsed.basePath}@${parsed.branch}: ${msg}`
19557
19720
  );
19558
19721
  }
19559
- return path42.join(cacheDir, parsed.basePath);
19722
+ return path43.join(cacheDir, parsed.basePath);
19560
19723
  }
19561
19724
  function cacheRoot2() {
19562
- return process.env[CACHE_ENV2]?.trim() || path42.join(os7.homedir(), ".cache", "kody", "state-repo");
19725
+ return process.env[CACHE_ENV2]?.trim() || path43.join(os7.homedir(), ".cache", "kody", "state-repo");
19563
19726
  }
19564
19727
  function cacheKey3(parsed) {
19565
19728
  return crypto3.createHash("sha256").update(`${parsed.owner}/${parsed.repo}#${parsed.branch}#${parsed.basePath}`).digest("hex").slice(0, 24);
@@ -19599,15 +19762,15 @@ var init_stateWorkspace = __esm({
19599
19762
  "use strict";
19600
19763
  init_stateRepo();
19601
19764
  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") }
19765
+ { stateDir: "capabilities", localDir: path43.join(".kody", "capabilities") },
19766
+ { stateDir: "agents", localDir: path43.join(".kody", "agents") },
19767
+ { stateDir: "context", localDir: path43.join(".kody", "context") },
19768
+ { stateDir: "memory", localDir: path43.join(".kody", "memory") }
19606
19769
  ];
19607
19770
  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") }
19771
+ { statePath: "instructions.md", localPath: path43.join(".kody", "instructions.md") },
19772
+ { statePath: "variables.json", localPath: path43.join(".kody", "variables.json") },
19773
+ { statePath: "secrets.enc", localPath: path43.join(".kody", "secrets.enc") }
19611
19774
  ];
19612
19775
  CACHE_ENV2 = "KODY_STATE_REPO_CACHE";
19613
19776
  TEST_FETCH_ENV = "KODY_STATE_WORKSPACE_FETCH_FOR_TESTS";
@@ -19681,8 +19844,8 @@ var init_tools = __esm({
19681
19844
 
19682
19845
  // src/executor.ts
19683
19846
  import { spawn as spawn7 } from "child_process";
19684
- import * as fs45 from "fs";
19685
- import * as path43 from "path";
19847
+ import * as fs46 from "fs";
19848
+ import * as path44 from "path";
19686
19849
  function isMutatingPostflight(scriptName) {
19687
19850
  return MUTATING_POSTFLIGHTS.has(scriptName ?? "");
19688
19851
  }
@@ -19727,9 +19890,7 @@ function jobReferenceBlock(profileName, profile, data) {
19727
19890
  const jobId = typeof data.jobId === "string" && data.jobId.length > 0 ? data.jobId : null;
19728
19891
  const flavor = typeof data.jobFlavor === "string" && data.jobFlavor.length > 0 ? data.jobFlavor : null;
19729
19892
  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
- );
19893
+ const isJob2 = Boolean(jobId || flavor || schedule || data.jobCapability || data.selectedImplementation || data.jobWhy);
19733
19894
  if (!isJob2) return null;
19734
19895
  const capability = typeof data.jobCapability === "string" && data.jobCapability.length > 0 ? data.jobCapability : profile.implementation ? profile.name : null;
19735
19896
  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 +20068,7 @@ async function runImplementation(profileName, input) {
19907
20068
  const jobWhyBlock = typeof ctx.data.jobWhy === "string" ? operatorRequestBlock(ctx.data.jobWhy) : null;
19908
20069
  const jobRefBlock = jobReferenceBlock(profileName, profile, ctx.data);
19909
20070
  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);
20071
+ const externalPlugins = (profile.claudeCode.plugins ?? []).map((p) => path44.isAbsolute(p) ? p : path44.resolve(profile.dir, p)).filter((p) => p.length > 0);
19911
20072
  const syntheticPath = ctx.data.syntheticPluginPath;
19912
20073
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
19913
20074
  const agents = loadSubagents(profile);
@@ -20333,17 +20494,17 @@ function clearStampedLifecycleLabels(profile, ctx) {
20333
20494
  function resolveProfilePath(profileName) {
20334
20495
  const found = resolveImplementation(profileName);
20335
20496
  if (found) return found;
20336
- const here = path43.dirname(new URL(import.meta.url).pathname);
20497
+ const here = path44.dirname(new URL(import.meta.url).pathname);
20337
20498
  const candidates = [
20338
- path43.join(here, "implementations", profileName, "profile.json"),
20499
+ path44.join(here, "implementations", profileName, "profile.json"),
20339
20500
  // same-dir sibling (dev)
20340
- path43.join(here, "..", "implementations", profileName, "profile.json"),
20501
+ path44.join(here, "..", "implementations", profileName, "profile.json"),
20341
20502
  // up one (prod: dist/bin → dist/implementations)
20342
- path43.join(here, "..", "src", "implementations", profileName, "profile.json")
20503
+ path44.join(here, "..", "src", "implementations", profileName, "profile.json")
20343
20504
  // fallback
20344
20505
  ];
20345
20506
  for (const c of candidates) {
20346
- if (fs45.existsSync(c)) return c;
20507
+ if (fs46.existsSync(c)) return c;
20347
20508
  }
20348
20509
  return candidates[0];
20349
20510
  }
@@ -20458,8 +20619,8 @@ function resolveShellTimeoutMs(entry) {
20458
20619
  }
20459
20620
  async function runShellEntry(entry, ctx, profile) {
20460
20621
  const shellName = entry.shell;
20461
- const shellPath = path43.join(profile.dir, shellName);
20462
- if (!fs45.existsSync(shellPath)) {
20622
+ const shellPath = path44.join(profile.dir, shellName);
20623
+ if (!fs46.existsSync(shellPath)) {
20463
20624
  ctx.skipAgent = true;
20464
20625
  ctx.output.exitCode = 99;
20465
20626
  ctx.output.reason = `shell script not found: ${shellName} (looked in ${profile.dir})`;
@@ -20610,101 +20771,6 @@ var init_executor = __esm({
20610
20771
  }
20611
20772
  });
20612
20773
 
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
20774
  // src/job.ts
20709
20775
  var job_exports = {};
20710
20776
  __export(job_exports, {
@@ -20824,6 +20890,7 @@ async function runCapabilityImplementationStep(valid, profileName, capabilityIde
20824
20890
  preloadedData.selectedImplementation = profileName;
20825
20891
  if (valid.schedule !== void 0 && valid.schedule.length > 0) preloadedData.jobSchedule = valid.schedule;
20826
20892
  if (valid.saveReport === true) preloadedData.jobSaveReport = true;
20893
+ if (valid.force === true) preloadedData.jobForce = true;
20827
20894
  if (valid.resultTarget) preloadedData.capabilityResultTarget = valid.resultTarget;
20828
20895
  if (capabilityContext) {
20829
20896
  preloadedData.capabilitySlug = capabilityContext.slug;
@@ -23032,8 +23099,10 @@ ${CI_HELP}`);
23032
23099
  `);
23033
23100
  const buildOnly = dispatch2.implementation === "preview-build";
23034
23101
  if (args.skipInstall || buildOnly) {
23035
- process.stdout.write(`\u2192 kody: skipping dep install (${buildOnly ? "build-only implementation" : "--skip-install"})
23036
- `);
23102
+ process.stdout.write(
23103
+ `\u2192 kody: skipping dep install (${buildOnly ? "build-only implementation" : "--skip-install"})
23104
+ `
23105
+ );
23037
23106
  } else {
23038
23107
  const code = installDeps(pm, cwd);
23039
23108
  if (code !== 0) {
@@ -26206,7 +26275,8 @@ Usage:
26206
26275
  kody-engine release --issue <N> [--cwd <path>] [--verbose|--quiet]
26207
26276
  kody-engine init [--cwd <path>] [--verbose|--quiet]
26208
26277
  kody-engine <action> [--cwd <path>] [--verbose|--quiet]
26209
- kody-engine implementation <name> [--cwd <path>] [--verbose|--quiet]
26278
+ kody-engine exec <name> [--cwd <path>] [--verbose|--quiet]
26279
+ kody-engine implementation <name> [--cwd <path>] [--verbose|--quiet]
26210
26280
  kody-engine ci [preflight flags \u2014 see: kody-engine ci --help]
26211
26281
  kody-engine chat [chat flags \u2014 see: kody-engine chat --help]
26212
26282
  kody-engine stats [--since 7d|--run <id>|--json|--cwd <path>]
@@ -26214,7 +26284,7 @@ Usage:
26214
26284
  kody-engine version
26215
26285
 
26216
26286
  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
26287
+ and resolves the implementation that runs it. Use the exec command only for
26218
26288
  internal implementation profiles and scheduled helpers.
26219
26289
 
26220
26290
  Exit codes:
@@ -26250,10 +26320,10 @@ function parseArgs(argv) {
26250
26320
  if (cmd === "stats") {
26251
26321
  return { ...result, command: "stats", statsArgv: argv.slice(1) };
26252
26322
  }
26253
- if (cmd === "implementation") {
26323
+ if (cmd === "implementation" || cmd === "exec") {
26254
26324
  const implementationName = argv[1];
26255
26325
  if (!implementationName || implementationName.startsWith("-")) {
26256
- result.errors.push("implementation requires a name");
26326
+ result.errors.push(`${cmd} requires a name`);
26257
26327
  return result;
26258
26328
  }
26259
26329
  if (!resolveImplementation(implementationName)) {
@@ -26288,7 +26358,7 @@ function parseArgs(argv) {
26288
26358
  return result;
26289
26359
  }
26290
26360
  const discoveredActions = listCapabilityActions().map((e) => e.action);
26291
- const available = ["ci", "chat", "stats", "implementation", "help", "version", ...discoveredActions];
26361
+ const available = ["ci", "chat", "stats", "exec", "implementation", "help", "version", ...discoveredActions];
26292
26362
  result.errors.push(`unknown command: ${cmd} (available: ${available.join(", ")})`);
26293
26363
  return result;
26294
26364
  }