@kody-ade/kody-engine-lite 0.1.101 → 0.1.103
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/README.md +1 -1
- package/dist/bin/cli.js +197 -7
- package/package.json +1 -1
- package/prompts/review-fix.md +12 -8
- package/prompts/taskify.md +19 -2
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) · [Features](docs/FEATURES.md) · [Pipeline](docs/PIPELINE.md) · [Comparison](docs/COMPARISON.md)
|
|
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)
|
|
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
|
@@ -1546,6 +1546,11 @@ ${prompt}` : prompt;
|
|
|
1546
1546
|
}
|
|
1547
1547
|
if (isMcpEnabledForStage(stageName, config.mcp) && taskHasUI(taskDir)) {
|
|
1548
1548
|
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
|
+
}
|
|
1549
1554
|
}
|
|
1550
1555
|
return assembled;
|
|
1551
1556
|
}
|
|
@@ -1769,7 +1774,7 @@ async function executeAgentStage(ctx, def) {
|
|
|
1769
1774
|
description: plainText.slice(0, 500),
|
|
1770
1775
|
scope: [],
|
|
1771
1776
|
risk_level: "low",
|
|
1772
|
-
hasUI:
|
|
1777
|
+
hasUI: true,
|
|
1773
1778
|
questions: []
|
|
1774
1779
|
}, null, 2);
|
|
1775
1780
|
fs6.writeFileSync(outputPath, fallback);
|
|
@@ -4532,12 +4537,7 @@ function detectMcpConfig(cwd, pm, pkg) {
|
|
|
4532
4537
|
const defaultPort = isNext ? 3e3 : isVite ? 5173 : 3e3;
|
|
4533
4538
|
const mcp = {
|
|
4534
4539
|
enabled: true,
|
|
4535
|
-
servers: {
|
|
4536
|
-
playwright: {
|
|
4537
|
-
command: "npx",
|
|
4538
|
-
args: ["@playwright/mcp@latest"]
|
|
4539
|
-
}
|
|
4540
|
-
},
|
|
4540
|
+
servers: {},
|
|
4541
4541
|
stages: ["build", "review"]
|
|
4542
4542
|
};
|
|
4543
4543
|
if (hasDevScript) {
|
|
@@ -5034,6 +5034,23 @@ REMINDER: Output the full prompt template first (unchanged), then your three app
|
|
|
5034
5034
|
}
|
|
5035
5035
|
}
|
|
5036
5036
|
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
|
+
}
|
|
5037
5054
|
console.log("\n\u2500\u2500 Labels \u2500\u2500");
|
|
5038
5055
|
try {
|
|
5039
5056
|
let repoSlug = "";
|
|
@@ -5111,6 +5128,7 @@ REMINDER: Output the full prompt template first (unchanged), then your three app
|
|
|
5111
5128
|
const filesToCommit = [
|
|
5112
5129
|
".kody/memory/architecture.md",
|
|
5113
5130
|
".kody/memory/conventions.md",
|
|
5131
|
+
".kody/qa-guide.md",
|
|
5114
5132
|
...installedSkillPaths
|
|
5115
5133
|
].filter((f) => fs22.existsSync(path21.join(cwd, f)));
|
|
5116
5134
|
if (fs22.existsSync(path21.join(cwd, "skills-lock.json"))) {
|
|
@@ -5224,6 +5242,178 @@ Create it manually.`, cwd);
|
|
|
5224
5242
|
console.log(" \u2713 Project bootstrap complete!");
|
|
5225
5243
|
console.log(" Kody now has project-specific memory and customized step files.\n");
|
|
5226
5244
|
}
|
|
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
|
+
}
|
|
5227
5417
|
function detectArchitectureBasic(cwd) {
|
|
5228
5418
|
const detected = [];
|
|
5229
5419
|
const pkgPath = path21.join(cwd, "package.json");
|
package/package.json
CHANGED
package/prompts/review-fix.md
CHANGED
|
@@ -5,19 +5,23 @@ mode: primary
|
|
|
5
5
|
tools: [read, write, edit, bash, glob, grep]
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
You are a review-fix agent
|
|
8
|
+
You are a review-fix agent following the Superpowers Executing Plans methodology.
|
|
9
9
|
|
|
10
|
-
|
|
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):
|
|
11
13
|
1. Fix ONLY Critical and Major issues (ignore Minor findings)
|
|
12
14
|
2. Use Edit for surgical changes — do NOT rewrite entire files
|
|
13
15
|
3. Run tests after EACH fix to verify nothing breaks
|
|
14
|
-
4. If a fix introduces new issues, revert and try a different approach
|
|
15
|
-
5.
|
|
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
|
|
16
19
|
|
|
17
|
-
|
|
20
|
+
For each Critical/Major finding:
|
|
18
21
|
1. Read the affected file to understand full context
|
|
19
|
-
2.
|
|
20
|
-
3.
|
|
21
|
-
4.
|
|
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
|
|
22
26
|
|
|
23
27
|
{{TASK_CONTEXT}}
|
package/prompts/taskify.md
CHANGED
|
@@ -7,7 +7,15 @@ tools: [read, glob, grep]
|
|
|
7
7
|
|
|
8
8
|
You are a task classification agent following the Superpowers Brainstorming methodology.
|
|
9
9
|
|
|
10
|
-
|
|
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
|
|
11
19
|
|
|
12
20
|
Output ONLY valid JSON. No markdown fences. No explanation. No extra text before or after the JSON.
|
|
13
21
|
|
|
@@ -19,6 +27,7 @@ Required JSON format:
|
|
|
19
27
|
"scope": ["list", "of", "exact/file/paths", "affected"],
|
|
20
28
|
"risk_level": "low | medium | high",
|
|
21
29
|
"hasUI": true,
|
|
30
|
+
"existing_patterns": ["list of existing patterns found that the implementation should reuse"],
|
|
22
31
|
"questions": []
|
|
23
32
|
}
|
|
24
33
|
|
|
@@ -31,9 +40,17 @@ Risk level heuristics:
|
|
|
31
40
|
- medium: multiple files, possible side effects, API changes, new dependencies, refactoring existing logic
|
|
32
41
|
- high: core business logic, data migrations, security, authentication, payment processing, database schema changes
|
|
33
42
|
|
|
34
|
-
|
|
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):
|
|
35
50
|
- ONLY ask product/requirements questions — things you CANNOT determine by reading code
|
|
36
51
|
- 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?
|
|
37
54
|
- Do NOT ask about technical implementation — that is the planner's job
|
|
38
55
|
- Do NOT ask about things you can find by reading the codebase (file structure, frameworks, patterns)
|
|
39
56
|
- If the task is clear and complete, leave questions as an empty array []
|