@kody-ade/kody-engine-lite 0.1.33 → 0.1.35
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 +161 -126
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -2516,15 +2516,70 @@ var init_task_resolution = __esm({
|
|
|
2516
2516
|
}
|
|
2517
2517
|
});
|
|
2518
2518
|
|
|
2519
|
-
// src/
|
|
2520
|
-
var entry_exports = {};
|
|
2519
|
+
// src/cli/task-state.ts
|
|
2521
2520
|
import * as fs18 from "fs";
|
|
2522
2521
|
import * as path17 from "path";
|
|
2522
|
+
function resolveTaskAction(issueNumber, existingTaskId, existingState) {
|
|
2523
|
+
if (!existingTaskId || !existingState) {
|
|
2524
|
+
return { action: "start-fresh", taskId: `${issueNumber}-${generateTaskId()}` };
|
|
2525
|
+
}
|
|
2526
|
+
if (existingState.state === "completed") {
|
|
2527
|
+
return { action: "already-completed", taskId: existingTaskId };
|
|
2528
|
+
}
|
|
2529
|
+
if (existingState.state === "running") {
|
|
2530
|
+
return { action: "already-running", taskId: existingTaskId };
|
|
2531
|
+
}
|
|
2532
|
+
if (existingState.state === "failed") {
|
|
2533
|
+
for (const stageName of STAGE_ORDER) {
|
|
2534
|
+
const stage = existingState.stages[stageName];
|
|
2535
|
+
if (!stage) continue;
|
|
2536
|
+
if (stage.error?.includes("paused")) {
|
|
2537
|
+
const idx = STAGE_ORDER.indexOf(stageName);
|
|
2538
|
+
if (idx < STAGE_ORDER.length - 1) {
|
|
2539
|
+
return { action: "resume", taskId: existingTaskId, fromStage: STAGE_ORDER[idx + 1] };
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
if (stage.state === "failed" || stage.state === "pending") {
|
|
2543
|
+
return { action: "resume", taskId: existingTaskId, fromStage: stageName };
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
return { action: "resume", taskId: existingTaskId, fromStage: "taskify" };
|
|
2547
|
+
}
|
|
2548
|
+
return { action: "start-fresh", taskId: `${issueNumber}-${generateTaskId()}` };
|
|
2549
|
+
}
|
|
2550
|
+
function resolveForIssue(issueNumber, projectDir) {
|
|
2551
|
+
const existingTaskId = findLatestTaskForIssue(issueNumber, projectDir);
|
|
2552
|
+
if (!existingTaskId) {
|
|
2553
|
+
return resolveTaskAction(issueNumber, null, null);
|
|
2554
|
+
}
|
|
2555
|
+
const statusPath = path17.join(projectDir, ".tasks", existingTaskId, "status.json");
|
|
2556
|
+
let existingState = null;
|
|
2557
|
+
if (fs18.existsSync(statusPath)) {
|
|
2558
|
+
try {
|
|
2559
|
+
existingState = JSON.parse(fs18.readFileSync(statusPath, "utf-8"));
|
|
2560
|
+
} catch {
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
return resolveTaskAction(issueNumber, existingTaskId, existingState);
|
|
2564
|
+
}
|
|
2565
|
+
var STAGE_ORDER;
|
|
2566
|
+
var init_task_state = __esm({
|
|
2567
|
+
"src/cli/task-state.ts"() {
|
|
2568
|
+
"use strict";
|
|
2569
|
+
init_task_resolution();
|
|
2570
|
+
STAGE_ORDER = ["taskify", "plan", "build", "verify", "review", "review-fix", "ship"];
|
|
2571
|
+
}
|
|
2572
|
+
});
|
|
2573
|
+
|
|
2574
|
+
// src/entry.ts
|
|
2575
|
+
var entry_exports = {};
|
|
2576
|
+
import * as fs19 from "fs";
|
|
2577
|
+
import * as path18 from "path";
|
|
2523
2578
|
async function main() {
|
|
2524
2579
|
const input = parseArgs();
|
|
2525
|
-
const projectDir = input.cwd ?
|
|
2580
|
+
const projectDir = input.cwd ? path18.resolve(input.cwd) : process.cwd();
|
|
2526
2581
|
if (input.cwd) {
|
|
2527
|
-
if (!
|
|
2582
|
+
if (!fs19.existsSync(projectDir)) {
|
|
2528
2583
|
console.error(`--cwd path does not exist: ${projectDir}`);
|
|
2529
2584
|
process.exit(1);
|
|
2530
2585
|
}
|
|
@@ -2534,16 +2589,35 @@ async function main() {
|
|
|
2534
2589
|
}
|
|
2535
2590
|
let taskId = input.taskId;
|
|
2536
2591
|
if (!taskId) {
|
|
2537
|
-
if (
|
|
2538
|
-
const
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2592
|
+
if (input.issueNumber) {
|
|
2593
|
+
const taskAction = resolveForIssue(input.issueNumber, projectDir);
|
|
2594
|
+
logger.info(`Task action: ${taskAction.action}`);
|
|
2595
|
+
if (taskAction.action === "already-completed") {
|
|
2596
|
+
logger.info(`Issue #${input.issueNumber} already completed (task ${taskAction.taskId})`);
|
|
2597
|
+
if (!input.local) {
|
|
2598
|
+
try {
|
|
2599
|
+
postComment(input.issueNumber, `\u2705 Issue #${input.issueNumber} already completed (task \`${taskAction.taskId}\`)`);
|
|
2600
|
+
} catch {
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
process.exit(0);
|
|
2604
|
+
}
|
|
2605
|
+
if (taskAction.action === "already-running") {
|
|
2606
|
+
logger.info(`Issue #${input.issueNumber} already running (task ${taskAction.taskId})`);
|
|
2607
|
+
if (!input.local) {
|
|
2608
|
+
try {
|
|
2609
|
+
postComment(input.issueNumber, `\u23F3 Pipeline already running for issue #${input.issueNumber} (task \`${taskAction.taskId}\`)`);
|
|
2610
|
+
} catch {
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
process.exit(0);
|
|
2614
|
+
}
|
|
2615
|
+
taskId = taskAction.taskId;
|
|
2616
|
+
if (taskAction.action === "resume") {
|
|
2617
|
+
input.fromStage = taskAction.fromStage;
|
|
2618
|
+
input.command = "rerun";
|
|
2619
|
+
logger.info(`Resuming task ${taskId} from ${taskAction.fromStage}`);
|
|
2542
2620
|
}
|
|
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
2621
|
} else if (input.command === "run" && input.task) {
|
|
2548
2622
|
taskId = generateTaskId();
|
|
2549
2623
|
} else {
|
|
@@ -2551,8 +2625,8 @@ async function main() {
|
|
|
2551
2625
|
process.exit(1);
|
|
2552
2626
|
}
|
|
2553
2627
|
}
|
|
2554
|
-
const taskDir =
|
|
2555
|
-
|
|
2628
|
+
const taskDir = path18.join(projectDir, ".tasks", taskId);
|
|
2629
|
+
fs19.mkdirSync(taskDir, { recursive: true });
|
|
2556
2630
|
if (input.command === "status") {
|
|
2557
2631
|
printStatus(taskId, taskDir);
|
|
2558
2632
|
return;
|
|
@@ -2560,67 +2634,27 @@ async function main() {
|
|
|
2560
2634
|
logger.info("Preflight checks:");
|
|
2561
2635
|
runPreflight();
|
|
2562
2636
|
if (input.task) {
|
|
2563
|
-
|
|
2637
|
+
fs19.writeFileSync(path18.join(taskDir, "task.md"), input.task);
|
|
2564
2638
|
}
|
|
2565
|
-
const taskMdPath =
|
|
2566
|
-
if (!
|
|
2639
|
+
const taskMdPath = path18.join(taskDir, "task.md");
|
|
2640
|
+
if (!fs19.existsSync(taskMdPath) && input.issueNumber) {
|
|
2567
2641
|
logger.info(`Fetching issue #${input.issueNumber} body as task...`);
|
|
2568
2642
|
const issue = getIssue(input.issueNumber);
|
|
2569
2643
|
if (issue) {
|
|
2570
2644
|
const taskContent = `# ${issue.title}
|
|
2571
2645
|
|
|
2572
2646
|
${issue.body ?? ""}`;
|
|
2573
|
-
|
|
2647
|
+
fs19.writeFileSync(taskMdPath, taskContent);
|
|
2574
2648
|
logger.info(` Task loaded from issue #${input.issueNumber}: ${issue.title}`);
|
|
2575
2649
|
}
|
|
2576
2650
|
}
|
|
2577
|
-
if (
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
process.exit(1);
|
|
2581
|
-
}
|
|
2651
|
+
if (!fs19.existsSync(taskMdPath)) {
|
|
2652
|
+
console.error("No task.md found. Provide --task, --issue-number, or ensure .tasks/<id>/task.md exists.");
|
|
2653
|
+
process.exit(1);
|
|
2582
2654
|
}
|
|
2583
2655
|
if (input.command === "fix" && !input.fromStage) {
|
|
2584
2656
|
input.fromStage = "build";
|
|
2585
2657
|
}
|
|
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
2658
|
const config = getProjectConfig();
|
|
2625
2659
|
let litellmProcess = null;
|
|
2626
2660
|
const cleanupLitellm = () => {
|
|
@@ -2694,7 +2728,7 @@ To rerun: \`@kody rerun ${taskId} --from <stage>\``
|
|
|
2694
2728
|
}
|
|
2695
2729
|
}
|
|
2696
2730
|
const state = await runPipeline(ctx);
|
|
2697
|
-
const files =
|
|
2731
|
+
const files = fs19.readdirSync(taskDir);
|
|
2698
2732
|
console.log(`
|
|
2699
2733
|
Artifacts in ${taskDir}:`);
|
|
2700
2734
|
for (const f of files) {
|
|
@@ -2738,6 +2772,7 @@ var init_entry = __esm({
|
|
|
2738
2772
|
init_args();
|
|
2739
2773
|
init_litellm();
|
|
2740
2774
|
init_task_resolution();
|
|
2775
|
+
init_task_state();
|
|
2741
2776
|
main().catch(async (err) => {
|
|
2742
2777
|
const msg = err instanceof Error ? err.message : String(err);
|
|
2743
2778
|
console.error(msg);
|
|
@@ -2755,15 +2790,15 @@ var init_entry = __esm({
|
|
|
2755
2790
|
});
|
|
2756
2791
|
|
|
2757
2792
|
// src/bin/cli.ts
|
|
2758
|
-
import * as
|
|
2759
|
-
import * as
|
|
2793
|
+
import * as fs20 from "fs";
|
|
2794
|
+
import * as path19 from "path";
|
|
2760
2795
|
import { execFileSync as execFileSync11 } from "child_process";
|
|
2761
2796
|
import { fileURLToPath } from "url";
|
|
2762
|
-
var __dirname =
|
|
2763
|
-
var PKG_ROOT =
|
|
2797
|
+
var __dirname = path19.dirname(fileURLToPath(import.meta.url));
|
|
2798
|
+
var PKG_ROOT = path19.resolve(__dirname, "..", "..");
|
|
2764
2799
|
function getVersion() {
|
|
2765
|
-
const pkgPath =
|
|
2766
|
-
const pkg = JSON.parse(
|
|
2800
|
+
const pkgPath = path19.join(PKG_ROOT, "package.json");
|
|
2801
|
+
const pkg = JSON.parse(fs20.readFileSync(pkgPath, "utf-8"));
|
|
2767
2802
|
return pkg.version;
|
|
2768
2803
|
}
|
|
2769
2804
|
function checkCommand2(name, args2, fix) {
|
|
@@ -2779,7 +2814,7 @@ function checkCommand2(name, args2, fix) {
|
|
|
2779
2814
|
}
|
|
2780
2815
|
}
|
|
2781
2816
|
function checkFile(filePath, description, fix) {
|
|
2782
|
-
if (
|
|
2817
|
+
if (fs20.existsSync(filePath)) {
|
|
2783
2818
|
return { name: description, ok: true, detail: filePath };
|
|
2784
2819
|
}
|
|
2785
2820
|
return { name: description, ok: false, fix };
|
|
@@ -2851,10 +2886,10 @@ function checkGhSecret(repoSlug, secretName) {
|
|
|
2851
2886
|
}
|
|
2852
2887
|
function detectArchitecture(cwd) {
|
|
2853
2888
|
const detected = [];
|
|
2854
|
-
const pkgPath =
|
|
2855
|
-
if (
|
|
2889
|
+
const pkgPath = path19.join(cwd, "package.json");
|
|
2890
|
+
if (fs20.existsSync(pkgPath)) {
|
|
2856
2891
|
try {
|
|
2857
|
-
const pkg = JSON.parse(
|
|
2892
|
+
const pkg = JSON.parse(fs20.readFileSync(pkgPath, "utf-8"));
|
|
2858
2893
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
2859
2894
|
if (allDeps.next) detected.push(`- Framework: Next.js ${allDeps.next}`);
|
|
2860
2895
|
else if (allDeps.react) detected.push(`- Framework: React ${allDeps.react}`);
|
|
@@ -2877,41 +2912,41 @@ function detectArchitecture(cwd) {
|
|
|
2877
2912
|
if (allDeps.tailwindcss) detected.push(`- CSS: Tailwind CSS ${allDeps.tailwindcss}`);
|
|
2878
2913
|
if (pkg.type === "module") detected.push("- Module system: ESM");
|
|
2879
2914
|
else detected.push("- Module system: CommonJS");
|
|
2880
|
-
if (
|
|
2881
|
-
else if (
|
|
2882
|
-
else if (
|
|
2883
|
-
else if (
|
|
2915
|
+
if (fs20.existsSync(path19.join(cwd, "pnpm-lock.yaml"))) detected.push("- Package manager: pnpm");
|
|
2916
|
+
else if (fs20.existsSync(path19.join(cwd, "yarn.lock"))) detected.push("- Package manager: yarn");
|
|
2917
|
+
else if (fs20.existsSync(path19.join(cwd, "bun.lockb"))) detected.push("- Package manager: bun");
|
|
2918
|
+
else if (fs20.existsSync(path19.join(cwd, "package-lock.json"))) detected.push("- Package manager: npm");
|
|
2884
2919
|
} catch {
|
|
2885
2920
|
}
|
|
2886
2921
|
}
|
|
2887
2922
|
try {
|
|
2888
|
-
const entries =
|
|
2923
|
+
const entries = fs20.readdirSync(cwd, { withFileTypes: true });
|
|
2889
2924
|
const dirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name);
|
|
2890
2925
|
if (dirs.length > 0) detected.push(`- Top-level directories: ${dirs.join(", ")}`);
|
|
2891
2926
|
} catch {
|
|
2892
2927
|
}
|
|
2893
|
-
const srcDir =
|
|
2894
|
-
if (
|
|
2928
|
+
const srcDir = path19.join(cwd, "src");
|
|
2929
|
+
if (fs20.existsSync(srcDir)) {
|
|
2895
2930
|
try {
|
|
2896
|
-
const srcEntries =
|
|
2931
|
+
const srcEntries = fs20.readdirSync(srcDir, { withFileTypes: true });
|
|
2897
2932
|
const srcDirs = srcEntries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
2898
2933
|
if (srcDirs.length > 0) detected.push(`- src/ structure: ${srcDirs.join(", ")}`);
|
|
2899
2934
|
} catch {
|
|
2900
2935
|
}
|
|
2901
2936
|
}
|
|
2902
2937
|
const configs = [];
|
|
2903
|
-
if (
|
|
2904
|
-
if (
|
|
2905
|
-
if (
|
|
2906
|
-
if (
|
|
2938
|
+
if (fs20.existsSync(path19.join(cwd, "tsconfig.json"))) configs.push("tsconfig.json");
|
|
2939
|
+
if (fs20.existsSync(path19.join(cwd, "docker-compose.yml")) || fs20.existsSync(path19.join(cwd, "docker-compose.yaml"))) configs.push("docker-compose");
|
|
2940
|
+
if (fs20.existsSync(path19.join(cwd, "Dockerfile"))) configs.push("Dockerfile");
|
|
2941
|
+
if (fs20.existsSync(path19.join(cwd, ".env")) || fs20.existsSync(path19.join(cwd, ".env.local"))) configs.push(".env");
|
|
2907
2942
|
if (configs.length > 0) detected.push(`- Config files: ${configs.join(", ")}`);
|
|
2908
2943
|
return detected;
|
|
2909
2944
|
}
|
|
2910
2945
|
function detectBasicConfig(cwd) {
|
|
2911
2946
|
let pm = "pnpm";
|
|
2912
|
-
if (
|
|
2913
|
-
else if (
|
|
2914
|
-
else if (!
|
|
2947
|
+
if (fs20.existsSync(path19.join(cwd, "yarn.lock"))) pm = "yarn";
|
|
2948
|
+
else if (fs20.existsSync(path19.join(cwd, "bun.lockb"))) pm = "bun";
|
|
2949
|
+
else if (!fs20.existsSync(path19.join(cwd, "pnpm-lock.yaml")) && fs20.existsSync(path19.join(cwd, "package-lock.json"))) pm = "npm";
|
|
2915
2950
|
let defaultBranch = "main";
|
|
2916
2951
|
try {
|
|
2917
2952
|
const ref = execFileSync11("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], {
|
|
@@ -2955,9 +2990,9 @@ function smartInit(cwd) {
|
|
|
2955
2990
|
const basic = detectBasicConfig(cwd);
|
|
2956
2991
|
let context = "";
|
|
2957
2992
|
const readIfExists = (rel, maxChars = 3e3) => {
|
|
2958
|
-
const p =
|
|
2959
|
-
if (
|
|
2960
|
-
const content =
|
|
2993
|
+
const p = path19.join(cwd, rel);
|
|
2994
|
+
if (fs20.existsSync(p)) {
|
|
2995
|
+
const content = fs20.readFileSync(p, "utf-8");
|
|
2961
2996
|
return content.slice(0, maxChars);
|
|
2962
2997
|
}
|
|
2963
2998
|
return null;
|
|
@@ -2983,14 +3018,14 @@ ${claudeMd}
|
|
|
2983
3018
|
|
|
2984
3019
|
`;
|
|
2985
3020
|
try {
|
|
2986
|
-
const topDirs =
|
|
3021
|
+
const topDirs = fs20.readdirSync(cwd, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => e.name);
|
|
2987
3022
|
context += `## Top-level directories
|
|
2988
3023
|
${topDirs.join(", ")}
|
|
2989
3024
|
|
|
2990
3025
|
`;
|
|
2991
|
-
const srcDir =
|
|
2992
|
-
if (
|
|
2993
|
-
const srcDirs =
|
|
3026
|
+
const srcDir = path19.join(cwd, "src");
|
|
3027
|
+
if (fs20.existsSync(srcDir)) {
|
|
3028
|
+
const srcDirs = fs20.readdirSync(srcDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
2994
3029
|
context += `## src/ subdirectories
|
|
2995
3030
|
${srcDirs.join(", ")}
|
|
2996
3031
|
|
|
@@ -3000,7 +3035,7 @@ ${srcDirs.join(", ")}
|
|
|
3000
3035
|
}
|
|
3001
3036
|
const existingFiles = [];
|
|
3002
3037
|
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 (
|
|
3038
|
+
if (fs20.existsSync(path19.join(cwd, f))) existingFiles.push(f);
|
|
3004
3039
|
}
|
|
3005
3040
|
if (existingFiles.length) context += `## Config files present
|
|
3006
3041
|
${existingFiles.join(", ")}
|
|
@@ -3106,7 +3141,7 @@ ${context}`;
|
|
|
3106
3141
|
function validateQualityCommands(cwd, config, pm) {
|
|
3107
3142
|
let scripts = {};
|
|
3108
3143
|
try {
|
|
3109
|
-
const pkg = JSON.parse(
|
|
3144
|
+
const pkg = JSON.parse(fs20.readFileSync(path19.join(cwd, "package.json"), "utf-8"));
|
|
3110
3145
|
scripts = pkg.scripts ?? {};
|
|
3111
3146
|
} catch {
|
|
3112
3147
|
return;
|
|
@@ -3140,7 +3175,7 @@ function validateQualityCommands(cwd, config, pm) {
|
|
|
3140
3175
|
function buildFallbackConfig(cwd, basic) {
|
|
3141
3176
|
const pkg = (() => {
|
|
3142
3177
|
try {
|
|
3143
|
-
return JSON.parse(
|
|
3178
|
+
return JSON.parse(fs20.readFileSync(path19.join(cwd, "package.json"), "utf-8"));
|
|
3144
3179
|
} catch {
|
|
3145
3180
|
return {};
|
|
3146
3181
|
}
|
|
@@ -3180,34 +3215,34 @@ function initCommand(opts) {
|
|
|
3180
3215
|
console.log(`Project: ${cwd}
|
|
3181
3216
|
`);
|
|
3182
3217
|
console.log("\u2500\u2500 Files \u2500\u2500");
|
|
3183
|
-
const templatesDir =
|
|
3184
|
-
const workflowSrc =
|
|
3185
|
-
const workflowDest =
|
|
3186
|
-
if (!
|
|
3218
|
+
const templatesDir = path19.join(PKG_ROOT, "templates");
|
|
3219
|
+
const workflowSrc = path19.join(templatesDir, "kody.yml");
|
|
3220
|
+
const workflowDest = path19.join(cwd, ".github", "workflows", "kody.yml");
|
|
3221
|
+
if (!fs20.existsSync(workflowSrc)) {
|
|
3187
3222
|
console.error(" \u2717 Template kody.yml not found in package");
|
|
3188
3223
|
process.exit(1);
|
|
3189
3224
|
}
|
|
3190
|
-
if (
|
|
3225
|
+
if (fs20.existsSync(workflowDest) && !opts.force) {
|
|
3191
3226
|
console.log(" \u25CB .github/workflows/kody.yml (exists, use --force to overwrite)");
|
|
3192
3227
|
} else {
|
|
3193
|
-
|
|
3194
|
-
|
|
3228
|
+
fs20.mkdirSync(path19.dirname(workflowDest), { recursive: true });
|
|
3229
|
+
fs20.copyFileSync(workflowSrc, workflowDest);
|
|
3195
3230
|
console.log(" \u2713 .github/workflows/kody.yml");
|
|
3196
3231
|
}
|
|
3197
|
-
const configDest =
|
|
3232
|
+
const configDest = path19.join(cwd, "kody.config.json");
|
|
3198
3233
|
let smartResult = null;
|
|
3199
|
-
if (!
|
|
3234
|
+
if (!fs20.existsSync(configDest) || opts.force) {
|
|
3200
3235
|
smartResult = smartInit(cwd);
|
|
3201
|
-
|
|
3236
|
+
fs20.writeFileSync(configDest, JSON.stringify(smartResult.config, null, 2) + "\n");
|
|
3202
3237
|
console.log(" \u2713 kody.config.json (auto-configured)");
|
|
3203
3238
|
} else {
|
|
3204
3239
|
console.log(" \u25CB kody.config.json (exists)");
|
|
3205
3240
|
}
|
|
3206
|
-
const gitignorePath =
|
|
3207
|
-
if (
|
|
3208
|
-
const content =
|
|
3241
|
+
const gitignorePath = path19.join(cwd, ".gitignore");
|
|
3242
|
+
if (fs20.existsSync(gitignorePath)) {
|
|
3243
|
+
const content = fs20.readFileSync(gitignorePath, "utf-8");
|
|
3209
3244
|
if (!content.includes(".tasks/")) {
|
|
3210
|
-
|
|
3245
|
+
fs20.appendFileSync(gitignorePath, "\n.tasks/\n");
|
|
3211
3246
|
console.log(" \u2713 .gitignore (added .tasks/)");
|
|
3212
3247
|
} else {
|
|
3213
3248
|
console.log(" \u25CB .gitignore (.tasks/ already present)");
|
|
@@ -3220,7 +3255,7 @@ function initCommand(opts) {
|
|
|
3220
3255
|
checkCommand2("git", ["--version"], "Install git"),
|
|
3221
3256
|
checkCommand2("node", ["--version"], "Install Node.js >= 22"),
|
|
3222
3257
|
checkCommand2("pnpm", ["--version"], "Install: npm i -g pnpm"),
|
|
3223
|
-
checkFile(
|
|
3258
|
+
checkFile(path19.join(cwd, "package.json"), "package.json", "Run: pnpm init")
|
|
3224
3259
|
];
|
|
3225
3260
|
for (const c of checks) {
|
|
3226
3261
|
if (c.ok) {
|
|
@@ -3297,9 +3332,9 @@ function initCommand(opts) {
|
|
|
3297
3332
|
}
|
|
3298
3333
|
}
|
|
3299
3334
|
console.log("\n\u2500\u2500 Config \u2500\u2500");
|
|
3300
|
-
if (
|
|
3335
|
+
if (fs20.existsSync(configDest)) {
|
|
3301
3336
|
try {
|
|
3302
|
-
const config = JSON.parse(
|
|
3337
|
+
const config = JSON.parse(fs20.readFileSync(configDest, "utf-8"));
|
|
3303
3338
|
const configChecks = [];
|
|
3304
3339
|
if (config.github?.owner && config.github?.repo) {
|
|
3305
3340
|
configChecks.push({ name: "github.owner/repo", ok: true, detail: `${config.github.owner}/${config.github.repo}` });
|
|
@@ -3326,21 +3361,21 @@ function initCommand(opts) {
|
|
|
3326
3361
|
}
|
|
3327
3362
|
}
|
|
3328
3363
|
console.log("\n\u2500\u2500 Project Memory \u2500\u2500");
|
|
3329
|
-
const memoryDir =
|
|
3330
|
-
|
|
3331
|
-
const archPath =
|
|
3332
|
-
const conventionsPath =
|
|
3333
|
-
if (
|
|
3364
|
+
const memoryDir = path19.join(cwd, ".kody", "memory");
|
|
3365
|
+
fs20.mkdirSync(memoryDir, { recursive: true });
|
|
3366
|
+
const archPath = path19.join(memoryDir, "architecture.md");
|
|
3367
|
+
const conventionsPath = path19.join(memoryDir, "conventions.md");
|
|
3368
|
+
if (fs20.existsSync(archPath) && !opts.force) {
|
|
3334
3369
|
console.log(" \u25CB .kody/memory/architecture.md (exists, use --force to regenerate)");
|
|
3335
3370
|
} else if (smartResult?.architecture) {
|
|
3336
|
-
|
|
3371
|
+
fs20.writeFileSync(archPath, smartResult.architecture);
|
|
3337
3372
|
const lineCount = smartResult.architecture.split("\n").length;
|
|
3338
3373
|
console.log(` \u2713 .kody/memory/architecture.md (${lineCount} lines, LLM-generated)`);
|
|
3339
3374
|
} else {
|
|
3340
3375
|
const archItems = detectArchitecture(cwd);
|
|
3341
3376
|
if (archItems.length > 0) {
|
|
3342
3377
|
const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
3343
|
-
|
|
3378
|
+
fs20.writeFileSync(archPath, `# Architecture (auto-detected ${timestamp2})
|
|
3344
3379
|
|
|
3345
3380
|
## Overview
|
|
3346
3381
|
${archItems.join("\n")}
|
|
@@ -3350,14 +3385,14 @@ ${archItems.join("\n")}
|
|
|
3350
3385
|
console.log(" \u25CB No architecture detected");
|
|
3351
3386
|
}
|
|
3352
3387
|
}
|
|
3353
|
-
if (
|
|
3388
|
+
if (fs20.existsSync(conventionsPath) && !opts.force) {
|
|
3354
3389
|
console.log(" \u25CB .kody/memory/conventions.md (exists, use --force to regenerate)");
|
|
3355
3390
|
} else if (smartResult?.conventions) {
|
|
3356
|
-
|
|
3391
|
+
fs20.writeFileSync(conventionsPath, smartResult.conventions);
|
|
3357
3392
|
const lineCount = smartResult.conventions.split("\n").length;
|
|
3358
3393
|
console.log(` \u2713 .kody/memory/conventions.md (${lineCount} lines, LLM-generated)`);
|
|
3359
3394
|
} else {
|
|
3360
|
-
|
|
3395
|
+
fs20.writeFileSync(conventionsPath, "# Conventions\n\n<!-- Auto-learned conventions will be appended here -->\n");
|
|
3361
3396
|
console.log(" \u2713 .kody/memory/conventions.md (seed)");
|
|
3362
3397
|
}
|
|
3363
3398
|
console.log("\n\u2500\u2500 Git \u2500\u2500");
|
|
@@ -3366,7 +3401,7 @@ ${archItems.join("\n")}
|
|
|
3366
3401
|
"kody.config.json",
|
|
3367
3402
|
".kody/memory/architecture.md",
|
|
3368
3403
|
".kody/memory/conventions.md"
|
|
3369
|
-
].filter((f) =>
|
|
3404
|
+
].filter((f) => fs20.existsSync(path19.join(cwd, f)));
|
|
3370
3405
|
if (filesToCommit.length > 0) {
|
|
3371
3406
|
try {
|
|
3372
3407
|
execFileSync11("git", ["add", ...filesToCommit], { cwd, stdio: "pipe" });
|