@kody-ade/kody-engine 0.4.455 → 0.4.457

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.455",
18
+ version: "0.4.457",
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",
@@ -1770,6 +1770,7 @@ function parseWorkflowStep(value) {
1770
1770
  const evidence = stringField(raw.evidence);
1771
1771
  const reason = stringField(raw.reason);
1772
1772
  const target = stringField(raw.target);
1773
+ const delivery = stringField(raw.delivery);
1773
1774
  const targetFact = stringField(raw.targetFact ?? raw.target_fact);
1774
1775
  const hasInput = Object.hasOwn(raw, "input");
1775
1776
  const next = parseWorkflowTransitions(raw.next);
@@ -1781,6 +1782,7 @@ function parseWorkflowStep(value) {
1781
1782
  ...action && isSafeSlug(action) ? { action } : {},
1782
1783
  ...evidence ? { evidence } : {},
1783
1784
  ...target === "issue" || target === "pr" ? { target } : {},
1785
+ ...delivery === "pull-request" ? { delivery } : {},
1784
1786
  ...targetFact ? { targetFact } : {},
1785
1787
  ...reason ? { reason } : {},
1786
1788
  ...next ? { next } : {},
@@ -4212,6 +4214,24 @@ var init_agencyBoundaryEval = __esm({
4212
4214
  }
4213
4215
  });
4214
4216
 
4217
+ // src/capabilityDelivery.ts
4218
+ function capabilityDeliveryTarget(input) {
4219
+ if (!input || typeof input !== "object" || Array.isArray(input)) return null;
4220
+ const value = input;
4221
+ const issue2 = positiveInteger(value.issue);
4222
+ const pr = positiveInteger(value.pr);
4223
+ if (issue2 === null === (pr === null)) return null;
4224
+ return issue2 === null ? { kind: "pr", number: pr } : { kind: "issue", number: issue2 };
4225
+ }
4226
+ function positiveInteger(value) {
4227
+ return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : null;
4228
+ }
4229
+ var init_capabilityDelivery = __esm({
4230
+ "src/capabilityDelivery.ts"() {
4231
+ "use strict";
4232
+ }
4233
+ });
4234
+
4215
4235
  // src/agency/capability-contract-validation.ts
4216
4236
  import Ajv from "ajv";
4217
4237
  function validateCapabilityContractValue(boundary, schema, value) {
@@ -4350,7 +4370,7 @@ function parseGoalEvidenceProgress(value) {
4350
4370
  ...stringField2(raw.reason) ? { reason: stringField2(raw.reason) } : {},
4351
4371
  ...stringField2(raw.nextAction) ? { nextAction: stringField2(raw.nextAction) } : {},
4352
4372
  ...stringField2(raw.nextRetryAt) ? { nextRetryAt: stringField2(raw.nextRetryAt) } : {},
4353
- ...positiveInteger(raw.issue) ? { issue: positiveInteger(raw.issue) } : {},
4373
+ ...positiveInteger2(raw.issue) ? { issue: positiveInteger2(raw.issue) } : {},
4354
4374
  ...stringField2(raw.updatedAt) ? { updatedAt: stringField2(raw.updatedAt) } : {}
4355
4375
  };
4356
4376
  }
@@ -4366,7 +4386,7 @@ function definedProgressFields(update) {
4366
4386
  function stringField2(value) {
4367
4387
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
4368
4388
  }
4369
- function positiveInteger(value) {
4389
+ function positiveInteger2(value) {
4370
4390
  if (typeof value === "number" && Number.isInteger(value) && value > 0) return value;
4371
4391
  return void 0;
4372
4392
  }
@@ -8904,6 +8924,9 @@ function validateWorkflow(value, options = {}) {
8904
8924
  }
8905
8925
  }
8906
8926
  validateDataMatch(step.runWhen, `${base}.runWhen`, issues);
8927
+ if (step.delivery !== void 0 && step.delivery !== "pull-request") {
8928
+ issue(issues, "invalid_delivery", `${base}.delivery`, "workflow step delivery must be pull-request");
8929
+ }
8907
8930
  if (step.input !== void 0 && !isJsonValue(step.input)) {
8908
8931
  issue(issues, "invalid_input", `${base}.input`, "workflow step input must be one JSON value");
8909
8932
  }
@@ -9118,6 +9141,7 @@ var init_workflowValidation = __esm({
9118
9141
  "action",
9119
9142
  "evidence",
9120
9143
  "target",
9144
+ "delivery",
9121
9145
  "targetFact",
9122
9146
  "reason",
9123
9147
  "next",
@@ -13033,124 +13057,6 @@ var init_dispatch = __esm({
13033
13057
  }
13034
13058
  });
13035
13059
 
13036
- // src/scripts/dispatchCapabilityFileTicks.ts
13037
- var dispatchCapabilityFileTicks;
13038
- var init_dispatchCapabilityFileTicks = __esm({
13039
- "src/scripts/dispatchCapabilityFileTicks.ts"() {
13040
- "use strict";
13041
- dispatchCapabilityFileTicks = async (ctx) => {
13042
- ctx.skipAgent = true;
13043
- ctx.data.jobTickResults = [];
13044
- ctx.output.exitCode = 0;
13045
- ctx.output.reason = "capability scheduling is owned by goals and loops";
13046
- process.stdout.write("[jobs] no flat capability fan-out; goals and loops own scheduled capability decisions\n");
13047
- };
13048
- }
13049
- });
13050
-
13051
- // src/scripts/dispatchCapabilityTicks.ts
13052
- function listIssuesByLabel(label, cwd) {
13053
- let raw = "";
13054
- try {
13055
- raw = gh(["issue", "list", "--state", "open", "--label", label, "--limit", "100", "--json", "number,title"], {
13056
- cwd
13057
- });
13058
- } catch {
13059
- return [];
13060
- }
13061
- let list;
13062
- try {
13063
- list = JSON.parse(raw);
13064
- } catch {
13065
- return [];
13066
- }
13067
- if (!Array.isArray(list)) return [];
13068
- return list.filter((x) => typeof x.number === "number" && typeof x.title === "string").map((x) => ({ number: x.number, title: x.title }));
13069
- }
13070
- var dispatchCapabilityTicks;
13071
- var init_dispatchCapabilityTicks = __esm({
13072
- "src/scripts/dispatchCapabilityTicks.ts"() {
13073
- "use strict";
13074
- init_issue();
13075
- init_job();
13076
- dispatchCapabilityTicks = async (ctx, _profile, args) => {
13077
- ctx.skipAgent = true;
13078
- const label = String(args?.label ?? "");
13079
- const targetImplementation = String(args?.targetImplementation ?? "");
13080
- if (!label) throw new Error("dispatchCapabilityTicks: `with.label` is required");
13081
- if (!targetImplementation) throw new Error("dispatchCapabilityTicks: `with.targetImplementation` is required");
13082
- const issueArg = String(args?.issueArg ?? "issue");
13083
- const issues = listIssuesByLabel(label, ctx.cwd);
13084
- ctx.data.jobIssueCount = issues.length;
13085
- if (issues.length === 0) {
13086
- process.stdout.write(`[jobs] no open issues with label "${label}"
13087
- `);
13088
- return;
13089
- }
13090
- process.stdout.write(`[jobs] ticking ${issues.length} issue(s) via ${targetImplementation}
13091
- `);
13092
- const results = [];
13093
- for (const issue2 of issues) {
13094
- process.stdout.write(`[jobs] \u2192 tick #${issue2.number}: ${issue2.title}
13095
- `);
13096
- try {
13097
- const out = await runJob(
13098
- mintScheduledJob({
13099
- capability: targetImplementation,
13100
- implementation: targetImplementation,
13101
- cliArgs: { [issueArg]: issue2.number }
13102
- }),
13103
- { cwd: ctx.cwd, config: ctx.config, verbose: ctx.verbose, quiet: ctx.quiet, chain: false }
13104
- );
13105
- results.push({ issue: issue2.number, exitCode: out.exitCode, reason: out.reason });
13106
- if (out.exitCode !== 0) {
13107
- process.stderr.write(`[jobs] tick #${issue2.number} failed (exit ${out.exitCode}): ${out.reason ?? ""}
13108
- `);
13109
- }
13110
- } catch (err) {
13111
- const msg = err instanceof Error ? err.message : String(err);
13112
- process.stderr.write(`[jobs] tick #${issue2.number} crashed: ${msg}
13113
- `);
13114
- results.push({ issue: issue2.number, exitCode: 99, reason: msg });
13115
- }
13116
- }
13117
- ctx.data.jobTickResults = results;
13118
- ctx.output.exitCode = 0;
13119
- };
13120
- }
13121
- });
13122
-
13123
- // src/scripts/dispatchClassified.ts
13124
- var VALID_CLASSES2, dispatchClassified;
13125
- var init_dispatchClassified = __esm({
13126
- "src/scripts/dispatchClassified.ts"() {
13127
- "use strict";
13128
- init_registry();
13129
- init_state();
13130
- init_saveTaskState();
13131
- VALID_CLASSES2 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
13132
- dispatchClassified = async (ctx, profile) => {
13133
- const issueNumber = ctx.args.issue;
13134
- if (!issueNumber) return;
13135
- const classification = ctx.data.classification;
13136
- if (!classification || !VALID_CLASSES2.has(classification)) return;
13137
- const action = ctx.data.action;
13138
- if (!action) return;
13139
- const base = typeof ctx.args.base === "string" && ctx.args.base.length > 0 ? ctx.args.base : void 0;
13140
- const state = ctx.data.taskState ?? emptyState();
13141
- const nextState = reduce(state, "classify", action, void 0, profile.agent, jobMetaFromData(ctx.data));
13142
- ctx.data.taskState = nextState;
13143
- ctx.data.taskStateRendered = renderStateComment(nextState);
13144
- await writeTaskState("issue", issueNumber, nextState, ctx.cwd, ctx.config);
13145
- const cliArgs = { issue: issueNumber };
13146
- if (base && getProfileInputs(classification)?.some((i) => i.name === "base")) {
13147
- cliArgs.base = base;
13148
- }
13149
- ctx.output.nextDispatch = { action: classification, cliArgs };
13150
- };
13151
- }
13152
- });
13153
-
13154
13060
  // src/goal/agencyModelRepository.ts
13155
13061
  import {
13156
13062
  createAgentDefinition,
@@ -13826,107 +13732,291 @@ var init_dispatchAgencyLoops = __esm({
13826
13732
  }
13827
13733
  });
13828
13734
 
13829
- // src/loopDefinitions.ts
13830
- import * as fs36 from "fs";
13831
- import * as path34 from "path";
13832
- function normalizeLoopDefinition(value) {
13833
- if (!value || typeof value !== "object" || Array.isArray(value)) return null;
13834
- const raw = value;
13835
- if (Object.keys(raw).some((key) => !["id", "trigger", "target", "input", "enabled"].includes(key))) return null;
13836
- if (typeof raw.id !== "string" || !ID.test(raw.id)) return null;
13837
- if (typeof raw.enabled !== "boolean") return null;
13838
- if (!isObject(raw.input) || !isObject(raw.trigger) || !isObject(raw.target)) return null;
13839
- const targetKind = raw.target.kind;
13840
- const targetId = raw.target.id;
13841
- if (targetKind !== "workflow" && targetKind !== "capability" || typeof targetId !== "string" || !ID.test(targetId)) {
13842
- return null;
13843
- }
13844
- const trigger = normalizeTrigger(raw.trigger);
13845
- if (!trigger) return null;
13846
- return {
13847
- id: raw.id,
13848
- trigger,
13849
- target: { kind: targetKind, id: targetId },
13850
- input: raw.input,
13851
- enabled: raw.enabled
13852
- };
13853
- }
13854
- function readLoopDefinition(cwd, id) {
13855
- if (!ID.test(id)) return null;
13856
- const roots = loopRoots(cwd);
13857
- for (const root of roots) {
13858
- const filePath = path34.join(root, "loops", id, "loop.json");
13859
- if (!fs36.existsSync(filePath)) continue;
13860
- try {
13861
- const loop = normalizeLoopDefinition(JSON.parse(fs36.readFileSync(filePath, "utf8")));
13862
- if (loop?.id === id) return loop;
13863
- process.stderr.write(`[kody] invalid simple Loop definition: ${filePath}
13864
- `);
13865
- } catch {
13866
- process.stderr.write(`[kody] unreadable simple Loop definition: ${filePath}
13867
- `);
13868
- }
13869
- }
13870
- process.stderr.write(
13871
- `[kody] simple Loop not found: ${id} (${roots.map((root) => path34.join(root, "loops", id, "loop.json")).join(", ")})
13872
- `
13873
- );
13874
- return null;
13875
- }
13876
- function listLoopDefinitions(cwd) {
13877
- const roots = loopRoots(cwd);
13878
- const byId = /* @__PURE__ */ new Map();
13879
- for (const root of roots.reverse()) {
13880
- const loopsDir = path34.join(root, "loops");
13881
- if (!fs36.existsSync(loopsDir)) continue;
13882
- for (const id of fs36.readdirSync(loopsDir).sort()) {
13883
- if (!ID.test(id)) continue;
13884
- const filePath = path34.join(loopsDir, id, "loop.json");
13885
- if (!fs36.existsSync(filePath)) continue;
13886
- try {
13887
- const loop = normalizeLoopDefinition(JSON.parse(fs36.readFileSync(filePath, "utf8")));
13888
- if (loop?.id === id) byId.set(id, loop);
13889
- } catch {
13890
- process.stderr.write(`[kody] unreadable simple Loop definition: ${filePath}
13891
- `);
13892
- }
13893
- }
13894
- }
13895
- return [...byId.values()].sort((left, right) => left.id.localeCompare(right.id));
13896
- }
13897
- function loopRoots(cwd) {
13898
- return [
13899
- path34.join(cwd, ".kody-engine", "runtime"),
13900
- path34.join(cwd, ".kody-engine", "definitions"),
13901
- definitionsRoot(cwd)
13902
- ].filter((root, index, roots) => roots.indexOf(root) === index);
13903
- }
13904
- function normalizeTrigger(raw) {
13905
- if (raw.type === "manual" && Object.keys(raw).length === 1) return { type: "manual" };
13906
- if (raw.type === "schedule" && typeof raw.every === "string" && /^\d+[mhd]$/.test(raw.every)) {
13907
- if (raw.at === void 0 && Object.keys(raw).every((key) => key === "type" || key === "every")) {
13908
- return { type: "schedule", every: raw.every };
13909
- }
13910
- if (isObject(raw.at) && typeof raw.at.time === "string" && typeof raw.at.timezone === "string" && Object.keys(raw.at).every((key) => key === "time" || key === "timezone") && Object.keys(raw).every((key) => key === "type" || key === "every" || key === "at")) {
13911
- return { type: "schedule", every: raw.every, at: { time: raw.at.time, timezone: raw.at.timezone } };
13912
- }
13735
+ // src/scripts/dispatchCapabilityFileTicks.ts
13736
+ var dispatchCapabilityFileTicks;
13737
+ var init_dispatchCapabilityFileTicks = __esm({
13738
+ "src/scripts/dispatchCapabilityFileTicks.ts"() {
13739
+ "use strict";
13740
+ dispatchCapabilityFileTicks = async (ctx) => {
13741
+ ctx.skipAgent = true;
13742
+ ctx.data.jobTickResults = [];
13743
+ ctx.output.exitCode = 0;
13744
+ ctx.output.reason = "capability scheduling is owned by goals and loops";
13745
+ process.stdout.write("[jobs] no flat capability fan-out; goals and loops own scheduled capability decisions\n");
13746
+ };
13913
13747
  }
13914
- if ((raw.type === "event" || raw.type === "webhook") && typeof raw.event === "string" && raw.event.trim() && Object.keys(raw).every((key) => key === "type" || key === "event")) {
13915
- return { type: raw.type, event: raw.event.trim() };
13748
+ });
13749
+
13750
+ // src/scripts/dispatchCapabilityTicks.ts
13751
+ function listIssuesByLabel(label, cwd) {
13752
+ let raw = "";
13753
+ try {
13754
+ raw = gh(["issue", "list", "--state", "open", "--label", label, "--limit", "100", "--json", "number,title"], {
13755
+ cwd
13756
+ });
13757
+ } catch {
13758
+ return [];
13916
13759
  }
13917
- if (raw.type === "condition" && typeof raw.expression === "string" && raw.expression.trim() && Object.keys(raw).every((key) => key === "type" || key === "expression")) {
13918
- return { type: "condition", expression: raw.expression.trim() };
13760
+ let list;
13761
+ try {
13762
+ list = JSON.parse(raw);
13763
+ } catch {
13764
+ return [];
13919
13765
  }
13920
- return null;
13921
- }
13922
- function isObject(value) {
13923
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
13766
+ if (!Array.isArray(list)) return [];
13767
+ return list.filter((x) => typeof x.number === "number" && typeof x.title === "string").map((x) => ({ number: x.number, title: x.title }));
13924
13768
  }
13925
- var ID;
13926
- var init_loopDefinitions = __esm({
13927
- "src/loopDefinitions.ts"() {
13769
+ var dispatchCapabilityTicks;
13770
+ var init_dispatchCapabilityTicks = __esm({
13771
+ "src/scripts/dispatchCapabilityTicks.ts"() {
13928
13772
  "use strict";
13929
- init_definition_paths();
13773
+ init_issue();
13774
+ init_job();
13775
+ dispatchCapabilityTicks = async (ctx, _profile, args) => {
13776
+ ctx.skipAgent = true;
13777
+ const label = String(args?.label ?? "");
13778
+ const targetImplementation = String(args?.targetImplementation ?? "");
13779
+ if (!label) throw new Error("dispatchCapabilityTicks: `with.label` is required");
13780
+ if (!targetImplementation) throw new Error("dispatchCapabilityTicks: `with.targetImplementation` is required");
13781
+ const issueArg = String(args?.issueArg ?? "issue");
13782
+ const issues = listIssuesByLabel(label, ctx.cwd);
13783
+ ctx.data.jobIssueCount = issues.length;
13784
+ if (issues.length === 0) {
13785
+ process.stdout.write(`[jobs] no open issues with label "${label}"
13786
+ `);
13787
+ return;
13788
+ }
13789
+ process.stdout.write(`[jobs] ticking ${issues.length} issue(s) via ${targetImplementation}
13790
+ `);
13791
+ const results = [];
13792
+ for (const issue2 of issues) {
13793
+ process.stdout.write(`[jobs] \u2192 tick #${issue2.number}: ${issue2.title}
13794
+ `);
13795
+ try {
13796
+ const out = await runJob(
13797
+ mintScheduledJob({
13798
+ capability: targetImplementation,
13799
+ implementation: targetImplementation,
13800
+ cliArgs: { [issueArg]: issue2.number }
13801
+ }),
13802
+ { cwd: ctx.cwd, config: ctx.config, verbose: ctx.verbose, quiet: ctx.quiet, chain: false }
13803
+ );
13804
+ results.push({ issue: issue2.number, exitCode: out.exitCode, reason: out.reason });
13805
+ if (out.exitCode !== 0) {
13806
+ process.stderr.write(`[jobs] tick #${issue2.number} failed (exit ${out.exitCode}): ${out.reason ?? ""}
13807
+ `);
13808
+ }
13809
+ } catch (err) {
13810
+ const msg = err instanceof Error ? err.message : String(err);
13811
+ process.stderr.write(`[jobs] tick #${issue2.number} crashed: ${msg}
13812
+ `);
13813
+ results.push({ issue: issue2.number, exitCode: 99, reason: msg });
13814
+ }
13815
+ }
13816
+ ctx.data.jobTickResults = results;
13817
+ ctx.output.exitCode = 0;
13818
+ };
13819
+ }
13820
+ });
13821
+
13822
+ // src/scripts/dispatchClassified.ts
13823
+ var VALID_CLASSES2, dispatchClassified;
13824
+ var init_dispatchClassified = __esm({
13825
+ "src/scripts/dispatchClassified.ts"() {
13826
+ "use strict";
13827
+ init_registry();
13828
+ init_state();
13829
+ init_saveTaskState();
13830
+ VALID_CLASSES2 = /* @__PURE__ */ new Set(["feature", "bug", "spec", "chore"]);
13831
+ dispatchClassified = async (ctx, profile) => {
13832
+ const issueNumber = ctx.args.issue;
13833
+ if (!issueNumber) return;
13834
+ const classification = ctx.data.classification;
13835
+ if (!classification || !VALID_CLASSES2.has(classification)) return;
13836
+ const action = ctx.data.action;
13837
+ if (!action) return;
13838
+ const base = typeof ctx.args.base === "string" && ctx.args.base.length > 0 ? ctx.args.base : void 0;
13839
+ const state = ctx.data.taskState ?? emptyState();
13840
+ const nextState = reduce(state, "classify", action, void 0, profile.agent, jobMetaFromData(ctx.data));
13841
+ ctx.data.taskState = nextState;
13842
+ ctx.data.taskStateRendered = renderStateComment(nextState);
13843
+ await writeTaskState("issue", issueNumber, nextState, ctx.cwd, ctx.config);
13844
+ const cliArgs = { issue: issueNumber };
13845
+ if (base && getProfileInputs(classification)?.some((i) => i.name === "base")) {
13846
+ cliArgs.base = base;
13847
+ }
13848
+ ctx.output.nextDispatch = { action: classification, cliArgs };
13849
+ };
13850
+ }
13851
+ });
13852
+
13853
+ // src/jobIdentity.ts
13854
+ function stableJobKey(job) {
13855
+ const capability = job.workflow ?? job.capability ?? job.action;
13856
+ const implementation = job.implementation ?? capability ?? "unknown";
13857
+ if (job.flavor === "scheduled" && job.capability) return `scheduled:${job.capability}:${implementation}`;
13858
+ const target = typeof job.target === "number" ? job.target : targetFromCliArgs(job.cliArgs);
13859
+ const work = capability && implementation && implementation !== capability ? `${capability}:${implementation}` : capability ?? implementation;
13860
+ return target === void 0 ? `${job.flavor}:${work}` : `${job.flavor}:${work}:${target}`;
13861
+ }
13862
+ function targetFromCliArgs(cliArgs) {
13863
+ if (!cliArgs) return void 0;
13864
+ for (const key of ["issue", "pr", "target", "issue_number"]) {
13865
+ const value = cliArgs[key];
13866
+ if (typeof value === "number" && Number.isFinite(value)) return value;
13867
+ }
13868
+ return void 0;
13869
+ }
13870
+ var init_jobIdentity = __esm({
13871
+ "src/jobIdentity.ts"() {
13872
+ "use strict";
13873
+ }
13874
+ });
13875
+
13876
+ // src/scripts/dispatchNextTaskJob.ts
13877
+ function taskJobToJob(job, issueArg) {
13878
+ const target = typeof job.target === "number" ? job.target : typeof issueArg === "number" ? issueArg : void 0;
13879
+ return {
13880
+ capability: job.capability ?? job.implementation,
13881
+ implementation: job.implementation,
13882
+ ...job.reason ? { why: job.reason } : {},
13883
+ ...job.agent ? { agent: job.agent } : {},
13884
+ ...job.schedule ? { schedule: job.schedule } : {},
13885
+ ...typeof target === "number" ? { target, cliArgs: { issue: target } } : { cliArgs: {} },
13886
+ flavor: job.flavor ?? "instant"
13887
+ };
13888
+ }
13889
+ function isJob(input) {
13890
+ if (!input || typeof input !== "object" || Array.isArray(input)) return false;
13891
+ const job = input;
13892
+ return (typeof job.capability === "string" || typeof job.action === "string") && (job.flavor === "instant" || job.flavor === "scheduled") && (!job.cliArgs || typeof job.cliArgs === "object" && !Array.isArray(job.cliArgs));
13893
+ }
13894
+ var dispatchNextTaskJob;
13895
+ var init_dispatchNextTaskJob = __esm({
13896
+ "src/scripts/dispatchNextTaskJob.ts"() {
13897
+ "use strict";
13898
+ init_jobIdentity();
13899
+ init_state();
13900
+ dispatchNextTaskJob = async (ctx, profile) => {
13901
+ const state = ctx.data.taskState ?? emptyState();
13902
+ const ids = Array.isArray(ctx.data.plannedTaskJobIds) ? ctx.data.plannedTaskJobIds.filter((id) => typeof id === "string") : void 0;
13903
+ const next = nextPendingTaskJob(state, ids);
13904
+ ctx.skipAgent = true;
13905
+ if (!next) {
13906
+ ctx.output.exitCode = 0;
13907
+ ctx.output.reason = "all planned task jobs are complete";
13908
+ return;
13909
+ }
13910
+ const plannedJobs = Array.isArray(ctx.data.plannedTaskJobs) ? ctx.data.plannedTaskJobs.filter(isJob) : [];
13911
+ ctx.output.nextJob = plannedJobs.find((job) => stableJobKey(job) === next.id) ?? taskJobToJob(next, ctx.args.issue);
13912
+ if (typeof ctx.args.issue === "number") {
13913
+ ctx.output.afterNextJob = { action: profile.action ?? profile.name, cliArgs: { issue: ctx.args.issue } };
13914
+ }
13915
+ };
13916
+ }
13917
+ });
13918
+
13919
+ // src/loopDefinitions.ts
13920
+ import * as fs36 from "fs";
13921
+ import * as path34 from "path";
13922
+ function normalizeLoopDefinition(value) {
13923
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
13924
+ const raw = value;
13925
+ if (Object.keys(raw).some((key) => !["id", "trigger", "target", "input", "enabled"].includes(key))) return null;
13926
+ if (typeof raw.id !== "string" || !ID.test(raw.id)) return null;
13927
+ if (typeof raw.enabled !== "boolean") return null;
13928
+ if (!isObject(raw.input) || !isObject(raw.trigger) || !isObject(raw.target)) return null;
13929
+ const targetKind = raw.target.kind;
13930
+ const targetId = raw.target.id;
13931
+ if (targetKind !== "workflow" && targetKind !== "capability" || typeof targetId !== "string" || !ID.test(targetId)) {
13932
+ return null;
13933
+ }
13934
+ const trigger = normalizeTrigger(raw.trigger);
13935
+ if (!trigger) return null;
13936
+ return {
13937
+ id: raw.id,
13938
+ trigger,
13939
+ target: { kind: targetKind, id: targetId },
13940
+ input: raw.input,
13941
+ enabled: raw.enabled
13942
+ };
13943
+ }
13944
+ function readLoopDefinition(cwd, id) {
13945
+ if (!ID.test(id)) return null;
13946
+ const roots = loopRoots(cwd);
13947
+ for (const root of roots) {
13948
+ const filePath = path34.join(root, "loops", id, "loop.json");
13949
+ if (!fs36.existsSync(filePath)) continue;
13950
+ try {
13951
+ const loop = normalizeLoopDefinition(JSON.parse(fs36.readFileSync(filePath, "utf8")));
13952
+ if (loop?.id === id) return loop;
13953
+ process.stderr.write(`[kody] invalid simple Loop definition: ${filePath}
13954
+ `);
13955
+ } catch {
13956
+ process.stderr.write(`[kody] unreadable simple Loop definition: ${filePath}
13957
+ `);
13958
+ }
13959
+ }
13960
+ process.stderr.write(
13961
+ `[kody] simple Loop not found: ${id} (${roots.map((root) => path34.join(root, "loops", id, "loop.json")).join(", ")})
13962
+ `
13963
+ );
13964
+ return null;
13965
+ }
13966
+ function listLoopDefinitions(cwd) {
13967
+ const roots = loopRoots(cwd);
13968
+ const byId = /* @__PURE__ */ new Map();
13969
+ for (const root of roots.reverse()) {
13970
+ const loopsDir = path34.join(root, "loops");
13971
+ if (!fs36.existsSync(loopsDir)) continue;
13972
+ for (const id of fs36.readdirSync(loopsDir).sort()) {
13973
+ if (!ID.test(id)) continue;
13974
+ const filePath = path34.join(loopsDir, id, "loop.json");
13975
+ if (!fs36.existsSync(filePath)) continue;
13976
+ try {
13977
+ const loop = normalizeLoopDefinition(JSON.parse(fs36.readFileSync(filePath, "utf8")));
13978
+ if (loop?.id === id) byId.set(id, loop);
13979
+ } catch {
13980
+ process.stderr.write(`[kody] unreadable simple Loop definition: ${filePath}
13981
+ `);
13982
+ }
13983
+ }
13984
+ }
13985
+ return [...byId.values()].sort((left, right) => left.id.localeCompare(right.id));
13986
+ }
13987
+ function loopRoots(cwd) {
13988
+ return [
13989
+ path34.join(cwd, ".kody-engine", "runtime"),
13990
+ path34.join(cwd, ".kody-engine", "definitions"),
13991
+ definitionsRoot(cwd)
13992
+ ].filter((root, index, roots) => roots.indexOf(root) === index);
13993
+ }
13994
+ function normalizeTrigger(raw) {
13995
+ if (raw.type === "manual" && Object.keys(raw).length === 1) return { type: "manual" };
13996
+ if (raw.type === "schedule" && typeof raw.every === "string" && /^\d+[mhd]$/.test(raw.every)) {
13997
+ if (raw.at === void 0 && Object.keys(raw).every((key) => key === "type" || key === "every")) {
13998
+ return { type: "schedule", every: raw.every };
13999
+ }
14000
+ if (isObject(raw.at) && typeof raw.at.time === "string" && typeof raw.at.timezone === "string" && Object.keys(raw.at).every((key) => key === "time" || key === "timezone") && Object.keys(raw).every((key) => key === "type" || key === "every" || key === "at")) {
14001
+ return { type: "schedule", every: raw.every, at: { time: raw.at.time, timezone: raw.at.timezone } };
14002
+ }
14003
+ }
14004
+ if ((raw.type === "event" || raw.type === "webhook") && typeof raw.event === "string" && raw.event.trim() && Object.keys(raw).every((key) => key === "type" || key === "event")) {
14005
+ return { type: raw.type, event: raw.event.trim() };
14006
+ }
14007
+ if (raw.type === "condition" && typeof raw.expression === "string" && raw.expression.trim() && Object.keys(raw).every((key) => key === "type" || key === "expression")) {
14008
+ return { type: "condition", expression: raw.expression.trim() };
14009
+ }
14010
+ return null;
14011
+ }
14012
+ function isObject(value) {
14013
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
14014
+ }
14015
+ var ID;
14016
+ var init_loopDefinitions = __esm({
14017
+ "src/loopDefinitions.ts"() {
14018
+ "use strict";
14019
+ init_definition_paths();
13930
14020
  ID = /^[a-z0-9][a-z0-9_-]{0,79}$/;
13931
14021
  }
13932
14022
  });
@@ -14051,72 +14141,6 @@ var init_dispatchSimpleLoops = __esm({
14051
14141
  }
14052
14142
  });
14053
14143
 
14054
- // src/jobIdentity.ts
14055
- function stableJobKey(job) {
14056
- const capability = job.workflow ?? job.capability ?? job.action;
14057
- const implementation = job.implementation ?? capability ?? "unknown";
14058
- if (job.flavor === "scheduled" && job.capability) return `scheduled:${job.capability}:${implementation}`;
14059
- const target = typeof job.target === "number" ? job.target : targetFromCliArgs(job.cliArgs);
14060
- const work = capability && implementation && implementation !== capability ? `${capability}:${implementation}` : capability ?? implementation;
14061
- return target === void 0 ? `${job.flavor}:${work}` : `${job.flavor}:${work}:${target}`;
14062
- }
14063
- function targetFromCliArgs(cliArgs) {
14064
- if (!cliArgs) return void 0;
14065
- for (const key of ["issue", "pr", "target", "issue_number"]) {
14066
- const value = cliArgs[key];
14067
- if (typeof value === "number" && Number.isFinite(value)) return value;
14068
- }
14069
- return void 0;
14070
- }
14071
- var init_jobIdentity = __esm({
14072
- "src/jobIdentity.ts"() {
14073
- "use strict";
14074
- }
14075
- });
14076
-
14077
- // src/scripts/dispatchNextTaskJob.ts
14078
- function taskJobToJob(job, issueArg) {
14079
- const target = typeof job.target === "number" ? job.target : typeof issueArg === "number" ? issueArg : void 0;
14080
- return {
14081
- capability: job.capability ?? job.implementation,
14082
- implementation: job.implementation,
14083
- ...job.reason ? { why: job.reason } : {},
14084
- ...job.agent ? { agent: job.agent } : {},
14085
- ...job.schedule ? { schedule: job.schedule } : {},
14086
- ...typeof target === "number" ? { target, cliArgs: { issue: target } } : { cliArgs: {} },
14087
- flavor: job.flavor ?? "instant"
14088
- };
14089
- }
14090
- function isJob(input) {
14091
- if (!input || typeof input !== "object" || Array.isArray(input)) return false;
14092
- const job = input;
14093
- return (typeof job.capability === "string" || typeof job.action === "string") && (job.flavor === "instant" || job.flavor === "scheduled") && (!job.cliArgs || typeof job.cliArgs === "object" && !Array.isArray(job.cliArgs));
14094
- }
14095
- var dispatchNextTaskJob;
14096
- var init_dispatchNextTaskJob = __esm({
14097
- "src/scripts/dispatchNextTaskJob.ts"() {
14098
- "use strict";
14099
- init_jobIdentity();
14100
- init_state();
14101
- dispatchNextTaskJob = async (ctx, profile) => {
14102
- const state = ctx.data.taskState ?? emptyState();
14103
- const ids = Array.isArray(ctx.data.plannedTaskJobIds) ? ctx.data.plannedTaskJobIds.filter((id) => typeof id === "string") : void 0;
14104
- const next = nextPendingTaskJob(state, ids);
14105
- ctx.skipAgent = true;
14106
- if (!next) {
14107
- ctx.output.exitCode = 0;
14108
- ctx.output.reason = "all planned task jobs are complete";
14109
- return;
14110
- }
14111
- const plannedJobs = Array.isArray(ctx.data.plannedTaskJobs) ? ctx.data.plannedTaskJobs.filter(isJob) : [];
14112
- ctx.output.nextJob = plannedJobs.find((job) => stableJobKey(job) === next.id) ?? taskJobToJob(next, ctx.args.issue);
14113
- if (typeof ctx.args.issue === "number") {
14114
- ctx.output.afterNextJob = { action: profile.action ?? profile.name, cliArgs: { issue: ctx.args.issue } };
14115
- }
14116
- };
14117
- }
14118
- });
14119
-
14120
14144
  // src/pr.ts
14121
14145
  import { execFileSync as execFileSync10 } from "child_process";
14122
14146
  function prMergeStatus(prNumber, cwd) {
@@ -15384,158 +15408,40 @@ var init_loadCapabilityState = __esm({
15384
15408
  const slug = profile.name;
15385
15409
  const backend = resolveBackend({ config: ctx.config, cwd: ctx.cwd, jobsDir });
15386
15410
  if (backend.hydrate) await backend.hydrate();
15387
- const loaded = await backend.load(slug);
15388
- ctx.data.jobSlug = slug;
15389
- ctx.data.jobState = loaded;
15390
- ctx.data.jobStateJson = JSON.stringify(loaded.state, null, 2);
15391
- ctx.data.capabilitySlug = slug;
15392
- ctx.data.capabilityTitle = profile.describe;
15393
- ctx.data.implementationSlug = profile.implementation ?? profile.name;
15394
- ctx.data.agentSlug = profile.agent ?? "";
15395
- ctx.data.agentTitle = "";
15396
- ctx.data.capabilitySchedule = String(ctx.data.jobSchedule ?? "");
15397
- const mentions = (profile.mentions ?? []).map((l) => `@${l}`).join(" ");
15398
- ctx.data.mentions = mentions;
15399
- const declaredTools = profile.capabilityTools ?? profile.capabilityTools ?? [];
15400
- if (declaredTools.length > 0) {
15401
- const unknown = declaredTools.filter((name) => !CAPABILITY_TOOL_PALETTE.has(name));
15402
- if (unknown.length > 0) {
15403
- throw new Error(
15404
- `loadCapabilityState: capability '${slug}' declared capabilityTools not in the kody-capability palette: ${unknown.join(", ")}. Available: ${[...CAPABILITY_MCP_TOOL_NAMES].join(", ")}`
15405
- );
15406
- }
15407
- const mode = profile.capabilityToolMode ?? "lock";
15408
- ctx.data.capabilityTools = declaredTools;
15409
- ctx.data.capabilityToolMode = mode;
15410
- ctx.data.capabilityToolsList = declaredTools.map((name) => `- \`${name}\``).join("\n");
15411
- ctx.data.capabilityOperatorMention = mentions;
15412
- const mcpToolNames = declaredTools.map((name) => `mcp__kody-capability__${name}`);
15413
- const submitStateTool = "mcp__kody-submit__submit_state";
15414
- profile.claudeCode.enableSubmitTool = true;
15415
- if (mode === "append") {
15416
- profile.claudeCode.tools = [.../* @__PURE__ */ new Set([...profile.claudeCode.tools ?? [], ...mcpToolNames, submitStateTool])];
15417
- return;
15418
- }
15419
- profile.claudeCode.tools = [...mcpToolNames, submitStateTool];
15420
- }
15421
- };
15422
- }
15423
- });
15424
-
15425
- // src/scripts/loadSimpleCapability.ts
15426
- import * as fs40 from "fs";
15427
- import * as path37 from "path";
15428
- function parseInput(supplied) {
15429
- if (typeof supplied !== "string") return supplied;
15430
- try {
15431
- return JSON.parse(supplied);
15432
- } catch {
15433
- return parseFlagInput(supplied) ?? supplied;
15434
- }
15435
- }
15436
- function parseFlagInput(value) {
15437
- const tokens = value.trim().split(/\s+/).filter(Boolean);
15438
- if (!tokens.some((token) => token.startsWith("--"))) return null;
15439
- const input = {};
15440
- const text2 = [];
15441
- for (let index = 0; index < tokens.length; index += 1) {
15442
- const token = tokens[index];
15443
- if (!token.startsWith("--") || token.length === 2) {
15444
- text2.push(token);
15445
- continue;
15446
- }
15447
- const equalAt = token.indexOf("=");
15448
- const name = equalAt >= 0 ? token.slice(2, equalAt) : token.slice(2);
15449
- const next = equalAt >= 0 ? token.slice(equalAt + 1) : tokens[index + 1];
15450
- if (equalAt < 0 && next && !next.startsWith("--")) index += 1;
15451
- input[name] = next && !next.startsWith("--") ? scalar(next) : true;
15452
- }
15453
- if (text2.length > 0) input.request = text2.join(" ");
15454
- return input;
15455
- }
15456
- function scalar(value) {
15457
- if (value === "true" || value === "false") return value === "true";
15458
- if (/^-?\d+$/.test(value)) return Number(value);
15459
- return value;
15460
- }
15461
- function capabilityEnvironment(input) {
15462
- const environment = {
15463
- KODY_CAPABILITY_INPUT: JSON.stringify(input ?? null)
15464
- };
15465
- if (!input || typeof input !== "object" || Array.isArray(input)) return environment;
15466
- for (const [name, value] of Object.entries(input)) {
15467
- if (value === void 0 || value === null) continue;
15468
- const key = name.toUpperCase().replace(/[^A-Z0-9]+/g, "_");
15469
- environment[`KODY_ARG_${key}`] = typeof value === "string" ? value : JSON.stringify(value);
15470
- }
15471
- return environment;
15472
- }
15473
- function listFiles(root) {
15474
- if (!fs40.existsSync(root)) return [];
15475
- const files = [];
15476
- const visit = (dir) => {
15477
- for (const entry of fs40.readdirSync(dir, { withFileTypes: true })) {
15478
- const absolute = path37.join(dir, entry.name);
15479
- if (entry.isSymbolicLink()) continue;
15480
- if (entry.isDirectory()) visit(absolute);
15481
- else if (entry.isFile()) files.push(path37.relative(root, absolute));
15482
- }
15483
- };
15484
- visit(root);
15485
- return files.sort();
15486
- }
15487
- var loadSimpleCapability;
15488
- var init_loadSimpleCapability = __esm({
15489
- "src/scripts/loadSimpleCapability.ts"() {
15490
- "use strict";
15491
- init_capabilityFolders();
15492
- init_definition_paths();
15493
- loadSimpleCapability = async (ctx) => {
15494
- const slug = typeof ctx.args.capability === "string" ? ctx.args.capability.trim() : "";
15495
- if (!/^[a-z][a-z0-9-]*$/.test(slug)) {
15496
- throw new Error("capability-run requires a valid capability slug");
15497
- }
15498
- const capability = readCapabilityFolder(capabilitiesRoot(ctx.cwd), slug);
15499
- if (!capability) {
15500
- throw new Error(`Capability "${slug}" is not a valid simple capability folder`);
15411
+ const loaded = await backend.load(slug);
15412
+ ctx.data.jobSlug = slug;
15413
+ ctx.data.jobState = loaded;
15414
+ ctx.data.jobStateJson = JSON.stringify(loaded.state, null, 2);
15415
+ ctx.data.capabilitySlug = slug;
15416
+ ctx.data.capabilityTitle = profile.describe;
15417
+ ctx.data.implementationSlug = profile.implementation ?? profile.name;
15418
+ ctx.data.agentSlug = profile.agent ?? "";
15419
+ ctx.data.agentTitle = "";
15420
+ ctx.data.capabilitySchedule = String(ctx.data.jobSchedule ?? "");
15421
+ const mentions = (profile.mentions ?? []).map((l) => `@${l}`).join(" ");
15422
+ ctx.data.mentions = mentions;
15423
+ const declaredTools = profile.capabilityTools ?? profile.capabilityTools ?? [];
15424
+ if (declaredTools.length > 0) {
15425
+ const unknown = declaredTools.filter((name) => !CAPABILITY_TOOL_PALETTE.has(name));
15426
+ if (unknown.length > 0) {
15427
+ throw new Error(
15428
+ `loadCapabilityState: capability '${slug}' declared capabilityTools not in the kody-capability palette: ${unknown.join(", ")}. Available: ${[...CAPABILITY_MCP_TOOL_NAMES].join(", ")}`
15429
+ );
15430
+ }
15431
+ const mode = profile.capabilityToolMode ?? "lock";
15432
+ ctx.data.capabilityTools = declaredTools;
15433
+ ctx.data.capabilityToolMode = mode;
15434
+ ctx.data.capabilityToolsList = declaredTools.map((name) => `- \`${name}\``).join("\n");
15435
+ ctx.data.capabilityOperatorMention = mentions;
15436
+ const mcpToolNames = declaredTools.map((name) => `mcp__kody-capability__${name}`);
15437
+ const submitStateTool = "mcp__kody-submit__submit_state";
15438
+ profile.claudeCode.enableSubmitTool = true;
15439
+ if (mode === "append") {
15440
+ profile.claudeCode.tools = [.../* @__PURE__ */ new Set([...profile.claudeCode.tools ?? [], ...mcpToolNames, submitStateTool])];
15441
+ return;
15442
+ }
15443
+ profile.claudeCode.tools = [...mcpToolNames, submitStateTool];
15501
15444
  }
15502
- const toolRoot = path37.join(capability.dir, "tools");
15503
- const skillRoot = path37.join(capability.dir, "skills");
15504
- const toolFiles = listFiles(toolRoot);
15505
- const skillFiles = listFiles(skillRoot);
15506
- const input = parseInput(ctx.args.input);
15507
- ctx.data.jobCapability = slug;
15508
- ctx.data.capabilityInput = input;
15509
- ctx.data.capabilityEnvironment = capabilityEnvironment(input);
15510
- ctx.data.prompt = [
15511
- capability.rawBody.trim(),
15512
- "",
15513
- "## Input",
15514
- "",
15515
- "```json",
15516
- JSON.stringify(input ?? null, null, 2),
15517
- "```",
15518
- "",
15519
- "Return one JSON value.",
15520
- ...skillFiles.length ? [
15521
- "",
15522
- "## Skills",
15523
- "",
15524
- ...skillFiles.flatMap((file) => [
15525
- `### ${file}`,
15526
- "",
15527
- fs40.readFileSync(path37.join(skillRoot, file), "utf-8"),
15528
- ""
15529
- ])
15530
- ] : [],
15531
- ...toolFiles.length ? [
15532
- "",
15533
- "## Tools",
15534
- "",
15535
- "Inspect or run these capability-owned files when needed:",
15536
- ...toolFiles.map((file) => `- ${path37.join(toolRoot, file)}`)
15537
- ] : []
15538
- ].join("\n");
15539
15445
  };
15540
15446
  }
15541
15447
  });
@@ -15841,8 +15747,8 @@ var init_loadIssueStateComment = __esm({
15841
15747
  });
15842
15748
 
15843
15749
  // src/scripts/loadJobFromFile.ts
15844
- import * as fs41 from "fs";
15845
- import * as path38 from "path";
15750
+ import * as fs40 from "fs";
15751
+ import * as path37 from "path";
15846
15752
  function parseJobFile(raw, slug) {
15847
15753
  let stripped = raw;
15848
15754
  if (stripped.startsWith("---\n")) {
@@ -15881,10 +15787,10 @@ var init_loadJobFromFile = __esm({
15881
15787
  if (!slug) {
15882
15788
  throw new Error(`loadJobFromFile: ctx.args.${slugArg} must be a non-empty slug`);
15883
15789
  }
15884
- const capability = resolveCapabilityFolder(slug, path38.resolve(ctx.cwd, jobsDir));
15790
+ const capability = resolveCapabilityFolder(slug, path37.resolve(ctx.cwd, jobsDir));
15885
15791
  if (!capability) {
15886
15792
  throw new Error(
15887
- `loadJobFromFile: capability folder not found or incomplete: ${path38.resolve(ctx.cwd, jobsDir, slug)}`
15793
+ `loadJobFromFile: capability folder not found or incomplete: ${path37.resolve(ctx.cwd, jobsDir, slug)}`
15888
15794
  );
15889
15795
  }
15890
15796
  const { title, body, config } = capability;
@@ -15894,12 +15800,12 @@ var init_loadJobFromFile = __esm({
15894
15800
  let agentIdentity = "";
15895
15801
  if (agentSlug) {
15896
15802
  const agentPath = resolveAgentFile(ctx.cwd, agentSlug, agentsDir);
15897
- if (!fs41.existsSync(agentPath)) {
15803
+ if (!fs40.existsSync(agentPath)) {
15898
15804
  throw new Error(
15899
15805
  `loadJobFromFile: capability '${slug}' declares agent '${agentSlug}' but ${agentPath} does not exist`
15900
15806
  );
15901
15807
  }
15902
- const agentRaw = fs41.readFileSync(agentPath, "utf-8");
15808
+ const agentRaw = fs40.readFileSync(agentPath, "utf-8");
15903
15809
  const parsed = parseJobFile(agentRaw, agentSlug);
15904
15810
  agentTitle = parsed.title;
15905
15811
  agentIdentity = parsed.body;
@@ -15979,13 +15885,13 @@ ${truncate2(issue2.body, FINDING_BODY_MAX_BYTES)}`;
15979
15885
  });
15980
15886
 
15981
15887
  // src/scripts/kodyVariables.ts
15982
- import * as fs42 from "fs";
15983
- import * as path39 from "path";
15888
+ import * as fs41 from "fs";
15889
+ import * as path38 from "path";
15984
15890
  function readKodyVariables(cwd) {
15985
- const full = path39.join(cwd, KODY_VARIABLES_REL_PATH);
15891
+ const full = path38.join(cwd, KODY_VARIABLES_REL_PATH);
15986
15892
  let raw;
15987
15893
  try {
15988
- raw = fs42.readFileSync(full, "utf-8");
15894
+ raw = fs41.readFileSync(full, "utf-8");
15989
15895
  } catch {
15990
15896
  return {};
15991
15897
  }
@@ -16161,8 +16067,8 @@ var init_runtimeSecrets = __esm({
16161
16067
  });
16162
16068
 
16163
16069
  // src/scripts/loadQaContext.ts
16164
- import * as fs43 from "fs";
16165
- import * as path40 from "path";
16070
+ import * as fs42 from "fs";
16071
+ import * as path39 from "path";
16166
16072
  function parseSlugList(value) {
16167
16073
  const inner = value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value;
16168
16074
  return inner.split(",").map(
@@ -16191,18 +16097,18 @@ function readProfileAgents(raw) {
16191
16097
  return { agent: agent ?? legacy ?? ["kody"], body };
16192
16098
  }
16193
16099
  function readProfile(cwd) {
16194
- const dir = path40.join(cwd, CONTEXT_DIR_REL_PATH);
16195
- if (!fs43.existsSync(dir)) return "";
16100
+ const dir = path39.join(cwd, CONTEXT_DIR_REL_PATH);
16101
+ if (!fs42.existsSync(dir)) return "";
16196
16102
  let entries;
16197
16103
  try {
16198
- entries = fs43.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
16104
+ entries = fs42.readdirSync(dir).filter((f) => f.endsWith(".md")).sort();
16199
16105
  } catch {
16200
16106
  return "";
16201
16107
  }
16202
16108
  const blocks = [];
16203
16109
  for (const file of entries) {
16204
16110
  try {
16205
- const raw = fs43.readFileSync(path40.join(dir, file), "utf-8");
16111
+ const raw = fs42.readFileSync(path39.join(dir, file), "utf-8");
16206
16112
  const { agent, body } = readProfileAgents(raw);
16207
16113
  if (!agent.includes(QA_AGENT) && !agent.includes(ALL_AGENTS)) continue;
16208
16114
  blocks.push(`## ${file}
@@ -16250,6 +16156,139 @@ var init_loadQaContext = __esm({
16250
16156
  }
16251
16157
  });
16252
16158
 
16159
+ // src/scripts/loadSimpleCapability.ts
16160
+ import * as fs43 from "fs";
16161
+ import * as path40 from "path";
16162
+ function parseInput(supplied) {
16163
+ if (typeof supplied !== "string") return supplied;
16164
+ try {
16165
+ return JSON.parse(supplied);
16166
+ } catch {
16167
+ return parseFlagInput(supplied) ?? supplied;
16168
+ }
16169
+ }
16170
+ function parseFlagInput(value) {
16171
+ const tokens = value.trim().split(/\s+/).filter(Boolean);
16172
+ if (!tokens.some((token) => token.startsWith("--"))) return null;
16173
+ const input = {};
16174
+ const text2 = [];
16175
+ for (let index = 0; index < tokens.length; index += 1) {
16176
+ const token = tokens[index];
16177
+ if (!token.startsWith("--") || token.length === 2) {
16178
+ text2.push(token);
16179
+ continue;
16180
+ }
16181
+ const equalAt = token.indexOf("=");
16182
+ const name = equalAt >= 0 ? token.slice(2, equalAt) : token.slice(2);
16183
+ const next = equalAt >= 0 ? token.slice(equalAt + 1) : tokens[index + 1];
16184
+ if (equalAt < 0 && next && !next.startsWith("--")) index += 1;
16185
+ input[name] = next && !next.startsWith("--") ? scalar(next) : true;
16186
+ }
16187
+ if (text2.length > 0) input.request = text2.join(" ");
16188
+ return input;
16189
+ }
16190
+ function scalar(value) {
16191
+ if (value === "true" || value === "false") return value === "true";
16192
+ if (/^-?\d+$/.test(value)) return Number(value);
16193
+ return value;
16194
+ }
16195
+ function capabilityEnvironment(input) {
16196
+ const environment = {
16197
+ KODY_CAPABILITY_INPUT: JSON.stringify(input ?? null)
16198
+ };
16199
+ if (!input || typeof input !== "object" || Array.isArray(input)) return environment;
16200
+ for (const [name, value] of Object.entries(input)) {
16201
+ if (value === void 0 || value === null) continue;
16202
+ const key = name.toUpperCase().replace(/[^A-Z0-9]+/g, "_");
16203
+ environment[`KODY_ARG_${key}`] = typeof value === "string" ? value : JSON.stringify(value);
16204
+ }
16205
+ return environment;
16206
+ }
16207
+ function listFiles(root) {
16208
+ if (!fs43.existsSync(root)) return [];
16209
+ const files = [];
16210
+ const visit = (dir) => {
16211
+ for (const entry of fs43.readdirSync(dir, { withFileTypes: true })) {
16212
+ const absolute = path40.join(dir, entry.name);
16213
+ if (entry.isSymbolicLink()) continue;
16214
+ if (entry.isDirectory()) visit(absolute);
16215
+ else if (entry.isFile()) files.push(path40.relative(root, absolute));
16216
+ }
16217
+ };
16218
+ visit(root);
16219
+ return files.sort();
16220
+ }
16221
+ var loadSimpleCapability;
16222
+ var init_loadSimpleCapability = __esm({
16223
+ "src/scripts/loadSimpleCapability.ts"() {
16224
+ "use strict";
16225
+ init_capabilityFolders();
16226
+ init_definition_paths();
16227
+ loadSimpleCapability = async (ctx) => {
16228
+ const slug = typeof ctx.args.capability === "string" ? ctx.args.capability.trim() : "";
16229
+ if (!/^[a-z][a-z0-9-]*$/.test(slug)) {
16230
+ throw new Error("capability-run requires a valid capability slug");
16231
+ }
16232
+ const capability = readCapabilityFolder(capabilitiesRoot(ctx.cwd), slug);
16233
+ if (!capability) {
16234
+ throw new Error(`Capability "${slug}" is not a valid simple capability folder`);
16235
+ }
16236
+ const toolRoot = path40.join(capability.dir, "tools");
16237
+ const skillRoot = path40.join(capability.dir, "skills");
16238
+ const toolFiles = listFiles(toolRoot);
16239
+ const skillFiles = listFiles(skillRoot);
16240
+ const input = parseInput(ctx.args.input);
16241
+ const delivery = ctx.data.jobDelivery === "pull-request";
16242
+ ctx.data.jobCapability = slug;
16243
+ ctx.data.capabilityInput = input;
16244
+ ctx.data.capabilityEnvironment = capabilityEnvironment(input);
16245
+ ctx.data.prompt = [
16246
+ capability.rawBody.trim(),
16247
+ "",
16248
+ "## Input",
16249
+ "",
16250
+ "```json",
16251
+ JSON.stringify(input ?? null, null, 2),
16252
+ "```",
16253
+ "",
16254
+ ...delivery ? [
16255
+ "## Delivery",
16256
+ "",
16257
+ "The wrapper owns git commits, pushes, and pull requests. Do not run git or gh write commands.",
16258
+ "Finish with exactly this structure:",
16259
+ "",
16260
+ "DONE",
16261
+ "PLAN_DEVIATIONS: none",
16262
+ "COMMIT_MSG: <conventional commit message>",
16263
+ "PR_SUMMARY:",
16264
+ "- <what changed>",
16265
+ "```json",
16266
+ '{"summary":"<result>","status":"changed"}',
16267
+ "```"
16268
+ ] : ["Return one JSON value."],
16269
+ ...skillFiles.length ? [
16270
+ "",
16271
+ "## Skills",
16272
+ "",
16273
+ ...skillFiles.flatMap((file) => [
16274
+ `### ${file}`,
16275
+ "",
16276
+ fs43.readFileSync(path40.join(skillRoot, file), "utf-8"),
16277
+ ""
16278
+ ])
16279
+ ] : [],
16280
+ ...toolFiles.length ? [
16281
+ "",
16282
+ "## Tools",
16283
+ "",
16284
+ "Inspect or run these capability-owned files when needed:",
16285
+ ...toolFiles.map((file) => `- ${path40.join(toolRoot, file)}`)
16286
+ ] : []
16287
+ ].join("\n");
16288
+ };
16289
+ }
16290
+ });
16291
+
16253
16292
  // src/taskContext.ts
16254
16293
  import * as fs44 from "fs";
16255
16294
  import * as path41 from "path";
@@ -17074,95 +17113,11 @@ var init_parseJobStateFromAgentResult = __esm({
17074
17113
  done: loaded.state.done
17075
17114
  };
17076
17115
  return;
17077
- }
17078
- ctx.data.nextStateParseError = result.error.startsWith("missing `") ? `agent did not emit a \`${fenceLabel}\` fenced block` : result.error;
17079
- return;
17080
- }
17081
- ctx.data.nextJobState = result.envelope;
17082
- };
17083
- }
17084
- });
17085
-
17086
- // src/scripts/parseSimpleCapabilityOutput.ts
17087
- function parseOutput(text2) {
17088
- if (!text2) return void 0;
17089
- try {
17090
- return JSON.parse(text2);
17091
- } catch {
17092
- }
17093
- const fences = [...text2.matchAll(/```([a-z0-9_-]+)?\s*([\s\S]*?)\s*```/gi)];
17094
- const jsonFences = fences.filter((match) => match[1]?.toLowerCase() === "json");
17095
- const labelledOutput = parseSingleJsonCandidate(jsonFences.map((match) => match[2]));
17096
- if (labelledOutput.found) return labelledOutput.value;
17097
- const plainOutput = parseSingleJsonCandidate(fences.filter((match) => !match[1]).map((match) => match[2]));
17098
- if (plainOutput.found) return plainOutput.value;
17099
- const legacyText = text2.trim();
17100
- return legacyText ? { summary: legacyText, output: legacyText } : void 0;
17101
- }
17102
- function parseSingleJsonCandidate(candidates) {
17103
- const parsed = [];
17104
- for (const candidate of candidates) {
17105
- if (!candidate) continue;
17106
- try {
17107
- parsed.push(JSON.parse(candidate));
17108
- } catch {
17109
- }
17110
- }
17111
- return parsed.length === 1 ? { found: true, value: parsed[0] } : { found: false };
17112
- }
17113
- function isObject2(value) {
17114
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
17115
- }
17116
- function stringValue4(value) {
17117
- return typeof value === "string" && value.trim() ? value.trim() : void 0;
17118
- }
17119
- var parseSimpleCapabilityOutput;
17120
- var init_parseSimpleCapabilityOutput = __esm({
17121
- "src/scripts/parseSimpleCapabilityOutput.ts"() {
17122
- "use strict";
17123
- parseSimpleCapabilityOutput = async (ctx, _profile, agentResult) => {
17124
- const output = parseOutput(agentResult?.finalText);
17125
- if (output === void 0) {
17126
- const reason2 = agentResult?.outcomeKind === "out_of_turns" ? "Capability execution limit reached" : "Capability execution ended before returning a result";
17127
- const blocked = {
17128
- status: "blocked",
17129
- reason: reason2,
17130
- summary: reason2
17131
- };
17132
- ctx.output.reason = reason2;
17133
- ctx.data.capabilityOutput = blocked;
17134
- ctx.data.capabilityResults = [
17135
- {
17136
- version: 1,
17137
- status: "blocked",
17138
- summary: reason2,
17139
- facts: blocked,
17140
- artifacts: [],
17141
- missingEvidence: [],
17142
- blockers: [reason2]
17143
- }
17144
- ];
17116
+ }
17117
+ ctx.data.nextStateParseError = result.error.startsWith("missing `") ? `agent did not emit a \`${fenceLabel}\` fenced block` : result.error;
17145
17118
  return;
17146
17119
  }
17147
- ctx.data.capabilityOutput = output;
17148
- const result = isObject2(output) ? output : {};
17149
- const data = isObject2(result.data) ? result.data : isObject2(output) ? output : { output };
17150
- const summary = typeof result.summary === "string" ? result.summary : "Capability completed";
17151
- const reason = typeof result.reason === "string" ? result.reason : summary;
17152
- const prUrl = stringValue4(data.pullRequestUrl) ?? stringValue4(data.prUrl) ?? stringValue4(result.pullRequestUrl) ?? stringValue4(result.prUrl);
17153
- if (prUrl) ctx.output.prUrl = prUrl;
17154
- ctx.output.reason = reason;
17155
- ctx.data.capabilityResults = [
17156
- {
17157
- version: 1,
17158
- status: "changed",
17159
- summary,
17160
- facts: data,
17161
- artifacts: prUrl ? [{ label: "Pull request", url: prUrl }] : [],
17162
- missingEvidence: [],
17163
- blockers: []
17164
- }
17165
- ];
17120
+ ctx.data.nextJobState = result.envelope;
17166
17121
  };
17167
17122
  }
17168
17123
  });
@@ -17274,6 +17229,90 @@ var init_parseReproOutput = __esm({
17274
17229
  }
17275
17230
  });
17276
17231
 
17232
+ // src/scripts/parseSimpleCapabilityOutput.ts
17233
+ function parseOutput(text2) {
17234
+ if (!text2) return void 0;
17235
+ try {
17236
+ return JSON.parse(text2);
17237
+ } catch {
17238
+ }
17239
+ const fences = [...text2.matchAll(/```([a-z0-9_-]+)?\s*([\s\S]*?)\s*```/gi)];
17240
+ const jsonFences = fences.filter((match) => match[1]?.toLowerCase() === "json");
17241
+ const labelledOutput = parseSingleJsonCandidate(jsonFences.map((match) => match[2]));
17242
+ if (labelledOutput.found) return labelledOutput.value;
17243
+ const plainOutput = parseSingleJsonCandidate(fences.filter((match) => !match[1]).map((match) => match[2]));
17244
+ if (plainOutput.found) return plainOutput.value;
17245
+ const legacyText = text2.trim();
17246
+ return legacyText ? { summary: legacyText, output: legacyText } : void 0;
17247
+ }
17248
+ function parseSingleJsonCandidate(candidates) {
17249
+ const parsed = [];
17250
+ for (const candidate of candidates) {
17251
+ if (!candidate) continue;
17252
+ try {
17253
+ parsed.push(JSON.parse(candidate));
17254
+ } catch {
17255
+ }
17256
+ }
17257
+ return parsed.length === 1 ? { found: true, value: parsed[0] } : { found: false };
17258
+ }
17259
+ function isObject2(value) {
17260
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
17261
+ }
17262
+ function stringValue4(value) {
17263
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
17264
+ }
17265
+ var parseSimpleCapabilityOutput;
17266
+ var init_parseSimpleCapabilityOutput = __esm({
17267
+ "src/scripts/parseSimpleCapabilityOutput.ts"() {
17268
+ "use strict";
17269
+ parseSimpleCapabilityOutput = async (ctx, _profile, agentResult) => {
17270
+ const output = parseOutput(agentResult?.finalText);
17271
+ if (output === void 0) {
17272
+ const reason2 = agentResult?.outcomeKind === "out_of_turns" ? "Capability execution limit reached" : "Capability execution ended before returning a result";
17273
+ const blocked = {
17274
+ status: "blocked",
17275
+ reason: reason2,
17276
+ summary: reason2
17277
+ };
17278
+ ctx.output.reason = reason2;
17279
+ ctx.data.capabilityOutput = blocked;
17280
+ ctx.data.capabilityResults = [
17281
+ {
17282
+ version: 1,
17283
+ status: "blocked",
17284
+ summary: reason2,
17285
+ facts: blocked,
17286
+ artifacts: [],
17287
+ missingEvidence: [],
17288
+ blockers: [reason2]
17289
+ }
17290
+ ];
17291
+ return;
17292
+ }
17293
+ ctx.data.capabilityOutput = output;
17294
+ const result = isObject2(output) ? output : {};
17295
+ const data = isObject2(result.data) ? result.data : isObject2(output) ? output : { output };
17296
+ const summary = typeof result.summary === "string" ? result.summary : "Capability completed";
17297
+ const reason = typeof result.reason === "string" ? result.reason : summary;
17298
+ const prUrl = stringValue4(data.pullRequestUrl) ?? stringValue4(data.prUrl) ?? stringValue4(result.pullRequestUrl) ?? stringValue4(result.prUrl);
17299
+ if (prUrl) ctx.output.prUrl = prUrl;
17300
+ ctx.output.reason = reason;
17301
+ ctx.data.capabilityResults = [
17302
+ {
17303
+ version: 1,
17304
+ status: "changed",
17305
+ summary,
17306
+ facts: data,
17307
+ artifacts: prUrl ? [{ label: "Pull request", url: prUrl }] : [],
17308
+ missingEvidence: [],
17309
+ blockers: []
17310
+ }
17311
+ ];
17312
+ };
17313
+ }
17314
+ });
17315
+
17277
17316
  // src/scripts/persistArtifacts.ts
17278
17317
  function readDottedString(source, dotted) {
17279
17318
  const parts = dotted.split(".");
@@ -17908,20 +17947,220 @@ async function prepareMethod(ctx, profile, method) {
17908
17947
  );
17909
17948
  return false;
17910
17949
  }
17911
- }
17912
- var prepareBrowserAuth;
17913
- var init_prepareBrowserAuth = __esm({
17914
- "src/scripts/prepareBrowserAuth.ts"() {
17950
+ }
17951
+ var prepareBrowserAuth;
17952
+ var init_prepareBrowserAuth = __esm({
17953
+ "src/scripts/prepareBrowserAuth.ts"() {
17954
+ "use strict";
17955
+ init_runtimeCleanup();
17956
+ init_kodyVariables();
17957
+ init_runtimeSecrets();
17958
+ prepareBrowserAuth = async (ctx, profile) => {
17959
+ const methods = profile.auth?.methods ?? [];
17960
+ for (const method of methods) {
17961
+ if (method.strategy !== "browser-storage-state" || method.adapter !== "kody-repository") continue;
17962
+ if (await prepareMethod(ctx, profile, method)) return;
17963
+ }
17964
+ };
17965
+ }
17966
+ });
17967
+
17968
+ // src/scripts/runFlow.ts
17969
+ function tryPost(issueNumber, body, cwd) {
17970
+ try {
17971
+ postIssueComment(issueNumber, body, cwd);
17972
+ } catch {
17973
+ }
17974
+ }
17975
+ function resolveBaseOverride(value) {
17976
+ if (!value) return null;
17977
+ if (value.length > 200) return null;
17978
+ if (value.includes("..")) return null;
17979
+ if (!/^[a-z0-9][a-z0-9/._-]*$/.test(value)) return null;
17980
+ return value;
17981
+ }
17982
+ var runFlow;
17983
+ var init_runFlow = __esm({
17984
+ "src/scripts/runFlow.ts"() {
17985
+ "use strict";
17986
+ init_branch();
17987
+ init_gha();
17988
+ init_issue();
17989
+ runFlow = async (ctx) => {
17990
+ const issueNumber = ctx.args.issue;
17991
+ const issue2 = getIssue(issueNumber, ctx.cwd);
17992
+ const cfgCtx = ctx.config.issueContext ?? {};
17993
+ const commentsFormatted = formatIssueComments(
17994
+ issue2.comments,
17995
+ cfgCtx.commentLimit ?? DEFAULT_COMMENT_LIMIT,
17996
+ cfgCtx.commentMaxBytes ?? DEFAULT_COMMENT_MAX_BYTES
17997
+ );
17998
+ ctx.data.issue = { ...issue2, commentsFormatted };
17999
+ if (issue2.isPullRequest) {
18000
+ ctx.data.commentTargetType = "pr";
18001
+ ctx.data.commentTargetNumber = issueNumber;
18002
+ ctx.skipAgent = true;
18003
+ ctx.output.exitCode = 1;
18004
+ ctx.output.reason = `run target #${issueNumber} is a pull request; dispatch a PR action or the source issue instead`;
18005
+ return;
18006
+ }
18007
+ ctx.data.commentTargetType = "issue";
18008
+ ctx.data.commentTargetNumber = issueNumber;
18009
+ const argBase = resolveBaseOverride(ctx.args.base);
18010
+ const baseRaw = ctx.args.base;
18011
+ if (baseRaw && !argBase) {
18012
+ process.stderr.write(`[kody runFlow] ignoring --base "${baseRaw}" (must match kody-task or goal-branch pattern)
18013
+ `);
18014
+ }
18015
+ const base = argBase;
18016
+ if (base) {
18017
+ ctx.data.baseBranch = base;
18018
+ process.stderr.write(`[kody runFlow] resolved base branch: ${base} (from --base)
18019
+ `);
18020
+ }
18021
+ const branchInfo = ensureFeatureBranch(
18022
+ issueNumber,
18023
+ issue2.title,
18024
+ ctx.config.git.defaultBranch,
18025
+ ctx.cwd,
18026
+ base ?? void 0
18027
+ );
18028
+ ctx.data.branch = branchInfo.branch;
18029
+ const runUrl = getRunUrl();
18030
+ const startMsg = runUrl ? `\u2699\uFE0F kody started \u2014 branch \`${ctx.data.branch}\`, run ${runUrl}` : `\u2699\uFE0F kody started \u2014 branch \`${ctx.data.branch}\``;
18031
+ tryPost(issueNumber, startMsg, ctx.cwd);
18032
+ };
18033
+ }
18034
+ });
18035
+
18036
+ // src/scripts/syncFlow.ts
18037
+ import { execFileSync as execFileSync16 } from "child_process";
18038
+ function restoreDone(prNumber, cwd) {
18039
+ try {
18040
+ setKodyLabel(prNumber, DONE2, cwd);
18041
+ } catch {
18042
+ }
18043
+ }
18044
+ function bail2(ctx, prNumber, reason) {
18045
+ ctx.skipAgent = true;
18046
+ ctx.output.exitCode = 1;
18047
+ ctx.output.reason = reason;
18048
+ const runUrl = getRunUrl();
18049
+ const runSuffix = runUrl ? ` ([logs](${runUrl}))` : "";
18050
+ tryPostPr3(prNumber, `\u274C kody sync could not complete${runSuffix}: ${reason}`, ctx.cwd);
18051
+ }
18052
+ function revParseHead(cwd) {
18053
+ try {
18054
+ return execFileSync16("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
18055
+ } catch {
18056
+ return "";
18057
+ }
18058
+ }
18059
+ function pushBranch(branch, cwd) {
18060
+ const result = pushWithRetry({ cwd: cwd ?? process.cwd(), branch, setUpstream: true });
18061
+ if (!result.ok) {
18062
+ throw new Error(result.reason);
18063
+ }
18064
+ }
18065
+ function tryPostPr3(prNumber, body, cwd) {
18066
+ try {
18067
+ postPrReviewComment(prNumber, body, cwd);
18068
+ } catch {
18069
+ }
18070
+ }
18071
+ var DONE2, syncFlow;
18072
+ var init_syncFlow = __esm({
18073
+ "src/scripts/syncFlow.ts"() {
18074
+ "use strict";
18075
+ init_branch();
18076
+ init_gha();
18077
+ init_issue();
18078
+ init_lifecycleLabels();
18079
+ init_pushWithRetry();
18080
+ DONE2 = {
18081
+ label: "kody:done",
18082
+ color: "0e8a16",
18083
+ description: "kody: PR ready for human review/merge"
18084
+ };
18085
+ syncFlow = async (ctx, _profile, args) => {
18086
+ const announceOnSuccess = Boolean(args?.announceOnSuccess);
18087
+ const prNumber = ctx.args.pr;
18088
+ const pr = getPr(prNumber, ctx.cwd);
18089
+ if (pr.state !== "OPEN") {
18090
+ bail2(ctx, prNumber, `PR #${prNumber} is not OPEN (state: ${pr.state})`);
18091
+ return;
18092
+ }
18093
+ ctx.data.pr = pr;
18094
+ if (announceOnSuccess) {
18095
+ ctx.data.commentTargetType = "pr";
18096
+ ctx.data.commentTargetNumber = prNumber;
18097
+ }
18098
+ checkoutPrBranch(prNumber, ctx.cwd);
18099
+ ctx.data.branch = getCurrentBranch(ctx.cwd);
18100
+ const baseBranch = pr.baseRefName || ctx.config.git.defaultBranch;
18101
+ ctx.data.baseBranch = baseBranch;
18102
+ const headBefore = revParseHead(ctx.cwd);
18103
+ const mergeStatus = mergeBase(baseBranch, ctx.cwd);
18104
+ if (mergeStatus === "error") {
18105
+ bail2(ctx, prNumber, `failed to merge origin/${baseBranch} (non-conflict error); see runner log`);
18106
+ return;
18107
+ }
18108
+ if (mergeStatus === "conflict") {
18109
+ bail2(
18110
+ ctx,
18111
+ prNumber,
18112
+ `merge from origin/${baseBranch} produced conflicts \u2014 run \`@kody resolve\` to let kody resolve them`
18113
+ );
18114
+ return;
18115
+ }
18116
+ const headAfter = revParseHead(ctx.cwd);
18117
+ if (headAfter === headBefore) {
18118
+ ctx.data.syncResult = "noop";
18119
+ if (announceOnSuccess) {
18120
+ ctx.output.exitCode = 0;
18121
+ ctx.output.reason = `already up to date with origin/${baseBranch}`;
18122
+ restoreDone(prNumber, ctx.cwd);
18123
+ }
18124
+ return;
18125
+ }
18126
+ try {
18127
+ pushBranch(ctx.data.branch, ctx.cwd);
18128
+ } catch (err) {
18129
+ const msg = err instanceof Error ? err.message : String(err);
18130
+ bail2(ctx, prNumber, `merge succeeded but push failed: ${msg}`);
18131
+ return;
18132
+ }
18133
+ ctx.data.syncResult = "merged";
18134
+ if (announceOnSuccess) {
18135
+ ctx.output.exitCode = 0;
18136
+ ctx.output.reason = `merged origin/${baseBranch} into ${ctx.data.branch}`;
18137
+ restoreDone(prNumber, ctx.cwd);
18138
+ }
18139
+ };
18140
+ }
18141
+ });
18142
+
18143
+ // src/scripts/prepareCapabilityDelivery.ts
18144
+ var prepareCapabilityDelivery;
18145
+ var init_prepareCapabilityDelivery = __esm({
18146
+ "src/scripts/prepareCapabilityDelivery.ts"() {
17915
18147
  "use strict";
17916
- init_runtimeCleanup();
17917
- init_kodyVariables();
17918
- init_runtimeSecrets();
17919
- prepareBrowserAuth = async (ctx, profile) => {
17920
- const methods = profile.auth?.methods ?? [];
17921
- for (const method of methods) {
17922
- if (method.strategy !== "browser-storage-state" || method.adapter !== "kody-repository") continue;
17923
- if (await prepareMethod(ctx, profile, method)) return;
18148
+ init_capabilityDelivery();
18149
+ init_runFlow();
18150
+ init_syncFlow();
18151
+ prepareCapabilityDelivery = async (ctx, profile) => {
18152
+ const target = capabilityDeliveryTarget(ctx.data.capabilityInput);
18153
+ if (!target) {
18154
+ throw new Error("pull-request delivery requires exactly one positive issue or pr input");
18155
+ }
18156
+ ctx.args[target.kind] = target.number;
18157
+ if (target.kind === "issue") {
18158
+ await runFlow(ctx, profile);
18159
+ return;
17924
18160
  }
18161
+ ctx.data.commentTargetType = "pr";
18162
+ ctx.data.commentTargetNumber = target.number;
18163
+ await syncFlow(ctx, profile);
17925
18164
  };
17926
18165
  }
17927
18166
  });
@@ -18081,7 +18320,7 @@ var init_publishReport = __esm({
18081
18320
  });
18082
18321
 
18083
18322
  // src/scripts/recordClassification.ts
18084
- import { execFileSync as execFileSync16 } from "child_process";
18323
+ import { execFileSync as execFileSync17 } from "child_process";
18085
18324
  function parseClassification(prSummary) {
18086
18325
  if (!prSummary) return null;
18087
18326
  const classMatch = prSummary.match(/classification:\s*(feature|bug|spec|chore)\b/i);
@@ -18093,7 +18332,7 @@ function parseClassification(prSummary) {
18093
18332
  }
18094
18333
  function tryAuditComment(issueNumber, body, cwd) {
18095
18334
  try {
18096
- execFileSync16("gh", ["issue", "comment", String(issueNumber), "--body", body], {
18335
+ execFileSync17("gh", ["issue", "comment", String(issueNumber), "--body", body], {
18097
18336
  cwd,
18098
18337
  timeout: API_TIMEOUT_MS5,
18099
18338
  stdio: ["ignore", "pipe", "pipe"]
@@ -18313,7 +18552,7 @@ var init_resolveArtifacts = __esm({
18313
18552
  });
18314
18553
 
18315
18554
  // src/scripts/resolveFlow.ts
18316
- import { execFileSync as execFileSync17 } from "child_process";
18555
+ import { execFileSync as execFileSync18 } from "child_process";
18317
18556
  function buildPreferBlock(prefer, baseBranch) {
18318
18557
  if (prefer !== "ours" && prefer !== "theirs") return "";
18319
18558
  const keepSide = prefer === "ours" ? "HEAD (this PR branch)" : `origin/${baseBranch} (base branch)`;
@@ -18333,7 +18572,7 @@ function buildPreferBlock(prefer, baseBranch) {
18333
18572
  }
18334
18573
  function getConflictedFiles(cwd) {
18335
18574
  try {
18336
- const out = execFileSync17("git", ["diff", "--name-only", "--diff-filter=U"], {
18575
+ const out = execFileSync18("git", ["diff", "--name-only", "--diff-filter=U"], {
18337
18576
  encoding: "utf-8",
18338
18577
  cwd,
18339
18578
  env: { ...process.env, HUSKY: "0" }
@@ -18348,7 +18587,7 @@ function getConflictMarkersPreview(files, cwd, maxBytes = CONFLICT_DIFF_MAX_BYTE
18348
18587
  let total = 0;
18349
18588
  for (const f of files) {
18350
18589
  try {
18351
- const content = execFileSync17("cat", [f], { encoding: "utf-8", cwd }).toString();
18590
+ const content = execFileSync18("cat", [f], { encoding: "utf-8", cwd }).toString();
18352
18591
  const snippet = `### ${f}
18353
18592
 
18354
18593
  \`\`\`
@@ -18363,7 +18602,7 @@ ${content.slice(0, 6e3)}
18363
18602
  }
18364
18603
  return chunks.join("\n");
18365
18604
  }
18366
- function tryPostPr3(prNumber, body, cwd) {
18605
+ function tryPostPr4(prNumber, body, cwd) {
18367
18606
  try {
18368
18607
  postPrReviewComment(prNumber, body, cwd);
18369
18608
  } catch {
@@ -18372,12 +18611,12 @@ function tryPostPr3(prNumber, body, cwd) {
18372
18611
  function pushEmptyCommit(branch, cwd) {
18373
18612
  const env = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
18374
18613
  try {
18375
- execFileSync17(
18614
+ execFileSync18(
18376
18615
  "git",
18377
18616
  ["commit", "--allow-empty", "-m", "chore: kody resolve refresh \u2014 empty commit to recompute mergeable status"],
18378
18617
  { cwd, env, stdio: ["ignore", "pipe", "pipe"] }
18379
18618
  );
18380
- execFileSync17("git", ["push", "-u", "origin", branch], {
18619
+ execFileSync18("git", ["push", "-u", "origin", branch], {
18381
18620
  cwd,
18382
18621
  env,
18383
18622
  stdio: ["ignore", "pipe", "pipe"]
@@ -18415,14 +18654,14 @@ var init_resolveFlow = __esm({
18415
18654
  ctx.output.exitCode = 0;
18416
18655
  ctx.output.reason = `PR #${prNumber} is mergeable (no conflicts) \u2014 nothing to resolve`;
18417
18656
  ctx.skipAgent = true;
18418
- tryPostPr3(prNumber, `\u2139\uFE0F kody resolve: ${ctx.output.reason}`, ctx.cwd);
18657
+ tryPostPr4(prNumber, `\u2139\uFE0F kody resolve: ${ctx.output.reason}`, ctx.cwd);
18419
18658
  return;
18420
18659
  }
18421
18660
  if (ghStatus.status === "BLOCKED") {
18422
18661
  ctx.output.exitCode = 0;
18423
18662
  ctx.output.reason = `PR #${prNumber} is mergeable but blocked by checks/reviews (mergeStateStatus=${ghStatus.mergeStateStatus}) \u2014 nothing for resolve to do`;
18424
18663
  ctx.skipAgent = true;
18425
- tryPostPr3(prNumber, `\u2139\uFE0F kody resolve: ${ctx.output.reason}`, ctx.cwd);
18664
+ tryPostPr4(prNumber, `\u2139\uFE0F kody resolve: ${ctx.output.reason}`, ctx.cwd);
18426
18665
  return;
18427
18666
  }
18428
18667
  checkoutPrBranch(prNumber, ctx.cwd);
@@ -18434,20 +18673,20 @@ var init_resolveFlow = __esm({
18434
18673
  ctx.output.exitCode = 0;
18435
18674
  ctx.output.reason = pushed ? `local merge clean despite GitHub reporting CONFLICTING \u2014 pushed empty commit to force re-evaluation` : `local merge clean despite GitHub reporting CONFLICTING \u2014 couldn't refresh GitHub cache (push failed)`;
18436
18675
  ctx.skipAgent = true;
18437
- tryPostPr3(prNumber, `\u2139\uFE0F kody resolve: ${ctx.output.reason}`, ctx.cwd);
18676
+ tryPostPr4(prNumber, `\u2139\uFE0F kody resolve: ${ctx.output.reason}`, ctx.cwd);
18438
18677
  return;
18439
18678
  }
18440
18679
  ctx.output.exitCode = 0;
18441
18680
  ctx.output.reason = `already up to date with origin/${baseBranch} \u2014 nothing to resolve`;
18442
18681
  ctx.skipAgent = true;
18443
- tryPostPr3(prNumber, `\u2139\uFE0F kody resolve: ${ctx.output.reason}`, ctx.cwd);
18682
+ tryPostPr4(prNumber, `\u2139\uFE0F kody resolve: ${ctx.output.reason}`, ctx.cwd);
18444
18683
  return;
18445
18684
  }
18446
18685
  if (mergeStatus === "error") {
18447
18686
  ctx.output.exitCode = 99;
18448
18687
  ctx.output.reason = `failed to merge origin/${baseBranch} (non-conflict error); see runner log`;
18449
18688
  ctx.skipAgent = true;
18450
- tryPostPr3(prNumber, `\u26A0\uFE0F kody resolve FAILED: ${ctx.output.reason}`, ctx.cwd);
18689
+ tryPostPr4(prNumber, `\u26A0\uFE0F kody resolve FAILED: ${ctx.output.reason}`, ctx.cwd);
18451
18690
  return;
18452
18691
  }
18453
18692
  const conflictedFiles = getConflictedFiles(ctx.cwd);
@@ -18462,7 +18701,7 @@ var init_resolveFlow = __esm({
18462
18701
  ctx.data.preferBlock = buildPreferBlock(ctx.args.prefer, baseBranch);
18463
18702
  const runUrl = getRunUrl();
18464
18703
  const runSuffix = runUrl ? `, run ${runUrl}` : "";
18465
- tryPostPr3(
18704
+ tryPostPr4(
18466
18705
  prNumber,
18467
18706
  `\u2699\uFE0F kody resolve started on \`${ctx.data.branch}\`${runSuffix} \u2014 ${conflictedFiles.length} conflicted file(s)`,
18468
18707
  ctx.cwd
@@ -18563,10 +18802,10 @@ var init_resolvePreviewUrl = __esm({
18563
18802
  });
18564
18803
 
18565
18804
  // src/scripts/resolveQaUrl.ts
18566
- import { execFileSync as execFileSync18 } from "child_process";
18805
+ import { execFileSync as execFileSync19 } from "child_process";
18567
18806
  function ghQuery(args, cwd) {
18568
18807
  try {
18569
- const out = execFileSync18("gh", args, {
18808
+ const out = execFileSync19("gh", args, {
18570
18809
  cwd,
18571
18810
  stdio: ["ignore", "pipe", "pipe"],
18572
18811
  encoding: "utf-8",
@@ -18643,7 +18882,7 @@ var init_resolveQaUrl = __esm({
18643
18882
  });
18644
18883
 
18645
18884
  // src/scripts/revertFlow.ts
18646
- import { execFileSync as execFileSync19 } from "child_process";
18885
+ import { execFileSync as execFileSync20 } from "child_process";
18647
18886
  function buildCommitMessage(resolved) {
18648
18887
  if (resolved.length === 1) {
18649
18888
  const { full, subject } = resolved[0];
@@ -18656,7 +18895,7 @@ function buildPrSummary(resolved) {
18656
18895
  return resolved.map((r) => `- Reverted \`${r.full.slice(0, 7)}\`${r.subject ? ` \u2014 ${r.subject}` : ""}`).join("\n");
18657
18896
  }
18658
18897
  function git4(args, cwd) {
18659
- return execFileSync19("git", args, {
18898
+ return execFileSync20("git", args, {
18660
18899
  encoding: "utf-8",
18661
18900
  timeout: 3e4,
18662
18901
  cwd,
@@ -18666,7 +18905,7 @@ function git4(args, cwd) {
18666
18905
  }
18667
18906
  function isAncestorOfHead(sha, cwd) {
18668
18907
  try {
18669
- execFileSync19("git", ["merge-base", "--is-ancestor", sha, "HEAD"], {
18908
+ execFileSync20("git", ["merge-base", "--is-ancestor", sha, "HEAD"], {
18670
18909
  cwd,
18671
18910
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
18672
18911
  stdio: ["ignore", "ignore", "ignore"]
@@ -18676,7 +18915,7 @@ function isAncestorOfHead(sha, cwd) {
18676
18915
  return false;
18677
18916
  }
18678
18917
  }
18679
- function tryPostPr4(prNumber, body, cwd) {
18918
+ function tryPostPr5(prNumber, body, cwd) {
18680
18919
  try {
18681
18920
  postPrReviewComment(prNumber, body, cwd);
18682
18921
  } catch (err) {
@@ -18712,7 +18951,7 @@ var init_revertFlow = __esm({
18712
18951
  ctx.output.exitCode = 64;
18713
18952
  ctx.output.reason = "no commit SHAs provided \u2014 usage: @kody revert <sha> [<sha> \u2026]";
18714
18953
  ctx.skipAgent = true;
18715
- tryPostPr4(prNumber, `\u26A0\uFE0F kody revert FAILED: ${ctx.output.reason}`, ctx.cwd);
18954
+ tryPostPr5(prNumber, `\u26A0\uFE0F kody revert FAILED: ${ctx.output.reason}`, ctx.cwd);
18716
18955
  return;
18717
18956
  }
18718
18957
  const requested = shasArg.split(/\s+/).filter((s) => s.length > 0);
@@ -18721,7 +18960,7 @@ var init_revertFlow = __esm({
18721
18960
  ctx.output.exitCode = 64;
18722
18961
  ctx.output.reason = `not valid SHA-shaped tokens: ${bad.join(", ")}`;
18723
18962
  ctx.skipAgent = true;
18724
- tryPostPr4(prNumber, `\u26A0\uFE0F kody revert FAILED: ${ctx.output.reason}`, ctx.cwd);
18963
+ tryPostPr5(prNumber, `\u26A0\uFE0F kody revert FAILED: ${ctx.output.reason}`, ctx.cwd);
18725
18964
  return;
18726
18965
  }
18727
18966
  const resolved = [];
@@ -18749,7 +18988,7 @@ var init_revertFlow = __esm({
18749
18988
  ctx.output.exitCode = 64;
18750
18989
  ctx.output.reason = `commit(s) not found in this PR branch: ${unreachable.join(", ")}`;
18751
18990
  ctx.skipAgent = true;
18752
- tryPostPr4(prNumber, `\u26A0\uFE0F kody revert FAILED: ${ctx.output.reason}`, ctx.cwd);
18991
+ tryPostPr5(prNumber, `\u26A0\uFE0F kody revert FAILED: ${ctx.output.reason}`, ctx.cwd);
18753
18992
  return;
18754
18993
  }
18755
18994
  ctx.args.shas = resolved.map((r) => r.full).join(" ");
@@ -18759,13 +18998,13 @@ var init_revertFlow = __esm({
18759
18998
  const runUrl = getRunUrl();
18760
18999
  const runSuffix = runUrl ? `, run ${runUrl}` : "";
18761
19000
  const shaList = resolved.map((r) => `\`${r.full.slice(0, 7)}\``).join(", ");
18762
- tryPostPr4(prNumber, `\u2699\uFE0F kody revert started on \`${ctx.data.branch}\`${runSuffix} \u2014 reverting ${shaList}`, ctx.cwd);
19001
+ tryPostPr5(prNumber, `\u2699\uFE0F kody revert started on \`${ctx.data.branch}\`${runSuffix} \u2014 reverting ${shaList}`, ctx.cwd);
18763
19002
  };
18764
19003
  }
18765
19004
  });
18766
19005
 
18767
19006
  // src/scripts/reviewFlow.ts
18768
- function tryPostPr5(prNumber, body, cwd) {
19007
+ function tryPostPr6(prNumber, body, cwd) {
18769
19008
  try {
18770
19009
  postPrReviewComment(prNumber, body, cwd);
18771
19010
  } catch {
@@ -18795,75 +19034,7 @@ var init_reviewFlow = __esm({
18795
19034
  ctx.data.prDiff = getPrDiff(prNumber, ctx.cwd);
18796
19035
  const runUrl = getRunUrl();
18797
19036
  const runSuffix = runUrl ? `, run ${runUrl}` : "";
18798
- tryPostPr5(prNumber, `\u{1F440} kody review started on PR #${prNumber}${runSuffix}`, ctx.cwd);
18799
- };
18800
- }
18801
- });
18802
-
18803
- // src/scripts/runFlow.ts
18804
- function tryPost(issueNumber, body, cwd) {
18805
- try {
18806
- postIssueComment(issueNumber, body, cwd);
18807
- } catch {
18808
- }
18809
- }
18810
- function resolveBaseOverride(value) {
18811
- if (!value) return null;
18812
- if (value.length > 200) return null;
18813
- if (value.includes("..")) return null;
18814
- if (!/^[a-z0-9][a-z0-9/._-]*$/.test(value)) return null;
18815
- return value;
18816
- }
18817
- var runFlow;
18818
- var init_runFlow = __esm({
18819
- "src/scripts/runFlow.ts"() {
18820
- "use strict";
18821
- init_branch();
18822
- init_gha();
18823
- init_issue();
18824
- runFlow = async (ctx) => {
18825
- const issueNumber = ctx.args.issue;
18826
- const issue2 = getIssue(issueNumber, ctx.cwd);
18827
- const cfgCtx = ctx.config.issueContext ?? {};
18828
- const commentsFormatted = formatIssueComments(
18829
- issue2.comments,
18830
- cfgCtx.commentLimit ?? DEFAULT_COMMENT_LIMIT,
18831
- cfgCtx.commentMaxBytes ?? DEFAULT_COMMENT_MAX_BYTES
18832
- );
18833
- ctx.data.issue = { ...issue2, commentsFormatted };
18834
- if (issue2.isPullRequest) {
18835
- ctx.data.commentTargetType = "pr";
18836
- ctx.data.commentTargetNumber = issueNumber;
18837
- ctx.skipAgent = true;
18838
- ctx.output.exitCode = 1;
18839
- ctx.output.reason = `run target #${issueNumber} is a pull request; dispatch a PR action or the source issue instead`;
18840
- return;
18841
- }
18842
- ctx.data.commentTargetType = "issue";
18843
- ctx.data.commentTargetNumber = issueNumber;
18844
- const argBase = resolveBaseOverride(ctx.args.base);
18845
- const baseRaw = ctx.args.base;
18846
- if (baseRaw && !argBase) {
18847
- process.stderr.write(`[kody runFlow] ignoring --base "${baseRaw}" (must match kody-task or goal-branch pattern)
18848
- `);
18849
- }
18850
- const base = argBase;
18851
- if (base) {
18852
- ctx.data.baseBranch = base;
18853
- process.stderr.write(`[kody runFlow] resolved base branch: ${base} (from --base)
18854
- `);
18855
- }
18856
- const branchInfo = ensureFeatureBranch(
18857
- issueNumber,
18858
- issue2.title,
18859
- ctx.config.git.defaultBranch,
18860
- ctx.cwd,
18861
- base ?? void 0
18862
- );
18863
- ctx.data.branch = branchInfo.branch;
18864
- const runUrl = getRunUrl();
18865
- const startMsg = runUrl ? `\u2699\uFE0F kody started \u2014 branch \`${ctx.data.branch}\`, run ${runUrl}` : `\u2699\uFE0F kody started \u2014 branch \`${ctx.data.branch}\``;
18866
- tryPost(issueNumber, startMsg, ctx.cwd);
19037
+ tryPostPr6(prNumber, `\u{1F440} kody review started on PR #${prNumber}${runSuffix}`, ctx.cwd);
18867
19038
  };
18868
19039
  }
18869
19040
  });
@@ -19633,7 +19804,7 @@ var init_skipAgent = __esm({
19633
19804
  });
19634
19805
 
19635
19806
  // src/scripts/stageMergeConflicts.ts
19636
- import { execFileSync as execFileSync20 } from "child_process";
19807
+ import { execFileSync as execFileSync21 } from "child_process";
19637
19808
  var stageMergeConflicts;
19638
19809
  var init_stageMergeConflicts = __esm({
19639
19810
  "src/scripts/stageMergeConflicts.ts"() {
@@ -19641,7 +19812,7 @@ var init_stageMergeConflicts = __esm({
19641
19812
  stageMergeConflicts = async (ctx) => {
19642
19813
  if (ctx.data.agentDone === false) return;
19643
19814
  try {
19644
- execFileSync20("git", ["add", "-A"], {
19815
+ execFileSync21("git", ["add", "-A"], {
19645
19816
  cwd: ctx.cwd,
19646
19817
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
19647
19818
  stdio: "pipe"
@@ -19694,113 +19865,6 @@ var init_startFlow = __esm({
19694
19865
  }
19695
19866
  });
19696
19867
 
19697
- // src/scripts/syncFlow.ts
19698
- import { execFileSync as execFileSync21 } from "child_process";
19699
- function restoreDone(prNumber, cwd) {
19700
- try {
19701
- setKodyLabel(prNumber, DONE2, cwd);
19702
- } catch {
19703
- }
19704
- }
19705
- function bail2(ctx, prNumber, reason) {
19706
- ctx.skipAgent = true;
19707
- ctx.output.exitCode = 1;
19708
- ctx.output.reason = reason;
19709
- const runUrl = getRunUrl();
19710
- const runSuffix = runUrl ? ` ([logs](${runUrl}))` : "";
19711
- tryPostPr6(prNumber, `\u274C kody sync could not complete${runSuffix}: ${reason}`, ctx.cwd);
19712
- }
19713
- function revParseHead(cwd) {
19714
- try {
19715
- return execFileSync21("git", ["rev-parse", "HEAD"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).toString().trim();
19716
- } catch {
19717
- return "";
19718
- }
19719
- }
19720
- function pushBranch(branch, cwd) {
19721
- const result = pushWithRetry({ cwd: cwd ?? process.cwd(), branch, setUpstream: true });
19722
- if (!result.ok) {
19723
- throw new Error(result.reason);
19724
- }
19725
- }
19726
- function tryPostPr6(prNumber, body, cwd) {
19727
- try {
19728
- postPrReviewComment(prNumber, body, cwd);
19729
- } catch {
19730
- }
19731
- }
19732
- var DONE2, syncFlow;
19733
- var init_syncFlow = __esm({
19734
- "src/scripts/syncFlow.ts"() {
19735
- "use strict";
19736
- init_branch();
19737
- init_gha();
19738
- init_issue();
19739
- init_lifecycleLabels();
19740
- init_pushWithRetry();
19741
- DONE2 = {
19742
- label: "kody:done",
19743
- color: "0e8a16",
19744
- description: "kody: PR ready for human review/merge"
19745
- };
19746
- syncFlow = async (ctx, _profile, args) => {
19747
- const announceOnSuccess = Boolean(args?.announceOnSuccess);
19748
- const prNumber = ctx.args.pr;
19749
- const pr = getPr(prNumber, ctx.cwd);
19750
- if (pr.state !== "OPEN") {
19751
- bail2(ctx, prNumber, `PR #${prNumber} is not OPEN (state: ${pr.state})`);
19752
- return;
19753
- }
19754
- ctx.data.pr = pr;
19755
- if (announceOnSuccess) {
19756
- ctx.data.commentTargetType = "pr";
19757
- ctx.data.commentTargetNumber = prNumber;
19758
- }
19759
- checkoutPrBranch(prNumber, ctx.cwd);
19760
- ctx.data.branch = getCurrentBranch(ctx.cwd);
19761
- const baseBranch = pr.baseRefName || ctx.config.git.defaultBranch;
19762
- ctx.data.baseBranch = baseBranch;
19763
- const headBefore = revParseHead(ctx.cwd);
19764
- const mergeStatus = mergeBase(baseBranch, ctx.cwd);
19765
- if (mergeStatus === "error") {
19766
- bail2(ctx, prNumber, `failed to merge origin/${baseBranch} (non-conflict error); see runner log`);
19767
- return;
19768
- }
19769
- if (mergeStatus === "conflict") {
19770
- bail2(
19771
- ctx,
19772
- prNumber,
19773
- `merge from origin/${baseBranch} produced conflicts \u2014 run \`@kody resolve\` to let kody resolve them`
19774
- );
19775
- return;
19776
- }
19777
- const headAfter = revParseHead(ctx.cwd);
19778
- if (headAfter === headBefore) {
19779
- ctx.data.syncResult = "noop";
19780
- if (announceOnSuccess) {
19781
- ctx.output.exitCode = 0;
19782
- ctx.output.reason = `already up to date with origin/${baseBranch}`;
19783
- restoreDone(prNumber, ctx.cwd);
19784
- }
19785
- return;
19786
- }
19787
- try {
19788
- pushBranch(ctx.data.branch, ctx.cwd);
19789
- } catch (err) {
19790
- const msg = err instanceof Error ? err.message : String(err);
19791
- bail2(ctx, prNumber, `merge succeeded but push failed: ${msg}`);
19792
- return;
19793
- }
19794
- ctx.data.syncResult = "merged";
19795
- if (announceOnSuccess) {
19796
- ctx.output.exitCode = 0;
19797
- ctx.output.reason = `merged origin/${baseBranch} into ${ctx.data.branch}`;
19798
- restoreDone(prNumber, ctx.cwd);
19799
- }
19800
- };
19801
- }
19802
- });
19803
-
19804
19868
  // src/scripts/validateAgencyModelProposal.ts
19805
19869
  import * as path46 from "path";
19806
19870
  function validateModelBundle(bundle, expectedKind, options = {}) {
@@ -20830,12 +20894,12 @@ var init_scripts = __esm({
20830
20894
  init_diagMcp();
20831
20895
  init_discoverQaContext();
20832
20896
  init_dispatch();
20897
+ init_dispatchAgencyLoops();
20833
20898
  init_dispatchCapabilityFileTicks();
20834
20899
  init_dispatchCapabilityTicks();
20835
20900
  init_dispatchClassified();
20836
- init_dispatchAgencyLoops();
20837
- init_dispatchSimpleLoops();
20838
20901
  init_dispatchNextTaskJob();
20902
+ init_dispatchSimpleLoops();
20839
20903
  init_ensurePr();
20840
20904
  init_evaluateAgencyBoundaries();
20841
20905
  init_failOnceTaskJob();
@@ -20846,7 +20910,6 @@ var init_scripts = __esm({
20846
20910
  init_initFlow();
20847
20911
  init_loadAgentAdhoc();
20848
20912
  init_loadCapabilityState();
20849
- init_loadSimpleCapability();
20850
20913
  init_loadCompanyIntents();
20851
20914
  init_loadCompanyPortfolio();
20852
20915
  init_loadConventions();
@@ -20859,6 +20922,7 @@ var init_scripts = __esm({
20859
20922
  init_loadMemoryContext();
20860
20923
  init_loadPriorArt();
20861
20924
  init_loadQaContext();
20925
+ init_loadSimpleCapability();
20862
20926
  init_loadTaskContext();
20863
20927
  init_loadTaskState();
20864
20928
  init_markFlowSuccess();
@@ -20871,8 +20935,8 @@ var init_scripts = __esm({
20871
20935
  init_parseAgentResult();
20872
20936
  init_parseIssueStateFromAgentResult();
20873
20937
  init_parseJobStateFromAgentResult();
20874
- init_parseSimpleCapabilityOutput();
20875
20938
  init_parseReproOutput();
20939
+ init_parseSimpleCapabilityOutput();
20876
20940
  init_persistArtifacts();
20877
20941
  init_persistFlowState();
20878
20942
  init_planTaskJobs();
@@ -20882,6 +20946,7 @@ var init_scripts = __esm({
20882
20946
  init_postResearchComment();
20883
20947
  init_postReviewResult();
20884
20948
  init_prepareBrowserAuth();
20949
+ init_prepareCapabilityDelivery();
20885
20950
  init_promoteQaGoal();
20886
20951
  init_publishReport();
20887
20952
  init_recordClassification();
@@ -20943,6 +21008,7 @@ var init_scripts = __esm({
20943
21008
  loadPriorArt,
20944
21009
  loadQaContext,
20945
21010
  prepareBrowserAuth,
21011
+ prepareCapabilityDelivery,
20946
21012
  buildSyntheticPlugin,
20947
21013
  resolveArtifacts,
20948
21014
  discoverQaContext,
@@ -22303,6 +22369,9 @@ function validateJob(input) {
22303
22369
  if (j.cliArgs !== void 0 && (typeof j.cliArgs !== "object" || j.cliArgs === null)) {
22304
22370
  throw new InvalidJobError("job.cliArgs must be an object when present");
22305
22371
  }
22372
+ if (j.delivery !== void 0 && j.delivery !== "pull-request") {
22373
+ throw new InvalidJobError(`job.delivery must be "pull-request" (got ${String(j.delivery)})`);
22374
+ }
22306
22375
  return {
22307
22376
  action: typeof j.action === "string" ? j.action : void 0,
22308
22377
  implementation: typeof j.implementation === "string" ? j.implementation : void 0,
@@ -22312,6 +22381,7 @@ function validateJob(input) {
22312
22381
  agent: typeof j.agent === "string" ? j.agent : void 0,
22313
22382
  schedule: typeof j.schedule === "string" ? j.schedule : void 0,
22314
22383
  target: typeof j.target === "number" ? j.target : void 0,
22384
+ delivery: j.delivery === "pull-request" ? j.delivery : void 0,
22315
22385
  cliArgs: j.cliArgs ?? {},
22316
22386
  workflowFacts: j.workflowFacts && typeof j.workflowFacts === "object" && !Array.isArray(j.workflowFacts) ? j.workflowFacts : void 0,
22317
22387
  workflowState: parseWorkflowRunState(j.workflowState) ?? void 0,
@@ -22364,7 +22434,7 @@ async function runJob(job, base) {
22364
22434
  }
22365
22435
  const workflow = capabilityContext?.config.workflow ?? workflowContext?.config.workflow;
22366
22436
  const workflowIdentity = valid.workflow ?? capabilityIdentity ?? workflowContext?.slug;
22367
- const capabilitySelectedImplementation = resolvedCapability?.implementation ?? capabilityContext?.config.implementation ?? capabilityContext?.config.implementations?.[0] ?? (capabilityContext?.config.role ? capabilityContext.slug : void 0) ?? (capabilityContext?.config.tickScript ? "capability-tick-scripted" : void 0);
22437
+ const capabilitySelectedImplementation = valid.delivery === "pull-request" && resolvedCapability?.implementation === "capability-run" ? "capability-delivery" : resolvedCapability?.implementation ?? capabilityContext?.config.implementation ?? capabilityContext?.config.implementations?.[0] ?? (capabilityContext?.config.role ? capabilityContext.slug : void 0) ?? (capabilityContext?.config.tickScript ? "capability-tick-scripted" : void 0);
22368
22438
  const profileName = explicitImplementation ?? capabilitySelectedImplementation;
22369
22439
  if (workflow && shouldRunCapabilityWorkflow(valid, workflow, workflowIdentity, capabilitySelectedImplementation, base)) {
22370
22440
  const workflowCapability = capabilityContext ?? workflowContext;
@@ -22454,6 +22524,7 @@ async function runCapabilityImplementationStep(valid, profileName, capabilityIde
22454
22524
  preloadedData.jobKey = stableJobKey(valid);
22455
22525
  preloadedData.jobFlavor = valid.flavor;
22456
22526
  if (valid.target !== void 0) preloadedData.jobTarget = valid.target;
22527
+ if (valid.delivery !== void 0) preloadedData.jobDelivery = valid.delivery;
22457
22528
  if (valid.action !== void 0 && valid.action.length > 0) preloadedData.jobAction = valid.action;
22458
22529
  if (capabilityIdentity !== void 0 && capabilityIdentity.length > 0)
22459
22530
  preloadedData.jobCapability = capabilityIdentity;
@@ -22493,11 +22564,13 @@ async function runCapabilityImplementationStep(valid, profileName, capabilityIde
22493
22564
  };
22494
22565
  const shouldApplyResolvedCapabilityArgs = valid.implementation === void 0 && resolvedCapability && profileName === resolvedCapability.implementation;
22495
22566
  input.cliArgs = shouldApplyResolvedCapabilityArgs ? { ...resolvedCapability.cliArgs, ...input.cliArgs } : input.cliArgs;
22496
- if (profileName === "capability-run" && capabilityIdentity) {
22567
+ if ((profileName === "capability-run" || profileName === "capability-delivery") && capabilityIdentity) {
22497
22568
  const capabilityInput = Object.keys(valid.cliArgs).length > 0 ? genericInputFromArgs(valid.cliArgs) : void 0;
22569
+ const deliveryTarget = profileName === "capability-delivery" && capabilityInput && typeof capabilityInput === "object" ? capabilityDeliveryArgs(capabilityInput) : {};
22498
22570
  input.cliArgs = {
22499
22571
  capability: capabilityIdentity,
22500
- ...capabilityInput !== void 0 ? { input: JSON.stringify(capabilityInput) } : {}
22572
+ ...capabilityInput !== void 0 ? { input: JSON.stringify(capabilityInput) } : {},
22573
+ ...deliveryTarget
22501
22574
  };
22502
22575
  }
22503
22576
  const run = base.chain === false ? runImplementation : runImplementationChain;
@@ -22854,6 +22927,7 @@ function workflowStepToJob(step, parent, chainData, cwd) {
22854
22927
  ...parent.agent ? { agent: parent.agent } : {},
22855
22928
  ...parent.schedule ? { schedule: parent.schedule } : {},
22856
22929
  ...typeof target === "number" ? { target } : {},
22930
+ ...step.delivery ? { delivery: step.delivery } : {},
22857
22931
  cliArgs,
22858
22932
  ...step.evidence ? { evidence: step.evidence } : parent.evidence ? { evidence: parent.evidence } : {},
22859
22933
  flavor: parent.flavor,
@@ -22863,6 +22937,10 @@ function workflowStepToJob(step, parent, chainData, cwd) {
22863
22937
  ...parent.resultTarget ? { resultTarget: parent.resultTarget } : {}
22864
22938
  };
22865
22939
  }
22940
+ function capabilityDeliveryArgs(input) {
22941
+ const target = capabilityDeliveryTarget(input);
22942
+ return target ? { [target.kind]: target.number } : {};
22943
+ }
22866
22944
  function usesGenericCapabilityInput(action, cwd) {
22867
22945
  const inputs = getCapabilityActionInputs(action, hydratedCapabilitiesRoot(cwd));
22868
22946
  return Boolean(inputs?.length === 1 && inputs[0]?.name === "input" && inputs[0]?.flag === "--input");
@@ -23027,6 +23105,7 @@ var init_job = __esm({
23027
23105
  "src/job.ts"() {
23028
23106
  "use strict";
23029
23107
  init_agencyBoundaryEval();
23108
+ init_capabilityDelivery();
23030
23109
  init_capabilityFolders();
23031
23110
  init_definition_paths();
23032
23111
  init_executor();