@kody-ade/kody-engine-lite 0.1.103 → 0.1.105

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.
Files changed (74) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/cli.js +12 -228
  3. package/package.json +1 -1
  4. package/prompts/review-fix.md +8 -12
  5. package/prompts/taskify.md +2 -19
  6. package/templates/kody.yml +17 -13
  7. package/dist/agent-runner.d.ts +0 -4
  8. package/dist/agent-runner.js +0 -122
  9. package/dist/ci/parse-inputs.d.ts +0 -6
  10. package/dist/ci/parse-inputs.js +0 -76
  11. package/dist/ci/parse-safety.d.ts +0 -6
  12. package/dist/ci/parse-safety.js +0 -22
  13. package/dist/cli/args.d.ts +0 -13
  14. package/dist/cli/args.js +0 -42
  15. package/dist/cli/litellm.d.ts +0 -2
  16. package/dist/cli/litellm.js +0 -85
  17. package/dist/cli/task-resolution.d.ts +0 -2
  18. package/dist/cli/task-resolution.js +0 -41
  19. package/dist/config.d.ts +0 -49
  20. package/dist/config.js +0 -72
  21. package/dist/context.d.ts +0 -4
  22. package/dist/context.js +0 -83
  23. package/dist/definitions.d.ts +0 -3
  24. package/dist/definitions.js +0 -59
  25. package/dist/entry.d.ts +0 -1
  26. package/dist/entry.js +0 -236
  27. package/dist/git-utils.d.ts +0 -13
  28. package/dist/git-utils.js +0 -174
  29. package/dist/github-api.d.ts +0 -14
  30. package/dist/github-api.js +0 -114
  31. package/dist/kody-utils.d.ts +0 -1
  32. package/dist/kody-utils.js +0 -9
  33. package/dist/learning/auto-learn.d.ts +0 -2
  34. package/dist/learning/auto-learn.js +0 -169
  35. package/dist/logger.d.ts +0 -14
  36. package/dist/logger.js +0 -51
  37. package/dist/memory.d.ts +0 -1
  38. package/dist/memory.js +0 -20
  39. package/dist/observer.d.ts +0 -9
  40. package/dist/observer.js +0 -80
  41. package/dist/pipeline/complexity.d.ts +0 -3
  42. package/dist/pipeline/complexity.js +0 -12
  43. package/dist/pipeline/executor-registry.d.ts +0 -3
  44. package/dist/pipeline/executor-registry.js +0 -20
  45. package/dist/pipeline/hooks.d.ts +0 -17
  46. package/dist/pipeline/hooks.js +0 -110
  47. package/dist/pipeline/questions.d.ts +0 -2
  48. package/dist/pipeline/questions.js +0 -44
  49. package/dist/pipeline/runner-selection.d.ts +0 -2
  50. package/dist/pipeline/runner-selection.js +0 -13
  51. package/dist/pipeline/state.d.ts +0 -4
  52. package/dist/pipeline/state.js +0 -37
  53. package/dist/pipeline.d.ts +0 -3
  54. package/dist/pipeline.js +0 -213
  55. package/dist/preflight.d.ts +0 -1
  56. package/dist/preflight.js +0 -69
  57. package/dist/retrospective.d.ts +0 -26
  58. package/dist/retrospective.js +0 -211
  59. package/dist/stages/agent.d.ts +0 -2
  60. package/dist/stages/agent.js +0 -94
  61. package/dist/stages/gate.d.ts +0 -2
  62. package/dist/stages/gate.js +0 -32
  63. package/dist/stages/review.d.ts +0 -2
  64. package/dist/stages/review.js +0 -32
  65. package/dist/stages/ship.d.ts +0 -3
  66. package/dist/stages/ship.js +0 -154
  67. package/dist/stages/verify.d.ts +0 -2
  68. package/dist/stages/verify.js +0 -94
  69. package/dist/types.d.ts +0 -61
  70. package/dist/types.js +0 -1
  71. package/dist/validators.d.ts +0 -8
  72. package/dist/validators.js +0 -42
  73. package/dist/verify-runner.d.ts +0 -11
  74. package/dist/verify-runner.js +0 -110
package/README.md CHANGED
@@ -154,7 +154,7 @@ kody-engine-lite rerun --issue-number 42 --from verify
154
154
 
155
155
  ## Documentation
156
156
 
157
- **Understand Kody:** [About](docs/ABOUT.md) · [Tech Stack](docs/TECH-STACK.md) · [Features](docs/FEATURES.md) · [Pipeline](docs/PIPELINE.md) · [Comparison](docs/COMPARISON.md)
157
+ **Understand Kody:** [About](docs/ABOUT.md) · [Features](docs/FEATURES.md) · [Pipeline](docs/PIPELINE.md) · [Comparison](docs/COMPARISON.md)
158
158
 
159
159
  **Set up & use:** [CLI](docs/CLI.md) · [Configuration](docs/CONFIGURATION.md) · [Bootstrap](docs/BOOTSTRAP.md) · [LiteLLM](docs/LITELLM.md)
160
160
 
package/dist/bin/cli.js CHANGED
@@ -305,13 +305,7 @@ function getProjectConfig() {
305
305
  },
306
306
  timeouts: raw.timeouts ?? void 0,
307
307
  contextTiers: raw.contextTiers ? { ...DEFAULT_CONFIG.contextTiers, ...raw.contextTiers } : DEFAULT_CONFIG.contextTiers,
308
- mcp: raw.mcp ? {
309
- servers: {},
310
- stages: ["build", "verify", "review", "review-fix"],
311
- ...raw.mcp,
312
- // Auto-enable when devServer is configured (user can still set enabled: false to override)
313
- enabled: raw.mcp.enabled ?? !!raw.mcp.devServer
314
- } : void 0
308
+ mcp: raw.mcp ? { enabled: false, servers: {}, stages: ["build", "verify", "review", "review-fix"], ...raw.mcp } : void 0
315
309
  };
316
310
  } catch {
317
311
  logger.warn("kody.config.json is invalid JSON \u2014 using defaults");
@@ -867,7 +861,7 @@ var init_github_api = __esm({
867
861
  "use strict";
868
862
  init_logger();
869
863
  API_TIMEOUT_MS = 3e4;
870
- LIFECYCLE_LABELS = ["planning", "building", "review", "shipping", "done", "failed", "waiting", "low", "medium", "high"];
864
+ LIFECYCLE_LABELS = ["planning", "building", "review", "done", "failed", "waiting", "low", "medium", "high"];
871
865
  KODY_MARKERS = [
872
866
  "Kody Review",
873
867
  "\u{1F916} Generated by Kody",
@@ -1287,20 +1281,6 @@ var init_context_tiers = __esm({
1287
1281
  });
1288
1282
 
1289
1283
  // src/mcp-config.ts
1290
- function withPlaywrightIfNeeded(mcpConfig, hasUI) {
1291
- if (!mcpConfig?.enabled || !hasUI) return mcpConfig;
1292
- const hasPlaywright = Object.keys(mcpConfig.servers).some(
1293
- (name) => name.toLowerCase().includes("playwright")
1294
- );
1295
- if (hasPlaywright) return mcpConfig;
1296
- return {
1297
- ...mcpConfig,
1298
- servers: {
1299
- ...mcpConfig.servers,
1300
- playwright: PLAYWRIGHT_SERVER
1301
- }
1302
- };
1303
- }
1304
1284
  function buildMcpConfigJson(mcpConfig) {
1305
1285
  if (!mcpConfig?.enabled) return void 0;
1306
1286
  if (Object.keys(mcpConfig.servers).length === 0) return void 0;
@@ -1317,18 +1297,15 @@ function buildMcpConfigJson(mcpConfig) {
1317
1297
  }
1318
1298
  function isMcpEnabledForStage(stageName, mcpConfig) {
1319
1299
  if (!mcpConfig?.enabled) return false;
1300
+ if (Object.keys(mcpConfig.servers).length === 0) return false;
1320
1301
  const allowedStages = mcpConfig.stages ?? DEFAULT_MCP_STAGES;
1321
1302
  return allowedStages.includes(stageName);
1322
1303
  }
1323
- var DEFAULT_MCP_STAGES, PLAYWRIGHT_SERVER;
1304
+ var DEFAULT_MCP_STAGES;
1324
1305
  var init_mcp_config = __esm({
1325
1306
  "src/mcp-config.ts"() {
1326
1307
  "use strict";
1327
1308
  DEFAULT_MCP_STAGES = ["build", "verify", "review", "review-fix"];
1328
- PLAYWRIGHT_SERVER = {
1329
- command: "npx",
1330
- args: ["-y", "@anthropic-ai/mcp-playwright"]
1331
- };
1332
1309
  }
1333
1310
  });
1334
1311
 
@@ -1546,11 +1523,6 @@ ${prompt}` : prompt;
1546
1523
  }
1547
1524
  if (isMcpEnabledForStage(stageName, config.mcp) && taskHasUI(taskDir)) {
1548
1525
  assembled = assembled + "\n\n" + getBrowserToolGuidance(stageName, taskDir);
1549
- const qaGuidePath = path5.join(projectDir, ".kody", "qa-guide.md");
1550
- if (fs5.existsSync(qaGuidePath)) {
1551
- const qaGuide = fs5.readFileSync(qaGuidePath, "utf-8").trim();
1552
- assembled = assembled + "\n\n" + qaGuide;
1553
- }
1554
1526
  }
1555
1527
  return assembled;
1556
1528
  }
@@ -1713,8 +1685,7 @@ async function executeAgentStage(ctx, def) {
1713
1685
  if (sessionInfo) {
1714
1686
  logger.info(` session: ${SESSION_GROUP[def.name]} (${sessionInfo.resumeSession ? "resume" : "new"})`);
1715
1687
  }
1716
- const mcpForStage = isMcpEnabledForStage(def.name, config.mcp) ? withPlaywrightIfNeeded(config.mcp, taskHasUI(ctx.taskDir)) : void 0;
1717
- const mcpConfigJson = buildMcpConfigJson(mcpForStage);
1688
+ const mcpConfigJson = isMcpEnabledForStage(def.name, config.mcp) ? buildMcpConfigJson(config.mcp) : void 0;
1718
1689
  if (mcpConfigJson) {
1719
1690
  logger.info(` MCP servers enabled for ${def.name}`);
1720
1691
  }
@@ -1774,7 +1745,7 @@ async function executeAgentStage(ctx, def) {
1774
1745
  description: plainText.slice(0, 500),
1775
1746
  scope: [],
1776
1747
  risk_level: "low",
1777
- hasUI: true,
1748
+ hasUI: false,
1778
1749
  questions: []
1779
1750
  }, null, 2);
1780
1751
  fs6.writeFileSync(outputPath, fallback);
@@ -2699,7 +2670,6 @@ function applyPreStageLabel(ctx, def) {
2699
2670
  if (!ctx.input.issueNumber || ctx.input.local) return;
2700
2671
  if (def.name === "build") setLifecycleLabel(ctx.input.issueNumber, "building");
2701
2672
  if (def.name === "review") setLifecycleLabel(ctx.input.issueNumber, "review");
2702
- if (def.name === "ship") setLifecycleLabel(ctx.input.issueNumber, "shipping");
2703
2673
  }
2704
2674
  function checkQuestionsAfterStage(ctx, def, state) {
2705
2675
  if (def.name !== "taskify" && def.name !== "plan") return null;
@@ -4537,7 +4507,12 @@ function detectMcpConfig(cwd, pm, pkg) {
4537
4507
  const defaultPort = isNext ? 3e3 : isVite ? 5173 : 3e3;
4538
4508
  const mcp = {
4539
4509
  enabled: true,
4540
- servers: {},
4510
+ servers: {
4511
+ playwright: {
4512
+ command: "npx",
4513
+ args: ["@playwright/mcp@latest"]
4514
+ }
4515
+ },
4541
4516
  stages: ["build", "review"]
4542
4517
  };
4543
4518
  if (hasDevScript) {
@@ -5034,23 +5009,6 @@ REMINDER: Output the full prompt template first (unchanged), then your three app
5034
5009
  }
5035
5010
  }
5036
5011
  console.log(` \u2713 Generated ${stepCount} step files in .kody/steps/`);
5037
- console.log("\n\u2500\u2500 QA Guide \u2500\u2500");
5038
- const qaGuidePath = path21.join(cwd, ".kody", "qa-guide.md");
5039
- if (!fs22.existsSync(qaGuidePath) || opts.force) {
5040
- const discovery = discoverQaContext(cwd);
5041
- if (discovery.routes.length > 0) {
5042
- const qaGuide = generateQaGuide(discovery);
5043
- fs22.writeFileSync(qaGuidePath, qaGuide);
5044
- console.log(` \u2713 .kody/qa-guide.md (${discovery.routes.length} routes, ${discovery.roles.length} roles)`);
5045
- if (discovery.loginPage) console.log(` \u2713 Login page detected: ${discovery.loginPage}`);
5046
- if (discovery.adminPath) console.log(` \u2713 Admin panel detected: ${discovery.adminPath}`);
5047
- console.log(" \u2139 Add QA_ADMIN_EMAIL, QA_ADMIN_PASSWORD, QA_USER_EMAIL, QA_USER_PASSWORD as GitHub secrets");
5048
- } else {
5049
- console.log(" \u25CB No routes detected \u2014 skipping QA guide");
5050
- }
5051
- } else {
5052
- console.log(" \u25CB .kody/qa-guide.md already exists (use --force to regenerate)");
5053
- }
5054
5012
  console.log("\n\u2500\u2500 Labels \u2500\u2500");
5055
5013
  try {
5056
5014
  let repoSlug = "";
@@ -5069,7 +5027,6 @@ REMINDER: Output the full prompt template first (unchanged), then your three app
5069
5027
  { name: "kody:planning", color: "c5def5", description: "Kody is analyzing and planning" },
5070
5028
  { name: "kody:building", color: "0e8a16", description: "Kody is building code" },
5071
5029
  { name: "kody:review", color: "fbca04", description: "Kody is reviewing code" },
5072
- { name: "kody:shipping", color: "1d76db", description: "Kody is creating the pull request" },
5073
5030
  { name: "kody:done", color: "0e8a16", description: "Kody completed successfully" },
5074
5031
  { name: "kody:failed", color: "d93f0b", description: "Kody pipeline failed" },
5075
5032
  { name: "kody:waiting", color: "fef2c0", description: "Kody is waiting for answers" },
@@ -5128,7 +5085,6 @@ REMINDER: Output the full prompt template first (unchanged), then your three app
5128
5085
  const filesToCommit = [
5129
5086
  ".kody/memory/architecture.md",
5130
5087
  ".kody/memory/conventions.md",
5131
- ".kody/qa-guide.md",
5132
5088
  ...installedSkillPaths
5133
5089
  ].filter((f) => fs22.existsSync(path21.join(cwd, f)));
5134
5090
  if (fs22.existsSync(path21.join(cwd, "skills-lock.json"))) {
@@ -5242,178 +5198,6 @@ Create it manually.`, cwd);
5242
5198
  console.log(" \u2713 Project bootstrap complete!");
5243
5199
  console.log(" Kody now has project-specific memory and customized step files.\n");
5244
5200
  }
5245
- function discoverQaContext(cwd) {
5246
- const result = {
5247
- routes: [],
5248
- authFiles: [],
5249
- loginPage: null,
5250
- adminPath: null,
5251
- roles: [],
5252
- devCommand: "",
5253
- devPort: 3e3
5254
- };
5255
- try {
5256
- const pkg = JSON.parse(fs22.readFileSync(path21.join(cwd, "package.json"), "utf-8"));
5257
- const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
5258
- const pm = fs22.existsSync(path21.join(cwd, "pnpm-lock.yaml")) ? "pnpm" : fs22.existsSync(path21.join(cwd, "yarn.lock")) ? "yarn" : "npm";
5259
- if (pkg.scripts?.dev) result.devCommand = `${pm} dev`;
5260
- if (allDeps.next || allDeps.nuxt) result.devPort = 3e3;
5261
- else if (allDeps.vite) result.devPort = 5173;
5262
- } catch {
5263
- }
5264
- const appDirs = ["src/app", "app"];
5265
- for (const appDir of appDirs) {
5266
- const fullAppDir = path21.join(cwd, appDir);
5267
- if (!fs22.existsSync(fullAppDir)) continue;
5268
- scanRoutes(fullAppDir, appDir, "", result);
5269
- break;
5270
- }
5271
- const authPatterns = ["middleware.ts", "middleware.js", "src/middleware.ts", "src/middleware.js"];
5272
- for (const p of authPatterns) {
5273
- if (fs22.existsSync(path21.join(cwd, p))) result.authFiles.push(p);
5274
- }
5275
- const authConfigGlobs = [
5276
- "src/app/api/auth",
5277
- "src/auth",
5278
- "src/lib/auth",
5279
- "auth.config.ts",
5280
- "auth.ts",
5281
- "src/app/api/oauth"
5282
- ];
5283
- for (const g of authConfigGlobs) {
5284
- if (fs22.existsSync(path21.join(cwd, g))) result.authFiles.push(g);
5285
- }
5286
- try {
5287
- const rolePaths = [
5288
- "src/types",
5289
- "src/lib",
5290
- "src/utils",
5291
- "src/constants",
5292
- "src/access",
5293
- "src/collections"
5294
- ];
5295
- for (const rp of rolePaths) {
5296
- const dir = path21.join(cwd, rp);
5297
- if (!fs22.existsSync(dir)) continue;
5298
- const files = fs22.readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".tsx"));
5299
- for (const f of files) {
5300
- try {
5301
- const content = fs22.readFileSync(path21.join(dir, f), "utf-8").slice(0, 5e3);
5302
- const roleMatches = content.match(/(?:role|Role|ROLE)\s*[=:]\s*['"](\w+)['"]/g);
5303
- if (roleMatches) {
5304
- for (const m of roleMatches) {
5305
- const val = m.match(/['"](\w+)['"]/);
5306
- if (val && !result.roles.includes(val[1])) result.roles.push(val[1]);
5307
- }
5308
- }
5309
- const enumMatch = content.match(/(?:enum|type)\s+\w*[Rr]ole\w*\s*[={]([^}]+)/s);
5310
- if (enumMatch) {
5311
- const vals = enumMatch[1].match(/['"](\w+)['"]/g);
5312
- if (vals) {
5313
- for (const v of vals) {
5314
- const clean = v.replace(/['"]/g, "");
5315
- if (!result.roles.includes(clean)) result.roles.push(clean);
5316
- }
5317
- }
5318
- }
5319
- } catch {
5320
- }
5321
- }
5322
- }
5323
- } catch {
5324
- }
5325
- return result;
5326
- }
5327
- function scanRoutes(dir, baseDir, prefix, result) {
5328
- let entries;
5329
- try {
5330
- entries = fs22.readdirSync(dir, { withFileTypes: true });
5331
- } catch {
5332
- return;
5333
- }
5334
- const hasPage = entries.some((e) => e.isFile() && /^page\.(tsx?|jsx?)$/.test(e.name));
5335
- if (hasPage) {
5336
- const routePath = prefix || "/";
5337
- const group = prefix.startsWith("/admin") ? "admin" : prefix.includes("/login") ? "auth" : prefix.includes("/signup") ? "auth" : prefix.includes("/api") ? "api" : "frontend";
5338
- result.routes.push({ path: routePath, group });
5339
- if (prefix.includes("/login")) result.loginPage = routePath;
5340
- if (prefix.startsWith("/admin") && !result.adminPath) result.adminPath = prefix;
5341
- }
5342
- for (const entry of entries) {
5343
- if (!entry.isDirectory()) continue;
5344
- if (entry.name === "node_modules" || entry.name === ".next") continue;
5345
- let segment = entry.name;
5346
- if (segment.startsWith("(") && segment.endsWith(")")) {
5347
- scanRoutes(path21.join(dir, entry.name), baseDir, prefix, result);
5348
- continue;
5349
- }
5350
- if (segment.startsWith("[") && segment.endsWith("]")) {
5351
- segment = `:${segment.slice(1, -1)}`;
5352
- }
5353
- if (segment.startsWith("[[") && segment.endsWith("]]")) {
5354
- segment = `:${segment.slice(2, -2)}?`;
5355
- }
5356
- scanRoutes(path21.join(dir, entry.name), baseDir, `${prefix}/${segment}`, result);
5357
- }
5358
- }
5359
- function generateQaGuide(discovery) {
5360
- const lines = ["# QA Guide", "", "## Authentication", ""];
5361
- if (discovery.loginPage) {
5362
- lines.push(`- Login page: \`${discovery.loginPage}\``);
5363
- }
5364
- lines.push(
5365
- "",
5366
- "### Test Accounts",
5367
- "<!-- Fill in your test/preview environment credentials below -->",
5368
- "| Role | Email | Password |",
5369
- "|------|-------|----------|",
5370
- "| Admin | admin@example.com | CHANGE_ME |",
5371
- "| User | user@example.com | CHANGE_ME |",
5372
- "",
5373
- "### Login Steps",
5374
- `1. Navigate to \`${discovery.loginPage ?? "/login"}\``,
5375
- "2. Enter credentials from the test accounts table above",
5376
- "3. Submit the login form",
5377
- "4. Verify redirect to dashboard or home page"
5378
- );
5379
- if (discovery.authFiles.length > 0) {
5380
- lines.push("", "### Auth Files");
5381
- for (const f of discovery.authFiles) {
5382
- lines.push(`- \`${f}\``);
5383
- }
5384
- }
5385
- if (discovery.roles.length > 0) {
5386
- lines.push("", "## Roles", "");
5387
- for (const role of discovery.roles) {
5388
- lines.push(`- \`${role}\``);
5389
- }
5390
- }
5391
- lines.push("", "## Key Pages", "");
5392
- const groups = {};
5393
- for (const route of discovery.routes) {
5394
- if (!groups[route.group]) groups[route.group] = [];
5395
- groups[route.group].push(route.path);
5396
- }
5397
- for (const [group, routes] of Object.entries(groups)) {
5398
- lines.push(`### ${group.charAt(0).toUpperCase() + group.slice(1)}`);
5399
- const sorted = routes.sort();
5400
- for (const r of sorted.slice(0, 20)) {
5401
- lines.push(`- \`${r}\``);
5402
- }
5403
- if (sorted.length > 20) {
5404
- lines.push(`- ... and ${sorted.length - 20} more`);
5405
- }
5406
- lines.push("");
5407
- }
5408
- lines.push(
5409
- "## Dev Server",
5410
- "",
5411
- `- Command: \`${discovery.devCommand || "pnpm dev"}\``,
5412
- `- URL: \`http://localhost:${discovery.devPort}\``,
5413
- ""
5414
- );
5415
- return lines.join("\n");
5416
- }
5417
5201
  function detectArchitectureBasic(cwd) {
5418
5202
  const detected = [];
5419
5203
  const pkgPath = path21.join(cwd, "package.json");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine-lite",
3
- "version": "0.1.103",
3
+ "version": "0.1.105",
4
4
  "description": "Autonomous SDLC pipeline: Kody orchestration + Claude Code + LiteLLM",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -5,23 +5,19 @@ mode: primary
5
5
  tools: [read, write, edit, bash, glob, grep]
6
6
  ---
7
7
 
8
- You are a review-fix agent following the Superpowers Executing Plans methodology.
8
+ You are a review-fix agent. The code review found issues that need fixing.
9
9
 
10
- The code review found issues that need fixing. Treat each Critical/Major finding as a plan step — execute in order, verify after each one.
11
-
12
- RULES (Superpowers Executing Plans discipline):
10
+ RULES:
13
11
  1. Fix ONLY Critical and Major issues (ignore Minor findings)
14
12
  2. Use Edit for surgical changes — do NOT rewrite entire files
15
13
  3. Run tests after EACH fix to verify nothing breaks
16
- 4. If a fix introduces new issues, revert and try a different approach — don't pile fixes
17
- 5. Document any deviations from the expected fix
18
- 6. Do NOT commit or push — the orchestrator handles git
14
+ 4. If a fix introduces new issues, revert and try a different approach
15
+ 5. Do NOT commit or push — the orchestrator handles git
19
16
 
20
- For each Critical/Major finding:
17
+ Read the review findings carefully. For each Critical/Major finding:
21
18
  1. Read the affected file to understand full context
22
- 2. Understand the root cause don't just patch the symptom
23
- 3. Make the minimal change to fix the issue
24
- 4. Run tests to verify the fix
25
- 5. Move to the next finding
19
+ 2. Make the minimal change to fix the issue
20
+ 3. Run tests to verify the fix
21
+ 4. Move to the next finding
26
22
 
27
23
  {{TASK_CONTEXT}}
@@ -7,15 +7,7 @@ tools: [read, glob, grep]
7
7
 
8
8
  You are a task classification agent following the Superpowers Brainstorming methodology.
9
9
 
10
- ## MANDATORY: Explore Before Classifying
11
-
12
- Before classifying, you MUST explore the project context:
13
- 1. **Examine the codebase** — Use Read, Glob, and Grep to understand project structure, existing patterns, and affected files.
14
- 2. **Find existing solutions** — Search for how similar problems are already solved in this codebase. If a pattern exists, the task should reuse it.
15
- 3. **Challenge assumptions** — Does the task description assume an approach? Are there simpler alternatives? Apply YAGNI ruthlessly.
16
- 4. **Identify ambiguity** — Could the requirements be interpreted two ways? Are there missing edge case decisions?
17
-
18
- ## Output
10
+ Before classifying, examine the codebase to understand the project structure, existing patterns, and affected files. Use Read, Glob, and Grep to explore.
19
11
 
20
12
  Output ONLY valid JSON. No markdown fences. No explanation. No extra text before or after the JSON.
21
13
 
@@ -27,7 +19,6 @@ Required JSON format:
27
19
  "scope": ["list", "of", "exact/file/paths", "affected"],
28
20
  "risk_level": "low | medium | high",
29
21
  "hasUI": true,
30
- "existing_patterns": ["list of existing patterns found that the implementation should reuse"],
31
22
  "questions": []
32
23
  }
33
24
 
@@ -40,17 +31,9 @@ Risk level heuristics:
40
31
  - medium: multiple files, possible side effects, API changes, new dependencies, refactoring existing logic
41
32
  - high: core business logic, data migrations, security, authentication, payment processing, database schema changes
42
33
 
43
- existing_patterns rules:
44
- - List patterns found in the codebase that are relevant to this task
45
- - Include the file path and a brief description of the pattern
46
- - If no relevant patterns exist, use an empty array []
47
- - These inform the planner — reuse existing solutions, don't invent new ones
48
-
49
- Questions rules (Superpowers Brainstorming discipline):
34
+ Questions rules:
50
35
  - ONLY ask product/requirements questions — things you CANNOT determine by reading code
51
36
  - Ask about: unclear scope, missing acceptance criteria, ambiguous user behavior, missing edge case decisions
52
- - Challenge assumptions — if the task implies an approach, consider simpler alternatives
53
- - Check for ambiguity — could requirements be interpreted two ways?
54
37
  - Do NOT ask about technical implementation — that is the planner's job
55
38
  - Do NOT ask about things you can find by reading the codebase (file structure, frameworks, patterns)
56
39
  - If the task is clear and complete, leave questions as an empty array []
@@ -105,18 +105,10 @@ jobs:
105
105
  env:
106
106
  BODY: ${{ github.event.comment.body }}
107
107
  run: |
108
- # Strip carriage returns — GitHub comments may contain \r\n line endings
109
- BODY=$(printf '%s' "$BODY" | tr -d '\r')
110
-
111
108
  # Extract: @kody [mode] [task-id] [--from stage]
112
109
  KODY_ARGS=$(echo "$BODY" | grep -oP '(?:@kody|/kody)\s+\K.*' || echo "")
113
- MODE=$(echo "$KODY_ARGS" | awk '{print $1}')
114
- RAW_TASK_ID=$(echo "$KODY_ARGS" | awk '{print $2}')
115
- # Don't treat flags (--from, --feedback) as task IDs
116
- case "$RAW_TASK_ID" in
117
- --*) TASK_ID="" ;;
118
- *) TASK_ID="$RAW_TASK_ID" ;;
119
- esac
110
+
111
+ # Extract flags first (before positional parsing)
120
112
  FROM_STAGE=$(echo "$KODY_ARGS" | grep -oP '(?<=--from )\S+' || echo "")
121
113
  FEEDBACK=$(echo "$KODY_ARGS" | grep -oP '(?<=--feedback ")[^"]*' || echo "")
122
114
  COMPLEXITY=""
@@ -128,12 +120,24 @@ jobs:
128
120
  DRY_RUN="true"
129
121
  fi
130
122
 
131
- # Validate mode
123
+ # Strip flags and their values for clean positional parsing
124
+ POSITIONAL=$(echo "$KODY_ARGS" | sed -E \
125
+ -e 's/--from\s+\S+//g' \
126
+ -e 's/--feedback\s+"[^"]*"//g' \
127
+ -e 's/--complexity\s+\S+//g' \
128
+ -e 's/--dry-run//g' \
129
+ -e 's/--ci-run-id\s+\S+//g' \
130
+ -e 's/\s+/ /g' -e 's/^ //' -e 's/ $//')
131
+
132
+ MODE=$(echo "$POSITIONAL" | awk '{print $1}')
133
+ TASK_ID=$(echo "$POSITIONAL" | awk '{print $2}')
134
+
135
+ # Validate mode — after flag stripping, only positional args remain
132
136
  case "$MODE" in
133
137
  full|rerun|fix|fix-ci|status|approve|review|resolve|bootstrap) ;;
134
138
  *)
135
- # If first arg isn't a mode, it might be a task-id or nothing
136
- if [ -n "$MODE" ] && [ "$MODE" != "" ]; then
139
+ # First positional isn't a known mode treat as task-id
140
+ if [ -n "$MODE" ]; then
137
141
  TASK_ID="$MODE"
138
142
  fi
139
143
  MODE="full"
@@ -1,4 +0,0 @@
1
- import type { AgentRunner } from "./types.js";
2
- import type { KodyConfig } from "./config.js";
3
- export declare function createClaudeCodeRunner(): AgentRunner;
4
- export declare function createRunners(config: KodyConfig): Record<string, AgentRunner>;
@@ -1,122 +0,0 @@
1
- import { spawn, execFileSync } from "child_process";
2
- const SIGKILL_GRACE_MS = 5000;
3
- const STDERR_TAIL_CHARS = 500;
4
- function writeStdin(child, prompt) {
5
- return new Promise((resolve, reject) => {
6
- if (!child.stdin) {
7
- resolve();
8
- return;
9
- }
10
- child.stdin.write(prompt, (err) => {
11
- if (err)
12
- reject(err);
13
- else {
14
- child.stdin.end();
15
- resolve();
16
- }
17
- });
18
- });
19
- }
20
- function waitForProcess(child, timeout) {
21
- return new Promise((resolve) => {
22
- const stdoutChunks = [];
23
- const stderrChunks = [];
24
- child.stdout?.on("data", (chunk) => stdoutChunks.push(chunk));
25
- child.stderr?.on("data", (chunk) => stderrChunks.push(chunk));
26
- const timer = setTimeout(() => {
27
- child.kill("SIGTERM");
28
- setTimeout(() => {
29
- if (!child.killed)
30
- child.kill("SIGKILL");
31
- }, SIGKILL_GRACE_MS);
32
- }, timeout);
33
- child.on("exit", (code) => {
34
- clearTimeout(timer);
35
- resolve({
36
- code,
37
- stdout: Buffer.concat(stdoutChunks).toString(),
38
- stderr: Buffer.concat(stderrChunks).toString(),
39
- });
40
- });
41
- child.on("error", (err) => {
42
- clearTimeout(timer);
43
- resolve({ code: -1, stdout: "", stderr: err.message });
44
- });
45
- });
46
- }
47
- async function runSubprocess(command, args, prompt, timeout, options) {
48
- const child = spawn(command, args, {
49
- cwd: options?.cwd ?? process.cwd(),
50
- env: {
51
- ...process.env,
52
- SKIP_BUILD: "1",
53
- SKIP_HOOKS: "1",
54
- ...options?.env,
55
- },
56
- stdio: ["pipe", "pipe", "pipe"],
57
- });
58
- try {
59
- await writeStdin(child, prompt);
60
- }
61
- catch (err) {
62
- return {
63
- outcome: "failed",
64
- error: `Failed to send prompt: ${err instanceof Error ? err.message : String(err)}`,
65
- };
66
- }
67
- const { code, stdout, stderr } = await waitForProcess(child, timeout);
68
- if (code === 0) {
69
- return { outcome: "completed", output: stdout };
70
- }
71
- return {
72
- outcome: code === null ? "timed_out" : "failed",
73
- error: `Exit code ${code}\n${stderr.slice(-STDERR_TAIL_CHARS)}`,
74
- };
75
- }
76
- function checkCommand(command, args) {
77
- try {
78
- execFileSync(command, args, { timeout: 10_000, stdio: "pipe" });
79
- return true;
80
- }
81
- catch {
82
- return false;
83
- }
84
- }
85
- // ─── Claude Code Runner ──────────────────────────────────────────────────────
86
- export function createClaudeCodeRunner() {
87
- return {
88
- async run(_stageName, prompt, model, timeout, _taskDir, options) {
89
- return runSubprocess("claude", [
90
- "--print",
91
- "--model", model,
92
- "--dangerously-skip-permissions",
93
- "--allowedTools", "Bash,Edit,Read,Write,Glob,Grep",
94
- ], prompt, timeout, options);
95
- },
96
- async healthCheck() {
97
- return checkCommand("claude", ["--version"]);
98
- },
99
- };
100
- }
101
- // ─── Runner Factory ──────────────────────────────────────────────────────────
102
- const RUNNER_FACTORIES = {
103
- "claude-code": createClaudeCodeRunner,
104
- };
105
- export function createRunners(config) {
106
- // New multi-runner config
107
- if (config.agent.runners && Object.keys(config.agent.runners).length > 0) {
108
- const runners = {};
109
- for (const [name, runnerConfig] of Object.entries(config.agent.runners)) {
110
- const factory = RUNNER_FACTORIES[runnerConfig.type];
111
- if (factory) {
112
- runners[name] = factory();
113
- }
114
- }
115
- return runners;
116
- }
117
- // Legacy single-runner fallback
118
- const runnerType = config.agent.runner ?? "claude-code";
119
- const factory = RUNNER_FACTORIES[runnerType];
120
- const defaultName = config.agent.defaultRunner ?? "claude";
121
- return { [defaultName]: factory ? factory() : createClaudeCodeRunner() };
122
- }
@@ -1,6 +0,0 @@
1
- /**
2
- * Parses @kody / /kody comment body into structured inputs.
3
- * Run by the parse job in GitHub Actions.
4
- * Reads from env, writes to $GITHUB_OUTPUT.
5
- */
6
- export {};