@kody-ade/kody-engine-lite 0.1.150 → 0.1.152
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +183 -29
- package/dist/bin/cli.js +34 -14
- package/kody.config.schema.json +0 -10
- package/package.json +1 -1
- package/prompts/taskify.md +2 -2
- package/templates/kody-watch.yml +13 -1
- package/dist/agent-runner.d.ts +0 -4
- package/dist/agent-runner.js +0 -122
- package/dist/ci/parse-inputs.d.ts +0 -6
- package/dist/ci/parse-inputs.js +0 -76
- package/dist/ci/parse-safety.d.ts +0 -6
- package/dist/ci/parse-safety.js +0 -22
- package/dist/cli/args.d.ts +0 -13
- package/dist/cli/args.js +0 -42
- package/dist/cli/litellm.d.ts +0 -2
- package/dist/cli/litellm.js +0 -85
- package/dist/cli/task-resolution.d.ts +0 -2
- package/dist/cli/task-resolution.js +0 -41
- package/dist/config.d.ts +0 -49
- package/dist/config.js +0 -72
- package/dist/context.d.ts +0 -4
- package/dist/context.js +0 -83
- package/dist/definitions.d.ts +0 -3
- package/dist/definitions.js +0 -59
- package/dist/entry.d.ts +0 -1
- package/dist/entry.js +0 -236
- package/dist/git-utils.d.ts +0 -13
- package/dist/git-utils.js +0 -174
- package/dist/github-api.d.ts +0 -14
- package/dist/github-api.js +0 -114
- package/dist/kody-utils.d.ts +0 -1
- package/dist/kody-utils.js +0 -9
- package/dist/learning/auto-learn.d.ts +0 -2
- package/dist/learning/auto-learn.js +0 -169
- package/dist/logger.d.ts +0 -14
- package/dist/logger.js +0 -51
- package/dist/memory.d.ts +0 -1
- package/dist/memory.js +0 -20
- package/dist/observer.d.ts +0 -9
- package/dist/observer.js +0 -80
- package/dist/pipeline/complexity.d.ts +0 -3
- package/dist/pipeline/complexity.js +0 -12
- package/dist/pipeline/executor-registry.d.ts +0 -3
- package/dist/pipeline/executor-registry.js +0 -20
- package/dist/pipeline/hooks.d.ts +0 -17
- package/dist/pipeline/hooks.js +0 -110
- package/dist/pipeline/questions.d.ts +0 -2
- package/dist/pipeline/questions.js +0 -44
- package/dist/pipeline/runner-selection.d.ts +0 -2
- package/dist/pipeline/runner-selection.js +0 -13
- package/dist/pipeline/state.d.ts +0 -4
- package/dist/pipeline/state.js +0 -37
- package/dist/pipeline.d.ts +0 -3
- package/dist/pipeline.js +0 -213
- package/dist/preflight.d.ts +0 -1
- package/dist/preflight.js +0 -69
- package/dist/retrospective.d.ts +0 -26
- package/dist/retrospective.js +0 -211
- package/dist/stages/agent.d.ts +0 -2
- package/dist/stages/agent.js +0 -94
- package/dist/stages/gate.d.ts +0 -2
- package/dist/stages/gate.js +0 -32
- package/dist/stages/review.d.ts +0 -2
- package/dist/stages/review.js +0 -32
- package/dist/stages/ship.d.ts +0 -3
- package/dist/stages/ship.js +0 -154
- package/dist/stages/verify.d.ts +0 -2
- package/dist/stages/verify.js +0 -94
- package/dist/types.d.ts +0 -61
- package/dist/types.js +0 -1
- package/dist/validators.d.ts +0 -8
- package/dist/validators.js +0 -42
- package/dist/verify-runner.d.ts +0 -11
- package/dist/verify-runner.js +0 -110
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aharon Yair Cohen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -16,30 +16,9 @@ Kody wraps Claude Code with a 7-stage autonomous pipeline — classify, plan, bu
|
|
|
16
16
|
|
|
17
17
|
[Why Kody? →](docs/ABOUT.md) · [Full comparison →](docs/COMPARISON.md)
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
▼
|
|
23
|
-
① TASKIFY ─── classify, scope, detect complexity, ask questions
|
|
24
|
-
│
|
|
25
|
-
▼
|
|
26
|
-
② PLAN ────── TDD implementation plan (deep reasoning)
|
|
27
|
-
│ 🛑 HIGH risk? Pause for human approval
|
|
28
|
-
▼
|
|
29
|
-
③ BUILD ───── implement via Claude Code tools
|
|
30
|
-
│
|
|
31
|
-
▼
|
|
32
|
-
④ VERIFY ──── run your quality commands (typecheck, tests, lint)
|
|
33
|
-
│ ✗ fail → AI diagnosis → autofix → retry
|
|
34
|
-
▼
|
|
35
|
-
⑤ REVIEW ──── AI code review (fresh session, no build bias)
|
|
36
|
-
│
|
|
37
|
-
▼
|
|
38
|
-
⑥ REVIEW-FIX ─ fix Critical and Major findings
|
|
39
|
-
│
|
|
40
|
-
▼
|
|
41
|
-
⑦ SHIP ────── push branch, create PR with Closes #N
|
|
42
|
-
```
|
|
19
|
+
<p align="center">
|
|
20
|
+
<img src="assets/pipeline.svg" alt="Kody 7-stage pipeline: Taskify → Plan → Build → Verify → Review → Review-Fix → Ship" width="600">
|
|
21
|
+
</p>
|
|
43
22
|
|
|
44
23
|
## Quick Start
|
|
45
24
|
|
|
@@ -89,10 +68,36 @@ Comment on any GitHub issue:
|
|
|
89
68
|
@kody
|
|
90
69
|
```
|
|
91
70
|
|
|
92
|
-
Kody picks up the issue and works through the pipeline autonomously
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
71
|
+
Kody picks up the issue and works through the pipeline autonomously:
|
|
72
|
+
|
|
73
|
+
<div align="center">
|
|
74
|
+
<picture>
|
|
75
|
+
<img src="assets/screenshots/kody-trigger.png" alt="GitHub issue showing @kody trigger, pipeline started comment, and complexity detection" width="680">
|
|
76
|
+
</picture>
|
|
77
|
+
<br><sub>▲ Comment <code>@kody</code> on an issue — pipeline starts automatically</sub>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<br>
|
|
81
|
+
|
|
82
|
+
You'll see labels updating in real-time, progress comments at each stage, and a pipeline summary when done:
|
|
83
|
+
|
|
84
|
+
<div align="center">
|
|
85
|
+
<picture>
|
|
86
|
+
<img src="assets/screenshots/kody-summary.png" alt="Pipeline summary table showing all stages completed with duration and retries" width="680">
|
|
87
|
+
</picture>
|
|
88
|
+
<br><sub>▲ Pipeline summary — all stages completed with duration and retries</sub>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<br>
|
|
92
|
+
|
|
93
|
+
The result is a PR with a rich description, passing quality checks, and `Closes #N`:
|
|
94
|
+
|
|
95
|
+
<div align="center">
|
|
96
|
+
<picture>
|
|
97
|
+
<img src="assets/screenshots/kody-pr.png" alt="Pull request created by Kody with description, scope, verify status, and Closes link" width="680">
|
|
98
|
+
</picture>
|
|
99
|
+
<br><sub>▲ PR created by Kody — description, scope, verify status, Closes #N</sub>
|
|
100
|
+
</div>
|
|
96
101
|
|
|
97
102
|
If the task is HIGH-risk, Kody pauses after planning and asks for approval before writing code.
|
|
98
103
|
|
|
@@ -110,11 +115,45 @@ ANTHROPIC_COMPATIBLE_API_KEY=your-key-here
|
|
|
110
115
|
|
|
111
116
|
Kody auto-starts the LiteLLM proxy. [Full LiteLLM guide →](docs/LITELLM.md)
|
|
112
117
|
|
|
118
|
+
## Which command should I use?
|
|
119
|
+
|
|
120
|
+
```mermaid
|
|
121
|
+
flowchart TD
|
|
122
|
+
Start(("What do you\nneed to do?")):::start
|
|
123
|
+
|
|
124
|
+
Start --> NewIssue{"New issue\n→ PR?"}
|
|
125
|
+
Start --> ExistingPR{"Existing\nPR?"}
|
|
126
|
+
Start --> Setup{"Setup /\nOnboarding?"}
|
|
127
|
+
|
|
128
|
+
NewIssue -->|"Simple/medium"| Kody["**@kody**\nFull pipeline: taskify → plan →\nbuild → verify → review → ship"]:::cmd
|
|
129
|
+
NewIssue -->|"Complex multi-area"| Decompose["**@kody decompose**\nParallel sub-tasks: analyze →\nsplit → parallel build → merge → ship"]:::cmd
|
|
130
|
+
|
|
131
|
+
ExistingPR --> PRWhat{"What's wrong?"}
|
|
132
|
+
|
|
133
|
+
PRWhat -->|"Need a code review"| Review["**@kody review**\nStandalone PR review with\nstructured findings + verdict"]:::cmd
|
|
134
|
+
PRWhat -->|"Human gave feedback"| Fix["**@kody fix**\nRe-run from build with\nPR feedback as context"]:::cmd
|
|
135
|
+
PRWhat -->|"CI is failing"| FixCI["**@kody fix-ci**\nFetch CI logs, diagnose,\nand push a fix"]:::cmd
|
|
136
|
+
PRWhat -->|"Merge conflicts"| Resolve["**@kody resolve**\nMerge default branch,\nAI-resolve conflicts, verify"]:::cmd
|
|
137
|
+
PRWhat -->|"Previous run failed\nor was paused"| Rerun["**@kody rerun**\nResume from failed/paused stage\n*--from stage* to pick stage"]:::cmd
|
|
138
|
+
|
|
139
|
+
Setup -->|"First time"| Init["**kody-engine-lite init**\nGenerate workflow + config"]:::setup
|
|
140
|
+
Init --> Bootstrap
|
|
141
|
+
Setup -->|"After major refactor"| Bootstrap["**@kody bootstrap**\nRegenerate memory +\nstep files + labels"]:::setup
|
|
142
|
+
|
|
143
|
+
Kody -.->|"Paused with\nquestions or\nrisk gate?"| Approve["**@kody approve**\nResume after pause"]:::cmd
|
|
144
|
+
|
|
145
|
+
classDef start fill:#1a1a2e,stroke:#e94560,color:#fff,stroke-width:2px
|
|
146
|
+
classDef cmd fill:#0f3460,stroke:#53d8fb,color:#fff,stroke-width:1px
|
|
147
|
+
classDef setup fill:#1a1a2e,stroke:#e9b44c,color:#fff,stroke-width:1px
|
|
148
|
+
```
|
|
149
|
+
|
|
113
150
|
## Commands
|
|
114
151
|
|
|
115
152
|
| Command | What it does |
|
|
116
153
|
|---------|-------------|
|
|
117
154
|
| `@kody` | Run full pipeline on an issue |
|
|
155
|
+
| `@kody decompose` | Parallel sub-tasks for complex issues — analyze, split, build in parallel, merge, verify, review, ship ([details](docs/DECOMPOSE.md)) |
|
|
156
|
+
| `@kody compose` | Retry merge + verify + review + ship after a decompose build succeeded |
|
|
118
157
|
| `@kody review` | Review any PR — structured findings + GitHub approve/request-changes (falls back to comment if self-review blocked) |
|
|
119
158
|
| `@kody fix` | Re-run from build with human PR feedback + Kody's review as context |
|
|
120
159
|
| `@kody fix-ci` | Fix failing CI checks (auto-triggered with loop guard) |
|
|
@@ -133,6 +172,8 @@ kody-engine-lite review --pr-number 42 # Standalone PR review
|
|
|
133
172
|
kody-engine-lite fix --issue-number 42 --feedback "Use middleware pattern"
|
|
134
173
|
kody-engine-lite fix-ci --pr-number 42
|
|
135
174
|
kody-engine-lite resolve --pr-number 42 # Merge + resolve conflicts
|
|
175
|
+
kody-engine-lite decompose --issue-number 42 # Parallel sub-tasks for complex issues
|
|
176
|
+
kody-engine-lite compose --task-id <id> # Retry compose after decompose
|
|
136
177
|
kody-engine-lite rerun --issue-number 42 --from verify
|
|
137
178
|
kody-engine-lite watch [--dry-run] # Run health monitoring locally
|
|
138
179
|
```
|
|
@@ -148,6 +189,7 @@ kody-engine-lite watch [--dry-run] # Run health monitoring locally
|
|
|
148
189
|
- **AI Failure Diagnosis** — classifies errors as fixable/infrastructure/pre-existing/abort before retry ([details](docs/FEATURES.md#ai-powered-failure-diagnosis))
|
|
149
190
|
- **Question Gates** — asks product/architecture questions when the task is unclear ([details](docs/FEATURES.md#question-gates))
|
|
150
191
|
- **Auto Fix-CI** — CI fails on a PR? Kody fetches logs, diagnoses, and pushes a fix ([details](docs/FEATURES.md#auto-fix-ci))
|
|
192
|
+
- **Parallel Decomposition** — complex tasks auto-split into independent sub-tasks that build in parallel, then merge and verify ([details](docs/DECOMPOSE.md))
|
|
151
193
|
- **Pattern Discovery** — searches for existing patterns before proposing new ones ([details](docs/FEATURES.md#pattern-discovery))
|
|
152
194
|
- **Decision Memory** — architectural decisions extracted from reviews persist across tasks ([details](docs/FEATURES.md#decision-memory))
|
|
153
195
|
- **Auto-Learning** — extracts coding conventions from each successful run ([details](docs/FEATURES.md#auto-learning-memory))
|
|
@@ -155,14 +197,126 @@ kody-engine-lite watch [--dry-run] # Run health monitoring locally
|
|
|
155
197
|
- **Kody Watch** — periodic health monitoring: pipeline health, security scanning, config validation every 30 min ([setup guide](docs/WATCH.md))
|
|
156
198
|
- **Anthropic-Compatible Models** — route through LiteLLM to use other providers like MiniMax, Gemini, etc. ([setup guide](docs/LITELLM.md) · [model test results](docs/model-compatibility.md))
|
|
157
199
|
|
|
200
|
+
## Architecture
|
|
201
|
+
|
|
202
|
+
<details>
|
|
203
|
+
<summary>System overview (click to expand)</summary>
|
|
204
|
+
|
|
205
|
+
```mermaid
|
|
206
|
+
flowchart TB
|
|
207
|
+
subgraph trigger ["Trigger"]
|
|
208
|
+
direction LR
|
|
209
|
+
Comment["@kody comment\non GitHub issue"]
|
|
210
|
+
Dispatch["workflow_dispatch\n(manual)"]
|
|
211
|
+
CLI["kody-engine-lite run\n(local CLI)"]
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
subgraph ci ["GitHub Actions"]
|
|
215
|
+
direction LR
|
|
216
|
+
Parse["**parse**\nValidate author\nExtract mode + task ID"]
|
|
217
|
+
Orchestrate["**orchestrate**\nCheckout, install deps,\nstart LiteLLM proxy"]
|
|
218
|
+
Parse --> Orchestrate
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
Entry["**entry.ts**\nPreflight checks → fetch issue →\ncreate runners → build context"]
|
|
222
|
+
|
|
223
|
+
subgraph pipeline ["Pipeline (pipeline.ts)"]
|
|
224
|
+
direction TB
|
|
225
|
+
|
|
226
|
+
subgraph explore ["Session: explore"]
|
|
227
|
+
direction LR
|
|
228
|
+
Taskify["**1 Taskify**\nClassify, scope,\ndetect complexity\n*→ task.json*"]
|
|
229
|
+
Plan["**2 Plan**\nTDD plan with\ndeep reasoning\n*→ plan.md*"]
|
|
230
|
+
Taskify --> Plan
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
subgraph build_session ["Session: build"]
|
|
234
|
+
direction LR
|
|
235
|
+
Build["**3 Build**\nImplement via\nClaude Code tools"]
|
|
236
|
+
Autofix["**Autofix**\nAI-diagnosed\nerror fixes"]
|
|
237
|
+
ReviewFix["**6 Review-Fix**\nFix Critical +\nMajor findings"]
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
subgraph verify_loop ["Quality Gate"]
|
|
241
|
+
Verify["**4 Verify**\ntypecheck + tests + lint"]
|
|
242
|
+
Diagnose{"Fail?"}
|
|
243
|
+
Verify --> Diagnose
|
|
244
|
+
Diagnose -->|"fixable"| Autofix
|
|
245
|
+
Autofix --> Verify
|
|
246
|
+
Diagnose -->|"infra/pre-existing"| Skip["Skip\n(mark passed)"]
|
|
247
|
+
Diagnose -->|"abort"| Abort["Stop pipeline"]
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
subgraph review_session ["Session: review (fresh — no build bias)"]
|
|
251
|
+
Review["**5 Review**\nPASS/FAIL verdict\n+ findings\n*→ review.md*"]
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
Ship["**7 Ship**\nPush branch → create PR\n→ comment on issue"]
|
|
255
|
+
|
|
256
|
+
explore --> build_session
|
|
257
|
+
Build --> verify_loop
|
|
258
|
+
Skip --> review_session
|
|
259
|
+
Diagnose -->|"pass"| review_session
|
|
260
|
+
review_session -->|"FAIL"| ReviewFix
|
|
261
|
+
ReviewFix -->|"retry review\n(max 2)"| review_session
|
|
262
|
+
review_session -->|"PASS"| Ship
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
subgraph support ["Support Systems"]
|
|
266
|
+
direction LR
|
|
267
|
+
Memory["**.kody/memory/**\narchitecture.md\nconventions.md\nobserver-log.jsonl"]
|
|
268
|
+
Steps["**.kody/steps/**\nRepo-customized\nprompts per stage"]
|
|
269
|
+
State["**status.json**\nStage states +\nsession IDs"]
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
subgraph outputs ["Outputs"]
|
|
273
|
+
direction LR
|
|
274
|
+
PR["Pull Request\nwith Closes #N"]
|
|
275
|
+
Labels["GitHub Labels\nkody:planning → kody:done"]
|
|
276
|
+
Artifacts["Task Artifacts\n.kody/tasks/id/"]
|
|
277
|
+
Learn["Auto-Learn\n+ Retrospective"]
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
QuestionGate{"Questions?\nPause for\nhuman input"}
|
|
281
|
+
RiskGate{"HIGH risk?\nPause for\napproval"}
|
|
282
|
+
|
|
283
|
+
Comment --> ci
|
|
284
|
+
Dispatch --> Orchestrate
|
|
285
|
+
CLI --> Entry
|
|
286
|
+
Orchestrate --> Entry
|
|
287
|
+
Entry --> pipeline
|
|
288
|
+
|
|
289
|
+
Taskify -.-> QuestionGate
|
|
290
|
+
Plan -.-> RiskGate
|
|
291
|
+
QuestionGate -.->|"@kody approve"| Plan
|
|
292
|
+
RiskGate -.->|"@kody approve"| Build
|
|
293
|
+
|
|
294
|
+
pipeline <-..-> support
|
|
295
|
+
Ship --> outputs
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
</details>
|
|
299
|
+
|
|
300
|
+
[Full architecture docs →](docs/ARCHITECTURE.md)
|
|
301
|
+
|
|
158
302
|
## Documentation
|
|
159
303
|
|
|
160
304
|
**Understand Kody:** [About](docs/ABOUT.md) · [Architecture](docs/ARCHITECTURE.md) · [Tech Stack](docs/TECH-STACK.md) · [Features](docs/FEATURES.md) · [Pipeline](docs/PIPELINE.md) · [Comparison](docs/COMPARISON.md)
|
|
161
305
|
|
|
162
|
-
**Set up & use:** [CLI](docs/CLI.md) · [Configuration](docs/CONFIGURATION.md) · [Bootstrap](docs/BOOTSTRAP.md) · [Tools](docs/TOOLS.md) · [Watch](docs/WATCH.md) · [LiteLLM](docs/LITELLM.md)
|
|
306
|
+
**Set up & use:** [CLI](docs/CLI.md) · [Configuration](docs/CONFIGURATION.md) · [Bootstrap](docs/BOOTSTRAP.md) · [Decompose](docs/DECOMPOSE.md) · [Tools](docs/TOOLS.md) · [Watch](docs/WATCH.md) · [LiteLLM](docs/LITELLM.md)
|
|
163
307
|
|
|
164
308
|
**Reference:** [FAQ](docs/FAQ.md) · [Model Compatibility](docs/model-compatibility.md)
|
|
165
309
|
|
|
310
|
+
## Generating Demo GIFs
|
|
311
|
+
|
|
312
|
+
Demo GIFs can be generated using [VHS](https://github.com/charmbracelet/vhs). Tape files are in `assets/tapes/`:
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
brew install vhs # install VHS
|
|
316
|
+
vhs assets/tapes/init.tape # → assets/demo-init.gif
|
|
317
|
+
vhs assets/tapes/run-local.tape # → assets/demo-run.gif (runs a real pipeline)
|
|
318
|
+
```
|
|
319
|
+
|
|
166
320
|
## License
|
|
167
321
|
|
|
168
322
|
MIT
|
package/dist/bin/cli.js
CHANGED
|
@@ -5622,6 +5622,7 @@ async function executeAgentStage(ctx, def) {
|
|
|
5622
5622
|
return { outcome: "completed", retries: 0 };
|
|
5623
5623
|
}
|
|
5624
5624
|
const prompt = buildFullPrompt(def.name, ctx.taskId, ctx.taskDir, ctx.projectDir, ctx.input.feedback);
|
|
5625
|
+
const promptTokens = estimateTokens(prompt);
|
|
5625
5626
|
let currentModelTier = def.modelTier;
|
|
5626
5627
|
if (ctx.input.feedback && def.name === "build") {
|
|
5627
5628
|
logger.info(` feedback: ${ctx.input.feedback.slice(0, 200)}${ctx.input.feedback.length > 200 ? "..." : ""}`);
|
|
@@ -5653,14 +5654,8 @@ async function executeAgentStage(ctx, def) {
|
|
|
5653
5654
|
if (ds && devServerStages.includes(def.name) && taskHasUI(ctx.taskDir)) {
|
|
5654
5655
|
logger.info(` Starting dev server: ${ds.command}`);
|
|
5655
5656
|
const envVars = {};
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
if (process.env[varName]) envVars[varName] = process.env[varName];
|
|
5659
|
-
}
|
|
5660
|
-
} else {
|
|
5661
|
-
for (const [k, v] of Object.entries(process.env)) {
|
|
5662
|
-
if (v !== void 0) envVars[k] = v;
|
|
5663
|
-
}
|
|
5657
|
+
for (const [k, v] of Object.entries(process.env)) {
|
|
5658
|
+
if (v !== void 0) envVars[k] = v;
|
|
5664
5659
|
}
|
|
5665
5660
|
devServerHandle = await startDevServer({
|
|
5666
5661
|
command: ds.command,
|
|
@@ -5710,7 +5705,7 @@ async function executeAgentStage(ctx, def) {
|
|
|
5710
5705
|
logger.info(` Dev server stopped`);
|
|
5711
5706
|
}
|
|
5712
5707
|
if (lastResult.outcome !== "completed") {
|
|
5713
|
-
return { outcome: lastResult.outcome, error: lastResult.error, retries };
|
|
5708
|
+
return { outcome: lastResult.outcome, error: lastResult.error, retries, promptTokens };
|
|
5714
5709
|
}
|
|
5715
5710
|
const result2 = lastResult;
|
|
5716
5711
|
if (def.outputFile && result2.output) {
|
|
@@ -5772,7 +5767,7 @@ async function executeAgentStage(ctx, def) {
|
|
|
5772
5767
|
}
|
|
5773
5768
|
}
|
|
5774
5769
|
appendStageContext(ctx.taskDir, def.name, result2.output);
|
|
5775
|
-
return { outcome: "completed", outputFile: def.outputFile, retries };
|
|
5770
|
+
return { outcome: "completed", outputFile: def.outputFile, retries, promptTokens };
|
|
5776
5771
|
}
|
|
5777
5772
|
function appendStageContext(taskDir, stageName, output) {
|
|
5778
5773
|
const contextPath = path23.join(taskDir, "context.md");
|
|
@@ -5795,6 +5790,7 @@ var init_agent = __esm({
|
|
|
5795
5790
|
"src/stages/agent.ts"() {
|
|
5796
5791
|
"use strict";
|
|
5797
5792
|
init_context();
|
|
5793
|
+
init_context_tiers();
|
|
5798
5794
|
init_validators();
|
|
5799
5795
|
init_config();
|
|
5800
5796
|
init_mcp_config();
|
|
@@ -6968,8 +6964,9 @@ function collectRunContext(ctx, state, pipelineStartTime) {
|
|
|
6968
6964
|
const s = state.stages[def.name];
|
|
6969
6965
|
const duration = computeStageDuration(s);
|
|
6970
6966
|
const durationStr = duration != null ? `, ${duration}ms` : "";
|
|
6967
|
+
const tokenStr = s.promptTokens != null ? `, ~${s.promptTokens} prompt tokens` : "";
|
|
6971
6968
|
const errorStr = s.error ? ` \u2014 ${s.error}` : "";
|
|
6972
|
-
lines.push(`${def.name}: ${s.state} (${s.retries} retries${durationStr})${errorStr}`);
|
|
6969
|
+
lines.push(`${def.name}: ${s.state} (${s.retries} retries${durationStr}${tokenStr})${errorStr}`);
|
|
6973
6970
|
if (s.state === "failed" || s.state === "timeout") {
|
|
6974
6971
|
failedStage = def.name;
|
|
6975
6972
|
}
|
|
@@ -7034,6 +7031,23 @@ function appendRetrospectiveEntry(projectDir, entry) {
|
|
|
7034
7031
|
}
|
|
7035
7032
|
fs34.appendFileSync(logPath, JSON.stringify(entry) + "\n");
|
|
7036
7033
|
}
|
|
7034
|
+
function computeTokenStats(state, projectDir) {
|
|
7035
|
+
const perStage = {};
|
|
7036
|
+
let totalPromptTokens = 0;
|
|
7037
|
+
let hasAny = false;
|
|
7038
|
+
for (const def of STAGES) {
|
|
7039
|
+
const s = state.stages[def.name];
|
|
7040
|
+
if (s.promptTokens != null) {
|
|
7041
|
+
perStage[def.name] = s.promptTokens;
|
|
7042
|
+
totalPromptTokens += s.promptTokens;
|
|
7043
|
+
hasAny = true;
|
|
7044
|
+
}
|
|
7045
|
+
}
|
|
7046
|
+
if (!hasAny) return void 0;
|
|
7047
|
+
const memory = readProjectMemory(projectDir);
|
|
7048
|
+
const memoryTokens = memory ? estimateTokens(memory) : 0;
|
|
7049
|
+
return { totalPromptTokens, memoryTokens, perStage };
|
|
7050
|
+
}
|
|
7037
7051
|
async function runRetrospective(ctx, state, pipelineStartTime) {
|
|
7038
7052
|
if (ctx.input.dryRun) return;
|
|
7039
7053
|
try {
|
|
@@ -7094,6 +7108,7 @@ ${previousText}
|
|
|
7094
7108
|
failedStage = def.name;
|
|
7095
7109
|
}
|
|
7096
7110
|
}
|
|
7111
|
+
const tokenStats = computeTokenStats(state, ctx.projectDir);
|
|
7097
7112
|
const entry = {
|
|
7098
7113
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7099
7114
|
taskId: state.taskId,
|
|
@@ -7104,7 +7119,8 @@ ${previousText}
|
|
|
7104
7119
|
observation,
|
|
7105
7120
|
patternMatch,
|
|
7106
7121
|
suggestion,
|
|
7107
|
-
pipelineFlaw
|
|
7122
|
+
pipelineFlaw,
|
|
7123
|
+
tokenStats
|
|
7108
7124
|
};
|
|
7109
7125
|
appendRetrospectiveEntry(ctx.projectDir, entry);
|
|
7110
7126
|
logger.info(`Retrospective: ${observation.slice(0, 120)}`);
|
|
@@ -7120,6 +7136,8 @@ var init_retrospective = __esm({
|
|
|
7120
7136
|
init_context();
|
|
7121
7137
|
init_config();
|
|
7122
7138
|
init_runner_selection();
|
|
7139
|
+
init_memory();
|
|
7140
|
+
init_context_tiers();
|
|
7123
7141
|
init_logger();
|
|
7124
7142
|
RETROSPECTIVE_PROMPT = `You are a pipeline retrospective analyst. You observe automated software development pipeline runs and identify flaws, patterns, and improvement opportunities.
|
|
7125
7143
|
|
|
@@ -7449,7 +7467,8 @@ async function runPipelineInner(ctx) {
|
|
|
7449
7467
|
state: "completed",
|
|
7450
7468
|
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7451
7469
|
retries: result2.retries,
|
|
7452
|
-
outputFile: result2.outputFile
|
|
7470
|
+
outputFile: result2.outputFile,
|
|
7471
|
+
promptTokens: result2.promptTokens
|
|
7453
7472
|
};
|
|
7454
7473
|
logger.info(`[${def.name}] \u2713 completed`);
|
|
7455
7474
|
const detected = autoDetectComplexity(ctx, def);
|
|
@@ -7467,7 +7486,8 @@ async function runPipelineInner(ctx) {
|
|
|
7467
7486
|
state.stages[def.name] = {
|
|
7468
7487
|
state: isTimeout ? "timeout" : "failed",
|
|
7469
7488
|
retries: result2.retries,
|
|
7470
|
-
error: isTimeout ? "Stage timed out" : result2.error ?? "Stage failed"
|
|
7489
|
+
error: isTimeout ? "Stage timed out" : result2.error ?? "Stage failed",
|
|
7490
|
+
promptTokens: result2.promptTokens
|
|
7471
7491
|
};
|
|
7472
7492
|
state.state = "failed";
|
|
7473
7493
|
state.sessions = ctx.sessions;
|
package/kody.config.schema.json
CHANGED
|
@@ -263,11 +263,6 @@
|
|
|
263
263
|
"readyTimeout": {
|
|
264
264
|
"type": "number",
|
|
265
265
|
"description": "Seconds to wait for the server to be ready. Default: 180"
|
|
266
|
-
},
|
|
267
|
-
"env": {
|
|
268
|
-
"type": "array",
|
|
269
|
-
"items": { "type": "string" },
|
|
270
|
-
"description": "List of GitHub secret names to forward as environment variables for the dev server process (e.g., ['BLOB_READ_WRITE_TOKEN', 'DATABASE_URL']). These are injected into the workflow env block during 'kody init'."
|
|
271
266
|
}
|
|
272
267
|
},
|
|
273
268
|
"required": ["command", "url"]
|
|
@@ -295,11 +290,6 @@
|
|
|
295
290
|
"readyTimeout": {
|
|
296
291
|
"type": "number",
|
|
297
292
|
"description": "Seconds to wait for the server to be ready. Default: 180"
|
|
298
|
-
},
|
|
299
|
-
"env": {
|
|
300
|
-
"type": "array",
|
|
301
|
-
"items": { "type": "string" },
|
|
302
|
-
"description": "List of GitHub secret names to forward as environment variables for the dev server process (e.g., ['BLOB_READ_WRITE_TOKEN', 'DATABASE_URL']). These are injected into the workflow env block during 'kody init'."
|
|
303
293
|
}
|
|
304
294
|
},
|
|
305
295
|
"required": ["command", "url"]
|
package/package.json
CHANGED
package/prompts/taskify.md
CHANGED
|
@@ -32,8 +32,8 @@ Required JSON format:
|
|
|
32
32
|
|
|
33
33
|
Risk level heuristics:
|
|
34
34
|
- low: single file change, no breaking changes, docs, config, isolated scripts, test additions, style changes
|
|
35
|
-
- medium:
|
|
36
|
-
- high: core business logic, data migrations, security, authentication, payment processing, database schema changes
|
|
35
|
+
- medium: 2-3 files, possible side effects, API changes, new dependencies, refactoring existing logic, adding a new utility/middleware with tests
|
|
36
|
+
- high: 4+ files across multiple directories, core business logic, data migrations, security, authentication, payment processing, database schema changes, cross-cutting concerns, system redesigns
|
|
37
37
|
|
|
38
38
|
existing_patterns rules:
|
|
39
39
|
- List patterns found in the codebase that are relevant to this task
|
package/templates/kody-watch.yml
CHANGED
|
@@ -35,11 +35,23 @@ jobs:
|
|
|
35
35
|
- name: Install Kody Engine
|
|
36
36
|
run: npm install -g @kody-ade/kody-engine-lite
|
|
37
37
|
|
|
38
|
+
- name: Export project secrets
|
|
39
|
+
env:
|
|
40
|
+
ALL_SECRETS: ${{ toJSON(secrets) }}
|
|
41
|
+
run: |
|
|
42
|
+
echo "$ALL_SECRETS" | jq -r 'to_entries[] | select(.key | test("^(GITHUB_TOKEN)$") | not) | @json' | while IFS= read -r entry; do
|
|
43
|
+
KEY=$(echo "$entry" | jq -r '.key')
|
|
44
|
+
VALUE=$(echo "$entry" | jq -r '.value')
|
|
45
|
+
DELIM="KODY_EOF_${KEY}"
|
|
46
|
+
echo "${KEY}<<${DELIM}" >> $GITHUB_ENV
|
|
47
|
+
echo "${VALUE}" >> $GITHUB_ENV
|
|
48
|
+
echo "${DELIM}" >> $GITHUB_ENV
|
|
49
|
+
done
|
|
50
|
+
|
|
38
51
|
- name: Run Kody Watch
|
|
39
52
|
env:
|
|
40
53
|
GH_TOKEN: ${{ github.token }}
|
|
41
54
|
REPO: ${{ github.repository }}
|
|
42
55
|
WATCH_DIGEST_ISSUE: ${{ vars.WATCH_DIGEST_ISSUE }}
|
|
43
|
-
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
44
56
|
DRY_RUN: ${{ inputs.dry_run || 'false' }}
|
|
45
57
|
run: npx kody-engine-lite watch ${{ inputs.dry_run == 'true' && '--dry-run' || '' }}
|
package/dist/agent-runner.d.ts
DELETED
package/dist/agent-runner.js
DELETED
|
@@ -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
|
-
}
|