@kody-ade/kody-engine-lite 0.1.127 → 0.1.129

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/cli.js CHANGED
@@ -180,8 +180,8 @@ var init_validators = __esm({
180
180
  });
181
181
 
182
182
  // src/config.ts
183
- import * as fs8 from "fs";
184
- import * as path7 from "path";
183
+ import * as fs7 from "fs";
184
+ import * as path6 from "path";
185
185
  function resolveStageConfig(config, stageName, modelTier) {
186
186
  const stageOverride = config.agent.stages?.[stageName];
187
187
  if (stageOverride) return stageOverride;
@@ -225,10 +225,10 @@ function setConfigDir(dir) {
225
225
  }
226
226
  function getProjectConfig() {
227
227
  if (_config) return _config;
228
- const configPath = path7.join(_configDir ?? process.cwd(), "kody.config.json");
229
- if (fs8.existsSync(configPath)) {
228
+ const configPath = path6.join(_configDir ?? process.cwd(), "kody.config.json");
229
+ if (fs7.existsSync(configPath)) {
230
230
  try {
231
- const result2 = parseJsonSafe(fs8.readFileSync(configPath, "utf-8"));
231
+ const result2 = parseJsonSafe(fs7.readFileSync(configPath, "utf-8"));
232
232
  if (!result2.ok) {
233
233
  logger.warn(`kody.config.json: ${result2.error} \u2014 using defaults`);
234
234
  _config = { ...DEFAULT_CONFIG };
@@ -302,24 +302,24 @@ var init_config = __esm({
302
302
  });
303
303
 
304
304
  // src/agent-runner.ts
305
- import { spawn, execFileSync as execFileSync6 } from "child_process";
305
+ import { spawn, execFileSync as execFileSync5 } from "child_process";
306
306
  function writeStdin(child, prompt) {
307
- return new Promise((resolve6, reject) => {
307
+ return new Promise((resolve5, reject) => {
308
308
  if (!child.stdin) {
309
- resolve6();
309
+ resolve5();
310
310
  return;
311
311
  }
312
312
  child.stdin.write(prompt, (err) => {
313
313
  if (err) reject(err);
314
314
  else {
315
315
  child.stdin.end();
316
- resolve6();
316
+ resolve5();
317
317
  }
318
318
  });
319
319
  });
320
320
  }
321
321
  function waitForProcess(child, timeout) {
322
- return new Promise((resolve6) => {
322
+ return new Promise((resolve5) => {
323
323
  const stdoutChunks = [];
324
324
  const stderrChunks = [];
325
325
  child.stdout?.on("data", (chunk) => stdoutChunks.push(chunk));
@@ -332,7 +332,7 @@ function waitForProcess(child, timeout) {
332
332
  }, timeout);
333
333
  child.on("exit", (code) => {
334
334
  clearTimeout(timer);
335
- resolve6({
335
+ resolve5({
336
336
  code,
337
337
  stdout: Buffer.concat(stdoutChunks).toString(),
338
338
  stderr: Buffer.concat(stderrChunks).toString()
@@ -340,7 +340,7 @@ function waitForProcess(child, timeout) {
340
340
  });
341
341
  child.on("error", (err) => {
342
342
  clearTimeout(timer);
343
- resolve6({ code: -1, stdout: "", stderr: err.message });
343
+ resolve5({ code: -1, stdout: "", stderr: err.message });
344
344
  });
345
345
  });
346
346
  }
@@ -376,7 +376,7 @@ ${errDetail}`
376
376
  }
377
377
  function checkCommand2(command2, args2) {
378
378
  try {
379
- execFileSync6(command2, args2, { timeout: 1e4, stdio: "pipe" });
379
+ execFileSync5(command2, args2, { timeout: 1e4, stdio: "pipe" });
380
380
  return true;
381
381
  } catch {
382
382
  return false;
@@ -521,7 +521,7 @@ var init_mcp_config = __esm({
521
521
  });
522
522
 
523
523
  // src/github-api.ts
524
- import { execFileSync as execFileSync7 } from "child_process";
524
+ import { execFileSync as execFileSync6 } from "child_process";
525
525
  function isGhExecError(err) {
526
526
  return typeof err === "object" && err !== null;
527
527
  }
@@ -545,7 +545,7 @@ function ghToken() {
545
545
  function gh(args2, options) {
546
546
  const token = ghToken();
547
547
  const env = token ? { ...process.env, GH_TOKEN: token } : { ...process.env };
548
- return execFileSync7("gh", args2, {
548
+ return execFileSync6("gh", args2, {
549
549
  encoding: "utf-8",
550
550
  timeout: API_TIMEOUT_MS,
551
551
  cwd: _ghCwd,
@@ -949,18 +949,18 @@ var init_github_api = __esm({
949
949
  });
950
950
 
951
951
  // src/cli/task-resolution.ts
952
- import * as fs10 from "fs";
953
- import * as path9 from "path";
954
- import { execFileSync as execFileSync8 } from "child_process";
952
+ import * as fs9 from "fs";
953
+ import * as path8 from "path";
954
+ import { execFileSync as execFileSync7 } from "child_process";
955
955
  function findLatestTaskForIssue(issueNumber, projectDir) {
956
- const tasksDir = path9.join(projectDir, ".kody", "tasks");
957
- if (!fs10.existsSync(tasksDir)) return null;
958
- const allDirs = fs10.readdirSync(tasksDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort().reverse();
956
+ const tasksDir = path8.join(projectDir, ".kody", "tasks");
957
+ if (!fs9.existsSync(tasksDir)) return null;
958
+ const allDirs = fs9.readdirSync(tasksDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort().reverse();
959
959
  const prefix = `${issueNumber}-`;
960
960
  const direct = allDirs.find((d) => d.startsWith(prefix));
961
961
  if (direct) return direct;
962
962
  try {
963
- const branch = execFileSync8("git", ["branch", "--show-current"], {
963
+ const branch = execFileSync7("git", ["branch", "--show-current"], {
964
964
  encoding: "utf-8",
965
965
  cwd: projectDir,
966
966
  timeout: 5e3,
@@ -999,14 +999,14 @@ function resolveTaskIdFromComments(issueNumber) {
999
999
  }
1000
1000
  }
1001
1001
  function findPausedTaskifyForIssue(issueNumber, projectDir) {
1002
- const tasksDir = path9.join(projectDir, ".kody", "tasks");
1003
- if (!fs10.existsSync(tasksDir)) return null;
1004
- const allDirs = fs10.readdirSync(tasksDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort().reverse();
1002
+ const tasksDir = path8.join(projectDir, ".kody", "tasks");
1003
+ if (!fs9.existsSync(tasksDir)) return null;
1004
+ const allDirs = fs9.readdirSync(tasksDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort().reverse();
1005
1005
  for (const dir of allDirs) {
1006
- const markerPath = path9.join(tasksDir, dir, "taskify.marker");
1007
- if (!fs10.existsSync(markerPath)) continue;
1006
+ const markerPath = path8.join(tasksDir, dir, "taskify.marker");
1007
+ if (!fs9.existsSync(markerPath)) continue;
1008
1008
  try {
1009
- const marker = JSON.parse(fs10.readFileSync(markerPath, "utf-8"));
1009
+ const marker = JSON.parse(fs9.readFileSync(markerPath, "utf-8"));
1010
1010
  if (marker.issueNumber === issueNumber) return dir;
1011
1011
  } catch {
1012
1012
  }
@@ -1030,10 +1030,10 @@ var init_task_resolution = __esm({
1030
1030
  });
1031
1031
 
1032
1032
  // src/cli/litellm.ts
1033
- import * as fs11 from "fs";
1033
+ import * as fs10 from "fs";
1034
1034
  import * as os from "os";
1035
- import * as path10 from "path";
1036
- import { execFileSync as execFileSync9 } from "child_process";
1035
+ import * as path9 from "path";
1036
+ import { execFileSync as execFileSync8 } from "child_process";
1037
1037
  async function checkLitellmHealth(url) {
1038
1038
  try {
1039
1039
  const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3e3) });
@@ -1132,17 +1132,17 @@ async function tryStartLitellm(url, projectDir, generatedConfig) {
1132
1132
  logger.warn("No provider configured in kody.config.json \u2014 cannot start LiteLLM proxy");
1133
1133
  return null;
1134
1134
  }
1135
- const configPath = path10.join(os.tmpdir(), "kody-litellm-config.yaml");
1136
- fs11.writeFileSync(configPath, generatedConfig);
1135
+ const configPath = path9.join(os.tmpdir(), "kody-litellm-config.yaml");
1136
+ fs10.writeFileSync(configPath, generatedConfig);
1137
1137
  const portMatch = url.match(/:(\d+)/);
1138
1138
  const port = portMatch ? portMatch[1] : "4000";
1139
1139
  let litellmFound = false;
1140
1140
  try {
1141
- execFileSync9("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
1141
+ execFileSync8("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
1142
1142
  litellmFound = true;
1143
1143
  } catch {
1144
1144
  try {
1145
- execFileSync9("python3", ["-c", "import litellm"], { timeout: 1e4, stdio: "pipe" });
1145
+ execFileSync8("python3", ["-c", "import litellm"], { timeout: 1e4, stdio: "pipe" });
1146
1146
  litellmFound = true;
1147
1147
  } catch {
1148
1148
  }
@@ -1155,17 +1155,17 @@ async function tryStartLitellm(url, projectDir, generatedConfig) {
1155
1155
  let cmd;
1156
1156
  let args2;
1157
1157
  try {
1158
- execFileSync9("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
1158
+ execFileSync8("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
1159
1159
  cmd = "litellm";
1160
1160
  args2 = ["--config", configPath, "--port", port];
1161
1161
  } catch {
1162
1162
  cmd = "python3";
1163
1163
  args2 = ["-m", "litellm", "--config", configPath, "--port", port];
1164
1164
  }
1165
- const dotenvPath = path10.join(projectDir, ".env");
1165
+ const dotenvPath = path9.join(projectDir, ".env");
1166
1166
  const dotenvVars = {};
1167
- if (fs11.existsSync(dotenvPath)) {
1168
- for (const rawLine of fs11.readFileSync(dotenvPath, "utf-8").split("\n")) {
1167
+ if (fs10.existsSync(dotenvPath)) {
1168
+ for (const rawLine of fs10.readFileSync(dotenvPath, "utf-8").split("\n")) {
1169
1169
  const line = rawLine.trim();
1170
1170
  if (!line || line.startsWith("#")) continue;
1171
1171
  const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
@@ -1225,8 +1225,8 @@ __export(taskify_command_exports, {
1225
1225
  taskifyCommand: () => taskifyCommand,
1226
1226
  topoSort: () => topoSort
1227
1227
  });
1228
- import * as fs12 from "fs";
1229
- import * as path11 from "path";
1228
+ import * as fs11 from "fs";
1229
+ import * as path10 from "path";
1230
1230
  import { fileURLToPath } from "url";
1231
1231
  import { execSync } from "child_process";
1232
1232
  function topoSort(tasks) {
@@ -1270,10 +1270,10 @@ function hasFlag(args2, flag) {
1270
1270
  async function runTaskifyCommand() {
1271
1271
  const args2 = process.argv.slice(3);
1272
1272
  const cwdArg = getArg(args2, "--cwd") ?? process.cwd();
1273
- const projectDir = path11.resolve(cwdArg);
1273
+ const projectDir = path10.resolve(cwdArg);
1274
1274
  const ticketId = getArg(args2, "--ticket") ?? process.env.TICKET_ID;
1275
1275
  const prdFileArg = getArg(args2, "--file") ?? process.env.PRD_FILE;
1276
- const prdFile = prdFileArg ? path11.resolve(projectDir, prdFileArg) : void 0;
1276
+ const prdFile = prdFileArg ? path10.resolve(projectDir, prdFileArg) : void 0;
1277
1277
  const issueNumberStr = getArg(args2, "--issue-number") ?? process.env.ISSUE_NUMBER ?? "";
1278
1278
  const issueNumber = issueNumberStr ? parseInt(issueNumberStr, 10) : void 0;
1279
1279
  const feedback = getArg(args2, "--feedback") ?? process.env.FEEDBACK;
@@ -1284,7 +1284,7 @@ async function runTaskifyCommand() {
1284
1284
  logger.error("Usage: kody taskify --ticket <ticket-id> OR kody taskify --file <prd.md> OR kody taskify --issue-number <n>");
1285
1285
  process.exit(1);
1286
1286
  }
1287
- if (prdFile && !fs12.existsSync(prdFile)) {
1287
+ if (prdFile && !fs11.existsSync(prdFile)) {
1288
1288
  logger.error(`File not found: ${prdFile}`);
1289
1289
  process.exit(1);
1290
1290
  }
@@ -1325,8 +1325,8 @@ async function runTaskifyCommand() {
1325
1325
  async function taskifyCommand(opts) {
1326
1326
  const { ticketId, prdFile, issueNumber, feedback, local, projectDir, taskId } = opts;
1327
1327
  const config = getProjectConfig();
1328
- const taskDir = path11.join(projectDir, ".kody", "tasks", taskId);
1329
- fs12.mkdirSync(taskDir, { recursive: true });
1328
+ const taskDir = path10.join(projectDir, ".kody", "tasks", taskId);
1329
+ fs11.mkdirSync(taskDir, { recursive: true });
1330
1330
  const mode = prdFile ? "file" : ticketId ? "ticket" : "issue";
1331
1331
  logger.info(`[taskify] mode=${mode} source=${ticketId ?? prdFile ?? `issue#${issueNumber}`} issue=${issueNumber ?? "none"} task=${taskId}`);
1332
1332
  let mcpConfigJson;
@@ -1350,7 +1350,7 @@ Add the required MCP server config to \`kody.config.json\` and try again.`
1350
1350
  }
1351
1351
  const sc = resolveStageConfig(config, "taskify", "strong");
1352
1352
  const model = sc.model;
1353
- const fileContent = prdFile ? fs12.readFileSync(prdFile, "utf-8") : void 0;
1353
+ const fileContent = prdFile ? fs11.readFileSync(prdFile, "utf-8") : void 0;
1354
1354
  let issueBody;
1355
1355
  if (mode === "issue" && issueNumber) {
1356
1356
  const issue = getIssue(issueNumber);
@@ -1366,10 +1366,10 @@ ${issue.body}`;
1366
1366
  let projectContext;
1367
1367
  {
1368
1368
  const parts = [];
1369
- const memoryPath = path11.join(projectDir, ".kody", "memory.md");
1370
- if (fs12.existsSync(memoryPath)) {
1369
+ const memoryPath = path10.join(projectDir, ".kody", "memory.md");
1370
+ if (fs11.existsSync(memoryPath)) {
1371
1371
  try {
1372
- const content = fs12.readFileSync(memoryPath, "utf-8").slice(0, 2e3);
1372
+ const content = fs11.readFileSync(memoryPath, "utf-8").slice(0, 2e3);
1373
1373
  if (content.trim()) parts.push(`### Project Memory
1374
1374
  ${content}`);
1375
1375
  } catch {
@@ -1388,14 +1388,14 @@ ${lines.join("\n")}
1388
1388
  }
1389
1389
  const prompt = buildPrompt({ ticketId, fileContent, issueBody, taskDir, feedback, projectContext });
1390
1390
  if (issueNumber && !local) {
1391
- const src = mode === "file" ? `file \`${path11.basename(prdFile)}\`` : mode === "ticket" ? `ticket **${ticketId}**` : `issue #${issueNumber} description`;
1391
+ const src = mode === "file" ? `file \`${path10.basename(prdFile)}\`` : mode === "ticket" ? `ticket **${ticketId}**` : `issue #${issueNumber} description`;
1392
1392
  const runUrl = process.env.RUN_URL ? ` ([logs](${process.env.RUN_URL}))` : "";
1393
1393
  postComment(issueNumber, `\u{1F680} Kody pipeline started: \`${taskId}\`${runUrl}
1394
1394
 
1395
1395
  Kody is decomposing ${src} into tasks...`);
1396
1396
  setLifecycleLabel(issueNumber, "planning");
1397
1397
  }
1398
- fs12.writeFileSync(path11.join(taskDir, MARKER_FILE), JSON.stringify({ ticketId, prdFile, issueNumber }));
1398
+ fs11.writeFileSync(path10.join(taskDir, MARKER_FILE), JSON.stringify({ ticketId, prdFile, issueNumber }));
1399
1399
  const runner = opts.runner ?? createClaudeCodeRunner();
1400
1400
  logger.info(` model=${model} timeout=${TASKIFY_TIMEOUT_MS / 1e3}s`);
1401
1401
  const result2 = await runner.run("taskify", prompt, model, TASKIFY_TIMEOUT_MS, taskDir, {
@@ -1413,8 +1413,8 @@ Kody is decomposing ${src} into tasks...`);
1413
1413
  }
1414
1414
  throw new TaskifyError(errMsg);
1415
1415
  }
1416
- const resultPath = path11.join(taskDir, RESULT_FILE);
1417
- if (!fs12.existsSync(resultPath)) {
1416
+ const resultPath = path10.join(taskDir, RESULT_FILE);
1417
+ if (!fs11.existsSync(resultPath)) {
1418
1418
  const errMsg = `Claude did not write ${RESULT_FILE}. Output:
1419
1419
 
1420
1420
  ${result2.output?.slice(0, 500) ?? "(none)"}`;
@@ -1428,7 +1428,7 @@ ${errMsg}`);
1428
1428
  }
1429
1429
  let parsed;
1430
1430
  try {
1431
- parsed = JSON.parse(fs12.readFileSync(resultPath, "utf-8"));
1431
+ parsed = JSON.parse(fs11.readFileSync(resultPath, "utf-8"));
1432
1432
  } catch {
1433
1433
  const errMsg = `Could not parse ${RESULT_FILE} as JSON.`;
1434
1434
  if (issueNumber && !local) {
@@ -1437,7 +1437,7 @@ ${errMsg}`);
1437
1437
  }
1438
1438
  throw new TaskifyError(errMsg);
1439
1439
  }
1440
- const sourceLabel = ticketId ?? (prdFile ? path11.basename(prdFile) : issueNumber ? `issue #${issueNumber}` : "spec");
1440
+ const sourceLabel = ticketId ?? (prdFile ? path10.basename(prdFile) : issueNumber ? `issue #${issueNumber}` : "spec");
1441
1441
  if (parsed.status === "questions") {
1442
1442
  handleQuestions(parsed, sourceLabel, issueNumber, local ?? false);
1443
1443
  } else if (parsed.status === "ready") {
@@ -1537,15 +1537,15 @@ function buildPrompt(opts) {
1537
1537
  const { ticketId, fileContent, issueBody, taskDir, feedback, projectContext } = opts;
1538
1538
  const scriptDir = new URL(".", import.meta.url).pathname;
1539
1539
  const candidates = [
1540
- path11.resolve(scriptDir, "..", "prompts", "taskify-ticket.md"),
1541
- path11.resolve(scriptDir, "..", "..", "prompts", "taskify-ticket.md"),
1542
- path11.resolve(__dirname, "..", "..", "prompts", "taskify-ticket.md"),
1543
- path11.resolve(__dirname, "..", "prompts", "taskify-ticket.md")
1540
+ path10.resolve(scriptDir, "..", "prompts", "taskify-ticket.md"),
1541
+ path10.resolve(scriptDir, "..", "..", "prompts", "taskify-ticket.md"),
1542
+ path10.resolve(__dirname, "..", "..", "prompts", "taskify-ticket.md"),
1543
+ path10.resolve(__dirname, "..", "prompts", "taskify-ticket.md")
1544
1544
  ];
1545
1545
  let template = "";
1546
1546
  for (const candidate of candidates) {
1547
- if (fs12.existsSync(candidate)) {
1548
- template = fs12.readFileSync(candidate, "utf-8");
1547
+ if (fs11.existsSync(candidate)) {
1548
+ template = fs11.readFileSync(candidate, "utf-8");
1549
1549
  break;
1550
1550
  }
1551
1551
  }
@@ -1569,13 +1569,13 @@ function buildPrompt(opts) {
1569
1569
  return template;
1570
1570
  }
1571
1571
  function isTaskifyRun(taskDir) {
1572
- return fs12.existsSync(path11.join(taskDir, MARKER_FILE));
1572
+ return fs11.existsSync(path10.join(taskDir, MARKER_FILE));
1573
1573
  }
1574
1574
  function readTaskifyMarker(taskDir) {
1575
- const markerPath = path11.join(taskDir, MARKER_FILE);
1576
- if (!fs12.existsSync(markerPath)) return null;
1575
+ const markerPath = path10.join(taskDir, MARKER_FILE);
1576
+ if (!fs11.existsSync(markerPath)) return null;
1577
1577
  try {
1578
- return JSON.parse(fs12.readFileSync(markerPath, "utf-8"));
1578
+ return JSON.parse(fs11.readFileSync(markerPath, "utf-8"));
1579
1579
  } catch {
1580
1580
  return null;
1581
1581
  }
@@ -1591,7 +1591,7 @@ var init_taskify_command = __esm({
1591
1591
  init_logger();
1592
1592
  init_task_resolution();
1593
1593
  init_litellm();
1594
- __dirname = path11.dirname(fileURLToPath(import.meta.url));
1594
+ __dirname = path10.dirname(fileURLToPath(import.meta.url));
1595
1595
  TaskifyError = class extends Error {
1596
1596
  constructor(message) {
1597
1597
  super(message);
@@ -1607,9 +1607,9 @@ var init_taskify_command = __esm({
1607
1607
  });
1608
1608
 
1609
1609
  // src/cli/test-model-tests.ts
1610
- import * as fs13 from "fs";
1610
+ import * as fs12 from "fs";
1611
1611
  import * as os2 from "os";
1612
- import * as path12 from "path";
1612
+ import * as path11 from "path";
1613
1613
  import * as zlib from "zlib";
1614
1614
  import { spawnSync, execSync as execSync2 } from "child_process";
1615
1615
  function canRunApiTests(ctx) {
@@ -1937,8 +1937,8 @@ async function testExtendedThinking(ctx) {
1937
1937
  async function testToolRead(ctx) {
1938
1938
  if (!canRunApiTests(ctx)) {
1939
1939
  const t2 = Date.now();
1940
- const testFile2 = path12.join(os2.tmpdir(), "kody-test-model-read.txt");
1941
- fs13.writeFileSync(testFile2, "KODY_SECRET_CONTENT_42");
1940
+ const testFile2 = path11.join(os2.tmpdir(), "kody-test-model-read.txt");
1941
+ fs12.writeFileSync(testFile2, "KODY_SECRET_CONTENT_42");
1942
1942
  try {
1943
1943
  const r = runClaudeTest(ctx, `Read the file ${testFile2} and tell me its exact contents. Reply with ONLY the file contents.`);
1944
1944
  const ok = r.stdout.includes("KODY_SECRET_CONTENT_42");
@@ -1952,12 +1952,12 @@ async function testToolRead(ctx) {
1952
1952
  { toolSelection: ok ? 100 : 0 }
1953
1953
  );
1954
1954
  } finally {
1955
- fs13.rmSync(testFile2, { force: true });
1955
+ fs12.rmSync(testFile2, { force: true });
1956
1956
  }
1957
1957
  }
1958
1958
  const t = Date.now();
1959
- const testFile = path12.join(os2.tmpdir(), "kody-test-model-read.txt");
1960
- fs13.writeFileSync(testFile, "KODY_SECRET_CONTENT_42");
1959
+ const testFile = path11.join(os2.tmpdir(), "kody-test-model-read.txt");
1960
+ fs12.writeFileSync(testFile, "KODY_SECRET_CONTENT_42");
1961
1961
  try {
1962
1962
  const conv = await runToolConversation(
1963
1963
  ctx,
@@ -1986,17 +1986,17 @@ async function testToolRead(ctx) {
1986
1986
  { toolSelection: calledRead ? 100 : 0 }
1987
1987
  );
1988
1988
  } finally {
1989
- fs13.rmSync(testFile, { force: true });
1989
+ fs12.rmSync(testFile, { force: true });
1990
1990
  }
1991
1991
  }
1992
1992
  async function testToolEdit(ctx) {
1993
1993
  if (!canRunApiTests(ctx)) {
1994
1994
  const t2 = Date.now();
1995
- const testFile = path12.join(os2.tmpdir(), "kody-test-model-edit.txt");
1996
- fs13.writeFileSync(testFile, "hello world");
1995
+ const testFile = path11.join(os2.tmpdir(), "kody-test-model-edit.txt");
1996
+ fs12.writeFileSync(testFile, "hello world");
1997
1997
  try {
1998
1998
  const r = runClaudeTest(ctx, `Use the Edit tool to replace "hello" with "goodbye" in ${testFile}. Do nothing else.`);
1999
- const content = fs13.existsSync(testFile) ? fs13.readFileSync(testFile, "utf-8") : "";
1999
+ const content = fs12.existsSync(testFile) ? fs12.readFileSync(testFile, "utf-8") : "";
2000
2000
  const ok = content.includes("goodbye");
2001
2001
  return result(
2002
2002
  "tool_edit",
@@ -2008,7 +2008,7 @@ async function testToolEdit(ctx) {
2008
2008
  { toolSelection: ok ? 100 : 0 }
2009
2009
  );
2010
2010
  } finally {
2011
- fs13.rmSync(testFile, { force: true });
2011
+ fs12.rmSync(testFile, { force: true });
2012
2012
  }
2013
2013
  }
2014
2014
  const t = Date.now();
@@ -2082,8 +2082,8 @@ async function testToolBash(ctx) {
2082
2082
  async function testImageAttachment(ctx) {
2083
2083
  if (!canRunApiTests(ctx)) {
2084
2084
  const t2 = Date.now();
2085
- const tmpPng = path12.join(os2.tmpdir(), "kody-test-image.png");
2086
- fs13.writeFileSync(tmpPng, createRedPng());
2085
+ const tmpPng = path11.join(os2.tmpdir(), "kody-test-image.png");
2086
+ fs12.writeFileSync(tmpPng, createRedPng());
2087
2087
  try {
2088
2088
  const r = runClaudeTest(ctx, `Read the image file at ${tmpPng} and tell me what color it is. Reply with just the color name.`);
2089
2089
  const text2 = r.stdout.toLowerCase();
@@ -2097,7 +2097,7 @@ async function testImageAttachment(ctx) {
2097
2097
  ok ? "Image processed correctly via CLI" : `Got: ${text2.slice(0, 80)}`
2098
2098
  );
2099
2099
  } finally {
2100
- fs13.rmSync(tmpPng, { force: true });
2100
+ fs12.rmSync(tmpPng, { force: true });
2101
2101
  }
2102
2102
  }
2103
2103
  const t = Date.now();
@@ -2312,10 +2312,10 @@ async function testReviewStage(ctx) {
2312
2312
  }
2313
2313
  async function testMcpTools(ctx) {
2314
2314
  const t = Date.now();
2315
- const mcpConfig = path12.join(os2.tmpdir(), `kody-test-mcp-${Date.now()}.json`);
2316
- const testFile = path12.join(ctx.projectDir, "kody-mcp-compat-test.txt");
2315
+ const mcpConfig = path11.join(os2.tmpdir(), `kody-test-mcp-${Date.now()}.json`);
2316
+ const testFile = path11.join(ctx.projectDir, "kody-mcp-compat-test.txt");
2317
2317
  try {
2318
- fs13.writeFileSync(mcpConfig, JSON.stringify({
2318
+ fs12.writeFileSync(mcpConfig, JSON.stringify({
2319
2319
  mcpServers: {
2320
2320
  filesystem: { command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", ctx.projectDir] }
2321
2321
  }
@@ -2326,8 +2326,8 @@ async function testMcpTools(ctx) {
2326
2326
  ["--mcp-config", mcpConfig],
2327
2327
  12e4
2328
2328
  );
2329
- const created = fs13.existsSync(testFile);
2330
- const content = created ? fs13.readFileSync(testFile, "utf-8").trim() : "";
2329
+ const created = fs12.existsSync(testFile);
2330
+ const content = created ? fs12.readFileSync(testFile, "utf-8").trim() : "";
2331
2331
  const correct = content.includes("mcp-ok");
2332
2332
  return result(
2333
2333
  "mcp_tools",
@@ -2340,8 +2340,8 @@ async function testMcpTools(ctx) {
2340
2340
  } catch (err) {
2341
2341
  return result("mcp_tools", "advanced", "warn", 0, Date.now() - t, `MCP test error: ${err instanceof Error ? err.message : String(err)}`);
2342
2342
  } finally {
2343
- fs13.rmSync(mcpConfig, { force: true });
2344
- fs13.rmSync(testFile, { force: true });
2343
+ fs12.rmSync(mcpConfig, { force: true });
2344
+ fs12.rmSync(testFile, { force: true });
2345
2345
  revertChanges(ctx.projectDir);
2346
2346
  }
2347
2347
  }
@@ -2522,10 +2522,10 @@ var test_model_command_exports = {};
2522
2522
  __export(test_model_command_exports, {
2523
2523
  runTestModelCommand: () => runTestModelCommand
2524
2524
  });
2525
- import * as fs14 from "fs";
2525
+ import * as fs13 from "fs";
2526
2526
  import * as os3 from "os";
2527
- import * as path13 from "path";
2528
- import { execFileSync as execFileSync10 } from "child_process";
2527
+ import * as path12 from "path";
2528
+ import { execFileSync as execFileSync9 } from "child_process";
2529
2529
  function parseTestModelArgs() {
2530
2530
  const args2 = process.argv.slice(3);
2531
2531
  function getArg3(flag) {
@@ -2598,16 +2598,16 @@ function generateConfig(provider, model, dropParams) {
2598
2598
  }
2599
2599
  async function startProxy(config, url) {
2600
2600
  try {
2601
- execFileSync10("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
2601
+ execFileSync9("which", ["litellm"], { timeout: 3e3, stdio: "pipe" });
2602
2602
  } catch {
2603
2603
  try {
2604
- execFileSync10("python3", ["-c", "import litellm"], { timeout: 1e4, stdio: "pipe" });
2604
+ execFileSync9("python3", ["-c", "import litellm"], { timeout: 1e4, stdio: "pipe" });
2605
2605
  } catch {
2606
2606
  logger.error("litellm not installed. Install: pip install 'litellm[proxy]'");
2607
2607
  return null;
2608
2608
  }
2609
2609
  }
2610
- fs14.writeFileSync(CONFIG_PATH, config);
2610
+ fs13.writeFileSync(CONFIG_PATH, config);
2611
2611
  const portMatch = url.match(/:(\d+)/);
2612
2612
  const port = portMatch ? portMatch[1] : "4099";
2613
2613
  const { spawn: spawn2 } = await import("child_process");
@@ -2649,7 +2649,7 @@ async function quickApiTest(url, model, apiKey) {
2649
2649
  }
2650
2650
  }
2651
2651
  function delay(ms) {
2652
- return new Promise((resolve6) => setTimeout(resolve6, ms));
2652
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
2653
2653
  }
2654
2654
  async function runTestModelCommand() {
2655
2655
  const opts = parseTestModelArgs();
@@ -2663,7 +2663,7 @@ async function runTestModelCommand() {
2663
2663
  proxyProcess.kill();
2664
2664
  proxyProcess = null;
2665
2665
  }
2666
- fs14.rmSync(CONFIG_PATH, { force: true });
2666
+ fs13.rmSync(CONFIG_PATH, { force: true });
2667
2667
  };
2668
2668
  process.on("SIGINT", () => {
2669
2669
  cleanup();
@@ -2757,7 +2757,7 @@ var init_test_model_command = __esm({
2757
2757
  init_test_model_report();
2758
2758
  TEST_PORT = 4099;
2759
2759
  TEST_URL = `http://localhost:${TEST_PORT}`;
2760
- CONFIG_PATH = path13.join(os3.tmpdir(), "kody-test-model-config.yaml");
2760
+ CONFIG_PATH = path12.join(os3.tmpdir(), "kody-test-model-config.yaml");
2761
2761
  }
2762
2762
  });
2763
2763
 
@@ -2768,7 +2768,7 @@ __export(parse_inputs_exports, {
2768
2768
  runCiParse: () => runCiParse,
2769
2769
  writeOutputs: () => writeOutputs
2770
2770
  });
2771
- import * as fs15 from "fs";
2771
+ import * as fs14 from "fs";
2772
2772
  function generateTimestamp() {
2773
2773
  const now = /* @__PURE__ */ new Date();
2774
2774
  const pad2 = (n) => String(n).padStart(2, "0");
@@ -2917,12 +2917,12 @@ function writeOutputs(result2) {
2917
2917
  function output(key, value) {
2918
2918
  if (outputFile) {
2919
2919
  if (value.includes("\n")) {
2920
- fs15.appendFileSync(outputFile, `${key}<<KODY_EOF
2920
+ fs14.appendFileSync(outputFile, `${key}<<KODY_EOF
2921
2921
  ${value}
2922
2922
  KODY_EOF
2923
2923
  `);
2924
2924
  } else {
2925
- fs15.appendFileSync(outputFile, `${key}=${value}
2925
+ fs14.appendFileSync(outputFile, `${key}=${value}
2926
2926
  `);
2927
2927
  }
2928
2928
  }
@@ -3047,7 +3047,7 @@ var init_definitions = __esm({
3047
3047
  });
3048
3048
 
3049
3049
  // src/git-utils.ts
3050
- import { execFileSync as execFileSync11 } from "child_process";
3050
+ import { execFileSync as execFileSync10 } from "child_process";
3051
3051
  function getHookSafeEnv() {
3052
3052
  if (!_hookSafeEnv) {
3053
3053
  _hookSafeEnv = { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" };
@@ -3055,7 +3055,7 @@ function getHookSafeEnv() {
3055
3055
  return _hookSafeEnv;
3056
3056
  }
3057
3057
  function git(args2, options) {
3058
- return execFileSync11("git", args2, {
3058
+ return execFileSync10("git", args2, {
3059
3059
  encoding: "utf-8",
3060
3060
  timeout: options?.timeout ?? 3e4,
3061
3061
  cwd: options?.cwd,
@@ -3241,14 +3241,14 @@ var init_git_utils = __esm({
3241
3241
  });
3242
3242
 
3243
3243
  // src/pipeline/state.ts
3244
- import * as fs16 from "fs";
3245
- import * as path14 from "path";
3244
+ import * as fs15 from "fs";
3245
+ import * as path13 from "path";
3246
3246
  function loadState(taskId, taskDir) {
3247
- const p = path14.join(taskDir, "status.json");
3248
- if (!fs16.existsSync(p)) return null;
3247
+ const p = path13.join(taskDir, "status.json");
3248
+ if (!fs15.existsSync(p)) return null;
3249
3249
  try {
3250
3250
  const result2 = parseJsonSafe(
3251
- fs16.readFileSync(p, "utf-8"),
3251
+ fs15.readFileSync(p, "utf-8"),
3252
3252
  ["taskId", "state", "stages", "createdAt", "updatedAt"]
3253
3253
  );
3254
3254
  if (!result2.ok) {
@@ -3266,10 +3266,10 @@ function writeState(state, taskDir) {
3266
3266
  ...state,
3267
3267
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
3268
3268
  };
3269
- const target = path14.join(taskDir, "status.json");
3269
+ const target = path13.join(taskDir, "status.json");
3270
3270
  const tmp = target + ".tmp";
3271
- fs16.writeFileSync(tmp, JSON.stringify(updated, null, 2));
3272
- fs16.renameSync(tmp, target);
3271
+ fs15.writeFileSync(tmp, JSON.stringify(updated, null, 2));
3272
+ fs15.renameSync(tmp, target);
3273
3273
  return updated;
3274
3274
  }
3275
3275
  function initState(taskId) {
@@ -3310,16 +3310,16 @@ var init_complexity = __esm({
3310
3310
  });
3311
3311
 
3312
3312
  // src/memory.ts
3313
- import * as fs17 from "fs";
3314
- import * as path15 from "path";
3313
+ import * as fs16 from "fs";
3314
+ import * as path14 from "path";
3315
3315
  function readProjectMemory(projectDir) {
3316
- const memoryDir = path15.join(projectDir, ".kody", "memory");
3317
- if (!fs17.existsSync(memoryDir)) return "";
3318
- const files = fs17.readdirSync(memoryDir).filter((f) => f.endsWith(".md")).sort();
3316
+ const memoryDir = path14.join(projectDir, ".kody", "memory");
3317
+ if (!fs16.existsSync(memoryDir)) return "";
3318
+ const files = fs16.readdirSync(memoryDir).filter((f) => f.endsWith(".md")).sort();
3319
3319
  if (files.length === 0) return "";
3320
3320
  const sections = [];
3321
3321
  for (const file of files) {
3322
- const content = fs17.readFileSync(path15.join(memoryDir, file), "utf-8").trim();
3322
+ const content = fs16.readFileSync(path14.join(memoryDir, file), "utf-8").trim();
3323
3323
  if (content) {
3324
3324
  sections.push(`## ${file.replace(".md", "")}
3325
3325
  ${content}`);
@@ -3338,8 +3338,8 @@ var init_memory = __esm({
3338
3338
  });
3339
3339
 
3340
3340
  // src/context-tiers.ts
3341
- import * as fs18 from "fs";
3342
- import * as path16 from "path";
3341
+ import * as fs17 from "fs";
3342
+ import * as path15 from "path";
3343
3343
  function estimateTokens(text) {
3344
3344
  return Math.ceil(text.length / 4);
3345
3345
  }
@@ -3430,7 +3430,7 @@ function generateL1Json(content) {
3430
3430
  }
3431
3431
  }
3432
3432
  function getTieredContent(filePath, content) {
3433
- const key = path16.basename(filePath);
3433
+ const key = path15.basename(filePath);
3434
3434
  return {
3435
3435
  source: filePath,
3436
3436
  L0: generateL0(content, key),
@@ -3442,15 +3442,15 @@ function selectTier(tiered, tier) {
3442
3442
  return tiered[tier];
3443
3443
  }
3444
3444
  function readProjectMemoryTiered(projectDir, tier) {
3445
- const memoryDir = path16.join(projectDir, ".kody", "memory");
3446
- if (!fs18.existsSync(memoryDir)) return "";
3447
- const files = fs18.readdirSync(memoryDir).filter((f) => f.endsWith(".md")).sort();
3445
+ const memoryDir = path15.join(projectDir, ".kody", "memory");
3446
+ if (!fs17.existsSync(memoryDir)) return "";
3447
+ const files = fs17.readdirSync(memoryDir).filter((f) => f.endsWith(".md")).sort();
3448
3448
  if (files.length === 0) return "";
3449
3449
  const tierLabel2 = tier === "L2" ? "full" : tier === "L1" ? "overview" : "abstract";
3450
3450
  const sections = [];
3451
3451
  for (const file of files) {
3452
- const filePath = path16.join(memoryDir, file);
3453
- const content = fs18.readFileSync(filePath, "utf-8").trim();
3452
+ const filePath = path15.join(memoryDir, file);
3453
+ const content = fs17.readFileSync(filePath, "utf-8").trim();
3454
3454
  if (!content) continue;
3455
3455
  const tiered = getTieredContent(filePath, content);
3456
3456
  const selected = selectTier(tiered, tier);
@@ -3473,9 +3473,9 @@ function injectTaskContextTiered(prompt, taskId, taskDir, policy, feedback) {
3473
3473
  `;
3474
3474
  context += `Task Directory: ${taskDir}
3475
3475
  `;
3476
- const taskMdPath = path16.join(taskDir, "task.md");
3477
- if (fs18.existsSync(taskMdPath)) {
3478
- const content = fs18.readFileSync(taskMdPath, "utf-8");
3476
+ const taskMdPath = path15.join(taskDir, "task.md");
3477
+ if (fs17.existsSync(taskMdPath)) {
3478
+ const content = fs17.readFileSync(taskMdPath, "utf-8");
3479
3479
  const selected = selectContent(taskMdPath, content, policy.taskDescription);
3480
3480
  const label = tierLabel("Task Description", policy.taskDescription);
3481
3481
  context += `
@@ -3483,9 +3483,9 @@ function injectTaskContextTiered(prompt, taskId, taskDir, policy, feedback) {
3483
3483
  ${selected}
3484
3484
  `;
3485
3485
  }
3486
- const taskJsonPath = path16.join(taskDir, "task.json");
3487
- if (fs18.existsSync(taskJsonPath)) {
3488
- const content = fs18.readFileSync(taskJsonPath, "utf-8");
3486
+ const taskJsonPath = path15.join(taskDir, "task.json");
3487
+ if (fs17.existsSync(taskJsonPath)) {
3488
+ const content = fs17.readFileSync(taskJsonPath, "utf-8");
3489
3489
  if (policy.taskClassification === "L2") {
3490
3490
  try {
3491
3491
  const taskDef = JSON.parse(content.replace(/^```json\s*\n?/m, "").replace(/\n?```\s*$/m, ""));
@@ -3511,9 +3511,9 @@ ${selected}
3511
3511
  }
3512
3512
  }
3513
3513
  }
3514
- const specPath = path16.join(taskDir, "spec.md");
3515
- if (fs18.existsSync(specPath)) {
3516
- const content = fs18.readFileSync(specPath, "utf-8");
3514
+ const specPath = path15.join(taskDir, "spec.md");
3515
+ if (fs17.existsSync(specPath)) {
3516
+ const content = fs17.readFileSync(specPath, "utf-8");
3517
3517
  const selected = selectContent(specPath, content, policy.spec);
3518
3518
  const label = tierLabel("Spec", policy.spec);
3519
3519
  context += `
@@ -3521,9 +3521,9 @@ ${selected}
3521
3521
  ${selected}
3522
3522
  `;
3523
3523
  }
3524
- const planPath = path16.join(taskDir, "plan.md");
3525
- if (fs18.existsSync(planPath)) {
3526
- const content = fs18.readFileSync(planPath, "utf-8");
3524
+ const planPath = path15.join(taskDir, "plan.md");
3525
+ if (fs17.existsSync(planPath)) {
3526
+ const content = fs17.readFileSync(planPath, "utf-8");
3527
3527
  const selected = selectContent(planPath, content, policy.plan);
3528
3528
  const label = tierLabel("Plan", policy.plan);
3529
3529
  context += `
@@ -3531,9 +3531,9 @@ ${selected}
3531
3531
  ${selected}
3532
3532
  `;
3533
3533
  }
3534
- const contextMdPath = path16.join(taskDir, "context.md");
3535
- if (fs18.existsSync(contextMdPath)) {
3536
- const content = fs18.readFileSync(contextMdPath, "utf-8");
3534
+ const contextMdPath = path15.join(taskDir, "context.md");
3535
+ if (fs17.existsSync(contextMdPath)) {
3536
+ const content = fs17.readFileSync(contextMdPath, "utf-8");
3537
3537
  const selected = selectContent(contextMdPath, content, policy.accumulatedContext);
3538
3538
  const label = tierLabel("Previous Stage Context", policy.accumulatedContext);
3539
3539
  context += `
@@ -3618,117 +3618,25 @@ var init_context_tiers = __esm({
3618
3618
  }
3619
3619
  });
3620
3620
 
3621
- // src/tools.ts
3622
- import * as fs19 from "fs";
3623
- import * as path17 from "path";
3624
- import { execSync as execSync3 } from "child_process";
3625
- import { parse as parseYaml } from "yaml";
3626
- function loadToolDeclarations(projectDir) {
3627
- const toolsPath = path17.join(projectDir, ".kody", "tools.yml");
3628
- if (!fs19.existsSync(toolsPath)) return [];
3629
- try {
3630
- const raw = fs19.readFileSync(toolsPath, "utf-8");
3631
- const parsed = parseYaml(raw);
3632
- if (!parsed || typeof parsed !== "object") return [];
3633
- return Object.entries(parsed).map(([name, value]) => {
3634
- const v = value;
3635
- return {
3636
- name,
3637
- detect: Array.isArray(v.detect) ? v.detect : [],
3638
- stages: Array.isArray(v.stages) ? v.stages : [],
3639
- setup: typeof v.setup === "string" ? v.setup : "",
3640
- skill: typeof v.skill === "string" ? v.skill : ""
3641
- };
3642
- });
3643
- } catch (err) {
3644
- logger.warn(`Failed to parse .kody/tools.yml: ${err instanceof Error ? err.message : String(err)}`);
3645
- return [];
3646
- }
3647
- }
3648
- function resolveSkillContent(skillFilename, projectDir) {
3649
- if (!skillFilename) return "";
3650
- const projectSkill = path17.join(projectDir, ".kody", "skills", skillFilename);
3651
- if (fs19.existsSync(projectSkill)) {
3652
- return fs19.readFileSync(projectSkill, "utf-8");
3653
- }
3654
- const scriptDir = new URL(".", import.meta.url).pathname;
3655
- const candidates = [
3656
- path17.resolve(scriptDir, "..", "skills", skillFilename),
3657
- path17.resolve(scriptDir, "..", "..", "skills", skillFilename)
3658
- ];
3659
- for (const candidate of candidates) {
3660
- if (fs19.existsSync(candidate)) {
3661
- return fs19.readFileSync(candidate, "utf-8");
3662
- }
3663
- }
3664
- logger.warn(`Skill file not found: ${skillFilename}`);
3665
- return "";
3666
- }
3667
- function detectTools(declarations, projectDir) {
3668
- const resolved = [];
3669
- for (const decl of declarations) {
3670
- const detected = decl.detect.some((pattern) => fs19.existsSync(path17.join(projectDir, pattern)));
3671
- if (!detected) continue;
3672
- const skillContent = resolveSkillContent(decl.skill, projectDir);
3673
- resolved.push({
3674
- name: decl.name,
3675
- stages: decl.stages,
3676
- setup: decl.setup,
3677
- skillContent
3678
- });
3679
- }
3680
- return resolved;
3681
- }
3682
- function runToolSetup(tools, projectDir) {
3683
- for (const tool of tools) {
3684
- if (!tool.setup) continue;
3685
- try {
3686
- logger.info(` Setting up ${tool.name}: ${tool.setup}`);
3687
- execSync3(tool.setup, { cwd: projectDir, timeout: 12e4, stdio: "pipe" });
3688
- logger.info(` \u2713 ${tool.name} setup complete`);
3689
- } catch (err) {
3690
- logger.warn(` \u26A0 ${tool.name} setup failed: ${err instanceof Error ? err.message : String(err)}`);
3691
- }
3692
- }
3693
- }
3694
- function getToolSkillsForStage(tools, stageName) {
3695
- const matched = tools.filter((t) => t.stages.includes(stageName) && t.skillContent);
3696
- if (matched.length === 0) return "";
3697
- const sections = matched.map((t) => `### ${t.name}
3698
-
3699
- ${t.skillContent}`);
3700
- return `## Available Tools
3701
-
3702
- The following tools are installed and ready to use in this environment.
3703
-
3704
- ${sections.join("\n\n")}`;
3705
- }
3706
- var init_tools = __esm({
3707
- "src/tools.ts"() {
3708
- "use strict";
3709
- init_logger();
3710
- }
3711
- });
3712
-
3713
3621
  // src/context.ts
3714
- import * as fs20 from "fs";
3715
- import * as path18 from "path";
3622
+ import * as fs18 from "fs";
3623
+ import * as path16 from "path";
3716
3624
  function readPromptFile(stageName, projectDir) {
3717
3625
  if (projectDir) {
3718
- const stepFile = path18.join(projectDir, ".kody", "steps", `${stageName}.md`);
3719
- if (fs20.existsSync(stepFile)) {
3720
- return fs20.readFileSync(stepFile, "utf-8");
3626
+ const stepFile = path16.join(projectDir, ".kody", "steps", `${stageName}.md`);
3627
+ if (fs18.existsSync(stepFile)) {
3628
+ return fs18.readFileSync(stepFile, "utf-8");
3721
3629
  }
3722
3630
  console.warn(` \u26A0 No step file at ${stepFile}, falling back to engine defaults. Run 'kody-engine-lite init --force' to generate step files.`);
3723
3631
  }
3724
3632
  const scriptDir = new URL(".", import.meta.url).pathname;
3725
3633
  const candidates = [
3726
- path18.resolve(scriptDir, "..", "prompts", `${stageName}.md`),
3727
- path18.resolve(scriptDir, "..", "..", "prompts", `${stageName}.md`)
3634
+ path16.resolve(scriptDir, "..", "prompts", `${stageName}.md`),
3635
+ path16.resolve(scriptDir, "..", "..", "prompts", `${stageName}.md`)
3728
3636
  ];
3729
3637
  for (const candidate of candidates) {
3730
- if (fs20.existsSync(candidate)) {
3731
- return fs20.readFileSync(candidate, "utf-8");
3638
+ if (fs18.existsSync(candidate)) {
3639
+ return fs18.readFileSync(candidate, "utf-8");
3732
3640
  }
3733
3641
  }
3734
3642
  throw new Error(`Prompt file not found: tried ${candidates.join(", ")}`);
@@ -3740,18 +3648,18 @@ function injectTaskContext(prompt, taskId, taskDir, feedback) {
3740
3648
  `;
3741
3649
  context += `Task Directory: ${taskDir}
3742
3650
  `;
3743
- const taskMdPath = path18.join(taskDir, "task.md");
3744
- if (fs20.existsSync(taskMdPath)) {
3745
- const taskMd = fs20.readFileSync(taskMdPath, "utf-8");
3651
+ const taskMdPath = path16.join(taskDir, "task.md");
3652
+ if (fs18.existsSync(taskMdPath)) {
3653
+ const taskMd = fs18.readFileSync(taskMdPath, "utf-8");
3746
3654
  context += `
3747
3655
  ## Task Description
3748
3656
  ${taskMd}
3749
3657
  `;
3750
3658
  }
3751
- const taskJsonPath = path18.join(taskDir, "task.json");
3752
- if (fs20.existsSync(taskJsonPath)) {
3659
+ const taskJsonPath = path16.join(taskDir, "task.json");
3660
+ if (fs18.existsSync(taskJsonPath)) {
3753
3661
  try {
3754
- const taskDef = JSON.parse(fs20.readFileSync(taskJsonPath, "utf-8"));
3662
+ const taskDef = JSON.parse(fs18.readFileSync(taskJsonPath, "utf-8"));
3755
3663
  context += `
3756
3664
  ## Task Classification
3757
3665
  `;
@@ -3764,27 +3672,27 @@ ${taskMd}
3764
3672
  } catch {
3765
3673
  }
3766
3674
  }
3767
- const specPath = path18.join(taskDir, "spec.md");
3768
- if (fs20.existsSync(specPath)) {
3769
- const spec = fs20.readFileSync(specPath, "utf-8");
3675
+ const specPath = path16.join(taskDir, "spec.md");
3676
+ if (fs18.existsSync(specPath)) {
3677
+ const spec = fs18.readFileSync(specPath, "utf-8");
3770
3678
  const truncated = spec.slice(0, MAX_TASK_CONTEXT_SPEC);
3771
3679
  context += `
3772
3680
  ## Spec Summary
3773
3681
  ${truncated}${spec.length > MAX_TASK_CONTEXT_SPEC ? "\n..." : ""}
3774
3682
  `;
3775
3683
  }
3776
- const planPath = path18.join(taskDir, "plan.md");
3777
- if (fs20.existsSync(planPath)) {
3778
- const plan = fs20.readFileSync(planPath, "utf-8");
3684
+ const planPath = path16.join(taskDir, "plan.md");
3685
+ if (fs18.existsSync(planPath)) {
3686
+ const plan = fs18.readFileSync(planPath, "utf-8");
3779
3687
  const truncated = plan.slice(0, MAX_TASK_CONTEXT_PLAN);
3780
3688
  context += `
3781
3689
  ## Plan Summary
3782
3690
  ${truncated}${plan.length > MAX_TASK_CONTEXT_PLAN ? "\n..." : ""}
3783
3691
  `;
3784
3692
  }
3785
- const contextMdPath = path18.join(taskDir, "context.md");
3786
- if (fs20.existsSync(contextMdPath)) {
3787
- const accumulated = fs20.readFileSync(contextMdPath, "utf-8");
3693
+ const contextMdPath = path16.join(taskDir, "context.md");
3694
+ if (fs18.existsSync(contextMdPath)) {
3695
+ const accumulated = fs18.readFileSync(contextMdPath, "utf-8");
3788
3696
  const truncated = accumulated.slice(-MAX_ACCUMULATED_CONTEXT);
3789
3697
  const prefix = accumulated.length > MAX_ACCUMULATED_CONTEXT ? "...(earlier context truncated)\n" : "";
3790
3698
  context += `
@@ -3802,17 +3710,17 @@ ${feedback}
3802
3710
  }
3803
3711
  function inferHasUIFromScope(scope) {
3804
3712
  return scope.some((filePath) => {
3805
- const ext = path18.extname(filePath).toLowerCase();
3713
+ const ext = path16.extname(filePath).toLowerCase();
3806
3714
  if (UI_EXTENSIONS.has(ext)) return true;
3807
3715
  const normalized = filePath.replace(/\\/g, "/");
3808
3716
  return UI_PATH_SEGMENTS.some((seg) => normalized.includes(seg));
3809
3717
  });
3810
3718
  }
3811
3719
  function taskHasUI(taskDir) {
3812
- const taskJsonPath = path18.join(taskDir, "task.json");
3813
- if (!fs20.existsSync(taskJsonPath)) return true;
3720
+ const taskJsonPath = path16.join(taskDir, "task.json");
3721
+ if (!fs18.existsSync(taskJsonPath)) return true;
3814
3722
  try {
3815
- const taskDef = JSON.parse(fs20.readFileSync(taskJsonPath, "utf-8"));
3723
+ const taskDef = JSON.parse(fs18.readFileSync(taskJsonPath, "utf-8"));
3816
3724
  const scope = Array.isArray(taskDef.scope) ? taskDef.scope : [];
3817
3725
  if (scope.length === 0) return true;
3818
3726
  return inferHasUIFromScope(scope);
@@ -3837,23 +3745,51 @@ function getBrowserToolGuidance(stageName, taskDir) {
3837
3745
  ### Dev Server Setup (REQUIRED before browsing)
3838
3746
  You MUST start the dev server before using any browser navigation tools:
3839
3747
  \`\`\`bash
3840
- # Start the dev server in the background
3841
- ${devServer.command} &
3842
- # Wait for it to be ready (look for "${devServer.readyPattern}" in output)
3843
- sleep 5
3748
+ # Start the dev server in the background with output redirected to a log file
3749
+ nohup ${devServer.command} > /tmp/dev-server.log 2>&1 &
3750
+ DEV_PID=$!
3751
+
3752
+ # Wait up to ${devServer.readyTimeout}s for the server to be ready
3753
+ for i in $(seq 1 ${devServer.readyTimeout}); do
3754
+ if curl -s -o /dev/null -w "%{http_code}" ${devServer.url} 2>/dev/null | grep -qE "^[23]"; then
3755
+ echo "Dev server is ready"
3756
+ break
3757
+ fi
3758
+ if ! kill -0 $DEV_PID 2>/dev/null; then
3759
+ echo "Dev server process died. Last 20 lines:"
3760
+ tail -20 /tmp/dev-server.log
3761
+ break
3762
+ fi
3763
+ sleep 1
3764
+ done
3844
3765
  \`\`\`
3845
3766
  The dev server URL is: ${devServer.url}
3846
- After you are done browsing, kill the dev server: \`kill %1 2>/dev/null || true\`` : `
3767
+ If the dev server fails to start (e.g. DB connection issues), skip browser verification and proceed with code-only changes. Do NOT hang waiting for it.
3768
+ After you are done browsing, kill the dev server: \`kill $DEV_PID 2>/dev/null || true\`` : `
3847
3769
  ### Dev Server Setup (REQUIRED before browsing)
3848
3770
  You MUST start the project's dev server before using any browser navigation tools.
3849
3771
  Check package.json for the dev command (usually \`pnpm dev\` or \`npm run dev\`).
3850
3772
  \`\`\`bash
3851
- # Start the dev server in the background
3852
- pnpm dev &
3853
- # Wait for it to be ready
3854
- sleep 5
3773
+ # Start the dev server in the background with output redirected
3774
+ nohup pnpm dev > /tmp/dev-server.log 2>&1 &
3775
+ DEV_PID=$!
3776
+
3777
+ # Wait up to 30s for the server to be ready
3778
+ for i in $(seq 1 30); do
3779
+ if curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 2>/dev/null | grep -qE "^[23]"; then
3780
+ echo "Dev server is ready"
3781
+ break
3782
+ fi
3783
+ if ! kill -0 $DEV_PID 2>/dev/null; then
3784
+ echo "Dev server process died. Last 20 lines:"
3785
+ tail -20 /tmp/dev-server.log
3786
+ break
3787
+ fi
3788
+ sleep 1
3789
+ done
3855
3790
  \`\`\`
3856
- After you are done browsing, kill the dev server: \`kill %1 2>/dev/null || true\``;
3791
+ If the dev server fails to start (e.g. DB connection issues), skip browser verification and proceed with code-only changes. Do NOT hang waiting for it.
3792
+ After you are done browsing, kill the dev server: \`kill $DEV_PID 2>/dev/null || true\``;
3857
3793
  if (stageName === "build" || stageName === "review-fix") {
3858
3794
  return `## Browser Visual Verification (MANDATORY for UI tasks)
3859
3795
 
@@ -3918,7 +3854,7 @@ ${devServerBlock}
3918
3854
 
3919
3855
  Use browser tools to navigate to pages and take snapshots to verify UI output.`;
3920
3856
  }
3921
- function buildFullPrompt(stageName, taskId, taskDir, projectDir, feedback, tools) {
3857
+ function buildFullPrompt(stageName, taskId, taskDir, projectDir, feedback) {
3922
3858
  const config = getProjectConfig();
3923
3859
  let assembled;
3924
3860
  if (config.contextTiers?.enabled) {
@@ -3934,18 +3870,12 @@ ${prompt}` : prompt;
3934
3870
  }
3935
3871
  if (isMcpEnabledForStage(stageName, config.mcp) && taskHasUI(taskDir)) {
3936
3872
  assembled = assembled + "\n\n" + getBrowserToolGuidance(stageName, taskDir);
3937
- const qaGuidePath = path18.join(projectDir, ".kody", "qa-guide.md");
3938
- if (fs20.existsSync(qaGuidePath)) {
3939
- const qaGuide = fs20.readFileSync(qaGuidePath, "utf-8").trim();
3873
+ const qaGuidePath = path16.join(projectDir, ".kody", "qa-guide.md");
3874
+ if (fs18.existsSync(qaGuidePath)) {
3875
+ const qaGuide = fs18.readFileSync(qaGuidePath, "utf-8").trim();
3940
3876
  assembled = assembled + "\n\n" + qaGuide;
3941
3877
  }
3942
3878
  }
3943
- if (tools?.length) {
3944
- const toolSkills = getToolSkillsForStage(tools, stageName);
3945
- if (toolSkills) {
3946
- assembled = assembled + "\n\n" + toolSkills;
3947
- }
3948
- }
3949
3879
  return assembled;
3950
3880
  }
3951
3881
  function buildFullPromptTiered(stageName, taskId, taskDir, projectDir, feedback) {
@@ -3985,7 +3915,6 @@ var init_context = __esm({
3985
3915
  init_config();
3986
3916
  init_context_tiers();
3987
3917
  init_mcp_config();
3988
- init_tools();
3989
3918
  MAX_TASK_CONTEXT_PLAN = 1500;
3990
3919
  MAX_TASK_CONTEXT_SPEC = 2e3;
3991
3920
  MAX_ACCUMULATED_CONTEXT = 4e3;
@@ -4035,8 +3964,8 @@ var init_runner_selection = __esm({
4035
3964
  });
4036
3965
 
4037
3966
  // src/stages/agent.ts
4038
- import * as fs21 from "fs";
4039
- import * as path19 from "path";
3967
+ import * as fs19 from "fs";
3968
+ import * as path17 from "path";
4040
3969
  function getSessionInfo(stageName, sessions) {
4041
3970
  const group = SESSION_GROUP[stageName];
4042
3971
  if (!group) return void 0;
@@ -4065,7 +3994,7 @@ async function executeAgentStage(ctx, def) {
4065
3994
  logger.info(` [dry-run] skipping ${def.name}`);
4066
3995
  return { outcome: "completed", retries: 0 };
4067
3996
  }
4068
- const prompt = buildFullPrompt(def.name, ctx.taskId, ctx.taskDir, ctx.projectDir, ctx.input.feedback, ctx.tools);
3997
+ const prompt = buildFullPrompt(def.name, ctx.taskId, ctx.taskDir, ctx.projectDir, ctx.input.feedback);
4069
3998
  let currentModelTier = def.modelTier;
4070
3999
  if (ctx.input.feedback && def.name === "build") {
4071
4000
  logger.info(` feedback: ${ctx.input.feedback.slice(0, 200)}${ctx.input.feedback.length > 200 ? "..." : ""}`);
@@ -4123,27 +4052,27 @@ async function executeAgentStage(ctx, def) {
4123
4052
  }
4124
4053
  const result2 = lastResult;
4125
4054
  if (def.outputFile && result2.output) {
4126
- fs21.writeFileSync(path19.join(ctx.taskDir, def.outputFile), result2.output);
4055
+ fs19.writeFileSync(path17.join(ctx.taskDir, def.outputFile), result2.output);
4127
4056
  }
4128
4057
  if (def.outputFile) {
4129
- const outputPath = path19.join(ctx.taskDir, def.outputFile);
4130
- if (!fs21.existsSync(outputPath)) {
4131
- const ext = path19.extname(def.outputFile);
4132
- const base = path19.basename(def.outputFile, ext);
4133
- const files = fs21.readdirSync(ctx.taskDir);
4058
+ const outputPath = path17.join(ctx.taskDir, def.outputFile);
4059
+ if (!fs19.existsSync(outputPath)) {
4060
+ const ext = path17.extname(def.outputFile);
4061
+ const base = path17.basename(def.outputFile, ext);
4062
+ const files = fs19.readdirSync(ctx.taskDir);
4134
4063
  const variant = files.find(
4135
4064
  (f) => f.startsWith(base + "-") && f.endsWith(ext)
4136
4065
  );
4137
4066
  if (variant) {
4138
- fs21.renameSync(path19.join(ctx.taskDir, variant), outputPath);
4067
+ fs19.renameSync(path17.join(ctx.taskDir, variant), outputPath);
4139
4068
  logger.info(` Renamed variant ${variant} \u2192 ${def.outputFile}`);
4140
4069
  }
4141
4070
  }
4142
4071
  }
4143
4072
  if (def.outputFile) {
4144
- const outputPath = path19.join(ctx.taskDir, def.outputFile);
4145
- if (fs21.existsSync(outputPath)) {
4146
- const content = fs21.readFileSync(outputPath, "utf-8");
4073
+ const outputPath = path17.join(ctx.taskDir, def.outputFile);
4074
+ if (fs19.existsSync(outputPath)) {
4075
+ const content = fs19.readFileSync(outputPath, "utf-8");
4147
4076
  const validation = validateStageOutput(def.name, content);
4148
4077
  if (!validation.valid) {
4149
4078
  if (def.name === "taskify") {
@@ -4157,7 +4086,7 @@ async function executeAgentStage(ctx, def) {
4157
4086
  const stripped = stripFences(retryResult.output);
4158
4087
  const retryValidation = validateTaskJson(stripped);
4159
4088
  if (retryValidation.valid) {
4160
- fs21.writeFileSync(outputPath, retryResult.output);
4089
+ fs19.writeFileSync(outputPath, retryResult.output);
4161
4090
  logger.info(` taskify retry produced valid JSON`);
4162
4091
  } else {
4163
4092
  logger.warn(` taskify retry still invalid: ${retryValidation.error}`);
@@ -4170,7 +4099,7 @@ async function executeAgentStage(ctx, def) {
4170
4099
  risk_level: "low",
4171
4100
  questions: []
4172
4101
  }, null, 2);
4173
- fs21.writeFileSync(outputPath, fallback);
4102
+ fs19.writeFileSync(outputPath, fallback);
4174
4103
  logger.info(` taskify fallback: generated minimal task.json (risk_level=low)`);
4175
4104
  }
4176
4105
  }
@@ -4184,7 +4113,7 @@ async function executeAgentStage(ctx, def) {
4184
4113
  return { outcome: "completed", outputFile: def.outputFile, retries };
4185
4114
  }
4186
4115
  function appendStageContext(taskDir, stageName, output) {
4187
- const contextPath = path19.join(taskDir, "context.md");
4116
+ const contextPath = path17.join(taskDir, "context.md");
4188
4117
  const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19);
4189
4118
  let summary;
4190
4119
  if (output && output.trim()) {
@@ -4197,7 +4126,7 @@ function appendStageContext(taskDir, stageName, output) {
4197
4126
  ### ${stageName} (${timestamp2})
4198
4127
  ${summary}
4199
4128
  `;
4200
- fs21.appendFileSync(contextPath, entry);
4129
+ fs19.appendFileSync(contextPath, entry);
4201
4130
  }
4202
4131
  var SESSION_GROUP;
4203
4132
  var init_agent = __esm({
@@ -4220,7 +4149,7 @@ var init_agent = __esm({
4220
4149
  });
4221
4150
 
4222
4151
  // src/verify-runner.ts
4223
- import { execFileSync as execFileSync12 } from "child_process";
4152
+ import { execFileSync as execFileSync11 } from "child_process";
4224
4153
  function isExecError(err) {
4225
4154
  return typeof err === "object" && err !== null;
4226
4155
  }
@@ -4256,7 +4185,7 @@ function runCommand(cmd, cwd, timeout) {
4256
4185
  return { success: true, output: "", timedOut: false };
4257
4186
  }
4258
4187
  try {
4259
- const output = execFileSync12(parts[0], parts.slice(1), {
4188
+ const output = execFileSync11(parts[0], parts.slice(1), {
4260
4189
  cwd,
4261
4190
  timeout,
4262
4191
  encoding: "utf-8",
@@ -4345,7 +4274,7 @@ var init_verify_runner = __esm({
4345
4274
  });
4346
4275
 
4347
4276
  // src/observer.ts
4348
- import { execFileSync as execFileSync13 } from "child_process";
4277
+ import { execFileSync as execFileSync12 } from "child_process";
4349
4278
  async function diagnoseFailure(stageName, errorOutput, modifiedFiles, runner, model, options) {
4350
4279
  const context = [
4351
4280
  `Stage: ${stageName}`,
@@ -4428,13 +4357,13 @@ ${modifiedFiles.map((f) => `- ${f}`).join("\n")}` : "No files were modified (bui
4428
4357
  }
4429
4358
  function getModifiedFiles(projectDir) {
4430
4359
  try {
4431
- const staged = execFileSync13("git", ["diff", "--name-only", "--cached"], {
4360
+ const staged = execFileSync12("git", ["diff", "--name-only", "--cached"], {
4432
4361
  encoding: "utf-8",
4433
4362
  cwd: projectDir,
4434
4363
  timeout: 5e3,
4435
4364
  stdio: ["pipe", "pipe", "pipe"]
4436
4365
  }).trim();
4437
- const unstaged = execFileSync13("git", ["diff", "--name-only"], {
4366
+ const unstaged = execFileSync12("git", ["diff", "--name-only"], {
4438
4367
  encoding: "utf-8",
4439
4368
  cwd: projectDir,
4440
4369
  timeout: 5e3,
@@ -4477,8 +4406,8 @@ Error context:
4477
4406
  });
4478
4407
 
4479
4408
  // src/stages/gate.ts
4480
- import * as fs22 from "fs";
4481
- import * as path20 from "path";
4409
+ import * as fs20 from "fs";
4410
+ import * as path18 from "path";
4482
4411
  function executeGateStage(ctx, def) {
4483
4412
  if (ctx.input.dryRun) {
4484
4413
  logger.info(` [dry-run] skipping ${def.name}`);
@@ -4521,7 +4450,7 @@ ${output}
4521
4450
  `);
4522
4451
  }
4523
4452
  }
4524
- fs22.writeFileSync(path20.join(ctx.taskDir, "verify.md"), lines.join(""));
4453
+ fs20.writeFileSync(path18.join(ctx.taskDir, "verify.md"), lines.join(""));
4525
4454
  return {
4526
4455
  outcome: verifyResult.pass ? "completed" : "failed",
4527
4456
  retries: 0
@@ -4536,9 +4465,9 @@ var init_gate = __esm({
4536
4465
  });
4537
4466
 
4538
4467
  // src/stages/verify.ts
4539
- import * as fs23 from "fs";
4540
- import * as path21 from "path";
4541
- import { execFileSync as execFileSync14 } from "child_process";
4468
+ import * as fs21 from "fs";
4469
+ import * as path19 from "path";
4470
+ import { execFileSync as execFileSync13 } from "child_process";
4542
4471
  async function executeVerifyWithAutofix(ctx, def) {
4543
4472
  const maxAttempts = def.maxRetries ?? 2;
4544
4473
  for (let attempt = 0; attempt <= maxAttempts; attempt++) {
@@ -4548,8 +4477,8 @@ async function executeVerifyWithAutofix(ctx, def) {
4548
4477
  return { ...gateResult, retries: attempt };
4549
4478
  }
4550
4479
  if (attempt < maxAttempts) {
4551
- const verifyPath = path21.join(ctx.taskDir, "verify.md");
4552
- const errorOutput = fs23.existsSync(verifyPath) ? fs23.readFileSync(verifyPath, "utf-8") : "Unknown error";
4480
+ const verifyPath = path19.join(ctx.taskDir, "verify.md");
4481
+ const errorOutput = fs21.existsSync(verifyPath) ? fs21.readFileSync(verifyPath, "utf-8") : "Unknown error";
4553
4482
  const modifiedFiles = getModifiedFiles(ctx.projectDir);
4554
4483
  const defaultRunner = getRunnerForStage(ctx, "taskify");
4555
4484
  const diagConfig = getProjectConfig();
@@ -4592,7 +4521,7 @@ ${diagnosis.resolution}`);
4592
4521
  const parts = parseCommand(cmd);
4593
4522
  if (parts.length === 0) return;
4594
4523
  try {
4595
- execFileSync14(parts[0], parts.slice(1), {
4524
+ execFileSync13(parts[0], parts.slice(1), {
4596
4525
  stdio: "pipe",
4597
4526
  timeout: FIX_COMMAND_TIMEOUT_MS
4598
4527
  });
@@ -4645,8 +4574,8 @@ var init_verify = __esm({
4645
4574
  });
4646
4575
 
4647
4576
  // src/review-standalone.ts
4648
- import * as fs24 from "fs";
4649
- import * as path22 from "path";
4577
+ import * as fs22 from "fs";
4578
+ import * as path20 from "path";
4650
4579
  function resolveReviewTarget(input) {
4651
4580
  if (input.prs.length === 0) {
4652
4581
  return {
@@ -4670,8 +4599,8 @@ Or comment on the specific PR: \`@kody review\``
4670
4599
  }
4671
4600
  async function runStandaloneReview(input) {
4672
4601
  const taskId = input.taskId ?? `review-${generateTaskId()}`;
4673
- const taskDir = path22.join(input.projectDir, ".kody", "tasks", taskId);
4674
- fs24.mkdirSync(taskDir, { recursive: true });
4602
+ const taskDir = path20.join(input.projectDir, ".kody", "tasks", taskId);
4603
+ fs22.mkdirSync(taskDir, { recursive: true });
4675
4604
  let diffInstruction = "";
4676
4605
  let filesChangedSection = "";
4677
4606
  if (input.baseBranch) {
@@ -4698,7 +4627,7 @@ ${fileList}`;
4698
4627
  const taskContent = `# ${input.prTitle}
4699
4628
 
4700
4629
  ${input.prBody ?? ""}${diffInstruction}${filesChangedSection}`;
4701
- fs24.writeFileSync(path22.join(taskDir, "task.md"), taskContent);
4630
+ fs22.writeFileSync(path20.join(taskDir, "task.md"), taskContent);
4702
4631
  const reviewDef = STAGES.find((s) => s.name === "review");
4703
4632
  const ctx = {
4704
4633
  taskId,
@@ -4720,10 +4649,10 @@ ${input.prBody ?? ""}${diffInstruction}${filesChangedSection}`;
4720
4649
  error: result2.error ?? "Review stage failed"
4721
4650
  };
4722
4651
  }
4723
- const reviewPath = path22.join(taskDir, "review.md");
4652
+ const reviewPath = path20.join(taskDir, "review.md");
4724
4653
  let reviewContent;
4725
- if (fs24.existsSync(reviewPath)) {
4726
- reviewContent = fs24.readFileSync(reviewPath, "utf-8");
4654
+ if (fs22.existsSync(reviewPath)) {
4655
+ reviewContent = fs22.readFileSync(reviewPath, "utf-8");
4727
4656
  }
4728
4657
  return {
4729
4658
  outcome: "completed",
@@ -4763,8 +4692,8 @@ var init_review_standalone = __esm({
4763
4692
  });
4764
4693
 
4765
4694
  // src/stages/review.ts
4766
- import * as fs25 from "fs";
4767
- import * as path23 from "path";
4695
+ import * as fs23 from "fs";
4696
+ import * as path21 from "path";
4768
4697
  async function executeReviewWithFix(ctx, def) {
4769
4698
  if (ctx.input.dryRun) {
4770
4699
  return { outcome: "completed", retries: 0 };
@@ -4778,11 +4707,11 @@ async function executeReviewWithFix(ctx, def) {
4778
4707
  if (reviewResult.outcome !== "completed") {
4779
4708
  return reviewResult;
4780
4709
  }
4781
- const reviewFile = path23.join(ctx.taskDir, "review.md");
4782
- if (!fs25.existsSync(reviewFile)) {
4710
+ const reviewFile = path21.join(ctx.taskDir, "review.md");
4711
+ if (!fs23.existsSync(reviewFile)) {
4783
4712
  return { outcome: "failed", retries: iteration, error: "review.md not found" };
4784
4713
  }
4785
- const content = fs25.readFileSync(reviewFile, "utf-8");
4714
+ const content = fs23.readFileSync(reviewFile, "utf-8");
4786
4715
  if (detectReviewVerdict(content) !== "fail") {
4787
4716
  return { ...reviewResult, retries: iteration };
4788
4717
  }
@@ -4811,15 +4740,15 @@ var init_review = __esm({
4811
4740
  });
4812
4741
 
4813
4742
  // src/stages/ship.ts
4814
- import * as fs26 from "fs";
4815
- import * as path24 from "path";
4816
- import { execFileSync as execFileSync15 } from "child_process";
4743
+ import * as fs24 from "fs";
4744
+ import * as path22 from "path";
4745
+ import { execFileSync as execFileSync14 } from "child_process";
4817
4746
  function buildPrBody(ctx) {
4818
4747
  const sections = [];
4819
- const taskJsonPath = path24.join(ctx.taskDir, "task.json");
4820
- if (fs26.existsSync(taskJsonPath)) {
4748
+ const taskJsonPath = path22.join(ctx.taskDir, "task.json");
4749
+ if (fs24.existsSync(taskJsonPath)) {
4821
4750
  try {
4822
- const raw = fs26.readFileSync(taskJsonPath, "utf-8");
4751
+ const raw = fs24.readFileSync(taskJsonPath, "utf-8");
4823
4752
  const cleaned = raw.replace(/^```json\s*\n?/m, "").replace(/\n?```\s*$/m, "");
4824
4753
  const task = JSON.parse(cleaned);
4825
4754
  if (task.description) {
@@ -4838,9 +4767,9 @@ ${task.scope.map((s) => `- \`${s}\``).join("\n")}`);
4838
4767
  } catch {
4839
4768
  }
4840
4769
  }
4841
- const reviewPath = path24.join(ctx.taskDir, "review.md");
4842
- if (fs26.existsSync(reviewPath)) {
4843
- const review = fs26.readFileSync(reviewPath, "utf-8");
4770
+ const reviewPath = path22.join(ctx.taskDir, "review.md");
4771
+ if (fs24.existsSync(reviewPath)) {
4772
+ const review = fs24.readFileSync(reviewPath, "utf-8");
4844
4773
  const summaryMatch = review.match(/## Summary\s*\n([\s\S]*?)(?=\n## |\n*$)/);
4845
4774
  if (summaryMatch) {
4846
4775
  const summary = summaryMatch[1].trim();
@@ -4857,14 +4786,14 @@ ${summary}`);
4857
4786
  **Review:** ${verdictMatch[1].toUpperCase() === "PASS" ? "\u2705 PASS" : "\u274C FAIL"}`);
4858
4787
  }
4859
4788
  }
4860
- const verifyPath = path24.join(ctx.taskDir, "verify.md");
4861
- if (fs26.existsSync(verifyPath)) {
4862
- const verify = fs26.readFileSync(verifyPath, "utf-8");
4789
+ const verifyPath = path22.join(ctx.taskDir, "verify.md");
4790
+ if (fs24.existsSync(verifyPath)) {
4791
+ const verify = fs24.readFileSync(verifyPath, "utf-8");
4863
4792
  if (/PASS/i.test(verify)) sections.push(`**Verify:** \u2705 typecheck + tests + lint passed`);
4864
4793
  }
4865
- const planPath = path24.join(ctx.taskDir, "plan.md");
4866
- if (fs26.existsSync(planPath)) {
4867
- const plan = fs26.readFileSync(planPath, "utf-8").trim();
4794
+ const planPath = path22.join(ctx.taskDir, "plan.md");
4795
+ if (fs24.existsSync(planPath)) {
4796
+ const plan = fs24.readFileSync(planPath, "utf-8").trim();
4868
4797
  if (plan) {
4869
4798
  const truncated = plan.length > 800 ? plan.slice(0, 800) + "\n..." : plan;
4870
4799
  sections.push(`
@@ -4884,25 +4813,25 @@ Closes #${ctx.input.issueNumber}`);
4884
4813
  return sections.join("\n");
4885
4814
  }
4886
4815
  function executeShipStage(ctx, _def) {
4887
- const shipPath = path24.join(ctx.taskDir, "ship.md");
4816
+ const shipPath = path22.join(ctx.taskDir, "ship.md");
4888
4817
  if (ctx.input.dryRun) {
4889
- fs26.writeFileSync(shipPath, "# Ship\n\nShip stage skipped \u2014 dry run.\n");
4818
+ fs24.writeFileSync(shipPath, "# Ship\n\nShip stage skipped \u2014 dry run.\n");
4890
4819
  return { outcome: "completed", outputFile: "ship.md", retries: 0 };
4891
4820
  }
4892
4821
  if (ctx.input.local && !ctx.input.issueNumber) {
4893
- fs26.writeFileSync(shipPath, "# Ship\n\nShip stage skipped \u2014 local mode, no issue number.\n");
4822
+ fs24.writeFileSync(shipPath, "# Ship\n\nShip stage skipped \u2014 local mode, no issue number.\n");
4894
4823
  return { outcome: "completed", outputFile: "ship.md", retries: 0 };
4895
4824
  }
4896
4825
  try {
4897
4826
  const head = getCurrentBranch(ctx.projectDir);
4898
4827
  const base = getDefaultBranch(ctx.projectDir);
4899
4828
  try {
4900
- execFileSync15("git", ["add", ctx.taskDir], {
4829
+ execFileSync14("git", ["add", ctx.taskDir], {
4901
4830
  cwd: ctx.projectDir,
4902
4831
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
4903
4832
  stdio: "pipe"
4904
4833
  });
4905
- execFileSync15("git", ["commit", "--no-gpg-sign", "-m", `chore: add kody task artifacts [skip ci]`], {
4834
+ execFileSync14("git", ["commit", "--no-gpg-sign", "-m", `chore: add kody task artifacts [skip ci]`], {
4906
4835
  cwd: ctx.projectDir,
4907
4836
  env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
4908
4837
  stdio: "pipe"
@@ -4916,7 +4845,7 @@ function executeShipStage(ctx, _def) {
4916
4845
  let repo = config.github?.repo;
4917
4846
  if (!owner || !repo) {
4918
4847
  try {
4919
- const remoteUrl = execFileSync15("git", ["remote", "get-url", "origin"], {
4848
+ const remoteUrl = execFileSync14("git", ["remote", "get-url", "origin"], {
4920
4849
  encoding: "utf-8",
4921
4850
  cwd: ctx.projectDir
4922
4851
  }).trim();
@@ -4937,28 +4866,28 @@ function executeShipStage(ctx, _def) {
4937
4866
  chore: "chore"
4938
4867
  };
4939
4868
  let prefix = "chore";
4940
- const taskJsonPath = path24.join(ctx.taskDir, "task.json");
4941
- if (fs26.existsSync(taskJsonPath)) {
4869
+ const taskJsonPath = path22.join(ctx.taskDir, "task.json");
4870
+ if (fs24.existsSync(taskJsonPath)) {
4942
4871
  try {
4943
- const raw = fs26.readFileSync(taskJsonPath, "utf-8");
4872
+ const raw = fs24.readFileSync(taskJsonPath, "utf-8");
4944
4873
  const cleaned = raw.replace(/^```json\s*\n?/m, "").replace(/\n?```\s*$/m, "");
4945
4874
  const task = JSON.parse(cleaned);
4946
4875
  prefix = TYPE_PREFIX[task.task_type] ?? "chore";
4947
4876
  } catch {
4948
4877
  }
4949
4878
  }
4950
- const taskMdPath = path24.join(ctx.taskDir, "task.md");
4951
- if (fs26.existsSync(taskMdPath)) {
4952
- const content = fs26.readFileSync(taskMdPath, "utf-8");
4879
+ const taskMdPath = path22.join(ctx.taskDir, "task.md");
4880
+ if (fs24.existsSync(taskMdPath)) {
4881
+ const content = fs24.readFileSync(taskMdPath, "utf-8");
4953
4882
  const heading = content.split("\n").find((l) => l.startsWith("# "));
4954
4883
  if (heading) {
4955
4884
  title = `${prefix}: ${heading.replace(/^#\s*/, "").trim()}`.slice(0, 72);
4956
4885
  }
4957
4886
  }
4958
4887
  if (title === "Update") {
4959
- if (fs26.existsSync(taskJsonPath)) {
4888
+ if (fs24.existsSync(taskJsonPath)) {
4960
4889
  try {
4961
- const raw = fs26.readFileSync(taskJsonPath, "utf-8");
4890
+ const raw = fs24.readFileSync(taskJsonPath, "utf-8");
4962
4891
  const cleaned = raw.replace(/^```json\s*\n?/m, "").replace(/\n?```\s*$/m, "");
4963
4892
  const task = JSON.parse(cleaned);
4964
4893
  if (task.title) title = `${prefix}: ${task.title}`.slice(0, 72);
@@ -4981,7 +4910,7 @@ function executeShipStage(ctx, _def) {
4981
4910
  } catch {
4982
4911
  }
4983
4912
  }
4984
- fs26.writeFileSync(shipPath, `# Ship
4913
+ fs24.writeFileSync(shipPath, `# Ship
4985
4914
 
4986
4915
  Updated existing PR: ${existingPr.url}
4987
4916
  PR #${existingPr.number}
@@ -5002,20 +4931,20 @@ PR #${existingPr.number}
5002
4931
  } catch {
5003
4932
  }
5004
4933
  }
5005
- fs26.writeFileSync(shipPath, `# Ship
4934
+ fs24.writeFileSync(shipPath, `# Ship
5006
4935
 
5007
4936
  PR created: ${pr.url}
5008
4937
  PR #${pr.number}
5009
4938
  `);
5010
4939
  } else {
5011
- fs26.writeFileSync(shipPath, "# Ship\n\nPushed branch but failed to create PR.\n");
4940
+ fs24.writeFileSync(shipPath, "# Ship\n\nPushed branch but failed to create PR.\n");
5012
4941
  }
5013
4942
  }
5014
4943
  return { outcome: "completed", outputFile: "ship.md", retries: 0 };
5015
4944
  } catch (err) {
5016
4945
  const msg = err instanceof Error ? err.message : String(err);
5017
4946
  try {
5018
- fs26.writeFileSync(shipPath, `# Ship
4947
+ fs24.writeFileSync(shipPath, `# Ship
5019
4948
 
5020
4949
  Failed: ${msg}
5021
4950
  `);
@@ -5064,15 +4993,15 @@ var init_executor_registry = __esm({
5064
4993
  });
5065
4994
 
5066
4995
  // src/pipeline/questions.ts
5067
- import * as fs27 from "fs";
5068
- import * as path25 from "path";
4996
+ import * as fs25 from "fs";
4997
+ import * as path23 from "path";
5069
4998
  function checkForQuestions(ctx, stageName) {
5070
4999
  if (ctx.input.local || !ctx.input.issueNumber) return false;
5071
5000
  try {
5072
5001
  if (stageName === "taskify") {
5073
- const taskJsonPath = path25.join(ctx.taskDir, "task.json");
5074
- if (!fs27.existsSync(taskJsonPath)) return false;
5075
- const raw = fs27.readFileSync(taskJsonPath, "utf-8");
5002
+ const taskJsonPath = path23.join(ctx.taskDir, "task.json");
5003
+ if (!fs25.existsSync(taskJsonPath)) return false;
5004
+ const raw = fs25.readFileSync(taskJsonPath, "utf-8");
5076
5005
  const cleaned = raw.replace(/^```json\s*\n?/m, "").replace(/\n?```\s*$/m, "");
5077
5006
  const taskJson = JSON.parse(cleaned);
5078
5007
  if (taskJson.questions && Array.isArray(taskJson.questions) && taskJson.questions.length > 0) {
@@ -5087,9 +5016,9 @@ Reply with \`@kody approve\` and your answers in the comment body.`;
5087
5016
  }
5088
5017
  }
5089
5018
  if (stageName === "plan") {
5090
- const planPath = path25.join(ctx.taskDir, "plan.md");
5091
- if (!fs27.existsSync(planPath)) return false;
5092
- const plan = fs27.readFileSync(planPath, "utf-8");
5019
+ const planPath = path23.join(ctx.taskDir, "plan.md");
5020
+ if (!fs25.existsSync(planPath)) return false;
5021
+ const plan = fs25.readFileSync(planPath, "utf-8");
5093
5022
  const questionsMatch = plan.match(/## Questions\s*\n([\s\S]*?)(?=\n## |\n*$)/);
5094
5023
  if (questionsMatch) {
5095
5024
  const questionsText = questionsMatch[1].trim();
@@ -5118,8 +5047,8 @@ var init_questions = __esm({
5118
5047
  });
5119
5048
 
5120
5049
  // src/pipeline/hooks.ts
5121
- import * as fs28 from "fs";
5122
- import * as path26 from "path";
5050
+ import * as fs26 from "fs";
5051
+ import * as path24 from "path";
5123
5052
  function applyPreStageLabel(ctx, def) {
5124
5053
  if (!ctx.input.issueNumber || ctx.input.local) return;
5125
5054
  if (def.name === "plan") setLifecycleLabel(ctx.input.issueNumber, "planning");
@@ -5160,9 +5089,9 @@ function autoDetectComplexity(ctx, def) {
5160
5089
  return { complexity, activeStages };
5161
5090
  }
5162
5091
  try {
5163
- const taskJsonPath = path26.join(ctx.taskDir, "task.json");
5164
- if (!fs28.existsSync(taskJsonPath)) return null;
5165
- const raw = fs28.readFileSync(taskJsonPath, "utf-8");
5092
+ const taskJsonPath = path24.join(ctx.taskDir, "task.json");
5093
+ if (!fs26.existsSync(taskJsonPath)) return null;
5094
+ const raw = fs26.readFileSync(taskJsonPath, "utf-8");
5166
5095
  const cleaned = raw.replace(/^```json\s*\n?/m, "").replace(/\n?```\s*$/m, "");
5167
5096
  const taskJson = JSON.parse(cleaned);
5168
5097
  if (!taskJson.risk_level || !isValidComplexity(taskJson.risk_level)) return null;
@@ -5192,8 +5121,8 @@ function checkRiskGate(ctx, def, state, complexity) {
5192
5121
  if (ctx.input.dryRun || ctx.input.local) return null;
5193
5122
  if (ctx.input.mode === "rerun") return null;
5194
5123
  if (!ctx.input.issueNumber) return null;
5195
- const planPath = path26.join(ctx.taskDir, "plan.md");
5196
- const plan = fs28.existsSync(planPath) ? fs28.readFileSync(planPath, "utf-8").slice(0, 1500) : "(plan not available)";
5124
+ const planPath = path24.join(ctx.taskDir, "plan.md");
5125
+ const plan = fs26.existsSync(planPath) ? fs26.readFileSync(planPath, "utf-8").slice(0, 1500) : "(plan not available)";
5197
5126
  try {
5198
5127
  postComment(
5199
5128
  ctx.input.issueNumber,
@@ -5260,22 +5189,22 @@ var init_hooks = __esm({
5260
5189
  });
5261
5190
 
5262
5191
  // src/learning/auto-learn.ts
5263
- import * as fs29 from "fs";
5264
- import * as path27 from "path";
5192
+ import * as fs27 from "fs";
5193
+ import * as path25 from "path";
5265
5194
  function stripAnsi(str) {
5266
5195
  return str.replace(/\x1b\[[0-9;]*m/g, "");
5267
5196
  }
5268
5197
  function autoLearn(ctx) {
5269
5198
  try {
5270
- const memoryDir = path27.join(ctx.projectDir, ".kody", "memory");
5271
- if (!fs29.existsSync(memoryDir)) {
5272
- fs29.mkdirSync(memoryDir, { recursive: true });
5199
+ const memoryDir = path25.join(ctx.projectDir, ".kody", "memory");
5200
+ if (!fs27.existsSync(memoryDir)) {
5201
+ fs27.mkdirSync(memoryDir, { recursive: true });
5273
5202
  }
5274
5203
  const learnings = [];
5275
5204
  const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
5276
- const verifyPath = path27.join(ctx.taskDir, "verify.md");
5277
- if (fs29.existsSync(verifyPath)) {
5278
- const verify = stripAnsi(fs29.readFileSync(verifyPath, "utf-8"));
5205
+ const verifyPath = path25.join(ctx.taskDir, "verify.md");
5206
+ if (fs27.existsSync(verifyPath)) {
5207
+ const verify = stripAnsi(fs27.readFileSync(verifyPath, "utf-8"));
5279
5208
  if (/vitest/i.test(verify)) learnings.push("- Uses vitest for testing");
5280
5209
  if (/jest/i.test(verify)) learnings.push("- Uses jest for testing");
5281
5210
  if (/eslint/i.test(verify)) learnings.push("- Uses eslint for linting");
@@ -5284,18 +5213,18 @@ function autoLearn(ctx) {
5284
5213
  if (/jsdom/i.test(verify)) learnings.push("- Test environment: jsdom");
5285
5214
  if (/node/i.test(verify) && /environment/i.test(verify)) learnings.push("- Test environment: node");
5286
5215
  }
5287
- const reviewPath = path27.join(ctx.taskDir, "review.md");
5288
- if (fs29.existsSync(reviewPath)) {
5289
- const review = fs29.readFileSync(reviewPath, "utf-8");
5216
+ const reviewPath = path25.join(ctx.taskDir, "review.md");
5217
+ if (fs27.existsSync(reviewPath)) {
5218
+ const review = fs27.readFileSync(reviewPath, "utf-8");
5290
5219
  if (/\.js extension/i.test(review)) learnings.push("- Imports use .js extensions (ESM)");
5291
5220
  if (/barrel export/i.test(review)) learnings.push("- Uses barrel exports (index.ts)");
5292
5221
  if (/timezone/i.test(review)) learnings.push("- Timezone handling is a concern in this codebase");
5293
5222
  if (/UTC/i.test(review)) learnings.push("- Date operations should consider UTC vs local time");
5294
5223
  }
5295
- const taskJsonPath = path27.join(ctx.taskDir, "task.json");
5296
- if (fs29.existsSync(taskJsonPath)) {
5224
+ const taskJsonPath = path25.join(ctx.taskDir, "task.json");
5225
+ if (fs27.existsSync(taskJsonPath)) {
5297
5226
  try {
5298
- const raw = stripAnsi(fs29.readFileSync(taskJsonPath, "utf-8"));
5227
+ const raw = stripAnsi(fs27.readFileSync(taskJsonPath, "utf-8"));
5299
5228
  const cleaned = raw.replace(/^```json\s*\n?/m, "").replace(/\n?```\s*$/m, "");
5300
5229
  const task = JSON.parse(cleaned);
5301
5230
  if (task.scope && Array.isArray(task.scope)) {
@@ -5306,12 +5235,12 @@ function autoLearn(ctx) {
5306
5235
  }
5307
5236
  }
5308
5237
  if (learnings.length > 0) {
5309
- const conventionsPath = path27.join(memoryDir, "conventions.md");
5238
+ const conventionsPath = path25.join(memoryDir, "conventions.md");
5310
5239
  const entry = `
5311
5240
  ## Learned ${timestamp2} (task: ${ctx.taskId})
5312
5241
  ${learnings.join("\n")}
5313
5242
  `;
5314
- fs29.appendFileSync(conventionsPath, entry);
5243
+ fs27.appendFileSync(conventionsPath, entry);
5315
5244
  logger.info(`Auto-learned ${learnings.length} convention(s)`);
5316
5245
  }
5317
5246
  autoLearnArchitecture(ctx.projectDir, memoryDir, timestamp2);
@@ -5319,8 +5248,8 @@ ${learnings.join("\n")}
5319
5248
  }
5320
5249
  }
5321
5250
  function autoLearnArchitecture(projectDir, memoryDir, timestamp2) {
5322
- const archPath = path27.join(memoryDir, "architecture.md");
5323
- if (fs29.existsSync(archPath)) return;
5251
+ const archPath = path25.join(memoryDir, "architecture.md");
5252
+ if (fs27.existsSync(archPath)) return;
5324
5253
  const detected = detectArchitectureBasic(projectDir);
5325
5254
  if (detected.length > 0) {
5326
5255
  const content = `# Architecture (auto-detected ${timestamp2})
@@ -5328,7 +5257,7 @@ function autoLearnArchitecture(projectDir, memoryDir, timestamp2) {
5328
5257
  ## Overview
5329
5258
  ${detected.join("\n")}
5330
5259
  `;
5331
- fs29.writeFileSync(archPath, content);
5260
+ fs27.writeFileSync(archPath, content);
5332
5261
  logger.info(`Auto-detected architecture (${detected.length} items)`);
5333
5262
  }
5334
5263
  }
@@ -5341,13 +5270,13 @@ var init_auto_learn = __esm({
5341
5270
  });
5342
5271
 
5343
5272
  // src/retrospective.ts
5344
- import * as fs30 from "fs";
5345
- import * as path28 from "path";
5273
+ import * as fs28 from "fs";
5274
+ import * as path26 from "path";
5346
5275
  function readArtifact(taskDir, filename, maxChars) {
5347
- const p = path28.join(taskDir, filename);
5348
- if (!fs30.existsSync(p)) return null;
5276
+ const p = path26.join(taskDir, filename);
5277
+ if (!fs28.existsSync(p)) return null;
5349
5278
  try {
5350
- const content = fs30.readFileSync(p, "utf-8");
5279
+ const content = fs28.readFileSync(p, "utf-8");
5351
5280
  return content.length > maxChars ? content.slice(0, maxChars) + "\n...(truncated)" : content;
5352
5281
  } catch {
5353
5282
  return null;
@@ -5400,13 +5329,13 @@ function collectRunContext(ctx, state, pipelineStartTime) {
5400
5329
  return lines.join("\n");
5401
5330
  }
5402
5331
  function getLogPath(projectDir) {
5403
- return path28.join(projectDir, ".kody", "memory", "observer-log.jsonl");
5332
+ return path26.join(projectDir, ".kody", "memory", "observer-log.jsonl");
5404
5333
  }
5405
5334
  function readPreviousRetrospectives(projectDir, limit = 10) {
5406
5335
  const logPath = getLogPath(projectDir);
5407
- if (!fs30.existsSync(logPath)) return [];
5336
+ if (!fs28.existsSync(logPath)) return [];
5408
5337
  try {
5409
- const content = fs30.readFileSync(logPath, "utf-8");
5338
+ const content = fs28.readFileSync(logPath, "utf-8");
5410
5339
  const lines = content.split("\n").filter(Boolean);
5411
5340
  const entries = [];
5412
5341
  const start = Math.max(0, lines.length - limit);
@@ -5433,11 +5362,11 @@ function formatPreviousEntries(entries) {
5433
5362
  }
5434
5363
  function appendRetrospectiveEntry(projectDir, entry) {
5435
5364
  const logPath = getLogPath(projectDir);
5436
- const dir = path28.dirname(logPath);
5437
- if (!fs30.existsSync(dir)) {
5438
- fs30.mkdirSync(dir, { recursive: true });
5365
+ const dir = path26.dirname(logPath);
5366
+ if (!fs28.existsSync(dir)) {
5367
+ fs28.mkdirSync(dir, { recursive: true });
5439
5368
  }
5440
- fs30.appendFileSync(logPath, JSON.stringify(entry) + "\n");
5369
+ fs28.appendFileSync(logPath, JSON.stringify(entry) + "\n");
5441
5370
  }
5442
5371
  async function runRetrospective(ctx, state, pipelineStartTime) {
5443
5372
  if (ctx.input.dryRun) return;
@@ -5604,9 +5533,75 @@ var init_summary = __esm({
5604
5533
  }
5605
5534
  });
5606
5535
 
5536
+ // src/tools.ts
5537
+ import * as fs29 from "fs";
5538
+ import * as path27 from "path";
5539
+ import { execSync as execSync3 } from "child_process";
5540
+ import { parse as parseYaml } from "yaml";
5541
+ function loadToolDeclarations(projectDir) {
5542
+ const toolsPath = path27.join(projectDir, ".kody", "tools.yml");
5543
+ if (!fs29.existsSync(toolsPath)) return [];
5544
+ try {
5545
+ const raw = fs29.readFileSync(toolsPath, "utf-8");
5546
+ const parsed = parseYaml(raw);
5547
+ if (!parsed || typeof parsed !== "object") return [];
5548
+ return Object.entries(parsed).map(([name, value]) => {
5549
+ const v = value;
5550
+ return {
5551
+ name,
5552
+ detect: Array.isArray(v.detect) ? v.detect : [],
5553
+ stages: Array.isArray(v.stages) ? v.stages : [],
5554
+ setup: typeof v.setup === "string" ? v.setup : ""
5555
+ };
5556
+ });
5557
+ } catch (err) {
5558
+ logger.warn(`Failed to parse .kody/tools.yml: ${err instanceof Error ? err.message : String(err)}`);
5559
+ return [];
5560
+ }
5561
+ }
5562
+ function detectTools(declarations, projectDir) {
5563
+ const resolved = [];
5564
+ for (const decl of declarations) {
5565
+ const detected = decl.detect.some((pattern) => fs29.existsSync(path27.join(projectDir, pattern)));
5566
+ if (!detected) continue;
5567
+ resolved.push({
5568
+ name: decl.name,
5569
+ stages: decl.stages,
5570
+ setup: decl.setup
5571
+ });
5572
+ }
5573
+ return resolved;
5574
+ }
5575
+ function runToolSetup(tools, projectDir) {
5576
+ for (const tool of tools) {
5577
+ if (tool.setup) {
5578
+ try {
5579
+ logger.info(` Setting up ${tool.name}: ${tool.setup}`);
5580
+ execSync3(tool.setup, { cwd: projectDir, timeout: 12e4, stdio: "pipe" });
5581
+ logger.info(` \u2713 ${tool.name} setup complete`);
5582
+ } catch (err) {
5583
+ logger.warn(` \u26A0 ${tool.name} setup failed: ${err instanceof Error ? err.message : String(err)}`);
5584
+ }
5585
+ }
5586
+ try {
5587
+ logger.info(` Installing skill for ${tool.name} from skills.sh`);
5588
+ execSync3(`npx skills add --skill ${tool.name} --yes`, { cwd: projectDir, timeout: 6e4, stdio: "pipe" });
5589
+ logger.info(` \u2713 ${tool.name} skill installed`);
5590
+ } catch (err) {
5591
+ logger.warn(` \u26A0 ${tool.name} skill install failed: ${err instanceof Error ? err.message : String(err)}`);
5592
+ }
5593
+ }
5594
+ }
5595
+ var init_tools = __esm({
5596
+ "src/tools.ts"() {
5597
+ "use strict";
5598
+ init_logger();
5599
+ }
5600
+ });
5601
+
5607
5602
  // src/pipeline.ts
5608
- import * as fs31 from "fs";
5609
- import * as path29 from "path";
5603
+ import * as fs30 from "fs";
5604
+ import * as path28 from "path";
5610
5605
  function ensureFeatureBranchIfNeeded(ctx) {
5611
5606
  if (ctx.input.dryRun) return;
5612
5607
  if (ctx.input.prNumber) {
@@ -5619,8 +5614,8 @@ function ensureFeatureBranchIfNeeded(ctx) {
5619
5614
  }
5620
5615
  if (!ctx.input.issueNumber) return;
5621
5616
  try {
5622
- const taskMdPath = path29.join(ctx.taskDir, "task.md");
5623
- const title = fs31.existsSync(taskMdPath) ? fs31.readFileSync(taskMdPath, "utf-8").split("\n")[0].slice(0, 50) : ctx.taskId;
5617
+ const taskMdPath = path28.join(ctx.taskDir, "task.md");
5618
+ const title = fs30.existsSync(taskMdPath) ? fs30.readFileSync(taskMdPath, "utf-8").split("\n")[0].slice(0, 50) : ctx.taskId;
5624
5619
  ensureFeatureBranch(ctx.input.issueNumber, title, ctx.projectDir);
5625
5620
  syncWithDefault(ctx.projectDir);
5626
5621
  } catch (err) {
@@ -5634,10 +5629,10 @@ function ensureFeatureBranchIfNeeded(ctx) {
5634
5629
  }
5635
5630
  }
5636
5631
  function acquireLock(taskDir) {
5637
- const lockPath = path29.join(taskDir, ".lock");
5638
- if (fs31.existsSync(lockPath)) {
5632
+ const lockPath = path28.join(taskDir, ".lock");
5633
+ if (fs30.existsSync(lockPath)) {
5639
5634
  try {
5640
- const pid = parseInt(fs31.readFileSync(lockPath, "utf-8").trim(), 10);
5635
+ const pid = parseInt(fs30.readFileSync(lockPath, "utf-8").trim(), 10);
5641
5636
  if (!isNaN(pid)) {
5642
5637
  try {
5643
5638
  process.kill(pid, 0);
@@ -5654,14 +5649,14 @@ function acquireLock(taskDir) {
5654
5649
  logger.warn(` Corrupt lock file \u2014 overwriting`);
5655
5650
  }
5656
5651
  try {
5657
- fs31.unlinkSync(lockPath);
5652
+ fs30.unlinkSync(lockPath);
5658
5653
  } catch {
5659
5654
  }
5660
5655
  }
5661
5656
  try {
5662
- const fd = fs31.openSync(lockPath, fs31.constants.O_WRONLY | fs31.constants.O_CREAT | fs31.constants.O_EXCL);
5663
- fs31.writeSync(fd, String(process.pid));
5664
- fs31.closeSync(fd);
5657
+ const fd = fs30.openSync(lockPath, fs30.constants.O_WRONLY | fs30.constants.O_CREAT | fs30.constants.O_EXCL);
5658
+ fs30.writeSync(fd, String(process.pid));
5659
+ fs30.closeSync(fd);
5665
5660
  } catch (err) {
5666
5661
  if (err.code === "EEXIST") {
5667
5662
  throw new Error("Pipeline already running (lock acquired by another process)");
@@ -5671,7 +5666,7 @@ function acquireLock(taskDir) {
5671
5666
  }
5672
5667
  function releaseLock(taskDir) {
5673
5668
  try {
5674
- fs31.unlinkSync(path29.join(taskDir, ".lock"));
5669
+ fs30.unlinkSync(path28.join(taskDir, ".lock"));
5675
5670
  } catch {
5676
5671
  }
5677
5672
  }
@@ -5885,8 +5880,8 @@ var init_pipeline = __esm({
5885
5880
  });
5886
5881
 
5887
5882
  // src/preflight.ts
5888
- import { execFileSync as execFileSync16 } from "child_process";
5889
- import * as fs32 from "fs";
5883
+ import { execFileSync as execFileSync15 } from "child_process";
5884
+ import * as fs31 from "fs";
5890
5885
  function check(name, fn) {
5891
5886
  try {
5892
5887
  const detail = fn() ?? void 0;
@@ -5898,7 +5893,7 @@ function check(name, fn) {
5898
5893
  function runPreflight() {
5899
5894
  const checks = [
5900
5895
  check("claude CLI", () => {
5901
- const v = execFileSync16("claude", ["--version"], {
5896
+ const v = execFileSync15("claude", ["--version"], {
5902
5897
  encoding: "utf-8",
5903
5898
  timeout: 1e4,
5904
5899
  stdio: ["pipe", "pipe", "pipe"]
@@ -5906,14 +5901,14 @@ function runPreflight() {
5906
5901
  return v;
5907
5902
  }),
5908
5903
  check("git repo", () => {
5909
- execFileSync16("git", ["rev-parse", "--is-inside-work-tree"], {
5904
+ execFileSync15("git", ["rev-parse", "--is-inside-work-tree"], {
5910
5905
  encoding: "utf-8",
5911
5906
  timeout: 5e3,
5912
5907
  stdio: ["pipe", "pipe", "pipe"]
5913
5908
  });
5914
5909
  }),
5915
5910
  check("pnpm", () => {
5916
- const v = execFileSync16("pnpm", ["--version"], {
5911
+ const v = execFileSync15("pnpm", ["--version"], {
5917
5912
  encoding: "utf-8",
5918
5913
  timeout: 5e3,
5919
5914
  stdio: ["pipe", "pipe", "pipe"]
@@ -5921,7 +5916,7 @@ function runPreflight() {
5921
5916
  return v;
5922
5917
  }),
5923
5918
  check("node >= 18", () => {
5924
- const v = execFileSync16("node", ["--version"], {
5919
+ const v = execFileSync15("node", ["--version"], {
5925
5920
  encoding: "utf-8",
5926
5921
  timeout: 5e3,
5927
5922
  stdio: ["pipe", "pipe", "pipe"]
@@ -5931,7 +5926,7 @@ function runPreflight() {
5931
5926
  return v;
5932
5927
  }),
5933
5928
  check("gh CLI", () => {
5934
- const v = execFileSync16("gh", ["--version"], {
5929
+ const v = execFileSync15("gh", ["--version"], {
5935
5930
  encoding: "utf-8",
5936
5931
  timeout: 5e3,
5937
5932
  stdio: ["pipe", "pipe", "pipe"]
@@ -5939,7 +5934,7 @@ function runPreflight() {
5939
5934
  return v;
5940
5935
  }),
5941
5936
  check("package.json", () => {
5942
- if (!fs32.existsSync("package.json")) throw new Error("not found");
5937
+ if (!fs31.existsSync("package.json")) throw new Error("not found");
5943
5938
  })
5944
5939
  ];
5945
5940
  const failed = checks.filter((c) => !c.ok);
@@ -6016,8 +6011,8 @@ var init_args = __esm({
6016
6011
  });
6017
6012
 
6018
6013
  // src/cli/task-state.ts
6019
- import * as fs33 from "fs";
6020
- import * as path30 from "path";
6014
+ import * as fs32 from "fs";
6015
+ import * as path29 from "path";
6021
6016
  function resolveTaskAction(issueNumber, existingTaskId, existingState) {
6022
6017
  if (!existingTaskId || !existingState) {
6023
6018
  return { action: "start-fresh", taskId: `${issueNumber}-${generateTaskId()}` };
@@ -6049,11 +6044,11 @@ function resolveTaskAction(issueNumber, existingTaskId, existingState) {
6049
6044
  function resolveForIssue(issueNumber, projectDir) {
6050
6045
  const existingTaskId = findLatestTaskForIssue(issueNumber, projectDir);
6051
6046
  if (existingTaskId) {
6052
- const statusPath = path30.join(projectDir, ".kody", "tasks", existingTaskId, "status.json");
6047
+ const statusPath = path29.join(projectDir, ".kody", "tasks", existingTaskId, "status.json");
6053
6048
  let existingState = null;
6054
- if (fs33.existsSync(statusPath)) {
6049
+ if (fs32.existsSync(statusPath)) {
6055
6050
  try {
6056
- existingState = JSON.parse(fs33.readFileSync(statusPath, "utf-8"));
6051
+ existingState = JSON.parse(fs32.readFileSync(statusPath, "utf-8"));
6057
6052
  } catch {
6058
6053
  }
6059
6054
  }
@@ -6086,12 +6081,12 @@ var resolve_exports = {};
6086
6081
  __export(resolve_exports, {
6087
6082
  runResolve: () => runResolve
6088
6083
  });
6089
- import { execFileSync as execFileSync17 } from "child_process";
6084
+ import { execFileSync as execFileSync16 } from "child_process";
6090
6085
  function getConflictContext(cwd, files) {
6091
6086
  const parts = [];
6092
6087
  for (const file of files.slice(0, 10)) {
6093
6088
  try {
6094
- const content = execFileSync17("git", ["diff", file], {
6089
+ const content = execFileSync16("git", ["diff", file], {
6095
6090
  cwd,
6096
6091
  encoding: "utf-8",
6097
6092
  stdio: ["pipe", "pipe", "pipe"]
@@ -6210,8 +6205,8 @@ var init_resolve = __esm({
6210
6205
 
6211
6206
  // src/entry.ts
6212
6207
  var entry_exports = {};
6213
- import * as fs34 from "fs";
6214
- import * as path31 from "path";
6208
+ import * as fs33 from "fs";
6209
+ import * as path30 from "path";
6215
6210
  async function ensureLitellmProxy(config, projectDir) {
6216
6211
  if (!anyStageNeedsProxy(config)) return null;
6217
6212
  const litellmUrl = getLitellmUrl();
@@ -6266,9 +6261,9 @@ async function runModelHealthCheck(config) {
6266
6261
  }
6267
6262
  async function main() {
6268
6263
  const input = parseArgs();
6269
- const projectDir = input.cwd ? path31.resolve(input.cwd) : process.cwd();
6264
+ const projectDir = input.cwd ? path30.resolve(input.cwd) : process.cwd();
6270
6265
  if (input.cwd) {
6271
- if (!fs34.existsSync(projectDir)) {
6266
+ if (!fs33.existsSync(projectDir)) {
6272
6267
  console.error(`--cwd path does not exist: ${projectDir}`);
6273
6268
  process.exit(1);
6274
6269
  }
@@ -6328,8 +6323,8 @@ async function main() {
6328
6323
  process.exit(1);
6329
6324
  }
6330
6325
  }
6331
- const taskDir = path31.join(projectDir, ".kody", "tasks", taskId);
6332
- fs34.mkdirSync(taskDir, { recursive: true });
6326
+ const taskDir = path30.join(projectDir, ".kody", "tasks", taskId);
6327
+ fs33.mkdirSync(taskDir, { recursive: true });
6333
6328
  if (input.command === "rerun" && isTaskifyRun(taskDir)) {
6334
6329
  const marker = readTaskifyMarker(taskDir);
6335
6330
  if (marker) {
@@ -6461,31 +6456,31 @@ async function main() {
6461
6456
  logger.info("Preflight checks:");
6462
6457
  runPreflight();
6463
6458
  if (input.task) {
6464
- fs34.writeFileSync(path31.join(taskDir, "task.md"), input.task);
6459
+ fs33.writeFileSync(path30.join(taskDir, "task.md"), input.task);
6465
6460
  }
6466
- const taskMdPath = path31.join(taskDir, "task.md");
6467
- if (!fs34.existsSync(taskMdPath) && isPRFix && input.prNumber) {
6461
+ const taskMdPath = path30.join(taskDir, "task.md");
6462
+ if (!fs33.existsSync(taskMdPath) && isPRFix && input.prNumber) {
6468
6463
  logger.info(`Fetching PR #${input.prNumber} details as task context...`);
6469
6464
  const prDetails = getPRDetails(input.prNumber);
6470
6465
  if (prDetails) {
6471
6466
  const taskContent = `# ${prDetails.title}
6472
6467
 
6473
6468
  ${prDetails.body ?? ""}`;
6474
- fs34.writeFileSync(taskMdPath, taskContent);
6469
+ fs33.writeFileSync(taskMdPath, taskContent);
6475
6470
  logger.info(` Task loaded from PR #${input.prNumber}: ${prDetails.title}`);
6476
6471
  }
6477
- } else if (!fs34.existsSync(taskMdPath) && input.issueNumber) {
6472
+ } else if (!fs33.existsSync(taskMdPath) && input.issueNumber) {
6478
6473
  logger.info(`Fetching issue #${input.issueNumber} body as task...`);
6479
6474
  const issue = getIssue(input.issueNumber);
6480
6475
  if (issue) {
6481
6476
  const taskContent = `# ${issue.title}
6482
6477
 
6483
6478
  ${issue.body ?? ""}`;
6484
- fs34.writeFileSync(taskMdPath, taskContent);
6479
+ fs33.writeFileSync(taskMdPath, taskContent);
6485
6480
  logger.info(` Task loaded from issue #${input.issueNumber}: ${issue.title}`);
6486
6481
  }
6487
6482
  }
6488
- if (!fs34.existsSync(taskMdPath)) {
6483
+ if (!fs33.existsSync(taskMdPath)) {
6489
6484
  console.error("No task.md found. Provide --task, --issue-number, or ensure .kody/tasks/<id>/task.md exists.");
6490
6485
  process.exit(1);
6491
6486
  }
@@ -6629,7 +6624,7 @@ To rerun: \`@kody rerun ${taskId} --from <stage>\``
6629
6624
  }
6630
6625
  }
6631
6626
  const state = await runPipeline(ctx);
6632
- const files = fs34.readdirSync(taskDir);
6627
+ const files = fs33.readdirSync(taskDir);
6633
6628
  console.log(`
6634
6629
  Artifacts in ${taskDir}:`);
6635
6630
  for (const f of files) {
@@ -6695,8 +6690,8 @@ var init_entry = __esm({
6695
6690
  });
6696
6691
 
6697
6692
  // src/bin/cli.ts
6698
- import * as fs35 from "fs";
6699
- import * as path32 from "path";
6693
+ import * as fs34 from "fs";
6694
+ import * as path31 from "path";
6700
6695
  import { fileURLToPath as fileURLToPath2 } from "url";
6701
6696
 
6702
6697
  // src/bin/commands/init.ts
@@ -7068,9 +7063,9 @@ function initCommand(opts, pkgRoot) {
7068
7063
 
7069
7064
  // src/bin/commands/bootstrap.ts
7070
7065
  init_architecture_detection();
7071
- import * as fs9 from "fs";
7072
- import * as path8 from "path";
7073
- import { execFileSync as execFileSync5 } from "child_process";
7066
+ import * as fs8 from "fs";
7067
+ import * as path7 from "path";
7068
+ import { execFileSync as execFileSync4 } from "child_process";
7074
7069
 
7075
7070
  // src/bin/qa-guide.ts
7076
7071
  import * as fs6 from "fs";
@@ -7580,100 +7575,6 @@ Required env vars: ${discovery.envVars.join(", ")}`);
7580
7575
  return result2;
7581
7576
  }
7582
7577
 
7583
- // src/bin/skills.ts
7584
- import * as fs7 from "fs";
7585
- import * as path6 from "path";
7586
- import { execFileSync as execFileSync4 } from "child_process";
7587
- var SKILL_MAPPINGS = [
7588
- {
7589
- detect: (deps) => "next" in deps,
7590
- skills: [
7591
- { package: "vercel-labs/agent-skills@vercel-react-best-practices", label: "React best practices (Vercel)" }
7592
- ]
7593
- },
7594
- {
7595
- detect: (deps) => "react" in deps && !("next" in deps),
7596
- skills: [
7597
- { package: "vercel-labs/agent-skills@vercel-react-best-practices", label: "React best practices (Vercel)" }
7598
- ]
7599
- },
7600
- {
7601
- detect: (deps) => FRONTEND_DEPS.some((d) => d in deps),
7602
- skills: [
7603
- { package: "microsoft/playwright-cli@playwright-cli", label: "Playwright browser automation" }
7604
- ]
7605
- }
7606
- ];
7607
- function detectSkillsForProject(cwd) {
7608
- const pkgPath = path6.join(cwd, "package.json");
7609
- if (!fs7.existsSync(pkgPath)) return [];
7610
- try {
7611
- const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
7612
- const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
7613
- const seen = /* @__PURE__ */ new Set();
7614
- const skills = [];
7615
- for (const mapping of SKILL_MAPPINGS) {
7616
- if (mapping.detect(allDeps)) {
7617
- for (const skill of mapping.skills) {
7618
- if (!seen.has(skill.package)) {
7619
- seen.add(skill.package);
7620
- skills.push(skill);
7621
- }
7622
- }
7623
- }
7624
- }
7625
- return skills;
7626
- } catch {
7627
- return [];
7628
- }
7629
- }
7630
- function installSkillsForProject(cwd) {
7631
- const skills = detectSkillsForProject(cwd);
7632
- if (skills.length === 0) {
7633
- console.log(" \u25CB No skills to install (no frontend framework detected)");
7634
- return [];
7635
- }
7636
- let installedSkills = {};
7637
- const lockPath = path6.join(cwd, "skills-lock.json");
7638
- if (fs7.existsSync(lockPath)) {
7639
- try {
7640
- const lock = JSON.parse(fs7.readFileSync(lockPath, "utf-8"));
7641
- installedSkills = lock.skills ?? {};
7642
- } catch {
7643
- }
7644
- }
7645
- const installedPaths = [];
7646
- for (const skill of skills) {
7647
- const skillName = skill.package.split("@").pop() ?? "";
7648
- if (skillName in installedSkills) {
7649
- console.log(` \u25CB ${skill.label} \u2014 already installed`);
7650
- const agentPath = `.agents/skills/${skillName}`;
7651
- const claudePath = `.claude/skills/${skillName}`;
7652
- if (fs7.existsSync(path6.join(cwd, agentPath))) installedPaths.push(agentPath);
7653
- if (fs7.existsSync(path6.join(cwd, claudePath))) installedPaths.push(claudePath);
7654
- continue;
7655
- }
7656
- try {
7657
- console.log(` Installing: ${skill.label} (${skill.package})`);
7658
- execFileSync4("npx", ["skills", "add", skill.package, "--yes"], {
7659
- cwd,
7660
- encoding: "utf-8",
7661
- timeout: 6e4,
7662
- stdio: ["pipe", "pipe", "pipe"]
7663
- });
7664
- const installedName = skill.package.split("@").pop() ?? "";
7665
- const agentPath = `.agents/skills/${installedName}`;
7666
- const claudePath = `.claude/skills/${installedName}`;
7667
- if (fs7.existsSync(path6.join(cwd, agentPath))) installedPaths.push(agentPath);
7668
- if (fs7.existsSync(path6.join(cwd, claudePath))) installedPaths.push(claudePath);
7669
- console.log(` \u2713 ${skill.label}`);
7670
- } catch {
7671
- console.log(` \u2717 ${skill.label} \u2014 failed to install`);
7672
- }
7673
- }
7674
- return installedPaths;
7675
- }
7676
-
7677
7578
  // src/bin/commands/bootstrap.ts
7678
7579
  init_config();
7679
7580
 
@@ -7703,20 +7604,20 @@ ${content}
7703
7604
  // src/bin/commands/bootstrap.ts
7704
7605
  var STEP_STAGES = ["taskify", "plan", "build", "autofix", "review", "review-fix"];
7705
7606
  function gatherSampleSourceFiles(cwd, maxFiles = 3, maxCharsEach = 2e3) {
7706
- const srcDir = path8.join(cwd, "src");
7707
- const baseDir = fs9.existsSync(srcDir) ? srcDir : cwd;
7607
+ const srcDir = path7.join(cwd, "src");
7608
+ const baseDir = fs8.existsSync(srcDir) ? srcDir : cwd;
7708
7609
  const results = [];
7709
7610
  function walk(dir) {
7710
7611
  const entries = [];
7711
7612
  try {
7712
- for (const entry of fs9.readdirSync(dir, { withFileTypes: true })) {
7613
+ for (const entry of fs8.readdirSync(dir, { withFileTypes: true })) {
7713
7614
  if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
7714
- const full = path8.join(dir, entry.name);
7615
+ const full = path7.join(dir, entry.name);
7715
7616
  if (entry.isDirectory()) {
7716
7617
  entries.push(...walk(full));
7717
7618
  } else if (/\.(ts|js)$/.test(entry.name) && !/\.(test|spec|config|d)\.(ts|js)$/.test(entry.name)) {
7718
7619
  try {
7719
- const stat = fs9.statSync(full);
7620
+ const stat = fs8.statSync(full);
7720
7621
  if (stat.size >= 200 && stat.size <= 5e3) {
7721
7622
  entries.push({ filePath: full, size: stat.size });
7722
7623
  }
@@ -7730,8 +7631,8 @@ function gatherSampleSourceFiles(cwd, maxFiles = 3, maxCharsEach = 2e3) {
7730
7631
  }
7731
7632
  const files = walk(baseDir).sort((a, b) => b.size - a.size).slice(0, maxFiles);
7732
7633
  for (const { filePath } of files) {
7733
- const rel = path8.relative(cwd, filePath);
7734
- const content = fs9.readFileSync(filePath, "utf-8").slice(0, maxCharsEach);
7634
+ const rel = path7.relative(cwd, filePath);
7635
+ const content = fs8.readFileSync(filePath, "utf-8").slice(0, maxCharsEach);
7735
7636
  results.push(`### File: ${rel}
7736
7637
  \`\`\`typescript
7737
7638
  ${content}
@@ -7743,9 +7644,9 @@ function ghComment(issueNumber, body, cwd) {
7743
7644
  try {
7744
7645
  let repoSlug = "";
7745
7646
  try {
7746
- const configPath = path8.join(cwd, "kody.config.json");
7747
- if (fs9.existsSync(configPath)) {
7748
- const config = JSON.parse(fs9.readFileSync(configPath, "utf-8"));
7647
+ const configPath = path7.join(cwd, "kody.config.json");
7648
+ if (fs8.existsSync(configPath)) {
7649
+ const config = JSON.parse(fs8.readFileSync(configPath, "utf-8"));
7749
7650
  if (config.github?.owner && config.github?.repo) {
7750
7651
  repoSlug = `${config.github.owner}/${config.github.repo}`;
7751
7652
  }
@@ -7753,7 +7654,7 @@ function ghComment(issueNumber, body, cwd) {
7753
7654
  } catch {
7754
7655
  }
7755
7656
  if (!repoSlug) return;
7756
- execFileSync5("gh", [
7657
+ execFileSync4("gh", [
7757
7658
  "issue",
7758
7659
  "comment",
7759
7660
  String(issueNumber),
@@ -7782,8 +7683,8 @@ function bootstrapCommand(opts, pkgRoot) {
7782
7683
  ghComment(issueNumber, "\u{1F527} **Bootstrap started** \u2014 analyzing project and generating configuration...", cwd);
7783
7684
  }
7784
7685
  const readIfExists = (rel, maxChars = 3e3) => {
7785
- const p = path8.join(cwd, rel);
7786
- if (fs9.existsSync(p)) return fs9.readFileSync(p, "utf-8").slice(0, maxChars);
7686
+ const p = path7.join(cwd, rel);
7687
+ if (fs8.existsSync(p)) return fs8.readFileSync(p, "utf-8").slice(0, maxChars);
7787
7688
  return null;
7788
7689
  };
7789
7690
  let repoContext = "";
@@ -7818,14 +7719,14 @@ ${sampleFiles}
7818
7719
 
7819
7720
  `;
7820
7721
  try {
7821
- const topDirs = fs9.readdirSync(cwd, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name);
7722
+ const topDirs = fs8.readdirSync(cwd, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name);
7822
7723
  repoContext += `## Top-level directories
7823
7724
  ${topDirs.join(", ")}
7824
7725
 
7825
7726
  `;
7826
- const srcDir = path8.join(cwd, "src");
7827
- if (fs9.existsSync(srcDir)) {
7828
- const srcDirs = fs9.readdirSync(srcDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
7727
+ const srcDir = path7.join(cwd, "src");
7728
+ if (fs8.existsSync(srcDir)) {
7729
+ const srcDirs = fs8.readdirSync(srcDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
7829
7730
  if (srcDirs.length > 0) repoContext += `## src/ subdirectories
7830
7731
  ${srcDirs.join(", ")}
7831
7732
 
@@ -7835,19 +7736,19 @@ ${srcDirs.join(", ")}
7835
7736
  }
7836
7737
  const existingFiles = [];
7837
7738
  for (const f of [".env.example", "CLAUDE.md", ".ai-docs", "vitest.config.ts", "vitest.config.mts", "jest.config.ts", "playwright.config.ts", ".eslintrc.js", "eslint.config.mjs", ".prettierrc"]) {
7838
- if (fs9.existsSync(path8.join(cwd, f))) existingFiles.push(f);
7739
+ if (fs8.existsSync(path7.join(cwd, f))) existingFiles.push(f);
7839
7740
  }
7840
7741
  if (existingFiles.length) repoContext += `## Config files present
7841
7742
  ${existingFiles.join(", ")}
7842
7743
 
7843
7744
  `;
7844
7745
  console.log("\u2500\u2500 Project Memory \u2500\u2500");
7845
- const memoryDir = path8.join(cwd, ".kody", "memory");
7846
- fs9.mkdirSync(memoryDir, { recursive: true });
7847
- const archPath = path8.join(memoryDir, "architecture.md");
7848
- const conventionsPath = path8.join(memoryDir, "conventions.md");
7849
- const existingArch = fs9.existsSync(archPath) ? fs9.readFileSync(archPath, "utf-8") : "";
7850
- const existingConv = fs9.existsSync(conventionsPath) ? fs9.readFileSync(conventionsPath, "utf-8") : "";
7746
+ const memoryDir = path7.join(cwd, ".kody", "memory");
7747
+ fs8.mkdirSync(memoryDir, { recursive: true });
7748
+ const archPath = path7.join(memoryDir, "architecture.md");
7749
+ const conventionsPath = path7.join(memoryDir, "conventions.md");
7750
+ const existingArch = fs8.existsSync(archPath) ? fs8.readFileSync(archPath, "utf-8") : "";
7751
+ const existingConv = fs8.existsSync(conventionsPath) ? fs8.readFileSync(conventionsPath, "utf-8") : "";
7851
7752
  const hasExisting = !!(existingArch || existingConv);
7852
7753
  const extendInstruction = hasExisting && !opts.force ? buildExtendInstruction(
7853
7754
  `### architecture.md:
@@ -7882,7 +7783,7 @@ Output ONLY valid JSON. No markdown fences. No explanation.
7882
7783
  ${repoContext}`;
7883
7784
  console.log(" \u23F3 Analyzing project...");
7884
7785
  try {
7885
- const output = execFileSync5("claude", [
7786
+ const output = execFileSync4("claude", [
7886
7787
  "--print",
7887
7788
  "--model",
7888
7789
  bootstrapModel,
@@ -7897,12 +7798,12 @@ ${repoContext}`;
7897
7798
  const cleaned = output.replace(/^```json\s*\n?/m, "").replace(/\n?```\s*$/m, "");
7898
7799
  const parsed = JSON.parse(cleaned);
7899
7800
  if (parsed.architecture) {
7900
- fs9.writeFileSync(archPath, parsed.architecture);
7801
+ fs8.writeFileSync(archPath, parsed.architecture);
7901
7802
  const lineCount = parsed.architecture.split("\n").length;
7902
7803
  console.log(` \u2713 .kody/memory/architecture.md (${lineCount} lines)`);
7903
7804
  }
7904
7805
  if (parsed.conventions) {
7905
- fs9.writeFileSync(conventionsPath, parsed.conventions);
7806
+ fs8.writeFileSync(conventionsPath, parsed.conventions);
7906
7807
  const lineCount = parsed.conventions.split("\n").length;
7907
7808
  console.log(` \u2713 .kody/memory/conventions.md (${lineCount} lines)`);
7908
7809
  }
@@ -7911,37 +7812,37 @@ ${repoContext}`;
7911
7812
  const detected = detectArchitectureBasic(cwd);
7912
7813
  if (detected.length > 0) {
7913
7814
  const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
7914
- fs9.writeFileSync(archPath, `# Architecture (auto-detected ${timestamp2})
7815
+ fs8.writeFileSync(archPath, `# Architecture (auto-detected ${timestamp2})
7915
7816
 
7916
7817
  ## Overview
7917
7818
  ${detected.join("\n")}
7918
7819
  `);
7919
7820
  console.log(` \u2713 .kody/memory/architecture.md (${detected.length} items, basic detection)`);
7920
7821
  }
7921
- fs9.writeFileSync(conventionsPath, "# Conventions\n\n<!-- Auto-learned conventions will be appended here -->\n");
7822
+ fs8.writeFileSync(conventionsPath, "# Conventions\n\n<!-- Auto-learned conventions will be appended here -->\n");
7922
7823
  console.log(" \u2713 .kody/memory/conventions.md (seed)");
7923
7824
  }
7924
7825
  console.log("\n\u2500\u2500 Step Files \u2500\u2500");
7925
- const stepsDir = path8.join(cwd, ".kody", "steps");
7926
- fs9.mkdirSync(stepsDir, { recursive: true });
7927
- const arch = fs9.existsSync(archPath) ? fs9.readFileSync(archPath, "utf-8") : "";
7928
- const conv = fs9.existsSync(conventionsPath) ? fs9.readFileSync(conventionsPath, "utf-8") : "";
7826
+ const stepsDir = path7.join(cwd, ".kody", "steps");
7827
+ fs8.mkdirSync(stepsDir, { recursive: true });
7828
+ const arch = fs8.existsSync(archPath) ? fs8.readFileSync(archPath, "utf-8") : "";
7829
+ const conv = fs8.existsSync(conventionsPath) ? fs8.readFileSync(conventionsPath, "utf-8") : "";
7929
7830
  console.log(" \u23F3 Customizing step files...");
7930
7831
  let stepCount = 0;
7931
7832
  for (const stage of STEP_STAGES) {
7932
- const templatePath = path8.join(pkgRoot, "prompts", `${stage}.md`);
7933
- if (!fs9.existsSync(templatePath)) {
7833
+ const templatePath = path7.join(pkgRoot, "prompts", `${stage}.md`);
7834
+ if (!fs8.existsSync(templatePath)) {
7934
7835
  console.log(` \u2717 ${stage}.md \u2014 template not found in engine`);
7935
7836
  continue;
7936
7837
  }
7937
- const stepOutputPath = path8.join(stepsDir, `${stage}.md`);
7938
- const existingStep = fs9.existsSync(stepOutputPath) ? fs9.readFileSync(stepOutputPath, "utf-8") : "";
7838
+ const stepOutputPath = path7.join(stepsDir, `${stage}.md`);
7839
+ const existingStep = fs8.existsSync(stepOutputPath) ? fs8.readFileSync(stepOutputPath, "utf-8") : "";
7939
7840
  const isExtend = !!existingStep && !opts.force;
7940
- const defaultPrompt = fs9.readFileSync(templatePath, "utf-8");
7841
+ const defaultPrompt = fs8.readFileSync(templatePath, "utf-8");
7941
7842
  const contextPlaceholder = "{{TASK_CONTEXT}}";
7942
7843
  const placeholderIdx = defaultPrompt.indexOf(contextPlaceholder);
7943
7844
  if (placeholderIdx === -1) {
7944
- fs9.copyFileSync(templatePath, stepOutputPath);
7845
+ fs8.copyFileSync(templatePath, stepOutputPath);
7945
7846
  stepCount++;
7946
7847
  console.log(` \u2713 ${stage}.md`);
7947
7848
  continue;
@@ -7984,7 +7885,7 @@ ${repoContext}
7984
7885
 
7985
7886
  REMINDER: Output the full prompt template first (unchanged), then your three appended sections. Do NOT include "${contextPlaceholder}".`;
7986
7887
  try {
7987
- const output = execFileSync5("claude", [
7888
+ const output = execFileSync4("claude", [
7988
7889
  "--print",
7989
7890
  "--model",
7990
7891
  bootstrapModel,
@@ -7999,13 +7900,13 @@ REMINDER: Output the full prompt template first (unchanged), then your three app
7999
7900
  let cleaned = output.replace(/^```(?:markdown|md)?\s*\n?/, "").replace(/\n?```\s*$/, "");
8000
7901
  cleaned = cleaned.replace(/\n*\{\{TASK_CONTEXT\}\}\s*$/, "").trimEnd();
8001
7902
  const finalPrompt = cleaned + "\n\n" + afterPlaceholder;
8002
- fs9.writeFileSync(stepOutputPath, finalPrompt);
7903
+ fs8.writeFileSync(stepOutputPath, finalPrompt);
8003
7904
  stepCount++;
8004
7905
  console.log(` \u2713 ${stage}.md (${isExtend ? "extended" : "generated"})`);
8005
7906
  } catch {
8006
7907
  if (!isExtend) {
8007
7908
  console.log(` \u26A0 ${stage}.md \u2014 customization failed, using default template`);
8008
- fs9.copyFileSync(templatePath, stepOutputPath);
7909
+ fs8.copyFileSync(templatePath, stepOutputPath);
8009
7910
  stepCount++;
8010
7911
  } else {
8011
7912
  console.log(` \u26A0 ${stage}.md \u2014 extend failed, keeping existing`);
@@ -8014,11 +7915,11 @@ REMINDER: Output the full prompt template first (unchanged), then your three app
8014
7915
  }
8015
7916
  console.log(` \u2713 Generated ${stepCount} step files in .kody/steps/`);
8016
7917
  console.log("\n\u2500\u2500 QA Guide \u2500\u2500");
8017
- const qaGuidePath = path8.join(cwd, ".kody", "qa-guide.md");
7918
+ const qaGuidePath = path7.join(cwd, ".kody", "qa-guide.md");
8018
7919
  const discovery = discoverQaContext(cwd);
8019
7920
  const hasRoutes = discovery.routes.length > 0 || discovery.collections.length > 0;
8020
7921
  if (hasRoutes) {
8021
- const existingQaGuide = fs9.existsSync(qaGuidePath) ? fs9.readFileSync(qaGuidePath, "utf-8") : "";
7922
+ const existingQaGuide = fs8.existsSync(qaGuidePath) ? fs8.readFileSync(qaGuidePath, "utf-8") : "";
8022
7923
  const isQaExtend = !!existingQaGuide && !opts.force;
8023
7924
  const serializedDiscovery = serializeDiscoveryForLLM(discovery);
8024
7925
  const qaExtendBlock = isQaExtend ? buildExtendInstruction(existingQaGuide, "QA guide") : "";
@@ -8087,7 +7988,7 @@ Command and URL.
8087
7988
  - Output ONLY the markdown. No explanation before or after.`;
8088
7989
  console.log(" \u23F3 Generating QA guide...");
8089
7990
  try {
8090
- const output = execFileSync5("claude", [
7991
+ const output = execFileSync4("claude", [
8091
7992
  "--print",
8092
7993
  "--model",
8093
7994
  bootstrapModel,
@@ -8100,12 +8001,12 @@ Command and URL.
8100
8001
  stdio: ["pipe", "pipe", "pipe"]
8101
8002
  }).trim();
8102
8003
  const cleaned = output.replace(/^```(?:markdown|md)?\s*\n?/, "").replace(/\n?```\s*$/, "");
8103
- fs9.writeFileSync(qaGuidePath, cleaned);
8004
+ fs8.writeFileSync(qaGuidePath, cleaned);
8104
8005
  console.log(` \u2713 .kody/qa-guide.md (${isQaExtend ? "extended" : "generated"}, ${discovery.routes.length} routes, ${discovery.collections.length} collections)`);
8105
8006
  } catch {
8106
8007
  console.log(" \u26A0 LLM QA generation failed \u2014 using template fallback");
8107
8008
  const qaGuide = generateQaGuideFallback(discovery);
8108
- fs9.writeFileSync(qaGuidePath, qaGuide);
8009
+ fs8.writeFileSync(qaGuidePath, qaGuide);
8109
8010
  console.log(` \u2713 .kody/qa-guide.md (fallback, ${discovery.routes.length} routes)`);
8110
8011
  }
8111
8012
  if (discovery.loginPage) console.log(` \u2713 Login page detected: ${discovery.loginPage}`);
@@ -8120,9 +8021,9 @@ Command and URL.
8120
8021
  try {
8121
8022
  let repoSlug = "";
8122
8023
  try {
8123
- const configPath = path8.join(cwd, "kody.config.json");
8124
- if (fs9.existsSync(configPath)) {
8125
- const config = JSON.parse(fs9.readFileSync(configPath, "utf-8"));
8024
+ const configPath = path7.join(cwd, "kody.config.json");
8025
+ if (fs8.existsSync(configPath)) {
8026
+ const config = JSON.parse(fs8.readFileSync(configPath, "utf-8"));
8126
8027
  if (config.github?.owner && config.github?.repo) {
8127
8028
  repoSlug = `${config.github.owner}/${config.github.repo}`;
8128
8029
  }
@@ -8152,7 +8053,7 @@ Command and URL.
8152
8053
  ];
8153
8054
  for (const label of labels) {
8154
8055
  try {
8155
- execFileSync5("gh", [
8056
+ execFileSync4("gh", [
8156
8057
  "label",
8157
8058
  "create",
8158
8059
  label.name,
@@ -8172,7 +8073,7 @@ Command and URL.
8172
8073
  console.log(` \u2713 ${label.name}`);
8173
8074
  } catch {
8174
8075
  try {
8175
- execFileSync5("gh", ["label", "list", "--repo", repoSlug, "--search", label.name], {
8076
+ execFileSync4("gh", ["label", "list", "--repo", repoSlug, "--search", label.name], {
8176
8077
  cwd,
8177
8078
  encoding: "utf-8",
8178
8079
  timeout: 1e4,
@@ -8191,47 +8092,40 @@ Command and URL.
8191
8092
  console.log(" \u25CB Label creation skipped");
8192
8093
  }
8193
8094
  console.log("\n\u2500\u2500 Tools \u2500\u2500");
8194
- const toolsYmlPath = path8.join(cwd, ".kody", "tools.yml");
8195
- if (!fs9.existsSync(toolsYmlPath) || opts.force) {
8095
+ const toolsYmlPath = path7.join(cwd, ".kody", "tools.yml");
8096
+ if (!fs8.existsSync(toolsYmlPath) || opts.force) {
8196
8097
  const toolsTemplate = `# Kody Tools Configuration
8197
8098
  # Uncomment and configure tools that your project uses.
8198
- # The engine will detect, install, and inject tool skills into pipeline stages.
8099
+ # The engine detects tools, runs setup commands, and installs matching skills from skills.sh.
8199
8100
  #
8200
8101
  # playwright:
8201
8102
  # detect: ["playwright.config.ts", "playwright.config.js"]
8202
8103
  # stages: [verify]
8203
8104
  # setup: "npx playwright install --with-deps chromium"
8204
- # skill: playwright-cli.md
8205
8105
  `;
8206
- fs9.writeFileSync(toolsYmlPath, toolsTemplate);
8106
+ fs8.writeFileSync(toolsYmlPath, toolsTemplate);
8207
8107
  console.log(" \u2713 .kody/tools.yml (template created)");
8208
8108
  } else {
8209
8109
  console.log(" \u25CB .kody/tools.yml (already exists, keeping)");
8210
8110
  }
8211
- console.log("\n\u2500\u2500 Skills \u2500\u2500");
8212
- const installedSkillPaths = installSkillsForProject(cwd);
8213
8111
  console.log("\n\u2500\u2500 Git \u2500\u2500");
8214
8112
  const filesToCommit = [
8215
8113
  ".kody/memory/architecture.md",
8216
8114
  ".kody/memory/conventions.md",
8217
8115
  ".kody/qa-guide.md",
8218
- ".kody/tools.yml",
8219
- ...installedSkillPaths
8220
- ].filter((f) => fs9.existsSync(path8.join(cwd, f)));
8221
- if (fs9.existsSync(path8.join(cwd, "skills-lock.json"))) {
8222
- filesToCommit.push("skills-lock.json");
8223
- }
8116
+ ".kody/tools.yml"
8117
+ ].filter((f) => fs8.existsSync(path7.join(cwd, f)));
8224
8118
  for (const stage of STEP_STAGES) {
8225
8119
  const stepFile = `.kody/steps/${stage}.md`;
8226
- if (fs9.existsSync(path8.join(cwd, stepFile))) {
8120
+ if (fs8.existsSync(path7.join(cwd, stepFile))) {
8227
8121
  filesToCommit.push(stepFile);
8228
8122
  }
8229
8123
  }
8230
8124
  if (filesToCommit.length > 0) {
8231
8125
  try {
8232
- const fullPaths = filesToCommit.map((f) => path8.join(cwd, f));
8126
+ const fullPaths = filesToCommit.map((f) => path7.join(cwd, f));
8233
8127
  for (let pass = 0; pass < 2; pass++) {
8234
- execFileSync5("npx", ["prettier", "--write", ...fullPaths], {
8128
+ execFileSync4("npx", ["prettier", "--write", ...fullPaths], {
8235
8129
  cwd,
8236
8130
  encoding: "utf-8",
8237
8131
  timeout: 3e4,
@@ -8247,24 +8141,24 @@ Command and URL.
8247
8141
  try {
8248
8142
  if (isCI3) {
8249
8143
  const branchName = `kody-bootstrap-${Date.now()}`;
8250
- execFileSync5("git", ["checkout", "-b", branchName], { cwd, stdio: "pipe" });
8251
- execFileSync5("git", ["add", ...filesToCommit], { cwd, stdio: "pipe" });
8252
- const staged = execFileSync5("git", ["diff", "--cached", "--name-only"], { cwd, encoding: "utf-8" }).trim();
8144
+ execFileSync4("git", ["checkout", "-b", branchName], { cwd, stdio: "pipe" });
8145
+ execFileSync4("git", ["add", ...filesToCommit], { cwd, stdio: "pipe" });
8146
+ const staged = execFileSync4("git", ["diff", "--cached", "--name-only"], { cwd, encoding: "utf-8" }).trim();
8253
8147
  if (staged) {
8254
- execFileSync5("git", ["commit", "-m", "chore: Add Kody project memory and step files\n\nBootstrap Kody Engine with project-specific architecture, conventions, and pipeline step files."], { cwd, stdio: "pipe" });
8255
- execFileSync5("git", ["push", "-u", "origin", branchName], { cwd, stdio: "pipe", timeout: 6e4 });
8148
+ execFileSync4("git", ["commit", "-m", "chore: Add Kody project memory and step files\n\nBootstrap Kody Engine with project-specific architecture, conventions, and pipeline step files."], { cwd, stdio: "pipe" });
8149
+ execFileSync4("git", ["push", "-u", "origin", branchName], { cwd, stdio: "pipe", timeout: 6e4 });
8256
8150
  console.log(` \u2713 Pushed branch: ${branchName}`);
8257
8151
  let baseBranch = "main";
8258
8152
  try {
8259
- const configPath = path8.join(cwd, "kody.config.json");
8260
- if (fs9.existsSync(configPath)) {
8261
- const config = JSON.parse(fs9.readFileSync(configPath, "utf-8"));
8153
+ const configPath = path7.join(cwd, "kody.config.json");
8154
+ if (fs8.existsSync(configPath)) {
8155
+ const config = JSON.parse(fs8.readFileSync(configPath, "utf-8"));
8262
8156
  baseBranch = config.git?.defaultBranch ?? "main";
8263
8157
  }
8264
8158
  } catch {
8265
8159
  }
8266
8160
  try {
8267
- const prUrl = execFileSync5("gh", [
8161
+ const prUrl = execFileSync4("gh", [
8268
8162
  "pr",
8269
8163
  "create",
8270
8164
  "--title",
@@ -8303,13 +8197,13 @@ Create it manually.`, cwd);
8303
8197
  console.log(" \u25CB No new changes to commit");
8304
8198
  }
8305
8199
  } else {
8306
- execFileSync5("git", ["add", ...filesToCommit], { cwd, stdio: "pipe" });
8307
- const staged = execFileSync5("git", ["diff", "--cached", "--name-only"], { cwd, encoding: "utf-8" }).trim();
8200
+ execFileSync4("git", ["add", ...filesToCommit], { cwd, stdio: "pipe" });
8201
+ const staged = execFileSync4("git", ["diff", "--cached", "--name-only"], { cwd, encoding: "utf-8" }).trim();
8308
8202
  if (staged) {
8309
- execFileSync5("git", ["commit", "-m", "chore: Add Kody project memory and step files\n\nBootstrap Kody Engine with project-specific architecture, conventions, and pipeline step files."], { cwd, stdio: "pipe" });
8203
+ execFileSync4("git", ["commit", "-m", "chore: Add Kody project memory and step files\n\nBootstrap Kody Engine with project-specific architecture, conventions, and pipeline step files."], { cwd, stdio: "pipe" });
8310
8204
  console.log(` \u2713 Committed: ${filesToCommit.join(", ")}`);
8311
8205
  try {
8312
- execFileSync5("git", ["push"], { cwd, stdio: "pipe", timeout: 6e4 });
8206
+ execFileSync4("git", ["push"], { cwd, stdio: "pipe", timeout: 6e4 });
8313
8207
  console.log(" \u2713 Pushed to origin");
8314
8208
  } catch {
8315
8209
  console.log(" \u25CB Push failed \u2014 run 'git push' manually");
@@ -8332,11 +8226,11 @@ Create it manually.`, cwd);
8332
8226
 
8333
8227
  // src/bin/cli.ts
8334
8228
  init_architecture_detection();
8335
- var __dirname2 = path32.dirname(fileURLToPath2(import.meta.url));
8336
- var PKG_ROOT = path32.resolve(__dirname2, "..", "..");
8229
+ var __dirname2 = path31.dirname(fileURLToPath2(import.meta.url));
8230
+ var PKG_ROOT = path31.resolve(__dirname2, "..", "..");
8337
8231
  function getVersion() {
8338
- const pkgPath = path32.join(PKG_ROOT, "package.json");
8339
- const pkg = JSON.parse(fs35.readFileSync(pkgPath, "utf-8"));
8232
+ const pkgPath = path31.join(PKG_ROOT, "package.json");
8233
+ const pkg = JSON.parse(fs34.readFileSync(pkgPath, "utf-8"));
8340
8234
  return pkg.version;
8341
8235
  }
8342
8236
  var args = process.argv.slice(2);
@@ -8364,6 +8258,5 @@ export {
8364
8258
  checkGhRepoAccess,
8365
8259
  checkGhSecret,
8366
8260
  detectArchitectureBasic,
8367
- detectBasicConfig,
8368
- detectSkillsForProject
8261
+ detectBasicConfig
8369
8262
  };