@hecer/yoke 1.11.0 → 1.13.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/.claude-plugin/plugin.json +13 -13
- package/.codex-plugin/plugin.json +7 -7
- package/CHANGELOG.md +435 -398
- package/README.md +943 -915
- package/TODOS.md +5 -5
- package/agents/docs.toml +6 -6
- package/agents/implementer.toml +6 -6
- package/agents/reviewer.toml +6 -6
- package/agents/security.toml +6 -6
- package/bench/README.md +86 -86
- package/bench/RESULTS.md +35 -35
- package/bench/output-compaction.mjs +65 -65
- package/bench/result-schema.mjs +12 -12
- package/bench/results/claude-2026-07-27T18-03-26.json +50 -50
- package/bench/results/codex-unavailable-1785175418318.json +15 -15
- package/bench/results/gemini-2026-07-27T18-03-44.json +46 -46
- package/bench/run-matrix.mjs +26 -26
- package/bench/run.mjs +106 -106
- package/canon/AGENTS.md +30 -30
- package/canon/context/DECISIONS.md +4 -4
- package/canon/context/GLOSSARY.md +11 -11
- package/canon/context/KNOWLEDGE.md +4 -4
- package/canon/context/PROJECT.md +15 -15
- package/canon/loop/loop-spec.md +65 -65
- package/canon/loop/prd.schema.md +41 -41
- package/canon/manifest.yaml +59 -59
- package/canon/policy/gates.md +7 -7
- package/canon/policy/roles.md +9 -9
- package/canon/skills/ATTRIBUTION.md +99 -99
- package/canon/skills/authoring-prd/SKILL.md +57 -57
- package/canon/skills/brainstorming/SKILL.md +164 -164
- package/canon/skills/codebase-design/DEEPENING.md +15 -15
- package/canon/skills/codebase-design/DESIGN-IT-TWICE.md +12 -12
- package/canon/skills/codebase-design/SKILL.md +39 -39
- package/canon/skills/dispatching-parallel-agents/SKILL.md +182 -182
- package/canon/skills/document-release/SKILL.md +302 -302
- package/canon/skills/domain-modeling/ADR-FORMAT.md +19 -19
- package/canon/skills/domain-modeling/CONTEXT-FORMAT.md +39 -39
- package/canon/skills/domain-modeling/SKILL.md +35 -35
- package/canon/skills/executing-plans/SKILL.md +70 -70
- package/canon/skills/finishing-a-development-branch/SKILL.md +200 -200
- package/canon/skills/health/SKILL.md +177 -177
- package/canon/skills/maintaining-context/SKILL.md +34 -34
- package/canon/skills/minimal-code/SKILL.md +21 -21
- package/canon/skills/no-ai-slop/SKILL.md +103 -103
- package/canon/skills/no-ai-slop/eval.md +43 -43
- package/canon/skills/plan-ceo-review/SKILL.md +541 -541
- package/canon/skills/plan-eng-review/SKILL.md +362 -362
- package/canon/skills/receiving-code-review/SKILL.md +213 -213
- package/canon/skills/requesting-code-review/SKILL.md +105 -105
- package/canon/skills/resolving-merge-conflicts/SKILL.md +18 -18
- package/canon/skills/retro/SKILL.md +397 -397
- package/canon/skills/review/SKILL.md +246 -246
- package/canon/skills/ship/SKILL.md +691 -691
- package/canon/skills/subagent-driven-development/SKILL.md +277 -277
- package/canon/skills/systematic-debugging/SKILL.md +296 -296
- package/canon/skills/tdd/SKILL.md +371 -371
- package/canon/skills/unslop-ui/SKILL.md +34 -34
- package/canon/skills/using-git-worktrees/SKILL.md +218 -218
- package/canon/skills/verification-before-completion/SKILL.md +139 -139
- package/canon/skills/visual-verification/SKILL.md +54 -54
- package/canon/skills/workflow/SKILL.md +22 -22
- package/canon/skills/writing-for-agents/SKILL-MECHANICS.md +27 -27
- package/canon/skills/writing-for-agents/SKILL.md +42 -42
- package/canon/skills/writing-plans/SKILL.md +152 -152
- package/canon/skills/writing-skills/SKILL.md +655 -655
- package/canon/skills/yoke-retrofit/SKILL.md +26 -26
- package/canon/skills/yoke-workflow/SKILL.md +20 -20
- package/canon/tools/codex-rtk-hook.mjs +35 -35
- package/canon/tools/gemini-rtk-hook.mjs +25 -25
- package/canon/tools/graphify.md +3 -3
- package/canon/tools/playwright-mcp.md +3 -3
- package/canon/tools/qwen-rtk-hook.mjs +25 -0
- package/canon/tools/rtk.md +7 -7
- package/canon/tools/serena.md +6 -6
- package/dist/agents/catalog.js +7 -0
- package/dist/agents/contracts.js +3 -1
- package/dist/agents/host.js +5 -1
- package/dist/agents/process-streams.js +62 -0
- package/dist/agents/process.js +43 -3
- package/dist/agents/providers.js +61 -6
- package/dist/agents/telemetry.js +133 -37
- package/dist/canon/manifest.js +2 -1
- package/dist/change/inbox.js +1 -1
- package/dist/cli.js +30 -24
- package/dist/dashboard/page.js +122 -122
- package/dist/dashboard/panels.js +91 -91
- package/dist/goals/command.js +3 -2
- package/dist/loop/claims.js +2 -1
- package/dist/loop/decision.js +3 -2
- package/dist/loop/parallel-command.js +4 -2
- package/dist/loop/prd.js +2 -1
- package/dist/loop/reporter.js +1 -0
- package/dist/loop/run-command.js +31 -10
- package/dist/prd/command.js +19 -19
- package/dist/quality/candidate-comparison.js +6 -1
- package/dist/quality/command.js +17 -2
- package/dist/quality/types.js +6 -1
- package/dist/retrofit/apply.js +95 -2
- package/dist/retrofit/config.js +9 -1
- package/dist/retrofit/detect.js +8 -0
- package/dist/retrofit/plan.js +6 -0
- package/dist/retrofit/planners/claude.js +14 -14
- package/dist/retrofit/planners/kilo.js +44 -0
- package/dist/retrofit/planners/opencode.js +44 -0
- package/dist/retrofit/planners/pi.js +24 -0
- package/dist/retrofit/planners/qwen.js +3 -3
- package/dist/retrofit/preserve.js +2 -2
- package/dist/retrofit/qwen-settings.js +17 -0
- package/dist/retrofit/skill-actions.js +4 -1
- package/dist/retrofit/tools.js +8 -0
- package/dist/review/command.js +3 -2
- package/dist/review/verdict.js +1 -1
- package/dist/routing/capability.js +2 -2
- package/dist/routing/planning.js +2 -0
- package/dist/routing/registry.js +3 -1
- package/dist/routing/router.js +7 -3
- package/dist/setup/command.js +35 -11
- package/dist/setup/model-presets.js +48 -0
- package/docs/CAPABILITY-ROUTING.md +51 -51
- package/docs/DASHBOARD-EVOLUTION.md +33 -33
- package/docs/HARNESSES.md +81 -0
- package/docs/MIGRATING-TO-1.0.md +33 -33
- package/docs/MIGRATING-TO-1.1.md +27 -27
- package/docs/MIGRATING-TO-1.4.md +70 -70
- package/docs/PRODUCT-DIRECTION-2026-09-05.md +210 -210
- package/docs/PUBLISHING.md +114 -114
- package/docs/QWEN-MODEL-SUPPORT.md +142 -0
- package/docs/VERIFIED-PROJECTS-VALIDATION.md +29 -29
- package/docs/VERIFIED-PROJECTS.md +167 -167
- package/docs/superpowers/plans/2026-06-28-baustein-e-context-layer.md +981 -981
- package/docs/superpowers/plans/2026-06-29-baustein-f-routing.md +258 -258
- package/docs/superpowers/plans/2026-06-29-baustein-g-loop-observability.md +1006 -1006
- package/docs/superpowers/plans/2026-06-29-baustein-h-loop-robustness.md +374 -374
- package/docs/superpowers/plans/2026-06-30-baustein-i-visual-design-verification.md +450 -450
- package/docs/superpowers/plans/2026-07-02-baustein-k-zero-to-100-bootstrap.md +1024 -1024
- package/docs/superpowers/plans/2026-07-02-baustein-m-flow-smoke-proofs.md +574 -574
- package/docs/superpowers/plans/2026-08-13-gauntlet-quality-loop.md +537 -537
- package/docs/superpowers/plans/2026-08-16-artifact-backed-output-compaction.md +329 -329
- package/docs/superpowers/plans/2026-09-05-verified-projects.md +83 -83
- package/docs/superpowers/specs/2026-06-28-baustein-e-context-layer-design.md +146 -146
- package/docs/superpowers/specs/2026-06-29-baustein-f-routing-design.md +106 -106
- package/docs/superpowers/specs/2026-06-29-baustein-g-loop-observability-design.md +186 -186
- package/docs/superpowers/specs/2026-06-29-baustein-h-loop-robustness-design.md +113 -113
- package/docs/superpowers/specs/2026-06-30-baustein-i-visual-design-verification-design.md +98 -98
- package/docs/superpowers/specs/2026-07-02-baustein-k-zero-to-100-bootstrap-design.md +200 -200
- package/docs/superpowers/specs/2026-07-02-baustein-m-flow-smoke-proofs-design.md +155 -155
- package/docs/superpowers/specs/2026-08-13-gauntlet-quality-loop-design.md +422 -422
- package/docs/superpowers/specs/2026-08-16-artifact-backed-output-compaction-design.md +166 -166
- package/gemini-extension.json +6 -6
- package/hooks/hooks.json +19 -19
- package/package.json +91 -87
- package/dist/dashboard/discovery.js +0 -73
- package/docs/community-outreach-2026-08-20.md +0 -85
- package/docs/launch-copy-2026-08-21.md +0 -193
package/dist/agents/telemetry.js
CHANGED
|
@@ -12,6 +12,13 @@ function parseJson(value) {
|
|
|
12
12
|
function isRecord(value) {
|
|
13
13
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
14
14
|
}
|
|
15
|
+
function textContent(value) {
|
|
16
|
+
if (typeof value === 'string')
|
|
17
|
+
return value;
|
|
18
|
+
if (!Array.isArray(value))
|
|
19
|
+
return '';
|
|
20
|
+
return value.filter(isRecord).filter(part => part.type === 'text' && typeof part.text === 'string').map(part => part.text).join('');
|
|
21
|
+
}
|
|
15
22
|
function directMachineResult(value) {
|
|
16
23
|
return isRecord(value) && value.schemaVersion === 1 ? value : undefined;
|
|
17
24
|
}
|
|
@@ -22,6 +29,8 @@ export function parseProviderResult(agent, output) {
|
|
|
22
29
|
if (direct !== undefined)
|
|
23
30
|
return direct;
|
|
24
31
|
}
|
|
32
|
+
if (agent === 'qwen')
|
|
33
|
+
return parseQwenResult(output);
|
|
25
34
|
const fragments = [];
|
|
26
35
|
for (const line of output.split(/\r?\n/u)) {
|
|
27
36
|
const parsed = parseJson(line);
|
|
@@ -45,9 +54,19 @@ export function parseProviderResult(agent, output) {
|
|
|
45
54
|
if (event.type === 'message' && event.role === 'assistant' && typeof event.content === 'string')
|
|
46
55
|
fragments.push(event.content);
|
|
47
56
|
break;
|
|
48
|
-
case '
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
case 'opencode':
|
|
58
|
+
case 'kilo': {
|
|
59
|
+
const part = isRecord(event.part) ? event.part : undefined;
|
|
60
|
+
if (event.type === 'text' && typeof part?.text === 'string')
|
|
61
|
+
fragments.push(part.text);
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
case 'pi':
|
|
65
|
+
if (event.type === 'message_end' && isRecord(event.message) && event.message.role === 'assistant') {
|
|
66
|
+
const text = textContent(event.message.content);
|
|
67
|
+
if (text)
|
|
68
|
+
fragments.push(text);
|
|
69
|
+
}
|
|
51
70
|
break;
|
|
52
71
|
}
|
|
53
72
|
}
|
|
@@ -67,6 +86,34 @@ export function parseProviderResult(agent, output) {
|
|
|
67
86
|
}
|
|
68
87
|
return null;
|
|
69
88
|
}
|
|
89
|
+
/** Qwen uses assistant content blocks and result envelopes, not Gemini messages. */
|
|
90
|
+
function parseQwenResult(output) {
|
|
91
|
+
let candidate = null;
|
|
92
|
+
for (const line of output.split(/\r?\n/u)) {
|
|
93
|
+
const parsed = parseJson(line);
|
|
94
|
+
if (!parsed.ok || !isRecord(parsed.value))
|
|
95
|
+
continue;
|
|
96
|
+
const event = parsed.value;
|
|
97
|
+
if (event.parent_tool_use_id != null)
|
|
98
|
+
continue;
|
|
99
|
+
if (event.type === 'result') {
|
|
100
|
+
if (event.is_error === true) {
|
|
101
|
+
candidate = null;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const structured = directMachineResult(event.structured_result);
|
|
105
|
+
const result = typeof event.result === 'string' ? parseJson(event.result) : { ok: false };
|
|
106
|
+
candidate = structured ?? (result.ok ? directMachineResult(result.value) : undefined) ?? null;
|
|
107
|
+
}
|
|
108
|
+
else if (event.type === 'assistant' && isRecord(event.message) && Array.isArray(event.message.content)) {
|
|
109
|
+
const text = event.message.content.filter(isRecord).filter(part => part.type === 'text' && typeof part.text === 'string').map(part => part.text).join('');
|
|
110
|
+
const result = parseJson(text);
|
|
111
|
+
if (result.ok)
|
|
112
|
+
candidate = directMachineResult(result.value) ?? candidate;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return candidate;
|
|
116
|
+
}
|
|
70
117
|
export function parseProviderTelemetry(agent, lines) {
|
|
71
118
|
let inputTokens;
|
|
72
119
|
let cachedInputTokens;
|
|
@@ -76,6 +123,10 @@ export function parseProviderTelemetry(agent, lines) {
|
|
|
76
123
|
let totalCostUsd;
|
|
77
124
|
let model;
|
|
78
125
|
let reportedModels = [];
|
|
126
|
+
const harnessTotals = agent === 'opencode' || agent === 'kilo'
|
|
127
|
+
? { input: 0, output: 0, cached: 0, cacheWrite: 0, reasoning: 0, cost: 0, hasInput: false, hasOutput: false, hasCached: false, hasCacheWrite: false, hasReasoning: false, hasCost: false }
|
|
128
|
+
: undefined;
|
|
129
|
+
let piUsage;
|
|
79
130
|
for (const line of lines) {
|
|
80
131
|
let parsed;
|
|
81
132
|
try {
|
|
@@ -87,8 +138,47 @@ export function parseProviderTelemetry(agent, lines) {
|
|
|
87
138
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
88
139
|
continue;
|
|
89
140
|
const event = parsed;
|
|
141
|
+
if (agent === 'qwen' && event.parent_tool_use_id != null)
|
|
142
|
+
continue;
|
|
90
143
|
const message = event.message && typeof event.message === 'object' ? event.message : undefined;
|
|
91
144
|
const stats = event.stats && typeof event.stats === 'object' ? event.stats : undefined;
|
|
145
|
+
const part = event.part && typeof event.part === 'object' ? event.part : undefined;
|
|
146
|
+
const partTokens = part?.tokens && typeof part.tokens === 'object' ? part.tokens : undefined;
|
|
147
|
+
if (harnessTotals && (event.type === 'step_finish' || part?.type === 'step-finish') && partTokens) {
|
|
148
|
+
const cache = partTokens.cache && typeof partTokens.cache === 'object' ? partTokens.cache : undefined;
|
|
149
|
+
const stepInput = finite(partTokens.input);
|
|
150
|
+
const stepOutput = finite(partTokens.output);
|
|
151
|
+
const stepCached = finite(partTokens.cached ?? partTokens.cacheRead ?? cache?.read);
|
|
152
|
+
const stepCacheWrite = finite(partTokens.cacheWrite ?? cache?.write);
|
|
153
|
+
const stepReasoning = finite(partTokens.reasoning);
|
|
154
|
+
const stepCost = finite(part?.cost ?? event.cost);
|
|
155
|
+
if (stepInput !== undefined) {
|
|
156
|
+
harnessTotals.input += stepInput;
|
|
157
|
+
harnessTotals.hasInput = true;
|
|
158
|
+
}
|
|
159
|
+
if (stepOutput !== undefined) {
|
|
160
|
+
harnessTotals.output += stepOutput;
|
|
161
|
+
harnessTotals.hasOutput = true;
|
|
162
|
+
}
|
|
163
|
+
if (stepCached !== undefined) {
|
|
164
|
+
harnessTotals.cached += stepCached;
|
|
165
|
+
harnessTotals.hasCached = true;
|
|
166
|
+
}
|
|
167
|
+
if (stepCacheWrite !== undefined) {
|
|
168
|
+
harnessTotals.cacheWrite += stepCacheWrite;
|
|
169
|
+
harnessTotals.hasCacheWrite = true;
|
|
170
|
+
}
|
|
171
|
+
if (stepReasoning !== undefined) {
|
|
172
|
+
harnessTotals.reasoning += stepReasoning;
|
|
173
|
+
harnessTotals.hasReasoning = true;
|
|
174
|
+
}
|
|
175
|
+
if (stepCost !== undefined) {
|
|
176
|
+
harnessTotals.cost += stepCost;
|
|
177
|
+
harnessTotals.hasCost = true;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (agent === 'pi' && event.type === 'message_update' && event.usage && typeof event.usage === 'object')
|
|
181
|
+
piUsage = event.usage;
|
|
92
182
|
const usage = (event.usage && typeof event.usage === 'object'
|
|
93
183
|
? event.usage
|
|
94
184
|
: message?.usage && typeof message.usage === 'object'
|
|
@@ -105,41 +195,12 @@ export function parseProviderTelemetry(agent, lines) {
|
|
|
105
195
|
// Older JSON stats only provide model-local token objects. Sum a field
|
|
106
196
|
// only when every model measured it; a missing measurement is not zero.
|
|
107
197
|
let source = usage ?? nestedModelTokens ?? modelUsage;
|
|
108
|
-
if (agent === 'gemini' && modelEntries.length > 0) {
|
|
198
|
+
if ((agent === 'gemini' || agent === 'qwen') && modelEntries.length > 0) {
|
|
109
199
|
reportedModels = modelEntries.map(([name]) => name);
|
|
110
200
|
model = firstModel?.[0];
|
|
111
201
|
const fields = {
|
|
112
|
-
input_tokens: ['input_tokens', 'inputTokens', 'promptTokenCount', 'input'],
|
|
113
|
-
output_tokens: ['output_tokens', 'outputTokens', 'candidatesTokenCount', 'output'],
|
|
114
|
-
cached_input_tokens: ['cached_input_tokens', 'cachedInputTokens', 'cachedContentTokenCount', 'cached'],
|
|
115
|
-
reasoning_output_tokens: ['reasoning_output_tokens', 'reasoningOutputTokens', 'thoughtsTokenCount', 'thoughts'],
|
|
116
|
-
};
|
|
117
|
-
const totals = {};
|
|
118
|
-
for (const [field, aliases] of Object.entries(fields)) {
|
|
119
|
-
const aggregate = aliases.map(key => finite(usage?.[key])).find(value => value !== undefined);
|
|
120
|
-
if (aggregate !== undefined) {
|
|
121
|
-
totals[field] = aggregate;
|
|
122
|
-
continue;
|
|
123
|
-
}
|
|
124
|
-
const values = modelEntries.map(([, value]) => {
|
|
125
|
-
const entry = isRecord(value) ? value : {};
|
|
126
|
-
const tokens = isRecord(entry.tokens) ? entry.tokens : entry;
|
|
127
|
-
return aliases.map(key => finite(tokens[key])).find(value => value !== undefined);
|
|
128
|
-
});
|
|
129
|
-
if (values.every(value => value !== undefined))
|
|
130
|
-
totals[field] = values.reduce((sum, value) => sum + value, 0);
|
|
131
|
-
}
|
|
132
|
-
source = { ...totals, ...usage };
|
|
133
|
-
const aggregateCached = finite(usage?.cached_input_tokens ?? usage?.cached);
|
|
134
|
-
if (aggregateCached !== undefined)
|
|
135
|
-
source.cached_input_tokens = aggregateCached;
|
|
136
|
-
}
|
|
137
|
-
if (agent === 'qwen' && modelEntries.length > 0) {
|
|
138
|
-
reportedModels = modelEntries.map(([name]) => name);
|
|
139
|
-
model = firstModel?.[0];
|
|
140
|
-
const fields = {
|
|
141
|
-
input_tokens: ['input_tokens', 'inputTokens', 'promptTokenCount', 'input'],
|
|
142
|
-
output_tokens: ['output_tokens', 'outputTokens', 'candidatesTokenCount', 'output'],
|
|
202
|
+
input_tokens: ['input_tokens', 'inputTokens', 'promptTokenCount', 'input', 'prompt'],
|
|
203
|
+
output_tokens: ['output_tokens', 'outputTokens', 'candidatesTokenCount', 'output', 'candidates'],
|
|
143
204
|
cached_input_tokens: ['cached_input_tokens', 'cachedInputTokens', 'cachedContentTokenCount', 'cached'],
|
|
144
205
|
reasoning_output_tokens: ['reasoning_output_tokens', 'reasoningOutputTokens', 'thoughtsTokenCount', 'thoughts'],
|
|
145
206
|
};
|
|
@@ -164,7 +225,7 @@ export function parseProviderTelemetry(agent, lines) {
|
|
|
164
225
|
source.cached_input_tokens = aggregateCached;
|
|
165
226
|
}
|
|
166
227
|
const inValue = finite(source?.input_tokens ?? source?.inputTokens ?? source?.prompt_tokens ?? source?.promptTokenCount ?? source?.input);
|
|
167
|
-
const cachedValue = finite(source?.cached_input_tokens ?? source?.cache_read_input_tokens ?? source?.cachedInputTokens ?? source?.cachedContentTokenCount ?? source?.cached);
|
|
228
|
+
const cachedValue = finite(source?.cached_input_tokens ?? source?.cache_read_input_tokens ?? source?.cachedInputTokens ?? source?.cacheRead ?? source?.cachedContentTokenCount ?? source?.cached);
|
|
168
229
|
const cacheWriteValue = finite(source?.cache_write_input_tokens ?? source?.cache_creation_input_tokens ?? source?.cacheWriteInputTokens ?? source?.cacheWrite);
|
|
169
230
|
const outValue = finite(source?.output_tokens ?? source?.outputTokens ?? source?.completion_tokens ?? source?.candidatesTokenCount ?? source?.output);
|
|
170
231
|
const reasoningValue = finite(source?.reasoning_output_tokens ?? source?.reasoningOutputTokens ?? source?.thoughtsTokenCount ?? source?.thoughts);
|
|
@@ -178,13 +239,48 @@ export function parseProviderTelemetry(agent, lines) {
|
|
|
178
239
|
outputTokens = outValue;
|
|
179
240
|
if (reasoningValue !== undefined)
|
|
180
241
|
reasoningOutputTokens = reasoningValue;
|
|
181
|
-
const
|
|
242
|
+
const usageCost = isRecord(usage?.cost) ? finite(usage.cost.total) : undefined;
|
|
243
|
+
const costValue = finite(event.total_cost_usd ?? event.totalCostUsd ?? stats?.total_cost_usd ?? usageCost);
|
|
182
244
|
if (costValue !== undefined)
|
|
183
245
|
totalCostUsd = costValue;
|
|
184
246
|
const eventModel = event.model ?? message?.model ?? firstModel?.[0];
|
|
185
247
|
if (typeof eventModel === 'string' && eventModel && reportedModels.length <= 1)
|
|
186
248
|
model = eventModel;
|
|
187
249
|
}
|
|
250
|
+
if (piUsage) {
|
|
251
|
+
const piInput = finite(piUsage.input);
|
|
252
|
+
const piOutput = finite(piUsage.output);
|
|
253
|
+
const piCached = finite(piUsage.cacheRead);
|
|
254
|
+
const piCacheWrite = finite(piUsage.cacheWrite);
|
|
255
|
+
const piReasoning = finite(piUsage.reasoning);
|
|
256
|
+
const piCost = isRecord(piUsage.cost) ? finite(piUsage.cost.total) : undefined;
|
|
257
|
+
if (piInput !== undefined)
|
|
258
|
+
inputTokens = piInput;
|
|
259
|
+
if (piOutput !== undefined)
|
|
260
|
+
outputTokens = piOutput;
|
|
261
|
+
if (piCached !== undefined)
|
|
262
|
+
cachedInputTokens = piCached;
|
|
263
|
+
if (piCacheWrite !== undefined)
|
|
264
|
+
cacheWriteInputTokens = piCacheWrite;
|
|
265
|
+
if (piReasoning !== undefined)
|
|
266
|
+
reasoningOutputTokens = piReasoning;
|
|
267
|
+
if (piCost !== undefined)
|
|
268
|
+
totalCostUsd = piCost;
|
|
269
|
+
}
|
|
270
|
+
if (harnessTotals) {
|
|
271
|
+
if (harnessTotals.hasInput)
|
|
272
|
+
inputTokens = harnessTotals.input;
|
|
273
|
+
if (harnessTotals.hasOutput)
|
|
274
|
+
outputTokens = harnessTotals.output;
|
|
275
|
+
if (harnessTotals.hasCached)
|
|
276
|
+
cachedInputTokens = harnessTotals.cached;
|
|
277
|
+
if (harnessTotals.hasCacheWrite)
|
|
278
|
+
cacheWriteInputTokens = harnessTotals.cacheWrite;
|
|
279
|
+
if (harnessTotals.hasReasoning)
|
|
280
|
+
reasoningOutputTokens = harnessTotals.reasoning;
|
|
281
|
+
if (harnessTotals.hasCost)
|
|
282
|
+
totalCostUsd = harnessTotals.cost;
|
|
283
|
+
}
|
|
188
284
|
if (inputTokens === undefined || outputTokens === undefined) {
|
|
189
285
|
const partialUsage = {
|
|
190
286
|
...(inputTokens !== undefined ? { inputTokens } : {}),
|
package/dist/canon/manifest.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { parse } from 'yaml';
|
|
3
3
|
import { readFileSync } from 'node:fs';
|
|
4
|
-
|
|
4
|
+
import { AgentSchema } from '../agents/contracts.js';
|
|
5
|
+
export { AgentSchema };
|
|
5
6
|
export const InvocationSchema = z.enum(['auto', 'manual']);
|
|
6
7
|
export const SkillEntrySchema = z.object({
|
|
7
8
|
id: z.string().min(1),
|
package/dist/change/inbox.js
CHANGED
|
@@ -104,7 +104,7 @@ export function buildChangePrompt(request, proposalPath, stories, brief = '') {
|
|
|
104
104
|
'Every proposed story must have passes: false and 2-5 structured acceptance criteria.',
|
|
105
105
|
'Every criterion must have a stable id, behavioral text, and one or more executable verify commands.',
|
|
106
106
|
'Each criterion id must appear in every verify command; each entry must be one approved test command without shell control operators.',
|
|
107
|
-
'Use only
|
|
107
|
+
'Use only a configured Yoke harness if an optional agent affinity is useful (claude, codex, gemini, qwen, opencode, kilo, or pi).',
|
|
108
108
|
'',
|
|
109
109
|
`Write ONLY a YAML array of the NEW stories to this exact file: ${proposalPath}`,
|
|
110
110
|
'Do not edit .yoke/prd.yaml or any source file. Do not commit.',
|
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { MODEL_PROVIDERS } from './setup/model-presets.js';
|
|
2
3
|
import { pathToFileURL } from 'node:url';
|
|
3
4
|
import { realpathSync } from 'node:fs';
|
|
4
5
|
import { checkProject, checkExitCode, protectAcceptance } from './check/command.js';
|
|
@@ -18,6 +19,7 @@ import { runLoopCleanup } from './loop/cleanup.js';
|
|
|
18
19
|
import { runFlowSmoke } from './smoke/command.js';
|
|
19
20
|
import { maybeNotifyUpdate, currentYokeVersion } from './update/check.js';
|
|
20
21
|
import { runUpgrade } from './update/upgrade.js';
|
|
22
|
+
import { AGENT_LIST, isSupportedAgent, SUPPORTED_AGENTS } from './agents/catalog.js';
|
|
21
23
|
import { printAudit, runAudit } from './audit/command.js';
|
|
22
24
|
import { runSetup } from './setup/command.js';
|
|
23
25
|
import { pendingChanges, queueChange } from './change/inbox.js';
|
|
@@ -104,7 +106,7 @@ export function main(argv) {
|
|
|
104
106
|
switch (cmd) {
|
|
105
107
|
case 'setup': {
|
|
106
108
|
const targetDir = rest.find(a => !a.startsWith('-')) ?? '.';
|
|
107
|
-
const valid = [
|
|
109
|
+
const valid = [...SUPPORTED_AGENTS];
|
|
108
110
|
const hostArg = rest.find(a => a.startsWith('--host='))?.slice('--host='.length);
|
|
109
111
|
if (hostArg && !valid.includes(hostArg)) {
|
|
110
112
|
console.error(`Invalid --host value: ${hostArg}`);
|
|
@@ -114,7 +116,7 @@ export function main(argv) {
|
|
|
114
116
|
const agentTokens = agentArg === 'all' ? valid : agentArg?.split(',').map(a => a.trim());
|
|
115
117
|
const invalidAgents = agentTokens?.filter(a => !valid.includes(a)) ?? [];
|
|
116
118
|
if (agentArg && (invalidAgents.length > 0 || agentTokens?.length === 0)) {
|
|
117
|
-
console.error(`Invalid --agent value: ${agentArg} (expected
|
|
119
|
+
console.error(`Invalid --agent value: ${agentArg} (expected ${AGENT_LIST}|all)`);
|
|
118
120
|
return 1;
|
|
119
121
|
}
|
|
120
122
|
const agents = agentTokens ? [...new Set(agentTokens)] : undefined;
|
|
@@ -140,7 +142,14 @@ export function main(argv) {
|
|
|
140
142
|
console.error('Invalid routing strategy');
|
|
141
143
|
return 1;
|
|
142
144
|
}
|
|
145
|
+
const modelProviderArg = rest.find(a => a.startsWith('--model-provider='))?.slice('--model-provider='.length);
|
|
146
|
+
const modelProviders = modelProviderArg?.split(',').map(value => value.trim());
|
|
147
|
+
if (modelProviders?.some(value => !MODEL_PROVIDERS.includes(value))) {
|
|
148
|
+
console.error('Invalid --model-provider (expected deepseek,kimi)');
|
|
149
|
+
return 1;
|
|
150
|
+
}
|
|
143
151
|
return runSetup(targetDir, {
|
|
152
|
+
modelProviders: modelProviders,
|
|
144
153
|
host: hostArg, agents, runner: runnerArg,
|
|
145
154
|
codeGraph: graphArg,
|
|
146
155
|
loop, routing, decisionPolicy: policyArg,
|
|
@@ -226,7 +235,7 @@ export function main(argv) {
|
|
|
226
235
|
}
|
|
227
236
|
if (sub === 'run' || sub === 'resume') {
|
|
228
237
|
const provider = value('runner') ?? 'codex';
|
|
229
|
-
if (!
|
|
238
|
+
if (!SUPPORTED_AGENTS.includes(provider))
|
|
230
239
|
throw new Error('Unknown runner');
|
|
231
240
|
return runProjectGoal(targetDir, { provider: provider, selection: { model: value('model') } }).then(goal => {
|
|
232
241
|
console.log(JSON.stringify(goal));
|
|
@@ -269,7 +278,7 @@ export function main(argv) {
|
|
|
269
278
|
const targetDir = rest.find(a => !a.startsWith('-')) ?? '.';
|
|
270
279
|
const loop = rest.includes('--loop');
|
|
271
280
|
const agentArg = rest.find(a => a.startsWith('--agent='))?.slice('--agent='.length);
|
|
272
|
-
const all = [
|
|
281
|
+
const all = [...SUPPORTED_AGENTS];
|
|
273
282
|
const agents = !agentArg || agentArg === 'all'
|
|
274
283
|
? (agentArg === 'all' ? all : undefined)
|
|
275
284
|
: agentArg.split(',').filter((a) => all.includes(a));
|
|
@@ -468,18 +477,17 @@ export function main(argv) {
|
|
|
468
477
|
return 1;
|
|
469
478
|
}
|
|
470
479
|
const runnerArg = rest.find(a => a.startsWith('--runner='))?.slice('--runner='.length);
|
|
471
|
-
const
|
|
472
|
-
const agent = runnerArg && valid.includes(runnerArg) ? runnerArg : undefined;
|
|
480
|
+
const agent = runnerArg && isSupportedAgent(runnerArg) ? runnerArg : undefined;
|
|
473
481
|
if (runnerArg && !agent) {
|
|
474
|
-
console.error(`Invalid --runner value: ${runnerArg} (expected
|
|
482
|
+
console.error(`Invalid --runner value: ${runnerArg} (expected ${AGENT_LIST})`);
|
|
475
483
|
return 1;
|
|
476
484
|
}
|
|
477
485
|
const isolate = rest.includes('--isolate') ? true : rest.includes('--no-isolate') ? false : undefined;
|
|
478
486
|
const reviewerArg = rest.find(a => a.startsWith('--reviewer='))?.slice('--reviewer='.length);
|
|
479
487
|
let reviewer;
|
|
480
488
|
if (reviewerArg) {
|
|
481
|
-
if (!
|
|
482
|
-
console.error(`Invalid --reviewer value: ${reviewerArg} (expected
|
|
489
|
+
if (!isSupportedAgent(reviewerArg)) {
|
|
490
|
+
console.error(`Invalid --reviewer value: ${reviewerArg} (expected ${AGENT_LIST})`);
|
|
483
491
|
return 1;
|
|
484
492
|
}
|
|
485
493
|
reviewer = reviewerArg;
|
|
@@ -522,19 +530,19 @@ export function main(argv) {
|
|
|
522
530
|
}
|
|
523
531
|
return runLoopCommand(targetDir, { maxIterations: rawMax, agent, isolate, resumeWorktree: rest.includes('--resume-worktree'), parallel, parallelAuto: parallelArg === '--parallel=auto', reviewer, review, allowSelfReview, timeoutMinutes, json, routing, onAmbiguity: oaArg, decisionPolicy: dpArg, permissions, ...qualityFlags.options });
|
|
524
532
|
}
|
|
525
|
-
console.log(
|
|
533
|
+
console.log(`usage: yoke loop <on|off|status|decision|answer|resume [--discard] [--quality|--no-quality] [--quality-rounds=N] [--quality-minutes=N] [--quality-policy=<blocking|advisory>] [--quality-unbounded] [--candidates=N]|cleanup [--remove-worktrees] [--discard-stale-recovery]|run [--max=N] [--parallel=<auto|N>] [--runner=<${AGENT_LIST}>] [--reviewer=<${AGENT_LIST}>] [--review] [--allow-self-review] [--routing|--no-routing] [--isolate|--no-isolate] [--unsafe] [--timeout=<minutes>] [--decision-policy=<auto|critical>] [--quality|--no-quality] [--quality-rounds=N] [--quality-minutes=N] [--quality-policy=<blocking|advisory>] [--quality-unbounded] [--candidates=N] [--json]> [targetDir]`);
|
|
526
534
|
return 1;
|
|
527
535
|
}
|
|
528
536
|
case 'new': {
|
|
529
537
|
const dir = rest.find(a => !a.startsWith('-'));
|
|
530
538
|
if (!dir) {
|
|
531
|
-
console.error(
|
|
539
|
+
console.error(`usage: yoke new <dir> [--idea="..."] [--agent=${AGENT_LIST}|all] [--runner=<${AGENT_LIST}>] [--loop]`);
|
|
532
540
|
return 1;
|
|
533
541
|
}
|
|
534
542
|
const idea = rest.find(a => a.startsWith('--idea='))?.slice('--idea='.length);
|
|
535
543
|
const loop = rest.includes('--loop');
|
|
536
544
|
const agentArg = rest.find(a => a.startsWith('--agent='))?.slice('--agent='.length);
|
|
537
|
-
const all = [
|
|
545
|
+
const all = [...SUPPORTED_AGENTS];
|
|
538
546
|
const agents = !agentArg || agentArg === 'all'
|
|
539
547
|
? (agentArg === 'all' ? all : undefined)
|
|
540
548
|
: agentArg.split(',').filter((a) => all.includes(a));
|
|
@@ -542,8 +550,8 @@ export function main(argv) {
|
|
|
542
550
|
console.warn('Unknown agent(s) in --agent; falling back to detection');
|
|
543
551
|
}
|
|
544
552
|
const runnerArg = rest.find(a => a.startsWith('--runner='))?.slice('--runner='.length);
|
|
545
|
-
if (runnerArg && !
|
|
546
|
-
console.error(`Invalid --runner value: ${runnerArg} (expected
|
|
553
|
+
if (runnerArg && !isSupportedAgent(runnerArg)) {
|
|
554
|
+
console.error(`Invalid --runner value: ${runnerArg} (expected ${AGENT_LIST})`);
|
|
547
555
|
return 1;
|
|
548
556
|
}
|
|
549
557
|
return runNew(dir, { idea, agents, runner: runnerArg, loop });
|
|
@@ -553,7 +561,7 @@ export function main(argv) {
|
|
|
553
561
|
const targetDir = rest.slice(1).find(a => !a.startsWith('-')) ?? '.';
|
|
554
562
|
if (sub === 'assess') {
|
|
555
563
|
const runner = rest.find(a => a.startsWith('--runner='))?.slice('--runner='.length);
|
|
556
|
-
if (runner && !
|
|
564
|
+
if (runner && !SUPPORTED_AGENTS.includes(runner)) {
|
|
557
565
|
console.error('Invalid planning runner');
|
|
558
566
|
return 1;
|
|
559
567
|
}
|
|
@@ -562,13 +570,12 @@ export function main(argv) {
|
|
|
562
570
|
if (sub === 'draft') {
|
|
563
571
|
const idea = rest.find(a => a.startsWith('--idea='))?.slice('--idea='.length);
|
|
564
572
|
if (!idea) {
|
|
565
|
-
console.error(
|
|
573
|
+
console.error(`usage: yoke prd draft [dir] --idea="..." [--runner=<${AGENT_LIST}>] [--force] [--timeout=<minutes>]`);
|
|
566
574
|
return 1;
|
|
567
575
|
}
|
|
568
|
-
const valid = ['claude', 'codex', 'gemini', 'qwen'];
|
|
569
576
|
const runnerArg = rest.find(a => a.startsWith('--runner='))?.slice('--runner='.length);
|
|
570
|
-
if (runnerArg && !
|
|
571
|
-
console.error(`Invalid --runner value: ${runnerArg} (expected
|
|
577
|
+
if (runnerArg && !isSupportedAgent(runnerArg)) {
|
|
578
|
+
console.error(`Invalid --runner value: ${runnerArg} (expected ${AGENT_LIST})`);
|
|
572
579
|
return 1;
|
|
573
580
|
}
|
|
574
581
|
const force = rest.includes('--force');
|
|
@@ -586,7 +593,7 @@ export function main(argv) {
|
|
|
586
593
|
}
|
|
587
594
|
if (sub === 'check')
|
|
588
595
|
return runPrdCheck(targetDir);
|
|
589
|
-
console.log(
|
|
596
|
+
console.log(`usage: yoke prd <draft|check|assess> [dir] [--idea="..."] [--runner=<${AGENT_LIST}>] [--story=<id>] [--reassess] [--force] [--timeout=<minutes>]`);
|
|
590
597
|
return 1;
|
|
591
598
|
}
|
|
592
599
|
case 'context': {
|
|
@@ -601,10 +608,9 @@ export function main(argv) {
|
|
|
601
608
|
}
|
|
602
609
|
case 'review': {
|
|
603
610
|
const targetDir = rest.find(a => !a.startsWith('-')) ?? '.';
|
|
604
|
-
const valid = ['claude', 'codex', 'gemini'];
|
|
605
611
|
const reviewerArg = rest.find(a => a.startsWith('--reviewer='))?.slice('--reviewer='.length);
|
|
606
|
-
if (reviewerArg && !
|
|
607
|
-
console.error(`Invalid --reviewer value: ${reviewerArg} (expected
|
|
612
|
+
if (reviewerArg && !isSupportedAgent(reviewerArg)) {
|
|
613
|
+
console.error(`Invalid --reviewer value: ${reviewerArg} (expected ${AGENT_LIST})`);
|
|
608
614
|
return 1;
|
|
609
615
|
}
|
|
610
616
|
const base = rest.find(a => a.startsWith('--base='))?.slice('--base='.length);
|
|
@@ -651,7 +657,7 @@ export function main(argv) {
|
|
|
651
657
|
return runUpgrade();
|
|
652
658
|
default:
|
|
653
659
|
console.log('Project workflows: yoke check [dir] [--json|--protect] | goal set|run|resume|pause|status|handoff|budget [dir] | projects add|list|remove | dashboard [dir] [--port=N]');
|
|
654
|
-
console.log(
|
|
660
|
+
console.log(`usage: yoke <setup [dir] | new <dir> [--idea="..."] | validate [canonDir] | retrofit [targetDir] [--agent=${AGENT_LIST}|all] [--code-graph=graphify|serena] [--loop] | change <add|status> [dir] | prd <draft|check|assess> [dir] | loop <on|off|status|decision|answer|resume|run|cleanup> | context <init|status> | review [dir] [--reviewer=<${AGENT_LIST}>] [--base=<ref>] [--focus="..."] | design-scan [dir] [--max=N] [--report] | flow-smoke [dir] [--url=<baseUrl>] [--label=<name>] | upgrade>`);
|
|
655
661
|
return cmd ? 1 : 0;
|
|
656
662
|
}
|
|
657
663
|
}
|