@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 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.mcp?.devServer;
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
- if (stageName === "build" || stageName === "review-fix") {
5015
- return `## Browser Visual Verification (MANDATORY for UI tasks)
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. Use \`browser_navigate\` to go to the affected page(s)
5037
- 3. Use \`browser_snapshot\` to capture the page and verify elements are present
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 with another snapshot
5039
- 5. If the task mentions responsive behavior, use \`browser_resize\` to test at different widths (e.g., 1200px, 768px, 480px) and take snapshots at each
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
- ### Available Browser Tools
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. Use \`browser_navigate\` to go to the affected page(s)
5063
- 3. Use \`browser_snapshot\` to verify elements, layout, and text content
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, use \`browser_resize\` to test at different widths
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 Playwright MCP browser tools for visual verification.
5099
+ You have access to browser tools for visual verification.
5074
5100
  ${devServerBlock}
5075
5101
 
5076
- Use browser tools to navigate to pages and take snapshots to verify UI output.`;
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
- if (isMcpEnabledForStage(stageName, config.mcp) && taskHasUI(taskDir)) {
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 ?? 30;
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.mcp?.devServer;
5323
- if (mcpConfigJson && ds && taskHasUI(ctx.taskDir)) {
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 ?? 30,
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 detectMcpConfig(cwd, pm, pkg) {
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
- const hasDevScript = !!scripts.dev;
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
 
@@ -241,7 +241,7 @@
241
241
  },
242
242
  "devServer": {
243
243
  "type": "object",
244
- "description": "Dev server configuration for browser tool verification",
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine-lite",
3
- "version": "0.1.141",
3
+ "version": "0.1.143",
4
4
  "description": "Autonomous SDLC pipeline: Kody orchestration + Claude Code + LiteLLM",
5
5
  "license": "MIT",
6
6
  "type": "module",