@kody-ade/kody-engine-lite 0.1.8 → 0.1.10
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 +27 -10
- package/dist/bin/cli.js +71 -4
- package/package.json +1 -1
- package/templates/kody.yml +10 -2
package/README.md
CHANGED
|
@@ -112,7 +112,7 @@ git push
|
|
|
112
112
|
Then comment on any issue:
|
|
113
113
|
|
|
114
114
|
```
|
|
115
|
-
@kody
|
|
115
|
+
@kody
|
|
116
116
|
```
|
|
117
117
|
|
|
118
118
|
## CLI Usage
|
|
@@ -121,19 +121,25 @@ Then comment on any issue:
|
|
|
121
121
|
|
|
122
122
|
```bash
|
|
123
123
|
# Run against current directory
|
|
124
|
-
kody-engine-lite run --task
|
|
124
|
+
kody-engine-lite run --task "Add a sum function to src/math.ts with tests"
|
|
125
125
|
|
|
126
126
|
# Run against a different project
|
|
127
|
-
kody-engine-lite run --task
|
|
127
|
+
kody-engine-lite run --task "Add feature X" --cwd /path/to/project
|
|
128
128
|
|
|
129
129
|
# Run from a GitHub issue (fetches issue body as task)
|
|
130
|
-
kody-engine-lite run --
|
|
130
|
+
kody-engine-lite run --issue-number 1 --cwd /path/to/project
|
|
131
131
|
|
|
132
132
|
# Dry run (no agent calls)
|
|
133
|
-
kody-engine-lite run --task
|
|
133
|
+
kody-engine-lite run --task "Test" --dry-run
|
|
134
134
|
|
|
135
|
-
# Resume from a failed stage
|
|
136
|
-
kody-engine-lite rerun --
|
|
135
|
+
# Resume from a failed/paused stage (auto-detects which stage)
|
|
136
|
+
kody-engine-lite rerun --issue-number 1
|
|
137
|
+
|
|
138
|
+
# Fix — rerun from build stage (skip taskify/plan)
|
|
139
|
+
kody-engine-lite fix --issue-number 1
|
|
140
|
+
|
|
141
|
+
# Fix with feedback
|
|
142
|
+
kody-engine-lite fix --issue-number 1 --feedback "Use middleware pattern instead"
|
|
137
143
|
|
|
138
144
|
# Check pipeline status
|
|
139
145
|
kody-engine-lite status --task-id my-task
|
|
@@ -154,9 +160,11 @@ Comment on any issue:
|
|
|
154
160
|
```
|
|
155
161
|
@kody # Run full pipeline (auto-generates task-id)
|
|
156
162
|
@kody full <task-id> # Run with specific task-id
|
|
157
|
-
@kody rerun
|
|
158
|
-
@kody rerun
|
|
159
|
-
@kody
|
|
163
|
+
@kody rerun # Resume latest task (auto-detects stage)
|
|
164
|
+
@kody rerun --from <stage> # Resume from specific stage
|
|
165
|
+
@kody fix # Re-build from build stage (skip taskify/plan)
|
|
166
|
+
@kody fix --feedback "Use X instead of Y" # Fix with specific guidance
|
|
167
|
+
@kody approve # Approve + provide answers to questions
|
|
160
168
|
@kody status <task-id> # Check status
|
|
161
169
|
```
|
|
162
170
|
|
|
@@ -195,6 +203,15 @@ Kody resumes automatically from where it paused, with your answers injected as c
|
|
|
195
203
|
| review-fix | sonnet | Applies known fixes from review findings |
|
|
196
204
|
| ship | — | Pushes branch + creates PR + comments on issue |
|
|
197
205
|
|
|
206
|
+
### Branch Syncing
|
|
207
|
+
|
|
208
|
+
On every run/rerun/fix, Kody automatically:
|
|
209
|
+
1. Checks out (or creates) the feature branch
|
|
210
|
+
2. Pulls latest from the default branch and merges into the feature branch
|
|
211
|
+
3. If there's a merge conflict, skips the sync and warns
|
|
212
|
+
|
|
213
|
+
This ensures the feature branch is always up-to-date before building.
|
|
214
|
+
|
|
198
215
|
### Automatic Loops
|
|
199
216
|
|
|
200
217
|
- **Verify + autofix**: If verify fails, runs lint-fix + format-fix + autofix agent, retries up to 2 times
|
package/dist/bin/cli.js
CHANGED
|
@@ -588,6 +588,27 @@ function ensureFeatureBranch(issueNumber, title, cwd) {
|
|
|
588
588
|
logger.info(` Created new branch: ${branchName}`);
|
|
589
589
|
return branchName;
|
|
590
590
|
}
|
|
591
|
+
function syncWithDefault(cwd) {
|
|
592
|
+
const defaultBranch = getDefaultBranch(cwd);
|
|
593
|
+
const current = getCurrentBranch(cwd);
|
|
594
|
+
if (current === defaultBranch) return;
|
|
595
|
+
try {
|
|
596
|
+
git(["fetch", "origin", defaultBranch], { cwd, timeout: 3e4 });
|
|
597
|
+
} catch {
|
|
598
|
+
logger.warn(" Failed to fetch latest from origin");
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
try {
|
|
602
|
+
git(["merge", `origin/${defaultBranch}`, "--no-edit"], { cwd, timeout: 3e4 });
|
|
603
|
+
logger.info(` Synced with origin/${defaultBranch}`);
|
|
604
|
+
} catch {
|
|
605
|
+
try {
|
|
606
|
+
git(["merge", "--abort"], { cwd });
|
|
607
|
+
} catch {
|
|
608
|
+
}
|
|
609
|
+
logger.warn(` Merge conflict with origin/${defaultBranch} \u2014 skipping sync`);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
591
612
|
function commitAll(message, cwd) {
|
|
592
613
|
const status = git(["status", "--porcelain"], { cwd });
|
|
593
614
|
if (!status) {
|
|
@@ -1197,8 +1218,9 @@ async function runPipeline(ctx) {
|
|
|
1197
1218
|
const taskMdPath = path4.join(ctx.taskDir, "task.md");
|
|
1198
1219
|
const title = fs4.existsSync(taskMdPath) ? fs4.readFileSync(taskMdPath, "utf-8").split("\n")[0].slice(0, 50) : ctx.taskId;
|
|
1199
1220
|
ensureFeatureBranch(ctx.input.issueNumber, title, ctx.projectDir);
|
|
1221
|
+
syncWithDefault(ctx.projectDir);
|
|
1200
1222
|
} catch (err) {
|
|
1201
|
-
logger.warn(` Failed to create feature branch: ${err}`);
|
|
1223
|
+
logger.warn(` Failed to create/sync feature branch: ${err}`);
|
|
1202
1224
|
}
|
|
1203
1225
|
}
|
|
1204
1226
|
let complexity = ctx.input.complexity ?? "high";
|
|
@@ -1628,12 +1650,13 @@ function parseArgs() {
|
|
|
1628
1650
|
console.log(`Usage:
|
|
1629
1651
|
kody run --task-id <id> [--task "<desc>"] [--cwd <path>] [--issue-number <n>] [--complexity low|medium|high] [--feedback "<text>"] [--local] [--dry-run]
|
|
1630
1652
|
kody rerun --task-id <id> --from <stage> [--cwd <path>] [--issue-number <n>]
|
|
1653
|
+
kody fix --task-id <id> [--cwd <path>] [--issue-number <n>] [--feedback "<text>"]
|
|
1631
1654
|
kody status --task-id <id> [--cwd <path>]
|
|
1632
1655
|
kody --help`);
|
|
1633
1656
|
process.exit(0);
|
|
1634
1657
|
}
|
|
1635
1658
|
const command2 = args2[0];
|
|
1636
|
-
if (!["run", "rerun", "status"].includes(command2)) {
|
|
1659
|
+
if (!["run", "rerun", "fix", "status"].includes(command2)) {
|
|
1637
1660
|
console.error(`Unknown command: ${command2}`);
|
|
1638
1661
|
process.exit(1);
|
|
1639
1662
|
}
|
|
@@ -1678,7 +1701,7 @@ async function main() {
|
|
|
1678
1701
|
}
|
|
1679
1702
|
let taskId = input.taskId;
|
|
1680
1703
|
if (!taskId) {
|
|
1681
|
-
if (input.command === "rerun" && input.issueNumber) {
|
|
1704
|
+
if ((input.command === "rerun" || input.command === "fix") && input.issueNumber) {
|
|
1682
1705
|
const found = findLatestTaskForIssue(input.issueNumber, projectDir);
|
|
1683
1706
|
if (!found) {
|
|
1684
1707
|
console.error(`No previous task found for issue #${input.issueNumber}`);
|
|
@@ -1724,6 +1747,9 @@ ${issue.body ?? ""}`;
|
|
|
1724
1747
|
process.exit(1);
|
|
1725
1748
|
}
|
|
1726
1749
|
}
|
|
1750
|
+
if (input.command === "fix" && !input.fromStage) {
|
|
1751
|
+
input.fromStage = "build";
|
|
1752
|
+
}
|
|
1727
1753
|
if (input.command === "rerun" && !input.fromStage) {
|
|
1728
1754
|
const statusPath = path5.join(taskDir, "status.json");
|
|
1729
1755
|
if (fs6.existsSync(statusPath)) {
|
|
@@ -1781,7 +1807,7 @@ ${issue.body ?? ""}`;
|
|
|
1781
1807
|
projectDir,
|
|
1782
1808
|
runners,
|
|
1783
1809
|
input: {
|
|
1784
|
-
mode: input.command === "rerun" ? "rerun" : "full",
|
|
1810
|
+
mode: input.command === "rerun" || input.command === "fix" ? "rerun" : "full",
|
|
1785
1811
|
fromStage: input.fromStage,
|
|
1786
1812
|
dryRun: input.dryRun,
|
|
1787
1813
|
issueNumber: input.issueNumber,
|
|
@@ -1814,6 +1840,12 @@ Artifacts in ${taskDir}:`);
|
|
|
1814
1840
|
console.log(` ${f}`);
|
|
1815
1841
|
}
|
|
1816
1842
|
if (state.state === "failed") {
|
|
1843
|
+
const isPaused = Object.values(state.stages).some(
|
|
1844
|
+
(s) => typeof s === "object" && s !== null && "error" in s && typeof s.error === "string" && s.error.includes("paused")
|
|
1845
|
+
);
|
|
1846
|
+
if (isPaused) {
|
|
1847
|
+
process.exit(0);
|
|
1848
|
+
}
|
|
1817
1849
|
if (ctx.input.issueNumber && !ctx.input.local) {
|
|
1818
1850
|
const failedStage = Object.entries(state.stages).find(
|
|
1819
1851
|
([, s]) => s.state === "failed" || s.state === "timeout"
|
|
@@ -2192,6 +2224,7 @@ ${context}`;
|
|
|
2192
2224
|
if (!config.agent.modelMap) {
|
|
2193
2225
|
config.agent.modelMap = { cheap: "haiku", mid: "sonnet", strong: "opus" };
|
|
2194
2226
|
}
|
|
2227
|
+
validateQualityCommands(cwd, config, basic.pm);
|
|
2195
2228
|
return {
|
|
2196
2229
|
config,
|
|
2197
2230
|
architecture: parsed.architecture ?? "",
|
|
@@ -2206,6 +2239,40 @@ ${context}`;
|
|
|
2206
2239
|
};
|
|
2207
2240
|
}
|
|
2208
2241
|
}
|
|
2242
|
+
function validateQualityCommands(cwd, config, pm) {
|
|
2243
|
+
let scripts = {};
|
|
2244
|
+
try {
|
|
2245
|
+
const pkg = JSON.parse(fs7.readFileSync(path6.join(cwd, "package.json"), "utf-8"));
|
|
2246
|
+
scripts = pkg.scripts ?? {};
|
|
2247
|
+
} catch {
|
|
2248
|
+
return;
|
|
2249
|
+
}
|
|
2250
|
+
const quality = config.quality ?? {};
|
|
2251
|
+
const overrides = [
|
|
2252
|
+
{ key: "typecheck", preferred: ["typecheck", "type-check"] },
|
|
2253
|
+
{ key: "lint", preferred: ["lint"] },
|
|
2254
|
+
{ key: "lintFix", preferred: ["lint:fix", "lint-fix"] },
|
|
2255
|
+
{ key: "format", preferred: ["format:check", "format-check", "prettier:check"] },
|
|
2256
|
+
{ key: "formatFix", preferred: ["format", "format:fix", "format-fix"] },
|
|
2257
|
+
{ key: "testUnit", preferred: ["test:unit", "test-unit", "test:ci"] }
|
|
2258
|
+
];
|
|
2259
|
+
for (const { key, preferred } of overrides) {
|
|
2260
|
+
const match = preferred.find((s) => scripts[s]);
|
|
2261
|
+
if (match) {
|
|
2262
|
+
const correct = `${pm} ${match}`;
|
|
2263
|
+
if (quality[key] !== correct) {
|
|
2264
|
+
quality[key] = correct;
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
if (quality[key]) {
|
|
2268
|
+
const scriptName = quality[key].replace(`${pm} `, "");
|
|
2269
|
+
if (scriptName && !scripts[scriptName] && !scriptName.includes(" ")) {
|
|
2270
|
+
quality[key] = "";
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
}
|
|
2274
|
+
config.quality = quality;
|
|
2275
|
+
}
|
|
2209
2276
|
function buildFallbackConfig(cwd, basic) {
|
|
2210
2277
|
const pkg = (() => {
|
|
2211
2278
|
try {
|
package/package.json
CHANGED
package/templates/kody.yml
CHANGED
|
@@ -103,7 +103,7 @@ jobs:
|
|
|
103
103
|
|
|
104
104
|
# Validate mode
|
|
105
105
|
case "$MODE" in
|
|
106
|
-
full|rerun|status|approve) ;;
|
|
106
|
+
full|rerun|fix|status|approve) ;;
|
|
107
107
|
*)
|
|
108
108
|
# If first arg isn't a mode, it might be a task-id or nothing
|
|
109
109
|
if [ -n "$MODE" ] && [ "$MODE" != "" ]; then
|
|
@@ -196,7 +196,15 @@ jobs:
|
|
|
196
196
|
FEEDBACK: ${{ github.event.inputs.feedback || needs.parse.outputs.feedback }}
|
|
197
197
|
DRY_RUN: ${{ github.event.inputs.dry_run || 'false' }}
|
|
198
198
|
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
199
|
-
run:
|
|
199
|
+
run: |
|
|
200
|
+
CMD="run"
|
|
201
|
+
[ "$MODE" = "rerun" ] && CMD="rerun"
|
|
202
|
+
[ "$MODE" = "fix" ] && CMD="fix"
|
|
203
|
+
ARGS="--task-id $TASK_ID --issue-number $ISSUE_NUMBER"
|
|
204
|
+
[ -n "$FROM_STAGE" ] && ARGS="$ARGS --from $FROM_STAGE"
|
|
205
|
+
[ -n "$FEEDBACK" ] && ARGS="$ARGS --feedback \"$FEEDBACK\""
|
|
206
|
+
[ "$DRY_RUN" = "true" ] && ARGS="$ARGS --dry-run"
|
|
207
|
+
kody-engine-lite $CMD $ARGS
|
|
200
208
|
|
|
201
209
|
- name: Pipeline summary
|
|
202
210
|
if: always()
|