@kody-ade/kody-engine 0.4.406 → 0.4.408

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.
Files changed (2) hide show
  1. package/dist/bin/kody.js +74 -17
  2. package/package.json +24 -25
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.406",
18
+ version: "0.4.408",
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",
@@ -1885,9 +1885,13 @@ var init_capabilityFolders = __esm({
1885
1885
  import * as fs5 from "fs";
1886
1886
  import * as path6 from "path";
1887
1887
  function definitionsRoot(cwd = process.cwd()) {
1888
+ const override = process.env.KODY_DEFINITIONS_ROOT?.trim();
1889
+ const overrideCwd = process.env.KODY_DEFINITIONS_ROOT_CWD?.trim();
1890
+ if (override && overrideCwd && path6.resolve(cwd) === path6.resolve(overrideCwd)) {
1891
+ return path6.resolve(override);
1892
+ }
1888
1893
  const hydrated = path6.join(cwd, ".kody-engine", "definitions");
1889
1894
  if (fs5.existsSync(hydrated)) return hydrated;
1890
- const override = process.env.KODY_DEFINITIONS_ROOT?.trim();
1891
1895
  return override ? path6.resolve(override) : hydrated;
1892
1896
  }
1893
1897
  function capabilitiesRoot(cwd = process.cwd()) {
@@ -1939,8 +1943,10 @@ function getBuiltinCapabilitiesRoot() {
1939
1943
  return candidates[0];
1940
1944
  }
1941
1945
  function getImplementationRoots() {
1942
- const projectCapabilitiesRoot = getProjectCapabilitiesRoot();
1943
- return [projectCapabilitiesRoot, getImplementationsRoot()];
1946
+ return getImplementationRootsForCwd(process.cwd());
1947
+ }
1948
+ function getImplementationRootsForCwd(cwd) {
1949
+ return [capabilitiesRoot(cwd), getImplementationsRoot()];
1944
1950
  }
1945
1951
  function getCapabilityRoots(projectCapabilitiesRoot = getProjectCapabilitiesRoot()) {
1946
1952
  return [projectCapabilitiesRoot, getBuiltinCapabilitiesRoot()];
@@ -2013,18 +2019,18 @@ function getCapabilityActionInputs(action, projectCapabilitiesRoot = getProjectC
2013
2019
  if (!resolved) return null;
2014
2020
  return getProfileInputs(resolved.implementation);
2015
2021
  }
2016
- function resolveCapabilityExecution(capability) {
2022
+ function resolveCapabilityExecution(capability, cwd = process.cwd()) {
2017
2023
  const firstWorkflowStep = capability.config.workflow?.steps[0];
2018
2024
  if (firstWorkflowStep) {
2019
2025
  const implementation2 = firstWorkflowStep.implementation ?? firstWorkflowStep.capability;
2020
2026
  return { implementation: implementation2, cliArgs: {} };
2021
2027
  }
2022
2028
  const implementation = capability.config.implementation ?? capability.config.implementations?.[0] ?? (capability.config.role ? capability.slug : void 0) ?? (capability.config.tickScript ? "capability-tick-scripted" : "capability-tick");
2023
- const cliArgs = implementationDeclaresInput(implementation, "capability") ? { capability: capability.slug } : {};
2029
+ const cliArgs = implementationDeclaresInput(implementation, "capability", cwd) ? { capability: capability.slug } : {};
2024
2030
  return { implementation, cliArgs };
2025
2031
  }
2026
- function implementationDeclaresInput(implementation, inputName) {
2027
- const profilePath = resolveImplementation(implementation);
2032
+ function implementationDeclaresInput(implementation, inputName, cwd = process.cwd()) {
2033
+ const profilePath = resolveImplementation(implementation, getImplementationRootsForCwd(cwd));
2028
2034
  if (!profilePath) return false;
2029
2035
  try {
2030
2036
  const raw = JSON.parse(fs6.readFileSync(profilePath, "utf-8"));
@@ -9285,7 +9291,7 @@ async function planGoalCapabilitySchedule(opts) {
9285
9291
  }
9286
9292
  };
9287
9293
  }
9288
- const dispatch2 = capabilityDispatch(capability);
9294
+ const dispatch2 = capabilityDispatch(capability, opts.cwd);
9289
9295
  statuses[due.slug] = markCapabilitySelected(statuses[due.slug], now);
9290
9296
  return {
9291
9297
  kind: "dispatch",
@@ -9345,8 +9351,8 @@ async function describeCapabilitySchedule(capability, slug, backend, previous) {
9345
9351
  lastFiredAt
9346
9352
  };
9347
9353
  }
9348
- function capabilityDispatch(capability) {
9349
- const { implementation, cliArgs } = resolveCapabilityExecution(capability);
9354
+ function capabilityDispatch(capability, cwd) {
9355
+ const { implementation, cliArgs } = resolveCapabilityExecution(capability, cwd);
9350
9356
  return { capability: capability.slug, implementation, cliArgs };
9351
9357
  }
9352
9358
  function compareOldestLastFired(a, b) {
@@ -11426,12 +11432,54 @@ var init_composePrompt = __esm({
11426
11432
  });
11427
11433
 
11428
11434
  // src/scripts/postReviewResult.ts
11435
+ function words(value) {
11436
+ return value.trim().split(/\s+/).filter(Boolean);
11437
+ }
11438
+ function removeForbiddenReviewSections(body) {
11439
+ const kept = [];
11440
+ let skipping = false;
11441
+ for (const line of body.split("\n")) {
11442
+ const heading = line.match(/^\s*(?:#{1,6}\s+(.+?)|\*\*(.+?)\*\*)\s*$/);
11443
+ if (heading) {
11444
+ const title = (heading[1] ?? heading[2] ?? "").replace(/[*_`]/g, "").trim();
11445
+ skipping = FORBIDDEN_REVIEW_SECTION.test(title);
11446
+ }
11447
+ if (!skipping) kept.push(line);
11448
+ }
11449
+ return kept.join("\n").replace(/\n{3,}/g, "\n\n").trim();
11450
+ }
11451
+ function prepareReviewBody(rawBody) {
11452
+ let body = rawBody.trim();
11453
+ const verdict = body.match(/(^|\n)(\s*#{1,6}\s*Verdict\s*:?\s*(?:PASS|CONCERNS|FAIL)\b)/i);
11454
+ if (verdict?.index !== void 0) {
11455
+ body = body.slice(verdict.index + verdict[1].length).trim();
11456
+ }
11457
+ body = removeForbiddenReviewSections(body);
11458
+ const bodyWords = words(body);
11459
+ if (bodyWords.length <= MAX_REVIEW_WORDS) return body;
11460
+ const noteWords = words(REVIEW_TRUNCATION_NOTE);
11461
+ const retainedWordCount = MAX_REVIEW_WORDS - noteWords.length;
11462
+ const matches = [...body.matchAll(/\S+/g)];
11463
+ const retainedEnd = matches[retainedWordCount - 1].index + matches[retainedWordCount - 1][0].length;
11464
+ return `${body.slice(0, retainedEnd).trimEnd()}
11465
+
11466
+ ${REVIEW_TRUNCATION_NOTE}`;
11467
+ }
11429
11468
  function inferVerdictFromReviewText(body) {
11430
11469
  const structuredVerdict = body.match(/"verdict"\s*:\s*"(pass|concerns|fail|partial)"/i);
11431
11470
  if (structuredVerdict) {
11432
11471
  const value = structuredVerdict[1].toUpperCase();
11433
11472
  return value === "PARTIAL" ? "CONCERNS" : value;
11434
11473
  }
11474
+ const status = body.match(
11475
+ /(^|\n)\s*(?:#{1,6}\s*)?(?:\*\*Status:\*\*|Status:)\s*(PASS|CONCERNS|FAIL|WARN|NONE|BLOCK|NEEDS_CONTEXT)\b/i
11476
+ );
11477
+ if (status) {
11478
+ const value = status[2].toUpperCase();
11479
+ if (value === "PASS" || value === "NONE") return "PASS";
11480
+ if (value === "CONCERNS" || value === "WARN") return "CONCERNS";
11481
+ return "FAIL";
11482
+ }
11435
11483
  if (/\bpartial\b/i.test(body) && /\b(finding|gap|unverified|unverifiable|blocker|issue)s?\b/i.test(body)) {
11436
11484
  return "CONCERNS";
11437
11485
  }
@@ -11474,11 +11522,14 @@ function reviewAction(verdict, payload) {
11474
11522
  function failedAction(reason) {
11475
11523
  return { type: "REVIEW_FAILED", payload: { reason }, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
11476
11524
  }
11477
- var postReviewResult;
11525
+ var MAX_REVIEW_WORDS, REVIEW_TRUNCATION_NOTE, FORBIDDEN_REVIEW_SECTION, postReviewResult;
11478
11526
  var init_postReviewResult = __esm({
11479
11527
  "src/scripts/postReviewResult.ts"() {
11480
11528
  "use strict";
11481
11529
  init_issue();
11530
+ MAX_REVIEW_WORDS = 600;
11531
+ REVIEW_TRUNCATION_NOTE = "> Review truncated to the highest-priority findings.";
11532
+ FORBIDDEN_REVIEW_SECTION = /^(?:clean\b|strengths?\b|suggest(?:ion|ed)s?\b|follow[- ]?ups?\b|verification\b|notes?\b|nits?\b|non[- ]issues?\b)/i;
11482
11533
  postReviewResult = async (ctx, _profile, agentResult) => {
11483
11534
  const prNumber = ctx.data.commentTargetNumber;
11484
11535
  if (!prNumber) {
@@ -11498,7 +11549,7 @@ var init_postReviewResult = __esm({
11498
11549
  ctx.data.action = failedAction(reason);
11499
11550
  return;
11500
11551
  }
11501
- const reviewBody = agentResult.finalText.trim();
11552
+ const reviewBody = prepareReviewBody(agentResult.finalText);
11502
11553
  if (!reviewBody) {
11503
11554
  try {
11504
11555
  postPrReviewComment(prNumber, `\u26A0\uFE0F kody review FAILED: agent produced no review body`, ctx.cwd);
@@ -19827,6 +19878,9 @@ function jobReferenceBlock(profileName, profile, data) {
19827
19878
  ];
19828
19879
  return lines.join("\n");
19829
19880
  }
19881
+ function shouldPromptForTaskArtifacts(tools) {
19882
+ return tools.some((tool6) => TASK_ARTIFACT_WRITE_TOOLS.has(tool6));
19883
+ }
19830
19884
  async function runImplementation(profileName, input) {
19831
19885
  const stageStartedAt = Date.now();
19832
19886
  let finishRunIndex = null;
@@ -19977,6 +20031,7 @@ async function runImplementation(profileName, input) {
19977
20031
  })
19978
20032
  };
19979
20033
  })() : null;
20034
+ const agentTaskArtifacts = taskArtifacts && shouldPromptForTaskArtifacts(profile.claudeCode.tools) ? taskArtifacts : null;
19980
20035
  const ndjsonDir = agentRunDir(input.cwd);
19981
20036
  const agentSlug = typeof profile.agent === "string" && profile.agent.length > 0 ? profile.agent : typeof ctx.data.jobAgent === "string" && ctx.data.jobAgent.length > 0 ? ctx.data.jobAgent : null;
19982
20037
  const agentIdentityBlock = agentSlug ? frameAgentIdentity(agentSlug, loadAgentIdentity(input.cwd, agentSlug)) : null;
@@ -20009,7 +20064,7 @@ async function runImplementation(profileName, input) {
20009
20064
  verbose: input.verbose,
20010
20065
  quiet: input.quiet,
20011
20066
  ndjsonDir,
20012
- additionalDirectories: taskArtifacts ? [taskArtifacts.absDir] : void 0,
20067
+ additionalDirectories: agentTaskArtifacts ? [agentTaskArtifacts.absDir] : void 0,
20013
20068
  allowedToolsOverride: profile.claudeCode.tools,
20014
20069
  permissionModeOverride: profile.claudeCode.permissionMode,
20015
20070
  mcpServers: profile.claudeCode.mcpServers.length > 0 ? profile.claudeCode.mcpServers : void 0,
@@ -20027,7 +20082,7 @@ async function runImplementation(profileName, input) {
20027
20082
  jobRefBlock,
20028
20083
  jobWhyBlock,
20029
20084
  profile.claudeCode.systemPromptAppend,
20030
- taskArtifacts?.promptAddendum
20085
+ agentTaskArtifacts?.promptAddendum
20031
20086
  ].filter((s) => typeof s === "string" && s.length > 0).join("\n\n") || void 0,
20032
20087
  cacheable: profile.claudeCode.cacheable,
20033
20088
  enableVerifyTool: profile.claudeCode.enableVerifyTool,
@@ -20684,7 +20739,7 @@ function flattenConfig(obj, prefix = "") {
20684
20739
  }
20685
20740
  return out;
20686
20741
  }
20687
- var MUTATING_POSTFLIGHTS, SHELL_MARKER_RE, MAX_CHAIN_HOPS, DEFAULT_SHELL_TIMEOUT_MS, SIGKILL_GRACE_MS;
20742
+ var MUTATING_POSTFLIGHTS, SHELL_MARKER_RE, TASK_ARTIFACT_WRITE_TOOLS, MAX_CHAIN_HOPS, DEFAULT_SHELL_TIMEOUT_MS, SIGKILL_GRACE_MS;
20688
20743
  var init_executor = __esm({
20689
20744
  "src/executor.ts"() {
20690
20745
  "use strict";
@@ -20717,6 +20772,7 @@ var init_executor = __esm({
20717
20772
  "openAgencyModelReviewPr"
20718
20773
  ]);
20719
20774
  SHELL_MARKER_RE = /^KODY_(SKIP_AGENT|PR_URL|REASON|CAPABILITY_REPORT|CAPABILITY_RESULT)=/m;
20775
+ TASK_ARTIFACT_WRITE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "NotebookEdit"]);
20720
20776
  MAX_CHAIN_HOPS = 60;
20721
20777
  DEFAULT_SHELL_TIMEOUT_MS = 3e5;
20722
20778
  SIGKILL_GRACE_MS = 5e3;
@@ -24346,6 +24402,7 @@ async function handleChatTurn(req, res, chatId, opts) {
24346
24402
  const storeRepoUrl = strField(body, "storeRepoUrl");
24347
24403
  const storeRef = strField(body, "storeRef");
24348
24404
  const runtime = strField(body, "runtime");
24405
+ const conversationId = strField(body, "conversationId") || chatId;
24349
24406
  const allowCrossRepo = boolField(body, "allowCrossRepo");
24350
24407
  const agentIdentity = agentIdentityField(body);
24351
24408
  let turnDriver = opts.driver;
@@ -24387,7 +24444,7 @@ async function handleChatTurn(req, res, chatId, opts) {
24387
24444
  const stateToken = repoToken || envGithubToken();
24388
24445
  const sessionFile = sessionFilePath(agentCwd, chatId);
24389
24446
  const sessionStore = opts.createStore({
24390
- sessionId: chatId,
24447
+ sessionId: conversationId,
24391
24448
  sessionFile,
24392
24449
  ...repo ? { tenantId: repo } : {}
24393
24450
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.406",
3
+ "version": "0.4.408",
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,28 +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
- "test:all": "vitest run tests --no-coverage",
29
- "typecheck": "tsc --noEmit",
30
- "lint": "biome check",
31
- "lint:fix": "biome check --write",
32
- "format": "biome format --write",
33
- "verify:package": "node scripts/verify-package-tarball.cjs",
34
- "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner",
35
- "prepublishOnly": "pnpm typecheck && vitest run tests/unit tests/int --no-coverage && pnpm build && pnpm verify:package"
36
- },
37
15
  "dependencies": {
38
16
  "@actions/cache": "^6.0.0",
39
17
  "@anthropic-ai/claude-agent-sdk": "0.2.119",
@@ -58,5 +36,26 @@
58
36
  "url": "git+https://github.com/aharonyaircohen/kody-engine.git"
59
37
  },
60
38
  "homepage": "https://github.com/aharonyaircohen/kody-engine",
61
- "bugs": "https://github.com/aharonyaircohen/kody-engine/issues"
62
- }
39
+ "bugs": "https://github.com/aharonyaircohen/kody-engine/issues",
40
+ "scripts": {
41
+ "kody:run": "tsx bin/kody.ts",
42
+ "serve": "tsx bin/kody.ts serve",
43
+ "serve:vscode": "tsx bin/kody.ts serve vscode",
44
+ "serve:claude": "tsx bin/kody.ts serve claude",
45
+ "clean:dist": "node scripts/clean-dist.cjs",
46
+ "build": "pnpm clean:dist && tsup && node scripts/copy-assets.cjs",
47
+ "check:modularity": "tsx scripts/check-script-modularity.ts",
48
+ "pretest": "pnpm check:modularity",
49
+ "test": "vitest run tests/unit tests/int --coverage",
50
+ "posttest": "tsx scripts/check-coverage-floor.ts",
51
+ "test:smoke": "vitest run tests/smoke --no-coverage",
52
+ "test:e2e": "vitest run tests/e2e --no-coverage",
53
+ "test:all": "vitest run tests --no-coverage",
54
+ "typecheck": "tsc --noEmit",
55
+ "lint": "biome check",
56
+ "lint:fix": "biome check --write",
57
+ "format": "biome format --write",
58
+ "verify:package": "node scripts/verify-package-tarball.cjs",
59
+ "brain:publish": "docker buildx build --platform linux/amd64 -f runner/Dockerfile.brain -t ghcr.io/${KODY_BRAIN_GHCR_OWNER:-aharonyaircohen}/kody-brain:latest --push runner"
60
+ }
61
+ }