@kody-ade/kody-engine-lite 0.1.71 → 0.1.73

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 (71) hide show
  1. package/README.md +32 -92
  2. package/dist/bin/cli.js +54 -9
  3. package/package.json +1 -1
  4. package/dist/agent-runner.d.ts +0 -4
  5. package/dist/agent-runner.js +0 -122
  6. package/dist/ci/parse-inputs.d.ts +0 -6
  7. package/dist/ci/parse-inputs.js +0 -76
  8. package/dist/ci/parse-safety.d.ts +0 -6
  9. package/dist/ci/parse-safety.js +0 -22
  10. package/dist/cli/args.d.ts +0 -13
  11. package/dist/cli/args.js +0 -42
  12. package/dist/cli/litellm.d.ts +0 -2
  13. package/dist/cli/litellm.js +0 -85
  14. package/dist/cli/task-resolution.d.ts +0 -2
  15. package/dist/cli/task-resolution.js +0 -41
  16. package/dist/config.d.ts +0 -49
  17. package/dist/config.js +0 -72
  18. package/dist/context.d.ts +0 -4
  19. package/dist/context.js +0 -83
  20. package/dist/definitions.d.ts +0 -3
  21. package/dist/definitions.js +0 -59
  22. package/dist/entry.d.ts +0 -1
  23. package/dist/entry.js +0 -236
  24. package/dist/git-utils.d.ts +0 -13
  25. package/dist/git-utils.js +0 -174
  26. package/dist/github-api.d.ts +0 -14
  27. package/dist/github-api.js +0 -114
  28. package/dist/kody-utils.d.ts +0 -1
  29. package/dist/kody-utils.js +0 -9
  30. package/dist/learning/auto-learn.d.ts +0 -2
  31. package/dist/learning/auto-learn.js +0 -169
  32. package/dist/logger.d.ts +0 -14
  33. package/dist/logger.js +0 -51
  34. package/dist/memory.d.ts +0 -1
  35. package/dist/memory.js +0 -20
  36. package/dist/observer.d.ts +0 -9
  37. package/dist/observer.js +0 -80
  38. package/dist/pipeline/complexity.d.ts +0 -3
  39. package/dist/pipeline/complexity.js +0 -12
  40. package/dist/pipeline/executor-registry.d.ts +0 -3
  41. package/dist/pipeline/executor-registry.js +0 -20
  42. package/dist/pipeline/hooks.d.ts +0 -17
  43. package/dist/pipeline/hooks.js +0 -110
  44. package/dist/pipeline/questions.d.ts +0 -2
  45. package/dist/pipeline/questions.js +0 -44
  46. package/dist/pipeline/runner-selection.d.ts +0 -2
  47. package/dist/pipeline/runner-selection.js +0 -13
  48. package/dist/pipeline/state.d.ts +0 -4
  49. package/dist/pipeline/state.js +0 -37
  50. package/dist/pipeline.d.ts +0 -3
  51. package/dist/pipeline.js +0 -213
  52. package/dist/preflight.d.ts +0 -1
  53. package/dist/preflight.js +0 -69
  54. package/dist/retrospective.d.ts +0 -26
  55. package/dist/retrospective.js +0 -211
  56. package/dist/stages/agent.d.ts +0 -2
  57. package/dist/stages/agent.js +0 -94
  58. package/dist/stages/gate.d.ts +0 -2
  59. package/dist/stages/gate.js +0 -32
  60. package/dist/stages/review.d.ts +0 -2
  61. package/dist/stages/review.js +0 -32
  62. package/dist/stages/ship.d.ts +0 -3
  63. package/dist/stages/ship.js +0 -154
  64. package/dist/stages/verify.d.ts +0 -2
  65. package/dist/stages/verify.js +0 -94
  66. package/dist/types.d.ts +0 -61
  67. package/dist/types.js +0 -1
  68. package/dist/validators.d.ts +0 -8
  69. package/dist/validators.js +0 -42
  70. package/dist/verify-runner.d.ts +0 -11
  71. package/dist/verify-runner.js +0 -110
package/README.md CHANGED
@@ -7,25 +7,19 @@
7
7
 
8
8
  Kody is a 7-stage autonomous SDLC pipeline that runs in GitHub Actions. It uses Claude Code (or any LLM via LiteLLM) to turn issues into production-ready PRs — with quality gates, AI-powered failure diagnosis, risk-based human approval, and shared context between stages.
9
9
 
10
+ > **Kody is the only AI coding tool that generates repo-customized prompts.** Every other tool sends the same generic instructions regardless of your codebase. Kody analyzes your repo's patterns, conventions, and gaps — then generates tailored instruction files for every pipeline stage. The AI writes code that looks like it belongs in your project because it was taught *from* your project. [Learn more →](docs/FEATURES.md#repo-aware-step-files-kodysteps)
11
+
10
12
  ## Why Kody?
11
13
 
12
14
  Most AI coding tools are **autocomplete** (Copilot) or **chat-based** (Cursor, Cline). You still drive. Kody is an **autonomous pipeline** — comment `@kody`, walk away, come back to a PR.
13
15
 
14
- | | Kody | Copilot Workspace | Devin | Cursor Agent |
15
- |---|---|---|---|---|
16
- | **Repo-aware instructions** | AI-customized per-stage prompts with your repo's patterns, gaps, and acceptance criteria | Generic prompts | Generic prompts | Generic prompts |
17
- | **Runs in CI** | GitHub Actions | GitHub Cloud | Devin Cloud | Local IDE |
18
- | **Fire and forget** | Yes | No interactive | Partially | No — IDE must be open |
19
- | **Pipeline stages** | 7 stages with quality gates | Plan → implement | Single agent | Single agent |
20
- | **Shared sessions** | Stages share Claude Code sessions (no cold starts) | Single conversation | Single conversation | Single conversation |
21
- | **Risk gate** | Pauses HIGH-risk for human approval | No | No | No |
22
- | **AI failure diagnosis** | Classifies errors before retry (fixable/infra/abort) | No | No | No |
23
- | **Incremental codebase improvement** | Step files encode gaps to fix — every task raises quality | No | No | No |
24
- | **Model flexible** | Any LLM via LiteLLM | GitHub models only | Proprietary | Cursor models |
25
- | **Open source** | MIT | Proprietary | Proprietary | Proprietary |
26
- | **Cost** | **Free** with free-tier models (Gemini, etc.) or pay-per-use with any LLM | $10-39/month | $20-500/month | Subscription |
27
-
28
- [Full comparison →](docs/COMPARISON.md)
16
+ - **Repo-aware prompts** auto-generated step files with your repo's patterns, gaps, and acceptance criteria
17
+ - **7 stages with quality gates** — not a single agent conversation
18
+ - **Fire and forget** runs in GitHub Actions, no IDE required
19
+ - **Any LLM** route through LiteLLM to use MiniMax, GPT, Gemini, or local models
20
+ - **Free** with free-tier modelsno subscriptions, no per-seat pricing
21
+
22
+ [How Kody compares to Copilot, Devin, Cursor, OpenHands, and others →](docs/COMPARISON.md)
29
23
 
30
24
  ## Pipeline
31
25
 
@@ -147,99 +141,44 @@ Set the `provider` field in `kody.config.json` — Kody auto-generates the LiteL
147
141
 
148
142
  Add the provider's API key to `.env`:
149
143
  ```
150
- MINIMAX_API_KEY=your-key-here
144
+ ANTHROPIC_COMPATIBLE_API_KEY=your-key-here
151
145
  ```
152
146
 
153
- That's it. Kody auto-starts the LiteLLM proxy and loads API keys from `.env`. For advanced routing (different models per tier, custom config), add a `litellm-config.yaml`. [Full LiteLLM guide →](docs/LITELLM.md)
147
+ That's it. Kody auto-starts the LiteLLM proxy and loads API keys from `.env`. For per-tier model control, configure `modelMap` in `kody.config.json`. [Full LiteLLM guide →](docs/LITELLM.md)
154
148
 
155
149
  ## Commands
156
150
 
157
- ### GitHub Comments
158
-
159
151
  | Command | What it does |
160
152
  |---------|-------------|
161
- | `@kody` | Run full pipeline |
153
+ | `@kody` | Run full pipeline on an issue |
162
154
  | `@kody approve` | Resume after questions or risk gate |
163
- | `@kody fix` | Re-run from build stage. Write feedback in the comment body — it gets injected into the build prompt |
155
+ | `@kody fix` | Re-run from build. Reads PR review comments as context |
156
+ | `@kody fix-ci` | Fix failing CI checks (auto-triggered on Kody PRs) |
164
157
  | `@kody rerun` | Resume from the failed or paused stage |
165
- | `@kody rerun --from <stage>` | Resume from a specific stage |
166
158
  | `@kody bootstrap` | Regenerate project memory and step files |
167
159
 
168
- ### CLI
169
-
170
160
  ```bash
171
- kody-engine-lite init [--force] # Setup repo: workflow, config, memory, step files
172
- kody-engine-lite bootstrap # Regenerate memory + step files (runs in GH Actions)
173
- kody-engine-lite run --issue-number 42 --local --cwd ./project
174
- kody-engine-lite run --task "Add retry utility" --local
161
+ kody-engine-lite init [--force] # Setup repo
162
+ kody-engine-lite run --issue-number 42 --local
175
163
  kody-engine-lite fix --issue-number 42 --feedback "Use middleware pattern"
164
+ kody-engine-lite fix-ci --pr-number 42
176
165
  kody-engine-lite rerun --issue-number 42 --from verify
177
- kody-engine-lite status --task-id 42-260327-102254
178
166
  ```
179
167
 
180
- ## Key Features
181
-
182
- ### Repo-Aware Step Files (`.kody/steps/`)
183
-
184
- **This is what separates Kody from every other AI coding tool.**
185
-
186
- Most AI tools send the same generic prompt regardless of whether they're working on a Next.js app, a Go microservice, or a Python ML pipeline. Kody generates **customized instruction files for every pipeline stage**, tailored to your specific repository.
168
+ [Full CLI reference with all flags and options →](docs/CLI.md)
187
169
 
188
- During `kody init`, Kody analyzes your codebase — your frameworks, patterns, conventions, file structure, even your anti-patterns — and produces 6 step files in `.kody/steps/`:
189
-
190
- | Step File | What It Controls |
191
- |-----------|-----------------|
192
- | `taskify.md` | How tasks are classified and scoped for this repo |
193
- | `plan.md` | Planning guidelines with your repo's architecture in mind |
194
- | `build.md` | Coding instructions with your actual patterns as examples |
195
- | `autofix.md` | How to fix verification failures using your toolchain |
196
- | `review.md` | Review checklist calibrated to your repo's quality bar |
197
- | `review-fix.md` | How to address review findings in your codebase |
198
-
199
- Each step file contains three repo-specific sections:
200
- - **Repo Patterns** — real code examples from your codebase (file paths, function signatures, snippets) showing what "good" looks like in your project
201
- - **Improvement Areas** — gaps, anti-patterns, and inconsistencies the AI should fix when it touches related code (e.g., "collections missing access control", "services not using DI")
202
- - **Acceptance Criteria** — a concrete checklist that defines "done" for each stage in your specific repo
203
-
204
- **Why this matters:**
205
- - Code produced by Kody **matches your existing patterns** — same naming, same abstractions, same file organization
206
- - Known gaps get **incrementally fixed** — every task that touches a weak area leaves it better
207
- - Quality bar is **explicit and auditable** — acceptance criteria are version-controlled in your repo, not hidden in a prompt
208
- - **You own and customize** the instructions — edit `.kody/steps/build.md` to change how Kody writes code, no engine changes needed
209
- - Re-run `kody init --force` after major refactors to **regenerate** step files from current state
210
-
211
- ```
212
- # Example: .kody/steps/build.md (auto-generated, then customizable)
213
- ---
214
- name: build
215
- ...
216
- ---
217
- <original engine prompt preserved>
218
-
219
- ## Repo Patterns
220
- **Collection pattern** (`src/collections/certificates.ts`):
221
- - Always cast `relationTo` with `as CollectionSlug`
222
- - Register every new collection in `src/payload.config.ts`
223
-
224
- ## Improvement Areas
225
- - Collections missing access control — add `access` guards when touching
226
- - Run `pnpm payload generate:types` after any schema change
227
-
228
- ## Acceptance Criteria
229
- - [ ] `pnpm tsc --noEmit` exits with zero errors
230
- - [ ] All new collections include explicit access control
231
- - [ ] User-supplied strings sanitized via `src/security/sanitizers.ts`
232
- ```
233
-
234
- ### Other Features
170
+ ## Key Features
235
171
 
236
- - **Shared Sessions** — stages in the same group share a Claude Code session, eliminating cold-start codebase re-exploration ([details](docs/FEATURES.md#shared-sessions))
237
- - **Risk Gate** — HIGH-risk tasks pause for human plan approval before building ([details](docs/FEATURES.md#risk-gate))
238
- - **AI Failure Diagnosis** — classifies errors as fixable/infrastructure/pre-existing/abort before retry ([details](docs/FEATURES.md#ai-powered-failure-diagnosis))
239
- - **Question Gates** — asks product/architecture questions when the task is unclear ([details](docs/FEATURES.md#question-gates))
240
- - **Any LLM** — route through LiteLLM to use MiniMax, GPT, Gemini, local models ([setup guide](docs/LITELLM.md))
241
- - **Retrospective** — analyzes each run, identifies patterns, suggests improvements ([details](docs/FEATURES.md#retrospective-system))
242
- - **Auto-Learning** — extracts coding conventions from each successful run ([details](docs/FEATURES.md#auto-learning-memory))
172
+ - **[Repo-Aware Step Files](docs/FEATURES.md#repo-aware-step-files-kodysteps)** — auto-generated per-stage instructions grounded in your actual code patterns, gaps, and acceptance criteria. Edit `.kody/steps/*.md` to customize how Kody works in your repo.
173
+ - **[Shared Sessions](docs/FEATURES.md#shared-sessions)** — stages in the same group share a Claude Code session, eliminating cold-start re-exploration
174
+ - **[Risk Gate](docs/FEATURES.md#risk-gate)** — HIGH-risk tasks pause for human approval before building
175
+ - **[AI Failure Diagnosis](docs/FEATURES.md#ai-powered-failure-diagnosis)** — classifies errors (fixable/infrastructure/pre-existing/abort) before retry
176
+ - **[Question Gates](docs/FEATURES.md#question-gates)** — asks product/architecture questions when the task is unclear
177
+ - **[Any LLM](docs/LITELLM.md)** — route through LiteLLM to use MiniMax, GPT, Gemini, local models
178
+ - **[Retrospective](docs/FEATURES.md#retrospective-system)** — analyzes each run, identifies patterns, suggests improvements
179
+ - **[Auto-Learning](docs/FEATURES.md#auto-learning-memory)** — extracts coding conventions from each successful run
180
+ - **[Pattern Discovery](docs/FEATURES.md#pattern-discovery)** — plan stage searches for existing patterns before proposing new approaches
181
+ - **[Decision Memory](docs/FEATURES.md#decision-memory)** — architectural decisions from code reviews are saved and enforced in future tasks
243
182
 
244
183
  ## Documentation
245
184
 
@@ -247,9 +186,10 @@ name: build
247
186
  |-----|-------------|
248
187
  | [Pipeline](docs/PIPELINE.md) | Stage details, shared sessions, complexity skipping, artifacts |
249
188
  | [Bootstrap](docs/BOOTSTRAP.md) | Project memory, step files, labels — what bootstrap generates and when to run it |
250
- | [Features](docs/FEATURES.md) | Risk gate, diagnosis, sessions, retrospective, auto-learn, labels |
189
+ | [Features](docs/FEATURES.md) | Risk gate, diagnosis, sessions, retrospective, auto-learn, pattern discovery, decision memory, PR feedback |
251
190
  | [LiteLLM](docs/LITELLM.md) | Non-Anthropic model setup, auto-start, tested providers |
252
- | [Configuration](docs/CONFIGURATION.md) | Full config reference, env vars, workflow setup |
191
+ | [CLI](docs/CLI.md) | Full command reference — all flags, env vars, and examples |
192
+ | [Configuration](docs/CONFIGURATION.md) | Config file reference, env vars, workflow setup |
253
193
  | [Comparison](docs/COMPARISON.md) | vs Copilot, Devin, Cursor, Cline, OpenHands, SWE-agent |
254
194
  | [Architecture](docs/ARCHITECTURE.md) | Source tree, state machine diagram, development guide |
255
195
  | [FAQ](docs/FAQ.md) | Common questions about usage, models, security, cost |
package/dist/bin/cli.js CHANGED
@@ -3400,6 +3400,35 @@ async function checkLitellmHealth(url) {
3400
3400
  return false;
3401
3401
  }
3402
3402
  }
3403
+ async function checkModelHealth(baseUrl, apiKey, model = "claude-haiku-4-5") {
3404
+ try {
3405
+ const res = await fetch(`${baseUrl}/v1/messages`, {
3406
+ method: "POST",
3407
+ headers: {
3408
+ "Content-Type": "application/json",
3409
+ "x-api-key": apiKey,
3410
+ "anthropic-version": "2023-06-01"
3411
+ },
3412
+ body: JSON.stringify({
3413
+ model,
3414
+ max_tokens: 4,
3415
+ messages: [{ role: "user", content: "Reply with: ok" }]
3416
+ }),
3417
+ signal: AbortSignal.timeout(3e4)
3418
+ });
3419
+ if (!res.ok) {
3420
+ const body2 = await res.text().catch(() => "");
3421
+ return { ok: false, error: `HTTP ${res.status}: ${body2.slice(0, 200)}` };
3422
+ }
3423
+ const body = await res.json();
3424
+ if (!body.content?.[0]?.text) {
3425
+ return { ok: false, error: "Empty response from model" };
3426
+ }
3427
+ return { ok: true };
3428
+ } catch (err) {
3429
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
3430
+ }
3431
+ }
3403
3432
  function generateLitellmConfig(provider, modelMap) {
3404
3433
  const apiKeyVar = providerApiKeyEnvVar(provider);
3405
3434
  const entries = ["model_list:"];
@@ -3416,17 +3445,12 @@ function generateLitellmConfig(provider, modelMap) {
3416
3445
  return entries.join("\n") + "\n";
3417
3446
  }
3418
3447
  async function tryStartLitellm(url, projectDir, generatedConfig) {
3419
- const manualConfigPath = path18.join(projectDir, "litellm-config.yaml");
3420
- let configPath;
3421
- if (fs19.existsSync(manualConfigPath)) {
3422
- configPath = manualConfigPath;
3423
- } else if (generatedConfig) {
3424
- configPath = path18.join(os.tmpdir(), "kody-litellm-config.yaml");
3425
- fs19.writeFileSync(configPath, generatedConfig);
3426
- } else {
3427
- logger.warn("litellm-config.yaml not found and no provider configured \u2014 cannot start proxy");
3448
+ if (!generatedConfig) {
3449
+ logger.warn("No provider configured in kody.config.json \u2014 cannot start LiteLLM proxy");
3428
3450
  return null;
3429
3451
  }
3452
+ const configPath = path18.join(os.tmpdir(), "kody-litellm-config.yaml");
3453
+ fs19.writeFileSync(configPath, generatedConfig);
3430
3454
  const portMatch = url.match(/:(\d+)/);
3431
3455
  const port = portMatch ? portMatch[1] : "4000";
3432
3456
  let litellmFound = false;
@@ -3600,6 +3624,25 @@ async function ensureLitellmProxy(config, projectDir) {
3600
3624
  }
3601
3625
  return litellmProcess;
3602
3626
  }
3627
+ async function runModelHealthCheck(config) {
3628
+ const usesProxy = needsLitellmProxy(config);
3629
+ const baseUrl = usesProxy ? getLitellmUrl() : "https://api.anthropic.com";
3630
+ const apiKey = usesProxy ? process.env.ANTHROPIC_COMPATIBLE_API_KEY : process.env.ANTHROPIC_API_KEY;
3631
+ if (!apiKey) {
3632
+ const keyName = usesProxy ? "ANTHROPIC_COMPATIBLE_API_KEY" : "ANTHROPIC_API_KEY";
3633
+ logger.warn(`Skipping model health check \u2014 ${keyName} not set`);
3634
+ return;
3635
+ }
3636
+ const model = config.agent.modelMap.cheap;
3637
+ logger.info(`Model health check (${model} via ${usesProxy ? "LiteLLM" : "Anthropic"})...`);
3638
+ const result = await checkModelHealth(baseUrl, apiKey, model);
3639
+ if (result.ok) {
3640
+ logger.info(" \u2713 Model responded");
3641
+ } else {
3642
+ logger.error(` \u2717 Model health check failed: ${result.error}`);
3643
+ process.exit(1);
3644
+ }
3645
+ }
3603
3646
  async function main() {
3604
3647
  const input = parseArgs();
3605
3648
  const projectDir = input.cwd ? path20.resolve(input.cwd) : process.cwd();
@@ -3693,6 +3736,7 @@ async function main() {
3693
3736
  }
3694
3737
  const config2 = getProjectConfig();
3695
3738
  const litellmProcess2 = await ensureLitellmProxy(config2, projectDir);
3739
+ await runModelHealthCheck(config2);
3696
3740
  const runners2 = createRunners(config2);
3697
3741
  const defaultRunnerName2 = config2.agent.defaultRunner ?? Object.keys(runners2)[0] ?? "claude";
3698
3742
  const defaultRunner2 = runners2[defaultRunnerName2];
@@ -3825,6 +3869,7 @@ ${input.feedback}`);
3825
3869
  }
3826
3870
  const config = getProjectConfig();
3827
3871
  let litellmProcess = await ensureLitellmProxy(config, projectDir);
3872
+ await runModelHealthCheck(config);
3828
3873
  const cleanupLitellm = () => {
3829
3874
  if (litellmProcess) {
3830
3875
  litellmProcess.kill?.();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine-lite",
3
- "version": "0.1.71",
3
+ "version": "0.1.73",
4
4
  "description": "Autonomous SDLC pipeline: Kody orchestration + Claude Code + LiteLLM",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -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 {};
@@ -1,76 +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
- import * as fs from "fs";
7
- const outputFile = process.env.GITHUB_OUTPUT;
8
- const triggerType = process.env.TRIGGER_TYPE ?? "dispatch";
9
- function output(key, value) {
10
- if (outputFile) {
11
- fs.appendFileSync(outputFile, `${key}=${value}\n`);
12
- }
13
- console.log(`${key}=${value}`);
14
- }
15
- // For workflow_dispatch, pass through inputs
16
- if (triggerType === "dispatch") {
17
- output("task_id", process.env.INPUT_TASK_ID ?? "");
18
- output("mode", process.env.INPUT_MODE ?? "full");
19
- output("from_stage", process.env.INPUT_FROM_STAGE ?? "");
20
- output("issue_number", process.env.INPUT_ISSUE_NUMBER ?? "");
21
- output("feedback", process.env.INPUT_FEEDBACK ?? "");
22
- output("valid", process.env.INPUT_TASK_ID ? "true" : "false");
23
- output("trigger_type", "dispatch");
24
- process.exit(0);
25
- }
26
- // For issue_comment, parse the comment body
27
- const commentBody = process.env.COMMENT_BODY ?? "";
28
- const issueNumber = process.env.ISSUE_NUMBER ?? "";
29
- // Match: @kody [mode] [task-id] [--from stage] [--feedback "text"]
30
- const kodyMatch = commentBody.match(/(?:@kody|\/kody)\s*(.*)/i);
31
- if (!kodyMatch) {
32
- output("valid", "false");
33
- output("trigger_type", "comment");
34
- process.exit(0);
35
- }
36
- const parts = kodyMatch[1].trim().split(/\s+/);
37
- const validModes = ["full", "rerun", "status"];
38
- let mode = "full";
39
- let taskId = "";
40
- let fromStage = "";
41
- let feedback = "";
42
- let i = 0;
43
- // First arg: mode or task-id
44
- if (parts[i] && validModes.includes(parts[i])) {
45
- mode = parts[i];
46
- i++;
47
- }
48
- // Second arg: task-id
49
- if (parts[i] && !parts[i].startsWith("--")) {
50
- taskId = parts[i];
51
- i++;
52
- }
53
- // Named args
54
- while (i < parts.length) {
55
- if (parts[i] === "--from" && parts[i + 1]) {
56
- fromStage = parts[i + 1];
57
- i += 2;
58
- }
59
- else if (parts[i] === "--feedback" && parts[i + 1]) {
60
- // Collect quoted feedback
61
- const rest = parts.slice(i + 1).join(" ");
62
- const quoted = rest.match(/^"([^"]*)"/);
63
- feedback = quoted ? quoted[1] : parts[i + 1];
64
- break;
65
- }
66
- else {
67
- i++;
68
- }
69
- }
70
- output("task_id", taskId);
71
- output("mode", mode);
72
- output("from_stage", fromStage);
73
- output("issue_number", issueNumber);
74
- output("feedback", feedback);
75
- output("valid", taskId ? "true" : "false");
76
- output("trigger_type", "comment");
@@ -1,6 +0,0 @@
1
- /**
2
- * Validates that a comment trigger is safe to execute.
3
- * Run by the parse job in GitHub Actions.
4
- * Reads from env, writes to $GITHUB_OUTPUT.
5
- */
6
- export {};
@@ -1,22 +0,0 @@
1
- /**
2
- * Validates that a comment trigger is safe to execute.
3
- * Run by the parse job in GitHub Actions.
4
- * Reads from env, writes to $GITHUB_OUTPUT.
5
- */
6
- import * as fs from "fs";
7
- const ALLOWED_ASSOCIATIONS = ["COLLABORATOR", "MEMBER", "OWNER"];
8
- const association = process.env.COMMENT_AUTHOR_ASSOCIATION ?? "";
9
- const outputFile = process.env.GITHUB_OUTPUT;
10
- function output(key, value) {
11
- if (outputFile) {
12
- fs.appendFileSync(outputFile, `${key}=${value}\n`);
13
- }
14
- console.log(`${key}=${value}`);
15
- }
16
- if (!ALLOWED_ASSOCIATIONS.includes(association)) {
17
- output("valid", "false");
18
- output("reason", `Author association '${association}' not in allowlist: ${ALLOWED_ASSOCIATIONS.join(", ")}`);
19
- process.exit(0);
20
- }
21
- output("valid", "true");
22
- output("reason", "");
@@ -1,13 +0,0 @@
1
- export interface CliInput {
2
- command: "run" | "rerun" | "fix" | "status";
3
- taskId?: string;
4
- task?: string;
5
- fromStage?: string;
6
- dryRun?: boolean;
7
- cwd?: string;
8
- issueNumber?: number;
9
- feedback?: string;
10
- local?: boolean;
11
- complexity?: "low" | "medium" | "high";
12
- }
13
- export declare function parseArgs(): CliInput;
package/dist/cli/args.js DELETED
@@ -1,42 +0,0 @@
1
- const isCI = !!process.env.GITHUB_ACTIONS;
2
- function getArg(args, flag) {
3
- const idx = args.indexOf(flag);
4
- if (idx !== -1 && args[idx + 1] && !args[idx + 1].startsWith("--")) {
5
- return args[idx + 1];
6
- }
7
- return undefined;
8
- }
9
- function hasFlag(args, flag) {
10
- return args.includes(flag);
11
- }
12
- export function parseArgs() {
13
- const args = process.argv.slice(2);
14
- if (hasFlag(args, "--help") || hasFlag(args, "-h") || args.length === 0) {
15
- console.log(`Usage:
16
- kody run --task-id <id> [--task "<desc>"] [--cwd <path>] [--issue-number <n>] [--complexity low|medium|high] [--feedback "<text>"] [--local] [--dry-run]
17
- kody rerun --task-id <id> --from <stage> [--cwd <path>] [--issue-number <n>]
18
- kody fix --task-id <id> [--cwd <path>] [--issue-number <n>] [--feedback "<text>"]
19
- kody status --task-id <id> [--cwd <path>]
20
- kody --help`);
21
- process.exit(0);
22
- }
23
- const command = args[0];
24
- if (!["run", "rerun", "fix", "status"].includes(command)) {
25
- console.error(`Unknown command: ${command}`);
26
- process.exit(1);
27
- }
28
- const issueStr = getArg(args, "--issue-number") ?? process.env.ISSUE_NUMBER;
29
- const localFlag = hasFlag(args, "--local");
30
- return {
31
- command,
32
- taskId: getArg(args, "--task-id") ?? process.env.TASK_ID,
33
- task: getArg(args, "--task"),
34
- fromStage: getArg(args, "--from") ?? process.env.FROM_STAGE,
35
- dryRun: hasFlag(args, "--dry-run") || process.env.DRY_RUN === "true",
36
- cwd: getArg(args, "--cwd"),
37
- issueNumber: issueStr ? parseInt(issueStr, 10) : undefined,
38
- feedback: getArg(args, "--feedback") ?? process.env.FEEDBACK,
39
- local: localFlag || (!isCI && !hasFlag(args, "--no-local")),
40
- complexity: (getArg(args, "--complexity") ?? process.env.COMPLEXITY),
41
- };
42
- }
@@ -1,2 +0,0 @@
1
- export declare function checkLitellmHealth(url: string): Promise<boolean>;
2
- export declare function tryStartLitellm(url: string, projectDir: string): Promise<ReturnType<typeof import("child_process").spawn> | null>;