@kody-ade/kody-engine-lite 0.1.133 → 0.1.135
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 +226 -92
- 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,74 +7795,80 @@ function detectToolsForBootstrap(cwd) {
|
|
|
7689
7795
|
(tool) => tool.detect.some((pattern) => fs8.existsSync(path7.join(cwd, pattern)))
|
|
7690
7796
|
);
|
|
7691
7797
|
}
|
|
7692
|
-
var
|
|
7693
|
-
|
|
7694
|
-
|
|
7695
|
-
|
|
7696
|
-
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
|
|
7712
|
-
|
|
7713
|
-
|
|
7714
|
-
|
|
7715
|
-
|
|
7716
|
-
]
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
|
|
7723
|
-
},
|
|
7724
|
-
{
|
|
7725
|
-
detect: (deps) => "payload" in deps,
|
|
7726
|
-
skills: [
|
|
7727
|
-
{ skill: "payloadcms/skills@payload", label: "Payload CMS patterns" }
|
|
7728
|
-
]
|
|
7729
|
-
},
|
|
7730
|
-
{
|
|
7731
|
-
detect: (deps) => "tailwindcss" in deps,
|
|
7732
|
-
skills: [
|
|
7733
|
-
{ skill: "wshobson/agents@tailwind-design-system", label: "Tailwind design system" }
|
|
7734
|
-
]
|
|
7735
|
-
}
|
|
7736
|
-
];
|
|
7737
|
-
function detectFrameworkSkills(cwd) {
|
|
7798
|
+
var FRAMEWORK_KEYWORDS = {
|
|
7799
|
+
next: "nextjs",
|
|
7800
|
+
react: "react",
|
|
7801
|
+
vue: "vue",
|
|
7802
|
+
svelte: "svelte",
|
|
7803
|
+
"@angular/core": "angular",
|
|
7804
|
+
payload: "payload cms",
|
|
7805
|
+
tailwindcss: "tailwind",
|
|
7806
|
+
nuxt: "nuxt",
|
|
7807
|
+
astro: "astro",
|
|
7808
|
+
"solid-js": "solidjs",
|
|
7809
|
+
express: "express",
|
|
7810
|
+
fastify: "fastify",
|
|
7811
|
+
prisma: "prisma"
|
|
7812
|
+
};
|
|
7813
|
+
function parseSkillsSearchOutput(output) {
|
|
7814
|
+
const stripped = output.replace(/\x1b\[[0-9;]*m/g, "");
|
|
7815
|
+
const results = [];
|
|
7816
|
+
for (const line of stripped.split("\n")) {
|
|
7817
|
+
const match = line.match(/^([a-zA-Z0-9._-]+\/[a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+)\s+([\d.]+)([KM]?)\s+installs/);
|
|
7818
|
+
if (!match) continue;
|
|
7819
|
+
const ref = match[1];
|
|
7820
|
+
const name = ref.split("@").pop() ?? "";
|
|
7821
|
+
let installs = parseFloat(match[2]);
|
|
7822
|
+
if (match[3] === "K") installs *= 1e3;
|
|
7823
|
+
if (match[3] === "M") installs *= 1e6;
|
|
7824
|
+
results.push({ ref, name, installs });
|
|
7825
|
+
}
|
|
7826
|
+
return results;
|
|
7827
|
+
}
|
|
7828
|
+
function detectProjectKeywords(cwd) {
|
|
7738
7829
|
const pkgPath = path7.join(cwd, "package.json");
|
|
7739
7830
|
if (!fs8.existsSync(pkgPath)) return [];
|
|
7740
7831
|
try {
|
|
7741
7832
|
const pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
|
|
7742
7833
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
7743
|
-
const
|
|
7744
|
-
const
|
|
7745
|
-
|
|
7746
|
-
if (rule.detect(allDeps)) {
|
|
7747
|
-
for (const skill of rule.skills) {
|
|
7748
|
-
if (!seen.has(skill.skill)) {
|
|
7749
|
-
seen.add(skill.skill);
|
|
7750
|
-
skills.push(skill);
|
|
7751
|
-
}
|
|
7752
|
-
}
|
|
7753
|
-
}
|
|
7834
|
+
const keywords = [];
|
|
7835
|
+
for (const [dep, keyword] of Object.entries(FRAMEWORK_KEYWORDS)) {
|
|
7836
|
+
if (dep in allDeps) keywords.push(keyword);
|
|
7754
7837
|
}
|
|
7755
|
-
return
|
|
7838
|
+
return keywords;
|
|
7756
7839
|
} catch {
|
|
7757
7840
|
return [];
|
|
7758
7841
|
}
|
|
7759
7842
|
}
|
|
7843
|
+
function searchSkills(keywords, exclude, limit) {
|
|
7844
|
+
const allResults = [];
|
|
7845
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7846
|
+
for (const keyword of keywords) {
|
|
7847
|
+
try {
|
|
7848
|
+
const output = execFileSync4("npx", ["skills", "find", keyword], {
|
|
7849
|
+
encoding: "utf-8",
|
|
7850
|
+
timeout: 15e3,
|
|
7851
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
7852
|
+
});
|
|
7853
|
+
for (const skill of parseSkillsSearchOutput(output)) {
|
|
7854
|
+
if (!seen.has(skill.ref) && !exclude.has(skill.name)) {
|
|
7855
|
+
seen.add(skill.ref);
|
|
7856
|
+
allResults.push(skill);
|
|
7857
|
+
}
|
|
7858
|
+
}
|
|
7859
|
+
} catch {
|
|
7860
|
+
}
|
|
7861
|
+
}
|
|
7862
|
+
return allResults.sort((a, b) => b.installs - a.installs).slice(0, limit);
|
|
7863
|
+
}
|
|
7864
|
+
function collectSkillPaths(cwd, skillName, paths) {
|
|
7865
|
+
for (const dir of [".claude/skills", ".agents/skills"]) {
|
|
7866
|
+
const skillPath = path7.join(dir, skillName);
|
|
7867
|
+
if (fs8.existsSync(path7.join(cwd, skillPath))) {
|
|
7868
|
+
paths.push(skillPath);
|
|
7869
|
+
}
|
|
7870
|
+
}
|
|
7871
|
+
}
|
|
7760
7872
|
function bootstrapCommand(opts, pkgRoot) {
|
|
7761
7873
|
const cwd = process.cwd();
|
|
7762
7874
|
setConfigDir(cwd);
|
|
@@ -8213,44 +8325,66 @@ ${entries}
|
|
|
8213
8325
|
}
|
|
8214
8326
|
console.log("\n\u2500\u2500 Skills \u2500\u2500");
|
|
8215
8327
|
const installedSkillPaths = [];
|
|
8216
|
-
const
|
|
8217
|
-
const
|
|
8328
|
+
const excludeSkills = /* @__PURE__ */ new Set();
|
|
8329
|
+
const claudeSkillsDir = path7.join(cwd, ".claude", "skills");
|
|
8330
|
+
if (fs8.existsSync(claudeSkillsDir)) {
|
|
8331
|
+
try {
|
|
8332
|
+
for (const entry of fs8.readdirSync(claudeSkillsDir, { withFileTypes: true })) {
|
|
8333
|
+
if (entry.isDirectory() || entry.isSymbolicLink()) excludeSkills.add(entry.name);
|
|
8334
|
+
}
|
|
8335
|
+
} catch {
|
|
8336
|
+
}
|
|
8337
|
+
}
|
|
8218
8338
|
for (const tool of detectToolsForBootstrap(cwd)) {
|
|
8219
|
-
if (tool.skill
|
|
8220
|
-
|
|
8221
|
-
|
|
8339
|
+
if (tool.skill) {
|
|
8340
|
+
const toolSkillName = tool.skill.split("@").pop() ?? "";
|
|
8341
|
+
excludeSkills.add(toolSkillName);
|
|
8222
8342
|
}
|
|
8223
8343
|
}
|
|
8224
|
-
for (const
|
|
8225
|
-
if (!
|
|
8226
|
-
|
|
8227
|
-
|
|
8344
|
+
for (const tool of detectToolsForBootstrap(cwd)) {
|
|
8345
|
+
if (!tool.skill) continue;
|
|
8346
|
+
const skillName = tool.skill.split("@").pop() ?? "";
|
|
8347
|
+
if (excludeSkills.has(skillName) && fs8.existsSync(path7.join(claudeSkillsDir, skillName))) continue;
|
|
8348
|
+
try {
|
|
8349
|
+
console.log(` Installing: ${tool.name} CLI (${tool.skill})`);
|
|
8350
|
+
execFileSync4("npx", ["skills", "add", tool.skill, "--yes"], {
|
|
8351
|
+
cwd,
|
|
8352
|
+
encoding: "utf-8",
|
|
8353
|
+
timeout: 6e4,
|
|
8354
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
8355
|
+
});
|
|
8356
|
+
collectSkillPaths(cwd, skillName, installedSkillPaths);
|
|
8357
|
+
excludeSkills.add(skillName);
|
|
8358
|
+
console.log(` \u2713 ${tool.name} CLI`);
|
|
8359
|
+
} catch {
|
|
8360
|
+
console.log(` \u2717 ${tool.name} CLI \u2014 failed to install`);
|
|
8228
8361
|
}
|
|
8229
8362
|
}
|
|
8230
|
-
|
|
8231
|
-
|
|
8232
|
-
|
|
8233
|
-
|
|
8234
|
-
|
|
8235
|
-
|
|
8236
|
-
|
|
8237
|
-
|
|
8238
|
-
|
|
8239
|
-
|
|
8240
|
-
|
|
8241
|
-
|
|
8242
|
-
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
}
|
|
8363
|
+
const keywords = detectProjectKeywords(cwd);
|
|
8364
|
+
if (keywords.length > 0) {
|
|
8365
|
+
console.log(` Searching skills.sh for: ${keywords.join(", ")}`);
|
|
8366
|
+
const found = searchSkills(keywords, excludeSkills, 5);
|
|
8367
|
+
if (found.length > 0) {
|
|
8368
|
+
for (const skill of found) {
|
|
8369
|
+
try {
|
|
8370
|
+
console.log(` Installing: ${skill.name} (${skill.ref})`);
|
|
8371
|
+
execFileSync4("npx", ["skills", "add", skill.ref, "--yes"], {
|
|
8372
|
+
cwd,
|
|
8373
|
+
encoding: "utf-8",
|
|
8374
|
+
timeout: 6e4,
|
|
8375
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
8376
|
+
});
|
|
8377
|
+
collectSkillPaths(cwd, skill.name, installedSkillPaths);
|
|
8378
|
+
console.log(` \u2713 ${skill.name}`);
|
|
8379
|
+
} catch {
|
|
8380
|
+
console.log(` \u2717 ${skill.name} \u2014 failed to install`);
|
|
8246
8381
|
}
|
|
8247
|
-
console.log(` \u2713 ${label}`);
|
|
8248
|
-
} catch {
|
|
8249
|
-
console.log(` \u2717 ${label} \u2014 failed to install`);
|
|
8250
8382
|
}
|
|
8383
|
+
} else {
|
|
8384
|
+
console.log(" \u25CB No matching skills found on skills.sh");
|
|
8251
8385
|
}
|
|
8252
8386
|
} else {
|
|
8253
|
-
console.log(" \u25CB No
|
|
8387
|
+
console.log(" \u25CB No frameworks detected \u2014 skipping skill search");
|
|
8254
8388
|
}
|
|
8255
8389
|
if (fs8.existsSync(path7.join(cwd, "skills-lock.json"))) {
|
|
8256
8390
|
installedSkillPaths.push("skills-lock.json");
|