@kody-ade/kody-engine 0.4.182 → 0.4.184
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/README.md +4 -0
- package/dist/bin/kody.js +56 -47
- package/dist/executables/goal-scheduler/profile.json +10 -0
- package/dist/executables/goal-scheduler/scheduler.sh +9 -0
- package/dist/executables/release-deploy/deploy.sh +0 -0
- package/dist/executables/release-prepare/prepare.sh +0 -0
- package/dist/executables/release-publish/publish.sh +0 -0
- package/dist/executables/resolve/apply-prefer.sh +0 -0
- package/dist/executables/revert/revert.sh +0 -0
- package/package.json +20 -21
package/README.md
CHANGED
|
@@ -18,6 +18,10 @@ You: open an issue → comment "@kody run"
|
|
|
18
18
|
kody: reads the issue → writes the code → runs your tests → opens a PR
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
+
<p align="center">
|
|
22
|
+
<img src="docs/assets/kody-demo.gif" alt="kody demo — issue to tested PR" width="800">
|
|
23
|
+
</p>
|
|
24
|
+
|
|
21
25
|
## Why kody
|
|
22
26
|
|
|
23
27
|
- **No infrastructure.** Runs on the GitHub Actions you already have. One ~20-line
|
package/dist/bin/kody.js
CHANGED
|
@@ -1309,7 +1309,7 @@ var init_loadPriorArt = __esm({
|
|
|
1309
1309
|
// package.json
|
|
1310
1310
|
var package_default = {
|
|
1311
1311
|
name: "@kody-ade/kody-engine",
|
|
1312
|
-
version: "0.4.
|
|
1312
|
+
version: "0.4.184",
|
|
1313
1313
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
1314
1314
|
license: "MIT",
|
|
1315
1315
|
type: "module",
|
|
@@ -1408,10 +1408,10 @@ var HttpSink = class {
|
|
|
1408
1408
|
signal: AbortSignal.timeout(5e3)
|
|
1409
1409
|
});
|
|
1410
1410
|
if (!res.ok) {
|
|
1411
|
-
this.logger.warn(`HttpSink POST ${url} \u2192 ${res.status}`);
|
|
1411
|
+
this.logger.warn(`HttpSink POST ${redactUrl(url)} \u2192 ${res.status}`);
|
|
1412
1412
|
}
|
|
1413
1413
|
} catch (err) {
|
|
1414
|
-
this.logger.warn(`HttpSink POST ${url} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1414
|
+
this.logger.warn(`HttpSink POST ${redactUrl(url)} failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1415
1415
|
}
|
|
1416
1416
|
}
|
|
1417
1417
|
};
|
|
@@ -1424,6 +1424,9 @@ var TeeSink = class {
|
|
|
1424
1424
|
await Promise.all(this.sinks.map((s) => s.emit(event)));
|
|
1425
1425
|
}
|
|
1426
1426
|
};
|
|
1427
|
+
function redactUrl(url) {
|
|
1428
|
+
return url.replace(/([?&]token=)[^&]+/gi, "$1***");
|
|
1429
|
+
}
|
|
1427
1430
|
function withSessionParam(baseUrl, sessionId) {
|
|
1428
1431
|
const joiner = baseUrl.includes("?") ? "&" : "?";
|
|
1429
1432
|
return `${baseUrl}${joiner}sessionId=${encodeURIComponent(sessionId)}`;
|
|
@@ -1653,7 +1656,11 @@ function loadConfig(projectDir = process.cwd()) {
|
|
|
1653
1656
|
issueContext: parseIssueContext(raw.issueContext),
|
|
1654
1657
|
testRequirements: parseTestRequirements(raw.testRequirements),
|
|
1655
1658
|
defaultExecutable: typeof raw.defaultExecutable === "string" && raw.defaultExecutable.length > 0 ? raw.defaultExecutable : "run",
|
|
1656
|
-
|
|
1659
|
+
// No bare-PR default ships with the engine (there is no `fix` executable),
|
|
1660
|
+
// so leave this unset unless a consumer configures a real one. Dispatch
|
|
1661
|
+
// treats a missing/unknown default as "unrecognized" rather than routing
|
|
1662
|
+
// to a phantom executable that would crash the executor at load time.
|
|
1663
|
+
defaultPrExecutable: typeof raw.defaultPrExecutable === "string" && raw.defaultPrExecutable.length > 0 ? raw.defaultPrExecutable : void 0,
|
|
1657
1664
|
aliases: mergeAliases(raw.aliases),
|
|
1658
1665
|
classify: parseClassifyConfig(raw.classify),
|
|
1659
1666
|
release: parseReleaseConfig(raw.release),
|
|
@@ -1716,9 +1723,7 @@ function parseJobsConfig(raw) {
|
|
|
1716
1723
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
1717
1724
|
}
|
|
1718
1725
|
var BUILTIN_ALIASES = {
|
|
1719
|
-
build: "run"
|
|
1720
|
-
orchestrate: "bug",
|
|
1721
|
-
orchestrator: "bug"
|
|
1726
|
+
build: "run"
|
|
1722
1727
|
};
|
|
1723
1728
|
function mergeAliases(raw) {
|
|
1724
1729
|
const out = { ...BUILTIN_ALIASES };
|
|
@@ -1968,6 +1973,7 @@ async function runAgent(opts) {
|
|
|
1968
1973
|
let ndjsonWriteFailed = false;
|
|
1969
1974
|
let ndjsonWriteError;
|
|
1970
1975
|
let sawMutatingTool = false;
|
|
1976
|
+
let sawTerminalSuccess = false;
|
|
1971
1977
|
try {
|
|
1972
1978
|
const queryOptions = {
|
|
1973
1979
|
model: opts.model.model,
|
|
@@ -2179,6 +2185,7 @@ async function runAgent(opts) {
|
|
|
2179
2185
|
if (m.subtype === "success") {
|
|
2180
2186
|
outcome = "completed";
|
|
2181
2187
|
outcomeKind = "ok";
|
|
2188
|
+
sawTerminalSuccess = true;
|
|
2182
2189
|
const text = (typeof m.result === "string" ? m.result : "").trim();
|
|
2183
2190
|
if (text) resultTexts.push(text);
|
|
2184
2191
|
} else {
|
|
@@ -2189,9 +2196,13 @@ async function runAgent(opts) {
|
|
|
2189
2196
|
}
|
|
2190
2197
|
}
|
|
2191
2198
|
} catch (e) {
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2199
|
+
if (sawTerminalSuccess) {
|
|
2200
|
+
errorMessage = e instanceof Error ? e.message : String(e);
|
|
2201
|
+
} else {
|
|
2202
|
+
outcome = "failed";
|
|
2203
|
+
outcomeKind = "model_error";
|
|
2204
|
+
errorMessage = e instanceof Error ? e.message : String(e);
|
|
2205
|
+
}
|
|
2195
2206
|
} finally {
|
|
2196
2207
|
try {
|
|
2197
2208
|
fullLog.end();
|
|
@@ -3295,7 +3306,7 @@ function autoDispatch(opts) {
|
|
|
3295
3306
|
}
|
|
3296
3307
|
}
|
|
3297
3308
|
if (!executable && !firstToken) {
|
|
3298
|
-
executable = isPr ? opts?.config?.defaultPrExecutable ??
|
|
3309
|
+
executable = isPr ? opts?.config?.defaultPrExecutable ?? null : opts?.config?.defaultExecutable ?? null;
|
|
3299
3310
|
}
|
|
3300
3311
|
if (isBotAuthor && !consumedFirstToken) {
|
|
3301
3312
|
process.stderr.write(
|
|
@@ -4532,7 +4543,7 @@ var FORBIDDEN_PATH_PREFIXES = [
|
|
|
4532
4543
|
"build/"
|
|
4533
4544
|
];
|
|
4534
4545
|
var ALLOWED_PATH_PREFIXES = [".kody/memory/", ".kody/tasks/"];
|
|
4535
|
-
var FORBIDDEN_PATH_EXACT = /* @__PURE__ */ new Set([".env", ".kody-pip-requirements.txt"]);
|
|
4546
|
+
var FORBIDDEN_PATH_EXACT = /* @__PURE__ */ new Set([".env", ".kody-pip-requirements.txt", "kody.config.json"]);
|
|
4536
4547
|
var FORBIDDEN_PATH_SUFFIXES = [".log"];
|
|
4537
4548
|
var CONVENTIONAL_PREFIXES = [
|
|
4538
4549
|
"feat:",
|
|
@@ -7545,13 +7556,33 @@ var ContentsApiBackend = class {
|
|
|
7545
7556
|
};
|
|
7546
7557
|
if (typeof loaded.handle === "string") payload.sha = loaded.handle;
|
|
7547
7558
|
ensureStateBranch(this.owner, this.repo, this.cwd);
|
|
7548
|
-
|
|
7559
|
+
try {
|
|
7560
|
+
this.put(loaded.path, payload);
|
|
7561
|
+
} catch (err) {
|
|
7562
|
+
if (!isShaConflict(err)) throw err;
|
|
7563
|
+
const current = this.load(slug);
|
|
7564
|
+
if (!current.created && isStateUnchanged(current.state, next)) return false;
|
|
7565
|
+
if (typeof current.handle === "string") payload.sha = current.handle;
|
|
7566
|
+
else delete payload.sha;
|
|
7567
|
+
process.stderr.write(
|
|
7568
|
+
`[kody] jobState: concurrent write detected for ${slug}; reloaded SHA and retrying (last-write-wins)
|
|
7569
|
+
`
|
|
7570
|
+
);
|
|
7571
|
+
this.put(loaded.path, payload);
|
|
7572
|
+
}
|
|
7573
|
+
return true;
|
|
7574
|
+
}
|
|
7575
|
+
put(filePath, payload) {
|
|
7576
|
+
gh(["api", "--method", "PUT", `/repos/${this.owner}/${this.repo}/contents/${filePath}`, "--input", "-"], {
|
|
7549
7577
|
cwd: this.cwd,
|
|
7550
7578
|
input: JSON.stringify(payload)
|
|
7551
7579
|
});
|
|
7552
|
-
return true;
|
|
7553
7580
|
}
|
|
7554
7581
|
};
|
|
7582
|
+
function isShaConflict(err) {
|
|
7583
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
7584
|
+
return /HTTP 409/i.test(msg) || /HTTP 422/i.test(msg) || /does not match|is at|but expected/i.test(msg);
|
|
7585
|
+
}
|
|
7555
7586
|
|
|
7556
7587
|
// src/scripts/jobState/localFileBackend.ts
|
|
7557
7588
|
import * as fs28 from "fs";
|
|
@@ -7654,7 +7685,9 @@ var LocalFileBackend = class {
|
|
|
7654
7685
|
const absPath = path27.join(this.cwd, loaded.path);
|
|
7655
7686
|
fs28.mkdirSync(path27.dirname(absPath), { recursive: true });
|
|
7656
7687
|
const body = JSON.stringify(next, null, 2) + "\n";
|
|
7657
|
-
|
|
7688
|
+
const tmpPath = `${absPath}.${process.pid}.tmp`;
|
|
7689
|
+
fs28.writeFileSync(tmpPath, body, "utf-8");
|
|
7690
|
+
fs28.renameSync(tmpPath, absPath);
|
|
7658
7691
|
return true;
|
|
7659
7692
|
}
|
|
7660
7693
|
cacheKeyPrefix() {
|
|
@@ -12643,49 +12676,27 @@ var runPreviewBuild = async (ctx, _profile, _args) => {
|
|
|
12643
12676
|
await flyCreateApp(appName, orgSlug, flyToken);
|
|
12644
12677
|
}
|
|
12645
12678
|
await flyAllocateSharedIps(appName, flyToken);
|
|
12646
|
-
const nscToken = doc.secrets?.NSC_TOKEN?.value?.trim();
|
|
12647
|
-
if (!nscToken) {
|
|
12648
|
-
ctx.output.exitCode = 99;
|
|
12649
|
-
ctx.output.reason = "runPreviewBuild: vault has no NSC_TOKEN \u2014 add it via the dashboard's /secrets page";
|
|
12650
|
-
return;
|
|
12651
|
-
}
|
|
12652
|
-
process.env.NSC_TOKEN = nscToken;
|
|
12653
|
-
await runCmd(
|
|
12654
|
-
"bash",
|
|
12655
|
-
[
|
|
12656
|
-
"-c",
|
|
12657
|
-
`if [ ! -x /usr/local/bin/nsc ]; then
|
|
12658
|
-
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') &&
|
|
12659
|
-
OS=$(uname -s | tr '[:upper:]' '[:lower:]') &&
|
|
12660
|
-
curl -fsSL "https://get.namespace.so/packages/nsc/latest?arch=$ARCH&os=$OS" -o /tmp/nsc.tar.gz &&
|
|
12661
|
-
tar -xzf /tmp/nsc.tar.gz -C /tmp &&
|
|
12662
|
-
sudo install -m 0755 /tmp/nsc /usr/local/bin/nsc &&
|
|
12663
|
-
rm -f /tmp/nsc /tmp/nsc.tar.gz
|
|
12664
|
-
fi
|
|
12665
|
-
/usr/local/bin/nsc version`
|
|
12666
|
-
],
|
|
12667
|
-
{ cwd: ctx.cwd }
|
|
12668
|
-
);
|
|
12669
|
-
await runCmd("/usr/local/bin/nsc", ["docker", "buildx", "setup"], {
|
|
12670
|
-
cwd: ctx.cwd
|
|
12671
|
-
});
|
|
12672
12679
|
await runCmd(
|
|
12673
12680
|
"docker",
|
|
12674
12681
|
["login", "registry.fly.io", "-u", "x", "--password-stdin"],
|
|
12675
12682
|
{ input: flyToken, cwd: ctx.cwd }
|
|
12676
12683
|
);
|
|
12677
12684
|
const buildArgs = [
|
|
12678
|
-
"buildx",
|
|
12679
12685
|
"build",
|
|
12680
12686
|
"-f",
|
|
12681
12687
|
"Dockerfile.preview",
|
|
12682
12688
|
"-t",
|
|
12683
|
-
`registry.fly.io/${appName}:${tag}
|
|
12684
|
-
"--push"
|
|
12689
|
+
`registry.fly.io/${appName}:${tag}`
|
|
12685
12690
|
];
|
|
12686
12691
|
if (baseImage) buildArgs.push("--build-arg", `BASE_IMAGE=${baseImage}`);
|
|
12687
12692
|
buildArgs.push(".");
|
|
12688
|
-
await runCmd("docker", buildArgs, {
|
|
12693
|
+
await runCmd("docker", buildArgs, {
|
|
12694
|
+
cwd: ctx.cwd,
|
|
12695
|
+
env: { DOCKER_BUILDKIT: "1" }
|
|
12696
|
+
});
|
|
12697
|
+
await runCmd("docker", ["push", `registry.fly.io/${appName}:${tag}`], {
|
|
12698
|
+
cwd: ctx.cwd
|
|
12699
|
+
});
|
|
12689
12700
|
const stale = await flyListMachines(appName, flyToken);
|
|
12690
12701
|
for (const m of stale) {
|
|
12691
12702
|
await flyDestroyMachine(appName, m.id, flyToken).catch(() => void 0);
|
|
@@ -12879,8 +12890,6 @@ var saveTaskState = async (ctx, profile) => {
|
|
|
12879
12890
|
if (!target || !number || !state) return;
|
|
12880
12891
|
const executable = profile.name;
|
|
12881
12892
|
const action = ctx.data.action ?? synthesizeAction(ctx);
|
|
12882
|
-
if (ctx.output.prUrl && !state.core.prUrl) state.core.prUrl = ctx.output.prUrl;
|
|
12883
|
-
if (typeof ctx.data.runUrl === "string") state.core.runUrl = ctx.data.runUrl;
|
|
12884
12893
|
const next = reduce(state, executable, action, profile.phase);
|
|
12885
12894
|
if (ctx.output.prUrl) next.core.prUrl = ctx.output.prUrl;
|
|
12886
12895
|
if (typeof ctx.data.runUrl === "string") next.core.runUrl = ctx.data.runUrl;
|
|
@@ -29,6 +29,16 @@
|
|
|
29
29
|
"verify": "gh auth status",
|
|
30
30
|
"usage": "",
|
|
31
31
|
"allowedUses": ["api", "issue", "pr"]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"name": "python3",
|
|
35
|
+
"install": {
|
|
36
|
+
"required": true,
|
|
37
|
+
"checkCommand": "command -v python3"
|
|
38
|
+
},
|
|
39
|
+
"verify": "python3 --version",
|
|
40
|
+
"usage": "scheduler.sh parses each goal's state.json to read its 'state' field",
|
|
41
|
+
"allowedUses": []
|
|
32
42
|
}
|
|
33
43
|
],
|
|
34
44
|
"inputArtifacts": [],
|
|
@@ -11,6 +11,15 @@ set -euo pipefail
|
|
|
11
11
|
|
|
12
12
|
goals_dir=".kody/goals"
|
|
13
13
|
|
|
14
|
+
# python3 parses each goal's state.json below. It is declared as a required
|
|
15
|
+
# cliTool in profile.json, but guard here too: without this check a missing
|
|
16
|
+
# interpreter silently makes EVERY goal read state="" → treated as inactive →
|
|
17
|
+
# nothing ticks, reported as a misleading success. Fail loud instead.
|
|
18
|
+
if ! command -v python3 >/dev/null 2>&1; then
|
|
19
|
+
echo "[goal-scheduler] FATAL: python3 not found on PATH (required to read goal state)" >&2
|
|
20
|
+
exit 1
|
|
21
|
+
fi
|
|
22
|
+
|
|
14
23
|
# Goal state lives on the dedicated `kody-state` branch, not the default branch
|
|
15
24
|
# (keeps `chore(goals): …` churn out of code history). Materialize it into the
|
|
16
25
|
# working tree so the glob below sees current state. Best-effort: `kody-state`
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.184",
|
|
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",
|
|
@@ -12,24 +12,6 @@
|
|
|
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
|
-
"build": "tsup && node scripts/copy-assets.cjs",
|
|
21
|
-
"check:modularity": "tsx scripts/check-script-modularity.ts",
|
|
22
|
-
"pretest": "pnpm check:modularity",
|
|
23
|
-
"test": "vitest run tests/unit tests/int --coverage",
|
|
24
|
-
"test:smoke": "vitest run tests/smoke --no-coverage",
|
|
25
|
-
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
26
|
-
"test:all": "vitest run tests --no-coverage",
|
|
27
|
-
"typecheck": "tsc --noEmit",
|
|
28
|
-
"lint": "biome check",
|
|
29
|
-
"lint:fix": "biome check --write",
|
|
30
|
-
"format": "biome format --write",
|
|
31
|
-
"prepublishOnly": "pnpm build"
|
|
32
|
-
},
|
|
33
15
|
"dependencies": {
|
|
34
16
|
"@actions/cache": "^6.0.0",
|
|
35
17
|
"@anthropic-ai/claude-agent-sdk": "0.2.119",
|
|
@@ -52,5 +34,22 @@
|
|
|
52
34
|
"url": "git+https://github.com/aharonyaircohen/kody-engine.git"
|
|
53
35
|
},
|
|
54
36
|
"homepage": "https://github.com/aharonyaircohen/kody-engine",
|
|
55
|
-
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
|
|
56
|
-
|
|
37
|
+
"bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
|
|
38
|
+
"scripts": {
|
|
39
|
+
"kody:run": "tsx bin/kody.ts",
|
|
40
|
+
"serve": "tsx bin/kody.ts serve",
|
|
41
|
+
"serve:vscode": "tsx bin/kody.ts serve vscode",
|
|
42
|
+
"serve:claude": "tsx bin/kody.ts serve claude",
|
|
43
|
+
"build": "tsup && node scripts/copy-assets.cjs",
|
|
44
|
+
"check:modularity": "tsx scripts/check-script-modularity.ts",
|
|
45
|
+
"pretest": "pnpm check:modularity",
|
|
46
|
+
"test": "vitest run tests/unit tests/int --coverage",
|
|
47
|
+
"test:smoke": "vitest run tests/smoke --no-coverage",
|
|
48
|
+
"test:e2e": "vitest run tests/e2e --no-coverage",
|
|
49
|
+
"test:all": "vitest run tests --no-coverage",
|
|
50
|
+
"typecheck": "tsc --noEmit",
|
|
51
|
+
"lint": "biome check",
|
|
52
|
+
"lint:fix": "biome check --write",
|
|
53
|
+
"format": "biome format --write"
|
|
54
|
+
}
|
|
55
|
+
}
|