@kody-ade/kody-engine-lite 0.1.25 → 0.1.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/README.md +74 -224
  2. package/dist/agent-runner.d.ts +4 -0
  3. package/dist/agent-runner.js +122 -0
  4. package/dist/bin/cli.js +365 -100
  5. package/dist/ci/parse-inputs.d.ts +6 -0
  6. package/dist/ci/parse-inputs.js +76 -0
  7. package/dist/ci/parse-safety.d.ts +6 -0
  8. package/dist/ci/parse-safety.js +22 -0
  9. package/dist/cli/args.d.ts +13 -0
  10. package/dist/cli/args.js +42 -0
  11. package/dist/cli/litellm.d.ts +2 -0
  12. package/dist/cli/litellm.js +85 -0
  13. package/dist/cli/task-resolution.d.ts +2 -0
  14. package/dist/cli/task-resolution.js +41 -0
  15. package/dist/config.d.ts +49 -0
  16. package/dist/config.js +72 -0
  17. package/dist/context.d.ts +4 -0
  18. package/dist/context.js +83 -0
  19. package/dist/definitions.d.ts +3 -0
  20. package/dist/definitions.js +59 -0
  21. package/dist/entry.d.ts +1 -0
  22. package/dist/entry.js +236 -0
  23. package/dist/git-utils.d.ts +13 -0
  24. package/dist/git-utils.js +174 -0
  25. package/dist/github-api.d.ts +14 -0
  26. package/dist/github-api.js +114 -0
  27. package/dist/kody-utils.d.ts +1 -0
  28. package/dist/kody-utils.js +9 -0
  29. package/dist/learning/auto-learn.d.ts +2 -0
  30. package/dist/learning/auto-learn.js +169 -0
  31. package/dist/logger.d.ts +14 -0
  32. package/dist/logger.js +51 -0
  33. package/dist/memory.d.ts +1 -0
  34. package/dist/memory.js +20 -0
  35. package/dist/observer.d.ts +9 -0
  36. package/dist/observer.js +80 -0
  37. package/dist/pipeline/complexity.d.ts +3 -0
  38. package/dist/pipeline/complexity.js +12 -0
  39. package/dist/pipeline/executor-registry.d.ts +3 -0
  40. package/dist/pipeline/executor-registry.js +20 -0
  41. package/dist/pipeline/hooks.d.ts +17 -0
  42. package/dist/pipeline/hooks.js +110 -0
  43. package/dist/pipeline/questions.d.ts +2 -0
  44. package/dist/pipeline/questions.js +44 -0
  45. package/dist/pipeline/runner-selection.d.ts +2 -0
  46. package/dist/pipeline/runner-selection.js +13 -0
  47. package/dist/pipeline/state.d.ts +4 -0
  48. package/dist/pipeline/state.js +37 -0
  49. package/dist/pipeline.d.ts +3 -0
  50. package/dist/pipeline.js +213 -0
  51. package/dist/preflight.d.ts +1 -0
  52. package/dist/preflight.js +69 -0
  53. package/dist/retrospective.d.ts +26 -0
  54. package/dist/retrospective.js +211 -0
  55. package/dist/stages/agent.d.ts +2 -0
  56. package/dist/stages/agent.js +94 -0
  57. package/dist/stages/gate.d.ts +2 -0
  58. package/dist/stages/gate.js +32 -0
  59. package/dist/stages/review.d.ts +2 -0
  60. package/dist/stages/review.js +32 -0
  61. package/dist/stages/ship.d.ts +3 -0
  62. package/dist/stages/ship.js +154 -0
  63. package/dist/stages/verify.d.ts +2 -0
  64. package/dist/stages/verify.js +94 -0
  65. package/dist/types.d.ts +61 -0
  66. package/dist/types.js +1 -0
  67. package/dist/validators.d.ts +8 -0
  68. package/dist/validators.js +42 -0
  69. package/dist/verify-runner.d.ts +11 -0
  70. package/dist/verify-runner.js +110 -0
  71. package/package.json +1 -1
package/dist/bin/cli.js CHANGED
@@ -698,6 +698,16 @@ ${truncated}${spec.length > MAX_TASK_CONTEXT_SPEC ? "\n..." : ""}
698
698
  context += `
699
699
  ## Plan Summary
700
700
  ${truncated}${plan.length > MAX_TASK_CONTEXT_PLAN ? "\n..." : ""}
701
+ `;
702
+ }
703
+ const contextMdPath = path4.join(taskDir, "context.md");
704
+ if (fs4.existsSync(contextMdPath)) {
705
+ const accumulated = fs4.readFileSync(contextMdPath, "utf-8");
706
+ const truncated = accumulated.slice(-MAX_ACCUMULATED_CONTEXT);
707
+ const prefix = accumulated.length > MAX_ACCUMULATED_CONTEXT ? "...(earlier context truncated)\n" : "";
708
+ context += `
709
+ ## Previous Stage Context
710
+ ${prefix}${truncated}
701
711
  `;
702
712
  }
703
713
  if (feedback) {
@@ -726,7 +736,7 @@ function resolveModel(modelTier, stageName) {
726
736
  if (mapped) return mapped;
727
737
  return DEFAULT_MODEL_MAP[modelTier] ?? "sonnet";
728
738
  }
729
- var DEFAULT_MODEL_MAP, MAX_TASK_CONTEXT_PLAN, MAX_TASK_CONTEXT_SPEC;
739
+ var DEFAULT_MODEL_MAP, MAX_TASK_CONTEXT_PLAN, MAX_TASK_CONTEXT_SPEC, MAX_ACCUMULATED_CONTEXT;
730
740
  var init_context = __esm({
731
741
  "src/context.ts"() {
732
742
  "use strict";
@@ -739,6 +749,7 @@ var init_context = __esm({
739
749
  };
740
750
  MAX_TASK_CONTEXT_PLAN = 1500;
741
751
  MAX_TASK_CONTEXT_SPEC = 2e3;
752
+ MAX_ACCUMULATED_CONTEXT = 4e3;
742
753
  }
743
754
  });
744
755
 
@@ -894,8 +905,25 @@ async function executeAgentStage(ctx, def) {
894
905
  }
895
906
  }
896
907
  }
908
+ appendStageContext(ctx.taskDir, def.name, result.output);
897
909
  return { outcome: "completed", outputFile: def.outputFile, retries: 0 };
898
910
  }
911
+ function appendStageContext(taskDir, stageName, output) {
912
+ const contextPath = path5.join(taskDir, "context.md");
913
+ const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19);
914
+ let summary;
915
+ if (output && output.trim()) {
916
+ summary = output.slice(0, 500);
917
+ if (output.length > 500) summary += "\n...(truncated)";
918
+ } else {
919
+ summary = "(stage completed via tool use \u2014 no text output)";
920
+ }
921
+ const entry = `
922
+ ### ${stageName} (${timestamp2})
923
+ ${summary}
924
+ `;
925
+ fs5.appendFileSync(contextPath, entry);
926
+ }
899
927
  var init_agent = __esm({
900
928
  "src/stages/agent.ts"() {
901
929
  "use strict";
@@ -1596,6 +1624,39 @@ function autoDetectComplexity(ctx, def) {
1596
1624
  return null;
1597
1625
  }
1598
1626
  }
1627
+ function checkRiskGate(ctx, def, state, complexity) {
1628
+ if (def.name !== "plan") return null;
1629
+ if (complexity !== "high") return null;
1630
+ if (ctx.input.dryRun || ctx.input.local) return null;
1631
+ if (ctx.input.mode === "rerun") return null;
1632
+ if (!ctx.input.issueNumber) return null;
1633
+ const planPath = path11.join(ctx.taskDir, "plan.md");
1634
+ const plan = fs11.existsSync(planPath) ? fs11.readFileSync(planPath, "utf-8").slice(0, 1500) : "(plan not available)";
1635
+ try {
1636
+ postComment(
1637
+ ctx.input.issueNumber,
1638
+ `\u{1F6D1} **Risk gate: HIGH complexity \u2014 awaiting approval**
1639
+
1640
+ <details><summary>\u{1F4CB} Plan summary</summary>
1641
+
1642
+ ${plan}
1643
+ </details>
1644
+
1645
+ To approve: \`@kody approve\``
1646
+ );
1647
+ setLifecycleLabel(ctx.input.issueNumber, "waiting");
1648
+ } catch {
1649
+ }
1650
+ state.state = "failed";
1651
+ state.stages[def.name] = {
1652
+ ...state.stages[def.name],
1653
+ state: "completed",
1654
+ error: "paused: risk gate \u2014 awaiting approval"
1655
+ };
1656
+ writeState(state, ctx.taskDir);
1657
+ logger.info(` Pipeline paused \u2014 HIGH risk gate, awaiting approval on issue`);
1658
+ return state;
1659
+ }
1599
1660
  function commitAfterStage(ctx, def) {
1600
1661
  if (ctx.input.dryRun || !ctx.input.issueNumber) return;
1601
1662
  if (def.name === "build") {
@@ -1761,14 +1822,212 @@ var init_auto_learn = __esm({
1761
1822
  }
1762
1823
  });
1763
1824
 
1764
- // src/pipeline.ts
1825
+ // src/retrospective.ts
1765
1826
  import * as fs13 from "fs";
1766
1827
  import * as path13 from "path";
1828
+ function readArtifact(taskDir, filename, maxChars) {
1829
+ const p = path13.join(taskDir, filename);
1830
+ if (!fs13.existsSync(p)) return null;
1831
+ try {
1832
+ const content = fs13.readFileSync(p, "utf-8");
1833
+ return content.length > maxChars ? content.slice(0, maxChars) + "\n...(truncated)" : content;
1834
+ } catch {
1835
+ return null;
1836
+ }
1837
+ }
1838
+ function computeStageDuration(stage) {
1839
+ if (!stage.startedAt) return void 0;
1840
+ const end = stage.completedAt ?? (/* @__PURE__ */ new Date()).toISOString();
1841
+ return new Date(end).getTime() - new Date(stage.startedAt).getTime();
1842
+ }
1843
+ function collectRunContext(ctx, state, pipelineStartTime) {
1844
+ const durationMs = Date.now() - pipelineStartTime;
1845
+ const lines = [];
1846
+ lines.push(`## This Run`);
1847
+ lines.push(`Task: ${state.taskId}`);
1848
+ lines.push(`Outcome: ${state.state}`);
1849
+ lines.push(`Duration: ${durationMs}ms (${Math.round(durationMs / 1e3)}s)`);
1850
+ lines.push(`Mode: ${ctx.input.mode}`);
1851
+ lines.push(``);
1852
+ lines.push(`### Stage Results`);
1853
+ let failedStage;
1854
+ for (const def of STAGES) {
1855
+ const s = state.stages[def.name];
1856
+ const duration = computeStageDuration(s);
1857
+ const durationStr = duration != null ? `, ${duration}ms` : "";
1858
+ const errorStr = s.error ? ` \u2014 ${s.error}` : "";
1859
+ lines.push(`${def.name}: ${s.state} (${s.retries} retries${durationStr})${errorStr}`);
1860
+ if (s.state === "failed" || s.state === "timeout") {
1861
+ failedStage = def.name;
1862
+ }
1863
+ }
1864
+ lines.push(``);
1865
+ const artifacts = [
1866
+ ["task.md", 300],
1867
+ ["task.json", 500],
1868
+ ["plan.md", 500],
1869
+ ["verify.md", 800],
1870
+ ["review.md", 500],
1871
+ ["ship.md", 300]
1872
+ ];
1873
+ lines.push(`### Artifacts`);
1874
+ for (const [filename, maxChars] of artifacts) {
1875
+ const content = readArtifact(ctx.taskDir, filename, maxChars);
1876
+ if (content) {
1877
+ lines.push(`#### ${filename}`);
1878
+ lines.push(content);
1879
+ lines.push(``);
1880
+ }
1881
+ }
1882
+ return lines.join("\n");
1883
+ }
1884
+ function getLogPath(projectDir) {
1885
+ return path13.join(projectDir, ".kody", "memory", "observer-log.jsonl");
1886
+ }
1887
+ function readPreviousRetrospectives(projectDir, limit = 10) {
1888
+ const logPath = getLogPath(projectDir);
1889
+ if (!fs13.existsSync(logPath)) return [];
1890
+ try {
1891
+ const content = fs13.readFileSync(logPath, "utf-8");
1892
+ const lines = content.split("\n").filter(Boolean);
1893
+ const entries = [];
1894
+ const start = Math.max(0, lines.length - limit);
1895
+ for (let i = start; i < lines.length; i++) {
1896
+ try {
1897
+ entries.push(JSON.parse(lines[i]));
1898
+ } catch {
1899
+ }
1900
+ }
1901
+ return entries;
1902
+ } catch {
1903
+ return [];
1904
+ }
1905
+ }
1906
+ function formatPreviousEntries(entries) {
1907
+ if (entries.length === 0) return "No previous runs recorded.";
1908
+ return entries.map((e) => {
1909
+ const failed = e.failedStage ? ` at ${e.failedStage}` : "";
1910
+ const pattern = e.patternMatch ? `Pattern: ${e.patternMatch}` : "Pattern: none";
1911
+ const flaw = e.pipelineFlaw ? ` | Flaw: ${e.pipelineFlaw.component} \u2014 ${e.pipelineFlaw.issue}` : "";
1912
+ return `[${e.timestamp.slice(0, 10)}] ${e.taskId}: ${e.outcome}${failed} \u2014 "${e.observation.slice(0, 120)}"
1913
+ ${pattern} | Suggestion: "${e.suggestion}"${flaw}`;
1914
+ }).join("\n");
1915
+ }
1916
+ function appendRetrospectiveEntry(projectDir, entry) {
1917
+ const logPath = getLogPath(projectDir);
1918
+ const dir = path13.dirname(logPath);
1919
+ if (!fs13.existsSync(dir)) {
1920
+ fs13.mkdirSync(dir, { recursive: true });
1921
+ }
1922
+ fs13.appendFileSync(logPath, JSON.stringify(entry) + "\n");
1923
+ }
1924
+ async function runRetrospective(ctx, state, pipelineStartTime) {
1925
+ if (ctx.input.dryRun) return;
1926
+ try {
1927
+ const durationMs = Date.now() - pipelineStartTime;
1928
+ const runContext = collectRunContext(ctx, state, pipelineStartTime);
1929
+ const previous = readPreviousRetrospectives(ctx.projectDir);
1930
+ const previousText = formatPreviousEntries(previous);
1931
+ const prompt = RETROSPECTIVE_PROMPT + `## Run Context
1932
+ ${runContext}
1933
+
1934
+ ## Previous Retrospectives (last ${previous.length} runs)
1935
+ ${previousText}
1936
+ `;
1937
+ const runner = getRunnerForStage(ctx, "taskify");
1938
+ const model = resolveModel("cheap");
1939
+ const result = await runner.run("retrospective", prompt, model, 3e4, "");
1940
+ let observation = "Retrospective analysis unavailable";
1941
+ let patternMatch = null;
1942
+ let suggestion = "No suggestion";
1943
+ let pipelineFlaw = null;
1944
+ if (result.outcome === "completed" && result.output) {
1945
+ const cleaned = result.output.replace(/^```json\s*\n?/m, "").replace(/\n?```\s*$/m, "").trim();
1946
+ try {
1947
+ const parsed = JSON.parse(cleaned);
1948
+ observation = parsed.observation ?? observation;
1949
+ patternMatch = parsed.patternMatch ?? null;
1950
+ suggestion = parsed.suggestion ?? suggestion;
1951
+ if (parsed.pipelineFlaw && parsed.pipelineFlaw.component) {
1952
+ pipelineFlaw = {
1953
+ component: parsed.pipelineFlaw.component,
1954
+ issue: parsed.pipelineFlaw.issue ?? "Unknown",
1955
+ evidence: parsed.pipelineFlaw.evidence ?? ""
1956
+ };
1957
+ }
1958
+ } catch {
1959
+ logger.warn(" Retrospective: failed to parse LLM output");
1960
+ }
1961
+ }
1962
+ const stageResults = {};
1963
+ let failedStage;
1964
+ for (const def of STAGES) {
1965
+ const s = state.stages[def.name];
1966
+ stageResults[def.name] = {
1967
+ state: s.state,
1968
+ retries: s.retries,
1969
+ durationMs: computeStageDuration(s),
1970
+ error: s.error
1971
+ };
1972
+ if (s.state === "failed" || s.state === "timeout") {
1973
+ failedStage = def.name;
1974
+ }
1975
+ }
1976
+ const entry = {
1977
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1978
+ taskId: state.taskId,
1979
+ outcome: state.state === "completed" ? "completed" : "failed",
1980
+ durationMs,
1981
+ stageResults,
1982
+ failedStage,
1983
+ observation,
1984
+ patternMatch,
1985
+ suggestion,
1986
+ pipelineFlaw
1987
+ };
1988
+ appendRetrospectiveEntry(ctx.projectDir, entry);
1989
+ logger.info(`Retrospective: ${observation.slice(0, 120)}`);
1990
+ } catch (err) {
1991
+ logger.warn(`Retrospective failed: ${err instanceof Error ? err.message : err}`);
1992
+ }
1993
+ }
1994
+ var RETROSPECTIVE_PROMPT;
1995
+ var init_retrospective = __esm({
1996
+ "src/retrospective.ts"() {
1997
+ "use strict";
1998
+ init_definitions();
1999
+ init_context();
2000
+ init_runner_selection();
2001
+ init_logger();
2002
+ RETROSPECTIVE_PROMPT = `You are a pipeline retrospective analyst. You observe automated software development pipeline runs and identify flaws, patterns, and improvement opportunities.
2003
+
2004
+ Output ONLY valid JSON. No markdown fences. No explanation.
2005
+
2006
+ {
2007
+ "observation": "One paragraph: what happened in this run, what went well, what went wrong",
2008
+ "patternMatch": "If this matches a pattern seen in previous runs, describe the pattern. Otherwise null",
2009
+ "suggestion": "One specific, actionable change to improve pipeline reliability or efficiency",
2010
+ "pipelineFlaw": {
2011
+ "component": "pipeline component name (e.g., verify, build, autofix, taskify prompt, review prompt, model selection, timeout config)",
2012
+ "issue": "concise description of the flaw",
2013
+ "evidence": "specific data from this run that supports this conclusion"
2014
+ }
2015
+ }
2016
+
2017
+ If no pipeline flaw is detected, set "pipelineFlaw" to null.
2018
+
2019
+ `;
2020
+ }
2021
+ });
2022
+
2023
+ // src/pipeline.ts
2024
+ import * as fs14 from "fs";
2025
+ import * as path14 from "path";
1767
2026
  function ensureFeatureBranchIfNeeded(ctx) {
1768
2027
  if (!ctx.input.issueNumber || ctx.input.dryRun) return;
1769
2028
  try {
1770
- const taskMdPath = path13.join(ctx.taskDir, "task.md");
1771
- const title = fs13.existsSync(taskMdPath) ? fs13.readFileSync(taskMdPath, "utf-8").split("\n")[0].slice(0, 50) : ctx.taskId;
2029
+ const taskMdPath = path14.join(ctx.taskDir, "task.md");
2030
+ const title = fs14.existsSync(taskMdPath) ? fs14.readFileSync(taskMdPath, "utf-8").split("\n")[0].slice(0, 50) : ctx.taskId;
1772
2031
  ensureFeatureBranch(ctx.input.issueNumber, title, ctx.projectDir);
1773
2032
  syncWithDefault(ctx.projectDir);
1774
2033
  } catch (err) {
@@ -1776,10 +2035,10 @@ function ensureFeatureBranchIfNeeded(ctx) {
1776
2035
  }
1777
2036
  }
1778
2037
  function acquireLock(taskDir) {
1779
- const lockPath = path13.join(taskDir, ".lock");
1780
- if (fs13.existsSync(lockPath)) {
2038
+ const lockPath = path14.join(taskDir, ".lock");
2039
+ if (fs14.existsSync(lockPath)) {
1781
2040
  try {
1782
- const pid = parseInt(fs13.readFileSync(lockPath, "utf-8").trim(), 10);
2041
+ const pid = parseInt(fs14.readFileSync(lockPath, "utf-8").trim(), 10);
1783
2042
  try {
1784
2043
  process.kill(pid, 0);
1785
2044
  throw new Error(`Pipeline already running (PID ${pid})`);
@@ -1790,11 +2049,11 @@ function acquireLock(taskDir) {
1790
2049
  if (e instanceof Error && e.message.startsWith("Pipeline already")) throw e;
1791
2050
  }
1792
2051
  }
1793
- fs13.writeFileSync(lockPath, String(process.pid));
2052
+ fs14.writeFileSync(lockPath, String(process.pid));
1794
2053
  }
1795
2054
  function releaseLock(taskDir) {
1796
2055
  try {
1797
- fs13.unlinkSync(path13.join(taskDir, ".lock"));
2056
+ fs14.unlinkSync(path14.join(taskDir, ".lock"));
1798
2057
  } catch {
1799
2058
  }
1800
2059
  }
@@ -1807,6 +2066,7 @@ async function runPipeline(ctx) {
1807
2066
  }
1808
2067
  }
1809
2068
  async function runPipelineInner(ctx) {
2069
+ const pipelineStartTime = Date.now();
1810
2070
  let state = loadState(ctx.taskId, ctx.taskDir);
1811
2071
  if (!state) {
1812
2072
  state = initState(ctx.taskId);
@@ -1888,6 +2148,8 @@ async function runPipelineInner(ctx) {
1888
2148
  complexity = detected.complexity;
1889
2149
  activeStages = detected.activeStages;
1890
2150
  }
2151
+ const gated = checkRiskGate(ctx, def, state, complexity);
2152
+ if (gated) return gated;
1891
2153
  commitAfterStage(ctx, def);
1892
2154
  } else {
1893
2155
  const isTimeout = result.outcome === "timed_out";
@@ -1916,6 +2178,8 @@ async function runPipelineInner(ctx) {
1916
2178
  }
1917
2179
  autoLearn(ctx);
1918
2180
  }
2181
+ await runRetrospective(ctx, state, pipelineStartTime).catch(() => {
2182
+ });
1919
2183
  return state;
1920
2184
  }
1921
2185
  function printStatus(taskId, taskDir) {
@@ -1949,12 +2213,13 @@ var init_pipeline = __esm({
1949
2213
  init_executor_registry();
1950
2214
  init_hooks();
1951
2215
  init_auto_learn();
2216
+ init_retrospective();
1952
2217
  }
1953
2218
  });
1954
2219
 
1955
2220
  // src/preflight.ts
1956
2221
  import { execFileSync as execFileSync8 } from "child_process";
1957
- import * as fs14 from "fs";
2222
+ import * as fs15 from "fs";
1958
2223
  function check(name, fn) {
1959
2224
  try {
1960
2225
  const detail = fn() ?? void 0;
@@ -2007,7 +2272,7 @@ function runPreflight() {
2007
2272
  return v;
2008
2273
  }),
2009
2274
  check("package.json", () => {
2010
- if (!fs14.existsSync("package.json")) throw new Error("not found");
2275
+ if (!fs15.existsSync("package.json")) throw new Error("not found");
2011
2276
  })
2012
2277
  ];
2013
2278
  const failed = checks.filter((c) => !c.ok);
@@ -2078,8 +2343,8 @@ var init_args = __esm({
2078
2343
  });
2079
2344
 
2080
2345
  // src/cli/litellm.ts
2081
- import * as fs15 from "fs";
2082
- import * as path14 from "path";
2346
+ import * as fs16 from "fs";
2347
+ import * as path15 from "path";
2083
2348
  import { execFileSync as execFileSync9 } from "child_process";
2084
2349
  async function checkLitellmHealth(url) {
2085
2350
  try {
@@ -2090,8 +2355,8 @@ async function checkLitellmHealth(url) {
2090
2355
  }
2091
2356
  }
2092
2357
  async function tryStartLitellm(url, projectDir) {
2093
- const configPath = path14.join(projectDir, "litellm-config.yaml");
2094
- if (!fs15.existsSync(configPath)) {
2358
+ const configPath = path15.join(projectDir, "litellm-config.yaml");
2359
+ if (!fs16.existsSync(configPath)) {
2095
2360
  logger.warn("litellm-config.yaml not found \u2014 cannot start proxy");
2096
2361
  return null;
2097
2362
  }
@@ -2118,10 +2383,10 @@ async function tryStartLitellm(url, projectDir) {
2118
2383
  cmd = "python3";
2119
2384
  args2 = ["-m", "litellm", "--config", configPath, "--port", port];
2120
2385
  }
2121
- const dotenvPath = path14.join(projectDir, ".env");
2386
+ const dotenvPath = path15.join(projectDir, ".env");
2122
2387
  const dotenvVars = {};
2123
- if (fs15.existsSync(dotenvPath)) {
2124
- for (const line of fs15.readFileSync(dotenvPath, "utf-8").split("\n")) {
2388
+ if (fs16.existsSync(dotenvPath)) {
2389
+ for (const line of fs16.readFileSync(dotenvPath, "utf-8").split("\n")) {
2125
2390
  const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
2126
2391
  if (match) dotenvVars[match[1]] = match[2];
2127
2392
  }
@@ -2161,13 +2426,13 @@ var init_litellm = __esm({
2161
2426
  });
2162
2427
 
2163
2428
  // src/cli/task-resolution.ts
2164
- import * as fs16 from "fs";
2165
- import * as path15 from "path";
2429
+ import * as fs17 from "fs";
2430
+ import * as path16 from "path";
2166
2431
  import { execFileSync as execFileSync10 } from "child_process";
2167
2432
  function findLatestTaskForIssue(issueNumber, projectDir) {
2168
- const tasksDir = path15.join(projectDir, ".tasks");
2169
- if (!fs16.existsSync(tasksDir)) return null;
2170
- const allDirs = fs16.readdirSync(tasksDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort().reverse();
2433
+ const tasksDir = path16.join(projectDir, ".tasks");
2434
+ if (!fs17.existsSync(tasksDir)) return null;
2435
+ const allDirs = fs17.readdirSync(tasksDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort().reverse();
2171
2436
  const prefix = `${issueNumber}-`;
2172
2437
  const direct = allDirs.find((d) => d.startsWith(prefix));
2173
2438
  if (direct) return direct;
@@ -2202,13 +2467,13 @@ var init_task_resolution = __esm({
2202
2467
 
2203
2468
  // src/entry.ts
2204
2469
  var entry_exports = {};
2205
- import * as fs17 from "fs";
2206
- import * as path16 from "path";
2470
+ import * as fs18 from "fs";
2471
+ import * as path17 from "path";
2207
2472
  async function main() {
2208
2473
  const input = parseArgs();
2209
- const projectDir = input.cwd ? path16.resolve(input.cwd) : process.cwd();
2474
+ const projectDir = input.cwd ? path17.resolve(input.cwd) : process.cwd();
2210
2475
  if (input.cwd) {
2211
- if (!fs17.existsSync(projectDir)) {
2476
+ if (!fs18.existsSync(projectDir)) {
2212
2477
  console.error(`--cwd path does not exist: ${projectDir}`);
2213
2478
  process.exit(1);
2214
2479
  }
@@ -2235,8 +2500,8 @@ async function main() {
2235
2500
  process.exit(1);
2236
2501
  }
2237
2502
  }
2238
- const taskDir = path16.join(projectDir, ".tasks", taskId);
2239
- fs17.mkdirSync(taskDir, { recursive: true });
2503
+ const taskDir = path17.join(projectDir, ".tasks", taskId);
2504
+ fs18.mkdirSync(taskDir, { recursive: true });
2240
2505
  if (input.command === "status") {
2241
2506
  printStatus(taskId, taskDir);
2242
2507
  return;
@@ -2244,22 +2509,22 @@ async function main() {
2244
2509
  logger.info("Preflight checks:");
2245
2510
  runPreflight();
2246
2511
  if (input.task) {
2247
- fs17.writeFileSync(path16.join(taskDir, "task.md"), input.task);
2512
+ fs18.writeFileSync(path17.join(taskDir, "task.md"), input.task);
2248
2513
  }
2249
- const taskMdPath = path16.join(taskDir, "task.md");
2250
- if (!fs17.existsSync(taskMdPath) && input.issueNumber) {
2514
+ const taskMdPath = path17.join(taskDir, "task.md");
2515
+ if (!fs18.existsSync(taskMdPath) && input.issueNumber) {
2251
2516
  logger.info(`Fetching issue #${input.issueNumber} body as task...`);
2252
2517
  const issue = getIssue(input.issueNumber);
2253
2518
  if (issue) {
2254
2519
  const taskContent = `# ${issue.title}
2255
2520
 
2256
2521
  ${issue.body ?? ""}`;
2257
- fs17.writeFileSync(taskMdPath, taskContent);
2522
+ fs18.writeFileSync(taskMdPath, taskContent);
2258
2523
  logger.info(` Task loaded from issue #${input.issueNumber}: ${issue.title}`);
2259
2524
  }
2260
2525
  }
2261
2526
  if (input.command === "run") {
2262
- if (!fs17.existsSync(taskMdPath)) {
2527
+ if (!fs18.existsSync(taskMdPath)) {
2263
2528
  console.error("No task.md found. Provide --task, --issue-number, or ensure .tasks/<id>/task.md exists.");
2264
2529
  process.exit(1);
2265
2530
  }
@@ -2268,10 +2533,10 @@ ${issue.body ?? ""}`;
2268
2533
  input.fromStage = "build";
2269
2534
  }
2270
2535
  if (input.command === "rerun" && !input.fromStage) {
2271
- const statusPath = path16.join(taskDir, "status.json");
2272
- if (fs17.existsSync(statusPath)) {
2536
+ const statusPath = path17.join(taskDir, "status.json");
2537
+ if (fs18.existsSync(statusPath)) {
2273
2538
  try {
2274
- const status = JSON.parse(fs17.readFileSync(statusPath, "utf-8"));
2539
+ const status = JSON.parse(fs18.readFileSync(statusPath, "utf-8"));
2275
2540
  const stageNames = ["taskify", "plan", "build", "verify", "review", "review-fix", "ship"];
2276
2541
  let foundPaused = false;
2277
2542
  for (const name of stageNames) {
@@ -2378,7 +2643,7 @@ To rerun: \`@kody rerun ${taskId} --from <stage>\``
2378
2643
  }
2379
2644
  }
2380
2645
  const state = await runPipeline(ctx);
2381
- const files = fs17.readdirSync(taskDir);
2646
+ const files = fs18.readdirSync(taskDir);
2382
2647
  console.log(`
2383
2648
  Artifacts in ${taskDir}:`);
2384
2649
  for (const f of files) {
@@ -2437,15 +2702,15 @@ var init_entry = __esm({
2437
2702
  });
2438
2703
 
2439
2704
  // src/bin/cli.ts
2440
- import * as fs18 from "fs";
2441
- import * as path17 from "path";
2705
+ import * as fs19 from "fs";
2706
+ import * as path18 from "path";
2442
2707
  import { execFileSync as execFileSync11 } from "child_process";
2443
2708
  import { fileURLToPath } from "url";
2444
- var __dirname = path17.dirname(fileURLToPath(import.meta.url));
2445
- var PKG_ROOT = path17.resolve(__dirname, "..", "..");
2709
+ var __dirname = path18.dirname(fileURLToPath(import.meta.url));
2710
+ var PKG_ROOT = path18.resolve(__dirname, "..", "..");
2446
2711
  function getVersion() {
2447
- const pkgPath = path17.join(PKG_ROOT, "package.json");
2448
- const pkg = JSON.parse(fs18.readFileSync(pkgPath, "utf-8"));
2712
+ const pkgPath = path18.join(PKG_ROOT, "package.json");
2713
+ const pkg = JSON.parse(fs19.readFileSync(pkgPath, "utf-8"));
2449
2714
  return pkg.version;
2450
2715
  }
2451
2716
  function checkCommand2(name, args2, fix) {
@@ -2461,7 +2726,7 @@ function checkCommand2(name, args2, fix) {
2461
2726
  }
2462
2727
  }
2463
2728
  function checkFile(filePath, description, fix) {
2464
- if (fs18.existsSync(filePath)) {
2729
+ if (fs19.existsSync(filePath)) {
2465
2730
  return { name: description, ok: true, detail: filePath };
2466
2731
  }
2467
2732
  return { name: description, ok: false, fix };
@@ -2533,10 +2798,10 @@ function checkGhSecret(repoSlug, secretName) {
2533
2798
  }
2534
2799
  function detectArchitecture(cwd) {
2535
2800
  const detected = [];
2536
- const pkgPath = path17.join(cwd, "package.json");
2537
- if (fs18.existsSync(pkgPath)) {
2801
+ const pkgPath = path18.join(cwd, "package.json");
2802
+ if (fs19.existsSync(pkgPath)) {
2538
2803
  try {
2539
- const pkg = JSON.parse(fs18.readFileSync(pkgPath, "utf-8"));
2804
+ const pkg = JSON.parse(fs19.readFileSync(pkgPath, "utf-8"));
2540
2805
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
2541
2806
  if (allDeps.next) detected.push(`- Framework: Next.js ${allDeps.next}`);
2542
2807
  else if (allDeps.react) detected.push(`- Framework: React ${allDeps.react}`);
@@ -2559,41 +2824,41 @@ function detectArchitecture(cwd) {
2559
2824
  if (allDeps.tailwindcss) detected.push(`- CSS: Tailwind CSS ${allDeps.tailwindcss}`);
2560
2825
  if (pkg.type === "module") detected.push("- Module system: ESM");
2561
2826
  else detected.push("- Module system: CommonJS");
2562
- if (fs18.existsSync(path17.join(cwd, "pnpm-lock.yaml"))) detected.push("- Package manager: pnpm");
2563
- else if (fs18.existsSync(path17.join(cwd, "yarn.lock"))) detected.push("- Package manager: yarn");
2564
- else if (fs18.existsSync(path17.join(cwd, "bun.lockb"))) detected.push("- Package manager: bun");
2565
- else if (fs18.existsSync(path17.join(cwd, "package-lock.json"))) detected.push("- Package manager: npm");
2827
+ if (fs19.existsSync(path18.join(cwd, "pnpm-lock.yaml"))) detected.push("- Package manager: pnpm");
2828
+ else if (fs19.existsSync(path18.join(cwd, "yarn.lock"))) detected.push("- Package manager: yarn");
2829
+ else if (fs19.existsSync(path18.join(cwd, "bun.lockb"))) detected.push("- Package manager: bun");
2830
+ else if (fs19.existsSync(path18.join(cwd, "package-lock.json"))) detected.push("- Package manager: npm");
2566
2831
  } catch {
2567
2832
  }
2568
2833
  }
2569
2834
  try {
2570
- const entries = fs18.readdirSync(cwd, { withFileTypes: true });
2835
+ const entries = fs19.readdirSync(cwd, { withFileTypes: true });
2571
2836
  const dirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name);
2572
2837
  if (dirs.length > 0) detected.push(`- Top-level directories: ${dirs.join(", ")}`);
2573
2838
  } catch {
2574
2839
  }
2575
- const srcDir = path17.join(cwd, "src");
2576
- if (fs18.existsSync(srcDir)) {
2840
+ const srcDir = path18.join(cwd, "src");
2841
+ if (fs19.existsSync(srcDir)) {
2577
2842
  try {
2578
- const srcEntries = fs18.readdirSync(srcDir, { withFileTypes: true });
2843
+ const srcEntries = fs19.readdirSync(srcDir, { withFileTypes: true });
2579
2844
  const srcDirs = srcEntries.filter((e) => e.isDirectory()).map((e) => e.name);
2580
2845
  if (srcDirs.length > 0) detected.push(`- src/ structure: ${srcDirs.join(", ")}`);
2581
2846
  } catch {
2582
2847
  }
2583
2848
  }
2584
2849
  const configs = [];
2585
- if (fs18.existsSync(path17.join(cwd, "tsconfig.json"))) configs.push("tsconfig.json");
2586
- if (fs18.existsSync(path17.join(cwd, "docker-compose.yml")) || fs18.existsSync(path17.join(cwd, "docker-compose.yaml"))) configs.push("docker-compose");
2587
- if (fs18.existsSync(path17.join(cwd, "Dockerfile"))) configs.push("Dockerfile");
2588
- if (fs18.existsSync(path17.join(cwd, ".env")) || fs18.existsSync(path17.join(cwd, ".env.local"))) configs.push(".env");
2850
+ if (fs19.existsSync(path18.join(cwd, "tsconfig.json"))) configs.push("tsconfig.json");
2851
+ if (fs19.existsSync(path18.join(cwd, "docker-compose.yml")) || fs19.existsSync(path18.join(cwd, "docker-compose.yaml"))) configs.push("docker-compose");
2852
+ if (fs19.existsSync(path18.join(cwd, "Dockerfile"))) configs.push("Dockerfile");
2853
+ if (fs19.existsSync(path18.join(cwd, ".env")) || fs19.existsSync(path18.join(cwd, ".env.local"))) configs.push(".env");
2589
2854
  if (configs.length > 0) detected.push(`- Config files: ${configs.join(", ")}`);
2590
2855
  return detected;
2591
2856
  }
2592
2857
  function detectBasicConfig(cwd) {
2593
2858
  let pm = "pnpm";
2594
- if (fs18.existsSync(path17.join(cwd, "yarn.lock"))) pm = "yarn";
2595
- else if (fs18.existsSync(path17.join(cwd, "bun.lockb"))) pm = "bun";
2596
- else if (!fs18.existsSync(path17.join(cwd, "pnpm-lock.yaml")) && fs18.existsSync(path17.join(cwd, "package-lock.json"))) pm = "npm";
2859
+ if (fs19.existsSync(path18.join(cwd, "yarn.lock"))) pm = "yarn";
2860
+ else if (fs19.existsSync(path18.join(cwd, "bun.lockb"))) pm = "bun";
2861
+ else if (!fs19.existsSync(path18.join(cwd, "pnpm-lock.yaml")) && fs19.existsSync(path18.join(cwd, "package-lock.json"))) pm = "npm";
2597
2862
  let defaultBranch = "main";
2598
2863
  try {
2599
2864
  const ref = execFileSync11("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
@@ -2637,9 +2902,9 @@ function smartInit(cwd) {
2637
2902
  const basic = detectBasicConfig(cwd);
2638
2903
  let context = "";
2639
2904
  const readIfExists = (rel, maxChars = 3e3) => {
2640
- const p = path17.join(cwd, rel);
2641
- if (fs18.existsSync(p)) {
2642
- const content = fs18.readFileSync(p, "utf-8");
2905
+ const p = path18.join(cwd, rel);
2906
+ if (fs19.existsSync(p)) {
2907
+ const content = fs19.readFileSync(p, "utf-8");
2643
2908
  return content.slice(0, maxChars);
2644
2909
  }
2645
2910
  return null;
@@ -2665,14 +2930,14 @@ ${claudeMd}
2665
2930
 
2666
2931
  `;
2667
2932
  try {
2668
- const topDirs = fs18.readdirSync(cwd, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name);
2933
+ const topDirs = fs19.readdirSync(cwd, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name);
2669
2934
  context += `## Top-level directories
2670
2935
  ${topDirs.join(", ")}
2671
2936
 
2672
2937
  `;
2673
- const srcDir = path17.join(cwd, "src");
2674
- if (fs18.existsSync(srcDir)) {
2675
- const srcDirs = fs18.readdirSync(srcDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
2938
+ const srcDir = path18.join(cwd, "src");
2939
+ if (fs19.existsSync(srcDir)) {
2940
+ const srcDirs = fs19.readdirSync(srcDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
2676
2941
  context += `## src/ subdirectories
2677
2942
  ${srcDirs.join(", ")}
2678
2943
 
@@ -2682,7 +2947,7 @@ ${srcDirs.join(", ")}
2682
2947
  }
2683
2948
  const existingFiles = [];
2684
2949
  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"]) {
2685
- if (fs18.existsSync(path17.join(cwd, f))) existingFiles.push(f);
2950
+ if (fs19.existsSync(path18.join(cwd, f))) existingFiles.push(f);
2686
2951
  }
2687
2952
  if (existingFiles.length) context += `## Config files present
2688
2953
  ${existingFiles.join(", ")}
@@ -2788,7 +3053,7 @@ ${context}`;
2788
3053
  function validateQualityCommands(cwd, config, pm) {
2789
3054
  let scripts = {};
2790
3055
  try {
2791
- const pkg = JSON.parse(fs18.readFileSync(path17.join(cwd, "package.json"), "utf-8"));
3056
+ const pkg = JSON.parse(fs19.readFileSync(path18.join(cwd, "package.json"), "utf-8"));
2792
3057
  scripts = pkg.scripts ?? {};
2793
3058
  } catch {
2794
3059
  return;
@@ -2822,7 +3087,7 @@ function validateQualityCommands(cwd, config, pm) {
2822
3087
  function buildFallbackConfig(cwd, basic) {
2823
3088
  const pkg = (() => {
2824
3089
  try {
2825
- return JSON.parse(fs18.readFileSync(path17.join(cwd, "package.json"), "utf-8"));
3090
+ return JSON.parse(fs19.readFileSync(path18.join(cwd, "package.json"), "utf-8"));
2826
3091
  } catch {
2827
3092
  return {};
2828
3093
  }
@@ -2862,34 +3127,34 @@ function initCommand(opts) {
2862
3127
  console.log(`Project: ${cwd}
2863
3128
  `);
2864
3129
  console.log("\u2500\u2500 Files \u2500\u2500");
2865
- const templatesDir = path17.join(PKG_ROOT, "templates");
2866
- const workflowSrc = path17.join(templatesDir, "kody.yml");
2867
- const workflowDest = path17.join(cwd, ".github", "workflows", "kody.yml");
2868
- if (!fs18.existsSync(workflowSrc)) {
3130
+ const templatesDir = path18.join(PKG_ROOT, "templates");
3131
+ const workflowSrc = path18.join(templatesDir, "kody.yml");
3132
+ const workflowDest = path18.join(cwd, ".github", "workflows", "kody.yml");
3133
+ if (!fs19.existsSync(workflowSrc)) {
2869
3134
  console.error(" \u2717 Template kody.yml not found in package");
2870
3135
  process.exit(1);
2871
3136
  }
2872
- if (fs18.existsSync(workflowDest) && !opts.force) {
3137
+ if (fs19.existsSync(workflowDest) && !opts.force) {
2873
3138
  console.log(" \u25CB .github/workflows/kody.yml (exists, use --force to overwrite)");
2874
3139
  } else {
2875
- fs18.mkdirSync(path17.dirname(workflowDest), { recursive: true });
2876
- fs18.copyFileSync(workflowSrc, workflowDest);
3140
+ fs19.mkdirSync(path18.dirname(workflowDest), { recursive: true });
3141
+ fs19.copyFileSync(workflowSrc, workflowDest);
2877
3142
  console.log(" \u2713 .github/workflows/kody.yml");
2878
3143
  }
2879
- const configDest = path17.join(cwd, "kody.config.json");
3144
+ const configDest = path18.join(cwd, "kody.config.json");
2880
3145
  let smartResult = null;
2881
- if (!fs18.existsSync(configDest) || opts.force) {
3146
+ if (!fs19.existsSync(configDest) || opts.force) {
2882
3147
  smartResult = smartInit(cwd);
2883
- fs18.writeFileSync(configDest, JSON.stringify(smartResult.config, null, 2) + "\n");
3148
+ fs19.writeFileSync(configDest, JSON.stringify(smartResult.config, null, 2) + "\n");
2884
3149
  console.log(" \u2713 kody.config.json (auto-configured)");
2885
3150
  } else {
2886
3151
  console.log(" \u25CB kody.config.json (exists)");
2887
3152
  }
2888
- const gitignorePath = path17.join(cwd, ".gitignore");
2889
- if (fs18.existsSync(gitignorePath)) {
2890
- const content = fs18.readFileSync(gitignorePath, "utf-8");
3153
+ const gitignorePath = path18.join(cwd, ".gitignore");
3154
+ if (fs19.existsSync(gitignorePath)) {
3155
+ const content = fs19.readFileSync(gitignorePath, "utf-8");
2891
3156
  if (!content.includes(".tasks/")) {
2892
- fs18.appendFileSync(gitignorePath, "\n.tasks/\n");
3157
+ fs19.appendFileSync(gitignorePath, "\n.tasks/\n");
2893
3158
  console.log(" \u2713 .gitignore (added .tasks/)");
2894
3159
  } else {
2895
3160
  console.log(" \u25CB .gitignore (.tasks/ already present)");
@@ -2902,7 +3167,7 @@ function initCommand(opts) {
2902
3167
  checkCommand2("git", ["--version"], "Install git"),
2903
3168
  checkCommand2("node", ["--version"], "Install Node.js >= 22"),
2904
3169
  checkCommand2("pnpm", ["--version"], "Install: npm i -g pnpm"),
2905
- checkFile(path17.join(cwd, "package.json"), "package.json", "Run: pnpm init")
3170
+ checkFile(path18.join(cwd, "package.json"), "package.json", "Run: pnpm init")
2906
3171
  ];
2907
3172
  for (const c of checks) {
2908
3173
  if (c.ok) {
@@ -2979,9 +3244,9 @@ function initCommand(opts) {
2979
3244
  }
2980
3245
  }
2981
3246
  console.log("\n\u2500\u2500 Config \u2500\u2500");
2982
- if (fs18.existsSync(configDest)) {
3247
+ if (fs19.existsSync(configDest)) {
2983
3248
  try {
2984
- const config = JSON.parse(fs18.readFileSync(configDest, "utf-8"));
3249
+ const config = JSON.parse(fs19.readFileSync(configDest, "utf-8"));
2985
3250
  const configChecks = [];
2986
3251
  if (config.github?.owner && config.github?.repo) {
2987
3252
  configChecks.push({ name: "github.owner/repo", ok: true, detail: `${config.github.owner}/${config.github.repo}` });
@@ -3008,21 +3273,21 @@ function initCommand(opts) {
3008
3273
  }
3009
3274
  }
3010
3275
  console.log("\n\u2500\u2500 Project Memory \u2500\u2500");
3011
- const memoryDir = path17.join(cwd, ".kody", "memory");
3012
- fs18.mkdirSync(memoryDir, { recursive: true });
3013
- const archPath = path17.join(memoryDir, "architecture.md");
3014
- const conventionsPath = path17.join(memoryDir, "conventions.md");
3015
- if (fs18.existsSync(archPath) && !opts.force) {
3276
+ const memoryDir = path18.join(cwd, ".kody", "memory");
3277
+ fs19.mkdirSync(memoryDir, { recursive: true });
3278
+ const archPath = path18.join(memoryDir, "architecture.md");
3279
+ const conventionsPath = path18.join(memoryDir, "conventions.md");
3280
+ if (fs19.existsSync(archPath) && !opts.force) {
3016
3281
  console.log(" \u25CB .kody/memory/architecture.md (exists, use --force to regenerate)");
3017
3282
  } else if (smartResult?.architecture) {
3018
- fs18.writeFileSync(archPath, smartResult.architecture);
3283
+ fs19.writeFileSync(archPath, smartResult.architecture);
3019
3284
  const lineCount = smartResult.architecture.split("\n").length;
3020
3285
  console.log(` \u2713 .kody/memory/architecture.md (${lineCount} lines, LLM-generated)`);
3021
3286
  } else {
3022
3287
  const archItems = detectArchitecture(cwd);
3023
3288
  if (archItems.length > 0) {
3024
3289
  const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3025
- fs18.writeFileSync(archPath, `# Architecture (auto-detected ${timestamp2})
3290
+ fs19.writeFileSync(archPath, `# Architecture (auto-detected ${timestamp2})
3026
3291
 
3027
3292
  ## Overview
3028
3293
  ${archItems.join("\n")}
@@ -3032,14 +3297,14 @@ ${archItems.join("\n")}
3032
3297
  console.log(" \u25CB No architecture detected");
3033
3298
  }
3034
3299
  }
3035
- if (fs18.existsSync(conventionsPath) && !opts.force) {
3300
+ if (fs19.existsSync(conventionsPath) && !opts.force) {
3036
3301
  console.log(" \u25CB .kody/memory/conventions.md (exists, use --force to regenerate)");
3037
3302
  } else if (smartResult?.conventions) {
3038
- fs18.writeFileSync(conventionsPath, smartResult.conventions);
3303
+ fs19.writeFileSync(conventionsPath, smartResult.conventions);
3039
3304
  const lineCount = smartResult.conventions.split("\n").length;
3040
3305
  console.log(` \u2713 .kody/memory/conventions.md (${lineCount} lines, LLM-generated)`);
3041
3306
  } else {
3042
- fs18.writeFileSync(conventionsPath, "# Conventions\n\n<!-- Auto-learned conventions will be appended here -->\n");
3307
+ fs19.writeFileSync(conventionsPath, "# Conventions\n\n<!-- Auto-learned conventions will be appended here -->\n");
3043
3308
  console.log(" \u2713 .kody/memory/conventions.md (seed)");
3044
3309
  }
3045
3310
  const allChecks = [...checks, ghAuth, ghRepo];