@kody-ade/kody-engine-lite 0.1.56 → 0.1.57

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/dist/bin/cli.js +93 -22
  2. package/kody.config.schema.json +2 -2
  3. package/package.json +1 -1
  4. package/prompts/autofix.md +27 -9
  5. package/prompts/review.md +83 -16
  6. package/templates/kody.yml +8 -8
  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/dist/bin/cli.js CHANGED
@@ -300,7 +300,7 @@ var init_config = __esm({
300
300
  repo: ""
301
301
  },
302
302
  paths: {
303
- taskDir: ".tasks"
303
+ taskDir: ".kody/tasks"
304
304
  },
305
305
  agent: {
306
306
  runner: "claude-code",
@@ -1101,7 +1101,6 @@ var init_agent = __esm({
1101
1101
  taskify: "explore",
1102
1102
  plan: "explore",
1103
1103
  build: "build",
1104
- autofix: "build",
1105
1104
  "review-fix": "build",
1106
1105
  review: "review"
1107
1106
  };
@@ -1601,6 +1600,20 @@ function executeShipStage(ctx, _def) {
1601
1600
  try {
1602
1601
  const head = getCurrentBranch(ctx.projectDir);
1603
1602
  const base = getDefaultBranch(ctx.projectDir);
1603
+ try {
1604
+ execFileSync7("git", ["add", ctx.taskDir], {
1605
+ cwd: ctx.projectDir,
1606
+ env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
1607
+ stdio: "pipe"
1608
+ });
1609
+ execFileSync7("git", ["commit", "--no-gpg-sign", "-m", `chore: add kody task artifacts [skip ci]`], {
1610
+ cwd: ctx.projectDir,
1611
+ env: { ...process.env, HUSKY: "0", SKIP_HOOKS: "1" },
1612
+ stdio: "pipe"
1613
+ });
1614
+ logger.info(" Committed task artifacts");
1615
+ } catch {
1616
+ }
1604
1617
  pushBranch(ctx.projectDir);
1605
1618
  const config = getProjectConfig();
1606
1619
  let owner = config.github?.owner;
@@ -1698,6 +1711,7 @@ Failed: ${msg}
1698
1711
  var init_ship = __esm({
1699
1712
  "src/stages/ship.ts"() {
1700
1713
  "use strict";
1714
+ init_logger();
1701
1715
  init_git_utils();
1702
1716
  init_github_api();
1703
1717
  init_config();
@@ -2526,7 +2540,7 @@ import * as fs16 from "fs";
2526
2540
  import * as path15 from "path";
2527
2541
  import { execFileSync as execFileSync9 } from "child_process";
2528
2542
  function findLatestTaskForIssue(issueNumber, projectDir) {
2529
- const tasksDir = path15.join(projectDir, ".tasks");
2543
+ const tasksDir = path15.join(projectDir, ".kody", "tasks");
2530
2544
  if (!fs16.existsSync(tasksDir)) return null;
2531
2545
  const allDirs = fs16.readdirSync(tasksDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name).sort().reverse();
2532
2546
  const prefix = `${issueNumber}-`;
@@ -2587,7 +2601,7 @@ Or comment on the specific PR: \`@kody review\``
2587
2601
  }
2588
2602
  async function runStandaloneReview(input) {
2589
2603
  const taskId = input.taskId ?? `review-${generateTaskId()}`;
2590
- const taskDir = path16.join(input.projectDir, ".tasks", taskId);
2604
+ const taskDir = path16.join(input.projectDir, ".kody", "tasks", taskId);
2591
2605
  fs17.mkdirSync(taskDir, { recursive: true });
2592
2606
  const taskContent = `# ${input.prTitle}
2593
2607
 
@@ -2830,7 +2844,7 @@ function resolveTaskAction(issueNumber, existingTaskId, existingState) {
2830
2844
  function resolveForIssue(issueNumber, projectDir) {
2831
2845
  const existingTaskId = findLatestTaskForIssue(issueNumber, projectDir);
2832
2846
  if (existingTaskId) {
2833
- const statusPath = path18.join(projectDir, ".tasks", existingTaskId, "status.json");
2847
+ const statusPath = path18.join(projectDir, ".kody", "tasks", existingTaskId, "status.json");
2834
2848
  let existingState = null;
2835
2849
  if (fs19.existsSync(statusPath)) {
2836
2850
  try {
@@ -2924,7 +2938,7 @@ async function main() {
2924
2938
  process.exit(1);
2925
2939
  }
2926
2940
  }
2927
- const taskDir = path19.join(projectDir, ".tasks", taskId);
2941
+ const taskDir = path19.join(projectDir, ".kody", "tasks", taskId);
2928
2942
  fs20.mkdirSync(taskDir, { recursive: true });
2929
2943
  if (input.command === "status") {
2930
2944
  printStatus(taskId, taskDir);
@@ -2963,10 +2977,12 @@ async function main() {
2963
2977
  const proxyRunning = await checkLitellmHealth(config2.agent.litellmUrl);
2964
2978
  if (!proxyRunning) {
2965
2979
  litellmProcess2 = await tryStartLitellm(config2.agent.litellmUrl, projectDir);
2980
+ if (!litellmProcess2) {
2981
+ logger.error("LiteLLM is configured (litellmUrl) but could not be started. Install it with: pip install 'litellm[proxy]'");
2982
+ process.exit(1);
2983
+ }
2966
2984
  }
2967
- if (config2.agent.litellmUrl) {
2968
- process.env.ANTHROPIC_BASE_URL = config2.agent.litellmUrl;
2969
- }
2985
+ process.env.ANTHROPIC_BASE_URL = config2.agent.litellmUrl;
2970
2986
  }
2971
2987
  const runners2 = createRunners(config2);
2972
2988
  const defaultRunnerName2 = config2.agent.defaultRunner ?? Object.keys(runners2)[0] ?? "claude";
@@ -3036,7 +3052,7 @@ ${issue.body ?? ""}`;
3036
3052
  }
3037
3053
  }
3038
3054
  if (!fs20.existsSync(taskMdPath)) {
3039
- console.error("No task.md found. Provide --task, --issue-number, or ensure .tasks/<id>/task.md exists.");
3055
+ console.error("No task.md found. Provide --task, --issue-number, or ensure .kody/tasks/<id>/task.md exists.");
3040
3056
  process.exit(1);
3041
3057
  }
3042
3058
  if (input.command === "fix" && !input.fromStage) {
@@ -3078,16 +3094,14 @@ ${input.feedback}` : reviewContext;
3078
3094
  if (!proxyRunning) {
3079
3095
  litellmProcess = await tryStartLitellm(config.agent.litellmUrl, projectDir);
3080
3096
  if (!litellmProcess) {
3081
- logger.warn("LiteLLM not available \u2014 falling back to Anthropic models");
3082
- config.agent.litellmUrl = void 0;
3097
+ logger.error("LiteLLM is configured (litellmUrl) but could not be started. Install it with: pip install 'litellm[proxy]'");
3098
+ process.exit(1);
3083
3099
  }
3084
3100
  } else {
3085
3101
  logger.info(`LiteLLM proxy already running at ${config.agent.litellmUrl}`);
3086
3102
  }
3087
- if (config.agent.litellmUrl) {
3088
- process.env.ANTHROPIC_BASE_URL = config.agent.litellmUrl;
3089
- logger.info(`ANTHROPIC_BASE_URL set to ${config.agent.litellmUrl}`);
3090
- }
3103
+ process.env.ANTHROPIC_BASE_URL = config.agent.litellmUrl;
3104
+ logger.info(`ANTHROPIC_BASE_URL set to ${config.agent.litellmUrl}`);
3091
3105
  }
3092
3106
  const runners = createRunners(config);
3093
3107
  const defaultRunnerName = config.agent.defaultRunner ?? Object.keys(runners)[0] ?? "claude";
@@ -3362,7 +3376,7 @@ function buildConfig(cwd, basic) {
3362
3376
  },
3363
3377
  git: { defaultBranch: basic.defaultBranch },
3364
3378
  github: { owner: basic.owner, repo: basic.repo },
3365
- paths: { taskDir: ".tasks" },
3379
+ paths: { taskDir: ".kody/tasks" },
3366
3380
  agent: {
3367
3381
  runner: "claude-code",
3368
3382
  defaultRunner: "claude",
@@ -3404,11 +3418,12 @@ function initCommand(opts) {
3404
3418
  const gitignorePath = path20.join(cwd, ".gitignore");
3405
3419
  if (fs21.existsSync(gitignorePath)) {
3406
3420
  const content = fs21.readFileSync(gitignorePath, "utf-8");
3407
- if (!content.includes(".tasks/")) {
3408
- fs21.appendFileSync(gitignorePath, "\n.tasks/\n");
3409
- console.log(" \u2713 .gitignore (added .tasks/)");
3421
+ if (content.includes(".tasks/")) {
3422
+ const updated = content.replace(/\n?\.tasks\/\n?/g, "\n");
3423
+ fs21.writeFileSync(gitignorePath, updated);
3424
+ console.log(" \u2713 .gitignore (removed legacy .tasks/ \u2014 tasks now committed in .kody/tasks/)");
3410
3425
  } else {
3411
- console.log(" \u25CB .gitignore (.tasks/ already present)");
3426
+ console.log(" \u25CB .gitignore (ok)");
3412
3427
  }
3413
3428
  }
3414
3429
  console.log("\n\u2500\u2500 Prerequisites \u2500\u2500");
@@ -3589,7 +3604,7 @@ function initCommand(opts) {
3589
3604
  console.log("");
3590
3605
  }
3591
3606
  }
3592
- var STEP_STAGES = ["taskify", "plan", "build", "autofix", "review", "review-fix"];
3607
+ var STEP_STAGES = ["taskify", "plan", "build", "review", "review-fix"];
3593
3608
  function gatherSampleSourceFiles(cwd, maxFiles = 3, maxCharsEach = 2e3) {
3594
3609
  const srcDir = path20.join(cwd, "src");
3595
3610
  const baseDir = fs21.existsSync(srcDir) ? srcDir : cwd;
@@ -3627,11 +3642,46 @@ ${content}
3627
3642
  }
3628
3643
  return results.join("\n\n");
3629
3644
  }
3645
+ function ghComment(issueNumber, body, cwd) {
3646
+ try {
3647
+ let repoSlug = "";
3648
+ try {
3649
+ const configPath = path20.join(cwd, "kody.config.json");
3650
+ if (fs21.existsSync(configPath)) {
3651
+ const config = JSON.parse(fs21.readFileSync(configPath, "utf-8"));
3652
+ if (config.github?.owner && config.github?.repo) {
3653
+ repoSlug = `${config.github.owner}/${config.github.repo}`;
3654
+ }
3655
+ }
3656
+ } catch {
3657
+ }
3658
+ if (!repoSlug) return;
3659
+ execFileSync11("gh", [
3660
+ "issue",
3661
+ "comment",
3662
+ String(issueNumber),
3663
+ "--repo",
3664
+ repoSlug,
3665
+ "--body",
3666
+ body
3667
+ ], {
3668
+ cwd,
3669
+ encoding: "utf-8",
3670
+ timeout: 15e3,
3671
+ stdio: ["pipe", "pipe", "pipe"]
3672
+ });
3673
+ } catch {
3674
+ }
3675
+ }
3630
3676
  function bootstrapCommand() {
3631
3677
  const cwd = process.cwd();
3678
+ const issueNumber = parseInt(process.env.ISSUE_NUMBER ?? "", 10) || 0;
3632
3679
  console.log(`
3633
3680
  \u{1F527} Kody Bootstrap \u2014 Generating project memory + step files
3634
3681
  `);
3682
+ if (issueNumber) {
3683
+ ghComment(issueNumber, "\u{1F527} **Bootstrap started** \u2014 analyzing project and generating configuration...", cwd);
3684
+ }
3635
3685
  const readIfExists = (rel, maxChars = 3e3) => {
3636
3686
  const p = path20.join(cwd, rel);
3637
3687
  if (fs21.existsSync(p)) return fs21.readFileSync(p, "utf-8").slice(0, maxChars);
@@ -3911,8 +3961,16 @@ REMINDER: Output the full prompt template first (unchanged), then your three app
3911
3961
  stdio: ["pipe", "pipe", "pipe"]
3912
3962
  }).trim();
3913
3963
  console.log(` \u2713 Created PR: ${prUrl}`);
3964
+ if (issueNumber) {
3965
+ ghComment(issueNumber, `\u2705 **Bootstrap complete** \u2014 PR created: ${prUrl}
3966
+
3967
+ Review and merge to activate project-specific pipeline configuration.`, cwd);
3968
+ }
3914
3969
  } catch (prErr) {
3915
3970
  console.log(` \u25CB PR creation failed: ${prErr instanceof Error ? prErr.message : prErr}`);
3971
+ if (issueNumber) {
3972
+ ghComment(issueNumber, `\u26A0\uFE0F **Bootstrap complete** \u2014 files generated and pushed to branch \`${branchName}\`, but PR creation failed. Create it manually.`, cwd);
3973
+ }
3916
3974
  }
3917
3975
  } else {
3918
3976
  console.log(" \u25CB No new changes to commit");
@@ -3935,6 +3993,9 @@ REMINDER: Output the full prompt template first (unchanged), then your three app
3935
3993
  }
3936
3994
  } catch (err) {
3937
3995
  console.log(` \u25CB Git commit skipped: ${err instanceof Error ? err.message : err}`);
3996
+ if (issueNumber) {
3997
+ ghComment(issueNumber, `\u274C **Bootstrap failed** \u2014 git operation error: ${err instanceof Error ? err.message : err}`, cwd);
3998
+ }
3938
3999
  }
3939
4000
  }
3940
4001
  console.log("\n\u2500\u2500 Done \u2500\u2500");
@@ -3982,3 +4043,13 @@ if (command === "init") {
3982
4043
  } else {
3983
4044
  Promise.resolve().then(() => init_entry());
3984
4045
  }
4046
+ export {
4047
+ buildConfig,
4048
+ checkCommand2 as checkCommand,
4049
+ checkFile,
4050
+ checkGhAuth,
4051
+ checkGhRepoAccess,
4052
+ checkGhSecret,
4053
+ detectArchitectureBasic,
4054
+ detectBasicConfig
4055
+ };
@@ -82,8 +82,8 @@
82
82
  "properties": {
83
83
  "taskDir": {
84
84
  "type": "string",
85
- "description": "Directory for pipeline artifacts and state (gitignored)",
86
- "default": ".tasks"
85
+ "description": "Directory for pipeline artifacts and state (committed to branch)",
86
+ "default": ".kody/tasks"
87
87
  }
88
88
  },
89
89
  "additionalProperties": false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine-lite",
3
- "version": "0.1.56",
3
+ "version": "0.1.57",
4
4
  "description": "Autonomous SDLC pipeline: Kody orchestration + Claude Code + LiteLLM",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,21 +1,39 @@
1
1
  ---
2
2
  name: autofix
3
- description: Fix verification errors (typecheck, lint, test failures)
3
+ description: Investigate root cause then fix verification errors (typecheck, lint, test failures)
4
4
  mode: primary
5
5
  tools: [read, write, edit, bash, glob, grep]
6
6
  ---
7
7
 
8
8
  You are an autofix agent. The verification stage failed. Fix the errors below.
9
9
 
10
- STRATEGY (in order):
11
- 1. Try quick wins first: run `pnpm lint:fix` and `pnpm format:fix` via Bash
12
- 2. Read the error output carefully understand WHAT failed and WHY
13
- 3. For type errors: Read the affected file, fix the type mismatch
14
- 4. For test failures: Read both the test and the implementation, fix the root cause
15
- 5. For lint errors: Apply the specific fix the linter suggests
10
+ IRON LAW: NO FIXES WITHOUT INVESTIGATION FIRST. Do not jump to changing code. Understand the failure first.
11
+
12
+ ## Phase 1 Investigate (do this BEFORE any edits)
13
+ 1. Read the full error output what exactly failed?
14
+ 2. Identify the affected files Read them to understand context
15
+ 3. Check recent changes: run `git diff HEAD~1` to see what changed
16
+ 4. Classify the failure pattern:
17
+ - **Type error**: mismatched types, missing properties, wrong generics
18
+ - **Test failure**: assertion mismatch, missing mock, changed behavior
19
+ - **Lint error**: style violation, unused import, naming convention
20
+ - **Runtime error**: null reference, missing dependency, config issue
21
+ - **Integration failure**: API contract mismatch, schema drift
22
+ 5. Identify root cause — is this a direct error in new code, or a side effect of a change elsewhere?
23
+
24
+ ## Phase 2 — Fix (only after root cause is clear)
25
+ 1. Try quick wins first: run configured lintFix and formatFix commands via Bash
26
+ 2. For type errors: fix the type mismatch at its source, not by adding type assertions
27
+ 3. For test failures: fix the root cause (implementation or test), not both — determine which is correct
28
+ 4. For lint errors: apply the specific fix the linter suggests
29
+ 5. For integration failures: trace the contract back to its definition, fix the mismatch at source
16
30
  6. After EACH fix, re-run the failing command to verify it passes
17
- 7. Do NOT commit or push the orchestrator handles git
31
+ 7. If a fix introduces new failures, REVERT and try a different approach
32
+ 8. Do NOT commit or push — the orchestrator handles git
18
33
 
19
- Do NOT make unrelated changes. Fix ONLY the reported errors.
34
+ ## Rules
35
+ - Fix ONLY the reported errors. Do NOT make unrelated changes.
36
+ - Minimal diff — use Edit for surgical changes, not Write for rewrites
37
+ - If the failure is pre-existing (not caused by this PR's changes), document it and move on
20
38
 
21
39
  {{TASK_CONTEXT}}
package/prompts/review.md CHANGED
@@ -5,9 +5,10 @@ mode: primary
5
5
  tools: [read, glob, grep, bash]
6
6
  ---
7
7
 
8
- You are a code review agent. Review all changes made for the task described below.
8
+ You are a code review agent following the Superpowers Structured Review methodology.
9
9
 
10
10
  Use Bash to run `git diff` to see what changed. Use Read to examine modified files in full context.
11
+ When the diff introduces new enum values, status strings, or type constants — use Grep to trace ALL consumers outside the diff.
11
12
 
12
13
  CRITICAL: You MUST output a structured review in the EXACT format below. Do NOT output conversational text, status updates, or summaries. Your entire output must be the structured review markdown.
13
14
 
@@ -21,28 +22,94 @@ Output markdown with this EXACT structure:
21
22
  ## Findings
22
23
 
23
24
  ### Critical
24
- <Security vulnerabilities, data loss risks, crashes, broken authentication>
25
25
  <If none: "None.">
26
26
 
27
27
  ### Major
28
- <Logic errors, missing edge cases, broken tests, significant performance issues, missing error handling>
29
28
  <If none: "None.">
30
29
 
31
30
  ### Minor
32
- <Style issues, naming improvements, readability, trivial performance, minor refactoring opportunities>
33
31
  <If none: "None.">
34
32
 
35
- Severity definitions:
36
- - **Critical**: Security vulnerability, data loss, application crash, broken authentication, injection risk. MUST fix before merge.
37
- - **Major**: Logic error, missing edge case, broken test, significant performance issue, missing input validation. SHOULD fix before merge.
38
- - **Minor**: Style issue, naming improvement, readability, micro-optimization. NICE to fix, not blocking.
39
-
40
- Review checklist:
41
- - [ ] Does the code match the plan?
42
- - [ ] Are edge cases handled?
43
- - [ ] Are there security concerns?
44
- - [ ] Are tests adequate?
45
- - [ ] Is error handling proper?
46
- - [ ] Are there any hardcoded values that should be configurable?
33
+ For each finding use: `file:line` — problem description. Suggested fix.
34
+
35
+ ---
36
+
37
+ ## Two-Pass Review
38
+
39
+ **Pass 1 CRITICAL (must fix before merge):**
40
+
41
+ ### SQL & Data Safety
42
+ - String interpolation in SQL — use parameterized queries even for `.to_i`/`.to_f` values
43
+ - TOCTOU races: check-then-set patterns that should be atomic `WHERE` + update
44
+ - Bypassing model validations via direct DB writes (e.g., `update_column`, raw queries)
45
+ - N+1 queries: missing eager loading for associations used in loops/views
46
+
47
+ ### Race Conditions & Concurrency
48
+ - Read-check-write without uniqueness constraint or duplicate key handling
49
+ - find-or-create without unique DB index — concurrent calls create duplicates
50
+ - Status transitions without atomic `WHERE old_status = ? UPDATE SET new_status`
51
+ - Unsafe HTML rendering (`dangerouslySetInnerHTML`, `v-html`, `.html_safe`) on user-controlled data (XSS)
52
+
53
+ ### LLM Output Trust Boundary
54
+ - LLM-generated values (emails, URLs, names) written to DB without format validation
55
+ - Structured tool output accepted without type/shape checks before DB writes
56
+ - LLM-generated URLs fetched without allowlist — SSRF risk
57
+ - LLM output stored in vector DBs without sanitization — stored prompt injection risk
58
+
59
+ ### Shell Injection
60
+ - `subprocess.run()` / `os.system()` with `shell=True` AND string interpolation — use argument arrays
61
+ - `eval()` / `exec()` on LLM-generated code without sandboxing
62
+
63
+ ### Enum & Value Completeness
64
+ When the diff introduces a new enum value, status string, tier name, or type constant:
65
+ - Trace it through every consumer (READ each file that switches/filters on that value)
66
+ - Check allowlists/filter arrays containing sibling values
67
+ - Check `case`/`if-elsif` chains — does the new value fall through to a wrong default?
68
+
69
+ **Pass 2 — INFORMATIONAL (should review, may auto-fix):**
70
+
71
+ ### Conditional Side Effects
72
+ - Code paths that branch but forget a side effect on one branch (e.g., promoted but URL only attached conditionally)
73
+ - Log messages claiming an action happened when it was conditionally skipped
74
+
75
+ ### Test Gaps
76
+ - Negative-path tests asserting type/status but not side effects
77
+ - Security enforcement features (blocking, rate limiting, auth) without integration tests
78
+ - Missing `.expects(:something).never` when a path should NOT call an external service
79
+
80
+ ### Dead Code & Consistency
81
+ - Variables assigned but never read
82
+ - Comments/docstrings describing old behavior after code changed
83
+ - Version mismatch between PR title and VERSION/CHANGELOG
84
+
85
+ ### Crypto & Entropy
86
+ - Truncation instead of hashing — less entropy, easier collisions
87
+ - `rand()` / `Math.random()` for security-sensitive values — use crypto-secure alternatives
88
+ - Non-constant-time comparisons (`==`) on secrets or tokens — timing attack risk
89
+
90
+ ### Performance & Bundle Impact
91
+ - Known-heavy dependencies added: moment.js (→ date-fns), full lodash (→ lodash-es), jquery
92
+ - Images without `loading="lazy"` or explicit dimensions (CLS)
93
+ - `useEffect` fetch waterfalls — combine or parallelize
94
+ - Synchronous `<script>` without async/defer
95
+
96
+ ### Type Coercion at Boundaries
97
+ - Values crossing language/serialization boundaries where type could change (numeric vs string)
98
+ - Hash/digest inputs without `.toString()` normalization before serialization
99
+
100
+ ---
101
+
102
+ ## Severity Definitions
103
+
104
+ - **Critical**: Security vulnerability, data loss, application crash, broken authentication, injection risk, race condition. MUST fix before merge.
105
+ - **Major**: Logic error, missing edge case, broken test, significant performance issue, missing input validation, enum completeness gap. SHOULD fix before merge.
106
+ - **Minor**: Style issue, naming improvement, readability, micro-optimization, stale comments. NICE to fix, not blocking.
107
+
108
+ ## Suppressions — do NOT flag these:
109
+ - Redundancy that aids readability
110
+ - "Add a comment explaining this threshold" — thresholds change, comments rot
111
+ - Consistency-only changes with no behavioral impact
112
+ - Issues already addressed in the diff you are reviewing — read the FULL diff first
113
+ - devDependencies additions (no production impact)
47
114
 
48
115
  {{TASK_CONTEXT}}
@@ -254,7 +254,7 @@ jobs:
254
254
  [ -n "$TASK_ID" ] && ARGS="$ARGS --task-id $TASK_ID"
255
255
  [ -n "$PR_NUMBER" ] && ARGS="$ARGS --pr-number $PR_NUMBER"
256
256
  [ -n "$FROM_STAGE" ] && ARGS="$ARGS --from $FROM_STAGE"
257
- [ -n "$FEEDBACK" ] && ARGS="$ARGS --feedback \"$FEEDBACK\""
257
+ # FEEDBACK is passed via env var, not CLI arg (avoids shell escaping issues)
258
258
  [ "$DRY_RUN" = "true" ] && ARGS="$ARGS --dry-run"
259
259
  kody-engine-lite $CMD $ARGS
260
260
  fi
@@ -263,7 +263,7 @@ jobs:
263
263
  if: always()
264
264
  run: |
265
265
  TASK_ID="${{ github.event.inputs.task_id || needs.parse.outputs.task_id }}"
266
- STATUS_FILE=".tasks/${TASK_ID}/status.json"
266
+ STATUS_FILE=".kody/tasks/${TASK_ID}/status.json"
267
267
  if [ -f "$STATUS_FILE" ]; then
268
268
  STATE=$(jq -r '.state' "$STATUS_FILE")
269
269
  ICON="❌"
@@ -292,7 +292,7 @@ jobs:
292
292
  uses: actions/upload-artifact@v4
293
293
  with:
294
294
  name: kody-tasks-${{ github.event.inputs.task_id || needs.parse.outputs.task_id }}
295
- path: .tasks/
295
+ path: .kody/tasks/
296
296
  retention-days: 7
297
297
 
298
298
  # ─── Error Notifications ─────────────────────────────────────────────────────
@@ -355,11 +355,11 @@ jobs:
355
355
  run: kody-engine-lite --help
356
356
  - name: Dry run
357
357
  run: |
358
- mkdir -p .tasks/smoke-test
359
- echo "Smoke test task" > .tasks/smoke-test/task.md
358
+ mkdir -p .kody/tasks/smoke-test
359
+ echo "Smoke test task" > .kody/tasks/smoke-test/task.md
360
360
  kody-engine-lite run --task-id smoke-test --dry-run || true
361
- if [ -f ".tasks/smoke-test/status.json" ]; then
361
+ if [ -f ".kody/tasks/smoke-test/status.json" ]; then
362
362
  echo "✓ status.json created"
363
- cat .tasks/smoke-test/status.json
363
+ cat .kody/tasks/smoke-test/status.json
364
364
  fi
365
- rm -rf .tasks/smoke-test
365
+ rm -rf .kody/tasks/smoke-test
@@ -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 {};