@kody-ade/kody-engine 0.4.580 → 0.4.582

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.580",
18
+ version: "0.4.582",
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",
@@ -8620,6 +8620,7 @@ var init_pushWithRetry = __esm({
8620
8620
 
8621
8621
  // src/commit.ts
8622
8622
  import { execFileSync as execFileSync6 } from "child_process";
8623
+ import { isDeepStrictEqual } from "util";
8623
8624
  import * as fs29 from "fs";
8624
8625
  import * as path28 from "path";
8625
8626
  function isGitHubYamlPath(filePath) {
@@ -8703,6 +8704,37 @@ function isForbiddenPath(p, deliveryPathAllowlist = []) {
8703
8704
  for (const pre of ALLOWED_PATH_PREFIXES) if (p.startsWith(pre)) return false;
8704
8705
  return false;
8705
8706
  }
8707
+ function isRecord(value) {
8708
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8709
+ }
8710
+ function isSafeConfigActivationChange(before, after) {
8711
+ if (!isRecord(before) || !isRecord(after)) return false;
8712
+ const beforeRest = { ...before };
8713
+ const afterRest = { ...after };
8714
+ for (const field of ACTIVATION_FIELDS) {
8715
+ const previous = before[field];
8716
+ const next = after[field];
8717
+ delete beforeRest[field];
8718
+ delete afterRest[field];
8719
+ if (previous === void 0 && next === void 0) continue;
8720
+ if (!Array.isArray(previous ?? []) || !Array.isArray(next)) return false;
8721
+ if (!next.every((value) => typeof value === "string")) return false;
8722
+ if (!previous.every((value) => typeof value === "string")) return false;
8723
+ const nextValues = new Set(next);
8724
+ if (!previous.every((value) => nextValues.has(value))) return false;
8725
+ }
8726
+ return isDeepStrictEqual(beforeRest, afterRest);
8727
+ }
8728
+ function isTrustedConfigActivationChange(filePath, deliveryPathAllowlist, cwd) {
8729
+ if (filePath !== "kody.config.json" || !deliveryPathAllowlist.includes(filePath)) return false;
8730
+ try {
8731
+ const before = JSON.parse(git(["show", "HEAD:kody.config.json"], cwd));
8732
+ const after = JSON.parse(fs29.readFileSync(path28.join(cwd ?? process.cwd(), filePath), "utf-8"));
8733
+ return isSafeConfigActivationChange(before, after);
8734
+ } catch {
8735
+ return false;
8736
+ }
8737
+ }
8706
8738
  function listChangedFiles(cwd) {
8707
8739
  const raw = execFileSync6("git", ["status", "--porcelain=v1", "-z", "--untracked-files=all"], {
8708
8740
  encoding: "utf-8",
@@ -8738,12 +8770,16 @@ function normalizeCommitMessage(raw) {
8738
8770
  }
8739
8771
  function commitAndPush(branch, agentMessage, cwd, deliveryPathAllowlist = []) {
8740
8772
  const allChanged = listChangedFiles(cwd);
8741
- const allowedFiles = allChanged.filter((f) => !isForbiddenPath(f, deliveryPathAllowlist));
8773
+ const allowedFiles = allChanged.filter(
8774
+ (f) => !isForbiddenPath(f, deliveryPathAllowlist) || isTrustedConfigActivationChange(f, deliveryPathAllowlist, cwd)
8775
+ );
8742
8776
  const mergeHeadExists = fs29.existsSync(path28.join(cwd ?? process.cwd(), ".git", "MERGE_HEAD"));
8743
8777
  if (allowedFiles.length === 0 && !mergeHeadExists) {
8744
8778
  return { committed: false, pushed: false, sha: "", message: "" };
8745
8779
  }
8746
- const forbiddenFiles = allChanged.filter((f) => isForbiddenPath(f, deliveryPathAllowlist));
8780
+ const forbiddenFiles = allChanged.filter(
8781
+ (f) => isForbiddenPath(f, deliveryPathAllowlist) && !isTrustedConfigActivationChange(f, deliveryPathAllowlist, cwd)
8782
+ );
8747
8783
  for (const f of forbiddenFiles) {
8748
8784
  try {
8749
8785
  git(["reset", "-q", "--", f], cwd);
@@ -8787,7 +8823,7 @@ function hasCommitsAhead(branch, defaultBranch, cwd) {
8787
8823
  }
8788
8824
  }
8789
8825
  }
8790
- var FORBIDDEN_PATH_PREFIXES, ALLOWED_PATH_PREFIXES, FORBIDDEN_PATH_EXACT, FORBIDDEN_PATH_SUFFIXES, CONVENTIONAL_PREFIXES;
8826
+ var FORBIDDEN_PATH_PREFIXES, ALLOWED_PATH_PREFIXES, FORBIDDEN_PATH_EXACT, FORBIDDEN_PATH_SUFFIXES, ACTIVATION_FIELDS, CONVENTIONAL_PREFIXES;
8791
8827
  var init_commit = __esm({
8792
8828
  "src/commit.ts"() {
8793
8829
  "use strict";
@@ -8810,6 +8846,14 @@ var init_commit = __esm({
8810
8846
  ALLOWED_PATH_PREFIXES = [];
8811
8847
  FORBIDDEN_PATH_EXACT = /* @__PURE__ */ new Set([".env", ".kody-pip-requirements.txt", "kody.config.json"]);
8812
8848
  FORBIDDEN_PATH_SUFFIXES = [".log"];
8849
+ ACTIVATION_FIELDS = [
8850
+ "activeAgents",
8851
+ "activeCapabilities",
8852
+ "activeWorkflows",
8853
+ "activePipelines",
8854
+ "activeCommands",
8855
+ "activeFeatures"
8856
+ ];
8813
8857
  CONVENTIONAL_PREFIXES = [
8814
8858
  "feat:",
8815
8859
  "fix:",
@@ -8844,7 +8888,7 @@ var init_abortUnfinishedGitOps = __esm({
8844
8888
  });
8845
8889
 
8846
8890
  // src/scripts/deliveryOutcome.ts
8847
- function isRecord(input) {
8891
+ function isRecord2(input) {
8848
8892
  return !!input && typeof input === "object" && !Array.isArray(input);
8849
8893
  }
8850
8894
  function setDeliveryNotRequired(data, reason) {
@@ -8852,7 +8896,7 @@ function setDeliveryNotRequired(data, reason) {
8852
8896
  }
8853
8897
  function readDeliveryOutcome(data) {
8854
8898
  const raw = data.deliveryOutcome;
8855
- if (!isRecord(raw)) return null;
8899
+ if (!isRecord2(raw)) return null;
8856
8900
  if (raw.kind !== "not_required" || typeof raw.reason !== "string" || raw.reason.length === 0) return null;
8857
8901
  return { kind: "not_required", reason: raw.reason };
8858
8902
  }
@@ -7,7 +7,7 @@
7
7
  "hooks": [
8
8
  {
9
9
  "type": "command",
10
- "command": "node -e 'let s=\"\";process.stdin.on(\"data\",c=>s+=c).on(\"end\",()=>{try{const d=JSON.parse(s);const cmd=(d.tool_input&&d.tool_input.command)||\"\";const wrap=new Set([\"command\",\"exec\",\"xargs\",\"env\",\"nohup\",\"time\",\"nice\",\"sudo\",\"doas\"]);const readGit=new Set([\"status\",\"log\",\"diff\",\"show\",\"rev-parse\",\"ls-files\"]);const blocked=cmd.split(/[;&|\\n]+/).some(p=>{const t=p.trim().split(/\\s+/).filter(Boolean);let i=0;while(i<t.length&&(wrap.has(t[i])||/^[A-Za-z_][A-Za-z0-9_]*=/.test(t[i])))i++;const tool=(t[i]||\"\").split(\"/\").pop();if(tool===\"gh\")return true;if(tool!==\"git\")return false;return !readGit.has(t[i+1])});const shc=/\\b(?:ba|z|da|k)?sh\\s+[^;|&]*-c\\s*[\"\\x27`]+\\s*(?:\\S*\\/)?(?:git|gh)\\b/.test(cmd);const sub=/[$`]\\(?\\s*(?:\\S*\\/)?(?:git|gh)\\s/.test(cmd);if(blocked||shc||sub){process.stderr.write(\"kody blocks Git and GitHub writes — the delivery wrapper owns VCS mutations\\n\");process.exit(2)}}catch{}})'"
10
+ "command": "node -e 'let s=\"\";process.stdin.on(\"data\",c=>s+=c).on(\"end\",()=>{try{const d=JSON.parse(s);const cmd=(d.tool_input&&d.tool_input.command)||\"\";const wrap=new Set([\"command\",\"exec\",\"xargs\",\"env\",\"nohup\",\"time\",\"nice\",\"sudo\",\"doas\"]);const readGit=new Set([\"status\",\"log\",\"diff\",\"show\",\"rev-parse\",\"ls-files\"]);const blocked=cmd.split(/[;&|\\n]+/).some(p=>{const t=p.trim().split(/\\s+/).filter(Boolean);let i=0;while(i<t.length&&(wrap.has(t[i])||/^[A-Za-z_][A-Za-z0-9_]*=/.test(t[i])))i++;const tool=(t[i]||\"\").split(\"/\").pop();if(tool===\"gh\")return true;if(tool!==\"git\")return false;return !readGit.has(t[i+1])});const shc=/\\b(?:ba|z|da|k)?sh\\s+[^;|&]*-c\\s*[\"\\x27`]+\\s*(?:\\S*\\/)?(?:git|gh)\\b/.test(cmd);const sub=/[$`]\\(?\\s*(?:\\S*\\/)?(?:git|gh)\\s/.test(cmd);if(blocked||shc||sub){process.stderr.write(\"kody blocks Git and GitHub writes — the delivery wrapper owns VCS mutations. Do not treat this expected block as a task failure; the delivery wrapper will commit and create the pull request. Continue verification and finish your result normally.\\n\");process.exit(2)}}catch{}})'"
11
11
  }
12
12
  ]
13
13
  }
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.580",
3
+ "version": "0.4.582",
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,30 +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
- "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
- "verify:live-release": "tsx scripts/live-release-gate.ts",
29
- "test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
30
- "test:all": "vitest run tests --no-coverage",
31
- "typecheck": "tsc --noEmit",
32
- "lint": "biome check",
33
- "lint:fix": "biome check --write",
34
- "format": "biome format --write",
35
- "verify:package": "node scripts/verify-package-tarball.cjs",
36
- "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",
37
- "prepublishOnly": "pnpm typecheck && pnpm test:runtime-services && pnpm build && pnpm verify:package"
38
- },
39
15
  "dependencies": {
40
16
  "@actions/cache": "^6.0.0",
41
17
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
@@ -62,5 +38,28 @@
62
38
  "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
63
39
  },
64
40
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
65
- "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
66
- }
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
+ "verify:live-release": "tsx scripts/live-release-gate.ts",
56
+ "test:runtime-services": "node --test \"tests/runtime-services/*.test.mjs\"",
57
+ "test:all": "vitest run tests --no-coverage",
58
+ "typecheck": "tsc --noEmit",
59
+ "lint": "biome check",
60
+ "lint:fix": "biome check --write",
61
+ "format": "biome format --write",
62
+ "verify:package": "node scripts/verify-package-tarball.cjs",
63
+ "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"
64
+ }
65
+ }