@hecer/yoke 1.12.0 → 1.14.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 +3 -3
- package/.codex-plugin/plugin.json +2 -2
- package/CHANGELOG.md +41 -0
- package/README.md +42 -30
- package/canon/loop/prd.schema.md +2 -2
- package/canon/manifest.yaml +2 -2
- package/canon/skills/authoring-prd/SKILL.md +1 -1
- package/canon/skills/yoke-retrofit/SKILL.md +2 -2
- package/canon/skills/yoke-workflow/SKILL.md +1 -1
- package/canon/tools/graphify.md +1 -1
- package/canon/tools/playwright-mcp.md +1 -1
- package/canon/tools/serena.md +1 -1
- package/dist/agents/catalog.js +7 -0
- package/dist/agents/contracts.js +3 -1
- package/dist/agents/host.js +4 -0
- package/dist/agents/process-streams.js +62 -0
- package/dist/agents/process.js +43 -3
- package/dist/agents/providers.js +45 -3
- package/dist/agents/telemetry.js +99 -2
- package/dist/canon/manifest.js +2 -1
- package/dist/change/inbox.js +1 -1
- package/dist/cli.js +22 -24
- package/dist/dashboard/analytics.js +193 -29
- package/dist/dashboard/contracts.js +23 -0
- package/dist/dashboard/page.js +39 -94
- package/dist/dashboard/panels.js +87 -33
- package/dist/dashboard/server.js +190 -15
- 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/observability/events.js +1 -1
- package/dist/observability/history.js +1 -1
- package/dist/prd/command.js +3 -3
- 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 +87 -1
- package/dist/retrofit/config.js +8 -0
- package/dist/retrofit/detect.js +6 -0
- package/dist/retrofit/plan.js +6 -0
- 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/skill-actions.js +3 -0
- 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 +13 -3
- package/docs/DASHBOARD-EVOLUTION.md +16 -2
- package/docs/DASHBOARD-OVERHAUL.md +146 -0
- package/docs/HARNESSES.md +81 -0
- package/gemini-extension.json +2 -2
- package/package.json +6 -2
package/dist/agents/providers.js
CHANGED
|
@@ -24,6 +24,20 @@ const argsFor = (agent, permissions) => {
|
|
|
24
24
|
const approval = permissions === 'read-only' ? 'plan' : 'auto-edit';
|
|
25
25
|
return ['--approval-mode', approval, '--sandbox', '--output-format', 'stream-json', ...(permissions === 'safe' ? ['--allowed-tools', 'run_shell_command'] : [])];
|
|
26
26
|
}
|
|
27
|
+
if (agent === 'opencode' || agent === 'kilo') {
|
|
28
|
+
const args = ['run', '--format', 'json', '--auto'];
|
|
29
|
+
if (permissions === 'read-only')
|
|
30
|
+
args.push('--agent', 'plan');
|
|
31
|
+
if (permissions === 'unsafe')
|
|
32
|
+
args.push('--dangerously-skip-permissions');
|
|
33
|
+
return args;
|
|
34
|
+
}
|
|
35
|
+
if (agent === 'pi') {
|
|
36
|
+
const args = ['--mode', 'json', '--no-session'];
|
|
37
|
+
if (permissions !== 'unsafe')
|
|
38
|
+
args.push('--tools', permissions === 'read-only' ? 'read,grep,find,ls' : 'read,bash,edit,write');
|
|
39
|
+
return args;
|
|
40
|
+
}
|
|
27
41
|
if (permissions === 'unsafe')
|
|
28
42
|
return ['--yolo', '--output-format', 'stream-json'];
|
|
29
43
|
const approval = permissions === 'read-only' ? 'plan' : 'auto_edit';
|
|
@@ -43,6 +57,10 @@ export function buildProviderInvocation(agent, prompt, cwd, permissions = 'safe'
|
|
|
43
57
|
throw new Error('Qwen does not support the reasoningEffort selection');
|
|
44
58
|
if (agent === 'qwen' && parsedSelection.nativeMultiAgent === true)
|
|
45
59
|
throw new Error('Qwen does not support enabling the nativeMultiAgent selection');
|
|
60
|
+
if (parsedSelection.provider && !['opencode', 'kilo', 'pi'].includes(agent))
|
|
61
|
+
throw new Error(`${agent} does not support an explicit provider selection`);
|
|
62
|
+
if (parsedSelection.variant && !['opencode', 'kilo', 'pi'].includes(agent))
|
|
63
|
+
throw new Error(`${agent} does not support a model variant selection`);
|
|
46
64
|
const args = argsFor(agent, permissions);
|
|
47
65
|
if (output.schemaFile !== undefined || output.jsonSchema !== undefined) {
|
|
48
66
|
if (agent === 'codex' && output.schemaFile && output.jsonSchema === undefined) {
|
|
@@ -56,9 +74,11 @@ export function buildProviderInvocation(agent, prompt, cwd, permissions = 'safe'
|
|
|
56
74
|
throw new Error('Inline structured output is unsupported by the Windows provider shell shim');
|
|
57
75
|
args.push('--json-schema', schema);
|
|
58
76
|
}
|
|
59
|
-
else
|
|
77
|
+
else if (!['opencode', 'kilo', 'pi'].includes(agent))
|
|
60
78
|
throw new Error(`${agent} structured output schema requires ${agent === 'codex' ? 'schemaFile' : agent === 'claude' ? 'jsonSchema' : 'a supported native schema option (unavailable)'}`);
|
|
61
79
|
}
|
|
80
|
+
if (parsedSelection.provider && agent === 'pi')
|
|
81
|
+
args.push('--provider', parsedSelection.provider);
|
|
62
82
|
if (parsedSelection.model) {
|
|
63
83
|
const qualified = agent === 'qwen' && parsedSelection.model.includes('::')
|
|
64
84
|
? parsedSelection.model.match(/^(openai|anthropic|gemini|vertex-ai|qwen-oauth)::(.+)$/u) : undefined;
|
|
@@ -68,14 +88,32 @@ export function buildProviderInvocation(agent, prompt, cwd, permissions = 'safe'
|
|
|
68
88
|
const model = ModelSelectionSchema.shape.model.parse(qualified[2]);
|
|
69
89
|
args.push('--auth-type', qualified[1], '--model', model);
|
|
70
90
|
}
|
|
71
|
-
else
|
|
72
|
-
|
|
91
|
+
else {
|
|
92
|
+
const qualified = parsedSelection.provider && (agent === 'opencode' || agent === 'kilo')
|
|
93
|
+
? `${parsedSelection.provider}/${parsedSelection.model}`
|
|
94
|
+
: parsedSelection.model;
|
|
95
|
+
args.push('--model', qualified);
|
|
96
|
+
}
|
|
73
97
|
}
|
|
74
98
|
if (parsedSelection.reasoningEffort) {
|
|
75
99
|
if (agent === 'claude')
|
|
76
100
|
args.push('--effort', parsedSelection.reasoningEffort);
|
|
77
101
|
else if (agent === 'codex')
|
|
78
102
|
args.push('--config', `model_reasoning_effort=${parsedSelection.reasoningEffort}`);
|
|
103
|
+
else if (agent === 'opencode' || agent === 'kilo')
|
|
104
|
+
args.push('--variant', parsedSelection.reasoningEffort);
|
|
105
|
+
else if (agent === 'pi')
|
|
106
|
+
args.push('--thinking', parsedSelection.reasoningEffort);
|
|
107
|
+
}
|
|
108
|
+
if (parsedSelection.variant) {
|
|
109
|
+
if (agent === 'opencode' || agent === 'kilo')
|
|
110
|
+
args.push('--variant', parsedSelection.variant);
|
|
111
|
+
else if (agent === 'pi') {
|
|
112
|
+
if (parsedSelection.reasoningEffort && parsedSelection.reasoningEffort !== parsedSelection.variant)
|
|
113
|
+
throw new Error('Pi reasoningEffort and variant selections must match');
|
|
114
|
+
if (!parsedSelection.reasoningEffort)
|
|
115
|
+
args.push('--thinking', parsedSelection.variant);
|
|
116
|
+
}
|
|
79
117
|
}
|
|
80
118
|
if (agent === 'codex' && parsedSelection.nativeMultiAgent === false)
|
|
81
119
|
args.push('--disable', 'multi_agent');
|
|
@@ -89,6 +127,10 @@ export function buildProviderInvocation(agent, prompt, cwd, permissions = 'safe'
|
|
|
89
127
|
args.push('--ignore-user-config');
|
|
90
128
|
else if (agent === 'claude')
|
|
91
129
|
args.push('--bare');
|
|
130
|
+
else if (agent === 'opencode' || agent === 'kilo')
|
|
131
|
+
args.push('--pure');
|
|
132
|
+
else if (agent === 'pi')
|
|
133
|
+
throw new Error('Pi does not support the bare startup selection');
|
|
92
134
|
}
|
|
93
135
|
if (agent === 'gemini' && parsedSelection.nativeMultiAgent === false) {
|
|
94
136
|
return { command: process.execPath, args: [fileURLToPath(new URL('../../hooks/bounded-gemini.mjs', import.meta.url)), ...args], input: prompt, cwd };
|
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
|
}
|
|
@@ -47,6 +54,20 @@ export function parseProviderResult(agent, output) {
|
|
|
47
54
|
if (event.type === 'message' && event.role === 'assistant' && typeof event.content === 'string')
|
|
48
55
|
fragments.push(event.content);
|
|
49
56
|
break;
|
|
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
|
+
}
|
|
70
|
+
break;
|
|
50
71
|
}
|
|
51
72
|
}
|
|
52
73
|
const joined = parseJson(fragments.join(''));
|
|
@@ -102,6 +123,10 @@ export function parseProviderTelemetry(agent, lines) {
|
|
|
102
123
|
let totalCostUsd;
|
|
103
124
|
let model;
|
|
104
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;
|
|
105
130
|
for (const line of lines) {
|
|
106
131
|
let parsed;
|
|
107
132
|
try {
|
|
@@ -117,6 +142,43 @@ export function parseProviderTelemetry(agent, lines) {
|
|
|
117
142
|
continue;
|
|
118
143
|
const message = event.message && typeof event.message === 'object' ? event.message : undefined;
|
|
119
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;
|
|
120
182
|
const usage = (event.usage && typeof event.usage === 'object'
|
|
121
183
|
? event.usage
|
|
122
184
|
: message?.usage && typeof message.usage === 'object'
|
|
@@ -163,7 +225,7 @@ export function parseProviderTelemetry(agent, lines) {
|
|
|
163
225
|
source.cached_input_tokens = aggregateCached;
|
|
164
226
|
}
|
|
165
227
|
const inValue = finite(source?.input_tokens ?? source?.inputTokens ?? source?.prompt_tokens ?? source?.promptTokenCount ?? source?.input);
|
|
166
|
-
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);
|
|
167
229
|
const cacheWriteValue = finite(source?.cache_write_input_tokens ?? source?.cache_creation_input_tokens ?? source?.cacheWriteInputTokens ?? source?.cacheWrite);
|
|
168
230
|
const outValue = finite(source?.output_tokens ?? source?.outputTokens ?? source?.completion_tokens ?? source?.candidatesTokenCount ?? source?.output);
|
|
169
231
|
const reasoningValue = finite(source?.reasoning_output_tokens ?? source?.reasoningOutputTokens ?? source?.thoughtsTokenCount ?? source?.thoughts);
|
|
@@ -177,13 +239,48 @@ export function parseProviderTelemetry(agent, lines) {
|
|
|
177
239
|
outputTokens = outValue;
|
|
178
240
|
if (reasoningValue !== undefined)
|
|
179
241
|
reasoningOutputTokens = reasoningValue;
|
|
180
|
-
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);
|
|
181
244
|
if (costValue !== undefined)
|
|
182
245
|
totalCostUsd = costValue;
|
|
183
246
|
const eventModel = event.model ?? message?.model ?? firstModel?.[0];
|
|
184
247
|
if (typeof eventModel === 'string' && eventModel && reportedModels.length <= 1)
|
|
185
248
|
model = eventModel;
|
|
186
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
|
+
}
|
|
187
284
|
if (inputTokens === undefined || outputTokens === undefined) {
|
|
188
285
|
const partialUsage = {
|
|
189
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
|
@@ -19,6 +19,7 @@ import { runLoopCleanup } from './loop/cleanup.js';
|
|
|
19
19
|
import { runFlowSmoke } from './smoke/command.js';
|
|
20
20
|
import { maybeNotifyUpdate, currentYokeVersion } from './update/check.js';
|
|
21
21
|
import { runUpgrade } from './update/upgrade.js';
|
|
22
|
+
import { AGENT_LIST, isSupportedAgent, SUPPORTED_AGENTS } from './agents/catalog.js';
|
|
22
23
|
import { printAudit, runAudit } from './audit/command.js';
|
|
23
24
|
import { runSetup } from './setup/command.js';
|
|
24
25
|
import { pendingChanges, queueChange } from './change/inbox.js';
|
|
@@ -105,7 +106,7 @@ export function main(argv) {
|
|
|
105
106
|
switch (cmd) {
|
|
106
107
|
case 'setup': {
|
|
107
108
|
const targetDir = rest.find(a => !a.startsWith('-')) ?? '.';
|
|
108
|
-
const valid = [
|
|
109
|
+
const valid = [...SUPPORTED_AGENTS];
|
|
109
110
|
const hostArg = rest.find(a => a.startsWith('--host='))?.slice('--host='.length);
|
|
110
111
|
if (hostArg && !valid.includes(hostArg)) {
|
|
111
112
|
console.error(`Invalid --host value: ${hostArg}`);
|
|
@@ -115,7 +116,7 @@ export function main(argv) {
|
|
|
115
116
|
const agentTokens = agentArg === 'all' ? valid : agentArg?.split(',').map(a => a.trim());
|
|
116
117
|
const invalidAgents = agentTokens?.filter(a => !valid.includes(a)) ?? [];
|
|
117
118
|
if (agentArg && (invalidAgents.length > 0 || agentTokens?.length === 0)) {
|
|
118
|
-
console.error(`Invalid --agent value: ${agentArg} (expected
|
|
119
|
+
console.error(`Invalid --agent value: ${agentArg} (expected ${AGENT_LIST}|all)`);
|
|
119
120
|
return 1;
|
|
120
121
|
}
|
|
121
122
|
const agents = agentTokens ? [...new Set(agentTokens)] : undefined;
|
|
@@ -234,7 +235,7 @@ export function main(argv) {
|
|
|
234
235
|
}
|
|
235
236
|
if (sub === 'run' || sub === 'resume') {
|
|
236
237
|
const provider = value('runner') ?? 'codex';
|
|
237
|
-
if (!
|
|
238
|
+
if (!SUPPORTED_AGENTS.includes(provider))
|
|
238
239
|
throw new Error('Unknown runner');
|
|
239
240
|
return runProjectGoal(targetDir, { provider: provider, selection: { model: value('model') } }).then(goal => {
|
|
240
241
|
console.log(JSON.stringify(goal));
|
|
@@ -277,7 +278,7 @@ export function main(argv) {
|
|
|
277
278
|
const targetDir = rest.find(a => !a.startsWith('-')) ?? '.';
|
|
278
279
|
const loop = rest.includes('--loop');
|
|
279
280
|
const agentArg = rest.find(a => a.startsWith('--agent='))?.slice('--agent='.length);
|
|
280
|
-
const all = [
|
|
281
|
+
const all = [...SUPPORTED_AGENTS];
|
|
281
282
|
const agents = !agentArg || agentArg === 'all'
|
|
282
283
|
? (agentArg === 'all' ? all : undefined)
|
|
283
284
|
: agentArg.split(',').filter((a) => all.includes(a));
|
|
@@ -476,18 +477,17 @@ export function main(argv) {
|
|
|
476
477
|
return 1;
|
|
477
478
|
}
|
|
478
479
|
const runnerArg = rest.find(a => a.startsWith('--runner='))?.slice('--runner='.length);
|
|
479
|
-
const
|
|
480
|
-
const agent = runnerArg && valid.includes(runnerArg) ? runnerArg : undefined;
|
|
480
|
+
const agent = runnerArg && isSupportedAgent(runnerArg) ? runnerArg : undefined;
|
|
481
481
|
if (runnerArg && !agent) {
|
|
482
|
-
console.error(`Invalid --runner value: ${runnerArg} (expected
|
|
482
|
+
console.error(`Invalid --runner value: ${runnerArg} (expected ${AGENT_LIST})`);
|
|
483
483
|
return 1;
|
|
484
484
|
}
|
|
485
485
|
const isolate = rest.includes('--isolate') ? true : rest.includes('--no-isolate') ? false : undefined;
|
|
486
486
|
const reviewerArg = rest.find(a => a.startsWith('--reviewer='))?.slice('--reviewer='.length);
|
|
487
487
|
let reviewer;
|
|
488
488
|
if (reviewerArg) {
|
|
489
|
-
if (!
|
|
490
|
-
console.error(`Invalid --reviewer value: ${reviewerArg} (expected
|
|
489
|
+
if (!isSupportedAgent(reviewerArg)) {
|
|
490
|
+
console.error(`Invalid --reviewer value: ${reviewerArg} (expected ${AGENT_LIST})`);
|
|
491
491
|
return 1;
|
|
492
492
|
}
|
|
493
493
|
reviewer = reviewerArg;
|
|
@@ -530,19 +530,19 @@ export function main(argv) {
|
|
|
530
530
|
}
|
|
531
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 });
|
|
532
532
|
}
|
|
533
|
-
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]`);
|
|
534
534
|
return 1;
|
|
535
535
|
}
|
|
536
536
|
case 'new': {
|
|
537
537
|
const dir = rest.find(a => !a.startsWith('-'));
|
|
538
538
|
if (!dir) {
|
|
539
|
-
console.error(
|
|
539
|
+
console.error(`usage: yoke new <dir> [--idea="..."] [--agent=${AGENT_LIST}|all] [--runner=<${AGENT_LIST}>] [--loop]`);
|
|
540
540
|
return 1;
|
|
541
541
|
}
|
|
542
542
|
const idea = rest.find(a => a.startsWith('--idea='))?.slice('--idea='.length);
|
|
543
543
|
const loop = rest.includes('--loop');
|
|
544
544
|
const agentArg = rest.find(a => a.startsWith('--agent='))?.slice('--agent='.length);
|
|
545
|
-
const all = [
|
|
545
|
+
const all = [...SUPPORTED_AGENTS];
|
|
546
546
|
const agents = !agentArg || agentArg === 'all'
|
|
547
547
|
? (agentArg === 'all' ? all : undefined)
|
|
548
548
|
: agentArg.split(',').filter((a) => all.includes(a));
|
|
@@ -550,8 +550,8 @@ export function main(argv) {
|
|
|
550
550
|
console.warn('Unknown agent(s) in --agent; falling back to detection');
|
|
551
551
|
}
|
|
552
552
|
const runnerArg = rest.find(a => a.startsWith('--runner='))?.slice('--runner='.length);
|
|
553
|
-
if (runnerArg && !
|
|
554
|
-
console.error(`Invalid --runner value: ${runnerArg} (expected
|
|
553
|
+
if (runnerArg && !isSupportedAgent(runnerArg)) {
|
|
554
|
+
console.error(`Invalid --runner value: ${runnerArg} (expected ${AGENT_LIST})`);
|
|
555
555
|
return 1;
|
|
556
556
|
}
|
|
557
557
|
return runNew(dir, { idea, agents, runner: runnerArg, loop });
|
|
@@ -561,7 +561,7 @@ export function main(argv) {
|
|
|
561
561
|
const targetDir = rest.slice(1).find(a => !a.startsWith('-')) ?? '.';
|
|
562
562
|
if (sub === 'assess') {
|
|
563
563
|
const runner = rest.find(a => a.startsWith('--runner='))?.slice('--runner='.length);
|
|
564
|
-
if (runner && !
|
|
564
|
+
if (runner && !SUPPORTED_AGENTS.includes(runner)) {
|
|
565
565
|
console.error('Invalid planning runner');
|
|
566
566
|
return 1;
|
|
567
567
|
}
|
|
@@ -570,13 +570,12 @@ export function main(argv) {
|
|
|
570
570
|
if (sub === 'draft') {
|
|
571
571
|
const idea = rest.find(a => a.startsWith('--idea='))?.slice('--idea='.length);
|
|
572
572
|
if (!idea) {
|
|
573
|
-
console.error(
|
|
573
|
+
console.error(`usage: yoke prd draft [dir] --idea="..." [--runner=<${AGENT_LIST}>] [--force] [--timeout=<minutes>]`);
|
|
574
574
|
return 1;
|
|
575
575
|
}
|
|
576
|
-
const valid = ['claude', 'codex', 'gemini', 'qwen'];
|
|
577
576
|
const runnerArg = rest.find(a => a.startsWith('--runner='))?.slice('--runner='.length);
|
|
578
|
-
if (runnerArg && !
|
|
579
|
-
console.error(`Invalid --runner value: ${runnerArg} (expected
|
|
577
|
+
if (runnerArg && !isSupportedAgent(runnerArg)) {
|
|
578
|
+
console.error(`Invalid --runner value: ${runnerArg} (expected ${AGENT_LIST})`);
|
|
580
579
|
return 1;
|
|
581
580
|
}
|
|
582
581
|
const force = rest.includes('--force');
|
|
@@ -594,7 +593,7 @@ export function main(argv) {
|
|
|
594
593
|
}
|
|
595
594
|
if (sub === 'check')
|
|
596
595
|
return runPrdCheck(targetDir);
|
|
597
|
-
console.log(
|
|
596
|
+
console.log(`usage: yoke prd <draft|check|assess> [dir] [--idea="..."] [--runner=<${AGENT_LIST}>] [--story=<id>] [--reassess] [--force] [--timeout=<minutes>]`);
|
|
598
597
|
return 1;
|
|
599
598
|
}
|
|
600
599
|
case 'context': {
|
|
@@ -609,10 +608,9 @@ export function main(argv) {
|
|
|
609
608
|
}
|
|
610
609
|
case 'review': {
|
|
611
610
|
const targetDir = rest.find(a => !a.startsWith('-')) ?? '.';
|
|
612
|
-
const valid = ['claude', 'codex', 'gemini', 'qwen'];
|
|
613
611
|
const reviewerArg = rest.find(a => a.startsWith('--reviewer='))?.slice('--reviewer='.length);
|
|
614
|
-
if (reviewerArg && !
|
|
615
|
-
console.error(`Invalid --reviewer value: ${reviewerArg} (expected
|
|
612
|
+
if (reviewerArg && !isSupportedAgent(reviewerArg)) {
|
|
613
|
+
console.error(`Invalid --reviewer value: ${reviewerArg} (expected ${AGENT_LIST})`);
|
|
616
614
|
return 1;
|
|
617
615
|
}
|
|
618
616
|
const base = rest.find(a => a.startsWith('--base='))?.slice('--base='.length);
|
|
@@ -659,7 +657,7 @@ export function main(argv) {
|
|
|
659
657
|
return runUpgrade();
|
|
660
658
|
default:
|
|
661
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]');
|
|
662
|
-
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>`);
|
|
663
661
|
return cmd ? 1 : 0;
|
|
664
662
|
}
|
|
665
663
|
}
|