@kody-ade/kody-engine 0.4.442 → 0.4.443

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.442",
18
+ version: "0.4.443",
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",
@@ -1681,62 +1681,38 @@ function listCapabilityFolderSlugs(absDir) {
1681
1681
  return entries.filter((e) => e.isDirectory() && !e.name.startsWith("_") && !e.name.startsWith(".")).filter((e) => isCapabilityFolder(path5.join(absDir, e.name))).map((e) => e.name).sort();
1682
1682
  }
1683
1683
  function isCapabilityFolder(dir) {
1684
- if (!fs4.existsSync(path5.join(dir, CAPABILITY_DEFINITION_FILE))) return false;
1685
1684
  if (!fs4.existsSync(path5.join(dir, CAPABILITY_BODY_FILE))) return false;
1686
1685
  const entries = fs4.readdirSync(dir, { withFileTypes: true });
1687
1686
  return entries.every(
1688
- (entry) => entry.name === CAPABILITY_DEFINITION_FILE || entry.name === CAPABILITY_BODY_FILE || entry.isDirectory() && (entry.name === "skills" || entry.name === "tools")
1687
+ (entry) => entry.name === CAPABILITY_BODY_FILE || entry.isDirectory() && (entry.name === "skills" || entry.name === "tools")
1689
1688
  );
1690
1689
  }
1691
1690
  function readCapabilityFolder(root, slug) {
1692
1691
  const dir = path5.join(root, slug);
1693
- const definitionPath = path5.join(dir, CAPABILITY_DEFINITION_FILE);
1694
- const profilePath = definitionPath;
1695
1692
  const bodyPath = path5.join(dir, CAPABILITY_BODY_FILE);
1696
- if (!fs4.existsSync(profilePath) || !fs4.statSync(profilePath).isFile()) return null;
1697
1693
  if (!fs4.existsSync(bodyPath) || !fs4.statSync(bodyPath).isFile()) return null;
1694
+ if (!isCapabilityFolder(dir)) return null;
1698
1695
  try {
1699
- const rawDefinition = JSON.parse(fs4.readFileSync(profilePath, "utf-8"));
1700
- const contract = parseCapabilityContract(rawDefinition);
1701
- if (!contract) return null;
1702
- const rawProfile = {
1703
- inputSchema: contract.input.schema,
1704
- outputSchema: contract.output.schema,
1705
- contract
1706
- };
1707
1696
  const rawBody = fs4.readFileSync(bodyPath, "utf-8");
1708
1697
  const { title, body } = parseCapabilityBody(rawBody, slug);
1709
1698
  return {
1710
1699
  slug,
1711
1700
  dir,
1712
- profilePath,
1701
+ profilePath: bodyPath,
1713
1702
  bodyPath,
1714
1703
  title,
1715
1704
  body,
1716
1705
  rawBody,
1717
1706
  config: {
1718
1707
  action: slug,
1719
- describe: title,
1720
- outputSchema: contract.output.schema
1708
+ describe: title
1721
1709
  },
1722
- rawProfile
1710
+ rawProfile: {}
1723
1711
  };
1724
1712
  } catch {
1725
1713
  return null;
1726
1714
  }
1727
1715
  }
1728
- function parseCapabilityContract(raw) {
1729
- if (Object.keys(raw).length !== 2 || !isPlainObject(raw.input) || !isPlainObject(raw.output)) return null;
1730
- const parseValue = (value) => {
1731
- if (Object.keys(value).some((key) => key !== "name" && key !== "schema") || typeof value.name !== "string" || !/^[a-z][a-z0-9-]*$/.test(value.name) || !isPlainObject(value.schema)) {
1732
- return null;
1733
- }
1734
- return { name: value.name, schema: value.schema };
1735
- };
1736
- const input = parseValue(raw.input);
1737
- const output = parseValue(raw.output);
1738
- return input && output ? { input, output } : null;
1739
- }
1740
1716
  function parseCapabilityBody(raw, slug) {
1741
1717
  const trimmed = raw.trim();
1742
1718
  const firstLine2 = trimmed.split("\n", 1)[0] ?? "";
@@ -1789,28 +1765,24 @@ function parseWorkflowStep(value) {
1789
1765
  const raw = value;
1790
1766
  const capability = stringField(raw.capability ?? raw.action);
1791
1767
  if (!capability || !isSafeSlug(capability)) return null;
1792
- const implementation = stringField(raw.implementation);
1793
1768
  const id = stringField(raw.id);
1794
1769
  const action = stringField(raw.action);
1795
1770
  const evidence = stringField(raw.evidence);
1796
1771
  const reason = stringField(raw.reason);
1797
1772
  const target = stringField(raw.target);
1798
1773
  const targetFact = stringField(raw.targetFact ?? raw.target_fact);
1799
- const cliArgs = raw.cliArgs;
1800
- const inputs = parseWorkflowInputs(raw.inputs);
1774
+ const hasInput = Object.hasOwn(raw, "input");
1801
1775
  const next = parseWorkflowTransitions(raw.next);
1802
1776
  const report = parseReportPublication(raw.report);
1803
1777
  return {
1804
1778
  capability,
1779
+ ...hasInput ? { input: raw.input } : {},
1805
1780
  ...id && isSafeStepId(id) ? { id } : {},
1806
1781
  ...action && isSafeSlug(action) ? { action } : {},
1807
- ...implementation && isSafeSlug(implementation) ? { implementation } : {},
1808
1782
  ...evidence ? { evidence } : {},
1809
1783
  ...target === "issue" || target === "pr" ? { target } : {},
1810
1784
  ...targetFact ? { targetFact } : {},
1811
1785
  ...reason ? { reason } : {},
1812
- ...cliArgs && typeof cliArgs === "object" && !Array.isArray(cliArgs) ? { cliArgs } : {},
1813
- ...inputs ? { inputs } : {},
1814
1786
  ...next ? { next } : {},
1815
1787
  ...isPlainObject(raw.runWhen) ? { runWhen: raw.runWhen } : {},
1816
1788
  ...stringList(raw.continueOn ?? raw.continue_on).length > 0 ? { continueOn: stringList(raw.continueOn ?? raw.continue_on) } : {},
@@ -1818,17 +1790,6 @@ function parseWorkflowStep(value) {
1818
1790
  ...report ? { report } : {}
1819
1791
  };
1820
1792
  }
1821
- function parseWorkflowInputs(value) {
1822
- if (!isPlainObject(value)) return void 0;
1823
- const inputs = {};
1824
- for (const [name, raw] of Object.entries(value)) {
1825
- if (!isSafeSlug(name) || !isPlainObject(raw)) continue;
1826
- const from = stringField(raw.from);
1827
- if (!from) continue;
1828
- inputs[name] = { from };
1829
- }
1830
- return Object.keys(inputs).length > 0 ? inputs : void 0;
1831
- }
1832
1793
  function parseWorkflowTransitions(value) {
1833
1794
  const rawTransitions = Array.isArray(value) ? value : value === void 0 ? [] : [value];
1834
1795
  const transitions = rawTransitions.map((raw) => {
@@ -1885,13 +1846,12 @@ function isSafeSlug(value) {
1885
1846
  function isSafeStepId(value) {
1886
1847
  return /^[A-Za-z][A-Za-z0-9_-]*$/.test(value) && !value.includes("..");
1887
1848
  }
1888
- var CAPABILITY_PROFILE_FILE, CAPABILITY_DEFINITION_FILE, CAPABILITY_BODY_FILE;
1849
+ var CAPABILITY_BODY_FILE, CAPABILITY_PROFILE_FILE;
1889
1850
  var init_capabilityFolders = __esm({
1890
1851
  "src/capabilityFolders.ts"() {
1891
1852
  "use strict";
1892
- CAPABILITY_PROFILE_FILE = "contract.json";
1893
- CAPABILITY_DEFINITION_FILE = "contract.json";
1894
1853
  CAPABILITY_BODY_FILE = "instructions.md";
1854
+ CAPABILITY_PROFILE_FILE = CAPABILITY_BODY_FILE;
1895
1855
  }
1896
1856
  });
1897
1857
 
@@ -2060,36 +2020,27 @@ function getCapabilityActionInputs(action, projectCapabilitiesRoot = getProjectC
2060
2020
  const resolved = resolveCapabilityAction(action, projectCapabilitiesRoot);
2061
2021
  if (!resolved) return null;
2062
2022
  const capability = resolveCapabilityFolder(resolved.capability, projectCapabilitiesRoot);
2063
- if (capability && path7.basename(capability.profilePath) === "contract.json") {
2064
- const schema = capability.rawProfile.inputSchema;
2065
- if (!schema || typeof schema !== "object" || Array.isArray(schema)) return [];
2066
- const properties = schema.properties;
2067
- if (!properties || typeof properties !== "object" || Array.isArray(properties)) return [];
2068
- const required2 = new Set(
2069
- Array.isArray(schema.required) ? schema.required.filter((value) => typeof value === "string") : []
2070
- );
2071
- return Object.entries(properties).map(([name, value]) => {
2072
- const property = value && typeof value === "object" && !Array.isArray(value) ? value : {};
2073
- const type = property.type === "integer" ? "int" : property.type === "boolean" ? "bool" : Array.isArray(property.enum) ? "enum" : "string";
2074
- return {
2075
- name,
2076
- flag: `--${name}`,
2077
- type,
2078
- required: required2.has(name),
2079
- ...type === "enum" && Array.isArray(property.enum) ? { values: property.enum.filter((item) => typeof item === "string") } : {},
2080
- describe: typeof property.description === "string" ? property.description : name
2081
- };
2082
- });
2023
+ if (capability && !capability.config.workflow) {
2024
+ return [
2025
+ {
2026
+ name: "input",
2027
+ flag: "--input",
2028
+ type: "string",
2029
+ required: false,
2030
+ bindsCommentRest: true,
2031
+ describe: "One JSON-compatible input value."
2032
+ }
2033
+ ];
2083
2034
  }
2084
2035
  return getProfileInputs(resolved.implementation);
2085
2036
  }
2086
2037
  function resolveCapabilityExecution(capability, cwd = process.cwd()) {
2087
- if (path7.basename(capability.profilePath) === "contract.json") {
2038
+ if (!capability.config.workflow) {
2088
2039
  return { implementation: "capability-run", cliArgs: { capability: capability.slug } };
2089
2040
  }
2090
2041
  const firstWorkflowStep = capability.config.workflow?.steps[0];
2091
2042
  if (firstWorkflowStep) {
2092
- const implementation2 = firstWorkflowStep.implementation ?? firstWorkflowStep.capability;
2043
+ const implementation2 = firstWorkflowStep.capability;
2093
2044
  return { implementation: implementation2, cliArgs: {} };
2094
2045
  }
2095
2046
  const implementation = capability.config.implementation ?? capability.config.implementations?.[0] ?? (capability.config.role ? capability.slug : void 0) ?? (capability.config.tickScript ? "capability-tick-scripted" : "capability-tick");
@@ -3471,7 +3422,7 @@ function stripAgentSecrets(env) {
3471
3422
  }
3472
3423
  return out;
3473
3424
  }
3474
- function buildAgentEnvironment(baseEnv, repoToken) {
3425
+ function buildAgentEnvironment(baseEnv, repoToken, requestEnvironment) {
3475
3426
  const env = stripAgentSecrets({
3476
3427
  ...baseEnv,
3477
3428
  SKIP_HOOKS: "1",
@@ -3484,13 +3435,14 @@ function buildAgentEnvironment(baseEnv, repoToken) {
3484
3435
  env.GITHUB_TOKEN = repoToken;
3485
3436
  env.GH_TOKEN = repoToken;
3486
3437
  }
3438
+ Object.assign(env, requestEnvironment);
3487
3439
  return env;
3488
3440
  }
3489
3441
  async function runAgent(opts) {
3490
3442
  const ndjsonDir = opts.ndjsonDir ?? agentRunDir(opts.cwd);
3491
3443
  fs8.mkdirSync(ndjsonDir, { recursive: true });
3492
3444
  const ndjsonPath = path9.join(ndjsonDir, "last-run.jsonl");
3493
- const env = buildAgentEnvironment(process.env, opts.repoToken);
3445
+ const env = buildAgentEnvironment(process.env, opts.repoToken, opts.environment);
3494
3446
  if (opts.litellmUrl) {
3495
3447
  env.ANTHROPIC_BASE_URL = opts.litellmUrl;
3496
3448
  env.ANTHROPIC_API_KEY = getAnthropicApiKeyOrDummy();
@@ -8892,7 +8844,7 @@ function validateWorkflow(value, options = {}) {
8892
8844
  }
8893
8845
  const graphMode = workflow?.startAt !== void 0 || rawSteps.some((entry) => {
8894
8846
  const step = asRecord2(entry);
8895
- return Boolean(step && (step.id !== void 0 || step.next !== void 0 || step.inputs !== void 0));
8847
+ return Boolean(step && (step.id !== void 0 || step.next !== void 0));
8896
8848
  });
8897
8849
  const steps = rawSteps.map(
8898
8850
  (entry) => typeof entry === "string" ? { capability: entry } : asRecord2(entry)
@@ -8929,33 +8881,8 @@ function validateWorkflow(value, options = {}) {
8929
8881
  }
8930
8882
  }
8931
8883
  validateDataMatch(step.runWhen, `${base}.runWhen`, issues);
8932
- const inputs = asRecord2(step.inputs);
8933
- if (step.inputs !== void 0 && !inputs) {
8934
- issue(issues, "invalid_inputs", `${base}.inputs`, "workflow step inputs must be an object");
8935
- }
8936
- if (inputs) {
8937
- for (const [name, mapping] of Object.entries(inputs)) {
8938
- const inputPath = `${base}.inputs.${name}`;
8939
- if (!SAFE_NAME.test(name)) issue(issues, "invalid_input_name", inputPath, `invalid input name ${name}`);
8940
- const from = text(asRecord2(mapping)?.from);
8941
- if (!from || !SAFE_DATA_PATH.test(from)) {
8942
- issue(
8943
- issues,
8944
- "invalid_data_path",
8945
- `${inputPath}.from`,
8946
- `workflow input ${name} must read from facts, evidence, artifacts, result, workflow, or lastOutcome`
8947
- );
8948
- }
8949
- const declared = capability ? options.capabilityInputs?.get(capability) : void 0;
8950
- if (declared && !declared.has(name)) {
8951
- issue(
8952
- issues,
8953
- "unknown_capability_input",
8954
- inputPath,
8955
- `capability ${capability} does not declare input ${name}`
8956
- );
8957
- }
8958
- }
8884
+ if (step.input !== void 0 && !isJsonValue(step.input)) {
8885
+ issue(issues, "invalid_input", `${base}.input`, "workflow step input must be one JSON value");
8959
8886
  }
8960
8887
  });
8961
8888
  if (!graphMode) return issues;
@@ -9140,6 +9067,12 @@ function isComparable(value) {
9140
9067
  if (value === null || ["string", "number", "boolean"].includes(typeof value)) return true;
9141
9068
  return Array.isArray(value) && value.length > 0 && value.every((item) => isComparable(item) && !Array.isArray(item));
9142
9069
  }
9070
+ function isJsonValue(value) {
9071
+ if (value === null || ["string", "number", "boolean"].includes(typeof value)) return true;
9072
+ if (Array.isArray(value)) return value.every(isJsonValue);
9073
+ if (!value || typeof value !== "object") return false;
9074
+ return Object.values(value).every(isJsonValue);
9075
+ }
9143
9076
  function issue(issues, code, path54, message) {
9144
9077
  issues.push({ code, path: path54, message });
9145
9078
  }
@@ -9153,14 +9086,12 @@ var init_workflowValidation = __esm({
9153
9086
  SUPPORTED_STEP_FIELDS = /* @__PURE__ */ new Set([
9154
9087
  "id",
9155
9088
  "capability",
9089
+ "input",
9156
9090
  "action",
9157
- "implementation",
9158
9091
  "evidence",
9159
9092
  "target",
9160
9093
  "targetFact",
9161
9094
  "reason",
9162
- "cliArgs",
9163
- "inputs",
9164
9095
  "next",
9165
9096
  "runWhen",
9166
9097
  "continueOn",
@@ -9189,10 +9120,7 @@ function normalizeWorkflowDefinition(value) {
9189
9120
  const name = typeof raw.name === "string" ? raw.name.trim() : "";
9190
9121
  const requestedAgent = typeof raw.agent === "string" ? raw.agent.trim() : "";
9191
9122
  const agent = /^[a-z][a-z0-9-]*$/.test(requestedAgent) ? requestedAgent : "kody";
9192
- const hasGraphConnections = Array.isArray(raw.steps) && raw.steps.some(
9193
- (step) => step && typeof step === "object" && !Array.isArray(step) && (step.next !== void 0 || step.inputs !== void 0)
9194
- );
9195
- if (hasGraphConnections) {
9123
+ if (Array.isArray(raw.steps)) {
9196
9124
  if (validateWorkflow({ steps: raw.steps, ...raw.startAt !== void 0 ? { startAt: raw.startAt } : {} }).length > 0) {
9197
9125
  return null;
9198
9126
  }
@@ -15244,6 +15172,51 @@ var init_loadCapabilityState = __esm({
15244
15172
  // src/scripts/loadSimpleCapability.ts
15245
15173
  import * as fs39 from "fs";
15246
15174
  import * as path36 from "path";
15175
+ function parseInput(supplied) {
15176
+ if (typeof supplied !== "string") return supplied;
15177
+ try {
15178
+ return JSON.parse(supplied);
15179
+ } catch {
15180
+ return parseFlagInput(supplied) ?? supplied;
15181
+ }
15182
+ }
15183
+ function parseFlagInput(value) {
15184
+ const tokens = value.trim().split(/\s+/).filter(Boolean);
15185
+ if (!tokens.some((token) => token.startsWith("--"))) return null;
15186
+ const input = {};
15187
+ const text2 = [];
15188
+ for (let index = 0; index < tokens.length; index += 1) {
15189
+ const token = tokens[index];
15190
+ if (!token.startsWith("--") || token.length === 2) {
15191
+ text2.push(token);
15192
+ continue;
15193
+ }
15194
+ const equalAt = token.indexOf("=");
15195
+ const name = equalAt >= 0 ? token.slice(2, equalAt) : token.slice(2);
15196
+ const next = equalAt >= 0 ? token.slice(equalAt + 1) : tokens[index + 1];
15197
+ if (equalAt < 0 && next && !next.startsWith("--")) index += 1;
15198
+ input[name] = next && !next.startsWith("--") ? scalar(next) : true;
15199
+ }
15200
+ if (text2.length > 0) input.request = text2.join(" ");
15201
+ return input;
15202
+ }
15203
+ function scalar(value) {
15204
+ if (value === "true" || value === "false") return value === "true";
15205
+ if (/^-?\d+$/.test(value)) return Number(value);
15206
+ return value;
15207
+ }
15208
+ function capabilityEnvironment(input) {
15209
+ const environment = {
15210
+ KODY_CAPABILITY_INPUT: JSON.stringify(input ?? null)
15211
+ };
15212
+ if (!input || typeof input !== "object" || Array.isArray(input)) return environment;
15213
+ for (const [name, value] of Object.entries(input)) {
15214
+ if (value === void 0 || value === null) continue;
15215
+ const key = name.toUpperCase().replace(/[^A-Z0-9]+/g, "_");
15216
+ environment[`KODY_ARG_${key}`] = typeof value === "string" ? value : JSON.stringify(value);
15217
+ }
15218
+ return environment;
15219
+ }
15247
15220
  function listFiles(root) {
15248
15221
  if (!fs39.existsSync(root)) return [];
15249
15222
  const files = [];
@@ -15262,8 +15235,8 @@ var loadSimpleCapability;
15262
15235
  var init_loadSimpleCapability = __esm({
15263
15236
  "src/scripts/loadSimpleCapability.ts"() {
15264
15237
  "use strict";
15265
- init_definition_paths();
15266
15238
  init_capabilityFolders();
15239
+ init_definition_paths();
15267
15240
  loadSimpleCapability = async (ctx) => {
15268
15241
  const slug = typeof ctx.args.capability === "string" ? ctx.args.capability.trim() : "";
15269
15242
  if (!/^[a-z][a-z0-9-]*$/.test(slug)) {
@@ -15273,37 +15246,24 @@ var init_loadSimpleCapability = __esm({
15273
15246
  if (!capability) {
15274
15247
  throw new Error(`Capability "${slug}" is not a valid simple capability folder`);
15275
15248
  }
15276
- const contract = capability.rawProfile.contract;
15277
15249
  const toolRoot = path36.join(capability.dir, "tools");
15278
15250
  const skillRoot = path36.join(capability.dir, "skills");
15279
15251
  const toolFiles = listFiles(toolRoot);
15280
15252
  const skillFiles = listFiles(skillRoot);
15281
- const supplied = ctx.args.input;
15282
- let input = supplied;
15283
- if (typeof supplied === "string") {
15284
- try {
15285
- input = JSON.parse(supplied);
15286
- } catch {
15287
- input = supplied;
15288
- }
15289
- }
15253
+ const input = parseInput(ctx.args.input);
15290
15254
  ctx.data.jobCapability = slug;
15291
15255
  ctx.data.capabilityInput = input;
15292
- ctx.data.capabilityContract = contract;
15256
+ ctx.data.capabilityEnvironment = capabilityEnvironment(input);
15293
15257
  ctx.data.prompt = [
15294
15258
  capability.rawBody.trim(),
15295
15259
  "",
15296
15260
  "## Input",
15297
15261
  "",
15298
15262
  "```json",
15299
- JSON.stringify({ [contract.input.name]: input }, null, 2),
15263
+ JSON.stringify(input ?? null, null, 2),
15300
15264
  "```",
15301
15265
  "",
15302
- "Return one JSON value matching the output contract:",
15303
- "",
15304
- "```json",
15305
- JSON.stringify({ [contract.output.name]: contract.output.schema }, null, 2),
15306
- "```",
15266
+ "Return one JSON value.",
15307
15267
  ...skillFiles.length ? [
15308
15268
  "",
15309
15269
  "## Skills",
@@ -16871,15 +16831,13 @@ var init_parseJobStateFromAgentResult = __esm({
16871
16831
  });
16872
16832
 
16873
16833
  // src/scripts/parseSimpleCapabilityOutput.ts
16874
- function parseEnvelope(text2) {
16875
- if (!text2) return null;
16834
+ function parseOutput(text2) {
16835
+ if (!text2) return void 0;
16876
16836
  const candidate = text2.match(/```(?:json)?\s*([\s\S]*?)\s*```/i)?.[1] ?? text2;
16877
16837
  try {
16878
- const parsed = JSON.parse(candidate);
16879
- if (!isObject(parsed) || !isObject(parsed.result)) return null;
16880
- return { result: parsed.result };
16838
+ return JSON.parse(candidate);
16881
16839
  } catch {
16882
- return null;
16840
+ return void 0;
16883
16841
  }
16884
16842
  }
16885
16843
  function isObject(value) {
@@ -16893,28 +16851,31 @@ var init_parseSimpleCapabilityOutput = __esm({
16893
16851
  "src/scripts/parseSimpleCapabilityOutput.ts"() {
16894
16852
  "use strict";
16895
16853
  parseSimpleCapabilityOutput = async (ctx, _profile, agentResult) => {
16896
- const envelope = parseEnvelope(agentResult?.finalText);
16897
- if (!envelope) {
16854
+ const output = parseOutput(agentResult?.finalText);
16855
+ if (output === void 0) {
16898
16856
  ctx.output.exitCode = 64;
16899
- ctx.output.reason = "simple capability did not return its JSON output contract";
16857
+ ctx.output.reason = "simple capability did not return one JSON value";
16900
16858
  return;
16901
16859
  }
16902
- const result = envelope.result;
16903
- const data = isObject(result.data) ? result.data : {};
16860
+ ctx.data.capabilityOutput = output;
16861
+ const result = isObject(output) ? output : {};
16862
+ const data = isObject(result.data) ? result.data : isObject(output) ? output : { output };
16904
16863
  const summary = typeof result.summary === "string" ? result.summary : "Capability completed";
16905
16864
  const reason = typeof result.reason === "string" ? result.reason : summary;
16906
16865
  const prUrl = stringValue4(data.pullRequestUrl) ?? stringValue4(data.prUrl) ?? stringValue4(result.pullRequestUrl) ?? stringValue4(result.prUrl);
16907
16866
  if (prUrl) ctx.output.prUrl = prUrl;
16908
16867
  ctx.output.reason = reason;
16909
- ctx.data.capabilityResults = [{
16910
- version: 1,
16911
- status: "changed",
16912
- summary,
16913
- facts: data,
16914
- artifacts: prUrl ? [{ label: "Pull request", url: prUrl }] : [],
16915
- missingEvidence: [],
16916
- blockers: []
16917
- }];
16868
+ ctx.data.capabilityResults = [
16869
+ {
16870
+ version: 1,
16871
+ status: "changed",
16872
+ summary,
16873
+ facts: data,
16874
+ artifacts: prUrl ? [{ label: "Pull request", url: prUrl }] : [],
16875
+ missingEvidence: [],
16876
+ blockers: []
16877
+ }
16878
+ ];
16918
16879
  };
16919
16880
  }
16920
16881
  });
@@ -19650,26 +19611,18 @@ function validateFilesForKind(kind, slug, files, strictSingleModel, failures, op
19650
19611
  const workflow = hasWorkflowObject ? profile.workflow : { steps: profile.steps, ...profile.startAt !== void 0 ? { startAt: profile.startAt } : {} };
19651
19612
  const known = options.capabilityRoot ? getCapabilityRoots(options.capabilityRoot).flatMap((root) => listCapabilityFolderSlugs(root)) : [];
19652
19613
  const uniqueKnown = [...new Set(known)];
19653
- const capabilityInputs = /* @__PURE__ */ new Map();
19654
19614
  const capabilityOutputs = /* @__PURE__ */ new Map();
19655
19615
  if (options.capabilityRoot) {
19656
19616
  for (const capability of uniqueKnown) {
19657
19617
  const folder = getCapabilityRoots(options.capabilityRoot).map((root) => readCapabilityFolder(root, capability)).find((entry) => entry !== null);
19658
- capabilityOutputs.set(capability, folder ? capabilityOutputConditionPaths(folder.config) : /* @__PURE__ */ new Set());
19659
- const inputs = getCapabilityActionInputs(capability, options.capabilityRoot);
19660
- if (inputs) {
19661
- capabilityInputs.set(
19662
- capability,
19663
- new Set(inputs.flatMap((input) => [input.name, input.flag.replace(/^--/, "")]))
19664
- );
19665
- }
19618
+ const outputPaths = folder ? capabilityOutputConditionPaths(folder.config) : /* @__PURE__ */ new Set();
19619
+ if (outputPaths.size > 0) capabilityOutputs.set(capability, outputPaths);
19666
19620
  }
19667
19621
  }
19668
19622
  failures.push(
19669
19623
  ...formatWorkflowValidationIssues(
19670
19624
  validateWorkflow(workflow, {
19671
19625
  ...uniqueKnown.length > 0 ? { knownCapabilities: new Set(uniqueKnown) } : {},
19672
- ...capabilityInputs.size > 0 ? { capabilityInputs } : {},
19673
19626
  ...capabilityOutputs.size > 0 ? { capabilityOutputs } : {}
19674
19627
  })
19675
19628
  )
@@ -21214,6 +21167,7 @@ async function runImplementation(profileName, input) {
21214
21167
  prompt,
21215
21168
  model,
21216
21169
  cwd: input.cwd,
21170
+ environment: ctx.data.capabilityEnvironment && typeof ctx.data.capabilityEnvironment === "object" && !Array.isArray(ctx.data.capabilityEnvironment) ? ctx.data.capabilityEnvironment : void 0,
21217
21171
  litellmUrl: lm?.url ?? null,
21218
21172
  // On a connection drop mid-run, restart the (possibly crashed) proxy
21219
21173
  // before the agent retries. No-op for direct-Anthropic runs (lm null).
@@ -21471,7 +21425,8 @@ async function runImplementation(profileName, input) {
21471
21425
  nextJob: ctx.output.nextJob,
21472
21426
  afterNextJob: ctx.output.afterNextJob,
21473
21427
  taskState: ctx.data.taskState,
21474
- capabilityResults
21428
+ capabilityResults,
21429
+ ...Object.hasOwn(ctx.data, "capabilityOutput") ? { capabilityOutput: ctx.data.capabilityOutput } : {}
21475
21430
  });
21476
21431
  } catch (err) {
21477
21432
  const msg = err instanceof Error ? err.message : String(err);
@@ -22251,12 +22206,10 @@ async function runCapabilityImplementationStep(valid, profileName, capabilityIde
22251
22206
  const shouldApplyResolvedCapabilityArgs = valid.implementation === void 0 && resolvedCapability && profileName === resolvedCapability.implementation;
22252
22207
  input.cliArgs = shouldApplyResolvedCapabilityArgs ? { ...resolvedCapability.cliArgs, ...input.cliArgs } : input.cliArgs;
22253
22208
  if (profileName === "capability-run" && capabilityIdentity) {
22254
- const capabilityInput = { ...valid.cliArgs };
22209
+ const capabilityInput = Object.keys(valid.cliArgs).length > 0 ? genericInputFromArgs(valid.cliArgs) : void 0;
22255
22210
  input.cliArgs = {
22256
22211
  capability: capabilityIdentity,
22257
- ...Object.keys(capabilityInput).length > 0 ? {
22258
- input: Object.keys(capabilityInput).length === 1 && typeof capabilityInput.input === "string" ? capabilityInput.input : JSON.stringify(capabilityInput)
22259
- } : {}
22212
+ ...capabilityInput !== void 0 ? { input: JSON.stringify(capabilityInput) } : {}
22260
22213
  };
22261
22214
  }
22262
22215
  const run = base.chain === false ? runImplementation : runImplementationChain;
@@ -22316,7 +22269,7 @@ async function runLinearCapabilityWorkflow(parent, workflow, capability, base) {
22316
22269
  );
22317
22270
  continue;
22318
22271
  }
22319
- const child = workflowStepToJob(step, parent, chainData);
22272
+ const child = workflowStepToJob(step, parent, chainData, base.cwd);
22320
22273
  process.stdout.write(
22321
22274
  `\u2192 kody: workflow ${capability.slug} step ${index + 1}/${workflow.steps.length} \u2192 ${label}
22322
22275
 
@@ -22341,6 +22294,7 @@ async function runLinearCapabilityWorkflow(parent, workflow, capability, base) {
22341
22294
  ...chainData,
22342
22295
  ...result.taskState ? { taskState: result.taskState } : {},
22343
22296
  ...outcome ? { workflowLastOutcome: outcome } : {},
22297
+ ...result.capabilityOutput !== void 0 ? { workflowLastOutput: result.capabilityOutput } : {},
22344
22298
  ...prUrl ? { workflowPrUrl: prUrl } : {},
22345
22299
  ...parsePrNumber5(prUrl) ? { workflowPrNumber: parsePrNumber5(prUrl) } : {}
22346
22300
  };
@@ -22354,12 +22308,11 @@ async function runLinearCapabilityWorkflow(parent, workflow, capability, base) {
22354
22308
  return withWorkflowBoundaryEval(capability, result);
22355
22309
  }
22356
22310
  function isGraphWorkflow(workflow) {
22357
- return workflow.startAt !== void 0 || workflow.steps.some((step) => step.id !== void 0 || step.next !== void 0 || step.inputs !== void 0);
22311
+ return workflow.startAt !== void 0 || workflow.steps.some((step) => step.id !== void 0 || step.next !== void 0);
22358
22312
  }
22359
22313
  function workflowError(workflow, base) {
22360
22314
  const projectCapabilitiesRoot = hydratedCapabilitiesRoot(base.cwd);
22361
22315
  const knownCapabilities = /* @__PURE__ */ new Set();
22362
- const capabilityInputs = /* @__PURE__ */ new Map();
22363
22316
  const capabilityOutputs = /* @__PURE__ */ new Map();
22364
22317
  for (const step of workflow.steps) {
22365
22318
  const action = step.action ?? step.capability;
@@ -22367,21 +22320,10 @@ function workflowError(workflow, base) {
22367
22320
  const resolvedFolder = resolveCapabilityFolder(step.capability, projectCapabilitiesRoot);
22368
22321
  if (!resolvedAction && !resolvedFolder) continue;
22369
22322
  knownCapabilities.add(step.capability);
22370
- capabilityOutputs.set(
22371
- step.capability,
22372
- resolvedFolder ? capabilityOutputConditionPaths(resolvedFolder.config) : /* @__PURE__ */ new Set()
22373
- );
22374
- const inputs = getCapabilityActionInputs(action, projectCapabilitiesRoot);
22375
- if (inputs) {
22376
- capabilityInputs.set(
22377
- step.capability,
22378
- new Set(inputs.flatMap((input) => [input.name, input.flag.replace(/^--/, "")]))
22379
- );
22380
- }
22323
+ const outputPaths = resolvedFolder ? capabilityOutputConditionPaths(resolvedFolder.config) : /* @__PURE__ */ new Set();
22324
+ if (outputPaths.size > 0) capabilityOutputs.set(step.capability, outputPaths);
22381
22325
  }
22382
- return formatWorkflowValidationIssues(
22383
- validateWorkflow(workflow, { knownCapabilities, capabilityInputs, capabilityOutputs })
22384
- )[0] ?? null;
22326
+ return formatWorkflowValidationIssues(validateWorkflow(workflow, { knownCapabilities, capabilityOutputs }))[0] ?? null;
22385
22327
  }
22386
22328
  function initialWorkflowState(parent, workflow) {
22387
22329
  const prior = parent.workflowState;
@@ -22456,7 +22398,7 @@ async function runGraphCapabilityWorkflow(parent, workflow, capability, base, ch
22456
22398
  await checkpoint?.(state);
22457
22399
  let child;
22458
22400
  try {
22459
- child = workflowStepToJob(step, parent, chainData);
22401
+ child = workflowStepToJob(step, parent, chainData, base.cwd);
22460
22402
  } catch (error) {
22461
22403
  const reason = error instanceof Error ? error.message : String(error);
22462
22404
  state.status = "blocked";
@@ -22490,6 +22432,7 @@ async function runGraphCapabilityWorkflow(parent, workflow, capability, base, ch
22490
22432
  ...result.taskState ? { taskState: result.taskState } : {},
22491
22433
  ...outcome ? { workflowLastOutcome: outcome } : {},
22492
22434
  ...result.capabilityResults?.at(-1) ? { workflowLastResult: result.capabilityResults.at(-1) } : {},
22435
+ ...result.capabilityOutput !== void 0 ? { workflowLastOutput: result.capabilityOutput } : {},
22493
22436
  ...prUrl ? { workflowPrUrl: prUrl } : {},
22494
22437
  ...parsePrNumber5(prUrl) ? { workflowPrNumber: parsePrNumber5(prUrl) } : {}
22495
22438
  };
@@ -22588,23 +22531,12 @@ function withWorkflowBoundaryEval(capability, result) {
22588
22531
  reason: result.reason ? `${result.reason}; agency boundary eval failed: ${failed.join(", ")}` : `agency boundary eval failed: ${failed.join(", ")}`
22589
22532
  };
22590
22533
  }
22591
- function workflowStepToJob(step, parent, chainData) {
22534
+ function workflowStepToJob(step, parent, chainData, cwd) {
22592
22535
  const action = step.action ?? step.capability;
22593
- const mappedArgs = {};
22594
- const conditionContext = workflowConditionContext(chainData);
22595
- for (const [name, mapping] of Object.entries(step.inputs ?? {})) {
22596
- const value = resolveDottedPath2(conditionContext, mapping.from);
22597
- if (value === void 0) {
22598
- throw new InvalidJobError(`workflow step ${step.id ?? action} needs missing input ${mapping.from}`);
22599
- }
22600
- mappedArgs[name] = value;
22601
- }
22536
+ const targetNumber = workflowStepTargetNumber(step, parent, chainData);
22602
22537
  const rawArgs = {
22603
- ...parent.cliArgs,
22604
- ...mappedArgs,
22605
- ...step.cliArgs ?? {}
22538
+ ...parent.cliArgs
22606
22539
  };
22607
- const targetNumber = workflowStepTargetNumber(step, parent, chainData);
22608
22540
  if (step.target === "pr") {
22609
22541
  if (typeof targetNumber !== "number") {
22610
22542
  throw new InvalidJobError(`workflow step ${action} needs a PR target but no prior PR URL is available`);
@@ -22613,12 +22545,16 @@ function workflowStepToJob(step, parent, chainData) {
22613
22545
  } else if (step.target === "issue" && typeof targetNumber === "number") {
22614
22546
  rawArgs.issue = targetNumber;
22615
22547
  }
22616
- const cliArgs = filterCliArgsForStep(action, rawArgs);
22548
+ const genericInput = capabilityStepInput(
22549
+ step.input ?? chainData.workflowLastOutput ?? genericInputFromArgs(rawArgs),
22550
+ step.target,
22551
+ targetNumber
22552
+ );
22553
+ const cliArgs = usesGenericCapabilityInput(action, cwd) ? genericInput === void 0 ? {} : { input: JSON.stringify(genericInput) } : filterCliArgsForStep(action, rawArgs);
22617
22554
  const target = typeof targetNumber === "number" ? targetNumber : typeof parent.target === "number" ? parent.target : targetFromCliArgs(cliArgs);
22618
22555
  return {
22619
22556
  action,
22620
22557
  capability: step.capability,
22621
- ...step.implementation ? { implementation: step.implementation } : {},
22622
22558
  ...composeStepWhy(parent.why, step) ? { why: composeStepWhy(parent.why, step) } : {},
22623
22559
  ...parent.agent ? { agent: parent.agent } : {},
22624
22560
  ...parent.schedule ? { schedule: parent.schedule } : {},
@@ -22632,6 +22568,38 @@ function workflowStepToJob(step, parent, chainData) {
22632
22568
  ...parent.resultTarget ? { resultTarget: parent.resultTarget } : {}
22633
22569
  };
22634
22570
  }
22571
+ function usesGenericCapabilityInput(action, cwd) {
22572
+ const inputs = getCapabilityActionInputs(action, hydratedCapabilitiesRoot(cwd));
22573
+ return Boolean(inputs?.length === 1 && inputs[0]?.name === "input" && inputs[0]?.flag === "--input");
22574
+ }
22575
+ function genericInputFromArgs(args) {
22576
+ if (Object.hasOwn(args, "input")) {
22577
+ const { input, ...routing } = args;
22578
+ const parsed = parseGenericInput(input);
22579
+ if (Object.keys(routing).length === 0) return parsed;
22580
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
22581
+ return { ...parsed, ...routing };
22582
+ }
22583
+ return { request: parsed, ...routing };
22584
+ }
22585
+ return args;
22586
+ }
22587
+ function parseGenericInput(value) {
22588
+ if (typeof value !== "string") return value;
22589
+ try {
22590
+ return JSON.parse(value);
22591
+ } catch {
22592
+ return value;
22593
+ }
22594
+ }
22595
+ function capabilityStepInput(input, target, targetNumber) {
22596
+ if (!target || targetNumber === void 0) return input;
22597
+ const routing = { [target]: targetNumber };
22598
+ if (input && typeof input === "object" && !Array.isArray(input)) {
22599
+ return { ...input, ...routing };
22600
+ }
22601
+ return input === void 0 ? routing : { request: input, ...routing };
22602
+ }
22635
22603
  function shouldRunWorkflowStep(step, data) {
22636
22604
  if (!step.runWhen) return true;
22637
22605
  const context = workflowConditionContext(data);
@@ -22647,15 +22615,17 @@ function workflowOutcome(result) {
22647
22615
  function workflowConditionContext(data) {
22648
22616
  const lastOutcome = data.workflowLastOutcome;
22649
22617
  const lastResult = data.workflowLastResult;
22618
+ const lastOutput = data.workflowLastOutput;
22650
22619
  return {
22651
22620
  ...data,
22652
22621
  facts: data.workflowFacts ?? {},
22653
22622
  evidence: data.workflowEvidence ?? {},
22654
22623
  artifacts: data.workflowArtifacts ?? [],
22655
- result: lastResult,
22624
+ result: lastOutput === void 0 ? lastResult : lastOutput,
22656
22625
  workflow: {
22657
22626
  lastOutcome,
22658
22627
  lastResult,
22628
+ lastOutput,
22659
22629
  issueNumber: data.workflowIssueNumber,
22660
22630
  prNumber: data.workflowPrNumber,
22661
22631
  prUrl: data.workflowPrUrl
@@ -22674,10 +22644,9 @@ function valueMatches(actual, expected) {
22674
22644
  return actual === expected;
22675
22645
  }
22676
22646
  function workflowStepTargetNumber(step, parent, chainData) {
22677
- if (step.target === "pr")
22678
- return workflowPrNumber(chainData) ?? workflowTargetFactNumber(step, chainData) ?? targetFromCliArgs(step.cliArgs ?? {});
22647
+ if (step.target === "pr") return workflowPrNumber(chainData) ?? workflowTargetFactNumber(step, chainData);
22679
22648
  if (step.target === "issue") return workflowIssueNumber(parent);
22680
- return typeof parent.target === "number" ? parent.target : targetFromCliArgs({ ...parent.cliArgs, ...step.cliArgs ?? {} });
22649
+ return typeof parent.target === "number" ? parent.target : targetFromCliArgs(parent.cliArgs);
22681
22650
  }
22682
22651
  function workflowResumeStartIndex(steps, evidence) {
22683
22652
  if (!evidence) return 0;
@@ -16,6 +16,7 @@
16
16
  "flag": "--input",
17
17
  "type": "string",
18
18
  "required": false,
19
+ "bindsCommentRest": true,
19
20
  "describe": "The capability's single JSON input."
20
21
  }
21
22
  ],
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.442",
3
+ "version": "0.4.443",
4
4
  "description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -12,6 +12,29 @@
12
12
  "templates",
13
13
  "kody.config.schema.json"
14
14
  ],
15
+ "scripts": {
16
+ "kody:run": "tsx bin/kody.ts",
17
+ "serve": "tsx bin/kody.ts serve",
18
+ "serve:vscode": "tsx bin/kody.ts serve vscode",
19
+ "serve:claude": "tsx bin/kody.ts serve claude",
20
+ "clean:dist": "node scripts/clean-dist.cjs",
21
+ "build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
22
+ "check:modularity": "tsx scripts/check-script-modularity.ts",
23
+ "pretest": "pnpm check:modularity",
24
+ "test": "vitest run tests/unit tests/int --coverage",
25
+ "posttest": "tsx scripts/check-coverage-floor.ts",
26
+ "test:smoke": "vitest run tests/smoke --no-coverage",
27
+ "test:e2e": "vitest run tests/e2e --no-coverage",
28
+ "test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
29
+ "test:all": "vitest run tests --no-coverage",
30
+ "typecheck": "tsc --noEmit",
31
+ "lint": "biome check",
32
+ "lint:fix": "biome check --write",
33
+ "format": "biome format --write",
34
+ "verify:package": "node scripts/verify-package-tarball.cjs",
35
+ "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain --build-arg KODY_ENGINE_REF=$(git rev-parse HEAD) -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner",
36
+ "prepublishOnly": "pnpm typecheck && vitest run tests/unit tests/int --no-coverage && pnpm test:runtime-services && pnpm build && pnpm verify:package"
37
+ },
15
38
  "dependencies": {
16
39
  "@actions/cache": "^6.0.0",
17
40
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
@@ -38,27 +61,5 @@
38
61
  "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
39
62
  },
40
63
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
41
- "bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
42
- "scripts": {
43
- "kody:run": "tsx bin/kody.ts",
44
- "serve": "tsx bin/kody.ts serve",
45
- "serve:vscode": "tsx bin/kody.ts serve vscode",
46
- "serve:claude": "tsx bin/kody.ts serve claude",
47
- "clean:dist": "node scripts/clean-dist.cjs",
48
- "build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
49
- "check:modularity": "tsx scripts/check-script-modularity.ts",
50
- "pretest": "pnpm check:modularity",
51
- "test": "vitest run tests/unit tests/int --coverage",
52
- "posttest": "tsx scripts/check-coverage-floor.ts",
53
- "test:smoke": "vitest run tests/smoke --no-coverage",
54
- "test:e2e": "vitest run tests/e2e --no-coverage",
55
- "test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
56
- "test:all": "vitest run tests --no-coverage",
57
- "typecheck": "tsc --noEmit",
58
- "lint": "biome check",
59
- "lint:fix": "biome check --write",
60
- "format": "biome format --write",
61
- "verify:package": "node scripts/verify-package-tarball.cjs",
62
- "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain --build-arg KODY_ENGINE_REF=$(git rev-parse HEAD) -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner"
63
- }
64
- }
64
+ "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
65
+ }