@kody-ade/kody-engine-lite 0.1.141 → 0.1.143
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 +98 -42
- package/kody.config.schema.json +29 -1
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -252,7 +252,9 @@ function getProjectConfig() {
|
|
|
252
252
|
...raw.mcp,
|
|
253
253
|
// Auto-enable when devServer is configured (user can still set enabled: false to override)
|
|
254
254
|
enabled: raw.mcp.enabled ?? !!raw.mcp.devServer
|
|
255
|
-
} : void 0
|
|
255
|
+
} : void 0,
|
|
256
|
+
// Top-level devServer takes precedence; fall back to mcp.devServer for backward compat
|
|
257
|
+
devServer: raw.devServer ?? raw.mcp?.devServer ?? void 0
|
|
256
258
|
};
|
|
257
259
|
} catch {
|
|
258
260
|
logger.warn("kody.config.json is invalid JSON \u2014 using defaults");
|
|
@@ -4943,7 +4945,7 @@ function taskHasUI(taskDir) {
|
|
|
4943
4945
|
}
|
|
4944
4946
|
function getDevServerInfo(taskDir) {
|
|
4945
4947
|
const config = getProjectConfig();
|
|
4946
|
-
const ds = config.
|
|
4948
|
+
const ds = config.devServer;
|
|
4947
4949
|
if (!ds) return void 0;
|
|
4948
4950
|
return {
|
|
4949
4951
|
command: ds.command,
|
|
@@ -5011,13 +5013,9 @@ done
|
|
|
5011
5013
|
\`\`\`
|
|
5012
5014
|
If the dev server fails to start (e.g. DB connection issues), skip browser verification and proceed with code-only changes. Do NOT hang waiting for it.
|
|
5013
5015
|
After you are done browsing, kill the dev server: \`kill $DEV_PID 2>/dev/null || true\``;
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
This task involves UI changes. You MUST visually verify your implementation using the browser tools.
|
|
5018
|
-
${devServerBlock}
|
|
5019
|
-
|
|
5020
|
-
### Available Browser Tools
|
|
5016
|
+
const config = getProjectConfig();
|
|
5017
|
+
const hasMcpPlaywright = isMcpEnabledForStage(stageName, config.mcp);
|
|
5018
|
+
const mcpTools = `### Available Browser Tools
|
|
5021
5019
|
- \`mcp__playwright__browser_navigate\` \u2014 go to a URL
|
|
5022
5020
|
- \`mcp__playwright__browser_snapshot\` \u2014 capture accessibility tree (shows all elements, text, roles)
|
|
5023
5021
|
- \`mcp__playwright__browser_take_screenshot\` \u2014 take a visual screenshot
|
|
@@ -5029,14 +5027,50 @@ ${devServerBlock}
|
|
|
5029
5027
|
- \`mcp__playwright__browser_press_key\` \u2014 press keyboard keys (Enter, Escape, Tab, etc.)
|
|
5030
5028
|
- \`mcp__playwright__browser_resize\` \u2014 resize viewport (test responsive layouts)
|
|
5031
5029
|
- \`mcp__playwright__browser_wait_for\` \u2014 wait for text to appear/disappear
|
|
5032
|
-
- \`mcp__playwright__browser_evaluate\` \u2014 run JavaScript on the page
|
|
5030
|
+
- \`mcp__playwright__browser_evaluate\` \u2014 run JavaScript on the page`;
|
|
5031
|
+
const cliTools = `### Browser Verification via Playwright CLI
|
|
5032
|
+
Use the \`playwright-cli\` commands in your bash tool to interact with the browser:
|
|
5033
|
+
\`\`\`bash
|
|
5034
|
+
# Take a screenshot of a page
|
|
5035
|
+
playwright-cli screenshot ${serverUrl ?? "http://localhost:3000"} --output /tmp/screenshot.png
|
|
5036
|
+
|
|
5037
|
+
# Navigate and interact
|
|
5038
|
+
playwright-cli open ${serverUrl ?? "http://localhost:3000"}
|
|
5039
|
+
|
|
5040
|
+
# Run a quick verification script
|
|
5041
|
+
npx playwright test --grep "homepage" --reporter=list
|
|
5042
|
+
\`\`\`
|
|
5043
|
+
|
|
5044
|
+
Alternatively, write and run a short Playwright script to verify the UI:
|
|
5045
|
+
\`\`\`bash
|
|
5046
|
+
node -e "
|
|
5047
|
+
const { chromium } = require('playwright');
|
|
5048
|
+
(async () => {
|
|
5049
|
+
const browser = await chromium.launch();
|
|
5050
|
+
const page = await browser.newPage();
|
|
5051
|
+
await page.goto('${serverUrl ?? "http://localhost:3000"}');
|
|
5052
|
+
await page.screenshot({ path: '/tmp/verify.png', fullPage: true });
|
|
5053
|
+
console.log('Title:', await page.title());
|
|
5054
|
+
await browser.close();
|
|
5055
|
+
})();
|
|
5056
|
+
"
|
|
5057
|
+
\`\`\`
|
|
5058
|
+
Use the screenshot output and page title to verify the UI is rendering correctly.`;
|
|
5059
|
+
const toolsBlock = hasMcpPlaywright ? mcpTools : cliTools;
|
|
5060
|
+
if (stageName === "build" || stageName === "review-fix") {
|
|
5061
|
+
return `## Browser Visual Verification (MANDATORY for UI tasks)
|
|
5062
|
+
|
|
5063
|
+
This task involves UI changes. You MUST visually verify your implementation using the browser tools.
|
|
5064
|
+
${devServerBlock}
|
|
5065
|
+
|
|
5066
|
+
${toolsBlock}
|
|
5033
5067
|
|
|
5034
5068
|
### Verification Steps (DO ALL OF THESE)
|
|
5035
5069
|
1. Start the dev server (see above)
|
|
5036
|
-
2.
|
|
5037
|
-
3.
|
|
5038
|
-
4. **Test interactions**: if the task involves buttons, forms, search, toggles, or any interactive elements \u2014 click them, type into them, and verify the result
|
|
5039
|
-
5. If the task mentions responsive behavior,
|
|
5070
|
+
2. Navigate to the affected page(s)
|
|
5071
|
+
3. Take a screenshot or snapshot to verify elements are present
|
|
5072
|
+
4. **Test interactions**: if the task involves buttons, forms, search, toggles, or any interactive elements \u2014 click them, type into them, and verify the result
|
|
5073
|
+
5. If the task mentions responsive behavior, test at different viewport widths (e.g., 1200px, 768px, 480px)
|
|
5040
5074
|
6. Kill the dev server when done
|
|
5041
5075
|
|
|
5042
5076
|
Do NOT skip the browser verification. The visual check AND interaction testing are required parts of implementing UI changes.`;
|
|
@@ -5047,22 +5081,14 @@ Do NOT skip the browser verification. The visual check AND interaction testing a
|
|
|
5047
5081
|
This task involves UI changes. You MUST visually verify the implementation using the browser tools before giving your verdict.
|
|
5048
5082
|
${devServerBlock}
|
|
5049
5083
|
|
|
5050
|
-
|
|
5051
|
-
- \`mcp__playwright__browser_navigate\` \u2014 go to a URL
|
|
5052
|
-
- \`mcp__playwright__browser_snapshot\` \u2014 capture accessibility tree (shows all elements, text, roles)
|
|
5053
|
-
- \`mcp__playwright__browser_take_screenshot\` \u2014 take a visual screenshot
|
|
5054
|
-
- \`mcp__playwright__browser_click\` \u2014 click an element
|
|
5055
|
-
- \`mcp__playwright__browser_type\` \u2014 type text into an input
|
|
5056
|
-
- \`mcp__playwright__browser_hover\` \u2014 hover over an element
|
|
5057
|
-
- \`mcp__playwright__browser_resize\` \u2014 resize viewport
|
|
5058
|
-
- \`mcp__playwright__browser_wait_for\` \u2014 wait for text to appear/disappear
|
|
5084
|
+
${toolsBlock}
|
|
5059
5085
|
|
|
5060
5086
|
### Review Verification Steps (DO ALL OF THESE)
|
|
5061
5087
|
1. Start the dev server (see above)
|
|
5062
|
-
2.
|
|
5063
|
-
3.
|
|
5088
|
+
2. Navigate to the affected page(s)
|
|
5089
|
+
3. Take a screenshot or snapshot to verify elements, layout, and text content
|
|
5064
5090
|
4. **Test interactions**: click buttons, fill forms, test search \u2014 verify the UI responds correctly
|
|
5065
|
-
5. If the task mentions responsive behavior,
|
|
5091
|
+
5. If the task mentions responsive behavior, test at different viewport widths
|
|
5066
5092
|
6. Include your browser verification findings in the review (what you saw, what you interacted with, what worked/failed)
|
|
5067
5093
|
7. Kill the dev server when done
|
|
5068
5094
|
|
|
@@ -5070,10 +5096,12 @@ Do NOT skip the browser verification. A review of UI changes without visual AND
|
|
|
5070
5096
|
}
|
|
5071
5097
|
return `## Browser Tools Available
|
|
5072
5098
|
|
|
5073
|
-
You have access to
|
|
5099
|
+
You have access to browser tools for visual verification.
|
|
5074
5100
|
${devServerBlock}
|
|
5075
5101
|
|
|
5076
|
-
|
|
5102
|
+
${toolsBlock}
|
|
5103
|
+
|
|
5104
|
+
Use browser tools to navigate to pages and take screenshots to verify UI output.`;
|
|
5077
5105
|
}
|
|
5078
5106
|
function buildFullPrompt(stageName, taskId, taskDir, projectDir, feedback) {
|
|
5079
5107
|
const config = getProjectConfig();
|
|
@@ -5089,7 +5117,9 @@ function buildFullPrompt(stageName, taskId, taskDir, projectDir, feedback) {
|
|
|
5089
5117
|
|
|
5090
5118
|
${prompt}` : prompt;
|
|
5091
5119
|
}
|
|
5092
|
-
|
|
5120
|
+
const browserStages = ["build", "review", "review-fix"];
|
|
5121
|
+
const hasBrowserTools = isMcpEnabledForStage(stageName, config.mcp) || config.devServer && browserStages.includes(stageName);
|
|
5122
|
+
if (hasBrowserTools && taskHasUI(taskDir)) {
|
|
5093
5123
|
assembled = assembled + "\n\n" + getBrowserToolGuidance(stageName, taskDir);
|
|
5094
5124
|
const qaGuidePath = path21.join(projectDir, ".kody", "qa-guide.md");
|
|
5095
5125
|
if (fs23.existsSync(qaGuidePath)) {
|
|
@@ -5198,8 +5228,23 @@ async function pollReady(url, timeoutSec) {
|
|
|
5198
5228
|
}
|
|
5199
5229
|
return false;
|
|
5200
5230
|
}
|
|
5231
|
+
async function waitForReady(url, timeoutSec, stdoutMatch) {
|
|
5232
|
+
const deadline = Date.now() + timeoutSec * 1e3;
|
|
5233
|
+
while (Date.now() < deadline) {
|
|
5234
|
+
if (stdoutMatch()) {
|
|
5235
|
+
logger.info(" Dev server stdout matched ready pattern");
|
|
5236
|
+
const httpTimeout = Math.min(15, Math.max(1, Math.floor((deadline - Date.now()) / 1e3)));
|
|
5237
|
+
return pollReady(url, httpTimeout);
|
|
5238
|
+
}
|
|
5239
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
5240
|
+
}
|
|
5241
|
+
return false;
|
|
5242
|
+
}
|
|
5201
5243
|
async function startDevServer(opts) {
|
|
5202
|
-
const timeout = opts.readyTimeout ??
|
|
5244
|
+
const timeout = opts.readyTimeout ?? 60;
|
|
5245
|
+
const patternStr = opts.readyPattern ?? DEFAULT_READY_PATTERN;
|
|
5246
|
+
const pattern = new RegExp(patternStr, "i");
|
|
5247
|
+
const useStdoutDetection = Boolean(opts.readyPattern);
|
|
5203
5248
|
const [cmd, ...args2] = opts.command.split(/\s+/);
|
|
5204
5249
|
let child;
|
|
5205
5250
|
try {
|
|
@@ -5216,8 +5261,10 @@ async function startDevServer(opts) {
|
|
|
5216
5261
|
}
|
|
5217
5262
|
let stdout = "";
|
|
5218
5263
|
let stderr = "";
|
|
5264
|
+
let stdoutMatched = false;
|
|
5219
5265
|
child.stdout?.on("data", (chunk) => {
|
|
5220
5266
|
stdout += chunk.toString();
|
|
5267
|
+
if (!stdoutMatched && pattern.test(stdout)) stdoutMatched = true;
|
|
5221
5268
|
});
|
|
5222
5269
|
child.stderr?.on("data", (chunk) => {
|
|
5223
5270
|
stderr += chunk.toString();
|
|
@@ -5227,7 +5274,7 @@ async function startDevServer(opts) {
|
|
|
5227
5274
|
exited = true;
|
|
5228
5275
|
});
|
|
5229
5276
|
child.unref();
|
|
5230
|
-
const ready = await pollReady(opts.url, timeout);
|
|
5277
|
+
const ready = useStdoutDetection ? await waitForReady(opts.url, timeout, () => stdoutMatched) : await pollReady(opts.url, timeout);
|
|
5231
5278
|
if (!ready) {
|
|
5232
5279
|
if (exited) {
|
|
5233
5280
|
logger.warn(` Dev server exited before becoming ready`);
|
|
@@ -5254,10 +5301,12 @@ async function startDevServer(opts) {
|
|
|
5254
5301
|
};
|
|
5255
5302
|
return { ready, url: opts.url, pid, stop };
|
|
5256
5303
|
}
|
|
5304
|
+
var DEFAULT_READY_PATTERN;
|
|
5257
5305
|
var init_dev_server = __esm({
|
|
5258
5306
|
"src/dev-server.ts"() {
|
|
5259
5307
|
"use strict";
|
|
5260
5308
|
init_logger();
|
|
5309
|
+
DEFAULT_READY_PATTERN = "Ready in|compiled|started server|Local:|localhost:";
|
|
5261
5310
|
}
|
|
5262
5311
|
});
|
|
5263
5312
|
|
|
@@ -5318,9 +5367,10 @@ async function executeAgentStage(ctx, def) {
|
|
|
5318
5367
|
if (mcpConfigJson) {
|
|
5319
5368
|
logger.info(` MCP servers enabled for ${def.name}`);
|
|
5320
5369
|
}
|
|
5370
|
+
const devServerStages = ["build", "review", "review-fix"];
|
|
5321
5371
|
let devServerHandle = null;
|
|
5322
|
-
const ds = config.
|
|
5323
|
-
if (
|
|
5372
|
+
const ds = config.devServer;
|
|
5373
|
+
if (ds && devServerStages.includes(def.name) && taskHasUI(ctx.taskDir)) {
|
|
5324
5374
|
logger.info(` Starting dev server: ${ds.command}`);
|
|
5325
5375
|
const envVars = {};
|
|
5326
5376
|
for (const varName of ds.env ?? []) {
|
|
@@ -5329,7 +5379,8 @@ async function executeAgentStage(ctx, def) {
|
|
|
5329
5379
|
devServerHandle = await startDevServer({
|
|
5330
5380
|
command: ds.command,
|
|
5331
5381
|
url: ds.url,
|
|
5332
|
-
readyTimeout: ds.readyTimeout ??
|
|
5382
|
+
readyTimeout: ds.readyTimeout ?? 60,
|
|
5383
|
+
readyPattern: ds.readyPattern ?? "Ready in|compiled|started server|Local:|localhost:",
|
|
5333
5384
|
envVars
|
|
5334
5385
|
});
|
|
5335
5386
|
if (devServerHandle.ready) {
|
|
@@ -8242,28 +8293,33 @@ function buildConfig(cwd, basic) {
|
|
|
8242
8293
|
};
|
|
8243
8294
|
const mcp = detectMcpConfig(cwd, basic.pm, pkg);
|
|
8244
8295
|
if (mcp) config.mcp = mcp;
|
|
8296
|
+
const devServer = detectDevServer(basic.pm, pkg);
|
|
8297
|
+
if (devServer) config.devServer = devServer;
|
|
8245
8298
|
return config;
|
|
8246
8299
|
}
|
|
8247
|
-
function
|
|
8300
|
+
function detectDevServer(pm, pkg) {
|
|
8248
8301
|
const allDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
8249
8302
|
const hasFrontend = FRONTEND_DEPS.some((dep) => dep in allDeps);
|
|
8250
8303
|
if (!hasFrontend) return void 0;
|
|
8251
8304
|
const scripts = pkg.scripts ?? {};
|
|
8252
|
-
|
|
8305
|
+
if (!scripts.dev) return void 0;
|
|
8253
8306
|
const isNext = "next" in allDeps || "nuxt" in allDeps;
|
|
8254
8307
|
const isVite = "vite" in allDeps;
|
|
8255
8308
|
const defaultPort = isNext ? 3e3 : isVite ? 5173 : 3e3;
|
|
8309
|
+
return {
|
|
8310
|
+
command: `${pm} dev`,
|
|
8311
|
+
url: `http://localhost:${defaultPort}`
|
|
8312
|
+
};
|
|
8313
|
+
}
|
|
8314
|
+
function detectMcpConfig(cwd, pm, pkg) {
|
|
8315
|
+
const allDeps = { ...pkg.dependencies ?? {}, ...pkg.devDependencies ?? {} };
|
|
8316
|
+
const hasFrontend = FRONTEND_DEPS.some((dep) => dep in allDeps);
|
|
8317
|
+
if (!hasFrontend) return void 0;
|
|
8256
8318
|
const mcp = {
|
|
8257
8319
|
enabled: true,
|
|
8258
8320
|
servers: {},
|
|
8259
8321
|
stages: ["build", "review"]
|
|
8260
8322
|
};
|
|
8261
|
-
if (hasDevScript) {
|
|
8262
|
-
mcp.devServer = {
|
|
8263
|
-
command: `${pm} dev`,
|
|
8264
|
-
url: `http://localhost:${defaultPort}`
|
|
8265
|
-
};
|
|
8266
|
-
}
|
|
8267
8323
|
return mcp;
|
|
8268
8324
|
}
|
|
8269
8325
|
|
package/kody.config.schema.json
CHANGED
|
@@ -241,7 +241,7 @@
|
|
|
241
241
|
},
|
|
242
242
|
"devServer": {
|
|
243
243
|
"type": "object",
|
|
244
|
-
"description": "
|
|
244
|
+
"description": "DEPRECATED: Use top-level devServer instead. Kept for backward compatibility.",
|
|
245
245
|
"properties": {
|
|
246
246
|
"command": {
|
|
247
247
|
"type": "string",
|
|
@@ -270,6 +270,34 @@
|
|
|
270
270
|
},
|
|
271
271
|
"required": ["enabled", "servers"],
|
|
272
272
|
"additionalProperties": false
|
|
273
|
+
},
|
|
274
|
+
"devServer": {
|
|
275
|
+
"type": "object",
|
|
276
|
+
"description": "Dev server configuration for browser tool verification. Works with any provider (MCP or CLI-based).",
|
|
277
|
+
"properties": {
|
|
278
|
+
"command": {
|
|
279
|
+
"type": "string",
|
|
280
|
+
"description": "Command to start the dev server (e.g., 'pnpm dev')"
|
|
281
|
+
},
|
|
282
|
+
"url": {
|
|
283
|
+
"type": "string",
|
|
284
|
+
"description": "URL where the dev server will be accessible (e.g., 'http://localhost:3000')"
|
|
285
|
+
},
|
|
286
|
+
"readyPattern": {
|
|
287
|
+
"type": "string",
|
|
288
|
+
"description": "Regex pattern to match in stdout when server is ready. Default: 'Ready in|compiled|started server|Local:'"
|
|
289
|
+
},
|
|
290
|
+
"readyTimeout": {
|
|
291
|
+
"type": "number",
|
|
292
|
+
"description": "Seconds to wait for the server to be ready. Default: 30"
|
|
293
|
+
},
|
|
294
|
+
"env": {
|
|
295
|
+
"type": "array",
|
|
296
|
+
"items": { "type": "string" },
|
|
297
|
+
"description": "List of GitHub secret names to forward as environment variables for the dev server process (e.g., ['BLOB_READ_WRITE_TOKEN', 'DATABASE_URL']). These are injected into the workflow env block during 'kody init'."
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
"required": ["command", "url"]
|
|
273
301
|
}
|
|
274
302
|
},
|
|
275
303
|
"additionalProperties": false
|