@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 CHANGED
@@ -112,7 +112,7 @@ git push
112
112
  Then comment on any issue:
113
113
 
114
114
  ```
115
- @kody full my-task-id
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-id my-task --task "Add a sum function to src/math.ts with tests"
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-id my-task --task "Add feature X" --cwd /path/to/project
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 --task-id my-task --issue-number 1 --cwd /path/to/project
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-id my-task --task "Test" --dry-run
133
+ kody-engine-lite run --task "Test" --dry-run
134
134
 
135
- # Resume from a failed stage
136
- kody-engine-lite rerun --task-id my-task --from review
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 --from <stage> # Resume latest task from stage
158
- @kody rerun <task-id> --from <stage> # Resume specific task
159
- @kody approve # Approve + provide answers to Kody's questions
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine-lite",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "Autonomous SDLC pipeline: Kody orchestration + Claude Code + LiteLLM",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -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: kody-engine-lite run --task-id "$TASK_ID" --issue-number "$ISSUE_NUMBER"
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()