@kody-ade/kody-engine-lite 0.1.34 → 0.1.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/bin/cli.js +177 -126
  2. package/package.json +1 -1
package/dist/bin/cli.js CHANGED
@@ -414,6 +414,14 @@ function getIssue(issueNumber) {
414
414
  return null;
415
415
  }
416
416
  }
417
+ function getIssueLabels(issueNumber) {
418
+ try {
419
+ const output = gh(["issue", "view", String(issueNumber), "--json", "labels", "--jq", ".labels[].name"]);
420
+ return output.split("\n").filter(Boolean);
421
+ } catch {
422
+ return [];
423
+ }
424
+ }
417
425
  function setLabel(issueNumber, label) {
418
426
  try {
419
427
  gh(["issue", "edit", String(issueNumber), "--add-label", label]);
@@ -2516,15 +2524,78 @@ var init_task_resolution = __esm({
2516
2524
  }
2517
2525
  });
2518
2526
 
2519
- // src/entry.ts
2520
- var entry_exports = {};
2527
+ // src/cli/task-state.ts
2521
2528
  import * as fs18 from "fs";
2522
2529
  import * as path17 from "path";
2530
+ function resolveTaskAction(issueNumber, existingTaskId, existingState) {
2531
+ if (!existingTaskId || !existingState) {
2532
+ return { action: "start-fresh", taskId: `${issueNumber}-${generateTaskId()}` };
2533
+ }
2534
+ if (existingState.state === "completed") {
2535
+ return { action: "already-completed", taskId: existingTaskId };
2536
+ }
2537
+ if (existingState.state === "running") {
2538
+ return { action: "already-running", taskId: existingTaskId };
2539
+ }
2540
+ if (existingState.state === "failed") {
2541
+ for (const stageName of STAGE_ORDER) {
2542
+ const stage = existingState.stages[stageName];
2543
+ if (!stage) continue;
2544
+ if (stage.error?.includes("paused")) {
2545
+ const idx = STAGE_ORDER.indexOf(stageName);
2546
+ if (idx < STAGE_ORDER.length - 1) {
2547
+ return { action: "resume", taskId: existingTaskId, fromStage: STAGE_ORDER[idx + 1] };
2548
+ }
2549
+ }
2550
+ if (stage.state === "failed" || stage.state === "pending") {
2551
+ return { action: "resume", taskId: existingTaskId, fromStage: stageName };
2552
+ }
2553
+ }
2554
+ return { action: "resume", taskId: existingTaskId, fromStage: "taskify" };
2555
+ }
2556
+ return { action: "start-fresh", taskId: `${issueNumber}-${generateTaskId()}` };
2557
+ }
2558
+ function resolveForIssue(issueNumber, projectDir) {
2559
+ const existingTaskId = findLatestTaskForIssue(issueNumber, projectDir);
2560
+ if (existingTaskId) {
2561
+ const statusPath = path17.join(projectDir, ".tasks", existingTaskId, "status.json");
2562
+ let existingState = null;
2563
+ if (fs18.existsSync(statusPath)) {
2564
+ try {
2565
+ existingState = JSON.parse(fs18.readFileSync(statusPath, "utf-8"));
2566
+ } catch {
2567
+ }
2568
+ }
2569
+ return resolveTaskAction(issueNumber, existingTaskId, existingState);
2570
+ }
2571
+ try {
2572
+ const labels = getIssueLabels(issueNumber);
2573
+ if (labels.includes("kody:done")) {
2574
+ return { action: "already-completed", taskId: `${issueNumber}-unknown` };
2575
+ }
2576
+ } catch {
2577
+ }
2578
+ return resolveTaskAction(issueNumber, null, null);
2579
+ }
2580
+ var STAGE_ORDER;
2581
+ var init_task_state = __esm({
2582
+ "src/cli/task-state.ts"() {
2583
+ "use strict";
2584
+ init_task_resolution();
2585
+ init_github_api();
2586
+ STAGE_ORDER = ["taskify", "plan", "build", "verify", "review", "review-fix", "ship"];
2587
+ }
2588
+ });
2589
+
2590
+ // src/entry.ts
2591
+ var entry_exports = {};
2592
+ import * as fs19 from "fs";
2593
+ import * as path18 from "path";
2523
2594
  async function main() {
2524
2595
  const input = parseArgs();
2525
- const projectDir = input.cwd ? path17.resolve(input.cwd) : process.cwd();
2596
+ const projectDir = input.cwd ? path18.resolve(input.cwd) : process.cwd();
2526
2597
  if (input.cwd) {
2527
- if (!fs18.existsSync(projectDir)) {
2598
+ if (!fs19.existsSync(projectDir)) {
2528
2599
  console.error(`--cwd path does not exist: ${projectDir}`);
2529
2600
  process.exit(1);
2530
2601
  }
@@ -2534,16 +2605,35 @@ async function main() {
2534
2605
  }
2535
2606
  let taskId = input.taskId;
2536
2607
  if (!taskId) {
2537
- if ((input.command === "rerun" || input.command === "fix") && input.issueNumber) {
2538
- const found = findLatestTaskForIssue(input.issueNumber, projectDir);
2539
- if (!found) {
2540
- console.error(`No previous task found for issue #${input.issueNumber}`);
2541
- process.exit(1);
2608
+ if (input.issueNumber) {
2609
+ const taskAction = resolveForIssue(input.issueNumber, projectDir);
2610
+ logger.info(`Task action: ${taskAction.action}`);
2611
+ if (taskAction.action === "already-completed") {
2612
+ logger.info(`Issue #${input.issueNumber} already completed (task ${taskAction.taskId})`);
2613
+ if (!input.local) {
2614
+ try {
2615
+ postComment(input.issueNumber, `\u2705 Issue #${input.issueNumber} already completed (task \`${taskAction.taskId}\`)`);
2616
+ } catch {
2617
+ }
2618
+ }
2619
+ process.exit(0);
2620
+ }
2621
+ if (taskAction.action === "already-running") {
2622
+ logger.info(`Issue #${input.issueNumber} already running (task ${taskAction.taskId})`);
2623
+ if (!input.local) {
2624
+ try {
2625
+ postComment(input.issueNumber, `\u23F3 Pipeline already running for issue #${input.issueNumber} (task \`${taskAction.taskId}\`)`);
2626
+ } catch {
2627
+ }
2628
+ }
2629
+ process.exit(0);
2630
+ }
2631
+ taskId = taskAction.taskId;
2632
+ if (taskAction.action === "resume") {
2633
+ input.fromStage = taskAction.fromStage;
2634
+ input.command = "rerun";
2635
+ logger.info(`Resuming task ${taskId} from ${taskAction.fromStage}`);
2542
2636
  }
2543
- taskId = found;
2544
- logger.info(`Found latest task for issue #${input.issueNumber}: ${taskId}`);
2545
- } else if (input.issueNumber) {
2546
- taskId = `${input.issueNumber}-${generateTaskId()}`;
2547
2637
  } else if (input.command === "run" && input.task) {
2548
2638
  taskId = generateTaskId();
2549
2639
  } else {
@@ -2551,8 +2641,8 @@ async function main() {
2551
2641
  process.exit(1);
2552
2642
  }
2553
2643
  }
2554
- const taskDir = path17.join(projectDir, ".tasks", taskId);
2555
- fs18.mkdirSync(taskDir, { recursive: true });
2644
+ const taskDir = path18.join(projectDir, ".tasks", taskId);
2645
+ fs19.mkdirSync(taskDir, { recursive: true });
2556
2646
  if (input.command === "status") {
2557
2647
  printStatus(taskId, taskDir);
2558
2648
  return;
@@ -2560,67 +2650,27 @@ async function main() {
2560
2650
  logger.info("Preflight checks:");
2561
2651
  runPreflight();
2562
2652
  if (input.task) {
2563
- fs18.writeFileSync(path17.join(taskDir, "task.md"), input.task);
2653
+ fs19.writeFileSync(path18.join(taskDir, "task.md"), input.task);
2564
2654
  }
2565
- const taskMdPath = path17.join(taskDir, "task.md");
2566
- if (!fs18.existsSync(taskMdPath) && input.issueNumber) {
2655
+ const taskMdPath = path18.join(taskDir, "task.md");
2656
+ if (!fs19.existsSync(taskMdPath) && input.issueNumber) {
2567
2657
  logger.info(`Fetching issue #${input.issueNumber} body as task...`);
2568
2658
  const issue = getIssue(input.issueNumber);
2569
2659
  if (issue) {
2570
2660
  const taskContent = `# ${issue.title}
2571
2661
 
2572
2662
  ${issue.body ?? ""}`;
2573
- fs18.writeFileSync(taskMdPath, taskContent);
2663
+ fs19.writeFileSync(taskMdPath, taskContent);
2574
2664
  logger.info(` Task loaded from issue #${input.issueNumber}: ${issue.title}`);
2575
2665
  }
2576
2666
  }
2577
- if (input.command === "run") {
2578
- if (!fs18.existsSync(taskMdPath)) {
2579
- console.error("No task.md found. Provide --task, --issue-number, or ensure .tasks/<id>/task.md exists.");
2580
- process.exit(1);
2581
- }
2667
+ if (!fs19.existsSync(taskMdPath)) {
2668
+ console.error("No task.md found. Provide --task, --issue-number, or ensure .tasks/<id>/task.md exists.");
2669
+ process.exit(1);
2582
2670
  }
2583
2671
  if (input.command === "fix" && !input.fromStage) {
2584
2672
  input.fromStage = "build";
2585
2673
  }
2586
- if (input.command === "rerun" && !input.fromStage) {
2587
- const statusPath = path17.join(taskDir, "status.json");
2588
- if (fs18.existsSync(statusPath)) {
2589
- try {
2590
- const status = JSON.parse(fs18.readFileSync(statusPath, "utf-8"));
2591
- const stageNames = ["taskify", "plan", "build", "verify", "review", "review-fix", "ship"];
2592
- let foundPaused = false;
2593
- for (const name of stageNames) {
2594
- const s = status.stages[name];
2595
- if (s?.error?.includes("paused")) {
2596
- const idx = stageNames.indexOf(name);
2597
- if (idx < stageNames.length - 1) {
2598
- input.fromStage = stageNames[idx + 1];
2599
- foundPaused = true;
2600
- logger.info(`Auto-detected resume from: ${input.fromStage} (after paused ${name})`);
2601
- break;
2602
- }
2603
- }
2604
- if (s?.state === "failed" || s?.state === "pending") {
2605
- input.fromStage = name;
2606
- foundPaused = true;
2607
- logger.info(`Auto-detected resume from: ${input.fromStage}`);
2608
- break;
2609
- }
2610
- }
2611
- if (!foundPaused) {
2612
- input.fromStage = "taskify";
2613
- logger.info("No paused/failed stage found, resuming from taskify");
2614
- }
2615
- } catch {
2616
- console.error("--from <stage> is required (could not read status.json)");
2617
- process.exit(1);
2618
- }
2619
- } else {
2620
- logger.info("No status.json found \u2014 running full pipeline with feedback");
2621
- input.command = "run";
2622
- }
2623
- }
2624
2674
  const config = getProjectConfig();
2625
2675
  let litellmProcess = null;
2626
2676
  const cleanupLitellm = () => {
@@ -2694,7 +2744,7 @@ To rerun: \`@kody rerun ${taskId} --from <stage>\``
2694
2744
  }
2695
2745
  }
2696
2746
  const state = await runPipeline(ctx);
2697
- const files = fs18.readdirSync(taskDir);
2747
+ const files = fs19.readdirSync(taskDir);
2698
2748
  console.log(`
2699
2749
  Artifacts in ${taskDir}:`);
2700
2750
  for (const f of files) {
@@ -2738,6 +2788,7 @@ var init_entry = __esm({
2738
2788
  init_args();
2739
2789
  init_litellm();
2740
2790
  init_task_resolution();
2791
+ init_task_state();
2741
2792
  main().catch(async (err) => {
2742
2793
  const msg = err instanceof Error ? err.message : String(err);
2743
2794
  console.error(msg);
@@ -2755,15 +2806,15 @@ var init_entry = __esm({
2755
2806
  });
2756
2807
 
2757
2808
  // src/bin/cli.ts
2758
- import * as fs19 from "fs";
2759
- import * as path18 from "path";
2809
+ import * as fs20 from "fs";
2810
+ import * as path19 from "path";
2760
2811
  import { execFileSync as execFileSync11 } from "child_process";
2761
2812
  import { fileURLToPath } from "url";
2762
- var __dirname = path18.dirname(fileURLToPath(import.meta.url));
2763
- var PKG_ROOT = path18.resolve(__dirname, "..", "..");
2813
+ var __dirname = path19.dirname(fileURLToPath(import.meta.url));
2814
+ var PKG_ROOT = path19.resolve(__dirname, "..", "..");
2764
2815
  function getVersion() {
2765
- const pkgPath = path18.join(PKG_ROOT, "package.json");
2766
- const pkg = JSON.parse(fs19.readFileSync(pkgPath, "utf-8"));
2816
+ const pkgPath = path19.join(PKG_ROOT, "package.json");
2817
+ const pkg = JSON.parse(fs20.readFileSync(pkgPath, "utf-8"));
2767
2818
  return pkg.version;
2768
2819
  }
2769
2820
  function checkCommand2(name, args2, fix) {
@@ -2779,7 +2830,7 @@ function checkCommand2(name, args2, fix) {
2779
2830
  }
2780
2831
  }
2781
2832
  function checkFile(filePath, description, fix) {
2782
- if (fs19.existsSync(filePath)) {
2833
+ if (fs20.existsSync(filePath)) {
2783
2834
  return { name: description, ok: true, detail: filePath };
2784
2835
  }
2785
2836
  return { name: description, ok: false, fix };
@@ -2851,10 +2902,10 @@ function checkGhSecret(repoSlug, secretName) {
2851
2902
  }
2852
2903
  function detectArchitecture(cwd) {
2853
2904
  const detected = [];
2854
- const pkgPath = path18.join(cwd, "package.json");
2855
- if (fs19.existsSync(pkgPath)) {
2905
+ const pkgPath = path19.join(cwd, "package.json");
2906
+ if (fs20.existsSync(pkgPath)) {
2856
2907
  try {
2857
- const pkg = JSON.parse(fs19.readFileSync(pkgPath, "utf-8"));
2908
+ const pkg = JSON.parse(fs20.readFileSync(pkgPath, "utf-8"));
2858
2909
  const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
2859
2910
  if (allDeps.next) detected.push(`- Framework: Next.js ${allDeps.next}`);
2860
2911
  else if (allDeps.react) detected.push(`- Framework: React ${allDeps.react}`);
@@ -2877,41 +2928,41 @@ function detectArchitecture(cwd) {
2877
2928
  if (allDeps.tailwindcss) detected.push(`- CSS: Tailwind CSS ${allDeps.tailwindcss}`);
2878
2929
  if (pkg.type === "module") detected.push("- Module system: ESM");
2879
2930
  else detected.push("- Module system: CommonJS");
2880
- if (fs19.existsSync(path18.join(cwd, "pnpm-lock.yaml"))) detected.push("- Package manager: pnpm");
2881
- else if (fs19.existsSync(path18.join(cwd, "yarn.lock"))) detected.push("- Package manager: yarn");
2882
- else if (fs19.existsSync(path18.join(cwd, "bun.lockb"))) detected.push("- Package manager: bun");
2883
- else if (fs19.existsSync(path18.join(cwd, "package-lock.json"))) detected.push("- Package manager: npm");
2931
+ if (fs20.existsSync(path19.join(cwd, "pnpm-lock.yaml"))) detected.push("- Package manager: pnpm");
2932
+ else if (fs20.existsSync(path19.join(cwd, "yarn.lock"))) detected.push("- Package manager: yarn");
2933
+ else if (fs20.existsSync(path19.join(cwd, "bun.lockb"))) detected.push("- Package manager: bun");
2934
+ else if (fs20.existsSync(path19.join(cwd, "package-lock.json"))) detected.push("- Package manager: npm");
2884
2935
  } catch {
2885
2936
  }
2886
2937
  }
2887
2938
  try {
2888
- const entries = fs19.readdirSync(cwd, { withFileTypes: true });
2939
+ const entries = fs20.readdirSync(cwd, { withFileTypes: true });
2889
2940
  const dirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name);
2890
2941
  if (dirs.length > 0) detected.push(`- Top-level directories: ${dirs.join(", ")}`);
2891
2942
  } catch {
2892
2943
  }
2893
- const srcDir = path18.join(cwd, "src");
2894
- if (fs19.existsSync(srcDir)) {
2944
+ const srcDir = path19.join(cwd, "src");
2945
+ if (fs20.existsSync(srcDir)) {
2895
2946
  try {
2896
- const srcEntries = fs19.readdirSync(srcDir, { withFileTypes: true });
2947
+ const srcEntries = fs20.readdirSync(srcDir, { withFileTypes: true });
2897
2948
  const srcDirs = srcEntries.filter((e) => e.isDirectory()).map((e) => e.name);
2898
2949
  if (srcDirs.length > 0) detected.push(`- src/ structure: ${srcDirs.join(", ")}`);
2899
2950
  } catch {
2900
2951
  }
2901
2952
  }
2902
2953
  const configs = [];
2903
- if (fs19.existsSync(path18.join(cwd, "tsconfig.json"))) configs.push("tsconfig.json");
2904
- if (fs19.existsSync(path18.join(cwd, "docker-compose.yml")) || fs19.existsSync(path18.join(cwd, "docker-compose.yaml"))) configs.push("docker-compose");
2905
- if (fs19.existsSync(path18.join(cwd, "Dockerfile"))) configs.push("Dockerfile");
2906
- if (fs19.existsSync(path18.join(cwd, ".env")) || fs19.existsSync(path18.join(cwd, ".env.local"))) configs.push(".env");
2954
+ if (fs20.existsSync(path19.join(cwd, "tsconfig.json"))) configs.push("tsconfig.json");
2955
+ if (fs20.existsSync(path19.join(cwd, "docker-compose.yml")) || fs20.existsSync(path19.join(cwd, "docker-compose.yaml"))) configs.push("docker-compose");
2956
+ if (fs20.existsSync(path19.join(cwd, "Dockerfile"))) configs.push("Dockerfile");
2957
+ if (fs20.existsSync(path19.join(cwd, ".env")) || fs20.existsSync(path19.join(cwd, ".env.local"))) configs.push(".env");
2907
2958
  if (configs.length > 0) detected.push(`- Config files: ${configs.join(", ")}`);
2908
2959
  return detected;
2909
2960
  }
2910
2961
  function detectBasicConfig(cwd) {
2911
2962
  let pm = "pnpm";
2912
- if (fs19.existsSync(path18.join(cwd, "yarn.lock"))) pm = "yarn";
2913
- else if (fs19.existsSync(path18.join(cwd, "bun.lockb"))) pm = "bun";
2914
- else if (!fs19.existsSync(path18.join(cwd, "pnpm-lock.yaml")) && fs19.existsSync(path18.join(cwd, "package-lock.json"))) pm = "npm";
2963
+ if (fs20.existsSync(path19.join(cwd, "yarn.lock"))) pm = "yarn";
2964
+ else if (fs20.existsSync(path19.join(cwd, "bun.lockb"))) pm = "bun";
2965
+ else if (!fs20.existsSync(path19.join(cwd, "pnpm-lock.yaml")) && fs20.existsSync(path19.join(cwd, "package-lock.json"))) pm = "npm";
2915
2966
  let defaultBranch = "main";
2916
2967
  try {
2917
2968
  const ref = execFileSync11("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
@@ -2955,9 +3006,9 @@ function smartInit(cwd) {
2955
3006
  const basic = detectBasicConfig(cwd);
2956
3007
  let context = "";
2957
3008
  const readIfExists = (rel, maxChars = 3e3) => {
2958
- const p = path18.join(cwd, rel);
2959
- if (fs19.existsSync(p)) {
2960
- const content = fs19.readFileSync(p, "utf-8");
3009
+ const p = path19.join(cwd, rel);
3010
+ if (fs20.existsSync(p)) {
3011
+ const content = fs20.readFileSync(p, "utf-8");
2961
3012
  return content.slice(0, maxChars);
2962
3013
  }
2963
3014
  return null;
@@ -2983,14 +3034,14 @@ ${claudeMd}
2983
3034
 
2984
3035
  `;
2985
3036
  try {
2986
- const topDirs = fs19.readdirSync(cwd, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name);
3037
+ const topDirs = fs20.readdirSync(cwd, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name);
2987
3038
  context += `## Top-level directories
2988
3039
  ${topDirs.join(", ")}
2989
3040
 
2990
3041
  `;
2991
- const srcDir = path18.join(cwd, "src");
2992
- if (fs19.existsSync(srcDir)) {
2993
- const srcDirs = fs19.readdirSync(srcDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
3042
+ const srcDir = path19.join(cwd, "src");
3043
+ if (fs20.existsSync(srcDir)) {
3044
+ const srcDirs = fs20.readdirSync(srcDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
2994
3045
  context += `## src/ subdirectories
2995
3046
  ${srcDirs.join(", ")}
2996
3047
 
@@ -3000,7 +3051,7 @@ ${srcDirs.join(", ")}
3000
3051
  }
3001
3052
  const existingFiles = [];
3002
3053
  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"]) {
3003
- if (fs19.existsSync(path18.join(cwd, f))) existingFiles.push(f);
3054
+ if (fs20.existsSync(path19.join(cwd, f))) existingFiles.push(f);
3004
3055
  }
3005
3056
  if (existingFiles.length) context += `## Config files present
3006
3057
  ${existingFiles.join(", ")}
@@ -3106,7 +3157,7 @@ ${context}`;
3106
3157
  function validateQualityCommands(cwd, config, pm) {
3107
3158
  let scripts = {};
3108
3159
  try {
3109
- const pkg = JSON.parse(fs19.readFileSync(path18.join(cwd, "package.json"), "utf-8"));
3160
+ const pkg = JSON.parse(fs20.readFileSync(path19.join(cwd, "package.json"), "utf-8"));
3110
3161
  scripts = pkg.scripts ?? {};
3111
3162
  } catch {
3112
3163
  return;
@@ -3140,7 +3191,7 @@ function validateQualityCommands(cwd, config, pm) {
3140
3191
  function buildFallbackConfig(cwd, basic) {
3141
3192
  const pkg = (() => {
3142
3193
  try {
3143
- return JSON.parse(fs19.readFileSync(path18.join(cwd, "package.json"), "utf-8"));
3194
+ return JSON.parse(fs20.readFileSync(path19.join(cwd, "package.json"), "utf-8"));
3144
3195
  } catch {
3145
3196
  return {};
3146
3197
  }
@@ -3180,34 +3231,34 @@ function initCommand(opts) {
3180
3231
  console.log(`Project: ${cwd}
3181
3232
  `);
3182
3233
  console.log("\u2500\u2500 Files \u2500\u2500");
3183
- const templatesDir = path18.join(PKG_ROOT, "templates");
3184
- const workflowSrc = path18.join(templatesDir, "kody.yml");
3185
- const workflowDest = path18.join(cwd, ".github", "workflows", "kody.yml");
3186
- if (!fs19.existsSync(workflowSrc)) {
3234
+ const templatesDir = path19.join(PKG_ROOT, "templates");
3235
+ const workflowSrc = path19.join(templatesDir, "kody.yml");
3236
+ const workflowDest = path19.join(cwd, ".github", "workflows", "kody.yml");
3237
+ if (!fs20.existsSync(workflowSrc)) {
3187
3238
  console.error(" \u2717 Template kody.yml not found in package");
3188
3239
  process.exit(1);
3189
3240
  }
3190
- if (fs19.existsSync(workflowDest) && !opts.force) {
3241
+ if (fs20.existsSync(workflowDest) && !opts.force) {
3191
3242
  console.log(" \u25CB .github/workflows/kody.yml (exists, use --force to overwrite)");
3192
3243
  } else {
3193
- fs19.mkdirSync(path18.dirname(workflowDest), { recursive: true });
3194
- fs19.copyFileSync(workflowSrc, workflowDest);
3244
+ fs20.mkdirSync(path19.dirname(workflowDest), { recursive: true });
3245
+ fs20.copyFileSync(workflowSrc, workflowDest);
3195
3246
  console.log(" \u2713 .github/workflows/kody.yml");
3196
3247
  }
3197
- const configDest = path18.join(cwd, "kody.config.json");
3248
+ const configDest = path19.join(cwd, "kody.config.json");
3198
3249
  let smartResult = null;
3199
- if (!fs19.existsSync(configDest) || opts.force) {
3250
+ if (!fs20.existsSync(configDest) || opts.force) {
3200
3251
  smartResult = smartInit(cwd);
3201
- fs19.writeFileSync(configDest, JSON.stringify(smartResult.config, null, 2) + "\n");
3252
+ fs20.writeFileSync(configDest, JSON.stringify(smartResult.config, null, 2) + "\n");
3202
3253
  console.log(" \u2713 kody.config.json (auto-configured)");
3203
3254
  } else {
3204
3255
  console.log(" \u25CB kody.config.json (exists)");
3205
3256
  }
3206
- const gitignorePath = path18.join(cwd, ".gitignore");
3207
- if (fs19.existsSync(gitignorePath)) {
3208
- const content = fs19.readFileSync(gitignorePath, "utf-8");
3257
+ const gitignorePath = path19.join(cwd, ".gitignore");
3258
+ if (fs20.existsSync(gitignorePath)) {
3259
+ const content = fs20.readFileSync(gitignorePath, "utf-8");
3209
3260
  if (!content.includes(".tasks/")) {
3210
- fs19.appendFileSync(gitignorePath, "\n.tasks/\n");
3261
+ fs20.appendFileSync(gitignorePath, "\n.tasks/\n");
3211
3262
  console.log(" \u2713 .gitignore (added .tasks/)");
3212
3263
  } else {
3213
3264
  console.log(" \u25CB .gitignore (.tasks/ already present)");
@@ -3220,7 +3271,7 @@ function initCommand(opts) {
3220
3271
  checkCommand2("git", ["--version"], "Install git"),
3221
3272
  checkCommand2("node", ["--version"], "Install Node.js >= 22"),
3222
3273
  checkCommand2("pnpm", ["--version"], "Install: npm i -g pnpm"),
3223
- checkFile(path18.join(cwd, "package.json"), "package.json", "Run: pnpm init")
3274
+ checkFile(path19.join(cwd, "package.json"), "package.json", "Run: pnpm init")
3224
3275
  ];
3225
3276
  for (const c of checks) {
3226
3277
  if (c.ok) {
@@ -3297,9 +3348,9 @@ function initCommand(opts) {
3297
3348
  }
3298
3349
  }
3299
3350
  console.log("\n\u2500\u2500 Config \u2500\u2500");
3300
- if (fs19.existsSync(configDest)) {
3351
+ if (fs20.existsSync(configDest)) {
3301
3352
  try {
3302
- const config = JSON.parse(fs19.readFileSync(configDest, "utf-8"));
3353
+ const config = JSON.parse(fs20.readFileSync(configDest, "utf-8"));
3303
3354
  const configChecks = [];
3304
3355
  if (config.github?.owner && config.github?.repo) {
3305
3356
  configChecks.push({ name: "github.owner/repo", ok: true, detail: `${config.github.owner}/${config.github.repo}` });
@@ -3326,21 +3377,21 @@ function initCommand(opts) {
3326
3377
  }
3327
3378
  }
3328
3379
  console.log("\n\u2500\u2500 Project Memory \u2500\u2500");
3329
- const memoryDir = path18.join(cwd, ".kody", "memory");
3330
- fs19.mkdirSync(memoryDir, { recursive: true });
3331
- const archPath = path18.join(memoryDir, "architecture.md");
3332
- const conventionsPath = path18.join(memoryDir, "conventions.md");
3333
- if (fs19.existsSync(archPath) && !opts.force) {
3380
+ const memoryDir = path19.join(cwd, ".kody", "memory");
3381
+ fs20.mkdirSync(memoryDir, { recursive: true });
3382
+ const archPath = path19.join(memoryDir, "architecture.md");
3383
+ const conventionsPath = path19.join(memoryDir, "conventions.md");
3384
+ if (fs20.existsSync(archPath) && !opts.force) {
3334
3385
  console.log(" \u25CB .kody/memory/architecture.md (exists, use --force to regenerate)");
3335
3386
  } else if (smartResult?.architecture) {
3336
- fs19.writeFileSync(archPath, smartResult.architecture);
3387
+ fs20.writeFileSync(archPath, smartResult.architecture);
3337
3388
  const lineCount = smartResult.architecture.split("\n").length;
3338
3389
  console.log(` \u2713 .kody/memory/architecture.md (${lineCount} lines, LLM-generated)`);
3339
3390
  } else {
3340
3391
  const archItems = detectArchitecture(cwd);
3341
3392
  if (archItems.length > 0) {
3342
3393
  const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
3343
- fs19.writeFileSync(archPath, `# Architecture (auto-detected ${timestamp2})
3394
+ fs20.writeFileSync(archPath, `# Architecture (auto-detected ${timestamp2})
3344
3395
 
3345
3396
  ## Overview
3346
3397
  ${archItems.join("\n")}
@@ -3350,14 +3401,14 @@ ${archItems.join("\n")}
3350
3401
  console.log(" \u25CB No architecture detected");
3351
3402
  }
3352
3403
  }
3353
- if (fs19.existsSync(conventionsPath) && !opts.force) {
3404
+ if (fs20.existsSync(conventionsPath) && !opts.force) {
3354
3405
  console.log(" \u25CB .kody/memory/conventions.md (exists, use --force to regenerate)");
3355
3406
  } else if (smartResult?.conventions) {
3356
- fs19.writeFileSync(conventionsPath, smartResult.conventions);
3407
+ fs20.writeFileSync(conventionsPath, smartResult.conventions);
3357
3408
  const lineCount = smartResult.conventions.split("\n").length;
3358
3409
  console.log(` \u2713 .kody/memory/conventions.md (${lineCount} lines, LLM-generated)`);
3359
3410
  } else {
3360
- fs19.writeFileSync(conventionsPath, "# Conventions\n\n<!-- Auto-learned conventions will be appended here -->\n");
3411
+ fs20.writeFileSync(conventionsPath, "# Conventions\n\n<!-- Auto-learned conventions will be appended here -->\n");
3361
3412
  console.log(" \u2713 .kody/memory/conventions.md (seed)");
3362
3413
  }
3363
3414
  console.log("\n\u2500\u2500 Git \u2500\u2500");
@@ -3366,7 +3417,7 @@ ${archItems.join("\n")}
3366
3417
  "kody.config.json",
3367
3418
  ".kody/memory/architecture.md",
3368
3419
  ".kody/memory/conventions.md"
3369
- ].filter((f) => fs19.existsSync(path18.join(cwd, f)));
3420
+ ].filter((f) => fs20.existsSync(path19.join(cwd, f)));
3370
3421
  if (filesToCommit.length > 0) {
3371
3422
  try {
3372
3423
  execFileSync11("git", ["add", ...filesToCommit], { cwd, stdio: "pipe" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine-lite",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
4
4
  "description": "Autonomous SDLC pipeline: Kody orchestration + Claude Code + LiteLLM",
5
5
  "license": "MIT",
6
6
  "type": "module",