@kody-ade/kody-engine-lite 0.1.63 → 0.1.65

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 (73) hide show
  1. package/dist/agent-runner.d.ts +4 -0
  2. package/dist/agent-runner.js +122 -0
  3. package/dist/bin/cli.js +162 -8
  4. package/dist/ci/parse-inputs.d.ts +6 -0
  5. package/dist/ci/parse-inputs.js +76 -0
  6. package/dist/ci/parse-safety.d.ts +6 -0
  7. package/dist/ci/parse-safety.js +22 -0
  8. package/dist/cli/args.d.ts +13 -0
  9. package/dist/cli/args.js +42 -0
  10. package/dist/cli/litellm.d.ts +2 -0
  11. package/dist/cli/litellm.js +85 -0
  12. package/dist/cli/task-resolution.d.ts +2 -0
  13. package/dist/cli/task-resolution.js +41 -0
  14. package/dist/config.d.ts +49 -0
  15. package/dist/config.js +72 -0
  16. package/dist/context.d.ts +4 -0
  17. package/dist/context.js +83 -0
  18. package/dist/definitions.d.ts +3 -0
  19. package/dist/definitions.js +59 -0
  20. package/dist/entry.d.ts +1 -0
  21. package/dist/entry.js +236 -0
  22. package/dist/git-utils.d.ts +13 -0
  23. package/dist/git-utils.js +174 -0
  24. package/dist/github-api.d.ts +14 -0
  25. package/dist/github-api.js +114 -0
  26. package/dist/kody-utils.d.ts +1 -0
  27. package/dist/kody-utils.js +9 -0
  28. package/dist/learning/auto-learn.d.ts +2 -0
  29. package/dist/learning/auto-learn.js +169 -0
  30. package/dist/logger.d.ts +14 -0
  31. package/dist/logger.js +51 -0
  32. package/dist/memory.d.ts +1 -0
  33. package/dist/memory.js +20 -0
  34. package/dist/observer.d.ts +9 -0
  35. package/dist/observer.js +80 -0
  36. package/dist/pipeline/complexity.d.ts +3 -0
  37. package/dist/pipeline/complexity.js +12 -0
  38. package/dist/pipeline/executor-registry.d.ts +3 -0
  39. package/dist/pipeline/executor-registry.js +20 -0
  40. package/dist/pipeline/hooks.d.ts +17 -0
  41. package/dist/pipeline/hooks.js +110 -0
  42. package/dist/pipeline/questions.d.ts +2 -0
  43. package/dist/pipeline/questions.js +44 -0
  44. package/dist/pipeline/runner-selection.d.ts +2 -0
  45. package/dist/pipeline/runner-selection.js +13 -0
  46. package/dist/pipeline/state.d.ts +4 -0
  47. package/dist/pipeline/state.js +37 -0
  48. package/dist/pipeline.d.ts +3 -0
  49. package/dist/pipeline.js +213 -0
  50. package/dist/preflight.d.ts +1 -0
  51. package/dist/preflight.js +69 -0
  52. package/dist/retrospective.d.ts +26 -0
  53. package/dist/retrospective.js +211 -0
  54. package/dist/stages/agent.d.ts +2 -0
  55. package/dist/stages/agent.js +94 -0
  56. package/dist/stages/gate.d.ts +2 -0
  57. package/dist/stages/gate.js +32 -0
  58. package/dist/stages/review.d.ts +2 -0
  59. package/dist/stages/review.js +32 -0
  60. package/dist/stages/ship.d.ts +3 -0
  61. package/dist/stages/ship.js +154 -0
  62. package/dist/stages/verify.d.ts +2 -0
  63. package/dist/stages/verify.js +94 -0
  64. package/dist/types.d.ts +61 -0
  65. package/dist/types.js +1 -0
  66. package/dist/validators.d.ts +8 -0
  67. package/dist/validators.js +42 -0
  68. package/dist/verify-runner.d.ts +11 -0
  69. package/dist/verify-runner.js +110 -0
  70. package/kody.config.schema.json +66 -0
  71. package/package.json +8 -9
  72. package/prompts/taskify.md +5 -0
  73. package/templates/kody.yml +6 -1
@@ -0,0 +1,61 @@
1
+ export type StageName = "taskify" | "plan" | "build" | "verify" | "review" | "review-fix" | "ship";
2
+ export type StageType = "agent" | "gate" | "deterministic";
3
+ export type PipelineState = "pending" | "running" | "completed" | "failed" | "timeout";
4
+ export interface StageDefinition {
5
+ name: StageName;
6
+ type: StageType;
7
+ modelTier: "cheap" | "mid" | "strong";
8
+ timeout: number;
9
+ maxRetries: number;
10
+ outputFile?: string;
11
+ retryWithAgent?: string;
12
+ }
13
+ export interface StageState {
14
+ state: PipelineState;
15
+ startedAt?: string;
16
+ completedAt?: string;
17
+ retries: number;
18
+ error?: string;
19
+ outputFile?: string;
20
+ }
21
+ export interface PipelineStatus {
22
+ taskId: string;
23
+ state: "running" | "completed" | "failed";
24
+ stages: Record<StageName, StageState>;
25
+ createdAt: string;
26
+ updatedAt: string;
27
+ }
28
+ export interface StageResult {
29
+ outcome: "completed" | "failed" | "timed_out";
30
+ outputFile?: string;
31
+ error?: string;
32
+ retries: number;
33
+ }
34
+ export interface AgentResult {
35
+ outcome: "completed" | "failed" | "timed_out";
36
+ output?: string;
37
+ error?: string;
38
+ }
39
+ export interface AgentRunnerOptions {
40
+ cwd?: string;
41
+ env?: Record<string, string>;
42
+ }
43
+ export interface AgentRunner {
44
+ run(stageName: string, prompt: string, model: string, timeout: number, taskDir: string, options?: AgentRunnerOptions): Promise<AgentResult>;
45
+ healthCheck(): Promise<boolean>;
46
+ }
47
+ export interface PipelineContext {
48
+ taskId: string;
49
+ taskDir: string;
50
+ projectDir: string;
51
+ runners: Record<string, AgentRunner>;
52
+ input: {
53
+ mode: "full" | "rerun" | "status";
54
+ fromStage?: string;
55
+ dryRun?: boolean;
56
+ issueNumber?: number;
57
+ feedback?: string;
58
+ local?: boolean;
59
+ complexity?: "low" | "medium" | "high";
60
+ };
61
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,8 @@
1
+ export interface ValidationResult {
2
+ valid: boolean;
3
+ error?: string;
4
+ }
5
+ export declare function stripFences(content: string): string;
6
+ export declare function validateTaskJson(content: string): ValidationResult;
7
+ export declare function validatePlanMd(content: string): ValidationResult;
8
+ export declare function validateReviewMd(content: string): ValidationResult;
@@ -0,0 +1,42 @@
1
+ const REQUIRED_TASK_FIELDS = [
2
+ "task_type",
3
+ "title",
4
+ "description",
5
+ "scope",
6
+ "risk_level",
7
+ ];
8
+ export function stripFences(content) {
9
+ return content.replace(/^```json\s*\n?/m, "").replace(/\n?```\s*$/m, "");
10
+ }
11
+ export function validateTaskJson(content) {
12
+ try {
13
+ const parsed = JSON.parse(stripFences(content));
14
+ for (const field of REQUIRED_TASK_FIELDS) {
15
+ if (!(field in parsed)) {
16
+ return { valid: false, error: `Missing field: ${field}` };
17
+ }
18
+ }
19
+ return { valid: true };
20
+ }
21
+ catch (err) {
22
+ return {
23
+ valid: false,
24
+ error: `Invalid JSON: ${err instanceof Error ? err.message : String(err)}`,
25
+ };
26
+ }
27
+ }
28
+ export function validatePlanMd(content) {
29
+ if (content.length < 10) {
30
+ return { valid: false, error: "Plan is too short (< 10 chars)" };
31
+ }
32
+ if (!/^##\s+\w+/m.test(content)) {
33
+ return { valid: false, error: "Plan has no markdown h2 sections" };
34
+ }
35
+ return { valid: true };
36
+ }
37
+ export function validateReviewMd(content) {
38
+ if (/pass/i.test(content) || /fail/i.test(content)) {
39
+ return { valid: true };
40
+ }
41
+ return { valid: false, error: "Review must contain 'pass' or 'fail'" };
42
+ }
@@ -0,0 +1,11 @@
1
+ export interface VerifyResult {
2
+ pass: boolean;
3
+ errors: string[];
4
+ summary: string[];
5
+ }
6
+ /**
7
+ * Parse a command string into [executable, ...args], respecting quoted arguments.
8
+ * e.g., 'pnpm -s "test:unit"' → ["pnpm", "-s", "test:unit"]
9
+ */
10
+ export declare function parseCommand(cmd: string): string[];
11
+ export declare function runQualityGates(taskDir: string, projectRoot?: string): VerifyResult;
@@ -0,0 +1,110 @@
1
+ import { execFileSync } from "child_process";
2
+ import { getProjectConfig, VERIFY_COMMAND_TIMEOUT_MS } from "./config.js";
3
+ import { logger } from "./logger.js";
4
+ function isExecError(err) {
5
+ return typeof err === "object" && err !== null;
6
+ }
7
+ /**
8
+ * Parse a command string into [executable, ...args], respecting quoted arguments.
9
+ * e.g., 'pnpm -s "test:unit"' → ["pnpm", "-s", "test:unit"]
10
+ */
11
+ export function parseCommand(cmd) {
12
+ const parts = [];
13
+ let current = "";
14
+ let inQuote = null;
15
+ for (const ch of cmd) {
16
+ if (inQuote) {
17
+ if (ch === inQuote) {
18
+ inQuote = null;
19
+ }
20
+ else {
21
+ current += ch;
22
+ }
23
+ }
24
+ else if (ch === '"' || ch === "'") {
25
+ inQuote = ch;
26
+ }
27
+ else if (/\s/.test(ch)) {
28
+ if (current) {
29
+ parts.push(current);
30
+ current = "";
31
+ }
32
+ }
33
+ else {
34
+ current += ch;
35
+ }
36
+ }
37
+ if (current)
38
+ parts.push(current);
39
+ if (inQuote)
40
+ logger.warn(`Unclosed quote in command: ${cmd}`);
41
+ return parts;
42
+ }
43
+ function runCommand(cmd, cwd, timeout) {
44
+ const parts = parseCommand(cmd);
45
+ if (parts.length === 0) {
46
+ return { success: true, output: "", timedOut: false };
47
+ }
48
+ try {
49
+ const output = execFileSync(parts[0], parts.slice(1), {
50
+ cwd,
51
+ timeout,
52
+ encoding: "utf-8",
53
+ stdio: ["pipe", "pipe", "pipe"],
54
+ env: { ...process.env, FORCE_COLOR: "0" },
55
+ });
56
+ return { success: true, output: output ?? "", timedOut: false };
57
+ }
58
+ catch (err) {
59
+ const stdout = isExecError(err) ? err.stdout ?? "" : "";
60
+ const stderr = isExecError(err) ? err.stderr ?? "" : "";
61
+ const killed = isExecError(err) ? !!err.killed : false;
62
+ return { success: false, output: `${stdout}${stderr}`, timedOut: killed };
63
+ }
64
+ }
65
+ function parseErrors(output) {
66
+ const errors = [];
67
+ for (const line of output.split("\n")) {
68
+ if (/error|Error|ERROR|failed|Failed|FAIL|warning:|Warning:|WARN/i.test(line)) {
69
+ errors.push(line.slice(0, 500));
70
+ }
71
+ }
72
+ return errors;
73
+ }
74
+ function extractSummary(output, cmdName) {
75
+ const summaryPatterns = /Test Suites|Tests|Coverage|ERRORS|FAILURES|success|completed/i;
76
+ const lines = output.split("\n").filter((l) => summaryPatterns.test(l));
77
+ return lines.slice(-3).map((l) => `[${cmdName}] ${l.trim()}`);
78
+ }
79
+ export function runQualityGates(taskDir, projectRoot) {
80
+ const config = getProjectConfig();
81
+ const cwd = projectRoot ?? process.cwd();
82
+ const allErrors = [];
83
+ const allSummary = [];
84
+ let allPass = true;
85
+ const commands = [
86
+ { name: "typecheck", cmd: config.quality.typecheck },
87
+ { name: "test", cmd: config.quality.testUnit },
88
+ ];
89
+ if (config.quality.lint) {
90
+ commands.push({ name: "lint", cmd: config.quality.lint });
91
+ }
92
+ for (const { name, cmd } of commands) {
93
+ if (!cmd)
94
+ continue;
95
+ logger.info(` Running ${name}: ${cmd}`);
96
+ const result = runCommand(cmd, cwd, VERIFY_COMMAND_TIMEOUT_MS);
97
+ if (result.timedOut) {
98
+ allErrors.push(`${name}: timed out after ${VERIFY_COMMAND_TIMEOUT_MS / 1000}s`);
99
+ allPass = false;
100
+ continue;
101
+ }
102
+ if (!result.success) {
103
+ allPass = false;
104
+ const errors = parseErrors(result.output);
105
+ allErrors.push(...errors.map((e) => `[${name}] ${e}`));
106
+ }
107
+ allSummary.push(...extractSummary(result.output, name));
108
+ }
109
+ return { pass: allPass, errors: allErrors, summary: allSummary };
110
+ }
@@ -128,6 +128,72 @@
128
128
  }
129
129
  },
130
130
  "additionalProperties": false
131
+ },
132
+ "mcp": {
133
+ "type": "object",
134
+ "description": "MCP (Model Context Protocol) server configuration. Enables external tools like browser automation.",
135
+ "properties": {
136
+ "enabled": {
137
+ "type": "boolean",
138
+ "description": "Enable MCP server integration",
139
+ "default": false
140
+ },
141
+ "servers": {
142
+ "type": "object",
143
+ "description": "Named MCP server definitions. Each key is a server name.",
144
+ "additionalProperties": {
145
+ "type": "object",
146
+ "properties": {
147
+ "command": {
148
+ "type": "string",
149
+ "description": "Command to start the MCP server (e.g., 'npx')"
150
+ },
151
+ "args": {
152
+ "type": "array",
153
+ "items": { "type": "string" },
154
+ "description": "Command arguments"
155
+ },
156
+ "env": {
157
+ "type": "object",
158
+ "additionalProperties": { "type": "string" },
159
+ "description": "Environment variables for the server process"
160
+ }
161
+ },
162
+ "required": ["command"]
163
+ }
164
+ },
165
+ "stages": {
166
+ "type": "array",
167
+ "items": { "type": "string" },
168
+ "description": "Stages that can use MCP tools. Defaults to [\"build\", \"verify\", \"review\", \"review-fix\"]",
169
+ "default": ["build", "verify", "review", "review-fix"]
170
+ },
171
+ "devServer": {
172
+ "type": "object",
173
+ "description": "Dev server configuration for browser tool verification",
174
+ "properties": {
175
+ "command": {
176
+ "type": "string",
177
+ "description": "Command to start the dev server (e.g., 'pnpm dev')"
178
+ },
179
+ "url": {
180
+ "type": "string",
181
+ "description": "URL where the dev server will be accessible (e.g., 'http://localhost:3000')"
182
+ },
183
+ "readyPattern": {
184
+ "type": "string",
185
+ "description": "Regex pattern to match in stdout when server is ready. Default: 'Ready in|compiled|started server|Local:'"
186
+ },
187
+ "readyTimeout": {
188
+ "type": "number",
189
+ "description": "Seconds to wait for the server to be ready. Default: 30"
190
+ }
191
+ },
192
+ "required": ["command", "url"]
193
+ }
194
+ },
195
+ "required": ["enabled", "servers"],
196
+ "additionalProperties": false
131
197
  }
132
198
  },
133
199
  "additionalProperties": false
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine-lite",
3
- "version": "0.1.63",
3
+ "version": "0.1.65",
4
4
  "description": "Autonomous SDLC pipeline: Kody orchestration + Claude Code + LiteLLM",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -13,13 +13,6 @@
13
13
  "templates",
14
14
  "kody.config.schema.json"
15
15
  ],
16
- "scripts": {
17
- "kody": "tsx src/entry.ts",
18
- "build": "tsup",
19
- "test": "vitest run",
20
- "typecheck": "tsc --noEmit",
21
- "prepublishOnly": "pnpm build"
22
- },
23
16
  "dependencies": {
24
17
  "dotenv": "^16.4.7"
25
18
  },
@@ -32,5 +25,11 @@
32
25
  },
33
26
  "engines": {
34
27
  "node": ">=22"
28
+ },
29
+ "scripts": {
30
+ "kody": "tsx src/entry.ts",
31
+ "build": "tsup",
32
+ "test": "vitest run",
33
+ "typecheck": "tsc --noEmit"
35
34
  }
36
- }
35
+ }
@@ -18,9 +18,14 @@ Required JSON format:
18
18
  "description": "Clear description of what the task requires",
19
19
  "scope": ["list", "of", "exact/file/paths", "affected"],
20
20
  "risk_level": "low | medium | high",
21
+ "hasUI": true,
21
22
  "questions": []
22
23
  }
23
24
 
25
+ hasUI heuristics:
26
+ - true: task touches frontend files (.tsx, .jsx, .vue, .svelte, .css, .scss, .html), UI components, pages, layouts, or styles
27
+ - false: task is purely backend, CLI, API, database, config, docs, or infrastructure
28
+
24
29
  Risk level heuristics:
25
30
  - low: single file change, no breaking changes, docs, config, isolated scripts, test additions, style changes
26
31
  - medium: multiple files, possible side effects, API changes, new dependencies, refactoring existing logic
@@ -224,6 +224,9 @@ jobs:
224
224
  - name: Install Claude Code CLI
225
225
  run: npm install -g @anthropic-ai/claude-code
226
226
 
227
+ - name: Install LiteLLM proxy
228
+ run: pip install 'litellm[proxy]'
229
+
227
230
  - name: Configure git
228
231
  run: |
229
232
  git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
@@ -232,6 +235,7 @@ jobs:
232
235
  - name: Run Kody pipeline
233
236
  env:
234
237
  ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
238
+ MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}
235
239
  GH_TOKEN: ${{ steps.app-token.outputs.token || secrets.GITHUB_TOKEN }}
236
240
  TASK_ID: ${{ github.event.inputs.task_id || needs.parse.outputs.task_id }}
237
241
  MODE: ${{ github.event.inputs.mode || needs.parse.outputs.mode || 'full' }}
@@ -254,7 +258,8 @@ jobs:
254
258
  [ -n "$TASK_ID" ] && ARGS="$ARGS --task-id $TASK_ID"
255
259
  [ -n "$PR_NUMBER" ] && ARGS="$ARGS --pr-number $PR_NUMBER"
256
260
  [ -n "$FROM_STAGE" ] && ARGS="$ARGS --from $FROM_STAGE"
257
- # FEEDBACK is passed via env var, not CLI arg (avoids shell escaping issues)
261
+ # FEEDBACK is also passed via env var (avoids shell escaping issues)
262
+ [ -n "$FEEDBACK" ] && ARGS="$ARGS --feedback \"$FEEDBACK\""
258
263
  [ "$DRY_RUN" = "true" ] && ARGS="$ARGS --dry-run"
259
264
  kody-engine-lite $CMD $ARGS
260
265
  fi