@kody-ade/kody-engine 0.4.407 → 0.4.409

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 +92 -23
  2. 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.407",
18
+ version: "0.4.409",
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",
@@ -2990,6 +2990,17 @@ var init_capabilityMcp = __esm({
2990
2990
  import { spawn as spawn2, spawnSync } from "child_process";
2991
2991
  import * as fs7 from "fs";
2992
2992
  import * as path8 from "path";
2993
+ function buildCloneProcess(repo, token, baseEnv = process.env) {
2994
+ const url = `https://github.com/${repo}.git`;
2995
+ const env = { ...baseEnv };
2996
+ if (!token) return { url, env };
2997
+ const parsedCount = Number.parseInt(env.GIT_CONFIG_COUNT ?? "0", 10);
2998
+ const count = Number.isInteger(parsedCount) && parsedCount >= 0 ? parsedCount : 0;
2999
+ env.GIT_CONFIG_COUNT = String(count + 1);
3000
+ env[`GIT_CONFIG_KEY_${count}`] = "http.https://github.com/.extraHeader";
3001
+ env[`GIT_CONFIG_VALUE_${count}`] = `Authorization: Basic ${Buffer.from(`x-access-token:${token}`).toString("base64")}`;
3002
+ return { url, env };
3003
+ }
2993
3004
  async function resolveAndClone(reposRoot, repo, repoToken, cloneRepo) {
2994
3005
  const name = repo?.trim();
2995
3006
  if (!name || !REPO_RE.test(name)) return null;
@@ -3020,17 +3031,19 @@ async function fetchRepo(opts) {
3020
3031
  }
3021
3032
  return dir;
3022
3033
  }
3023
- var REPO_RE, repoClones, defaultCloneRepo;
3034
+ var REPO_RE, repoClones, GIT_CREDENTIAL_HELPER, defaultCloneRepo;
3024
3035
  var init_repoWorkspace = __esm({
3025
3036
  "src/repoWorkspace.ts"() {
3026
3037
  "use strict";
3027
3038
  REPO_RE = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
3028
3039
  repoClones = /* @__PURE__ */ new Map();
3040
+ GIT_CREDENTIAL_HELPER = `!f() { if [ "$1" = get ] && [ -n "$GITHUB_TOKEN" ]; then printf '%s\\n' username=x-access-token "password=$GITHUB_TOKEN"; fi; }; f`;
3029
3041
  defaultCloneRepo = (repo, token, dir) => {
3030
3042
  fs7.mkdirSync(path8.dirname(dir), { recursive: true });
3031
- const authUrl = token ? `https://x-access-token:${token}@github.com/${repo}.git` : `https://github.com/${repo}.git`;
3043
+ const clone = buildCloneProcess(repo, token);
3032
3044
  return new Promise((resolve16, reject) => {
3033
- const child = spawn2("git", ["clone", "--depth=1", authUrl, dir], {
3045
+ const child = spawn2("git", ["clone", "--depth=1", clone.url, dir], {
3046
+ env: clone.env,
3034
3047
  stdio: "inherit"
3035
3048
  });
3036
3049
  child.on("exit", (code) => {
@@ -3043,6 +3056,9 @@ var init_repoWorkspace = __esm({
3043
3056
  const email = process.env.GIT_AUTHOR_EMAIL ?? "kody-bot@users.noreply.github.com";
3044
3057
  spawnSync("git", ["-C", dir, "config", "user.name", name]);
3045
3058
  spawnSync("git", ["-C", dir, "config", "user.email", email]);
3059
+ if (token) {
3060
+ spawnSync("git", ["-C", dir, "config", "credential.helper", GIT_CREDENTIAL_HELPER]);
3061
+ }
3046
3062
  } catch {
3047
3063
  }
3048
3064
  resolve16();
@@ -3169,24 +3185,26 @@ function stripAgentSecrets(env) {
3169
3185
  }
3170
3186
  return out;
3171
3187
  }
3172
- async function runAgent(opts) {
3173
- const ndjsonDir = opts.ndjsonDir ?? agentRunDir(opts.cwd);
3174
- fs8.mkdirSync(ndjsonDir, { recursive: true });
3175
- const ndjsonPath = path9.join(ndjsonDir, "last-run.jsonl");
3188
+ function buildAgentEnvironment(baseEnv, repoToken) {
3176
3189
  const env = stripAgentSecrets({
3177
- ...process.env,
3190
+ ...baseEnv,
3178
3191
  SKIP_HOOKS: "1",
3179
3192
  HUSKY: "0",
3180
- CI: process.env.CI ?? "1",
3181
- // MCP servers are spawned asynchronously by the SDK. With the default
3182
- // non-blocking behavior, the SDK announces its tool list at session
3183
- // init while servers are still in `pending`, so their tools never
3184
- // reach the model. Block until each MCP completes its handshake (or
3185
- // the timeout below elapses) so the tool list is complete on first
3186
- // turn.
3187
- MCP_CONNECTION_NONBLOCKING: process.env.MCP_CONNECTION_NONBLOCKING ?? "false",
3188
- MCP_TIMEOUT: process.env.MCP_TIMEOUT ?? "60000"
3193
+ CI: baseEnv.CI ?? "1",
3194
+ MCP_CONNECTION_NONBLOCKING: baseEnv.MCP_CONNECTION_NONBLOCKING ?? "false",
3195
+ MCP_TIMEOUT: baseEnv.MCP_TIMEOUT ?? "60000"
3189
3196
  });
3197
+ if (repoToken) {
3198
+ env.GITHUB_TOKEN = repoToken;
3199
+ env.GH_TOKEN = repoToken;
3200
+ }
3201
+ return env;
3202
+ }
3203
+ async function runAgent(opts) {
3204
+ const ndjsonDir = opts.ndjsonDir ?? agentRunDir(opts.cwd);
3205
+ fs8.mkdirSync(ndjsonDir, { recursive: true });
3206
+ const ndjsonPath = path9.join(ndjsonDir, "last-run.jsonl");
3207
+ const env = buildAgentEnvironment(process.env, opts.repoToken);
3190
3208
  if (opts.litellmUrl) {
3191
3209
  env.ANTHROPIC_BASE_URL = opts.litellmUrl;
3192
3210
  env.ANTHROPIC_API_KEY = getAnthropicApiKeyOrDummy();
@@ -11432,12 +11450,54 @@ var init_composePrompt = __esm({
11432
11450
  });
11433
11451
 
11434
11452
  // src/scripts/postReviewResult.ts
11453
+ function words(value) {
11454
+ return value.trim().split(/\s+/).filter(Boolean);
11455
+ }
11456
+ function removeForbiddenReviewSections(body) {
11457
+ const kept = [];
11458
+ let skipping = false;
11459
+ for (const line of body.split("\n")) {
11460
+ const heading = line.match(/^\s*(?:#{1,6}\s+(.+?)|\*\*(.+?)\*\*)\s*$/);
11461
+ if (heading) {
11462
+ const title = (heading[1] ?? heading[2] ?? "").replace(/[*_`]/g, "").trim();
11463
+ skipping = FORBIDDEN_REVIEW_SECTION.test(title);
11464
+ }
11465
+ if (!skipping) kept.push(line);
11466
+ }
11467
+ return kept.join("\n").replace(/\n{3,}/g, "\n\n").trim();
11468
+ }
11469
+ function prepareReviewBody(rawBody) {
11470
+ let body = rawBody.trim();
11471
+ const verdict = body.match(/(^|\n)(\s*#{1,6}\s*Verdict\s*:?\s*(?:PASS|CONCERNS|FAIL)\b)/i);
11472
+ if (verdict?.index !== void 0) {
11473
+ body = body.slice(verdict.index + verdict[1].length).trim();
11474
+ }
11475
+ body = removeForbiddenReviewSections(body);
11476
+ const bodyWords = words(body);
11477
+ if (bodyWords.length <= MAX_REVIEW_WORDS) return body;
11478
+ const noteWords = words(REVIEW_TRUNCATION_NOTE);
11479
+ const retainedWordCount = MAX_REVIEW_WORDS - noteWords.length;
11480
+ const matches = [...body.matchAll(/\S+/g)];
11481
+ const retainedEnd = matches[retainedWordCount - 1].index + matches[retainedWordCount - 1][0].length;
11482
+ return `${body.slice(0, retainedEnd).trimEnd()}
11483
+
11484
+ ${REVIEW_TRUNCATION_NOTE}`;
11485
+ }
11435
11486
  function inferVerdictFromReviewText(body) {
11436
11487
  const structuredVerdict = body.match(/"verdict"\s*:\s*"(pass|concerns|fail|partial)"/i);
11437
11488
  if (structuredVerdict) {
11438
11489
  const value = structuredVerdict[1].toUpperCase();
11439
11490
  return value === "PARTIAL" ? "CONCERNS" : value;
11440
11491
  }
11492
+ const status = body.match(
11493
+ /(^|\n)\s*(?:#{1,6}\s*)?(?:\*\*Status:\*\*|Status:)\s*(PASS|CONCERNS|FAIL|WARN|NONE|BLOCK|NEEDS_CONTEXT)\b/i
11494
+ );
11495
+ if (status) {
11496
+ const value = status[2].toUpperCase();
11497
+ if (value === "PASS" || value === "NONE") return "PASS";
11498
+ if (value === "CONCERNS" || value === "WARN") return "CONCERNS";
11499
+ return "FAIL";
11500
+ }
11441
11501
  if (/\bpartial\b/i.test(body) && /\b(finding|gap|unverified|unverifiable|blocker|issue)s?\b/i.test(body)) {
11442
11502
  return "CONCERNS";
11443
11503
  }
@@ -11480,11 +11540,14 @@ function reviewAction(verdict, payload) {
11480
11540
  function failedAction(reason) {
11481
11541
  return { type: "REVIEW_FAILED", payload: { reason }, timestamp: (/* @__PURE__ */ new Date()).toISOString() };
11482
11542
  }
11483
- var postReviewResult;
11543
+ var MAX_REVIEW_WORDS, REVIEW_TRUNCATION_NOTE, FORBIDDEN_REVIEW_SECTION, postReviewResult;
11484
11544
  var init_postReviewResult = __esm({
11485
11545
  "src/scripts/postReviewResult.ts"() {
11486
11546
  "use strict";
11487
11547
  init_issue();
11548
+ MAX_REVIEW_WORDS = 600;
11549
+ REVIEW_TRUNCATION_NOTE = "> Review truncated to the highest-priority findings.";
11550
+ 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;
11488
11551
  postReviewResult = async (ctx, _profile, agentResult) => {
11489
11552
  const prNumber = ctx.data.commentTargetNumber;
11490
11553
  if (!prNumber) {
@@ -11504,7 +11567,7 @@ var init_postReviewResult = __esm({
11504
11567
  ctx.data.action = failedAction(reason);
11505
11568
  return;
11506
11569
  }
11507
- const reviewBody = agentResult.finalText.trim();
11570
+ const reviewBody = prepareReviewBody(agentResult.finalText);
11508
11571
  if (!reviewBody) {
11509
11572
  try {
11510
11573
  postPrReviewComment(prNumber, `\u26A0\uFE0F kody review FAILED: agent produced no review body`, ctx.cwd);
@@ -19833,6 +19896,9 @@ function jobReferenceBlock(profileName, profile, data) {
19833
19896
  ];
19834
19897
  return lines.join("\n");
19835
19898
  }
19899
+ function shouldPromptForTaskArtifacts(tools) {
19900
+ return tools.some((tool6) => TASK_ARTIFACT_WRITE_TOOLS.has(tool6));
19901
+ }
19836
19902
  async function runImplementation(profileName, input) {
19837
19903
  const stageStartedAt = Date.now();
19838
19904
  let finishRunIndex = null;
@@ -19983,6 +20049,7 @@ async function runImplementation(profileName, input) {
19983
20049
  })
19984
20050
  };
19985
20051
  })() : null;
20052
+ const agentTaskArtifacts = taskArtifacts && shouldPromptForTaskArtifacts(profile.claudeCode.tools) ? taskArtifacts : null;
19986
20053
  const ndjsonDir = agentRunDir(input.cwd);
19987
20054
  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;
19988
20055
  const agentIdentityBlock = agentSlug ? frameAgentIdentity(agentSlug, loadAgentIdentity(input.cwd, agentSlug)) : null;
@@ -20015,7 +20082,7 @@ async function runImplementation(profileName, input) {
20015
20082
  verbose: input.verbose,
20016
20083
  quiet: input.quiet,
20017
20084
  ndjsonDir,
20018
- additionalDirectories: taskArtifacts ? [taskArtifacts.absDir] : void 0,
20085
+ additionalDirectories: agentTaskArtifacts ? [agentTaskArtifacts.absDir] : void 0,
20019
20086
  allowedToolsOverride: profile.claudeCode.tools,
20020
20087
  permissionModeOverride: profile.claudeCode.permissionMode,
20021
20088
  mcpServers: profile.claudeCode.mcpServers.length > 0 ? profile.claudeCode.mcpServers : void 0,
@@ -20033,7 +20100,7 @@ async function runImplementation(profileName, input) {
20033
20100
  jobRefBlock,
20034
20101
  jobWhyBlock,
20035
20102
  profile.claudeCode.systemPromptAppend,
20036
- taskArtifacts?.promptAddendum
20103
+ agentTaskArtifacts?.promptAddendum
20037
20104
  ].filter((s) => typeof s === "string" && s.length > 0).join("\n\n") || void 0,
20038
20105
  cacheable: profile.claudeCode.cacheable,
20039
20106
  enableVerifyTool: profile.claudeCode.enableVerifyTool,
@@ -20690,7 +20757,7 @@ function flattenConfig(obj, prefix = "") {
20690
20757
  }
20691
20758
  return out;
20692
20759
  }
20693
- var MUTATING_POSTFLIGHTS, SHELL_MARKER_RE, MAX_CHAIN_HOPS, DEFAULT_SHELL_TIMEOUT_MS, SIGKILL_GRACE_MS;
20760
+ var MUTATING_POSTFLIGHTS, SHELL_MARKER_RE, TASK_ARTIFACT_WRITE_TOOLS, MAX_CHAIN_HOPS, DEFAULT_SHELL_TIMEOUT_MS, SIGKILL_GRACE_MS;
20694
20761
  var init_executor = __esm({
20695
20762
  "src/executor.ts"() {
20696
20763
  "use strict";
@@ -20723,6 +20790,7 @@ var init_executor = __esm({
20723
20790
  "openAgencyModelReviewPr"
20724
20791
  ]);
20725
20792
  SHELL_MARKER_RE = /^KODY_(SKIP_AGENT|PR_URL|REASON|CAPABILITY_REPORT|CAPABILITY_RESULT)=/m;
20793
+ TASK_ARTIFACT_WRITE_TOOLS = /* @__PURE__ */ new Set(["Write", "Edit", "NotebookEdit"]);
20726
20794
  MAX_CHAIN_HOPS = 60;
20727
20795
  DEFAULT_SHELL_TIMEOUT_MS = 3e5;
20728
20796
  SIGKILL_GRACE_MS = 5e3;
@@ -22321,6 +22389,7 @@ async function runChatTurn(opts) {
22321
22389
  ],
22322
22390
  systemPromptAppend: systemPrompt,
22323
22391
  ...opts.reasoningEffort ? { reasoningEffort: opts.reasoningEffort } : {},
22392
+ ...opts.repoToken ? { repoToken: opts.repoToken } : {},
22324
22393
  // Cross-repo work is opt-in. Repo Brain's default path remains focused
22325
22394
  // on the selected repo even though the server stores clones under
22326
22395
  // reposRoot.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.407",
3
+ "version": "0.4.409",
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",