@kody-ade/kody-engine-lite 0.1.94 → 0.1.96

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/dist/bin/cli.js +15 -11
  2. package/package.json +1 -1
  3. package/templates/kody.yml +4 -0
  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/dist/bin/cli.js CHANGED
@@ -444,8 +444,8 @@ function ensureFeatureBranch(issueNumber, title, cwd) {
444
444
  logger.info(` Created new branch: ${branchName}`);
445
445
  return branchName;
446
446
  }
447
- function syncWithDefault(cwd, branch) {
448
- const defaultBranch = branch ?? getDefaultBranch(cwd);
447
+ function syncWithDefault(cwd) {
448
+ const defaultBranch = getDefaultBranch(cwd);
449
449
  const current = getCurrentBranch(cwd);
450
450
  if (current === defaultBranch) return;
451
451
  try {
@@ -1746,6 +1746,18 @@ async function executeAgentStage(ctx, def) {
1746
1746
  logger.info(` taskify retry produced valid JSON`);
1747
1747
  } else {
1748
1748
  logger.warn(` taskify retry still invalid: ${retryValidation.error}`);
1749
+ const plainText = content.trim();
1750
+ const fallback = JSON.stringify({
1751
+ task_type: "chore",
1752
+ title: plainText.slice(0, 72).replace(/\n/g, " "),
1753
+ description: plainText.slice(0, 500),
1754
+ scope: [],
1755
+ risk_level: "low",
1756
+ hasUI: false,
1757
+ questions: []
1758
+ }, null, 2);
1759
+ fs6.writeFileSync(outputPath, fallback);
1760
+ logger.info(` taskify fallback: generated minimal task.json (risk_level=low)`);
1749
1761
  }
1750
1762
  }
1751
1763
  } else {
@@ -3172,7 +3184,7 @@ function ensureFeatureBranchIfNeeded(ctx) {
3172
3184
  if (ctx.input.dryRun) return;
3173
3185
  if (ctx.input.prNumber) {
3174
3186
  try {
3175
- syncWithDefault(ctx.projectDir, ctx.input.prBaseBranch);
3187
+ syncWithDefault(ctx.projectDir);
3176
3188
  } catch (err) {
3177
3189
  logger.warn(` Failed to sync with default branch: ${err}`);
3178
3190
  }
@@ -4206,13 +4218,6 @@ ${input.feedback}`);
4206
4218
  console.error(`Runner "${defaultRunnerName}" health check failed`);
4207
4219
  process.exit(1);
4208
4220
  }
4209
- let prBaseBranch;
4210
- if (input.prNumber) {
4211
- const prDetails = getPRDetails(input.prNumber);
4212
- if (prDetails) {
4213
- prBaseBranch = prDetails.baseBranch;
4214
- }
4215
- }
4216
4221
  const ctx = {
4217
4222
  taskId,
4218
4223
  taskDir,
@@ -4224,7 +4229,6 @@ ${input.feedback}`);
4224
4229
  dryRun: input.dryRun,
4225
4230
  issueNumber: input.issueNumber,
4226
4231
  prNumber: input.prNumber,
4227
- prBaseBranch,
4228
4232
  feedback: input.feedback,
4229
4233
  local: input.local,
4230
4234
  complexity: input.complexity
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine-lite",
3
- "version": "0.1.94",
3
+ "version": "0.1.96",
4
4
  "description": "Autonomous SDLC pipeline: Kody orchestration + Claude Code + LiteLLM",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -113,6 +113,7 @@ jobs:
113
113
  esac
114
114
  FROM_STAGE=$(echo "$KODY_ARGS" | grep -oP '(?<=--from )\S+' || echo "")
115
115
  FEEDBACK=$(echo "$KODY_ARGS" | grep -oP '(?<=--feedback ")[^"]*' || echo "")
116
+ COMPLEXITY=$(echo "$KODY_ARGS" | grep -oP '(?<=--complexity )\S+' || echo "")
116
117
  DRY_RUN="false"
117
118
  if echo "$KODY_ARGS" | grep -q -- '--dry-run'; then
118
119
  DRY_RUN="true"
@@ -190,6 +191,7 @@ jobs:
190
191
  echo "$FEEDBACK"
191
192
  echo "KODY_EOF"
192
193
  } >> $GITHUB_OUTPUT
194
+ echo "complexity=$COMPLEXITY" >> $GITHUB_OUTPUT
193
195
  echo "ci_run_id=${CI_RUN_ID:-}" >> $GITHUB_OUTPUT
194
196
  echo "dry_run=$DRY_RUN" >> $GITHUB_OUTPUT
195
197
  echo "valid=true" >> $GITHUB_OUTPUT
@@ -265,6 +267,7 @@ jobs:
265
267
  ISSUE_NUMBER: ${{ github.event.inputs.issue_number || needs.parse.outputs.issue_number }}
266
268
  PR_NUMBER: ${{ needs.parse.outputs.pr_number }}
267
269
  FEEDBACK: ${{ github.event.inputs.feedback || needs.parse.outputs.feedback }}
270
+ COMPLEXITY: ${{ needs.parse.outputs.complexity }}
268
271
  CI_RUN_ID: ${{ needs.parse.outputs.ci_run_id }}
269
272
  DRY_RUN: ${{ github.event.inputs.dry_run || needs.parse.outputs.dry_run || 'false' }}
270
273
  RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
@@ -283,6 +286,7 @@ jobs:
283
286
  [ -n "$TASK_ID" ] && ARGS="$ARGS --task-id $TASK_ID"
284
287
  [ -n "$PR_NUMBER" ] && ARGS="$ARGS --pr-number $PR_NUMBER"
285
288
  [ -n "$FROM_STAGE" ] && ARGS="$ARGS --from $FROM_STAGE"
289
+ [ -n "$COMPLEXITY" ] && ARGS="$ARGS --complexity $COMPLEXITY"
286
290
  # FEEDBACK is also passed via env var (avoids shell escaping issues)
287
291
  [ -n "$FEEDBACK" ] && ARGS="$ARGS --feedback \"$FEEDBACK\""
288
292
  [ "$DRY_RUN" = "true" ] && ARGS="$ARGS --dry-run"
@@ -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>;
@@ -1,85 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
- import { execFileSync } from "child_process";
4
- import { logger } from "../logger.js";
5
- export async function checkLitellmHealth(url) {
6
- try {
7
- const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3000) });
8
- return response.ok;
9
- }
10
- catch {
11
- return false;
12
- }
13
- }
14
- export async function tryStartLitellm(url, projectDir) {
15
- const configPath = path.join(projectDir, "litellm-config.yaml");
16
- if (!fs.existsSync(configPath)) {
17
- logger.warn("litellm-config.yaml not found — cannot start proxy");
18
- return null;
19
- }
20
- // Extract port from URL
21
- const portMatch = url.match(/:(\d+)/);
22
- const port = portMatch ? portMatch[1] : "4000";
23
- // Check if litellm is installed
24
- try {
25
- execFileSync("litellm", ["--version"], { timeout: 5000, stdio: "pipe" });
26
- }
27
- catch {
28
- try {
29
- execFileSync("python3", ["-m", "litellm", "--version"], { timeout: 5000, stdio: "pipe" });
30
- }
31
- catch {
32
- logger.warn("litellm not installed (pip install 'litellm[proxy]')");
33
- return null;
34
- }
35
- }
36
- logger.info(`Starting LiteLLM proxy on port ${port}...`);
37
- // Determine command
38
- let cmd;
39
- let args;
40
- try {
41
- execFileSync("litellm", ["--version"], { timeout: 5000, stdio: "pipe" });
42
- cmd = "litellm";
43
- args = ["--config", configPath, "--port", port];
44
- }
45
- catch {
46
- cmd = "python3";
47
- args = ["-m", "litellm", "--config", configPath, "--port", port];
48
- }
49
- // Load API key env vars from project .env (only *_API_KEY patterns)
50
- const dotenvPath = path.join(projectDir, ".env");
51
- const dotenvVars = {};
52
- if (fs.existsSync(dotenvPath)) {
53
- for (const line of fs.readFileSync(dotenvPath, "utf-8").split("\n")) {
54
- const match = line.match(/^([A-Z_][A-Z0-9_]*_API_KEY)=(.*)$/);
55
- if (match)
56
- dotenvVars[match[1]] = match[2];
57
- }
58
- if (Object.keys(dotenvVars).length > 0) {
59
- logger.info(` Loaded API keys: ${Object.keys(dotenvVars).join(", ")}`);
60
- }
61
- }
62
- const { spawn } = await import("child_process");
63
- const child = spawn(cmd, args, {
64
- stdio: ["ignore", "pipe", "pipe"],
65
- detached: true,
66
- env: { ...process.env, ...dotenvVars },
67
- });
68
- // Capture stderr for debugging
69
- let proxyStderr = "";
70
- child.stderr?.on("data", (chunk) => { proxyStderr += chunk.toString(); });
71
- // Wait for health
72
- for (let i = 0; i < 30; i++) {
73
- await new Promise((r) => setTimeout(r, 2000));
74
- if (await checkLitellmHealth(url)) {
75
- logger.info(`LiteLLM proxy ready at ${url}`);
76
- return child;
77
- }
78
- }
79
- if (proxyStderr) {
80
- logger.warn(`LiteLLM stderr: ${proxyStderr.slice(-1000)}`);
81
- }
82
- logger.warn("LiteLLM proxy failed to start within 60s");
83
- child.kill();
84
- return null;
85
- }
@@ -1,2 +0,0 @@
1
- export declare function findLatestTaskForIssue(issueNumber: number, projectDir: string): string | null;
2
- export declare function generateTaskId(): string;
@@ -1,41 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
- import { execFileSync } from "child_process";
4
- export function findLatestTaskForIssue(issueNumber, projectDir) {
5
- const tasksDir = path.join(projectDir, ".tasks");
6
- if (!fs.existsSync(tasksDir))
7
- return null;
8
- // Only consider directories (not files)
9
- const allDirs = fs.readdirSync(tasksDir, { withFileTypes: true })
10
- .filter((d) => d.isDirectory())
11
- .map((d) => d.name)
12
- .sort()
13
- .reverse();
14
- // Direct match: tasks starting with issue number
15
- const prefix = `${issueNumber}-`;
16
- const direct = allDirs.find((d) => d.startsWith(prefix));
17
- if (direct)
18
- return direct;
19
- // Fallback for PR comments: extract issue number from current git branch
20
- // Branch format: <issueNum>--<slug> (e.g., 1031--security-8x-route)
21
- try {
22
- const branch = execFileSync("git", ["branch", "--show-current"], {
23
- encoding: "utf-8", cwd: projectDir, timeout: 5000, stdio: ["pipe", "pipe", "pipe"],
24
- }).trim();
25
- const branchIssueMatch = branch.match(/^(\d+)-/);
26
- if (branchIssueMatch) {
27
- const branchIssueNum = branchIssueMatch[1];
28
- const branchPrefix = `${branchIssueNum}-`;
29
- const fromBranch = allDirs.find((d) => d.startsWith(branchPrefix));
30
- if (fromBranch)
31
- return fromBranch;
32
- }
33
- }
34
- catch { /* ignore */ }
35
- return null;
36
- }
37
- export function generateTaskId() {
38
- const now = new Date();
39
- const pad = (n) => String(n).padStart(2, "0");
40
- return `${String(now.getFullYear()).slice(2)}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
41
- }
package/dist/config.d.ts DELETED
@@ -1,49 +0,0 @@
1
- export interface RunnerConfig {
2
- type: "claude-code";
3
- }
4
- export interface KodyConfig {
5
- quality: {
6
- typecheck: string;
7
- lint: string;
8
- lintFix: string;
9
- format: string;
10
- formatFix: string;
11
- testUnit: string;
12
- };
13
- git: {
14
- defaultBranch: string;
15
- userEmail?: string;
16
- userName?: string;
17
- };
18
- github: {
19
- owner: string;
20
- repo: string;
21
- };
22
- paths: {
23
- taskDir: string;
24
- };
25
- agent: {
26
- runner?: string;
27
- modelMap: {
28
- cheap: string;
29
- mid: string;
30
- strong: string;
31
- };
32
- litellmUrl?: string;
33
- usePerStageRouting?: boolean;
34
- defaultRunner?: string;
35
- runners?: Record<string, RunnerConfig>;
36
- stageRunners?: Record<string, string>;
37
- };
38
- }
39
- export declare const SIGKILL_GRACE_MS = 5000;
40
- export declare const MAX_PR_TITLE_LENGTH = 72;
41
- export declare const STDERR_TAIL_CHARS = 500;
42
- export declare const API_TIMEOUT_MS = 30000;
43
- export declare const DEFAULT_MAX_FIX_ATTEMPTS = 2;
44
- export declare const AGENT_RETRY_DELAY_MS = 2000;
45
- export declare const VERIFY_COMMAND_TIMEOUT_MS: number;
46
- export declare const FIX_COMMAND_TIMEOUT_MS: number;
47
- export declare function setConfigDir(dir: string): void;
48
- export declare function getProjectConfig(): KodyConfig;
49
- export declare function resetProjectConfig(): void;
package/dist/config.js DELETED
@@ -1,72 +0,0 @@
1
- import * as fs from "fs";
2
- import * as path from "path";
3
- import { logger } from "./logger.js";
4
- const DEFAULT_CONFIG = {
5
- quality: {
6
- typecheck: "pnpm -s tsc --noEmit",
7
- lint: "pnpm -s lint",
8
- lintFix: "pnpm lint:fix",
9
- format: "pnpm -s format:check",
10
- formatFix: "pnpm format:fix",
11
- testUnit: "pnpm -s test",
12
- },
13
- git: {
14
- defaultBranch: "dev",
15
- },
16
- github: {
17
- owner: "",
18
- repo: "",
19
- },
20
- paths: {
21
- taskDir: ".tasks",
22
- },
23
- agent: {
24
- runner: "claude-code",
25
- defaultRunner: "claude",
26
- modelMap: { cheap: "haiku", mid: "sonnet", strong: "opus" },
27
- },
28
- };
29
- // Pipeline constants
30
- export const SIGKILL_GRACE_MS = 5000;
31
- export const MAX_PR_TITLE_LENGTH = 72;
32
- export const STDERR_TAIL_CHARS = 500;
33
- export const API_TIMEOUT_MS = 30_000;
34
- export const DEFAULT_MAX_FIX_ATTEMPTS = 2;
35
- export const AGENT_RETRY_DELAY_MS = 2000;
36
- export const VERIFY_COMMAND_TIMEOUT_MS = 5 * 60 * 1000;
37
- export const FIX_COMMAND_TIMEOUT_MS = 2 * 60 * 1000;
38
- let _config = null;
39
- let _configDir = null;
40
- export function setConfigDir(dir) {
41
- _configDir = dir;
42
- _config = null;
43
- }
44
- export function getProjectConfig() {
45
- if (_config)
46
- return _config;
47
- const configPath = path.join(_configDir ?? process.cwd(), "kody.config.json");
48
- if (fs.existsSync(configPath)) {
49
- try {
50
- const raw = JSON.parse(fs.readFileSync(configPath, "utf-8"));
51
- _config = {
52
- quality: { ...DEFAULT_CONFIG.quality, ...raw.quality },
53
- git: { ...DEFAULT_CONFIG.git, ...raw.git },
54
- github: { ...DEFAULT_CONFIG.github, ...raw.github },
55
- paths: { ...DEFAULT_CONFIG.paths, ...raw.paths },
56
- agent: { ...DEFAULT_CONFIG.agent, ...raw.agent },
57
- };
58
- }
59
- catch {
60
- logger.warn("kody.config.json is invalid JSON — using defaults");
61
- _config = { ...DEFAULT_CONFIG };
62
- }
63
- }
64
- else {
65
- _config = { ...DEFAULT_CONFIG };
66
- }
67
- return _config;
68
- }
69
- export function resetProjectConfig() {
70
- _config = null;
71
- _configDir = null;
72
- }
package/dist/context.d.ts DELETED
@@ -1,4 +0,0 @@
1
- export declare function readPromptFile(stageName: string): string;
2
- export declare function injectTaskContext(prompt: string, taskId: string, taskDir: string, feedback?: string): string;
3
- export declare function buildFullPrompt(stageName: string, taskId: string, taskDir: string, projectDir: string, feedback?: string): string;
4
- export declare function resolveModel(modelTier: string, stageName?: string): string;