@kody-ade/kody-engine 0.2.20 → 0.2.21

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/kody2.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // package.json
4
4
  var package_default = {
5
5
  name: "@kody-ade/kody-engine",
6
- version: "0.2.20",
6
+ version: "0.2.21",
7
7
  description: "kody2 \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
8
8
  license: "MIT",
9
9
  type: "module",
@@ -488,6 +488,8 @@ function loadProfile(profilePath) {
488
488
  cliTools: parseCliTools(profilePath, r.cliTools),
489
489
  scripts: parseScripts(profilePath, r.scripts),
490
490
  outputContract: r.outputContract,
491
+ inputArtifacts: parseInputArtifacts(profilePath, r.input),
492
+ outputArtifacts: parseOutputArtifacts(profilePath, r.output),
491
493
  dir: path4.dirname(profilePath)
492
494
  };
493
495
  return profile;
@@ -601,6 +603,51 @@ function parseScripts(p, raw) {
601
603
  postflight: parseScriptList(p, "postflight", r.postflight)
602
604
  };
603
605
  }
606
+ function parseInputArtifacts(p, raw) {
607
+ if (raw === void 0 || raw === null) return [];
608
+ if (typeof raw !== "object" || Array.isArray(raw)) {
609
+ throw new ProfileError(p, `"input" must be an object with an "artifacts" array`);
610
+ }
611
+ const list = raw.artifacts;
612
+ if (list === void 0 || list === null) return [];
613
+ if (!Array.isArray(list)) throw new ProfileError(p, `"input.artifacts" must be an array`);
614
+ const out = [];
615
+ for (const [i, item] of list.entries()) {
616
+ if (typeof item === "string") {
617
+ out.push({ name: item });
618
+ continue;
619
+ }
620
+ if (!item || typeof item !== "object") {
621
+ throw new ProfileError(p, `input.artifacts[${i}] must be a string or object`);
622
+ }
623
+ const r = item;
624
+ const name = requireString(p, r, "name");
625
+ const spec = { name };
626
+ if (typeof r.required === "boolean") spec.required = r.required;
627
+ out.push(spec);
628
+ }
629
+ return out;
630
+ }
631
+ function parseOutputArtifacts(p, raw) {
632
+ if (raw === void 0 || raw === null) return [];
633
+ if (typeof raw !== "object" || Array.isArray(raw)) return [];
634
+ const list = raw.artifacts;
635
+ if (list === void 0 || list === null) return [];
636
+ if (!Array.isArray(list)) throw new ProfileError(p, `"output.artifacts" must be an array`);
637
+ const out = [];
638
+ for (const [i, item] of list.entries()) {
639
+ if (!item || typeof item !== "object") {
640
+ throw new ProfileError(p, `output.artifacts[${i}] must be an object`);
641
+ }
642
+ const r = item;
643
+ out.push({
644
+ name: requireString(p, r, "name"),
645
+ format: typeof r.format === "string" ? r.format : "text",
646
+ from: requireString(p, r, "from")
647
+ });
648
+ }
649
+ return out;
650
+ }
604
651
  function parseScriptList(p, key, raw) {
605
652
  if (!Array.isArray(raw)) {
606
653
  throw new ProfileError(p, `scripts.${key} must be an array`);
@@ -2126,6 +2173,7 @@ function emptyState() {
2126
2173
  attempts: {}
2127
2174
  },
2128
2175
  executables: {},
2176
+ artifacts: {},
2129
2177
  history: []
2130
2178
  };
2131
2179
  }
@@ -2172,6 +2220,7 @@ function parseStateComment(body) {
2172
2220
  schemaVersion: 1,
2173
2221
  core: { ...emptyState().core, ...parsed.core },
2174
2222
  executables: parsed.executables ?? {},
2223
+ artifacts: parsed.artifacts && typeof parsed.artifacts === "object" ? parsed.artifacts : {},
2175
2224
  history: Array.isArray(parsed.history) ? parsed.history : []
2176
2225
  };
2177
2226
  } catch {
@@ -2200,6 +2249,7 @@ function reduce(state, executable, action) {
2200
2249
  phase: phaseFromAction(executable, action)
2201
2250
  },
2202
2251
  executables: newExecutables,
2252
+ artifacts: { ...state.artifacts ?? {} },
2203
2253
  history: newHistory
2204
2254
  };
2205
2255
  }
@@ -2228,7 +2278,13 @@ function renderStateComment(state) {
2228
2278
  lines.push("");
2229
2279
  lines.push("```json");
2230
2280
  lines.push(JSON.stringify(
2231
- { schemaVersion: state.schemaVersion, core: state.core, executables: state.executables, history: state.history },
2281
+ {
2282
+ schemaVersion: state.schemaVersion,
2283
+ core: state.core,
2284
+ artifacts: state.artifacts ?? {},
2285
+ executables: state.executables,
2286
+ history: state.history
2287
+ },
2232
2288
  null,
2233
2289
  2
2234
2290
  ));
@@ -2249,6 +2305,10 @@ function renderStateComment(state) {
2249
2305
  if (attempts) lines.push(`- **Attempts:** ${attempts}`);
2250
2306
  if (state.core.prUrl) lines.push(`- **PR:** ${state.core.prUrl}`);
2251
2307
  if (state.core.runUrl) lines.push(`- **Run:** ${state.core.runUrl}`);
2308
+ const artifactNames = Object.keys(state.artifacts ?? {});
2309
+ if (artifactNames.length > 0) {
2310
+ lines.push(`- **Artifacts:** ${artifactNames.map((n) => `\`${n}\``).join(", ")}`);
2311
+ }
2252
2312
  lines.push("");
2253
2313
  if (state.history.length > 0) {
2254
2314
  lines.push("### Recent history");
@@ -2266,6 +2326,12 @@ function readTaskState(target, number, cwd) {
2266
2326
  const existing = findStateComment(target, number, cwd);
2267
2327
  return existing ? parseStateComment(existing.body) : emptyState();
2268
2328
  }
2329
+ function setArtifact(state, name, artifact) {
2330
+ return {
2331
+ ...state,
2332
+ artifacts: { ...state.artifacts ?? {}, [name]: artifact }
2333
+ };
2334
+ }
2269
2335
  function writeTaskState(target, number, state, cwd) {
2270
2336
  const body = renderStateComment(state);
2271
2337
  const existing = findStateComment(target, number, cwd);
@@ -2329,6 +2395,35 @@ function makeAction(type, payload) {
2329
2395
  return { type, payload, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
2330
2396
  }
2331
2397
 
2398
+ // src/scripts/persistArtifacts.ts
2399
+ var persistArtifacts = async (ctx, profile) => {
2400
+ if (profile.outputArtifacts.length === 0) return;
2401
+ let state = ctx.data.taskState ?? emptyState();
2402
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2403
+ for (const spec of profile.outputArtifacts) {
2404
+ const content = readDottedString(ctx.data, spec.from);
2405
+ if (!content) continue;
2406
+ state = setArtifact(state, spec.name, {
2407
+ format: spec.format,
2408
+ producedBy: profile.name,
2409
+ createdAt: now,
2410
+ content
2411
+ });
2412
+ }
2413
+ ctx.data.taskState = state;
2414
+ };
2415
+ function readDottedString(source, dotted) {
2416
+ const parts = dotted.split(".");
2417
+ let cur = source;
2418
+ for (const p of parts) {
2419
+ if (cur === null || cur === void 0) return "";
2420
+ cur = cur[p];
2421
+ }
2422
+ if (typeof cur === "string") return cur;
2423
+ if (cur === null || cur === void 0) return "";
2424
+ return String(cur);
2425
+ }
2426
+
2332
2427
  // src/scripts/postIssueComment.ts
2333
2428
  var postIssueComment2 = async (ctx) => {
2334
2429
  if (ctx.skipAgent && ctx.output.exitCode !== void 0) return;
@@ -2735,6 +2830,29 @@ ${truncate2(r.stderr, 2e3)}
2735
2830
  `);
2736
2831
  }
2737
2832
 
2833
+ // src/scripts/resolveArtifacts.ts
2834
+ var resolveArtifacts = async (ctx, profile) => {
2835
+ if (profile.inputArtifacts.length === 0) return;
2836
+ const state = ctx.data.taskState;
2837
+ const available = state?.artifacts ?? {};
2838
+ const resolved = {};
2839
+ const missing = [];
2840
+ for (const spec of profile.inputArtifacts) {
2841
+ const found = available[spec.name];
2842
+ if (found && typeof found.content === "string") {
2843
+ resolved[spec.name] = found.content;
2844
+ } else if (spec.required) {
2845
+ missing.push(spec.name);
2846
+ }
2847
+ }
2848
+ ctx.data.artifacts = resolved;
2849
+ if (missing.length > 0) {
2850
+ ctx.skipAgent = true;
2851
+ ctx.output.exitCode = 64;
2852
+ ctx.output.reason = `required input artifacts missing from task state: ${missing.join(", ")}`;
2853
+ }
2854
+ };
2855
+
2738
2856
  // src/scripts/resolveFlow.ts
2739
2857
  import { execFileSync as execFileSync12 } from "child_process";
2740
2858
  var CONFLICT_DIFF_MAX_BYTES = 4e4;
@@ -3208,6 +3326,7 @@ var preflightScripts = {
3208
3326
  loadConventions,
3209
3327
  loadCoverageRules,
3210
3328
  buildSyntheticPlugin,
3329
+ resolveArtifacts,
3211
3330
  composePrompt
3212
3331
  };
3213
3332
  var postflightScripts = {
@@ -3218,6 +3337,7 @@ var postflightScripts = {
3218
3337
  ensurePr: ensurePr2,
3219
3338
  postIssueComment: postIssueComment2,
3220
3339
  postReviewResult,
3340
+ persistArtifacts,
3221
3341
  writeRunSummary,
3222
3342
  saveTaskState
3223
3343
  };
@@ -48,6 +48,9 @@
48
48
  {
49
49
  "script": "parseAgentResult"
50
50
  },
51
+ {
52
+ "script": "persistArtifacts"
53
+ },
51
54
  {
52
55
  "script": "writeRunSummary"
53
56
  },
@@ -60,6 +63,13 @@
60
63
  "actionTypes": [
61
64
  "PLAN_COMPLETED",
62
65
  "PLAN_FAILED"
66
+ ],
67
+ "artifacts": [
68
+ {
69
+ "name": "plan",
70
+ "format": "markdown",
71
+ "from": "prSummary"
72
+ }
63
73
  ]
64
74
  }
65
75
  }
@@ -39,6 +39,9 @@
39
39
  {
40
40
  "script": "loadTaskState"
41
41
  },
42
+ {
43
+ "script": "resolveArtifacts"
44
+ },
42
45
  {
43
46
  "script": "loadConventions"
44
47
  },
@@ -76,6 +79,11 @@
76
79
  }
77
80
  ]
78
81
  },
82
+ "input": {
83
+ "artifacts": [
84
+ { "name": "plan", "required": false }
85
+ ]
86
+ },
79
87
  "output": {
80
88
  "actionTypes": [
81
89
  "RUN_COMPLETED",
@@ -7,6 +7,11 @@ You are Kody, an autonomous engineer. Take a GitHub issue from spec to a tested
7
7
  {{conventionsBlock}}{{coverageBlock}}{{toolsUsage}}# Issue #{{issue.number}}: {{issue.title}}
8
8
  {{issue.body}}
9
9
 
10
+ # Existing plan (produced by `@kody2 plan`, if present)
11
+ {{artifacts.plan}}
12
+
13
+ If the plan above is non-empty, TREAT IT AS AUTHORITATIVE — follow its file list and approach rather than inventing your own. Deviate only if the plan is wrong; if you do, say so explicitly. If the plan is empty, proceed from first principles.
14
+
10
15
  # Required steps (all in this one session — no handoff)
11
16
  1. **Research** — read the issue carefully. Use Grep/Glob/Read to investigate the codebase: locate relevant files, understand existing patterns, check related tests, identify constraints. Do not edit anything yet.
12
17
  2. **Plan** — before any Edit/Write, output a short plan (5–10 lines): what files you'll change, the approach, what could go wrong. No fluff.
@@ -33,10 +33,39 @@ export interface Profile {
33
33
  postflight: ScriptEntry[]
34
34
  }
35
35
  outputContract?: OutputContract
36
+ /**
37
+ * Declared artifacts consumed by this executable. The resolveArtifacts
38
+ * preflight loads each into ctx.data.artifacts[name] from the task-state
39
+ * comment. If `required: true` and the artifact is absent, the executable
40
+ * fails fast.
41
+ */
42
+ inputArtifacts: InputArtifactSpec[]
43
+ /**
44
+ * Declared artifacts produced by this executable. The persistArtifacts
45
+ * postflight reads the named source field from ctx.data and writes an
46
+ * Artifact entry into the task-state comment's `artifacts` map.
47
+ */
48
+ outputArtifacts: OutputArtifactSpec[]
36
49
  /** Absolute directory the profile was loaded from. Used to resolve prompt.md. */
37
50
  dir: string
38
51
  }
39
52
 
53
+ export interface InputArtifactSpec {
54
+ /** Artifact name (the key in state.artifacts). */
55
+ name: string
56
+ /** If true, the executable fails when this artifact is missing from state. */
57
+ required?: boolean
58
+ }
59
+
60
+ export interface OutputArtifactSpec {
61
+ /** Artifact name (the key in state.artifacts). */
62
+ name: string
63
+ /** Informational format tag ("markdown", "text", …). */
64
+ format: string
65
+ /** Dotted path into ctx.data to read the payload from (e.g. "prSummary"). */
66
+ from: string
67
+ }
68
+
40
69
  export interface InputSpec {
41
70
  name: string
42
71
  flag: string
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.2.20",
3
+ "version": "0.2.21",
4
4
  "description": "kody2 — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
5
5
  "license": "MIT",
6
6
  "type": "module",