@mknightzzz/stw 0.1.0
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 +277 -0
- package/dist/agentic-fallback.d.ts +3 -0
- package/dist/agentic-fallback.js +32 -0
- package/dist/agentic-fallback.js.map +1 -0
- package/dist/agentic-prompt.d.ts +2 -0
- package/dist/agentic-prompt.js +68 -0
- package/dist/agentic-prompt.js.map +1 -0
- package/dist/agentic-runtime.d.ts +48 -0
- package/dist/agentic-runtime.js +149 -0
- package/dist/agentic-runtime.js.map +1 -0
- package/dist/agentic-types.d.ts +37 -0
- package/dist/agentic-types.js +2 -0
- package/dist/agentic-types.js.map +1 -0
- package/dist/agents.d.ts +7 -0
- package/dist/agents.js +2 -0
- package/dist/agents.js.map +1 -0
- package/dist/assignments.d.ts +7 -0
- package/dist/assignments.js +125 -0
- package/dist/assignments.js.map +1 -0
- package/dist/checkpoint.d.ts +35 -0
- package/dist/checkpoint.js +78 -0
- package/dist/checkpoint.js.map +1 -0
- package/dist/circuit-breaker.d.ts +17 -0
- package/dist/circuit-breaker.js +65 -0
- package/dist/circuit-breaker.js.map +1 -0
- package/dist/claim.d.ts +6 -0
- package/dist/claim.js +135 -0
- package/dist/claim.js.map +1 -0
- package/dist/clarity-gate.d.ts +12 -0
- package/dist/clarity-gate.js +83 -0
- package/dist/clarity-gate.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +38 -0
- package/dist/cli.js.map +1 -0
- package/dist/command-dispatch.d.ts +45 -0
- package/dist/command-dispatch.js +206 -0
- package/dist/command-dispatch.js.map +1 -0
- package/dist/command-parser.d.ts +11 -0
- package/dist/command-parser.js +101 -0
- package/dist/command-parser.js.map +1 -0
- package/dist/commands/clean.d.ts +10 -0
- package/dist/commands/clean.js +133 -0
- package/dist/commands/clean.js.map +1 -0
- package/dist/commands/execution.d.ts +2 -0
- package/dist/commands/execution.js +327 -0
- package/dist/commands/execution.js.map +1 -0
- package/dist/commands/go.d.ts +2 -0
- package/dist/commands/go.js +197 -0
- package/dist/commands/go.js.map +1 -0
- package/dist/commands/helpers.d.ts +44 -0
- package/dist/commands/helpers.js +231 -0
- package/dist/commands/helpers.js.map +1 -0
- package/dist/commands/idea.d.ts +2 -0
- package/dist/commands/idea.js +89 -0
- package/dist/commands/idea.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +94 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/integration.d.ts +7 -0
- package/dist/commands/integration.js +139 -0
- package/dist/commands/integration.js.map +1 -0
- package/dist/commands/maintenance.d.ts +2 -0
- package/dist/commands/maintenance.js +301 -0
- package/dist/commands/maintenance.js.map +1 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +356 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +198 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/spec.d.ts +2 -0
- package/dist/commands/spec.js +35 -0
- package/dist/commands/spec.js.map +1 -0
- package/dist/commands/stats.d.ts +2 -0
- package/dist/commands/stats.js +80 -0
- package/dist/commands/stats.js.map +1 -0
- package/dist/commands/task-ops.d.ts +2 -0
- package/dist/commands/task-ops.js +406 -0
- package/dist/commands/task-ops.js.map +1 -0
- package/dist/config.d.ts +18 -0
- package/dist/config.js +338 -0
- package/dist/config.js.map +1 -0
- package/dist/cost.d.ts +30 -0
- package/dist/cost.js +167 -0
- package/dist/cost.js.map +1 -0
- package/dist/crash-recovery.d.ts +9 -0
- package/dist/crash-recovery.js +42 -0
- package/dist/crash-recovery.js.map +1 -0
- package/dist/diagnostic.d.ts +48 -0
- package/dist/diagnostic.js +328 -0
- package/dist/diagnostic.js.map +1 -0
- package/dist/doctor.d.ts +31 -0
- package/dist/doctor.js +225 -0
- package/dist/doctor.js.map +1 -0
- package/dist/drift.d.ts +11 -0
- package/dist/drift.js +57 -0
- package/dist/drift.js.map +1 -0
- package/dist/git-utils.d.ts +20 -0
- package/dist/git-utils.js +206 -0
- package/dist/git-utils.js.map +1 -0
- package/dist/gitlab.d.ts +54 -0
- package/dist/gitlab.js +101 -0
- package/dist/gitlab.js.map +1 -0
- package/dist/idea.d.ts +35 -0
- package/dist/idea.js +251 -0
- package/dist/idea.js.map +1 -0
- package/dist/import-resolution.d.ts +13 -0
- package/dist/import-resolution.js +111 -0
- package/dist/import-resolution.js.map +1 -0
- package/dist/inbox-renderer.d.ts +2 -0
- package/dist/inbox-renderer.js +67 -0
- package/dist/inbox-renderer.js.map +1 -0
- package/dist/init.d.ts +105 -0
- package/dist/init.js +235 -0
- package/dist/init.js.map +1 -0
- package/dist/llm-reviewer.d.ts +14 -0
- package/dist/llm-reviewer.js +109 -0
- package/dist/llm-reviewer.js.map +1 -0
- package/dist/lock.d.ts +26 -0
- package/dist/lock.js +76 -0
- package/dist/lock.js.map +1 -0
- package/dist/logger.d.ts +24 -0
- package/dist/logger.js +40 -0
- package/dist/logger.js.map +1 -0
- package/dist/math-utils.d.ts +2 -0
- package/dist/math-utils.js +7 -0
- package/dist/math-utils.js.map +1 -0
- package/dist/mechanical-review.d.ts +30 -0
- package/dist/mechanical-review.js +76 -0
- package/dist/mechanical-review.js.map +1 -0
- package/dist/merge.d.ts +83 -0
- package/dist/merge.js +363 -0
- package/dist/merge.js.map +1 -0
- package/dist/parallel.d.ts +35 -0
- package/dist/parallel.js +214 -0
- package/dist/parallel.js.map +1 -0
- package/dist/plan-validation.d.ts +19 -0
- package/dist/plan-validation.js +253 -0
- package/dist/plan-validation.js.map +1 -0
- package/dist/planner-prompt.d.ts +33 -0
- package/dist/planner-prompt.js +244 -0
- package/dist/planner-prompt.js.map +1 -0
- package/dist/planner.d.ts +29 -0
- package/dist/planner.js +511 -0
- package/dist/planner.js.map +1 -0
- package/dist/poller.d.ts +34 -0
- package/dist/poller.js +91 -0
- package/dist/poller.js.map +1 -0
- package/dist/progress.d.ts +34 -0
- package/dist/progress.js +122 -0
- package/dist/progress.js.map +1 -0
- package/dist/prompt-builder.d.ts +51 -0
- package/dist/prompt-builder.js +481 -0
- package/dist/prompt-builder.js.map +1 -0
- package/dist/provider.d.ts +14 -0
- package/dist/provider.js +278 -0
- package/dist/provider.js.map +1 -0
- package/dist/question-handler.d.ts +18 -0
- package/dist/question-handler.js +154 -0
- package/dist/question-handler.js.map +1 -0
- package/dist/question-triage.d.ts +31 -0
- package/dist/question-triage.js +175 -0
- package/dist/question-triage.js.map +1 -0
- package/dist/repo-detection.d.ts +8 -0
- package/dist/repo-detection.js +18 -0
- package/dist/repo-detection.js.map +1 -0
- package/dist/retry-context.d.ts +2 -0
- package/dist/retry-context.js +196 -0
- package/dist/retry-context.js.map +1 -0
- package/dist/router.d.ts +18 -0
- package/dist/router.js +137 -0
- package/dist/router.js.map +1 -0
- package/dist/run-artifact-types.d.ts +43 -0
- package/dist/run-artifact-types.js +2 -0
- package/dist/run-artifact-types.js.map +1 -0
- package/dist/run-summary.d.ts +14 -0
- package/dist/run-summary.js +347 -0
- package/dist/run-summary.js.map +1 -0
- package/dist/run-sync.d.ts +11 -0
- package/dist/run-sync.js +110 -0
- package/dist/run-sync.js.map +1 -0
- package/dist/run.d.ts +26 -0
- package/dist/run.js +150 -0
- package/dist/run.js.map +1 -0
- package/dist/scope-expansion.d.ts +10 -0
- package/dist/scope-expansion.js +117 -0
- package/dist/scope-expansion.js.map +1 -0
- package/dist/scope.d.ts +4 -0
- package/dist/scope.js +37 -0
- package/dist/scope.js.map +1 -0
- package/dist/scorecard.d.ts +18 -0
- package/dist/scorecard.js +128 -0
- package/dist/scorecard.js.map +1 -0
- package/dist/spec-templates.d.ts +2 -0
- package/dist/spec-templates.js +285 -0
- package/dist/spec-templates.js.map +1 -0
- package/dist/spec-validator.d.ts +8 -0
- package/dist/spec-validator.js +144 -0
- package/dist/spec-validator.js.map +1 -0
- package/dist/status.d.ts +68 -0
- package/dist/status.js +261 -0
- package/dist/status.js.map +1 -0
- package/dist/storage.d.ts +9 -0
- package/dist/storage.js +35 -0
- package/dist/storage.js.map +1 -0
- package/dist/task-executor-completion.d.ts +12 -0
- package/dist/task-executor-completion.js +67 -0
- package/dist/task-executor-completion.js.map +1 -0
- package/dist/task-executor-fallback.d.ts +20 -0
- package/dist/task-executor-fallback.js +12 -0
- package/dist/task-executor-fallback.js.map +1 -0
- package/dist/task-executor.d.ts +34 -0
- package/dist/task-executor.js +521 -0
- package/dist/task-executor.js.map +1 -0
- package/dist/task-graph.d.ts +11 -0
- package/dist/task-graph.js +226 -0
- package/dist/task-graph.js.map +1 -0
- package/dist/task-pipeline-helpers.d.ts +45 -0
- package/dist/task-pipeline-helpers.js +160 -0
- package/dist/task-pipeline-helpers.js.map +1 -0
- package/dist/task-review.d.ts +51 -0
- package/dist/task-review.js +410 -0
- package/dist/task-review.js.map +1 -0
- package/dist/transitions.d.ts +13 -0
- package/dist/transitions.js +104 -0
- package/dist/transitions.js.map +1 -0
- package/dist/types.d.ts +405 -0
- package/dist/types.js +101 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +23 -0
- package/dist/utils.js.map +1 -0
- package/dist/validation.d.ts +19 -0
- package/dist/validation.js +73 -0
- package/dist/validation.js.map +1 -0
- package/dist/worker-response.d.ts +12 -0
- package/dist/worker-response.js +60 -0
- package/dist/worker-response.js.map +1 -0
- package/dist/worker-runner.d.ts +19 -0
- package/dist/worker-runner.js +347 -0
- package/dist/worker-runner.js.map +1 -0
- package/dist/worktree-cleanup.d.ts +44 -0
- package/dist/worktree-cleanup.js +325 -0
- package/dist/worktree-cleanup.js.map +1 -0
- package/dist/worktree.d.ts +22 -0
- package/dist/worktree.js +213 -0
- package/dist/worktree.js.map +1 -0
- package/examples/spec.md +58 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 mknightz
|
|
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
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# stw
|
|
2
|
+
|
|
3
|
+
**Spec-to-Workers** (pronounced "stew") — a CLI orchestrator that turns markdown specs into reviewed, merge-ready code changes using AI agents.
|
|
4
|
+
|
|
5
|
+
You write a spec (or describe an idea), STW decomposes it into isolated tasks, routes each to an AI worker, validates the output, and produces a merge request with all changes.
|
|
6
|
+
|
|
7
|
+
## How it works
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
spec.md ──> planner ──> task graph ──> workers ──> review ──> MR
|
|
11
|
+
T1 ──────> worker ──> validate ──┐
|
|
12
|
+
T2 ──────> worker ──> validate ──┤
|
|
13
|
+
T3 (dep: T1) ─> worker ─> validate ┘
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
1. **Ingest** — validates your spec and creates a run
|
|
17
|
+
2. **Plan** — an AI planner decomposes the spec into a task graph with scopes, dependencies, and acceptance checks
|
|
18
|
+
3. **Run** — workers execute tasks in dependency order, each in an isolated git worktree
|
|
19
|
+
4. **Review** — mechanical checks (tests, typecheck, scope) + optional LLM review
|
|
20
|
+
5. **Retry** — failed tasks get diagnostic context and retry with tier escalation
|
|
21
|
+
6. **Merge** — completed work is pushed and synced as a GitLab/GitHub MR
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
### 1. Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install -g stw
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 2. Prerequisites
|
|
32
|
+
|
|
33
|
+
STW needs an API provider for AI models and (optionally) an agentic backend for complex tasks.
|
|
34
|
+
|
|
35
|
+
**API provider** (required): An OpenAI-compatible API endpoint. [OpenRouter](https://openrouter.ai) works out of the box and gives access to multiple model families.
|
|
36
|
+
|
|
37
|
+
**Agentic backend** (optional, recommended): Either [OpenCode](https://github.com/opencode-ai/opencode) or [Claude Code](https://docs.anthropic.com/en/docs/claude-code) for tasks that need iterative exploration (read-edit-test loops).
|
|
38
|
+
|
|
39
|
+
### 3. Initialize
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
cd your-repo
|
|
43
|
+
stw init
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This creates `.stw/config.yml` with model routing, budget limits, and backend settings. Default preset is `minimax` (balanced cost/quality). Other presets: `glm5` (budget), `anthropic` (premium), `mixed` (multi-provider).
|
|
47
|
+
|
|
48
|
+
Set your API key:
|
|
49
|
+
```bash
|
|
50
|
+
export OPENROUTER_API_KEY="sk-or-..."
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 4. Check readiness
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
stw doctor
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Verifies: API keys are set, agentic backends are installed, config is valid.
|
|
60
|
+
|
|
61
|
+
### 5. Create a spec
|
|
62
|
+
|
|
63
|
+
Write a markdown spec describing what you want built:
|
|
64
|
+
|
|
65
|
+
```markdown
|
|
66
|
+
# Spec: Add rate limiting to API endpoints
|
|
67
|
+
|
|
68
|
+
## Objective
|
|
69
|
+
Add configurable rate limiting to all public API endpoints.
|
|
70
|
+
|
|
71
|
+
## Requirements
|
|
72
|
+
|
|
73
|
+
### T1: Rate limiter middleware
|
|
74
|
+
Create a rate limiting middleware in src/middleware/rate-limit.ts.
|
|
75
|
+
- Accept requests-per-minute and burst-size as parameters
|
|
76
|
+
- Use a sliding window algorithm
|
|
77
|
+
- Return 429 with Retry-After header when exceeded
|
|
78
|
+
|
|
79
|
+
### T2: Apply middleware to routes
|
|
80
|
+
Wire the rate limiter into src/routes/api.ts for all public endpoints.
|
|
81
|
+
- Default: 60 requests/minute, burst of 10
|
|
82
|
+
- Make limits configurable via environment variables
|
|
83
|
+
|
|
84
|
+
## Constraints
|
|
85
|
+
- No external dependencies (use in-memory store)
|
|
86
|
+
- Must not break existing tests
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Save it as `specs/rate-limiting.md`.
|
|
90
|
+
|
|
91
|
+
### 6. Run
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# Ingest the spec and create a run
|
|
95
|
+
stw ingest specs/rate-limiting.md
|
|
96
|
+
|
|
97
|
+
# Auto-plan tasks (or provide your own task graph)
|
|
98
|
+
stw plan-auto <run-id>
|
|
99
|
+
|
|
100
|
+
# Execute all tasks
|
|
101
|
+
stw run <run-id>
|
|
102
|
+
|
|
103
|
+
# Check progress
|
|
104
|
+
stw progress <run-id>
|
|
105
|
+
|
|
106
|
+
# Push to GitLab/GitHub
|
|
107
|
+
stw merge <run-id>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Or use the shortcut that does ingest + plan + run in one command:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
stw go "add rate limiting to API endpoints" --spec specs/rate-limiting.md
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Or skip the spec entirely — just describe what you want:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
stw go "add dark mode toggle to the settings page"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
STW generates a structured spec from your rough idea, plans tasks, and executes them.
|
|
123
|
+
|
|
124
|
+
## Configuration
|
|
125
|
+
|
|
126
|
+
STW uses `.stw/config.yml` in your repo root. Key sections:
|
|
127
|
+
|
|
128
|
+
```yaml
|
|
129
|
+
# API providers — where to send model requests
|
|
130
|
+
providers:
|
|
131
|
+
openrouter:
|
|
132
|
+
api_key_env: OPENROUTER_API_KEY # env var name (not the key itself)
|
|
133
|
+
base_url: https://openrouter.ai/api/v1
|
|
134
|
+
models:
|
|
135
|
+
strong: anthropic/claude-sonnet-4 # for planning, review, complex tasks
|
|
136
|
+
medium: openai/gpt-4.1-mini # for moderate tasks
|
|
137
|
+
cheap: google/gemini-2.5-flash # for mechanical/simple tasks
|
|
138
|
+
|
|
139
|
+
# How tasks are routed to providers
|
|
140
|
+
routing_policy:
|
|
141
|
+
require_plan_approval: true # require human approval before execution
|
|
142
|
+
provider_preferences:
|
|
143
|
+
strong: [openrouter]
|
|
144
|
+
medium: [openrouter]
|
|
145
|
+
cheap: [openrouter]
|
|
146
|
+
|
|
147
|
+
# Execution defaults
|
|
148
|
+
defaults:
|
|
149
|
+
max_retries: 2 # retries per task before escalation
|
|
150
|
+
max_concurrent_workers: 2 # parallel task execution
|
|
151
|
+
task_timeout_minutes: 30
|
|
152
|
+
api_timeout_seconds: 120
|
|
153
|
+
execution_mode: agentic # 'agentic' or 'conservative'
|
|
154
|
+
|
|
155
|
+
# Agentic backend config (for iterative task execution)
|
|
156
|
+
agentic:
|
|
157
|
+
default_backend: opencode # 'opencode' or 'claude-code'
|
|
158
|
+
backends:
|
|
159
|
+
opencode:
|
|
160
|
+
command: opencode # path to opencode binary
|
|
161
|
+
claude-code:
|
|
162
|
+
command: claude # path to claude binary
|
|
163
|
+
|
|
164
|
+
# Optional: per-run budget limits
|
|
165
|
+
budget:
|
|
166
|
+
max_run_usd: 5.00
|
|
167
|
+
warn_at_usd: 3.00
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Model tiers
|
|
171
|
+
|
|
172
|
+
STW routes tasks to models based on complexity:
|
|
173
|
+
|
|
174
|
+
| Tier | Use case | Example models |
|
|
175
|
+
|------|----------|----------------|
|
|
176
|
+
| **cheap** | Mechanical refactors, simple edits | gemini-2.5-flash, minimax-m2.5 |
|
|
177
|
+
| **medium** | Moderate reasoning, test writing | gpt-4.1-mini, minimax-m2.7 |
|
|
178
|
+
| **strong** | Planning, review, complex logic | claude-sonnet-4, kimi-k2.5 |
|
|
179
|
+
|
|
180
|
+
The planner assigns tiers automatically. If a task fails, STW escalates to the next tier and retries.
|
|
181
|
+
|
|
182
|
+
## Architecture
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
src/
|
|
186
|
+
cli.ts # Command-line interface (commander.js)
|
|
187
|
+
planner.ts # AI planner — decomposes specs into task graphs
|
|
188
|
+
planner-prompt.ts # Planner prompt construction + import graph
|
|
189
|
+
task-executor.ts # Task execution pipeline (worker → review → retry)
|
|
190
|
+
worker-runner.ts # Worker dispatch (single-call API or agentic subprocess)
|
|
191
|
+
task-review.ts # Mechanical review + LLM review phase
|
|
192
|
+
validation.ts # Acceptance check execution (tests, typecheck)
|
|
193
|
+
router.ts # Model tier routing with escalation
|
|
194
|
+
provider.ts # OpenAI-compatible API client
|
|
195
|
+
agentic-runtime.ts # Agentic backend subprocess management
|
|
196
|
+
parallel.ts # Parallel task execution + merge-back
|
|
197
|
+
scope.ts # Scope enforcement (which files a task can touch)
|
|
198
|
+
cost.ts # Cost tracking and budget enforcement
|
|
199
|
+
config.ts # Config loading and validation
|
|
200
|
+
git-utils.ts # Git operations (worktrees, diffs, merges)
|
|
201
|
+
|
|
202
|
+
tests/ # 1100+ tests (vitest)
|
|
203
|
+
specs/ # Spec files (input)
|
|
204
|
+
.stw/ # Runtime state (runs, tasks, artifacts)
|
|
205
|
+
config.yml # Project configuration
|
|
206
|
+
runs/ # Per-run state and artifacts
|
|
207
|
+
<run-id>/
|
|
208
|
+
manifest.json # Run metadata
|
|
209
|
+
tasks/
|
|
210
|
+
T1/ # Per-task artifacts
|
|
211
|
+
status.json
|
|
212
|
+
diff.patch
|
|
213
|
+
worker_prompt.md
|
|
214
|
+
response.json
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## CLI commands
|
|
218
|
+
|
|
219
|
+
| Command | Description |
|
|
220
|
+
|---------|-------------|
|
|
221
|
+
| `stw init` | Initialize repo with `.stw/config.yml` |
|
|
222
|
+
| `stw idea <description>` | Generate a spec from a rough idea |
|
|
223
|
+
| `stw ingest <spec>` | Validate spec and create a run |
|
|
224
|
+
| `stw plan-auto <run-id>` | AI-generate task graph from spec |
|
|
225
|
+
| `stw run <run-id>` | Execute all tasks in dependency order |
|
|
226
|
+
| `stw go <spec>` | Ingest + plan + run in one command |
|
|
227
|
+
| `stw progress <run-id>` | Show run progress and task status |
|
|
228
|
+
| `stw status <run-id>` | Detailed run/task status |
|
|
229
|
+
| `stw retry <run-id> <task-id>` | Retry a failed/escalated task |
|
|
230
|
+
| `stw resume <run-id>` | Resume from last checkpoint |
|
|
231
|
+
| `stw stop <run-id>` | Pause a running run |
|
|
232
|
+
| `stw continue <run-id>` | Resume a paused run |
|
|
233
|
+
| `stw merge <run-id>` | Push and create MR |
|
|
234
|
+
| `stw doctor` | Check local readiness |
|
|
235
|
+
| `stw cleanup [run-id]` | Clean up old runs/worktrees |
|
|
236
|
+
| `stw logs <run-id>` | View structured logs |
|
|
237
|
+
|
|
238
|
+
## How tasks execute
|
|
239
|
+
|
|
240
|
+
Each task runs in an isolated git worktree branched from the run branch:
|
|
241
|
+
|
|
242
|
+
1. **Worktree created** — fresh copy of the codebase
|
|
243
|
+
2. **Dependencies installed** — `npm ci` if `package-lock.json` exists
|
|
244
|
+
3. **Worker executes** — either a single API call (fast, cheap) or an agentic session (iterative, higher quality)
|
|
245
|
+
4. **Scope enforced** — worker output is checked against declared scope files
|
|
246
|
+
5. **Acceptance checks run** — tests, typecheck, linter (configured per task)
|
|
247
|
+
6. **LLM review** (optional) — a reviewer model checks the diff
|
|
248
|
+
7. **On failure** — diagnostic context is captured, task retries with the previous attempt's errors
|
|
249
|
+
8. **On exhausted retries** — tier escalates (cheap → medium → strong), or task is marked escalated for human review
|
|
250
|
+
9. **On success** — changes merge back to the run branch
|
|
251
|
+
|
|
252
|
+
## Troubleshooting
|
|
253
|
+
|
|
254
|
+
**"No models configured for requested tier"** — Your `.stw/config.yml` is missing model entries for the tier the planner assigned. Add models to your provider's `models` section or adjust the preset.
|
|
255
|
+
|
|
256
|
+
**"Planner received null content"** — The planner model returned empty output. This happens with some reasoning models when `max_tokens` is too low. Try a different model or increase `api_timeout_seconds`.
|
|
257
|
+
|
|
258
|
+
**Task keeps escalating** — Check `.stw/runs/<run-id>/tasks/<task-id>/diagnostic.json` for the failure reason. Common causes: acceptance checks reference non-existent files, scope is too narrow (missing caller files), or the spec is ambiguous.
|
|
259
|
+
|
|
260
|
+
**"Agentic backend failed"** — The agentic backend (opencode/claude) crashed or timed out. Run `stw doctor` to verify it's installed. Check `agentic_transcript.txt` in the task directory for details.
|
|
261
|
+
|
|
262
|
+
**Budget exceeded** — Run `stw progress <run-id>` to see cost breakdown. Adjust `budget.max_run_usd` in config or use `stw continue <run-id>` to resume with remaining budget.
|
|
263
|
+
|
|
264
|
+
## Development
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
git clone https://gitlab.com/mygelknightz/mamarracho.git stw
|
|
268
|
+
cd stw
|
|
269
|
+
npm install
|
|
270
|
+
npm test # run all tests
|
|
271
|
+
npm run lint # eslint + tsc
|
|
272
|
+
npm run build # compile TypeScript
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## License
|
|
276
|
+
|
|
277
|
+
MIT
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export function shouldFallbackToAgentic(task, config) {
|
|
2
|
+
// If already agentic, no need to fallback
|
|
3
|
+
if (task.planner_metadata?.execution_mode === 'agentic') {
|
|
4
|
+
return false;
|
|
5
|
+
}
|
|
6
|
+
const fallback = config.defaults.agentic_fallback;
|
|
7
|
+
if (!fallback?.enabled) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
// If task_types is empty or undefined, all task types are eligible
|
|
11
|
+
// Only filter if task_types is explicitly provided with items
|
|
12
|
+
if (fallback.task_types && fallback.task_types.length > 0 && !fallback.task_types.includes(task.task_type)) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
// Filter by risk levels if specified
|
|
16
|
+
if (fallback.risk_levels && fallback.risk_levels.length > 0 && !fallback.risk_levels.includes(task.risk)) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
export function buildAgenticFallbackTask(task, config) {
|
|
22
|
+
return {
|
|
23
|
+
...task,
|
|
24
|
+
planner_metadata: {
|
|
25
|
+
...task.planner_metadata,
|
|
26
|
+
execution_mode: 'agentic',
|
|
27
|
+
agentic_backend: task.planner_metadata?.agentic_backend ?? config.defaults.agentic?.default_backend,
|
|
28
|
+
fallback_to_single_call: true,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=agentic-fallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentic-fallback.js","sourceRoot":"","sources":["../src/agentic-fallback.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,uBAAuB,CAAC,IAAU,EAAE,MAAiB;IACnE,0CAA0C;IAC1C,IAAI,IAAI,CAAC,gBAAgB,EAAE,cAAc,KAAK,SAAS,EAAE,CAAC;QACxD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IAClD,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC;QACvB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,mEAAmE;IACnE,8DAA8D;IAC9D,IAAI,QAAQ,CAAC,UAAU,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3G,OAAO,KAAK,CAAC;IACf,CAAC;IAED,qCAAqC;IACrC,IAAI,QAAQ,CAAC,WAAW,IAAI,QAAQ,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACzG,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,IAAU,EAAE,MAAiB;IACpE,OAAO;QACL,GAAG,IAAI;QACP,gBAAgB,EAAE;YAChB,GAAG,IAAI,CAAC,gBAAgB;YACxB,cAAc,EAAE,SAAS;YACzB,eAAe,EAAE,IAAI,CAAC,gBAAgB,EAAE,eAAe,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,eAAe;YACnG,uBAAuB,EAAE,IAAI;SAC9B;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { writeAtomic } from './storage.js';
|
|
3
|
+
import { buildRetryContext, gatherTaskContext, getSpecContext, renderContextFiles, renderList, withResolvedQuestionAnswers, } from './prompt-builder.js';
|
|
4
|
+
export function buildAgenticPrompt(options) {
|
|
5
|
+
const resolvedOptions = withResolvedQuestionAnswers(options);
|
|
6
|
+
const contextFiles = gatherTaskContext(resolvedOptions.repoRoot, resolvedOptions.task.scope.files);
|
|
7
|
+
const prompt = renderAgenticPrompt(resolvedOptions, contextFiles);
|
|
8
|
+
writeAtomic(join(resolvedOptions.taskDir, 'agentic_prompt.md'), prompt + '\n');
|
|
9
|
+
return prompt;
|
|
10
|
+
}
|
|
11
|
+
function renderAgenticPrompt(options, contextFiles) {
|
|
12
|
+
const specContext = getSpecContext(options.repoRoot, options.task.spec_path, options.task.id);
|
|
13
|
+
const specSection = specContext ? ['', '## Spec Context', '', specContext] : [];
|
|
14
|
+
const questionAnswers = options.questionAnswers
|
|
15
|
+
? Object.entries(options.questionAnswers)
|
|
16
|
+
.map(([questionNumber, answer]) => [Number(questionNumber), answer])
|
|
17
|
+
.sort((left, right) => left[0] - right[0])
|
|
18
|
+
: [];
|
|
19
|
+
const persistedAnswers = questionAnswers.length > 0
|
|
20
|
+
? [
|
|
21
|
+
'',
|
|
22
|
+
'## Persisted Human Answers',
|
|
23
|
+
'The following answers have been provided to previous questions:',
|
|
24
|
+
...questionAnswers.map(([num, answer]) => `- Question ${num}: ${answer}`),
|
|
25
|
+
]
|
|
26
|
+
: [];
|
|
27
|
+
const retryContext = options.retryReason
|
|
28
|
+
? ['', buildRetryContext(options.taskDir, options.retryReason, options.retryContextLevel ?? 'full')]
|
|
29
|
+
: [];
|
|
30
|
+
return [
|
|
31
|
+
'You are the worker for one Spec-to-Workers task running in agentic mode.',
|
|
32
|
+
`Task ID: ${options.task.id}`,
|
|
33
|
+
`Task Description: ${options.task.description}`,
|
|
34
|
+
'',
|
|
35
|
+
'You are executing directly inside the task worktree.',
|
|
36
|
+
'Edit files in place only within the declared task scope.',
|
|
37
|
+
'Do not return a structured JSON file-response artifact.',
|
|
38
|
+
'Your job is to mutate the worktree directly and leave the final state ready for validation.',
|
|
39
|
+
'',
|
|
40
|
+
'Allowed file scope:',
|
|
41
|
+
renderList(options.task.scope.files),
|
|
42
|
+
'',
|
|
43
|
+
'Prohibited areas:',
|
|
44
|
+
'- Any file not explicitly listed in the allowed file scope',
|
|
45
|
+
'- Supporting context files included below are read-only unless they are also in scope',
|
|
46
|
+
'',
|
|
47
|
+
'Success conditions:',
|
|
48
|
+
'- The resulting worktree changes stay within declared scope',
|
|
49
|
+
'- Acceptance checks should pass after your edits',
|
|
50
|
+
'- Leave the worktree in a reviewable state',
|
|
51
|
+
'',
|
|
52
|
+
'Code conventions:',
|
|
53
|
+
'- TypeScript with ESM modules: use .js extensions in relative imports (e.g., import { foo } from "./bar.js")',
|
|
54
|
+
'- Follow the patterns shown in the repository context files below',
|
|
55
|
+
'',
|
|
56
|
+
'Acceptance checks:',
|
|
57
|
+
renderList(options.task.acceptance_checks),
|
|
58
|
+
...retryContext,
|
|
59
|
+
...persistedAnswers,
|
|
60
|
+
...specSection,
|
|
61
|
+
'',
|
|
62
|
+
'Repository context:',
|
|
63
|
+
renderContextFiles(contextFiles),
|
|
64
|
+
]
|
|
65
|
+
.flat()
|
|
66
|
+
.join('\n');
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=agentic-prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentic-prompt.js","sourceRoot":"","sources":["../src/agentic-prompt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { AgenticBackend, AgenticBackendConfig } from './agentic-types.js';
|
|
2
|
+
import type { ActionableFailureClass, ExecutionObservabilityMode, StwConfig, Task } from './types.js';
|
|
3
|
+
export interface AgenticRunArtifact {
|
|
4
|
+
schema_version: 1;
|
|
5
|
+
task_id: string;
|
|
6
|
+
backend: AgenticBackend;
|
|
7
|
+
command: string[];
|
|
8
|
+
transcript_path: string;
|
|
9
|
+
summary_path: string;
|
|
10
|
+
changed_files: string[];
|
|
11
|
+
acceptance_checks_executed: boolean;
|
|
12
|
+
fallback_used: boolean;
|
|
13
|
+
exit_code: number;
|
|
14
|
+
model?: string;
|
|
15
|
+
execution_mode: ExecutionObservabilityMode;
|
|
16
|
+
failure_class?: ActionableFailureClass;
|
|
17
|
+
}
|
|
18
|
+
export declare function buildAgenticCommand(backend: AgenticBackend, prompt: string, promptPath: string, commandOverride?: string[], modelOverride?: string): string[];
|
|
19
|
+
export declare function formatBackendTranscript(backend: AgenticBackend, transcript: string, backendConfig?: AgenticBackendConfig): string;
|
|
20
|
+
export declare function getTaskExecutionObservabilityMode(task: Task): ExecutionObservabilityMode;
|
|
21
|
+
export declare function persistAgenticArtifacts(taskDir: string, input: {
|
|
22
|
+
task: Task;
|
|
23
|
+
backend: AgenticBackend;
|
|
24
|
+
command: string[];
|
|
25
|
+
transcript: string;
|
|
26
|
+
changedFiles: string[];
|
|
27
|
+
exitCode: number;
|
|
28
|
+
model?: string;
|
|
29
|
+
failureClass?: ActionableFailureClass;
|
|
30
|
+
}): void;
|
|
31
|
+
export declare function listChangedFiles(output: string, toRelativePath: (filePath: string) => string): string[];
|
|
32
|
+
export declare function resolveAgenticTierSelection(task: Task, config: StwConfig): {
|
|
33
|
+
backend: AgenticBackend;
|
|
34
|
+
model?: string;
|
|
35
|
+
};
|
|
36
|
+
export interface AgenticUsage {
|
|
37
|
+
input_tokens: number;
|
|
38
|
+
output_tokens: number;
|
|
39
|
+
cost_usd: number;
|
|
40
|
+
steps: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Parses JSONL output from an agentic backend (opencode --format json or claude-code --output-format json)
|
|
44
|
+
* and sums token usage and cost from all step_finish events.
|
|
45
|
+
*
|
|
46
|
+
* Returns null if no step_finish events are found (e.g. plain text output).
|
|
47
|
+
*/
|
|
48
|
+
export declare function parseAgenticUsage(output: string): AgenticUsage | null;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { writeAtomic, writeJson } from './storage.js';
|
|
3
|
+
export function buildAgenticCommand(backend, prompt, promptPath, commandOverride, modelOverride) {
|
|
4
|
+
if (backend === 'opencode') {
|
|
5
|
+
const base = commandOverride && commandOverride.length > 0 ? [commandOverride[0] ?? 'opencode'] : ['opencode'];
|
|
6
|
+
if (modelOverride && !base.includes('-m') && !base.includes('--model')) {
|
|
7
|
+
base.push('-m', modelOverride);
|
|
8
|
+
}
|
|
9
|
+
base.push('run', '--format', 'json');
|
|
10
|
+
return [...base, 'Follow the attached task contract exactly.', '-f', promptPath];
|
|
11
|
+
}
|
|
12
|
+
const claudeBase = commandOverride && commandOverride.length > 0 ? [...commandOverride] : ['claude'];
|
|
13
|
+
if (modelOverride && !claudeBase.includes('--model')) {
|
|
14
|
+
claudeBase.push('--model', modelOverride);
|
|
15
|
+
}
|
|
16
|
+
if (!claudeBase.includes('-p')) {
|
|
17
|
+
claudeBase.push('-p');
|
|
18
|
+
}
|
|
19
|
+
// Restrict Claude Code to safe tools only (no Agent spawning inside worker tasks)
|
|
20
|
+
if (!claudeBase.includes('--allowedTools')) {
|
|
21
|
+
claudeBase.push('--allowedTools', 'Edit,Write,Read,Bash,Glob,Grep');
|
|
22
|
+
}
|
|
23
|
+
// Request JSON output for structured transcript parsing
|
|
24
|
+
if (!claudeBase.includes('--output-format')) {
|
|
25
|
+
claudeBase.push('--output-format', 'json');
|
|
26
|
+
}
|
|
27
|
+
if (commandOverride && commandOverride.length > 0) {
|
|
28
|
+
return [...claudeBase, prompt];
|
|
29
|
+
}
|
|
30
|
+
switch (backend) {
|
|
31
|
+
case 'claude-code':
|
|
32
|
+
return [...claudeBase, prompt];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function formatBackendTranscript(backend, transcript, backendConfig) {
|
|
36
|
+
const defaultFormat = backend === 'opencode' ? 'jsonl' : 'plain';
|
|
37
|
+
const format = backendConfig?.transcript_format ?? defaultFormat;
|
|
38
|
+
const header = backend === 'opencode'
|
|
39
|
+
? format === 'jsonl'
|
|
40
|
+
? '[opencode transcript: jsonl]'
|
|
41
|
+
: '[opencode transcript]'
|
|
42
|
+
: format === 'jsonl'
|
|
43
|
+
? '[claude-code transcript: jsonl]'
|
|
44
|
+
: '[claude-code transcript]';
|
|
45
|
+
return `${header}\n${transcript}`.trim() + '\n';
|
|
46
|
+
}
|
|
47
|
+
export function getTaskExecutionObservabilityMode(task) {
|
|
48
|
+
if (task.planner_metadata?.execution_mode === 'agentic') {
|
|
49
|
+
return task.planner_metadata?.fallback_to_single_call === true ? 'fallback_to_agentic' : 'direct_agentic';
|
|
50
|
+
}
|
|
51
|
+
return 'conservative_only';
|
|
52
|
+
}
|
|
53
|
+
export function persistAgenticArtifacts(taskDir, input) {
|
|
54
|
+
const transcriptPath = join(taskDir, 'agentic_transcript.txt');
|
|
55
|
+
const summaryPath = join(taskDir, 'agentic_summary.json');
|
|
56
|
+
const executionMode = getTaskExecutionObservabilityMode(input.task);
|
|
57
|
+
writeAtomic(transcriptPath, input.transcript);
|
|
58
|
+
const summary = {
|
|
59
|
+
schema_version: 1,
|
|
60
|
+
task_id: input.task.id,
|
|
61
|
+
backend: input.backend,
|
|
62
|
+
command: input.command,
|
|
63
|
+
changed_files: input.changedFiles,
|
|
64
|
+
acceptance_checks_executed: false,
|
|
65
|
+
fallback_used: input.task.planner_metadata?.fallback_to_single_call === true,
|
|
66
|
+
transcript_preview: input.transcript.slice(0, 500),
|
|
67
|
+
execution_mode: executionMode,
|
|
68
|
+
failure_class: input.failureClass,
|
|
69
|
+
};
|
|
70
|
+
writeJson(summaryPath, summary);
|
|
71
|
+
const artifact = {
|
|
72
|
+
schema_version: 1,
|
|
73
|
+
task_id: input.task.id,
|
|
74
|
+
backend: input.backend,
|
|
75
|
+
command: input.command,
|
|
76
|
+
transcript_path: transcriptPath,
|
|
77
|
+
summary_path: summaryPath,
|
|
78
|
+
changed_files: input.changedFiles,
|
|
79
|
+
acceptance_checks_executed: false,
|
|
80
|
+
fallback_used: input.task.planner_metadata?.fallback_to_single_call === true,
|
|
81
|
+
exit_code: input.exitCode,
|
|
82
|
+
model: input.model,
|
|
83
|
+
execution_mode: executionMode,
|
|
84
|
+
failure_class: input.failureClass,
|
|
85
|
+
};
|
|
86
|
+
writeJson(join(taskDir, 'agentic_run.json'), artifact);
|
|
87
|
+
}
|
|
88
|
+
export function listChangedFiles(output, toRelativePath) {
|
|
89
|
+
return output
|
|
90
|
+
.split('\n')
|
|
91
|
+
.filter((line) => line.trim().length > 0)
|
|
92
|
+
.map((line) => line.slice(3).trim())
|
|
93
|
+
.map((filePath) => toRelativePath(filePath));
|
|
94
|
+
}
|
|
95
|
+
export function resolveAgenticTierSelection(task, config) {
|
|
96
|
+
const tierSelection = config.defaults.agentic?.tiers?.[task.model_tier];
|
|
97
|
+
const backend = task.planner_metadata?.agentic_backend ?? tierSelection?.backend ?? config.defaults.agentic?.default_backend;
|
|
98
|
+
if (!backend) {
|
|
99
|
+
throw new Error('Agentic execution requested but no agentic backend is configured on planner_metadata, defaults.agentic.tiers, or defaults.agentic.default_backend');
|
|
100
|
+
}
|
|
101
|
+
const model = tierSelection?.backend === backend ? tierSelection.model : undefined;
|
|
102
|
+
return { backend, model };
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Parses JSONL output from an agentic backend (opencode --format json or claude-code --output-format json)
|
|
106
|
+
* and sums token usage and cost from all step_finish events.
|
|
107
|
+
*
|
|
108
|
+
* Returns null if no step_finish events are found (e.g. plain text output).
|
|
109
|
+
*/
|
|
110
|
+
export function parseAgenticUsage(output) {
|
|
111
|
+
let totalInput = 0;
|
|
112
|
+
let totalOutput = 0;
|
|
113
|
+
let totalCost = 0;
|
|
114
|
+
let steps = 0;
|
|
115
|
+
for (const line of output.split('\n')) {
|
|
116
|
+
const trimmed = line.trim();
|
|
117
|
+
if (!trimmed)
|
|
118
|
+
continue;
|
|
119
|
+
try {
|
|
120
|
+
const event = JSON.parse(trimmed);
|
|
121
|
+
// opencode step_finish events
|
|
122
|
+
if (event.type === 'step_finish' && event.part) {
|
|
123
|
+
totalInput += event.part.tokens?.input ?? 0;
|
|
124
|
+
totalOutput += event.part.tokens?.output ?? 0;
|
|
125
|
+
totalCost += event.part.cost ?? 0;
|
|
126
|
+
steps++;
|
|
127
|
+
}
|
|
128
|
+
// Claude Code final result (single JSON object with result.usage)
|
|
129
|
+
if (event.result?.usage) {
|
|
130
|
+
totalInput += event.result.usage.input_tokens ?? 0;
|
|
131
|
+
totalOutput += event.result.usage.output_tokens ?? 0;
|
|
132
|
+
totalCost += event.result.cost_usd ?? 0;
|
|
133
|
+
steps++;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// Skip non-JSON lines
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (steps === 0)
|
|
141
|
+
return null;
|
|
142
|
+
return {
|
|
143
|
+
input_tokens: totalInput,
|
|
144
|
+
output_tokens: totalOutput,
|
|
145
|
+
cost_usd: totalCost,
|
|
146
|
+
steps,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=agentic-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentic-runtime.js","sourceRoot":"","sources":["../src/agentic-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAiCtD,MAAM,UAAU,mBAAmB,CACjC,OAAuB,EACvB,MAAc,EACd,UAAkB,EAClB,eAA0B,EAC1B,aAAsB;IAEtB,IAAI,OAAO,KAAK,UAAU,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,eAAe,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QAC/G,IAAI,aAAa,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YACvE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QACjC,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC;QACrC,OAAO,CAAC,GAAG,IAAI,EAAE,4CAA4C,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;IACnF,CAAC;IAED,MAAM,UAAU,GAAG,eAAe,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IACrG,IAAI,aAAa,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACrD,UAAU,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IAC5C,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IACD,kFAAkF;IAClF,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAC3C,UAAU,CAAC,IAAI,CAAC,gBAAgB,EAAE,gCAAgC,CAAC,CAAC;IACtE,CAAC;IACD,wDAAwD;IACxD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;QAC5C,UAAU,CAAC,IAAI,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC;IAC7C,CAAC;IACD,IAAI,eAAe,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClD,OAAO,CAAC,GAAG,UAAU,EAAE,MAAM,CAAC,CAAC;IACjC,CAAC;IAED,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,aAAa;YAChB,OAAO,CAAC,GAAG,UAAU,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,OAAuB,EACvB,UAAkB,EAClB,aAAoC;IAEpC,MAAM,aAAa,GAAG,OAAO,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IACjE,MAAM,MAAM,GAAG,aAAa,EAAE,iBAAiB,IAAI,aAAa,CAAC;IACjE,MAAM,MAAM,GACV,OAAO,KAAK,UAAU;QACpB,CAAC,CAAC,MAAM,KAAK,OAAO;YAClB,CAAC,CAAC,8BAA8B;YAChC,CAAC,CAAC,uBAAuB;QAC3B,CAAC,CAAC,MAAM,KAAK,OAAO;YAClB,CAAC,CAAC,iCAAiC;YACnC,CAAC,CAAC,0BAA0B,CAAC;IACnC,OAAO,GAAG,MAAM,KAAK,UAAU,EAAE,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,iCAAiC,CAAC,IAAU;IAC1D,IAAI,IAAI,CAAC,gBAAgB,EAAE,cAAc,KAAK,SAAS,EAAE,CAAC;QACxD,OAAO,IAAI,CAAC,gBAAgB,EAAE,uBAAuB,KAAK,IAAI,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,gBAAgB,CAAC;IAC5G,CAAC;IACD,OAAO,mBAAmB,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,uBAAuB,CACrC,OAAe,EACf,KASC;IAED,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,EAAE,wBAAwB,CAAC,CAAC;IAC/D,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,sBAAsB,CAAC,CAAC;IAC1D,MAAM,aAAa,GAAG,iCAAiC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpE,WAAW,CAAC,cAAc,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAE9C,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,CAAC;QACjB,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE;QACtB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,aAAa,EAAE,KAAK,CAAC,YAAY;QACjC,0BAA0B,EAAE,KAAK;QACjC,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC,gBAAgB,EAAE,uBAAuB,KAAK,IAAI;QAC5E,kBAAkB,EAAE,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;QAClD,cAAc,EAAE,aAAa;QAC7B,aAAa,EAAE,KAAK,CAAC,YAAY;KAClC,CAAC;IACF,SAAS,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAEhC,MAAM,QAAQ,GAAuB;QACnC,cAAc,EAAE,CAAC;QACjB,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE;QACtB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,eAAe,EAAE,cAAc;QAC/B,YAAY,EAAE,WAAW;QACzB,aAAa,EAAE,KAAK,CAAC,YAAY;QACjC,0BAA0B,EAAE,KAAK;QACjC,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC,gBAAgB,EAAE,uBAAuB,KAAK,IAAI;QAC5E,SAAS,EAAE,KAAK,CAAC,QAAQ;QACzB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,cAAc,EAAE,aAAa;QAC7B,aAAa,EAAE,KAAK,CAAC,YAAY;KAClC,CAAC;IACF,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC,EAAE,QAAQ,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAc,EAAE,cAA4C;IAC3F,OAAO,MAAM;SACV,KAAK,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;SACxC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACnC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,2BAA2B,CACzC,IAAU,EACV,MAAiB;IAEjB,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACxE,MAAM,OAAO,GACX,IAAI,CAAC,gBAAgB,EAAE,eAAe,IAAI,aAAa,EAAE,OAAO,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAC/G,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,mJAAmJ,CACpJ,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,aAAa,EAAE,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IACnF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAC5B,CAAC;AASD;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAW/B,CAAC;YAEF,8BAA8B;YAC9B,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;gBAC/C,UAAU,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,CAAC;gBAC5C,WAAW,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,CAAC;gBAC9C,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;gBAClC,KAAK,EAAE,CAAC;YACV,CAAC;YAED,kEAAkE;YAClE,IAAI,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;gBACxB,UAAU,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,IAAI,CAAC,CAAC;gBACnD,WAAW,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,IAAI,CAAC,CAAC;gBACrD,SAAS,IAAI,KAAK,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;gBACxC,KAAK,EAAE,CAAC;YACV,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,sBAAsB;QACxB,CAAC;IACH,CAAC;IAED,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAE7B,OAAO;QACL,YAAY,EAAE,UAAU;QACxB,aAAa,EAAE,WAAW;QAC1B,QAAQ,EAAE,SAAS;QACnB,KAAK;KACN,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ModelTier, RiskLevel } from './types.js';
|
|
2
|
+
export declare const AGENTIC_BACKENDS: readonly ["opencode", "claude-code"];
|
|
3
|
+
export type AgenticBackend = (typeof AGENTIC_BACKENDS)[number];
|
|
4
|
+
export interface AgenticFallbackConfig {
|
|
5
|
+
enabled?: boolean;
|
|
6
|
+
task_types?: string[];
|
|
7
|
+
risk_levels?: RiskLevel[];
|
|
8
|
+
}
|
|
9
|
+
export interface AgenticBackendConfig {
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
command?: string[];
|
|
12
|
+
transcript_format?: 'plain' | 'jsonl';
|
|
13
|
+
}
|
|
14
|
+
export interface AgenticConfig {
|
|
15
|
+
default_backend?: AgenticBackend;
|
|
16
|
+
backends?: Partial<Record<AgenticBackend, AgenticBackendConfig>>;
|
|
17
|
+
tiers?: Partial<Record<ModelTier, {
|
|
18
|
+
backend: AgenticBackend;
|
|
19
|
+
model?: string;
|
|
20
|
+
}>>;
|
|
21
|
+
}
|
|
22
|
+
export interface AgenticBackendResolution {
|
|
23
|
+
backend: AgenticBackend;
|
|
24
|
+
available: boolean;
|
|
25
|
+
command: string;
|
|
26
|
+
commandSource: 'default' | 'config';
|
|
27
|
+
configPath: string | null;
|
|
28
|
+
configPathSource: 'config' | 'default' | 'none';
|
|
29
|
+
resolvedConfigPath: string | null;
|
|
30
|
+
configExists: boolean | null;
|
|
31
|
+
installHint: string;
|
|
32
|
+
error?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface AgenticRuntimeRequirement {
|
|
35
|
+
backend: AgenticBackend;
|
|
36
|
+
context?: string;
|
|
37
|
+
}
|