@kody-ade/kody-engine-lite 0.1.132 → 0.1.134
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 +201 -15
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -1183,8 +1183,8 @@ async function tryStartLitellm(url, projectDir, generatedConfig) {
|
|
|
1183
1183
|
logger.info(` Loaded API keys: ${Object.keys(dotenvVars).join(", ")}`);
|
|
1184
1184
|
}
|
|
1185
1185
|
}
|
|
1186
|
-
const { spawn:
|
|
1187
|
-
const child =
|
|
1186
|
+
const { spawn: spawn3 } = await import("child_process");
|
|
1187
|
+
const child = spawn3(cmd, args2, {
|
|
1188
1188
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1189
1189
|
detached: true,
|
|
1190
1190
|
env: { ...process.env, ...dotenvVars }
|
|
@@ -2610,8 +2610,8 @@ async function startProxy(config, url) {
|
|
|
2610
2610
|
fs13.writeFileSync(CONFIG_PATH, config);
|
|
2611
2611
|
const portMatch = url.match(/:(\d+)/);
|
|
2612
2612
|
const port = portMatch ? portMatch[1] : "4099";
|
|
2613
|
-
const { spawn:
|
|
2614
|
-
const child =
|
|
2613
|
+
const { spawn: spawn3 } = await import("child_process");
|
|
2614
|
+
const child = spawn3("litellm", ["--config", CONFIG_PATH, "--port", port], {
|
|
2615
2615
|
stdio: ["ignore", "pipe", "pipe"],
|
|
2616
2616
|
detached: true,
|
|
2617
2617
|
env: process.env
|
|
@@ -3741,7 +3741,15 @@ function getDevServerInfo(taskDir) {
|
|
|
3741
3741
|
}
|
|
3742
3742
|
function getBrowserToolGuidance(stageName, taskDir) {
|
|
3743
3743
|
const devServer = getDevServerInfo(taskDir);
|
|
3744
|
-
const
|
|
3744
|
+
const engineManagedServer = process.env.KODY_DEV_SERVER_READY !== void 0;
|
|
3745
|
+
const serverReady = process.env.KODY_DEV_SERVER_READY === "true";
|
|
3746
|
+
const serverUrl = process.env.KODY_DEV_SERVER_URL ?? devServer?.url;
|
|
3747
|
+
const devServerBlock = engineManagedServer ? serverReady ? `
|
|
3748
|
+
### Dev Server
|
|
3749
|
+
The dev server is already running at ${serverUrl}. Do NOT start it yourself.
|
|
3750
|
+
You can use browser tools to navigate to ${serverUrl} directly.` : `
|
|
3751
|
+
### Dev Server
|
|
3752
|
+
The dev server failed to start (e.g. DB connection issues). Skip browser verification and proceed with code-only changes. Do NOT attempt to start the dev server yourself \u2014 it will hang.` : devServer ? `
|
|
3745
3753
|
### Dev Server Setup (REQUIRED before browsing)
|
|
3746
3754
|
You MUST start the dev server before using any browser navigation tools:
|
|
3747
3755
|
\`\`\`bash
|
|
@@ -3963,6 +3971,76 @@ var init_runner_selection = __esm({
|
|
|
3963
3971
|
}
|
|
3964
3972
|
});
|
|
3965
3973
|
|
|
3974
|
+
// src/dev-server.ts
|
|
3975
|
+
import { spawn as spawn2 } from "child_process";
|
|
3976
|
+
async function pollReady(url, timeoutSec) {
|
|
3977
|
+
const deadline = Date.now() + timeoutSec * 1e3;
|
|
3978
|
+
while (Date.now() < deadline) {
|
|
3979
|
+
try {
|
|
3980
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(2e3) });
|
|
3981
|
+
if (res.ok || res.status >= 200 && res.status < 400) return true;
|
|
3982
|
+
} catch {
|
|
3983
|
+
}
|
|
3984
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
3985
|
+
}
|
|
3986
|
+
return false;
|
|
3987
|
+
}
|
|
3988
|
+
async function startDevServer(opts) {
|
|
3989
|
+
const timeout = opts.readyTimeout ?? 30;
|
|
3990
|
+
const [cmd, ...args2] = opts.command.split(/\s+/);
|
|
3991
|
+
let child;
|
|
3992
|
+
try {
|
|
3993
|
+
child = spawn2(cmd, args2, {
|
|
3994
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3995
|
+
detached: true,
|
|
3996
|
+
shell: true,
|
|
3997
|
+
env: { ...process.env, ...opts.envVars }
|
|
3998
|
+
});
|
|
3999
|
+
} catch (err) {
|
|
4000
|
+
logger.warn(` Dev server failed to spawn: ${err instanceof Error ? err.message : String(err)}`);
|
|
4001
|
+
return { ready: false, url: opts.url, pid: void 0, stop: () => {
|
|
4002
|
+
} };
|
|
4003
|
+
}
|
|
4004
|
+
let stderr = "";
|
|
4005
|
+
child.stderr?.on("data", (chunk) => {
|
|
4006
|
+
stderr += chunk.toString();
|
|
4007
|
+
});
|
|
4008
|
+
let exited = false;
|
|
4009
|
+
child.on("exit", () => {
|
|
4010
|
+
exited = true;
|
|
4011
|
+
});
|
|
4012
|
+
child.unref();
|
|
4013
|
+
const ready = await pollReady(opts.url, timeout);
|
|
4014
|
+
if (!ready) {
|
|
4015
|
+
if (exited) {
|
|
4016
|
+
logger.warn(` Dev server exited before becoming ready`);
|
|
4017
|
+
} else {
|
|
4018
|
+
logger.warn(` Dev server did not respond within ${timeout}s at ${opts.url}`);
|
|
4019
|
+
}
|
|
4020
|
+
if (stderr) {
|
|
4021
|
+
logger.warn(` Dev server stderr (last 500 chars): ${stderr.slice(-500)}`);
|
|
4022
|
+
}
|
|
4023
|
+
}
|
|
4024
|
+
const pid = child.pid;
|
|
4025
|
+
const stop = () => {
|
|
4026
|
+
try {
|
|
4027
|
+
if (pid) process.kill(-pid, "SIGTERM");
|
|
4028
|
+
} catch {
|
|
4029
|
+
}
|
|
4030
|
+
try {
|
|
4031
|
+
child.kill("SIGTERM");
|
|
4032
|
+
} catch {
|
|
4033
|
+
}
|
|
4034
|
+
};
|
|
4035
|
+
return { ready, url: opts.url, pid, stop };
|
|
4036
|
+
}
|
|
4037
|
+
var init_dev_server = __esm({
|
|
4038
|
+
"src/dev-server.ts"() {
|
|
4039
|
+
"use strict";
|
|
4040
|
+
init_logger();
|
|
4041
|
+
}
|
|
4042
|
+
});
|
|
4043
|
+
|
|
3966
4044
|
// src/stages/agent.ts
|
|
3967
4045
|
import * as fs19 from "fs";
|
|
3968
4046
|
import * as path17 from "path";
|
|
@@ -4020,6 +4098,29 @@ async function executeAgentStage(ctx, def) {
|
|
|
4020
4098
|
if (mcpConfigJson) {
|
|
4021
4099
|
logger.info(` MCP servers enabled for ${def.name}`);
|
|
4022
4100
|
}
|
|
4101
|
+
let devServerHandle = null;
|
|
4102
|
+
const ds = config.mcp?.devServer;
|
|
4103
|
+
if (mcpConfigJson && ds && taskHasUI(ctx.taskDir)) {
|
|
4104
|
+
logger.info(` Starting dev server: ${ds.command}`);
|
|
4105
|
+
const envVars = {};
|
|
4106
|
+
for (const varName of ds.env ?? []) {
|
|
4107
|
+
if (process.env[varName]) envVars[varName] = process.env[varName];
|
|
4108
|
+
}
|
|
4109
|
+
devServerHandle = await startDevServer({
|
|
4110
|
+
command: ds.command,
|
|
4111
|
+
url: ds.url,
|
|
4112
|
+
readyTimeout: ds.readyTimeout ?? 30,
|
|
4113
|
+
envVars
|
|
4114
|
+
});
|
|
4115
|
+
if (devServerHandle.ready) {
|
|
4116
|
+
logger.info(` Dev server ready at ${ds.url}`);
|
|
4117
|
+
extraEnv.KODY_DEV_SERVER_URL = ds.url;
|
|
4118
|
+
extraEnv.KODY_DEV_SERVER_READY = "true";
|
|
4119
|
+
} else {
|
|
4120
|
+
logger.warn(` Dev server not ready \u2014 Claude will work without browser verification`);
|
|
4121
|
+
extraEnv.KODY_DEV_SERVER_READY = "false";
|
|
4122
|
+
}
|
|
4123
|
+
}
|
|
4023
4124
|
const runner = getRunnerForStage(ctx, def.name);
|
|
4024
4125
|
const maxRetries = def.maxRetries ?? 0;
|
|
4025
4126
|
let lastResult = await runner.run(def.name, prompt, model, def.timeout, ctx.taskDir, {
|
|
@@ -4047,6 +4148,10 @@ async function executeAgentStage(ctx, def) {
|
|
|
4047
4148
|
mcpConfigJson
|
|
4048
4149
|
});
|
|
4049
4150
|
}
|
|
4151
|
+
if (devServerHandle) {
|
|
4152
|
+
devServerHandle.stop();
|
|
4153
|
+
logger.info(` Dev server stopped`);
|
|
4154
|
+
}
|
|
4050
4155
|
if (lastResult.outcome !== "completed") {
|
|
4051
4156
|
return { outcome: lastResult.outcome, error: lastResult.error, retries };
|
|
4052
4157
|
}
|
|
@@ -4137,6 +4242,7 @@ var init_agent = __esm({
|
|
|
4137
4242
|
init_config();
|
|
4138
4243
|
init_mcp_config();
|
|
4139
4244
|
init_runner_selection();
|
|
4245
|
+
init_dev_server();
|
|
4140
4246
|
init_logger();
|
|
4141
4247
|
SESSION_GROUP = {
|
|
4142
4248
|
taskify: "explore",
|
|
@@ -7689,6 +7795,74 @@ function detectToolsForBootstrap(cwd) {
|
|
|
7689
7795
|
(tool) => tool.detect.some((pattern) => fs8.existsSync(path7.join(cwd, pattern)))
|
|
7690
7796
|
);
|
|
7691
7797
|
}
|
|
7798
|
+
var FRAMEWORK_SKILL_RULES = [
|
|
7799
|
+
{
|
|
7800
|
+
detect: (deps) => "next" in deps,
|
|
7801
|
+
skills: [
|
|
7802
|
+
{ skill: "vercel-labs/agent-skills@vercel-react-best-practices", label: "React best practices" },
|
|
7803
|
+
{ skill: "wshobson/agents@nextjs-app-router-patterns", label: "Next.js App Router patterns" }
|
|
7804
|
+
]
|
|
7805
|
+
},
|
|
7806
|
+
{
|
|
7807
|
+
detect: (deps) => "react" in deps && !("next" in deps),
|
|
7808
|
+
skills: [
|
|
7809
|
+
{ skill: "vercel-labs/agent-skills@vercel-react-best-practices", label: "React best practices" }
|
|
7810
|
+
]
|
|
7811
|
+
},
|
|
7812
|
+
{
|
|
7813
|
+
detect: (deps) => "vue" in deps,
|
|
7814
|
+
skills: [
|
|
7815
|
+
{ skill: "antfu/skills@vue", label: "Vue best practices" }
|
|
7816
|
+
]
|
|
7817
|
+
},
|
|
7818
|
+
{
|
|
7819
|
+
detect: (deps) => "svelte" in deps || "@sveltejs/kit" in deps,
|
|
7820
|
+
skills: [
|
|
7821
|
+
{ skill: "ejirocodes/agent-skills@svelte5-best-practices", label: "Svelte best practices" }
|
|
7822
|
+
]
|
|
7823
|
+
},
|
|
7824
|
+
{
|
|
7825
|
+
detect: (deps) => "@angular/core" in deps,
|
|
7826
|
+
skills: [
|
|
7827
|
+
{ skill: "analogjs/angular-skills@angular-component", label: "Angular component patterns" }
|
|
7828
|
+
]
|
|
7829
|
+
},
|
|
7830
|
+
{
|
|
7831
|
+
detect: (deps) => "payload" in deps,
|
|
7832
|
+
skills: [
|
|
7833
|
+
{ skill: "payloadcms/skills@payload", label: "Payload CMS patterns" }
|
|
7834
|
+
]
|
|
7835
|
+
},
|
|
7836
|
+
{
|
|
7837
|
+
detect: (deps) => "tailwindcss" in deps,
|
|
7838
|
+
skills: [
|
|
7839
|
+
{ skill: "wshobson/agents@tailwind-design-system", label: "Tailwind design system" }
|
|
7840
|
+
]
|
|
7841
|
+
}
|
|
7842
|
+
];
|
|
7843
|
+
function detectFrameworkSkills(cwd) {
|
|
7844
|
+
const pkgPath = path7.join(cwd, "package.json");
|
|
7845
|
+
if (!fs8.existsSync(pkgPath)) return [];
|
|
7846
|
+
try {
|
|
7847
|
+
const pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
|
|
7848
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
7849
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7850
|
+
const skills = [];
|
|
7851
|
+
for (const rule of FRAMEWORK_SKILL_RULES) {
|
|
7852
|
+
if (rule.detect(allDeps)) {
|
|
7853
|
+
for (const skill of rule.skills) {
|
|
7854
|
+
if (!seen.has(skill.skill)) {
|
|
7855
|
+
seen.add(skill.skill);
|
|
7856
|
+
skills.push(skill);
|
|
7857
|
+
}
|
|
7858
|
+
}
|
|
7859
|
+
}
|
|
7860
|
+
}
|
|
7861
|
+
return skills;
|
|
7862
|
+
} catch {
|
|
7863
|
+
return [];
|
|
7864
|
+
}
|
|
7865
|
+
}
|
|
7692
7866
|
function bootstrapCommand(opts, pkgRoot) {
|
|
7693
7867
|
const cwd = process.cwd();
|
|
7694
7868
|
setConfigDir(cwd);
|
|
@@ -8145,32 +8319,44 @@ ${entries}
|
|
|
8145
8319
|
}
|
|
8146
8320
|
console.log("\n\u2500\u2500 Skills \u2500\u2500");
|
|
8147
8321
|
const installedSkillPaths = [];
|
|
8148
|
-
const
|
|
8149
|
-
const
|
|
8150
|
-
|
|
8151
|
-
|
|
8322
|
+
const allSkills = [];
|
|
8323
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8324
|
+
for (const tool of detectToolsForBootstrap(cwd)) {
|
|
8325
|
+
if (tool.skill && !seen.has(tool.skill)) {
|
|
8326
|
+
seen.add(tool.skill);
|
|
8327
|
+
allSkills.push({ skill: tool.skill, label: `${tool.name} CLI` });
|
|
8328
|
+
}
|
|
8329
|
+
}
|
|
8330
|
+
for (const mapping of detectFrameworkSkills(cwd)) {
|
|
8331
|
+
if (!seen.has(mapping.skill)) {
|
|
8332
|
+
seen.add(mapping.skill);
|
|
8333
|
+
allSkills.push(mapping);
|
|
8334
|
+
}
|
|
8335
|
+
}
|
|
8336
|
+
if (allSkills.length > 0) {
|
|
8337
|
+
for (const { skill, label } of allSkills) {
|
|
8152
8338
|
try {
|
|
8153
|
-
console.log(` Installing: ${
|
|
8154
|
-
execFileSync4("npx", ["skills", "add",
|
|
8339
|
+
console.log(` Installing: ${label} (${skill})`);
|
|
8340
|
+
execFileSync4("npx", ["skills", "add", skill, "--yes"], {
|
|
8155
8341
|
cwd,
|
|
8156
8342
|
encoding: "utf-8",
|
|
8157
8343
|
timeout: 6e4,
|
|
8158
8344
|
stdio: ["pipe", "pipe", "pipe"]
|
|
8159
8345
|
});
|
|
8346
|
+
const skillName = skill.split("@").pop() ?? "";
|
|
8160
8347
|
for (const dir of [".claude/skills", ".agents/skills"]) {
|
|
8161
|
-
const skillName = tool.skill.split("@").pop() ?? "";
|
|
8162
8348
|
const skillPath = path7.join(dir, skillName);
|
|
8163
8349
|
if (fs8.existsSync(path7.join(cwd, skillPath))) {
|
|
8164
8350
|
installedSkillPaths.push(skillPath);
|
|
8165
8351
|
}
|
|
8166
8352
|
}
|
|
8167
|
-
console.log(` \u2713 ${
|
|
8353
|
+
console.log(` \u2713 ${label}`);
|
|
8168
8354
|
} catch {
|
|
8169
|
-
console.log(` \u2717 ${
|
|
8355
|
+
console.log(` \u2717 ${label} \u2014 failed to install`);
|
|
8170
8356
|
}
|
|
8171
8357
|
}
|
|
8172
8358
|
} else {
|
|
8173
|
-
console.log(" \u25CB No
|
|
8359
|
+
console.log(" \u25CB No skills to install (no frameworks detected)");
|
|
8174
8360
|
}
|
|
8175
8361
|
if (fs8.existsSync(path7.join(cwd, "skills-lock.json"))) {
|
|
8176
8362
|
installedSkillPaths.push("skills-lock.json");
|