@kody-ade/kody-engine 0.3.0 → 0.3.1
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/kody.js +112 -6
- package/dist/executables/fix/profile.json +19 -2
- package/dist/executables/plan/profile.json +22 -2
- package/dist/executables/plan/prompt.md +117 -9
- package/dist/executables/research/profile.json +24 -2
- package/dist/executables/research/prompt.md +4 -0
- package/dist/executables/review/profile.json +19 -2
- package/dist/executables/ui-review/profile.json +7 -1
- package/package.json +1 -1
package/dist/bin/kody.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// package.json
|
|
4
4
|
var package_default = {
|
|
5
5
|
name: "@kody-ade/kody-engine",
|
|
6
|
-
version: "0.3.
|
|
6
|
+
version: "0.3.1",
|
|
7
7
|
description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
8
8
|
license: "MIT",
|
|
9
9
|
type: "module",
|
|
@@ -366,7 +366,14 @@ async function runAgent(opts) {
|
|
|
366
366
|
env
|
|
367
367
|
};
|
|
368
368
|
if (opts.mcpServers && opts.mcpServers.length > 0) {
|
|
369
|
-
queryOptions.mcpServers =
|
|
369
|
+
queryOptions.mcpServers = Object.fromEntries(
|
|
370
|
+
opts.mcpServers.map((s) => {
|
|
371
|
+
const cfg = { command: s.command };
|
|
372
|
+
if (s.args) cfg.args = s.args;
|
|
373
|
+
if (s.env) cfg.env = s.env;
|
|
374
|
+
return [s.name, cfg];
|
|
375
|
+
})
|
|
376
|
+
);
|
|
370
377
|
}
|
|
371
378
|
if (opts.pluginPaths && opts.pluginPaths.length > 0) {
|
|
372
379
|
queryOptions.plugins = opts.pluginPaths.map((p) => ({ type: "local", path: p }));
|
|
@@ -1428,6 +1435,7 @@ function parseAgentResult(finalText) {
|
|
|
1428
1435
|
prSummary: "",
|
|
1429
1436
|
feedbackActions: "",
|
|
1430
1437
|
planDeviations: "",
|
|
1438
|
+
priorArt: "",
|
|
1431
1439
|
failureReason: "agent produced no final message"
|
|
1432
1440
|
};
|
|
1433
1441
|
const failedMatch = text.match(/(?:^|\n)\s*FAILED\s*:\s*(.+?)\s*$/s);
|
|
@@ -1438,6 +1446,7 @@ function parseAgentResult(finalText) {
|
|
|
1438
1446
|
prSummary: "",
|
|
1439
1447
|
feedbackActions: "",
|
|
1440
1448
|
planDeviations: "",
|
|
1449
|
+
priorArt: "",
|
|
1441
1450
|
failureReason: failedMatch[1].trim()
|
|
1442
1451
|
};
|
|
1443
1452
|
}
|
|
@@ -1450,6 +1459,7 @@ function parseAgentResult(finalText) {
|
|
|
1450
1459
|
prSummary: "",
|
|
1451
1460
|
feedbackActions: "",
|
|
1452
1461
|
planDeviations: "",
|
|
1462
|
+
priorArt: "",
|
|
1453
1463
|
failureReason: "no DONE or FAILED marker in agent output"
|
|
1454
1464
|
};
|
|
1455
1465
|
}
|
|
@@ -1458,24 +1468,27 @@ function parseAgentResult(finalText) {
|
|
|
1458
1468
|
const feedbackActions = extractBlock(
|
|
1459
1469
|
text,
|
|
1460
1470
|
/(?:^|\n)[ \t]*FEEDBACK_ACTIONS\s*:[ \t]*\n/i,
|
|
1461
|
-
/(?:^|\n)[ \t]*(?:PLAN_DEVIATIONS|COMMIT_MSG|PR_SUMMARY)\s*:/i
|
|
1471
|
+
/(?:^|\n)[ \t]*(?:PLAN_DEVIATIONS|COMMIT_MSG|PR_SUMMARY|PRIOR_ART)\s*:/i
|
|
1462
1472
|
);
|
|
1463
1473
|
let planDeviations = extractBlock(
|
|
1464
1474
|
text,
|
|
1465
1475
|
/(?:^|\n)[ \t]*PLAN_DEVIATIONS\s*:[ \t]*\n/i,
|
|
1466
|
-
/(?:^|\n)[ \t]*(?:COMMIT_MSG|PR_SUMMARY|FEEDBACK_ACTIONS)\s*:/i
|
|
1476
|
+
/(?:^|\n)[ \t]*(?:COMMIT_MSG|PR_SUMMARY|FEEDBACK_ACTIONS|PRIOR_ART)\s*:/i
|
|
1467
1477
|
);
|
|
1468
1478
|
if (!planDeviations) {
|
|
1469
1479
|
const inline = text.match(/(?:^|\n)[ \t]*PLAN_DEVIATIONS\s*:[ \t]*(.+?)[ \t]*(?:\n|$)/i);
|
|
1470
1480
|
if (inline) planDeviations = inline[1].trim();
|
|
1471
1481
|
}
|
|
1482
|
+
let priorArt = "";
|
|
1483
|
+
const priorArtInline = text.match(/(?:^|\n)[ \t]*PRIOR_ART\s*:[ \t]*(.+?)[ \t]*(?:\n|$)/i);
|
|
1484
|
+
if (priorArtInline) priorArt = priorArtInline[1].trim();
|
|
1472
1485
|
const summaryStart = text.search(/(^|\n)[ \t]*PR_SUMMARY\s*:[ \t]*\n/i);
|
|
1473
1486
|
let prSummary = "";
|
|
1474
1487
|
if (summaryStart !== -1) {
|
|
1475
1488
|
const afterMarker = text.slice(summaryStart).replace(/^[\s\S]*?PR_SUMMARY\s*:[ \t]*\n/i, "");
|
|
1476
1489
|
prSummary = afterMarker.replace(/\n\s*```\s*$/g, "").replace(/```\s*$/g, "").trim();
|
|
1477
1490
|
}
|
|
1478
|
-
return { done: true, commitMessage, prSummary, feedbackActions, planDeviations, failureReason: "" };
|
|
1491
|
+
return { done: true, commitMessage, prSummary, feedbackActions, planDeviations, priorArt, failureReason: "" };
|
|
1479
1492
|
}
|
|
1480
1493
|
function extractBlock(text, startMarker, endMarker) {
|
|
1481
1494
|
const startIdx = text.search(startMarker);
|
|
@@ -3600,6 +3613,97 @@ var loadIssueContext = async (ctx) => {
|
|
|
3600
3613
|
ctx.data.commentTargetNumber = issueNumber;
|
|
3601
3614
|
};
|
|
3602
3615
|
|
|
3616
|
+
// src/scripts/loadPriorArt.ts
|
|
3617
|
+
var PER_PR_DIFF_MAX_BYTES = 8e3;
|
|
3618
|
+
var TOTAL_MAX_BYTES = 3e4;
|
|
3619
|
+
var TRUNCATED_SUFFIX = "\n\n\u2026 (truncated)";
|
|
3620
|
+
var loadPriorArt = async (ctx, _profile, args) => {
|
|
3621
|
+
const artifactName = typeof args?.artifactName === "string" ? args.artifactName : "priorArt";
|
|
3622
|
+
const state = ctx.data.taskState;
|
|
3623
|
+
const artifact = state?.artifacts?.[artifactName];
|
|
3624
|
+
const prNumbers = parsePrNumbers(artifact?.content);
|
|
3625
|
+
if (prNumbers.length === 0) {
|
|
3626
|
+
ctx.data.priorArt = "";
|
|
3627
|
+
return;
|
|
3628
|
+
}
|
|
3629
|
+
const blocks = [];
|
|
3630
|
+
for (const n of prNumbers) {
|
|
3631
|
+
const block = fetchPrBlock(n, ctx.cwd);
|
|
3632
|
+
if (block) blocks.push(block);
|
|
3633
|
+
}
|
|
3634
|
+
const joined = blocks.join("\n\n---\n\n");
|
|
3635
|
+
ctx.data.priorArt = joined.length > TOTAL_MAX_BYTES ? joined.slice(0, TOTAL_MAX_BYTES) + TRUNCATED_SUFFIX : joined;
|
|
3636
|
+
};
|
|
3637
|
+
function parsePrNumbers(raw) {
|
|
3638
|
+
if (!raw) return [];
|
|
3639
|
+
const trimmed = raw.trim();
|
|
3640
|
+
if (!trimmed) return [];
|
|
3641
|
+
try {
|
|
3642
|
+
const parsed = JSON.parse(trimmed);
|
|
3643
|
+
if (!Array.isArray(parsed)) return [];
|
|
3644
|
+
return parsed.filter((n) => typeof n === "number" && Number.isInteger(n) && n > 0);
|
|
3645
|
+
} catch {
|
|
3646
|
+
return [];
|
|
3647
|
+
}
|
|
3648
|
+
}
|
|
3649
|
+
function fetchPrBlock(prNumber, cwd) {
|
|
3650
|
+
try {
|
|
3651
|
+
const metaRaw = gh2(["pr", "view", String(prNumber), "--json", "title,state,url,mergedAt,closedAt"], { cwd });
|
|
3652
|
+
const meta = JSON.parse(metaRaw);
|
|
3653
|
+
const diff = truncate3(safeGh(["pr", "diff", String(prNumber)], cwd), PER_PR_DIFF_MAX_BYTES);
|
|
3654
|
+
const commentsRaw = safeGh(["pr", "view", String(prNumber), "--json", "comments,reviews"], cwd);
|
|
3655
|
+
const commentsBlock = formatReviewComments(commentsRaw);
|
|
3656
|
+
const lines = [
|
|
3657
|
+
`## Prior art: PR #${prNumber} \u2014 ${meta.title ?? "(no title)"} [${meta.state ?? "unknown"}]`,
|
|
3658
|
+
meta.url ? meta.url : "",
|
|
3659
|
+
"",
|
|
3660
|
+
"### Diff",
|
|
3661
|
+
"```diff",
|
|
3662
|
+
diff || "(empty)",
|
|
3663
|
+
"```"
|
|
3664
|
+
];
|
|
3665
|
+
if (commentsBlock) {
|
|
3666
|
+
lines.push("");
|
|
3667
|
+
lines.push("### Review comments");
|
|
3668
|
+
lines.push(commentsBlock);
|
|
3669
|
+
}
|
|
3670
|
+
return lines.filter((l) => l !== "").join("\n");
|
|
3671
|
+
} catch (err) {
|
|
3672
|
+
return `## Prior art: PR #${prNumber}
|
|
3673
|
+
_Could not fetch \u2014 ${err instanceof Error ? err.message : String(err)}_`;
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
function safeGh(args, cwd) {
|
|
3677
|
+
try {
|
|
3678
|
+
return gh2(args, { cwd });
|
|
3679
|
+
} catch {
|
|
3680
|
+
return "";
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3683
|
+
function truncate3(s, max) {
|
|
3684
|
+
return s.length <= max ? s : s.slice(0, max) + TRUNCATED_SUFFIX;
|
|
3685
|
+
}
|
|
3686
|
+
function formatReviewComments(raw) {
|
|
3687
|
+
if (!raw) return "";
|
|
3688
|
+
try {
|
|
3689
|
+
const parsed = JSON.parse(raw);
|
|
3690
|
+
const out = [];
|
|
3691
|
+
for (const c of parsed.comments ?? []) {
|
|
3692
|
+
if (!c.body) continue;
|
|
3693
|
+
out.push(`- **${c.author?.login ?? "unknown"}**: ${c.body.replace(/\n/g, " ").slice(0, 500)}`);
|
|
3694
|
+
}
|
|
3695
|
+
for (const r of parsed.reviews ?? []) {
|
|
3696
|
+
if (!r.body && !r.state) continue;
|
|
3697
|
+
const state = r.state ? ` (${r.state})` : "";
|
|
3698
|
+
const body = r.body ? `: ${r.body.replace(/\n/g, " ").slice(0, 500)}` : "";
|
|
3699
|
+
out.push(`- **${r.author?.login ?? "unknown"}**${state}${body}`);
|
|
3700
|
+
}
|
|
3701
|
+
return out.join("\n");
|
|
3702
|
+
} catch {
|
|
3703
|
+
return "";
|
|
3704
|
+
}
|
|
3705
|
+
}
|
|
3706
|
+
|
|
3603
3707
|
// src/scripts/loadTaskState.ts
|
|
3604
3708
|
var loadTaskState = async (ctx) => {
|
|
3605
3709
|
const target = ctx.data.commentTargetType;
|
|
@@ -3656,6 +3760,7 @@ var parseAgentResult2 = async (ctx, profile, agentResult) => {
|
|
|
3656
3760
|
ctx.data.prSummary = parsed.prSummary;
|
|
3657
3761
|
ctx.data.feedbackActions = parsed.feedbackActions;
|
|
3658
3762
|
ctx.data.planDeviations = parsed.planDeviations;
|
|
3763
|
+
ctx.data.priorArt = parsed.priorArt;
|
|
3659
3764
|
ctx.data.agentFailureReason = parsed.failureReason;
|
|
3660
3765
|
ctx.data.agentOutcome = agentResult.outcome;
|
|
3661
3766
|
ctx.data.agentError = agentResult.error;
|
|
@@ -4924,6 +5029,7 @@ var preflightScripts = {
|
|
|
4924
5029
|
loadIssueContext,
|
|
4925
5030
|
loadConventions,
|
|
4926
5031
|
loadCoverageRules,
|
|
5032
|
+
loadPriorArt,
|
|
4927
5033
|
loadQaGuide,
|
|
4928
5034
|
buildSyntheticPlugin,
|
|
4929
5035
|
resolveArtifacts,
|
|
@@ -5079,7 +5185,7 @@ async function runExecutable(profileName, input) {
|
|
|
5079
5185
|
ndjsonDir,
|
|
5080
5186
|
allowedToolsOverride: profile.claudeCode.tools,
|
|
5081
5187
|
permissionModeOverride: profile.claudeCode.permissionMode,
|
|
5082
|
-
mcpServers: profile.claudeCode.mcpServers,
|
|
5188
|
+
mcpServers: profile.claudeCode.mcpServers.length > 0 ? profile.claudeCode.mcpServers : void 0,
|
|
5083
5189
|
pluginPaths: pluginPaths.length > 0 ? pluginPaths : void 0,
|
|
5084
5190
|
maxTurns: profile.claudeCode.maxTurns,
|
|
5085
5191
|
maxThinkingTokens: profile.claudeCode.maxThinkingTokens,
|
|
@@ -36,9 +36,26 @@
|
|
|
36
36
|
"commands": [],
|
|
37
37
|
"subagents": [],
|
|
38
38
|
"plugins": [],
|
|
39
|
-
"mcpServers": [
|
|
39
|
+
"mcpServers": [
|
|
40
|
+
{
|
|
41
|
+
"name": "playwright",
|
|
42
|
+
"command": "npx",
|
|
43
|
+
"args": ["-y", "@playwright/mcp@latest"]
|
|
44
|
+
}
|
|
45
|
+
]
|
|
40
46
|
},
|
|
41
|
-
"cliTools": [
|
|
47
|
+
"cliTools": [
|
|
48
|
+
{
|
|
49
|
+
"name": "playwright",
|
|
50
|
+
"install": {
|
|
51
|
+
"required": false,
|
|
52
|
+
"checkCommand": "npx --no-install playwright --version",
|
|
53
|
+
"installCommand": "npx --yes playwright install --with-deps chromium"
|
|
54
|
+
},
|
|
55
|
+
"verify": "npx --no-install playwright --version",
|
|
56
|
+
"usage": ""
|
|
57
|
+
}
|
|
58
|
+
],
|
|
42
59
|
"scripts": {
|
|
43
60
|
"preflight": [
|
|
44
61
|
{
|
|
@@ -27,9 +27,26 @@
|
|
|
27
27
|
"commands": [],
|
|
28
28
|
"subagents": [],
|
|
29
29
|
"plugins": [],
|
|
30
|
-
"mcpServers": [
|
|
30
|
+
"mcpServers": [
|
|
31
|
+
{
|
|
32
|
+
"name": "playwright",
|
|
33
|
+
"command": "npx",
|
|
34
|
+
"args": ["-y", "@playwright/mcp@latest"]
|
|
35
|
+
}
|
|
36
|
+
]
|
|
31
37
|
},
|
|
32
|
-
"cliTools": [
|
|
38
|
+
"cliTools": [
|
|
39
|
+
{
|
|
40
|
+
"name": "playwright",
|
|
41
|
+
"install": {
|
|
42
|
+
"required": false,
|
|
43
|
+
"checkCommand": "npx --no-install playwright --version",
|
|
44
|
+
"installCommand": "npx --yes playwright install --with-deps chromium"
|
|
45
|
+
},
|
|
46
|
+
"verify": "npx --no-install playwright --version",
|
|
47
|
+
"usage": ""
|
|
48
|
+
}
|
|
49
|
+
],
|
|
33
50
|
"scripts": {
|
|
34
51
|
"preflight": [
|
|
35
52
|
{
|
|
@@ -49,6 +66,9 @@
|
|
|
49
66
|
{
|
|
50
67
|
"script": "loadConventions"
|
|
51
68
|
},
|
|
69
|
+
{
|
|
70
|
+
"script": "loadPriorArt"
|
|
71
|
+
},
|
|
52
72
|
{
|
|
53
73
|
"script": "composePrompt"
|
|
54
74
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
You are a senior engineer producing
|
|
1
|
+
You are a senior engineer producing a **deep, detailed implementation plan** for the GitHub issue below. The plan must be thorough enough that another engineer can implement the feature without re-doing research — file locations, function signatures, algorithms, edge cases, and tests are all specified. You will NOT write code. You will NOT run git or gh commands. You will NOT modify files.
|
|
2
2
|
|
|
3
|
-
1. Use Read / Grep / Glob / Bash (read-only) to study the codebase as much as needed.
|
|
3
|
+
1. Use Read / Grep / Glob / Bash (read-only) to study the codebase as much as needed. Depth matters more than speed — invest turns in understanding before writing.
|
|
4
4
|
2. Emit a final message with the plan wrapped in the required markers (see "Required output").
|
|
5
5
|
|
|
6
6
|
---
|
|
@@ -17,6 +17,43 @@ Recent comments (most recent first, truncated):
|
|
|
17
17
|
|
|
18
18
|
{{conventionsBlock}}
|
|
19
19
|
|
|
20
|
+
{{priorArt}}
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# Delta mode — if a prior plan comment exists
|
|
25
|
+
|
|
26
|
+
Before writing the plan, scan the "Recent comments" block above for a previous
|
|
27
|
+
comment whose body starts with `## Plan for issue`. If one exists, you are in
|
|
28
|
+
**delta mode**:
|
|
29
|
+
|
|
30
|
+
1. Treat the prior plan as the baseline. Do NOT regenerate unchanged sections
|
|
31
|
+
from scratch.
|
|
32
|
+
2. Integrate the signal from comments posted AFTER the prior plan: user
|
|
33
|
+
answers, correction directives, new clarifying info, closed/merged PRs that
|
|
34
|
+
appeared since.
|
|
35
|
+
3. In each section, mark changed bullets with `(updated)`, new bullets with
|
|
36
|
+
`(new)`, and removed items with `(removed — <reason>)`. Preserve unchanged
|
|
37
|
+
bullets verbatim so reviewers can diff.
|
|
38
|
+
4. If nothing material has changed since the prior plan, output
|
|
39
|
+
`FAILED: no new information since last plan` instead of a duplicate.
|
|
40
|
+
|
|
41
|
+
If no prior `## Plan for issue` comment exists, produce a full first-pass
|
|
42
|
+
plan under the Required output structure below.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
# Research floor (MUST be done before writing the plan)
|
|
47
|
+
|
|
48
|
+
Before producing the final plan, you MUST have read:
|
|
49
|
+
|
|
50
|
+
- Every file you intend to change (the full file, not just a grep hit).
|
|
51
|
+
- The tests for each file you intend to change, if tests exist for that module.
|
|
52
|
+
- At least one sibling module that already implements the same pattern you're about to follow (reference implementations).
|
|
53
|
+
- The full prior-art diffs above (if any) — not just titles. Those represent failed solutions; understanding why they failed is part of the plan.
|
|
54
|
+
|
|
55
|
+
If a file you need to read does not exist, say so explicitly in the plan under "Ambiguities" — do NOT guess at its contents.
|
|
56
|
+
|
|
20
57
|
---
|
|
21
58
|
|
|
22
59
|
# Required output
|
|
@@ -27,16 +64,87 @@ Your FINAL message must be exactly this shape (no extra text before or after):
|
|
|
27
64
|
DONE
|
|
28
65
|
COMMIT_MSG: plan: <very short title>
|
|
29
66
|
PR_SUMMARY:
|
|
30
|
-
<A
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
67
|
+
<A deep, detailed implementation plan in markdown with the following sections, in order. Omit a section only if its trigger condition is not met — do not leave placeholders. Depth is expected; brevity for its own sake is not a goal.
|
|
68
|
+
|
|
69
|
+
## Existing patterns found
|
|
70
|
+
For each major part of the change, name the sibling module in this repo that
|
|
71
|
+
already solves a similar problem and state how this plan reuses it.
|
|
72
|
+
- Pattern: <what kind of pattern — e.g. "admin field with custom React component", "fetch-then-group client hook", "JSON strings module">
|
|
73
|
+
- Reference: <exact path in this repo, e.g. `src/ui/admin/LessonBlocksField/index.tsx`>
|
|
74
|
+
- Reuse: <how this plan follows it — which hooks/APIs/idioms are mirrored, what deviates and why>
|
|
75
|
+
If you searched and found nothing applicable, say so explicitly: "Searched
|
|
76
|
+
for X / Y / Z — no existing pattern; proposing new convention because …".
|
|
77
|
+
Proposing a new pattern when an existing one covers the use case is a
|
|
78
|
+
planning failure — fall back to reuse unless you name a concrete reason.
|
|
79
|
+
|
|
80
|
+
## Changes (per file)
|
|
81
|
+
For EACH file you will change or create, include:
|
|
82
|
+
- Path (exact).
|
|
83
|
+
- Why this file — one sentence tying the change to the issue.
|
|
84
|
+
- Current state — what's there today (function/class/export names, relevant line ranges). Skip for new files.
|
|
85
|
+
- Target state — what will be there after the change, at the same level of specificity.
|
|
86
|
+
- Exact locations of edits (function name, line range if stable, or anchor like "after the `meta` group field, before the closing `fields: []`").
|
|
87
|
+
- For new files: rough shape including exports, key functions with signatures, and top-level module comment.
|
|
88
|
+
- Dependencies touched (imports added/removed, new packages) — call out if anything needs installing.
|
|
89
|
+
|
|
90
|
+
## Algorithms & pseudocode
|
|
91
|
+
REQUIRED for any non-trivial logic (sorting, diffing, state transitions, concurrency, batching, caching, conflict resolution).
|
|
92
|
+
- Write pseudocode (not production code) showing the actual algorithm — inputs, steps, outputs.
|
|
93
|
+
- Call out invariants the algorithm preserves.
|
|
94
|
+
- Call out complexity (N swaps vs N-squared recalc vs single-batch write).
|
|
95
|
+
- If there's a choice between two algorithms, explain why you picked this one.
|
|
96
|
+
|
|
97
|
+
## How clarifying answers shape the plan
|
|
98
|
+
REQUIRED if research asked clarifying questions and the issue comments contain user answers.
|
|
99
|
+
- For each answered question: name the concrete design choice the answer forces — not a restatement of the answer.
|
|
100
|
+
- "Answer: yes → init orders 10/20/30 on first interaction" → spell out: which function performs the init, when it runs (mount vs first-swap), how it detects the "first use" state, what happens on re-entry.
|
|
101
|
+
|
|
102
|
+
## Why this will work
|
|
103
|
+
REQUIRED if research cites a prior failed attempt (closed PR, reverted commit, previous run that didn't land), or if prior-art above contains a diff.
|
|
104
|
+
- Root-cause hypothesis — what specifically went wrong in the prior attempt (cite lines from the prior diff above).
|
|
105
|
+
- The specific change in THIS plan that addresses the root cause — name the file/line/hook/config that differs from the prior attempt.
|
|
106
|
+
- How you will verify the fix works — a concrete behavioral check (URL + action + expected UI, or API call + expected response, or a test case). Not "typecheck passes."
|
|
107
|
+
|
|
108
|
+
## API surface verification
|
|
109
|
+
REQUIRED for every hook, import, SDK method, framework primitive, or config key the plan names.
|
|
110
|
+
- Build a table or list. For each named symbol: the file path where it's defined, or the exact package + export (with a `node_modules/...` path you actually read), or the mark `UNVERIFIED`.
|
|
111
|
+
- Do not guess. If you could not find it with Read / Grep / Glob, it is UNVERIFIED. Do not rely on UNVERIFIED symbols in the plan — flag them as blockers.
|
|
112
|
+
- Include negative evidence too: "Searched for `useXxx` in `@payloadcms/ui` exports — not found; planner assumed `useDocumentInfo` instead."
|
|
113
|
+
|
|
114
|
+
## Initial data state → transition → steady state
|
|
115
|
+
REQUIRED if the feature mutates existing data (reorder, migrate, backfill, rename, enable).
|
|
116
|
+
- Initial state: describe the data as it is in production today, including edge cases (rows with NULL, rows with default zero, orphan rows, etc).
|
|
117
|
+
- Transition: the exact step(s) that move data from initial → steady, including who triggers them (user action, migration script, on-mount hook), idempotency, and rollback behavior.
|
|
118
|
+
- Steady state: what invariants hold after transition.
|
|
119
|
+
- Failure modes during transition: partial-apply, race conditions, concurrent writers.
|
|
120
|
+
|
|
121
|
+
## Error paths & failure handling
|
|
122
|
+
For each external call or mutation in the plan (API request, DB write, file op, SDK call), enumerate:
|
|
123
|
+
- What can fail (network, validation, auth, not-found, conflict, rate limit).
|
|
124
|
+
- What the UI/caller does on each failure — retry, surface error, rollback, log-and-continue.
|
|
125
|
+
- What state the system is left in if the op fails mid-way.
|
|
126
|
+
|
|
127
|
+
## Test plan
|
|
128
|
+
- Specific test cases by name, with inputs and expected outputs. Not "add unit tests."
|
|
129
|
+
- Unit tests: one line per test naming what it asserts.
|
|
130
|
+
- Integration / behavioral tests: one line each, naming the flow covered and the assertion.
|
|
131
|
+
- Regression tests for the prior-art failure mode (if applicable) — a test that would have caught the prior bug.
|
|
132
|
+
- Manual verification steps: URL + click sequence + expected UI, or API call + expected response.
|
|
133
|
+
|
|
134
|
+
## Ambiguities & assumptions
|
|
135
|
+
- List anything still unresolved that needs human input before implementation.
|
|
136
|
+
- List every assumption the plan makes that was NOT confirmed by the issue, comments, or code (e.g. "assumed `usePayload` hook exists — UNVERIFIED").
|
|
137
|
+
|
|
138
|
+
## Verification checklist
|
|
139
|
+
- Build / typecheck / test / lint commands expected to pass after implementation.
|
|
140
|
+
- Each concrete behavioral check from "Test plan" restated as a pass/fail gate.
|
|
141
|
+
|
|
142
|
+
No filler. No marketing language. Depth over brevity.>
|
|
36
143
|
```
|
|
37
144
|
|
|
38
145
|
# Rules
|
|
39
146
|
- Read-only. Do NOT modify any file.
|
|
40
147
|
- Do NOT run git or gh commands.
|
|
41
|
-
- No speculative scope — plan only what the issue asks for.
|
|
148
|
+
- No speculative scope — plan only what the issue asks for, but plan it THOROUGHLY.
|
|
42
149
|
- If the issue is ambiguous and you cannot make progress without input, output `FAILED: <what's unclear>` instead of a plan.
|
|
150
|
+
- If the Research floor cannot be met because required files are missing or unreadable, output `FAILED: <what could not be read>` instead of a half-blind plan.
|
|
@@ -27,9 +27,26 @@
|
|
|
27
27
|
"commands": [],
|
|
28
28
|
"subagents": [],
|
|
29
29
|
"plugins": [],
|
|
30
|
-
"mcpServers": [
|
|
30
|
+
"mcpServers": [
|
|
31
|
+
{
|
|
32
|
+
"name": "playwright",
|
|
33
|
+
"command": "npx",
|
|
34
|
+
"args": ["-y", "@playwright/mcp@latest"]
|
|
35
|
+
}
|
|
36
|
+
]
|
|
31
37
|
},
|
|
32
|
-
"cliTools": [
|
|
38
|
+
"cliTools": [
|
|
39
|
+
{
|
|
40
|
+
"name": "playwright",
|
|
41
|
+
"install": {
|
|
42
|
+
"required": false,
|
|
43
|
+
"checkCommand": "npx --no-install playwright --version",
|
|
44
|
+
"installCommand": "npx --yes playwright install --with-deps chromium"
|
|
45
|
+
},
|
|
46
|
+
"verify": "npx --no-install playwright --version",
|
|
47
|
+
"usage": ""
|
|
48
|
+
}
|
|
49
|
+
],
|
|
33
50
|
"scripts": {
|
|
34
51
|
"preflight": [
|
|
35
52
|
{
|
|
@@ -84,6 +101,11 @@
|
|
|
84
101
|
"name": "research",
|
|
85
102
|
"format": "markdown",
|
|
86
103
|
"from": "prSummary"
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"name": "priorArt",
|
|
107
|
+
"format": "json",
|
|
108
|
+
"from": "priorArt"
|
|
87
109
|
}
|
|
88
110
|
]
|
|
89
111
|
}
|
|
@@ -25,6 +25,7 @@ Your FINAL message must be exactly this shape (no extra text before or after):
|
|
|
25
25
|
```
|
|
26
26
|
DONE
|
|
27
27
|
COMMIT_MSG: research: <very short title>
|
|
28
|
+
PRIOR_ART: <JSON array of closed or merged PR numbers from this repo that are prior attempts at THIS issue, or [] if none. Include only PRs that actually touched the same feature/area — not every PR your research happens to mention. Example: [1086] or []. Must be valid JSON parseable as number[].>
|
|
28
29
|
PR_SUMMARY:
|
|
29
30
|
<A research doc in markdown with EXACTLY these sections, in order:
|
|
30
31
|
|
|
@@ -82,6 +83,9 @@ Gaps & assumptions — they live in the prior comment. Keep the whole delta
|
|
|
82
83
|
under 25 lines. If nothing has changed since the prior research, output
|
|
83
84
|
`FAILED: no new information since last research` instead.
|
|
84
85
|
|
|
86
|
+
`PRIOR_ART:` is still required in delta mode (carry forward the prior list,
|
|
87
|
+
or update it if new PRs became relevant since).
|
|
88
|
+
|
|
85
89
|
If no prior `## Research for issue` comment exists in the thread, produce
|
|
86
90
|
the full first-pass structure below.
|
|
87
91
|
|
|
@@ -28,9 +28,26 @@
|
|
|
28
28
|
"commands": [],
|
|
29
29
|
"subagents": [],
|
|
30
30
|
"plugins": [],
|
|
31
|
-
"mcpServers": [
|
|
31
|
+
"mcpServers": [
|
|
32
|
+
{
|
|
33
|
+
"name": "playwright",
|
|
34
|
+
"command": "npx",
|
|
35
|
+
"args": ["-y", "@playwright/mcp@latest"]
|
|
36
|
+
}
|
|
37
|
+
]
|
|
32
38
|
},
|
|
33
|
-
"cliTools": [
|
|
39
|
+
"cliTools": [
|
|
40
|
+
{
|
|
41
|
+
"name": "playwright",
|
|
42
|
+
"install": {
|
|
43
|
+
"required": false,
|
|
44
|
+
"checkCommand": "npx --no-install playwright --version",
|
|
45
|
+
"installCommand": "npx --yes playwright install --with-deps chromium"
|
|
46
|
+
},
|
|
47
|
+
"verify": "npx --no-install playwright --version",
|
|
48
|
+
"usage": ""
|
|
49
|
+
}
|
|
50
|
+
],
|
|
34
51
|
"scripts": {
|
|
35
52
|
"preflight": [
|
|
36
53
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kody-ade/kody-engine",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative executable profiles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|