@kody-ade/kody-engine 0.3.38 → 0.3.40
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
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// package.json
|
|
4
4
|
var package_default = {
|
|
5
5
|
name: "@kody-ade/kody-engine",
|
|
6
|
-
version: "0.3.
|
|
6
|
+
version: "0.3.40",
|
|
7
7
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
8
8
|
license: "MIT",
|
|
9
9
|
type: "module",
|
|
@@ -4058,112 +4058,69 @@ var loadIssueStateComment = async (ctx, _profile, args) => {
|
|
|
4058
4058
|
import * as fs20 from "fs";
|
|
4059
4059
|
import * as path18 from "path";
|
|
4060
4060
|
|
|
4061
|
-
// src/scripts/
|
|
4062
|
-
function
|
|
4063
|
-
return
|
|
4061
|
+
// src/scripts/missionStateFile.ts
|
|
4062
|
+
function stateFilePath(missionsDir, slug) {
|
|
4063
|
+
return `${missionsDir.replace(/\/+$/, "")}/${slug}.state.json`;
|
|
4064
4064
|
}
|
|
4065
|
-
function
|
|
4065
|
+
function loadMissionState(owner, repo, filePath, cwd) {
|
|
4066
4066
|
let raw = "";
|
|
4067
4067
|
try {
|
|
4068
|
-
raw = gh2(["api",
|
|
4069
|
-
} catch {
|
|
4070
|
-
|
|
4068
|
+
raw = gh2(["api", `/repos/${owner}/${repo}/contents/${filePath}`], { cwd });
|
|
4069
|
+
} catch (err) {
|
|
4070
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4071
|
+
if (/HTTP 404/i.test(msg) || /Not Found/i.test(msg)) {
|
|
4072
|
+
return { path: filePath, sha: null, state: initialStateEnvelope("seed"), created: true };
|
|
4073
|
+
}
|
|
4074
|
+
throw err;
|
|
4071
4075
|
}
|
|
4072
4076
|
let parsed;
|
|
4073
4077
|
try {
|
|
4074
4078
|
parsed = JSON.parse(raw);
|
|
4075
4079
|
} catch {
|
|
4076
|
-
return
|
|
4080
|
+
throw new Error(`loadMissionState: contents API for ${filePath} did not return JSON`);
|
|
4077
4081
|
}
|
|
4078
|
-
if (!
|
|
4079
|
-
|
|
4080
|
-
id: g.id,
|
|
4081
|
-
description: typeof g.description === "string" ? g.description : null,
|
|
4082
|
-
files: g.files ?? {}
|
|
4083
|
-
}));
|
|
4084
|
-
}
|
|
4085
|
-
function getGist(gistId, cwd) {
|
|
4086
|
-
let raw = "";
|
|
4087
|
-
try {
|
|
4088
|
-
raw = gh2(["api", `/gists/${gistId}`], { cwd });
|
|
4089
|
-
} catch {
|
|
4090
|
-
return null;
|
|
4091
|
-
}
|
|
4092
|
-
let parsed;
|
|
4093
|
-
try {
|
|
4094
|
-
parsed = JSON.parse(raw);
|
|
4095
|
-
} catch {
|
|
4096
|
-
return null;
|
|
4082
|
+
if (!parsed || typeof parsed !== "object") {
|
|
4083
|
+
throw new Error(`loadMissionState: contents API for ${filePath} returned non-object`);
|
|
4097
4084
|
}
|
|
4098
|
-
if (!parsed || typeof parsed !== "object") return null;
|
|
4099
4085
|
const o = parsed;
|
|
4100
|
-
if (typeof o.
|
|
4101
|
-
|
|
4102
|
-
id: o.id,
|
|
4103
|
-
description: typeof o.description === "string" ? o.description : null,
|
|
4104
|
-
files: o.files ?? {}
|
|
4105
|
-
};
|
|
4106
|
-
}
|
|
4107
|
-
function findGistByDescription(description, cwd) {
|
|
4108
|
-
const all = listGists(cwd);
|
|
4109
|
-
return all.find((g) => g.description === description) ?? null;
|
|
4110
|
-
}
|
|
4111
|
-
function readEnvelope(gist) {
|
|
4112
|
-
const file = gist.files["state.json"];
|
|
4113
|
-
if (!file?.content) return null;
|
|
4114
|
-
let parsed;
|
|
4115
|
-
try {
|
|
4116
|
-
parsed = JSON.parse(file.content);
|
|
4117
|
-
} catch {
|
|
4118
|
-
const fallback = parseStateCommentBody("kody-mission-state", file.content);
|
|
4119
|
-
return fallback ?? null;
|
|
4086
|
+
if (o.type !== "file" || o.encoding !== "base64" || typeof o.content !== "string") {
|
|
4087
|
+
throw new Error(`loadMissionState: ${filePath} is not a base64 file`);
|
|
4120
4088
|
}
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
function findMissionGist(owner, repo, slug, cwd) {
|
|
4124
|
-
const desc = gistDescription(owner, repo, slug);
|
|
4125
|
-
const gist = findGistByDescription(desc, cwd);
|
|
4126
|
-
if (!gist) return null;
|
|
4127
|
-
const full = getGist(gist.id, cwd) ?? gist;
|
|
4128
|
-
const envelope = readEnvelope(full);
|
|
4129
|
-
if (!envelope) return null;
|
|
4130
|
-
return { gistId: full.id, state: envelope };
|
|
4131
|
-
}
|
|
4132
|
-
function createMissionGist(owner, repo, slug, cursor = "seed", cwd) {
|
|
4133
|
-
const description = gistDescription(owner, repo, slug);
|
|
4134
|
-
const initial = initialStateEnvelope(cursor);
|
|
4135
|
-
const payload = {
|
|
4136
|
-
description,
|
|
4137
|
-
public: false,
|
|
4138
|
-
files: {
|
|
4139
|
-
"state.json": { content: JSON.stringify(initial, null, 2) + "\n" }
|
|
4140
|
-
}
|
|
4141
|
-
};
|
|
4142
|
-
const raw = gh2(["api", "--method", "POST", "/gists", "--input", "-"], {
|
|
4143
|
-
cwd,
|
|
4144
|
-
input: JSON.stringify(payload)
|
|
4145
|
-
});
|
|
4146
|
-
let parsed;
|
|
4089
|
+
const decoded = Buffer.from(o.content, "base64").toString("utf-8");
|
|
4090
|
+
let envelope;
|
|
4147
4091
|
try {
|
|
4148
|
-
|
|
4092
|
+
envelope = JSON.parse(decoded);
|
|
4149
4093
|
} catch {
|
|
4150
|
-
throw new Error(`
|
|
4094
|
+
throw new Error(`loadMissionState: ${filePath} is not valid JSON`);
|
|
4151
4095
|
}
|
|
4152
|
-
if (!
|
|
4153
|
-
throw new Error(
|
|
4096
|
+
if (!isStateEnvelope(envelope)) {
|
|
4097
|
+
throw new Error(`loadMissionState: ${filePath} is not a StateEnvelope`);
|
|
4154
4098
|
}
|
|
4155
|
-
return {
|
|
4099
|
+
return { path: filePath, sha: o.sha, state: envelope, created: false };
|
|
4156
4100
|
}
|
|
4157
|
-
function
|
|
4101
|
+
function writeMissionState(owner, repo, loaded, next, cwd) {
|
|
4102
|
+
if (!loaded.created && deepEqualsState(loaded.state, next)) {
|
|
4103
|
+
return false;
|
|
4104
|
+
}
|
|
4105
|
+
const body = JSON.stringify(next, null, 2) + "\n";
|
|
4158
4106
|
const payload = {
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
}
|
|
4107
|
+
message: `chore(missions): update state for ${stateFileSlug(loaded.path)} (rev ${next.rev})`,
|
|
4108
|
+
content: Buffer.from(body, "utf-8").toString("base64")
|
|
4162
4109
|
};
|
|
4163
|
-
|
|
4110
|
+
if (loaded.sha) payload.sha = loaded.sha;
|
|
4111
|
+
gh2(["api", "--method", "PUT", `/repos/${owner}/${repo}/contents/${loaded.path}`, "--input", "-"], {
|
|
4164
4112
|
cwd,
|
|
4165
4113
|
input: JSON.stringify(payload)
|
|
4166
4114
|
});
|
|
4115
|
+
return true;
|
|
4116
|
+
}
|
|
4117
|
+
function deepEqualsState(a, b) {
|
|
4118
|
+
if (a.cursor !== b.cursor || a.done !== b.done) return false;
|
|
4119
|
+
return JSON.stringify(a.data) === JSON.stringify(b.data);
|
|
4120
|
+
}
|
|
4121
|
+
function stateFileSlug(filePath) {
|
|
4122
|
+
const last = filePath.split("/").pop() ?? filePath;
|
|
4123
|
+
return last.replace(/\.state\.json$/i, "");
|
|
4167
4124
|
}
|
|
4168
4125
|
|
|
4169
4126
|
// src/scripts/loadMissionFromFile.ts
|
|
@@ -4185,14 +4142,11 @@ var loadMissionFromFile = async (ctx, _profile, args) => {
|
|
|
4185
4142
|
}
|
|
4186
4143
|
const raw = fs20.readFileSync(absPath, "utf-8");
|
|
4187
4144
|
const { title, body } = parseMissionFile(raw, slug);
|
|
4188
|
-
|
|
4189
|
-
if (!loaded) {
|
|
4190
|
-
loaded = createMissionGist(owner, repo, slug, "seed", ctx.cwd);
|
|
4191
|
-
}
|
|
4145
|
+
const loaded = loadMissionState(owner, repo, stateFilePath(missionsDir, slug), ctx.cwd);
|
|
4192
4146
|
ctx.data.missionSlug = slug;
|
|
4193
4147
|
ctx.data.missionTitle = title;
|
|
4194
4148
|
ctx.data.missionIntent = body;
|
|
4195
|
-
ctx.data.
|
|
4149
|
+
ctx.data.missionState = loaded;
|
|
4196
4150
|
ctx.data.missionStateJson = JSON.stringify(loaded.state, null, 2);
|
|
4197
4151
|
};
|
|
4198
4152
|
function parseMissionFile(raw, slug) {
|
|
@@ -4523,7 +4477,7 @@ var parseMissionStateFromAgentResult = async (ctx, _profile, agentResult, args)
|
|
|
4523
4477
|
ctx.data.nextStateParseError = "agent did not run";
|
|
4524
4478
|
return;
|
|
4525
4479
|
}
|
|
4526
|
-
const fenceRegex = new RegExp(
|
|
4480
|
+
const fenceRegex = new RegExp(`\`\`\`${escapeRegex2(fenceLabel)}\\s*\\n([\\s\\S]*?)\\n\`\`\``, "m");
|
|
4527
4481
|
const match = fenceRegex.exec(agentResult.finalText);
|
|
4528
4482
|
if (!match) {
|
|
4529
4483
|
ctx.data.nextStateParseError = `agent did not emit a \`${fenceLabel}\` fenced block`;
|
|
@@ -4540,7 +4494,7 @@ var parseMissionStateFromAgentResult = async (ctx, _profile, agentResult, args)
|
|
|
4540
4494
|
ctx.data.nextStateParseError = "state must be an object with string `cursor`, object `data`, and boolean `done`";
|
|
4541
4495
|
return;
|
|
4542
4496
|
}
|
|
4543
|
-
const loaded = ctx.data.
|
|
4497
|
+
const loaded = ctx.data.missionState;
|
|
4544
4498
|
const prevRev = loaded?.state.rev ?? 0;
|
|
4545
4499
|
const next = {
|
|
4546
4500
|
version: 1,
|
|
@@ -5701,8 +5655,8 @@ var writeIssueStateComment = async (ctx, _profile, _agentResult, args) => {
|
|
|
5701
5655
|
}
|
|
5702
5656
|
};
|
|
5703
5657
|
|
|
5704
|
-
// src/scripts/
|
|
5705
|
-
var
|
|
5658
|
+
// src/scripts/writeMissionStateFile.ts
|
|
5659
|
+
var writeMissionStateFile = async (ctx, _profile, _agentResult) => {
|
|
5706
5660
|
const parseError = ctx.data.nextStateParseError;
|
|
5707
5661
|
if (parseError) {
|
|
5708
5662
|
process.stderr.write(`[kody] mission state write skipped: ${parseError}
|
|
@@ -5715,11 +5669,16 @@ var writeMissionGistState = async (ctx, _profile, _agentResult) => {
|
|
|
5715
5669
|
if (!next) {
|
|
5716
5670
|
return;
|
|
5717
5671
|
}
|
|
5718
|
-
const loaded = ctx.data.
|
|
5672
|
+
const loaded = ctx.data.missionState;
|
|
5719
5673
|
if (!loaded) {
|
|
5720
|
-
throw new Error("
|
|
5674
|
+
throw new Error("writeMissionStateFile: ctx.data.missionState missing \u2014 preflight must run first");
|
|
5675
|
+
}
|
|
5676
|
+
const owner = ctx.config.github.owner;
|
|
5677
|
+
const repo = ctx.config.github.repo;
|
|
5678
|
+
if (!owner || !repo) {
|
|
5679
|
+
throw new Error("writeMissionStateFile: ctx.config.github.owner/repo must be set");
|
|
5721
5680
|
}
|
|
5722
|
-
|
|
5681
|
+
writeMissionState(owner, repo, loaded, next, ctx.cwd);
|
|
5723
5682
|
};
|
|
5724
5683
|
|
|
5725
5684
|
// src/scripts/writeRunSummary.ts
|
|
@@ -5787,7 +5746,7 @@ var postflightScripts = {
|
|
|
5787
5746
|
parseIssueStateFromAgentResult,
|
|
5788
5747
|
parseMissionStateFromAgentResult,
|
|
5789
5748
|
writeIssueStateComment,
|
|
5790
|
-
|
|
5749
|
+
writeMissionStateFile,
|
|
5791
5750
|
requireFeedbackActions,
|
|
5792
5751
|
requirePlanDeviations,
|
|
5793
5752
|
verify,
|
|
@@ -6000,6 +5959,8 @@ async function runExecutable(profileName, input) {
|
|
|
6000
5959
|
}
|
|
6001
5960
|
}
|
|
6002
5961
|
function resolveProfilePath(profileName) {
|
|
5962
|
+
const found = resolveExecutable(profileName);
|
|
5963
|
+
if (found) return found;
|
|
6003
5964
|
const here = path19.dirname(new URL(import.meta.url).pathname);
|
|
6004
5965
|
const candidates = [
|
|
6005
5966
|
path19.join(here, "executables", profileName, "profile.json"),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.40",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|