@kody-ade/kody-engine 0.4.584 → 0.4.586
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 +70 -9
- package/package.json +1 -1
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.
|
|
18
|
+
version: "0.4.586",
|
|
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",
|
|
@@ -2189,6 +2189,7 @@ function readCapabilityFolder(root, slug) {
|
|
|
2189
2189
|
...contract.execution ? { execution: contract.execution } : {},
|
|
2190
2190
|
...contract.deliveryPolicy ? { deliveryPolicy: contract.deliveryPolicy } : {},
|
|
2191
2191
|
...contract.deliveryPathAllowlist ? { deliveryPathAllowlist: contract.deliveryPathAllowlist } : {},
|
|
2192
|
+
...contract.deliveryConfigAllowlist ? { deliveryConfigAllowlist: contract.deliveryConfigAllowlist } : {},
|
|
2192
2193
|
input: contract.input,
|
|
2193
2194
|
output: contract.output
|
|
2194
2195
|
} : {},
|
|
@@ -2229,11 +2230,18 @@ function parseCapabilityContract(raw) {
|
|
|
2229
2230
|
throw new Error('contract.json requiredSubagents are supported only when execution is "agent"');
|
|
2230
2231
|
}
|
|
2231
2232
|
const deliveryPathAllowlist = parseDeliveryPathAllowlist(parsed.deliveryPathAllowlist);
|
|
2233
|
+
const deliveryConfigAllowlist = parseDeliveryConfigAllowlist(parsed.deliveryConfigAllowlist);
|
|
2232
2234
|
if (deliveryPathAllowlist && parsed.execution !== "agent") {
|
|
2233
2235
|
throw new Error('contract.json deliveryPathAllowlist is supported only when execution is "agent"');
|
|
2234
2236
|
}
|
|
2237
|
+
if (deliveryConfigAllowlist && parsed.execution !== "agent") {
|
|
2238
|
+
throw new Error('contract.json deliveryConfigAllowlist is supported only when execution is "agent"');
|
|
2239
|
+
}
|
|
2240
|
+
if (deliveryConfigAllowlist && Object.keys(deliveryConfigAllowlist).some((filePath) => !deliveryPathAllowlist?.includes(filePath))) {
|
|
2241
|
+
throw new Error("contract.json deliveryConfigAllowlist files must also be deliveryPathAllowlist entries");
|
|
2242
|
+
}
|
|
2235
2243
|
const unsupported = Object.keys(parsed).filter(
|
|
2236
|
-
(key) => key !== "execution" && key !== "deliveryPolicy" && key !== "deliveryPathAllowlist" && key !== "requirements" && key !== "secrets" && key !== "timeoutMs" && key !== "requiredSubagents" && key !== "input" && key !== "output"
|
|
2244
|
+
(key) => key !== "execution" && key !== "deliveryPolicy" && key !== "deliveryPathAllowlist" && key !== "deliveryConfigAllowlist" && key !== "requirements" && key !== "secrets" && key !== "timeoutMs" && key !== "requiredSubagents" && key !== "input" && key !== "output"
|
|
2237
2245
|
);
|
|
2238
2246
|
if (unsupported.length > 0) {
|
|
2239
2247
|
throw new Error(`contract.json contains unsupported fields: ${unsupported.join(", ")}`);
|
|
@@ -2248,6 +2256,7 @@ function parseCapabilityContract(raw) {
|
|
|
2248
2256
|
...parsed.execution ? { execution: parsed.execution } : {},
|
|
2249
2257
|
...parsed.deliveryPolicy === "checkpoint" ? { deliveryPolicy: "checkpoint" } : {},
|
|
2250
2258
|
...deliveryPathAllowlist ? { deliveryPathAllowlist } : {},
|
|
2259
|
+
...deliveryConfigAllowlist ? { deliveryConfigAllowlist } : {},
|
|
2251
2260
|
...requirements ? { requirements } : {},
|
|
2252
2261
|
...secrets ? { secrets } : {},
|
|
2253
2262
|
...timeoutMs !== void 0 ? { timeoutMs } : {},
|
|
@@ -2256,6 +2265,23 @@ function parseCapabilityContract(raw) {
|
|
|
2256
2265
|
output: parsed.output
|
|
2257
2266
|
};
|
|
2258
2267
|
}
|
|
2268
|
+
function parseDeliveryConfigAllowlist(raw) {
|
|
2269
|
+
if (raw === void 0) return void 0;
|
|
2270
|
+
if (!isPlainObject(raw) || Object.keys(raw).length === 0) {
|
|
2271
|
+
throw new Error("contract.json deliveryConfigAllowlist must be a non-empty object");
|
|
2272
|
+
}
|
|
2273
|
+
const parsed = {};
|
|
2274
|
+
for (const [filePath, paths] of Object.entries(raw)) {
|
|
2275
|
+
if (filePath !== "kody.config.json" || !Array.isArray(paths) || paths.length === 0 || paths.length > 32) {
|
|
2276
|
+
throw new Error("contract.json deliveryConfigAllowlist contains an unsupported config file or path list");
|
|
2277
|
+
}
|
|
2278
|
+
if (!paths.every((value) => typeof value === "string" && /^[A-Za-z][A-Za-z0-9]*(?:\.[A-Za-z][A-Za-z0-9]*)*$/.test(value))) {
|
|
2279
|
+
throw new Error("contract.json deliveryConfigAllowlist must contain safe dotted config paths");
|
|
2280
|
+
}
|
|
2281
|
+
parsed[filePath] = [...new Set(paths)];
|
|
2282
|
+
}
|
|
2283
|
+
return parsed;
|
|
2284
|
+
}
|
|
2259
2285
|
function parseDeliveryPathAllowlist(raw) {
|
|
2260
2286
|
if (raw === void 0) return void 0;
|
|
2261
2287
|
if (!Array.isArray(raw) || raw.length === 0 || raw.length > 64) {
|
|
@@ -8632,6 +8658,7 @@ function git(args, cwd) {
|
|
|
8632
8658
|
return execFileSync6("git", args, {
|
|
8633
8659
|
encoding: "utf-8",
|
|
8634
8660
|
timeout: 12e4,
|
|
8661
|
+
maxBuffer: GIT_MAX_BUFFER_BYTES,
|
|
8635
8662
|
cwd,
|
|
8636
8663
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
8637
8664
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -8733,12 +8760,32 @@ function isSafeConfigActivationChange(before, after) {
|
|
|
8733
8760
|
}
|
|
8734
8761
|
return isDeepStrictEqual(beforeCompanyRest, afterCompanyRest);
|
|
8735
8762
|
}
|
|
8736
|
-
function
|
|
8763
|
+
function isSafeConfigChange(before, after, allowedPaths = []) {
|
|
8764
|
+
if (!isRecord(before) || !isRecord(after)) return false;
|
|
8765
|
+
const beforeCopy = structuredClone(before);
|
|
8766
|
+
const afterCopy = structuredClone(after);
|
|
8767
|
+
for (const dottedPath of allowedPaths) {
|
|
8768
|
+
deleteConfigPath(beforeCopy, dottedPath);
|
|
8769
|
+
deleteConfigPath(afterCopy, dottedPath);
|
|
8770
|
+
}
|
|
8771
|
+
return isSafeConfigActivationChange(beforeCopy, afterCopy);
|
|
8772
|
+
}
|
|
8773
|
+
function deleteConfigPath(value, dottedPath) {
|
|
8774
|
+
const segments = dottedPath.split(".");
|
|
8775
|
+
let cursor = value;
|
|
8776
|
+
for (const segment of segments.slice(0, -1)) {
|
|
8777
|
+
const next = cursor[segment];
|
|
8778
|
+
if (!isRecord(next)) return;
|
|
8779
|
+
cursor = next;
|
|
8780
|
+
}
|
|
8781
|
+
delete cursor[segments.at(-1)];
|
|
8782
|
+
}
|
|
8783
|
+
function isTrustedConfigActivationChange(filePath, deliveryPathAllowlist, deliveryConfigAllowlist, cwd) {
|
|
8737
8784
|
if (filePath !== "kody.config.json" || !deliveryPathAllowlist.includes(filePath)) return false;
|
|
8738
8785
|
try {
|
|
8739
8786
|
const before = JSON.parse(git(["show", "HEAD:kody.config.json"], cwd));
|
|
8740
8787
|
const after = JSON.parse(fs29.readFileSync(path28.join(cwd ?? process.cwd(), filePath), "utf-8"));
|
|
8741
|
-
return
|
|
8788
|
+
return isSafeConfigChange(before, after, deliveryConfigAllowlist[filePath] ?? []);
|
|
8742
8789
|
} catch {
|
|
8743
8790
|
return false;
|
|
8744
8791
|
}
|
|
@@ -8746,6 +8793,7 @@ function isTrustedConfigActivationChange(filePath, deliveryPathAllowlist, cwd) {
|
|
|
8746
8793
|
function listChangedFiles(cwd) {
|
|
8747
8794
|
const raw = execFileSync6("git", ["status", "--porcelain=v1", "-z", "--untracked-files=all"], {
|
|
8748
8795
|
encoding: "utf-8",
|
|
8796
|
+
maxBuffer: GIT_MAX_BUFFER_BYTES,
|
|
8749
8797
|
cwd,
|
|
8750
8798
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
8751
8799
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -8758,6 +8806,7 @@ function listAllowlistedIgnoredFiles(deliveryPathAllowlist, cwd) {
|
|
|
8758
8806
|
if (deliveryPathAllowlist.length === 0) return [];
|
|
8759
8807
|
const raw = execFileSync6("git", ["ls-files", "--others", "--ignored", "--exclude-standard", "-z"], {
|
|
8760
8808
|
encoding: "utf-8",
|
|
8809
|
+
maxBuffer: GIT_MAX_BUFFER_BYTES,
|
|
8761
8810
|
cwd,
|
|
8762
8811
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
8763
8812
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -8768,6 +8817,7 @@ function listFilesInCommit(ref = "HEAD", cwd) {
|
|
|
8768
8817
|
try {
|
|
8769
8818
|
const raw = execFileSync6("git", ["show", "--name-only", "--pretty=format:", "-z", ref], {
|
|
8770
8819
|
encoding: "utf-8",
|
|
8820
|
+
maxBuffer: GIT_MAX_BUFFER_BYTES,
|
|
8771
8821
|
cwd,
|
|
8772
8822
|
env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
|
|
8773
8823
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -8786,19 +8836,19 @@ function normalizeCommitMessage(raw) {
|
|
|
8786
8836
|
}
|
|
8787
8837
|
return `chore: ${trimmed}`;
|
|
8788
8838
|
}
|
|
8789
|
-
function commitAndPush(branch, agentMessage, cwd, deliveryPathAllowlist = []) {
|
|
8839
|
+
function commitAndPush(branch, agentMessage, cwd, deliveryPathAllowlist = [], deliveryConfigAllowlist = {}) {
|
|
8790
8840
|
const ignoredAllowedFiles = listAllowlistedIgnoredFiles(deliveryPathAllowlist, cwd);
|
|
8791
8841
|
const ignoredAllowedSet = new Set(ignoredAllowedFiles);
|
|
8792
8842
|
const allChanged = [.../* @__PURE__ */ new Set([...listChangedFiles(cwd), ...ignoredAllowedFiles])];
|
|
8793
8843
|
const allowedFiles = allChanged.filter(
|
|
8794
|
-
(f) => !isForbiddenPath(f, deliveryPathAllowlist) || isTrustedConfigActivationChange(f, deliveryPathAllowlist, cwd)
|
|
8844
|
+
(f) => !isForbiddenPath(f, deliveryPathAllowlist) || isTrustedConfigActivationChange(f, deliveryPathAllowlist, deliveryConfigAllowlist, cwd)
|
|
8795
8845
|
);
|
|
8796
8846
|
const mergeHeadExists = fs29.existsSync(path28.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
|
|
8797
8847
|
if (allowedFiles.length === 0 && !mergeHeadExists) {
|
|
8798
8848
|
return { committed: false, pushed: false, sha: "", message: "" };
|
|
8799
8849
|
}
|
|
8800
8850
|
const forbiddenFiles = allChanged.filter(
|
|
8801
|
-
(f) => isForbiddenPath(f, deliveryPathAllowlist) && !isTrustedConfigActivationChange(f, deliveryPathAllowlist, cwd)
|
|
8851
|
+
(f) => isForbiddenPath(f, deliveryPathAllowlist) && !isTrustedConfigActivationChange(f, deliveryPathAllowlist, deliveryConfigAllowlist, cwd)
|
|
8802
8852
|
);
|
|
8803
8853
|
for (const f of forbiddenFiles) {
|
|
8804
8854
|
try {
|
|
@@ -8843,11 +8893,12 @@ function hasCommitsAhead(branch, defaultBranch, cwd) {
|
|
|
8843
8893
|
}
|
|
8844
8894
|
}
|
|
8845
8895
|
}
|
|
8846
|
-
var FORBIDDEN_PATH_PREFIXES, ALLOWED_PATH_PREFIXES, FORBIDDEN_PATH_EXACT, FORBIDDEN_PATH_SUFFIXES, ACTIVATION_FIELDS, CONVENTIONAL_PREFIXES;
|
|
8896
|
+
var GIT_MAX_BUFFER_BYTES, FORBIDDEN_PATH_PREFIXES, ALLOWED_PATH_PREFIXES, FORBIDDEN_PATH_EXACT, FORBIDDEN_PATH_SUFFIXES, ACTIVATION_FIELDS, CONVENTIONAL_PREFIXES;
|
|
8847
8897
|
var init_commit = __esm({
|
|
8848
8898
|
"src/commit.ts"() {
|
|
8849
8899
|
"use strict";
|
|
8850
8900
|
init_pushWithRetry();
|
|
8901
|
+
GIT_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
8851
8902
|
FORBIDDEN_PATH_PREFIXES = [
|
|
8852
8903
|
".kody/",
|
|
8853
8904
|
// legacy consumer state must never be reintroduced or committed
|
|
@@ -12467,8 +12518,15 @@ var init_commitAndPush = __esm({
|
|
|
12467
12518
|
}
|
|
12468
12519
|
const message = ctx.data.commitMessage || DEFAULT_COMMIT_MESSAGE;
|
|
12469
12520
|
const deliveryPathAllowlist = Array.isArray(ctx.data.deliveryPathAllowlist) ? ctx.data.deliveryPathAllowlist : [];
|
|
12521
|
+
const deliveryConfigAllowlist = ctx.data.deliveryConfigAllowlist && typeof ctx.data.deliveryConfigAllowlist === "object" ? ctx.data.deliveryConfigAllowlist : {};
|
|
12470
12522
|
try {
|
|
12471
|
-
const result2 = commitAndPush(
|
|
12523
|
+
const result2 = commitAndPush(
|
|
12524
|
+
branch,
|
|
12525
|
+
message,
|
|
12526
|
+
ctx.cwd,
|
|
12527
|
+
deliveryPathAllowlist,
|
|
12528
|
+
deliveryConfigAllowlist
|
|
12529
|
+
);
|
|
12472
12530
|
ctx.data.commitResult = result2;
|
|
12473
12531
|
const postCommitFiles = result2.committed ? listFilesInCommit("HEAD", ctx.cwd) : listChangedFiles(ctx.cwd);
|
|
12474
12532
|
ctx.data.changedFiles = postCommitFiles.filter(
|
|
@@ -16451,6 +16509,9 @@ var init_loadSimpleCapability = __esm({
|
|
|
16451
16509
|
if (capability.contract?.deliveryPathAllowlist) {
|
|
16452
16510
|
ctx.data.deliveryPathAllowlist = capability.contract.deliveryPathAllowlist;
|
|
16453
16511
|
}
|
|
16512
|
+
if (capability.contract?.deliveryConfigAllowlist) {
|
|
16513
|
+
ctx.data.deliveryConfigAllowlist = capability.contract.deliveryConfigAllowlist;
|
|
16514
|
+
}
|
|
16454
16515
|
if (capability.contract?.requirements) {
|
|
16455
16516
|
ctx.data.capabilityRequirements = capability.contract.requirements;
|
|
16456
16517
|
}
|
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.586",
|
|
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",
|