vgxness 1.9.2 → 1.9.4
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/README.md +12 -6
- package/dist/agents/agent-resolver.js +33 -3
- package/dist/agents/canonical-agent-manifest.js +68 -21
- package/dist/agents/canonical-agent-projection.js +46 -4
- package/dist/cli/cli-help.js +14 -3
- package/dist/cli/commands/index.js +1 -0
- package/dist/cli/commands/interactive-entrypoint-dispatcher.js +8 -0
- package/dist/cli/commands/mcp-dispatcher.js +49 -18
- package/dist/cli/commands/memory-sdd-dispatcher.js +71 -5
- package/dist/cli/commands/setup-dispatcher.js +22 -10
- package/dist/cli/commands/status-dispatcher.js +130 -0
- package/dist/cli/commands/workflow-dispatcher.js +11 -5
- package/dist/cli/dispatcher.js +9 -1
- package/dist/cli/product-resume-renderer.js +32 -0
- package/dist/cli/product-status-renderer.js +81 -0
- package/dist/cli/sdd-renderer.js +90 -7
- package/dist/cli/tui/main-menu/main-menu-read-model.js +8 -8
- package/dist/cli/tui/setup/setup-tui-services.js +27 -10
- package/dist/code/cli/code-command.js +7 -4
- package/dist/code/reporting/summary.js +4 -1
- package/dist/code/runtime/code-runtime.js +27 -4
- package/dist/code/runtime/sdd-context.js +18 -2
- package/dist/governance/governance-report-builder.js +18 -7
- package/dist/mcp/claude-code-agent-config.js +10 -4
- package/dist/mcp/client-install-opencode-contract.js +2 -2
- package/dist/mcp/client-install-opencode.js +10 -6
- package/dist/mcp/control-plane.js +56 -0
- package/dist/mcp/opencode-default-agent-config.js +7 -4
- package/dist/mcp/provider-status.js +86 -81
- package/dist/mcp/schema.js +25 -7
- package/dist/mcp/stdio-server.js +4 -0
- package/dist/mcp/validation.js +39 -3
- package/dist/resume/product-resume.js +166 -0
- package/dist/runs/repositories/runs.js +12 -1
- package/dist/runs/run-service.js +62 -5
- package/dist/sdd/schema.js +8 -0
- package/dist/sdd/sdd-continuation-plan.js +81 -0
- package/dist/sdd/sdd-workflow-service.js +103 -16
- package/dist/skills/skill-resolver.js +21 -4
- package/dist/status/product-status.js +121 -0
- package/docs/architecture.md +1 -1
- package/docs/cli.md +40 -12
- package/docs/code-runtime.md +3 -0
- package/docs/glossary.md +1 -1
- package/docs/mcp.md +18 -4
- package/package.json +1 -1
|
@@ -6,14 +6,15 @@ import { planMemoryImportDryRun, validateMemoryImportPackage, writeMemoryImportO
|
|
|
6
6
|
import { MemoryService } from '../../memory/memory-service.js';
|
|
7
7
|
import { createNaturalLanguagePlan } from '../../orchestrator/natural-language-planner.js';
|
|
8
8
|
import { OpenCodeInjectionPreviewService } from '../../providers/opencode/injection-preview.js';
|
|
9
|
+
import { RunService } from '../../runs/run-service.js';
|
|
9
10
|
import { ArtifactPortabilityService } from '../../sdd/artifact-portability-service.js';
|
|
10
|
-
import {
|
|
11
|
+
import { normalizeSddArtifact, normalizeSddPhaseInput, sddPhases } from '../../sdd/schema.js';
|
|
11
12
|
import { SddWorkflowService } from '../../sdd/sdd-workflow-service.js';
|
|
12
13
|
import { SkillRegistryService } from '../../skills/skill-registry-service.js';
|
|
13
14
|
import { acceptedAtFlag, databasePathSelectionFor, optionalNumberFlag, optionalScopeFlag, optionalStringFlag, optionalTrimmedFlag, requiredFlag, requiredTrimmedFlag, scopeFlag, } from '../cli-flags.js';
|
|
14
15
|
import { okText, usageFailure, validationFailure } from '../cli-help.js';
|
|
15
16
|
import { formatMemoryImportValidationErrors, jsonResult, openCliDatabase, readJsonFile, resultFailure, validateMemoryImportMode, writeJsonFile, } from '../cli-helpers.js';
|
|
16
|
-
import { renderSddArtifactAccepted, renderSddCockpit, renderSddArtifactDetail, renderSddArtifactList, renderSddNext, renderSddStatus, } from '../sdd-renderer.js';
|
|
17
|
+
import { renderSddArtifactAccepted, renderSddArtifactReopened, renderSddCockpit, renderSddArtifactDetail, renderSddArtifactList, renderSddContinuationPlan, renderSddNext, renderSddStatus, sddContinuationPlanFrom, } from '../sdd-renderer.js';
|
|
17
18
|
export function runMemoryCommand(command, parsed, database) {
|
|
18
19
|
const handlers = createMemoryToolHandlers({ service: new MemoryService(database), config: { localMemoryEnabled: true } });
|
|
19
20
|
const context = { actor: 'cli' };
|
|
@@ -122,6 +123,26 @@ export function runSddCommand(command, parsed, database, environment) {
|
|
|
122
123
|
return jsonResult(next);
|
|
123
124
|
return okText(renderSddNext({ project: project.value, decision: next.value }));
|
|
124
125
|
}
|
|
126
|
+
if (command === 'continue') {
|
|
127
|
+
const next = service.getNext({ project: project.value, change: change.value });
|
|
128
|
+
if (!next.ok)
|
|
129
|
+
return jsonResult(next);
|
|
130
|
+
const cockpit = service.getCockpit({ project: project.value, change: change.value });
|
|
131
|
+
if (!cockpit.ok)
|
|
132
|
+
return jsonResult(cockpit);
|
|
133
|
+
const explicitDatabasePath = optionalStringFlag(parsed.flags, 'db');
|
|
134
|
+
const relatedRun = new RunService(database).findRelatedInterruptedSddRun({ project: project.value, change: change.value });
|
|
135
|
+
if (!relatedRun.ok)
|
|
136
|
+
return jsonResult(relatedRun);
|
|
137
|
+
const plan = sddContinuationPlanFrom({
|
|
138
|
+
project: project.value,
|
|
139
|
+
next: next.value,
|
|
140
|
+
cockpit: cockpit.value,
|
|
141
|
+
...(relatedRun.value === undefined ? {} : { relatedRunContext: relatedRun.value }),
|
|
142
|
+
...(explicitDatabasePath === undefined ? {} : { explicitDatabasePath }),
|
|
143
|
+
});
|
|
144
|
+
return parsed.flags.json === true ? jsonResult({ ok: true, value: plan }) : okText(renderSddContinuationPlan(plan));
|
|
145
|
+
}
|
|
125
146
|
if (command === 'cockpit') {
|
|
126
147
|
const cockpit = service.getCockpit({ project: project.value, change: change.value });
|
|
127
148
|
if (!cockpit.ok || parsed.flags.json === true)
|
|
@@ -159,7 +180,8 @@ export function runSddCommand(command, parsed, database, environment) {
|
|
|
159
180
|
return resultFailure(note);
|
|
160
181
|
if (!acceptedAt.ok)
|
|
161
182
|
return resultFailure(acceptedAt);
|
|
162
|
-
|
|
183
|
+
const canonicalPhase = normalizeSddPhaseInput(phase.value);
|
|
184
|
+
if (canonicalPhase === undefined)
|
|
163
185
|
return resultFailure(validationFailure(`Unknown SDD phase: ${phase.value}. Expected one of: ${sddPhases.join(', ')}`));
|
|
164
186
|
const acceptedBy = {
|
|
165
187
|
type: 'human',
|
|
@@ -169,7 +191,7 @@ export function runSddCommand(command, parsed, database, environment) {
|
|
|
169
191
|
const accepted = service.acceptArtifact({
|
|
170
192
|
project: project.value,
|
|
171
193
|
change: change.value,
|
|
172
|
-
phase:
|
|
194
|
+
phase: canonicalPhase,
|
|
173
195
|
acceptedBy,
|
|
174
196
|
acceptedAt: acceptedAt.value,
|
|
175
197
|
...(note.value === undefined ? {} : { note: note.value }),
|
|
@@ -179,7 +201,7 @@ export function runSddCommand(command, parsed, database, environment) {
|
|
|
179
201
|
const view = {
|
|
180
202
|
project: accepted.value.project,
|
|
181
203
|
change: change.value,
|
|
182
|
-
phase:
|
|
204
|
+
phase: canonicalPhase,
|
|
183
205
|
topicKey: accepted.value.topicKey,
|
|
184
206
|
artifactId: accepted.value.id,
|
|
185
207
|
status: 'accepted',
|
|
@@ -189,6 +211,50 @@ export function runSddCommand(command, parsed, database, environment) {
|
|
|
189
211
|
};
|
|
190
212
|
return parsed.flags.json === true ? jsonResult({ ok: true, value: view }) : okText(renderSddArtifactAccepted(view));
|
|
191
213
|
}
|
|
214
|
+
if (command === 'reopen-artifact') {
|
|
215
|
+
const phase = requiredTrimmedFlag(parsed.flags, 'phase');
|
|
216
|
+
const actor = requiredTrimmedFlag(parsed.flags, 'actor');
|
|
217
|
+
const displayName = optionalTrimmedFlag(parsed.flags, 'display-name');
|
|
218
|
+
const note = optionalTrimmedFlag(parsed.flags, 'note');
|
|
219
|
+
if (!phase.ok)
|
|
220
|
+
return resultFailure(phase);
|
|
221
|
+
if (!actor.ok)
|
|
222
|
+
return resultFailure(actor);
|
|
223
|
+
if (!displayName.ok)
|
|
224
|
+
return resultFailure(displayName);
|
|
225
|
+
if (!note.ok)
|
|
226
|
+
return resultFailure(note);
|
|
227
|
+
const canonicalPhase = normalizeSddPhaseInput(phase.value);
|
|
228
|
+
if (canonicalPhase === undefined)
|
|
229
|
+
return resultFailure(validationFailure(`Unknown SDD phase: ${phase.value}. Expected one of: ${sddPhases.join(', ')}`));
|
|
230
|
+
const reopenedBy = {
|
|
231
|
+
type: 'human',
|
|
232
|
+
id: actor.value,
|
|
233
|
+
displayName: displayName.value ?? actor.value,
|
|
234
|
+
};
|
|
235
|
+
const reopened = service.reopenArtifact({
|
|
236
|
+
project: project.value,
|
|
237
|
+
change: change.value,
|
|
238
|
+
phase: canonicalPhase,
|
|
239
|
+
reopenedBy,
|
|
240
|
+
...(note.value === undefined ? {} : { note: note.value }),
|
|
241
|
+
});
|
|
242
|
+
if (!reopened.ok)
|
|
243
|
+
return resultFailure(reopened);
|
|
244
|
+
const normalized = normalizeSddArtifact(reopened.value);
|
|
245
|
+
const view = {
|
|
246
|
+
project: reopened.value.project,
|
|
247
|
+
change: change.value,
|
|
248
|
+
phase: canonicalPhase,
|
|
249
|
+
topicKey: reopened.value.topicKey,
|
|
250
|
+
artifactId: reopened.value.id,
|
|
251
|
+
status: normalized.metadata.status,
|
|
252
|
+
reopenedBy,
|
|
253
|
+
reopenedAt: normalized.metadata.reopen?.reopenedAt ?? normalized.metadata.updatedAt ?? reopened.value.updatedAt,
|
|
254
|
+
...(note.value === undefined ? {} : { note: note.value }),
|
|
255
|
+
};
|
|
256
|
+
return parsed.flags.json === true ? jsonResult({ ok: true, value: view }) : okText(renderSddArtifactReopened(view));
|
|
257
|
+
}
|
|
192
258
|
if (command === 'get-artifact') {
|
|
193
259
|
const phase = requiredFlag(parsed.flags, 'phase');
|
|
194
260
|
if (!phase.ok)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
2
|
import { AgentRegistryService } from '../../agents/agent-registry-service.js';
|
|
3
|
+
import { computeEffectiveManagerInstructions } from '../../agents/manager-profile-overlay-service.js';
|
|
3
4
|
import { resolveAgentProfileModel } from '../../agents/profile-model-routing.js';
|
|
4
5
|
import { installOpenCodeMcpClient } from '../../mcp/client-install-opencode.js';
|
|
5
6
|
import { createMcpClientSetupPreview } from '../../mcp/client-setup-preview.js';
|
|
@@ -144,16 +145,27 @@ export async function applySetupPlanInput(input, environment) {
|
|
|
144
145
|
plan: plan.value,
|
|
145
146
|
},
|
|
146
147
|
};
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
148
|
+
const opened = openCliDatabase(plan.value.db.path);
|
|
149
|
+
if (!opened.ok)
|
|
150
|
+
return opened;
|
|
151
|
+
let result;
|
|
152
|
+
try {
|
|
153
|
+
const effectiveManagerInstructions = computeEffectiveManagerInstructions(opened.value, input.project);
|
|
154
|
+
result = await installOpenCodeMcpClient({
|
|
155
|
+
cwd: environment.cwd,
|
|
156
|
+
databasePath: plan.value.db.path,
|
|
157
|
+
databasePathSource: plan.value.db.source === 'global-default' || plan.value.db.source === 'environment' ? plan.value.db.source : 'flag',
|
|
158
|
+
scope: input.scope ?? vgxnessSetupDefaults.defaultOpenCodeScope,
|
|
159
|
+
env: environment.env,
|
|
160
|
+
confirmed: true,
|
|
161
|
+
mcpOnly: input.installMode === 'mcp-only',
|
|
162
|
+
...(input.overwriteVgxness === undefined ? {} : { overwriteVgxness: input.overwriteVgxness }),
|
|
163
|
+
...(effectiveManagerInstructions === undefined ? {} : { effectiveManagerInstructions }),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
opened.value.close();
|
|
168
|
+
}
|
|
157
169
|
return result.status === 'installed'
|
|
158
170
|
? {
|
|
159
171
|
ok: true,
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { MemoryService } from '../../memory/memory-service.js';
|
|
2
|
+
import { openMemoryDatabase } from '../../memory/sqlite/database.js';
|
|
3
|
+
import { SddWorkflowService } from '../../sdd/sdd-workflow-service.js';
|
|
4
|
+
import { buildProductStatus } from '../../status/product-status.js';
|
|
5
|
+
import { RunService } from '../../runs/run-service.js';
|
|
6
|
+
import { buildProductResume } from '../../resume/product-resume.js';
|
|
7
|
+
import { databasePathFor, optionalStringFlag, requiredFlag } from '../cli-flags.js';
|
|
8
|
+
import { jsonResult, resultFailure } from '../cli-helpers.js';
|
|
9
|
+
import { okText, usageFailure } from '../cli-help.js';
|
|
10
|
+
import { renderProductResume } from '../product-resume-renderer.js';
|
|
11
|
+
import { buildProductNext, renderProductNext, renderProductStatus } from '../product-status-renderer.js';
|
|
12
|
+
export function runProductStatusCommand(parsed, environment) {
|
|
13
|
+
const extraPositionals = rejectExtraPositionals(parsed, 'status');
|
|
14
|
+
if (extraPositionals !== undefined)
|
|
15
|
+
return extraPositionals;
|
|
16
|
+
const status = buildProductStatusForCli(parsed, environment);
|
|
17
|
+
if (!status.ok)
|
|
18
|
+
return status.value;
|
|
19
|
+
return parsed.flags.json === true ? jsonResult({ ok: true, value: status.value }) : okText(renderProductStatus(status.value));
|
|
20
|
+
}
|
|
21
|
+
export function runProductNextCommand(parsed, environment) {
|
|
22
|
+
const extraPositionals = rejectExtraPositionals(parsed, 'next');
|
|
23
|
+
if (extraPositionals !== undefined)
|
|
24
|
+
return extraPositionals;
|
|
25
|
+
const status = buildProductStatusForCli(parsed, environment);
|
|
26
|
+
if (!status.ok)
|
|
27
|
+
return status.value;
|
|
28
|
+
const next = buildProductNext(status.value);
|
|
29
|
+
return parsed.flags.json === true ? jsonResult({ ok: true, value: next }) : okText(renderProductNext(next));
|
|
30
|
+
}
|
|
31
|
+
export function runProductResumeCommand(parsed, environment) {
|
|
32
|
+
const extraPositionals = rejectExtraPositionals(parsed, 'resume');
|
|
33
|
+
if (extraPositionals !== undefined)
|
|
34
|
+
return extraPositionals;
|
|
35
|
+
const resume = buildProductResumeForCli(parsed, environment);
|
|
36
|
+
if (!resume.ok)
|
|
37
|
+
return resume.value;
|
|
38
|
+
return parsed.flags.json === true ? jsonResult({ ok: true, value: resume.value }) : okText(renderProductResume(resume.value));
|
|
39
|
+
}
|
|
40
|
+
function rejectExtraPositionals(parsed, command) {
|
|
41
|
+
const extra = parsed.positionals.slice(1);
|
|
42
|
+
return extra.length === 0 ? undefined : usageFailure(`${command} does not accept positional arguments: ${extra.join(' ')}`);
|
|
43
|
+
}
|
|
44
|
+
function buildProductStatusForCli(parsed, environment) {
|
|
45
|
+
const projectFlag = parsed.flags.project === undefined ? { ok: true, value: undefined } : requiredFlag(parsed.flags, 'project');
|
|
46
|
+
if (!projectFlag.ok)
|
|
47
|
+
return { ok: false, value: resultFailure(projectFlag) };
|
|
48
|
+
const changeFlag = parsed.flags.change === undefined ? { ok: true, value: undefined } : requiredFlag(parsed.flags, 'change');
|
|
49
|
+
if (!changeFlag.ok)
|
|
50
|
+
return { ok: false, value: resultFailure(changeFlag) };
|
|
51
|
+
const project = projectFlag.value;
|
|
52
|
+
const change = changeFlag.value;
|
|
53
|
+
if (change === undefined) {
|
|
54
|
+
const status = buildProductStatus({ cwd: environment.cwd, ...(project === undefined ? {} : { project }) });
|
|
55
|
+
return { ok: true, value: status };
|
|
56
|
+
}
|
|
57
|
+
const selectedDatabasePath = databasePathFor(parsed.flags, environment);
|
|
58
|
+
if (!selectedDatabasePath.ok)
|
|
59
|
+
return { ok: false, value: resultFailure(selectedDatabasePath) };
|
|
60
|
+
const explicitDatabasePath = optionalStringFlag(parsed.flags, 'db');
|
|
61
|
+
const opened = openMemoryDatabase({ path: selectedDatabasePath.value, readonly: true });
|
|
62
|
+
if (!opened.ok) {
|
|
63
|
+
const status = buildProductStatus({
|
|
64
|
+
cwd: environment.cwd,
|
|
65
|
+
...(project === undefined ? {} : { project }),
|
|
66
|
+
change,
|
|
67
|
+
databasePath: selectedDatabasePath.value,
|
|
68
|
+
databaseError: opened.error.message,
|
|
69
|
+
});
|
|
70
|
+
return { ok: true, value: status };
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const status = buildProductStatus({
|
|
74
|
+
cwd: environment.cwd,
|
|
75
|
+
...(project === undefined ? {} : { project }),
|
|
76
|
+
change,
|
|
77
|
+
databasePath: selectedDatabasePath.value,
|
|
78
|
+
...(explicitDatabasePath === undefined ? {} : { explicitDatabasePath }),
|
|
79
|
+
sdd: new SddWorkflowService(new MemoryService(opened.value)),
|
|
80
|
+
runs: new RunService(opened.value),
|
|
81
|
+
});
|
|
82
|
+
return { ok: true, value: status };
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
opened.value.close();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function buildProductResumeForCli(parsed, environment) {
|
|
89
|
+
const projectFlag = parsed.flags.project === undefined ? { ok: true, value: undefined } : requiredFlag(parsed.flags, 'project');
|
|
90
|
+
if (!projectFlag.ok)
|
|
91
|
+
return { ok: false, value: resultFailure(projectFlag) };
|
|
92
|
+
const runIdFlag = parsed.flags['run-id'] === undefined ? { ok: true, value: undefined } : requiredFlag(parsed.flags, 'run-id');
|
|
93
|
+
if (!runIdFlag.ok)
|
|
94
|
+
return { ok: false, value: resultFailure(runIdFlag) };
|
|
95
|
+
const project = projectFlag.value;
|
|
96
|
+
const runId = runIdFlag.value;
|
|
97
|
+
if (runId === undefined && project === undefined) {
|
|
98
|
+
const resume = buildProductResume({ cwd: environment.cwd, ...(project === undefined ? {} : { project }) });
|
|
99
|
+
return { ok: true, value: resume };
|
|
100
|
+
}
|
|
101
|
+
const selectedDatabasePath = databasePathFor(parsed.flags, environment);
|
|
102
|
+
if (!selectedDatabasePath.ok)
|
|
103
|
+
return { ok: false, value: resultFailure(selectedDatabasePath) };
|
|
104
|
+
const opened = openMemoryDatabase({ path: selectedDatabasePath.value, readonly: true });
|
|
105
|
+
if (!opened.ok) {
|
|
106
|
+
const visibleDatabasePath = runId === undefined && parsed.flags.db === undefined ? undefined : selectedDatabasePath.value;
|
|
107
|
+
const resume = buildProductResume({
|
|
108
|
+
cwd: environment.cwd,
|
|
109
|
+
...(project === undefined ? {} : { project }),
|
|
110
|
+
...(runId === undefined ? {} : { runId }),
|
|
111
|
+
...(visibleDatabasePath === undefined ? {} : { databasePath: visibleDatabasePath }),
|
|
112
|
+
databaseError: opened.error.message,
|
|
113
|
+
});
|
|
114
|
+
return { ok: true, value: resume };
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
const flaggedDatabasePath = parsed.flags.db === undefined ? undefined : selectedDatabasePath.value;
|
|
118
|
+
const resume = buildProductResume({
|
|
119
|
+
cwd: environment.cwd,
|
|
120
|
+
...(project === undefined ? {} : { project }),
|
|
121
|
+
...(runId === undefined ? {} : { runId }),
|
|
122
|
+
...(flaggedDatabasePath === undefined ? {} : { databasePath: flaggedDatabasePath }),
|
|
123
|
+
runs: new RunService(opened.value),
|
|
124
|
+
});
|
|
125
|
+
return { ok: true, value: resume };
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
opened.value.close();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { AgentRegistryService } from '../../agents/agent-registry-service.js';
|
|
2
|
+
import { canonicalOpenCodeDefaultModel } from '../../agents/canonical-agent-manifest.js';
|
|
2
3
|
import { createNaturalLanguagePlan } from '../../orchestrator/natural-language-planner.js';
|
|
3
4
|
import { RunService } from '../../runs/run-service.js';
|
|
4
|
-
import {
|
|
5
|
+
import { normalizeSddPhaseInput, sddPhases } from '../../sdd/schema.js';
|
|
5
6
|
import { CommandAllowlistAdapter, commandAllowlistIds } from '../../workflows/command-allowlist-adapter.js';
|
|
6
7
|
import { GuardedProviderWorkflowExecutor, operationMetadataForWorkflowExecution, SafeNonDispatchingWorkflowExecutor, workflowExecutorSafety, } from '../../workflows/workflow-executor.js';
|
|
7
8
|
import { getWorkflowDefinition, listWorkflows } from '../../workflows/workflow-registry.js';
|
|
@@ -248,11 +249,16 @@ export function runWorkflowRunCommand(workflow, parsed, database) {
|
|
|
248
249
|
const selected = getWorkflowDefinition(workflow);
|
|
249
250
|
const planner = createNaturalLanguagePlan({ project, intent: intent.value });
|
|
250
251
|
const recommended = getWorkflowDefinition(planner.workflow);
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
252
|
+
const phaseInput = optionalStringFlag(parsed.flags, 'phase') ?? selected.defaultPhase;
|
|
253
|
+
let phase = phaseInput;
|
|
254
|
+
if (selected.id === 'sdd') {
|
|
255
|
+
const canonicalPhase = normalizeSddPhaseInput(phaseInput);
|
|
256
|
+
if (canonicalPhase === undefined)
|
|
257
|
+
return resultFailure(validationFailure(`Unknown SDD phase: ${phaseInput}. Expected one of: ${sddPhases.join(', ')}`));
|
|
258
|
+
phase = canonicalPhase;
|
|
259
|
+
}
|
|
254
260
|
const providerAdapter = optionalStringFlag(parsed.flags, 'provider-adapter') ?? 'opencode';
|
|
255
|
-
const model = optionalStringFlag(parsed.flags, 'model') ??
|
|
261
|
+
const model = optionalStringFlag(parsed.flags, 'model') ?? canonicalOpenCodeDefaultModel;
|
|
256
262
|
const registry = new AgentRegistryService(database);
|
|
257
263
|
const explicitAgentId = optionalStringFlag(parsed.flags, 'agent-id');
|
|
258
264
|
const selectedAgent = resolveWorkflowRunAgent({
|
package/dist/cli/dispatcher.js
CHANGED
|
@@ -5,7 +5,7 @@ import { databasePathFor, parseArgs, requiredFlag } from './cli-flags.js';
|
|
|
5
5
|
import { okText, usageFailure, visibleHelpText } from './cli-help.js';
|
|
6
6
|
import { openCliDatabase, resultFailure } from './cli-helpers.js';
|
|
7
7
|
import { runBootAgentSeedUpgrade } from '../agents/boot-upgrade.js';
|
|
8
|
-
import { runAgentCommand, runApprovalsCommand, runCodeCliCommand, runDefaultInteractiveEntrypoint, runDoctorAliasCommand, runInitCommand, runMcpDoctorCommand, runMcpInstallCommand, runMcpSetupCommand, runMemoryCommand, runMemoryImportCommand, runOpenCodeCommand, runOrchestratorCommand, runPermissionsCommand, runRunsCommand, runSddCommand, runSetupApplyCommand, runSetupLifecycleCommand, runSetupPlanCommand, runSetupRollbackCommand, runSkillCommand, runSubagentCommand, runVerificationPlanCommand, runVerificationReportCommand, runWorkflowExecuteCommand, runWorkflowPreviewCommand, runWorkflowRunCommand, } from './commands/index.js';
|
|
8
|
+
import { runAgentCommand, runApprovalsCommand, runCodeCliCommand, runDefaultInteractiveEntrypoint, runDoctorAliasCommand, runInitCommand, runMcpDoctorCommand, runMcpInstallCommand, runMcpSetupCommand, runMemoryCommand, runMemoryImportCommand, runOpenCodeCommand, runOrchestratorCommand, runPermissionsCommand, runProductNextCommand, runProductResumeCommand, runRunsCommand, runSddCommand, runSetupApplyCommand, runSetupLifecycleCommand, runSetupPlanCommand, runSetupRollbackCommand, runProductStatusCommand, runSkillCommand, runSubagentCommand, runVerificationPlanCommand, runVerificationReportCommand, runWorkflowExecuteCommand, runWorkflowPreviewCommand, runWorkflowRunCommand, } from './commands/index.js';
|
|
9
9
|
const _promptBuffers = new WeakMap();
|
|
10
10
|
const require = createRequire(import.meta.url);
|
|
11
11
|
const packageJson = require('../../package.json');
|
|
@@ -17,6 +17,12 @@ export function dispatchCli(argv, environment) {
|
|
|
17
17
|
const [area, command] = parsed.positionals;
|
|
18
18
|
if (!area || area === 'help' || area === '--help' || area === '-h')
|
|
19
19
|
return okText(visibleHelpText());
|
|
20
|
+
if (area === 'status')
|
|
21
|
+
return runProductStatusCommand(parsed, environment);
|
|
22
|
+
if (area === 'next')
|
|
23
|
+
return runProductNextCommand(parsed, environment);
|
|
24
|
+
if (area === 'resume')
|
|
25
|
+
return runProductResumeCommand(parsed, environment);
|
|
20
26
|
if (area === 'init')
|
|
21
27
|
return runSetupPlanCommand(parsed, environment);
|
|
22
28
|
if (area === 'doctor')
|
|
@@ -192,10 +198,12 @@ function validateCommand(area, command) {
|
|
|
192
198
|
command === 'execute' ||
|
|
193
199
|
command === 'status' ||
|
|
194
200
|
command === 'next' ||
|
|
201
|
+
command === 'continue' ||
|
|
195
202
|
command === 'cockpit' ||
|
|
196
203
|
command === 'ready' ||
|
|
197
204
|
command === 'save-artifact' ||
|
|
198
205
|
command === 'accept-artifact' ||
|
|
206
|
+
command === 'reopen-artifact' ||
|
|
199
207
|
command === 'get-artifact' ||
|
|
200
208
|
command === 'list-artifacts' ||
|
|
201
209
|
command === 'export' ||
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export function renderProductResume(resume) {
|
|
2
|
+
const lines = [
|
|
3
|
+
'Resume',
|
|
4
|
+
...resume.resume.map((line) => `- ${line}`),
|
|
5
|
+
'',
|
|
6
|
+
'Why',
|
|
7
|
+
...resume.why.map((line) => `- ${line}`),
|
|
8
|
+
'',
|
|
9
|
+
'Blockers',
|
|
10
|
+
...(resume.blockers.length === 0 ? ['- none'] : resume.blockers.map((line) => `- ${line}`)),
|
|
11
|
+
...(resume.candidateRuns.length === 0
|
|
12
|
+
? []
|
|
13
|
+
: [
|
|
14
|
+
'',
|
|
15
|
+
'Candidate runs',
|
|
16
|
+
...resume.candidateRuns.flatMap((run) => [
|
|
17
|
+
`- ${run.runId} [${run.status}] ${run.workflow}/${run.phase}`,
|
|
18
|
+
...(run.userIntent === undefined ? [] : [` Intent: ${run.userIntent}`]),
|
|
19
|
+
` Latest checkpoint: ${run.latestCheckpointLabel ?? 'none'}`,
|
|
20
|
+
` Resume command: ${run.command}`,
|
|
21
|
+
]),
|
|
22
|
+
]),
|
|
23
|
+
'',
|
|
24
|
+
'Command',
|
|
25
|
+
`- ${resume.command}`,
|
|
26
|
+
...(resume.manualNextCommands.length === 0 ? [] : ['', 'Related commands', ...resume.manualNextCommands.map((line) => `- ${line}`)]),
|
|
27
|
+
'',
|
|
28
|
+
'Safety',
|
|
29
|
+
...resume.safety.map((line) => `- ${line}`),
|
|
30
|
+
];
|
|
31
|
+
return `${lines.join('\n')}\n`;
|
|
32
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export function renderProductStatus(status) {
|
|
2
|
+
const commandHeading = productCommandHeading(status);
|
|
3
|
+
const lines = [
|
|
4
|
+
'Status',
|
|
5
|
+
...status.status.map((line) => `- ${line}`),
|
|
6
|
+
'',
|
|
7
|
+
'Blockers',
|
|
8
|
+
...status.blockers.map((line) => `- ${line}`),
|
|
9
|
+
'',
|
|
10
|
+
'Next',
|
|
11
|
+
...status.next.map((line) => `- ${line}`),
|
|
12
|
+
'',
|
|
13
|
+
commandHeading,
|
|
14
|
+
`- ${status.command}`,
|
|
15
|
+
'',
|
|
16
|
+
...(status.relatedRunContext === undefined ? [] : relatedRunContextLines(status.relatedRunContext, true)),
|
|
17
|
+
'Safety',
|
|
18
|
+
...status.safety.map((line) => `- ${line}`),
|
|
19
|
+
];
|
|
20
|
+
return `${lines.join('\n')}\n`;
|
|
21
|
+
}
|
|
22
|
+
export function buildProductNext(status) {
|
|
23
|
+
const blockers = status.blockers.filter((line) => line !== 'none');
|
|
24
|
+
const blocked = status.sddNextStatus === undefined ? blockers.length > 0 : status.sddNextStatus === 'blocked';
|
|
25
|
+
const visibleBlockers = blocked ? blockers : [];
|
|
26
|
+
const command = blocked ? buildBlockedNextCommand(status) : status.command;
|
|
27
|
+
return {
|
|
28
|
+
version: 1,
|
|
29
|
+
kind: 'product-next',
|
|
30
|
+
project: status.project,
|
|
31
|
+
...(status.change === undefined ? {} : { change: status.change }),
|
|
32
|
+
blocked,
|
|
33
|
+
next: status.next,
|
|
34
|
+
why: status.status,
|
|
35
|
+
blockers: visibleBlockers,
|
|
36
|
+
command,
|
|
37
|
+
...(status.relatedRunContext === undefined ? {} : { relatedRunContext: status.relatedRunContext }),
|
|
38
|
+
safety: status.safety,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function buildBlockedNextCommand(status) {
|
|
42
|
+
const change = status.change === undefined ? '<change>' : status.change;
|
|
43
|
+
const dbHint = status.command.includes('--db <path>') ? ' --db <path>' : '';
|
|
44
|
+
return `vgxness next --project ${status.project.value} --change ${change}${dbHint}`;
|
|
45
|
+
}
|
|
46
|
+
export function renderProductNext(next) {
|
|
47
|
+
const commandHeading = next.blocked ? 'Recovery/diagnostic command' : 'Manual fallback command';
|
|
48
|
+
const lines = [
|
|
49
|
+
'Next',
|
|
50
|
+
...next.next.map((line) => `- ${line}`),
|
|
51
|
+
'',
|
|
52
|
+
'Why',
|
|
53
|
+
...next.why.map((line) => `- ${line}`),
|
|
54
|
+
...(next.blockers.length === 0 ? [] : ['', 'Blockers', ...next.blockers.map((line) => `- ${line}`)]),
|
|
55
|
+
'',
|
|
56
|
+
commandHeading,
|
|
57
|
+
`- ${next.command}`,
|
|
58
|
+
'',
|
|
59
|
+
...(next.relatedRunContext === undefined ? [] : relatedRunContextLines(next.relatedRunContext, true)),
|
|
60
|
+
'Safety',
|
|
61
|
+
...next.safety.map((line) => `- ${line}`),
|
|
62
|
+
];
|
|
63
|
+
return `${lines.join('\n')}\n`;
|
|
64
|
+
}
|
|
65
|
+
function productCommandHeading(status) {
|
|
66
|
+
if (status.sddNextStatus === 'runnable')
|
|
67
|
+
return 'Manual fallback command';
|
|
68
|
+
return 'Recovery/diagnostic command';
|
|
69
|
+
}
|
|
70
|
+
function relatedRunContextLines(relatedRunContext, includeTrailingBlank) {
|
|
71
|
+
return [
|
|
72
|
+
'Related interrupted run:',
|
|
73
|
+
`- Run ID: ${relatedRunContext.runId}`,
|
|
74
|
+
`- Status: ${relatedRunContext.status}`,
|
|
75
|
+
`- Workflow/phase: ${relatedRunContext.workflow}/${relatedRunContext.phase}`,
|
|
76
|
+
`- Latest checkpoint: ${relatedRunContext.latestCheckpointLabel ?? 'none'}`,
|
|
77
|
+
`- Recommendation: ${relatedRunContext.recommendation}`,
|
|
78
|
+
`- Resume command: ${relatedRunContext.resumeCommand}`,
|
|
79
|
+
...(includeTrailingBlank ? [''] : []),
|
|
80
|
+
];
|
|
81
|
+
}
|
package/dist/cli/sdd-renderer.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { normalizeSddArtifact, sddPhases } from '../sdd/schema.js';
|
|
1
|
+
import { normalizeSddArtifact, sddPhases, } from '../sdd/schema.js';
|
|
2
|
+
export { sddContinuationPlanFrom } from '../sdd/sdd-continuation-plan.js';
|
|
2
3
|
export function renderSddStatus(input) {
|
|
3
4
|
const missing = input.status.phases.filter((phase) => !phase.present).map((phase) => phase.topicKey);
|
|
4
5
|
const blockers = input.status.phases.filter((phase) => phase.present && phase.accepted !== true);
|
|
@@ -9,7 +10,8 @@ export function renderSddStatus(input) {
|
|
|
9
10
|
? 'No next SDD phase remains for this change.'
|
|
10
11
|
: input.status.nextReadyPhase === undefined
|
|
11
12
|
? 'Review blockers or accept present draft artifacts before continuing.'
|
|
12
|
-
: `
|
|
13
|
+
: `Continue the ${input.status.nextReadyPhase} phase in OpenCode using VGXNESS MCP and hidden SDD subagents.`;
|
|
14
|
+
const commandLabel = directPhaseCommand === undefined ? 'Diagnostic command' : 'Manual fallback command';
|
|
13
15
|
const lines = [
|
|
14
16
|
'SDD Status',
|
|
15
17
|
`Project: ${input.project}`,
|
|
@@ -27,17 +29,22 @@ export function renderSddStatus(input) {
|
|
|
27
29
|
...(missing.length === 0 ? ['- none'] : missing.map((topicKey) => `- ${topicKey}`)),
|
|
28
30
|
'',
|
|
29
31
|
`Recommended action: ${recommendedAction}`,
|
|
30
|
-
|
|
32
|
+
`${commandLabel}: ${directPhaseCommand ?? `vgxness sdd next --project ${input.project} --change ${input.status.change}`}`,
|
|
31
33
|
`JSON: vgxness sdd status --project ${input.project} --change ${input.status.change} --json`,
|
|
32
34
|
];
|
|
33
35
|
return `${lines.join('\n')}\n`;
|
|
34
36
|
}
|
|
35
37
|
export function renderSddNext(input) {
|
|
36
38
|
const blockers = input.decision.blockedPrerequisites ?? [];
|
|
39
|
+
const guidance = input.decision.blockerGuidance ?? [];
|
|
37
40
|
const phase = input.decision.nextPhase ?? 'none';
|
|
38
41
|
const commandGuidance = input.decision.status === 'runnable' && input.decision.nextPhase !== undefined
|
|
39
42
|
? `vgxness code sdd ${input.decision.change} ${input.decision.nextPhase} --save-artifact`
|
|
40
43
|
: `vgxness sdd status --project ${input.project} --change ${input.decision.change}`;
|
|
44
|
+
const commandLabel = input.decision.status === 'runnable' && input.decision.nextPhase !== undefined ? 'Manual fallback command' : 'Diagnostic command';
|
|
45
|
+
const primaryAction = input.decision.status === 'runnable' && input.decision.nextPhase !== undefined
|
|
46
|
+
? `Continue the ${input.decision.nextPhase} phase in OpenCode using VGXNESS MCP and hidden SDD subagents.`
|
|
47
|
+
: input.decision.recommendedAction;
|
|
41
48
|
const lines = [
|
|
42
49
|
'SDD Next',
|
|
43
50
|
`Project: ${input.project}`,
|
|
@@ -47,17 +54,71 @@ export function renderSddNext(input) {
|
|
|
47
54
|
`Reason: ${input.decision.reason}`,
|
|
48
55
|
'',
|
|
49
56
|
'Blockers:',
|
|
50
|
-
...(blockers.length === 0
|
|
57
|
+
...(blockers.length === 0
|
|
58
|
+
? ['- none']
|
|
59
|
+
: blockers.map((blocker) => `- ${blocker.phase}: ${blocker.reason} at ${blocker.topicKey}${blocker.action === undefined ? '' : `; action=${blocker.action}`}`)),
|
|
51
60
|
'',
|
|
52
61
|
'Missing topic keys:',
|
|
53
62
|
...(input.decision.missingArtifactTopicKeys.length === 0 ? ['- none'] : input.decision.missingArtifactTopicKeys.map((topicKey) => `- ${topicKey}`)),
|
|
54
63
|
'',
|
|
55
|
-
|
|
56
|
-
|
|
64
|
+
'Missing prerequisite topic keys:',
|
|
65
|
+
...((input.decision.missingPrerequisiteTopicKeys ?? []).length === 0
|
|
66
|
+
? ['- none']
|
|
67
|
+
: (input.decision.missingPrerequisiteTopicKeys ?? []).map((topicKey) => `- ${topicKey}`)),
|
|
68
|
+
'',
|
|
69
|
+
'Next actions:',
|
|
70
|
+
...(guidance.length === 0 ? ['- none'] : guidance.map((item) => `- ${item.phase}: ${item.action}`)),
|
|
71
|
+
'',
|
|
72
|
+
`Recommended action: ${primaryAction}`,
|
|
73
|
+
`${commandLabel}: ${commandGuidance}`,
|
|
57
74
|
`JSON: vgxness sdd next --project ${input.project} --change ${input.decision.change} --json`,
|
|
58
75
|
];
|
|
59
76
|
return `${lines.join('\n')}\n`;
|
|
60
77
|
}
|
|
78
|
+
export function renderSddContinuationPlan(plan) {
|
|
79
|
+
const suggestedCommandLabel = plan.status === 'runnable' ? 'Manual fallback command' : 'Diagnostic command';
|
|
80
|
+
const lines = [
|
|
81
|
+
'SDD Continue (read-only)',
|
|
82
|
+
`Project: ${plan.project}`,
|
|
83
|
+
`Change: ${plan.change}`,
|
|
84
|
+
`Status: ${plan.status}`,
|
|
85
|
+
`Next phase: ${plan.nextPhase ?? 'none'}`,
|
|
86
|
+
`Actionable phase: ${plan.actionablePhase ?? 'none'}`,
|
|
87
|
+
`Reason: ${plan.reason}`,
|
|
88
|
+
'',
|
|
89
|
+
'Recommended plan:',
|
|
90
|
+
`- ${plan.status === 'runnable' && plan.nextPhase !== undefined ? `Continue the ${plan.nextPhase} phase in OpenCode using VGXNESS MCP and hidden SDD subagents.` : plan.recommendedAction}`,
|
|
91
|
+
`- ${suggestedCommandLabel}: ${plan.suggestedCommand}`,
|
|
92
|
+
`- Diagnostic command: ${plan.inspectCommand}`,
|
|
93
|
+
'',
|
|
94
|
+
'Blocker-specific next actions:',
|
|
95
|
+
...(plan.blockerActions.length === 0
|
|
96
|
+
? ['- none']
|
|
97
|
+
: plan.blockerActions.map((item) => {
|
|
98
|
+
const draftRun = item.draftRunCommand === undefined ? '' : `; manualFallbackCommand=${item.draftRunCommand}`;
|
|
99
|
+
const warning = item.warning === undefined ? '' : `; warning=${item.warning}`;
|
|
100
|
+
return `- ${item.phase}: ${item.action}; recoveryCommand=${item.command}${draftRun}${warning}`;
|
|
101
|
+
})),
|
|
102
|
+
'',
|
|
103
|
+
...(plan.relatedRunContext === undefined
|
|
104
|
+
? []
|
|
105
|
+
: [
|
|
106
|
+
'Related interrupted run:',
|
|
107
|
+
`- Run ID: ${plan.relatedRunContext.runId}`,
|
|
108
|
+
`- Status: ${plan.relatedRunContext.status}`,
|
|
109
|
+
`- Workflow/phase: ${plan.relatedRunContext.workflow}/${plan.relatedRunContext.phase}`,
|
|
110
|
+
`- Latest checkpoint: ${plan.relatedRunContext.latestCheckpointLabel ?? 'none'}`,
|
|
111
|
+
`- Recommendation: ${plan.relatedRunContext.recommendation}`,
|
|
112
|
+
`- Resume command: ${plan.relatedRunContext.resumeCommand}`,
|
|
113
|
+
'',
|
|
114
|
+
]),
|
|
115
|
+
'Safety:',
|
|
116
|
+
...plan.safety.map((line) => `- ${line}`),
|
|
117
|
+
'',
|
|
118
|
+
`JSON: vgxness sdd continue --project ${plan.project} --change ${plan.change} --json${continuationDbFlag(plan.explicitDatabasePath)}`,
|
|
119
|
+
];
|
|
120
|
+
return `${lines.join('\n')}\n`;
|
|
121
|
+
}
|
|
61
122
|
export function renderSddCockpit(cockpit) {
|
|
62
123
|
const blockers = cockpit.aggregateBlockers;
|
|
63
124
|
const lines = [
|
|
@@ -71,13 +132,18 @@ export function renderSddCockpit(cockpit) {
|
|
|
71
132
|
`Legacy artifacts: ${cockpit.legacyCount}`,
|
|
72
133
|
'',
|
|
73
134
|
'Aggregate blockers:',
|
|
74
|
-
...(blockers.length === 0
|
|
135
|
+
...(blockers.length === 0
|
|
136
|
+
? ['- none']
|
|
137
|
+
: blockers.map((blocker) => `- ${blocker.kind}: ${blocker.phase} at ${blocker.topicKey} - ${blocker.reason}${blocker.action === undefined ? '' : `; action=${blocker.action}`}`)),
|
|
75
138
|
'',
|
|
76
139
|
`Inspect: ${cockpit.inspectCommand}`,
|
|
77
140
|
`Cockpit JSON: ${cockpit.inspectCommand}`,
|
|
78
141
|
];
|
|
79
142
|
return `${lines.join('\n')}\n`;
|
|
80
143
|
}
|
|
144
|
+
function continuationDbFlag(explicitDatabasePath) {
|
|
145
|
+
return explicitDatabasePath === undefined ? '' : ` --db ${explicitDatabasePath}`;
|
|
146
|
+
}
|
|
81
147
|
export function renderSddArtifactAccepted(input) {
|
|
82
148
|
const lines = [
|
|
83
149
|
'SDD Artifact Accepted',
|
|
@@ -93,6 +159,23 @@ export function renderSddArtifactAccepted(input) {
|
|
|
93
159
|
];
|
|
94
160
|
return `${lines.join('\n')}\n`;
|
|
95
161
|
}
|
|
162
|
+
export function renderSddArtifactReopened(input) {
|
|
163
|
+
const lines = [
|
|
164
|
+
'SDD Artifact Reopened',
|
|
165
|
+
`- Project: ${input.project}`,
|
|
166
|
+
`- Change: ${input.change}`,
|
|
167
|
+
`- Phase: ${input.phase}`,
|
|
168
|
+
`- Status: ${input.status}`,
|
|
169
|
+
`- Topic key: ${input.topicKey}`,
|
|
170
|
+
`- Artifact ID: ${input.artifactId}`,
|
|
171
|
+
`- Reopened by: ${input.reopenedBy.displayName} (${input.reopenedBy.id})`,
|
|
172
|
+
`- Reopened at: ${input.reopenedAt}`,
|
|
173
|
+
...(input.note === undefined ? [] : [`- Note: ${input.note}`]),
|
|
174
|
+
`Next step: update the ${input.phase} artifact, then request human acceptance again.`,
|
|
175
|
+
`JSON: vgxness sdd reopen-artifact --project ${input.project} --change ${input.change} --phase ${input.phase} --actor ${input.reopenedBy.id} --json`,
|
|
176
|
+
];
|
|
177
|
+
return `${lines.join('\n')}\n`;
|
|
178
|
+
}
|
|
96
179
|
export function renderSddArtifactList(input) {
|
|
97
180
|
const artifactsByPhase = new Map(input.artifacts.map((artifact) => [artifact.phase, artifact]));
|
|
98
181
|
const hasArtifacts = input.artifacts.length > 0;
|