vgxness 1.5.1 → 1.6.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/README.md +23 -2
- package/dist/agents/agent-seed-service.js +10 -0
- package/dist/agents/canonical-agent-manifest.js +177 -0
- package/dist/agents/canonical-agent-projection.js +164 -0
- package/dist/agents/renderers/claude-renderer.js +30 -52
- package/dist/cli/bun-bin.js +6 -0
- package/dist/cli/cli-flags.js +1 -1
- package/dist/cli/cli-help.js +7 -4
- package/dist/cli/commands/agent-skill-dispatcher.js +6 -5
- package/dist/cli/commands/interactive-entrypoint-dispatcher.js +2 -2
- package/dist/cli/commands/mcp-dispatcher.js +75 -3
- package/dist/cli/commands/setup-dispatcher.js +9 -0
- package/dist/cli/index.js +1 -1
- package/dist/cli/tui/main-menu/main-menu-read-model.js +41 -44
- package/dist/cli/tui/main-menu/main-menu-render-shape.js +15 -15
- package/dist/cli/tui/opentui/main-menu/screen.js +39 -41
- package/dist/cli/tui/opentui/main-menu/smoke.js +1 -1
- package/dist/cli/tui/opentui/main-menu/view.js +1 -1
- package/dist/cli/tui/setup/setup-tui-read-model.js +15 -12
- package/dist/governance/governance-report-builder.js +45 -26
- package/dist/mcp/claude-code-agent-config.js +95 -0
- package/dist/mcp/claude-code-cli.js +71 -0
- package/dist/mcp/claude-code-config.js +84 -0
- package/dist/mcp/claude-code-project-memory.js +127 -0
- package/dist/mcp/claude-code-scope.js +18 -0
- package/dist/mcp/client-install-claude-code-contract.js +114 -0
- package/dist/mcp/client-install-claude-code.js +136 -0
- package/dist/mcp/index.js +8 -0
- package/dist/mcp/opencode-default-agent-config.js +7 -113
- package/dist/mcp/provider-canonical-agent-manifest.js +39 -0
- package/dist/mcp/provider-change-plan.js +109 -1
- package/dist/mcp/provider-doctor.js +105 -1
- package/dist/mcp/provider-health-types.js +4 -0
- package/dist/mcp/provider-status.js +159 -3
- package/dist/mcp/schema.js +6 -5
- package/dist/mcp/validation.js +1 -1
- package/dist/memory/memory-service.js +4 -0
- package/dist/sdd/sdd-workflow-service.js +129 -59
- package/dist/setup/providers/claude-setup-adapter.js +13 -8
- package/dist/setup/setup-plan.js +60 -1
- package/docs/architecture.md +55 -113
- package/docs/cli.md +90 -2
- package/docs/code-runtime.md +218 -0
- package/docs/contributing.md +120 -0
- package/docs/glossary.md +211 -0
- package/docs/mcp.md +144 -0
- package/docs/prd.md +23 -26
- package/docs/providers.md +150 -0
- package/docs/roadmap.md +88 -0
- package/docs/safety.md +147 -0
- package/docs/storage.md +93 -0
- package/package.json +1 -1
- package/docs/funcionamiento-del-sistema.md +0 -865
- package/docs/harness-gap-analysis.md +0 -243
- package/docs/vgxcode.md +0 -87
- package/docs/vgxness-code.md +0 -48
|
@@ -21,7 +21,9 @@ export class SddWorkflowService {
|
|
|
21
21
|
if (!validated.ok)
|
|
22
22
|
return validated;
|
|
23
23
|
const phases = this.getPhaseStatuses(validated.value.project, validated.value.change);
|
|
24
|
-
|
|
24
|
+
if (!phases.ok)
|
|
25
|
+
return phases;
|
|
26
|
+
const readiness = getReadinessFromStatuses(validated.value.change, validated.value.phase, phases.value);
|
|
25
27
|
return {
|
|
26
28
|
ok: true,
|
|
27
29
|
value: {
|
|
@@ -36,20 +38,27 @@ export class SddWorkflowService {
|
|
|
36
38
|
if (!validated.ok)
|
|
37
39
|
return validated;
|
|
38
40
|
const phases = this.getPhaseStatuses(validated.value.project, validated.value.change);
|
|
39
|
-
|
|
41
|
+
if (!phases.ok)
|
|
42
|
+
return phases;
|
|
43
|
+
return statusFromPhases(validated.value.change, phases.value);
|
|
40
44
|
}
|
|
41
45
|
getNext(input) {
|
|
42
46
|
const validated = validateProjectAndChange(input.project, input.change);
|
|
43
47
|
if (!validated.ok)
|
|
44
48
|
return validated;
|
|
45
49
|
const phases = this.getPhaseStatuses(validated.value.project, validated.value.change);
|
|
46
|
-
|
|
50
|
+
if (!phases.ok)
|
|
51
|
+
return phases;
|
|
52
|
+
return ok(nextDecisionFromStatuses(validated.value.change, phases.value));
|
|
47
53
|
}
|
|
48
54
|
getCockpit(input) {
|
|
49
55
|
const validated = validateProjectAndChange(input.project, input.change);
|
|
50
56
|
if (!validated.ok)
|
|
51
57
|
return validated;
|
|
52
|
-
const
|
|
58
|
+
const snapshot = this.loadPhaseSnapshot(validated.value.project, validated.value.change);
|
|
59
|
+
if (!snapshot.ok)
|
|
60
|
+
return snapshot;
|
|
61
|
+
const phases = snapshot.value.phases;
|
|
53
62
|
const next = nextDecisionFromStatuses(validated.value.change, phases);
|
|
54
63
|
const cockpitPhases = phases.map((phaseStatus) => {
|
|
55
64
|
const readiness = {
|
|
@@ -57,9 +66,7 @@ export class SddWorkflowService {
|
|
|
57
66
|
phase: phaseStatus.phase,
|
|
58
67
|
...getReadinessFromStatuses(validated.value.change, phaseStatus.phase, phases),
|
|
59
68
|
};
|
|
60
|
-
const artifact = phaseStatus.present
|
|
61
|
-
? this.artifactSummaryForPhase(validated.value.project, validated.value.change, phaseStatus.phase, phaseStatus.topicKey)
|
|
62
|
-
: undefined;
|
|
69
|
+
const artifact = phaseStatus.present ? cockpitArtifactSummaryFromSnapshotItem(phaseStatus) : undefined;
|
|
63
70
|
const blockers = cockpitBlockersForPhase(phaseStatus, readiness);
|
|
64
71
|
return {
|
|
65
72
|
phase: phaseStatus.phase,
|
|
@@ -100,6 +107,39 @@ export class SddWorkflowService {
|
|
|
100
107
|
};
|
|
101
108
|
return ok(cockpit);
|
|
102
109
|
}
|
|
110
|
+
getGovernanceSnapshot(input) {
|
|
111
|
+
const validated = validateProjectAndChange(input.project, input.change);
|
|
112
|
+
if (!validated.ok)
|
|
113
|
+
return validated;
|
|
114
|
+
const snapshot = this.loadPhaseSnapshot(validated.value.project, validated.value.change);
|
|
115
|
+
if (!snapshot.ok)
|
|
116
|
+
return snapshot;
|
|
117
|
+
const phases = snapshot.value.phases;
|
|
118
|
+
const status = statusFromPhases(validated.value.change, phases);
|
|
119
|
+
if (!status.ok)
|
|
120
|
+
return status;
|
|
121
|
+
const warnings = [];
|
|
122
|
+
const artifacts = phases.flatMap((phase) => {
|
|
123
|
+
if (phase.artifact === undefined)
|
|
124
|
+
return [];
|
|
125
|
+
const envelope = normalizeSddArtifact(phase.artifact);
|
|
126
|
+
warnings.push(...envelope.warnings);
|
|
127
|
+
if ((input.payloadMode ?? 'compact') === 'compact') {
|
|
128
|
+
const compactArtifact = compactGovernanceArtifact(envelope.artifact);
|
|
129
|
+
return [
|
|
130
|
+
{
|
|
131
|
+
phase: phase.phase,
|
|
132
|
+
topicKey: phase.topicKey,
|
|
133
|
+
artifact: compactArtifact,
|
|
134
|
+
envelope: { ...envelope, artifact: compactArtifact },
|
|
135
|
+
},
|
|
136
|
+
];
|
|
137
|
+
}
|
|
138
|
+
return [{ phase: phase.phase, topicKey: phase.topicKey, artifact: phase.artifact, envelope }];
|
|
139
|
+
});
|
|
140
|
+
const readiness = input.phase === undefined ? undefined : { change: validated.value.change, phase: input.phase, ...getReadinessFromStatuses(validated.value.change, input.phase, phases) };
|
|
141
|
+
return ok({ status: status.value, artifacts, ...(readiness === undefined ? {} : { readiness }), warnings });
|
|
142
|
+
}
|
|
103
143
|
saveArtifact(input) {
|
|
104
144
|
const validated = this.validatePhaseInput(input);
|
|
105
145
|
if (!validated.ok)
|
|
@@ -161,12 +201,33 @@ export class SddWorkflowService {
|
|
|
161
201
|
if (input.payloadMode !== 'compact')
|
|
162
202
|
return artifact;
|
|
163
203
|
const statuses = this.getPhaseStatuses(validated.value.project, validated.value.change);
|
|
164
|
-
|
|
204
|
+
if (!statuses.ok)
|
|
205
|
+
return statuses;
|
|
206
|
+
return ok(compactArtifactProjection(artifact.value, validated.value.change, validated.value.phase, getReadinessFromStatuses(validated.value.change, validated.value.phase, statuses.value)));
|
|
165
207
|
}
|
|
166
208
|
listArtifacts(input) {
|
|
167
209
|
const validated = validateProjectAndChange(input.project, input.change);
|
|
168
210
|
if (!validated.ok)
|
|
169
211
|
return validated;
|
|
212
|
+
if (input.payloadMode === 'compact') {
|
|
213
|
+
const snapshot = this.loadPhaseSnapshot(validated.value.project, validated.value.change);
|
|
214
|
+
if (!snapshot.ok)
|
|
215
|
+
return snapshot;
|
|
216
|
+
return ok({
|
|
217
|
+
project: validated.value.project,
|
|
218
|
+
change: validated.value.change,
|
|
219
|
+
artifacts: snapshot.value.phases.flatMap((phase) => {
|
|
220
|
+
if (phase.artifact === undefined)
|
|
221
|
+
return [];
|
|
222
|
+
return [compactArtifactProjection(phase.artifact, validated.value.change, phase.phase, getReadinessFromStatuses(validated.value.change, phase.phase, snapshot.value.phases))];
|
|
223
|
+
}),
|
|
224
|
+
fullRetrieval: {
|
|
225
|
+
tool: 'vgxness_sdd_list_artifacts',
|
|
226
|
+
payloadMode: 'verbose',
|
|
227
|
+
input: { project: validated.value.project, change: validated.value.change },
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
}
|
|
170
231
|
const listed = this.memory.listArtifactsByTopicPrefix(validated.value.project, `sdd/${validated.value.change}/`, this.context);
|
|
171
232
|
if (!listed.ok)
|
|
172
233
|
return listed;
|
|
@@ -174,22 +235,7 @@ export class SddWorkflowService {
|
|
|
174
235
|
const artifacts = sddPhases
|
|
175
236
|
.map((phase) => artifactsByTopicKey.get(sddTopicKey(validated.value.change, phase)))
|
|
176
237
|
.filter((artifact) => artifact !== undefined);
|
|
177
|
-
|
|
178
|
-
return ok({ project: validated.value.project, change: validated.value.change, artifacts });
|
|
179
|
-
const statuses = this.getPhaseStatuses(validated.value.project, validated.value.change);
|
|
180
|
-
return ok({
|
|
181
|
-
project: validated.value.project,
|
|
182
|
-
change: validated.value.change,
|
|
183
|
-
artifacts: artifacts.map((artifact) => {
|
|
184
|
-
const phase = isSddPhase(artifact.phase) ? artifact.phase : phaseFromTopicKey(validated.value.change, artifact.topicKey);
|
|
185
|
-
return compactArtifactProjection(artifact, validated.value.change, phase, getReadinessFromStatuses(validated.value.change, phase, statuses));
|
|
186
|
-
}),
|
|
187
|
-
fullRetrieval: {
|
|
188
|
-
tool: 'vgxness_sdd_list_artifacts',
|
|
189
|
-
payloadMode: 'verbose',
|
|
190
|
-
input: { project: validated.value.project, change: validated.value.change },
|
|
191
|
-
},
|
|
192
|
-
});
|
|
238
|
+
return ok({ project: validated.value.project, change: validated.value.change, artifacts });
|
|
193
239
|
}
|
|
194
240
|
validatePhaseInput(input) {
|
|
195
241
|
const validated = validateProjectAndChange(input.project, input.change);
|
|
@@ -200,31 +246,38 @@ export class SddWorkflowService {
|
|
|
200
246
|
return { ok: true, value: { ...validated.value, phase: input.phase } };
|
|
201
247
|
}
|
|
202
248
|
getPhaseStatuses(project, change) {
|
|
203
|
-
|
|
249
|
+
const snapshot = this.loadPhaseSnapshot(project, change);
|
|
250
|
+
if (!snapshot.ok)
|
|
251
|
+
return snapshot;
|
|
252
|
+
return ok(snapshot.value.phases.map(({ artifact, acceptance, createdAt, updatedAt, warnings, ...status }) => status));
|
|
253
|
+
}
|
|
254
|
+
loadPhaseSnapshot(project, change) {
|
|
255
|
+
const listed = this.memory.listArtifactsByTopicPrefixNoTrace(project, `sdd/${change}/`);
|
|
256
|
+
if (!listed.ok)
|
|
257
|
+
return listed;
|
|
258
|
+
const artifactsByTopicKey = new Map(listed.value.map((artifact) => [artifact.topicKey, artifact]));
|
|
259
|
+
const phases = sddPhases.map((phase) => {
|
|
204
260
|
const topicKey = sddTopicKey(change, phase);
|
|
205
|
-
const artifact =
|
|
206
|
-
if (
|
|
207
|
-
return { phase, topicKey, present: false, state: 'missing', accepted: false, legacy: false };
|
|
208
|
-
|
|
261
|
+
const artifact = artifactsByTopicKey.get(topicKey);
|
|
262
|
+
if (artifact === undefined)
|
|
263
|
+
return { phase, topicKey, present: false, state: 'missing', accepted: false, legacy: false, warnings: [] };
|
|
264
|
+
const envelope = normalizeSddArtifact(artifact);
|
|
265
|
+
return {
|
|
266
|
+
phase,
|
|
267
|
+
topicKey,
|
|
268
|
+
present: true,
|
|
269
|
+
state: envelope.metadata.status,
|
|
270
|
+
accepted: envelope.metadata.status === 'accepted',
|
|
271
|
+
legacy: envelope.warnings.includes('legacy-artifact-defaulted-to-draft'),
|
|
272
|
+
artifactId: artifact.id,
|
|
273
|
+
artifact,
|
|
274
|
+
...(envelope.metadata.acceptance === undefined ? {} : { acceptance: envelope.metadata.acceptance }),
|
|
275
|
+
createdAt: artifact.createdAt,
|
|
276
|
+
updatedAt: artifact.updatedAt,
|
|
277
|
+
warnings: envelope.warnings,
|
|
278
|
+
};
|
|
209
279
|
});
|
|
210
|
-
|
|
211
|
-
artifactSummaryForPhase(project, change, phase, topicKey) {
|
|
212
|
-
const artifact = this.memory.getArtifact(project, topicKey, this.context);
|
|
213
|
-
if (!artifact.ok)
|
|
214
|
-
return undefined;
|
|
215
|
-
const envelope = normalizeSddArtifact(artifact.value);
|
|
216
|
-
return {
|
|
217
|
-
phase,
|
|
218
|
-
topicKey,
|
|
219
|
-
present: true,
|
|
220
|
-
accepted: envelope.metadata.status === 'accepted',
|
|
221
|
-
legacy: envelope.warnings.includes('legacy-artifact-defaulted-to-draft'),
|
|
222
|
-
state: envelope.metadata.status,
|
|
223
|
-
artifactId: artifact.value.id,
|
|
224
|
-
...(envelope.metadata.acceptance === undefined ? {} : { acceptance: envelope.metadata.acceptance }),
|
|
225
|
-
createdAt: artifact.value.createdAt,
|
|
226
|
-
updatedAt: artifact.value.updatedAt,
|
|
227
|
-
};
|
|
280
|
+
return ok({ project, change, phases });
|
|
228
281
|
}
|
|
229
282
|
}
|
|
230
283
|
export function nextDecisionFromStatuses(change, phases) {
|
|
@@ -336,18 +389,6 @@ function getReadinessFromStatuses(change, phase, phases) {
|
|
|
336
389
|
blockedPrerequisites,
|
|
337
390
|
};
|
|
338
391
|
}
|
|
339
|
-
function phaseStatusFromArtifact(phase, topicKey, artifact) {
|
|
340
|
-
const envelope = normalizeSddArtifact(artifact);
|
|
341
|
-
return {
|
|
342
|
-
phase,
|
|
343
|
-
topicKey,
|
|
344
|
-
present: true,
|
|
345
|
-
state: envelope.metadata.status,
|
|
346
|
-
accepted: envelope.metadata.status === 'accepted',
|
|
347
|
-
legacy: envelope.warnings.includes('legacy-artifact-defaulted-to-draft'),
|
|
348
|
-
artifactId: artifact.id,
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
392
|
function compactArtifactProjection(artifact, change, phase, readiness) {
|
|
352
393
|
const envelope = normalizeSddArtifact(artifact);
|
|
353
394
|
const summary = summarizePayloadContent(artifact.content);
|
|
@@ -370,6 +411,35 @@ function compactArtifactProjection(artifact, change, phase, readiness) {
|
|
|
370
411
|
updatedAt: artifact.updatedAt,
|
|
371
412
|
};
|
|
372
413
|
}
|
|
414
|
+
function cockpitArtifactSummaryFromSnapshotItem(item) {
|
|
415
|
+
if (item.artifact === undefined || item.artifactId === undefined || item.createdAt === undefined || item.updatedAt === undefined)
|
|
416
|
+
return undefined;
|
|
417
|
+
return {
|
|
418
|
+
phase: item.phase,
|
|
419
|
+
topicKey: item.topicKey,
|
|
420
|
+
present: true,
|
|
421
|
+
accepted: item.accepted === true,
|
|
422
|
+
legacy: item.legacy === true,
|
|
423
|
+
state: item.state ?? 'draft',
|
|
424
|
+
artifactId: item.artifactId,
|
|
425
|
+
...(item.acceptance === undefined ? {} : { acceptance: item.acceptance }),
|
|
426
|
+
createdAt: item.createdAt,
|
|
427
|
+
updatedAt: item.updatedAt,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function compactGovernanceArtifact(artifact) {
|
|
431
|
+
return {
|
|
432
|
+
id: artifact.id,
|
|
433
|
+
project: artifact.project,
|
|
434
|
+
topicKey: artifact.topicKey,
|
|
435
|
+
phase: artifact.phase,
|
|
436
|
+
observationId: artifact.observationId,
|
|
437
|
+
createdAt: artifact.createdAt,
|
|
438
|
+
updatedAt: artifact.updatedAt,
|
|
439
|
+
contentLength: artifact.content.length,
|
|
440
|
+
contentOmitted: true,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
373
443
|
function cockpitBlockersForPhase(status, readiness) {
|
|
374
444
|
const blockers = [];
|
|
375
445
|
if (!status.present)
|
|
@@ -3,24 +3,29 @@ import { noWriteActionSafety } from './provider-setup-adapter.js';
|
|
|
3
3
|
export const claudeSetupAdapter = {
|
|
4
4
|
id: 'claude',
|
|
5
5
|
displayName: 'Claude',
|
|
6
|
-
supportLevel: '
|
|
7
|
-
capabilities: ['mcp-preview', 'manual-guidance'],
|
|
8
|
-
targets: [
|
|
6
|
+
supportLevel: 'supported-secondary',
|
|
7
|
+
capabilities: ['mcp-preview', 'mcp-install-plan', 'cli-mcp-plan', 'agent-preview', 'doctor', 'manual-guidance'],
|
|
8
|
+
targets: [
|
|
9
|
+
{ kind: 'project-config', label: 'Project .mcp.json', path: '.mcp.json', writableBySetup: false },
|
|
10
|
+
{ kind: 'project-config', label: 'Project Claude agents', path: '.claude/agents/*.md', writableBySetup: false },
|
|
11
|
+
{ kind: 'project-config', label: 'Project Claude memory', path: 'CLAUDE.md', writableBySetup: false },
|
|
12
|
+
{ kind: 'user-config', label: 'User Claude agents', path: '~/.claude/agents/*.md', writableBySetup: false },
|
|
13
|
+
],
|
|
9
14
|
getStatus(context) {
|
|
10
15
|
return {
|
|
11
16
|
providerId: 'claude',
|
|
12
|
-
status: '
|
|
13
|
-
summary: 'Claude
|
|
17
|
+
status: 'supported-secondary',
|
|
18
|
+
summary: 'Claude is supported as a secondary, non-default provider for CLI MCP registration planning/apply, project .mcp.json compatibility, and project/user agent planning; explicit guarded apply is required for writes or CLI execution.',
|
|
14
19
|
evidence: context.databasePath !== undefined
|
|
15
20
|
? ['Claude MCP preview can be generated from the selected database path.']
|
|
16
21
|
: ['Claude MCP preview uses a placeholder until a database path is selected.'],
|
|
17
|
-
guidance: ['
|
|
22
|
+
guidance: ['Use `vgxness mcp install claude --scope local|project|user --yes --run-id <id>` for explicit guarded apply. VGXNESS may write project-root CLAUDE.md only through confirmed, preflighted Claude project install; it never manually writes ~/.claude.json or .claude/CLAUDE.md. Read-only setup/status surfaces do not execute Claude Code.'],
|
|
18
23
|
actions: [
|
|
19
24
|
{
|
|
20
25
|
id: 'claude-manual-guidance',
|
|
21
|
-
label: 'Review Claude
|
|
26
|
+
label: 'Review guarded Claude install guidance',
|
|
22
27
|
kind: 'manual-guidance',
|
|
23
|
-
description: '
|
|
28
|
+
description: 'Supported secondary guidance only in setup status; writes are available only through the guarded mcp install claude flow with run preflight metadata.',
|
|
24
29
|
safety: noWriteActionSafety,
|
|
25
30
|
},
|
|
26
31
|
],
|
package/dist/setup/setup-plan.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { join, resolve } from 'node:path';
|
|
2
|
+
import { planClaudeCodeMcpInstall } from '../mcp/client-install-claude-code-contract.js';
|
|
2
3
|
import { planOpenCodeMcpInstall } from '../mcp/client-install-opencode-contract.js';
|
|
3
4
|
import { resolveMemoryDatabasePath } from '../memory/storage-paths.js';
|
|
4
5
|
import { vgxnessSetupDefaults } from './setup-defaults.js';
|
|
@@ -45,8 +46,66 @@ export function createSetupPlan(input) {
|
|
|
45
46
|
},
|
|
46
47
|
};
|
|
47
48
|
}
|
|
49
|
+
if (provider === 'claude') {
|
|
50
|
+
const claude = planClaudeCodeMcpInstall({
|
|
51
|
+
cwd: input.workspaceRoot,
|
|
52
|
+
databasePath: database.value.path,
|
|
53
|
+
databasePathSource: database.value.planSource,
|
|
54
|
+
...(input.overwriteVgxness === undefined ? {} : { overwriteVgxness: input.overwriteVgxness }),
|
|
55
|
+
});
|
|
56
|
+
const targetPath = claude.targetPath;
|
|
57
|
+
if (claude.status === 'refused') {
|
|
58
|
+
return {
|
|
59
|
+
ok: true,
|
|
60
|
+
value: {
|
|
61
|
+
...common,
|
|
62
|
+
provider: 'claude',
|
|
63
|
+
db: { mode: database.value.mode, path: database.value.path, source: database.value.source },
|
|
64
|
+
status: 'conflict',
|
|
65
|
+
actions: [],
|
|
66
|
+
conflicts: [
|
|
67
|
+
{
|
|
68
|
+
id: claude.reason,
|
|
69
|
+
severity: 'blocking',
|
|
70
|
+
message: claude.message,
|
|
71
|
+
targetPath,
|
|
72
|
+
recovery: claude.reason === 'malformed_claude_project_memory' || claude.reason === 'conflicting_claude_project_memory' ? 'Manually reconcile the VGXNESS markers in project-root CLAUDE.md, then rerun `vgxness setup plan --provider claude`.' : 'Inspect the Claude project config, resolve the conflict, then rerun `vgxness setup plan --provider claude`.',
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
backupsPlanned: [],
|
|
76
|
+
nextCommands: ['vgxness mcp install claude --plan --scope project', 'Resolve the reported Claude project conflict before guarded apply.'],
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
ok: true,
|
|
82
|
+
value: {
|
|
83
|
+
...common,
|
|
84
|
+
provider: 'claude',
|
|
85
|
+
db: { mode: database.value.mode, path: database.value.path, source: database.value.source },
|
|
86
|
+
status: 'ready',
|
|
87
|
+
actions: [
|
|
88
|
+
{
|
|
89
|
+
id: 'claude-guarded-install',
|
|
90
|
+
description: `Review supported secondary Claude plan for CLI MCP registration, .mcp.json compatibility, ${claude.agentNames.length} .claude/agents/*.md file(s), and guarded project-root CLAUDE.md managed block; apply only with vgxness mcp install claude --scope project --yes --run-id <id>.`,
|
|
91
|
+
mutating: false,
|
|
92
|
+
targetPath,
|
|
93
|
+
backupRequired: claude.backupRequired,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
conflicts: [],
|
|
97
|
+
backupsPlanned: claude.targets
|
|
98
|
+
.filter((target) => target.kind !== 'cli-mcp-registration' && ((target.kind === 'project-memory' && target.backupRequired) || target.action === 'merge' || target.action === 'update-vgxness'))
|
|
99
|
+
.map((target) => ({
|
|
100
|
+
targetPath: target.path,
|
|
101
|
+
reason: target.kind === 'project-memory' ? 'A future guarded Claude apply would create a managed VGXNESS backup before appending or updating the project-root CLAUDE.md managed block.' : 'A future guarded Claude apply would create a managed VGXNESS backup before merging or updating Claude project configuration.',
|
|
102
|
+
})),
|
|
103
|
+
nextCommands: ['vgxness mcp install claude --plan --scope project', 'vgxness mcp install claude --scope project --yes --run-id <id>', 'OpenCode remains the default/primary provider.'],
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
48
107
|
if (provider !== 'opencode')
|
|
49
|
-
return { ok: false, error: { code: 'validation_failed', message: 'Provider must be opencode or none for the setup plan.' } };
|
|
108
|
+
return { ok: false, error: { code: 'validation_failed', message: 'Provider must be opencode, claude, or none for the setup plan.' } };
|
|
50
109
|
const opencode = planOpenCodeMcpInstall({
|
|
51
110
|
cwd: input.workspaceRoot,
|
|
52
111
|
databasePath: database.value.path,
|
package/docs/architecture.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# vgxness Architecture
|
|
2
2
|
|
|
3
|
+
> **Scope:** this document describes the v1.5.1 architecture as it is actually built. It is the source of truth for how the product works today. Planned work that is not yet shipped lives in [Roadmap](./roadmap.md); historical planning context that no longer reflects reality has been retired. Where this doc disagrees with code, code wins — file a doc-sync task against the relevant module.
|
|
4
|
+
|
|
3
5
|
`vgxness` is a local-first, provider-agnostic, Gentle-AI-like harness for agentic development. Its core architecture separates the product domain from provider-specific tooling so agents, skills, memory, SDD workflows, runs, and traces can work across OpenCode, Claude Code, and future adapters such as Pi.
|
|
4
6
|
|
|
5
|
-
The architectural goal is not only to install better prompts or agent configs. `vgxness`
|
|
7
|
+
The architectural goal is not only to install better prompts or agent configs. `vgxness` combines an ecosystem configurator with a runtime control plane and a native code runtime: configured agents may execute the work, the control plane keeps explicit state for phase readiness, artifacts, runs, approvals, checkpoints, and audit evidence, and the code runtime performs bounded agentic work in the local workspace with explicit permission decisions.
|
|
6
8
|
|
|
7
|
-
The user-facing shape is deliberately
|
|
9
|
+
The user-facing shape is deliberately four-surface: **MCP for agents**, **CLI for scriptable operator control**, **TUI for guided setup and visual local operations**, and **code runtime (`vgxness code`)** for bounded agentic work in the workspace. All four call the same core services instead of reimplementing workflow rules.
|
|
8
10
|
|
|
9
11
|
## Architecture decision summary
|
|
10
12
|
|
|
@@ -14,9 +16,9 @@ The user-facing shape is deliberately three-surface: **MCP for agents**, **CLI f
|
|
|
14
16
|
| Reference model | Similar product surface to Gentle-AI/`gentle-pi`: agent setup, SDD orchestration, memory wiring, skills, profiles, permissions, verification. |
|
|
15
17
|
| Differentiator | Verifiable runtime state engine: SDD phases, artifacts, runs, approvals, checkpoints, and audit trails are queryable product state, not prompt-only convention. |
|
|
16
18
|
| Core workflow | SDD-first canonical state: explore → proposal → spec → design → tasks → apply-progress → verify → archive. |
|
|
17
|
-
| Interfaces | MCP server for AI tools, CLI for automation/power users, TUI for guided install/status/profile/SDD workflows. |
|
|
19
|
+
| Interfaces | MCP server for AI tools, CLI for automation/power users, TUI for guided install/status/profile/SDD workflows, and `vgxness code` runtime for bounded workspace work. |
|
|
18
20
|
| Installation UX | Step-based guided setup with doctor checks, dry-run support, and no manual provider JSON editing on the happy path. |
|
|
19
|
-
| Provider strategy | Provider-agnostic domain model with OpenCode and Claude Code
|
|
21
|
+
| Provider strategy | Provider-agnostic domain model with OpenCode as the primary/default supported provider; Claude Code is supported secondary for CLI MCP registration, project `.mcp.json` compatibility, guarded project-root `CLAUDE.md` managed memory, and project/user agent planning. Claude scopes are `local`, `project`, and `user` (`personal`/`global` map to `user`). Read-only status/doctor/change-plan never execute Claude Code or inspect private Claude config, and VGXNESS never manually mutates `~/.claude.json` or `.claude/CLAUDE.md`. Pi/`gentle-pi` compatibility is a future adapter/reference target. The code runtime speaks to any OpenAI-compatible endpoint through `src/code/providers/openai-compatible-provider-adapter.ts`. |
|
|
20
22
|
| Memory | Project memory plus personal/global memory, backed locally. |
|
|
21
23
|
| Agents | Agents/subagents are registered in a neutral schema, then rendered into provider-specific config. |
|
|
22
24
|
| Skills | Skills are first-class, versioned, attachable to agents/workflows/adapters, and improved through reviewable proposals. |
|
|
@@ -98,7 +100,7 @@ Gentle-AI/`gentle-pi` are strong references for the configurator and agent-behav
|
|
|
98
100
|
| Project | Repo-specific memory, SDD artifacts, run history, adapter config, project agents/skills. | `.vgx/` or project-local SQLite store. |
|
|
99
101
|
| Personal/global | User preferences, reusable skills, cross-project patterns, global agents. | User-level config/data directory. |
|
|
100
102
|
|
|
101
|
-
The exact path format is still open, but the architectural rule is fixed: **project data and personal data must not be collapsed into one scope**.
|
|
103
|
+
The exact path format is still open, but the architectural rule is fixed: **project data and personal data must not be collapsed into one scope**. See [Storage](./storage.md) for schema, migration layout, and lifecycle.
|
|
102
104
|
|
|
103
105
|
## SDD workflow engine
|
|
104
106
|
|
|
@@ -116,18 +118,20 @@ Current phase artifacts use one canonical topic key each:
|
|
|
116
118
|
sdd/{change}/{phase}
|
|
117
119
|
```
|
|
118
120
|
|
|
119
|
-
Readiness is
|
|
121
|
+
Readiness is a combination of prerequisite phase artifacts, explicit human acceptance, and aggregate blockers — artifact presence alone is not enough:
|
|
120
122
|
|
|
121
|
-
| Phase | Required
|
|
122
|
-
|
|
123
|
-
| `explore` | none |
|
|
124
|
-
| `proposal` | `explore` |
|
|
125
|
-
| `spec` | `proposal` |
|
|
126
|
-
| `design` | `proposal`, `spec` |
|
|
127
|
-
| `tasks` | `proposal`, `spec`, `design` |
|
|
128
|
-
| `apply-progress` | `tasks` |
|
|
129
|
-
| `verify` | `apply-progress` |
|
|
130
|
-
| `archive` | `verify` |
|
|
123
|
+
| Phase | Required prerequisites | Acceptance required |
|
|
124
|
+
|---|---|---|
|
|
125
|
+
| `explore` | none | no |
|
|
126
|
+
| `proposal` | `explore` | yes (on `explore`) |
|
|
127
|
+
| `spec` | `proposal` | yes (on `proposal`) |
|
|
128
|
+
| `design` | `proposal`, `spec` | yes (on `proposal`, `spec`) |
|
|
129
|
+
| `tasks` | `proposal`, `spec`, `design` | yes (on `proposal`, `spec`, `design`) |
|
|
130
|
+
| `apply-progress` | `tasks` | yes (on `tasks`) |
|
|
131
|
+
| `verify` | `apply-progress` | yes (on `apply-progress`) |
|
|
132
|
+
| `archive` | `verify` | yes (on `verify`) |
|
|
133
|
+
|
|
134
|
+
`SddWorkflowService.getReady(...)` returns a structured `SddReadiness` with `blockedPrerequisites`, while `getCockpit(...)` aggregates `SddCockpitBlocker`s of kind `missing-topic-key`, `unaccepted-phase`, `legacy-artifact`, or `readiness`. Acceptance is recorded only by a human actor (`acceptedBy.type === 'human'`); the runtime rejects agent or anonymous acceptance.
|
|
131
135
|
|
|
132
136
|
Current service API:
|
|
133
137
|
|
|
@@ -409,7 +413,7 @@ NO silent mutation of active skills. Eso es una línea roja.
|
|
|
409
413
|
|
|
410
414
|
## Provider adapter contract
|
|
411
415
|
|
|
412
|
-
Adapters render registry definitions into provider artifacts without changing the registry model.
|
|
416
|
+
Adapters render registry definitions into provider artifacts without changing the registry model. The full adapter contract, render API, and how to add a new provider live in [Providers](./providers.md); this section is a high-level map.
|
|
413
417
|
|
|
414
418
|
Current contract:
|
|
415
419
|
|
|
@@ -435,7 +439,7 @@ vgxness agents render --provider opencode --project vgxness --name apply-agent
|
|
|
435
439
|
Rendering is intentionally read-only: it returns generated content in the CLI response. It does **not** write `.opencode/`, `.claude/`, or any user/global provider config.
|
|
436
440
|
OpenCode agent keys are sanitized deterministically from registry names, and rendering rejects key collisions instead of overwriting generated config.
|
|
437
441
|
|
|
438
|
-
Claude Code
|
|
442
|
+
Claude Code install planning/apply is supported through the guarded MCP install path. Rendering remains read-only; confirmed project compatibility writes are limited to `.mcp.json`, `.claude/agents/*.md`, and the project-root `CLAUDE.md` managed block with run preflight metadata. Local/user MCP registration is represented through Claude CLI argv after confirmation/preflight, not private config mutation.
|
|
439
443
|
|
|
440
444
|
### OpenCode injection preview
|
|
441
445
|
|
|
@@ -462,15 +466,15 @@ The checked-in OpenCode default config and `seeds/agents/agent-seed-v1.json` def
|
|
|
462
466
|
|
|
463
467
|
## Run lifecycle
|
|
464
468
|
|
|
465
|
-
A run is the core unit of execution.
|
|
466
|
-
|
|
467
|
-
Current terminal lifecycle rules:
|
|
469
|
+
A run is the core unit of execution. Run records are stored locally in SQLite and stay provider-neutral. The lifecycle is complete in v1.5.1:
|
|
468
470
|
|
|
469
471
|
```text
|
|
470
|
-
created →
|
|
472
|
+
created → planned → running → needs-human
|
|
473
|
+
↓
|
|
474
|
+
completed | failed | blocked | cancelled
|
|
471
475
|
```
|
|
472
476
|
|
|
473
|
-
The
|
|
477
|
+
All eight statuses are first-class on `RunStatus` (`src/runs/schema.ts:1`). The control plane exposes lifecycle operations through `vgxness_run_start`, `vgxness_run_checkpoint`, `vgxness_run_finalize`, and the read-only `vgxness_run_resume_inspect` and `vgxness_run_resume_gate` tools. Finalize is safe by default: terminal runs cannot be finalized again, and the final `outcome` must match the terminal `status` (`success`/`partial`/`failure`/`blocked`/`cancelled`).
|
|
474
478
|
|
|
475
479
|
Current run fields:
|
|
476
480
|
|
|
@@ -556,14 +560,13 @@ runService.appendCheckpoint({
|
|
|
556
560
|
});
|
|
557
561
|
```
|
|
558
562
|
|
|
559
|
-
|
|
563
|
+
Open follow-up for run execution lives in [Roadmap](./roadmap.md). The remaining work is:
|
|
564
|
+
|
|
565
|
+
- real provider/tool invocation behind sandboxed executors (the lifecycle and policy recording are stable; the actual executor is still test-only)
|
|
566
|
+
- CLI/MCP orchestration for `resume-after-approval` once a safe executor exists outside tests
|
|
567
|
+
- richer verification evidence summaries that link runs, tasks, and verifications
|
|
560
568
|
|
|
561
|
-
|
|
562
|
-
- real provider/tool invocation behind sandboxed executors
|
|
563
|
-
- CLI or adapter orchestration for resume-after-approval once a safe executor exists outside tests
|
|
564
|
-
- operator UX for retry admission and retry execution, with clear separation between reservation and actual execution
|
|
565
|
-
- sandbox/worktree execution strategies after decision recording is stable
|
|
566
|
-
- richer verification evidence summaries
|
|
569
|
+
What is already shipped: 8-state lifecycle, approval records, reserved attempts, retry policy evaluation with `never`/`after-abandoned`/`after-failure`/`after-failure-or-abandoned`, run insights with debug summary, run snapshot export (`RunSnapshotPackageV1`), and a `runs retry-check --approval <id> [--policy <json>]` operator command.
|
|
567
570
|
|
|
568
571
|
## Trace model
|
|
569
572
|
|
|
@@ -584,9 +587,7 @@ Minimum trace events:
|
|
|
584
587
|
|
|
585
588
|
## Permission model
|
|
586
589
|
|
|
587
|
-
Permissions
|
|
588
|
-
|
|
589
|
-
Minimum categories:
|
|
590
|
+
Permissions are defined in `vgxness` first, then mapped to adapters through the neutral `permissions` field on agents and subagents. The full safety model, including approval flow, redactors, and runtime gates, is in [Safety model](./safety.md). Categories:
|
|
590
591
|
|
|
591
592
|
| Category | Examples |
|
|
592
593
|
|---|---|
|
|
@@ -600,15 +601,9 @@ Minimum categories:
|
|
|
600
601
|
| `provider-tool` | opaque adapter/provider tool calls |
|
|
601
602
|
| `secrets` | environment variables, credentials, tokens |
|
|
602
603
|
|
|
603
|
-
Operations
|
|
604
|
-
|
|
605
|
-
- `allow`
|
|
606
|
-
- `ask`
|
|
607
|
-
- `deny`
|
|
608
|
-
|
|
609
|
-
Default stance for destructive or external operations: **ask or deny**, never implicit allow.
|
|
604
|
+
Operations resolve to `allow`, `ask`, or `deny`. Destructive or external operations default to **ask or deny**, never implicit allow.
|
|
610
605
|
|
|
611
|
-
Current foundation API: `evaluatePermission(request)` in `src/permissions
|
|
606
|
+
Current foundation API: `evaluatePermission(request)` in `src/permissions/policy-evaluator.ts` returns `allow`, `ask`, or `deny` with a reason. Defaults are intentionally conservative:
|
|
612
607
|
|
|
613
608
|
- workspace reads are allowed only when the target path stays inside `workspaceRoot`
|
|
614
609
|
- edits, shell, git, network, memory writes/searches, and provider-specific tools ask by default
|
|
@@ -616,84 +611,31 @@ Current foundation API: `evaluatePermission(request)` in `src/permissions/` retu
|
|
|
616
611
|
- destructive, external, privileged, or ambiguous requests require ask even when an agent override would otherwise allow the category
|
|
617
612
|
- workspace boundary denials cannot be relaxed by agent/subagent overrides
|
|
618
613
|
|
|
619
|
-
|
|
614
|
+
The code runtime layers a second, finer-grained decision on top of the policy evaluator: per-tool definitions declare whether a tool is `read`, `confirm`, or `restricted`; `PolicyApprovalBroker`, `StdioApprovalBroker`, and `ConservativePermissionGateway` (in `src/code/runtime/approval-coordinator.ts`) wire approval prompts to the runtime event stream. See [Code runtime](./code-runtime.md) for the workspace-side contract.
|
|
620
615
|
|
|
621
|
-
##
|
|
616
|
+
## Interface surface (current)
|
|
622
617
|
|
|
623
|
-
|
|
618
|
+
CLI surface groups are documented in [CLI reference](./cli.md). The plural form is canonical — singular shortcuts are not added.
|
|
624
619
|
|
|
625
|
-
|
|
626
|
-
vgxness init
|
|
627
|
-
vgxness memory search|get|save|update
|
|
628
|
-
vgxness agents list|register|get|resolve|render
|
|
629
|
-
vgxness skills list|register|propose|approve-proposal|apply-proposal
|
|
630
|
-
vgxness sdd status|next|ready|save-artifact|accept-artifact|list-artifacts
|
|
631
|
-
vgxness runs list|get|timeline|debug|resume-inspect|resume-gate
|
|
632
|
-
vgxness mcp doctor|install
|
|
633
|
-
vgxness opencode preview
|
|
634
|
-
```
|
|
635
|
-
|
|
636
|
-
Representative MCP tools mirror the same core services for agent use. For the current exact tool names, use `SUPPORTED_VGX_MCP_TOOL_NAMES`:
|
|
637
|
-
|
|
638
|
-
```text
|
|
639
|
-
vgxness_sdd_status
|
|
640
|
-
vgxness_sdd_next
|
|
641
|
-
vgxness_sdd_ready
|
|
642
|
-
vgxness_sdd_save_artifact
|
|
643
|
-
vgxness_sdd_get_artifact
|
|
644
|
-
vgxness_sdd_list_artifacts
|
|
645
|
-
vgxness_memory_search
|
|
646
|
-
vgxness_memory_get
|
|
647
|
-
vgxness_memory_save
|
|
648
|
-
vgxness_memory_update
|
|
649
|
-
vgxness_run_start
|
|
650
|
-
vgxness_run_list
|
|
651
|
-
vgxness_run_get
|
|
652
|
-
vgxness_run_preflight
|
|
653
|
-
vgxness_run_checkpoint
|
|
654
|
-
vgxness_run_finalize
|
|
655
|
-
vgxness_agent_resolve
|
|
656
|
-
vgxness_agent_activate
|
|
657
|
-
vgxness_manager_profile_get
|
|
658
|
-
vgxness_manager_profile_set
|
|
659
|
-
vgxness_skill_payload
|
|
660
|
-
vgxness_opencode_manager_payload
|
|
661
|
-
```
|
|
662
|
-
|
|
663
|
-
The CLI and TUI are human/operator control surfaces. MCP is the agent-facing control surface. Provider integrations are the execution/configuration plane.
|
|
620
|
+
MCP tools mirror the same core services for agent use. The full, current list of 38 tools is in [MCP tools](./mcp.md) and `SUPPORTED_VGX_MCP_TOOL_NAMES` (`src/mcp/schema.ts`); treat that array as the source of truth. The CLI and TUI are human/operator control surfaces. MCP is the agent-facing control surface. Provider integrations and the code runtime sit on the execution plane.
|
|
664
621
|
|
|
665
622
|
## Evaluation strategy
|
|
666
623
|
|
|
667
|
-
Minimum eval/test targets:
|
|
668
|
-
|
|
669
|
-
- Agent resolution selects the expected agent.
|
|
670
|
-
- Skill resolution injects the expected skill.
|
|
671
|
-
- Adapter rendering produces valid provider config.
|
|
672
|
-
- Permission rules block unsafe operations.
|
|
673
|
-
- SDD artifact chains remain complete.
|
|
674
|
-
- Memory upserts preserve revisions.
|
|
675
|
-
- Run resume restores expected state.
|
|
676
|
-
- Skill improvement proposals are versioned and require approval.
|
|
677
|
-
- MCP tools call the same core services as CLI/TUI and return actionable blocked states.
|
|
678
|
-
-
|
|
624
|
+
Minimum eval/test targets (asserted through `node:test` files under `test/`, totaling 95 files as of v1.5.1):
|
|
625
|
+
|
|
626
|
+
- Agent resolution selects the expected agent (`test/agents/agent-resolver.test.ts`).
|
|
627
|
+
- Skill resolution injects the expected skill (`test/skills/`).
|
|
628
|
+
- Adapter rendering produces valid provider config (`test/agents/provider-renderer.test.ts`).
|
|
629
|
+
- Permission rules block unsafe operations (`test/permissions/policy-evaluator.test.ts`).
|
|
630
|
+
- SDD artifact chains remain complete and human acceptance is enforced (`test/sdd/sdd-workflow-service.test.ts`).
|
|
631
|
+
- Memory upserts preserve revisions (`test/memory/`).
|
|
632
|
+
- Run resume restores expected run state and respects retry policies (`test/runs/`).
|
|
633
|
+
- Skill improvement proposals are versioned and require approval (`test/skills/`).
|
|
634
|
+
- MCP tools call the same core services as CLI/TUI and return actionable blocked states (`test/mcp/`).
|
|
635
|
+
- Code runtime behavior across inspect, plan, craft-preview, and craft modes (`test/code/`).
|
|
636
|
+
- OpenCode config rendering and drift detection (`test/mcp/opencode-agent-config-drift.test.ts`).
|
|
679
637
|
- Installation dry-run reports the exact provider config changes before mutation.
|
|
680
638
|
|
|
681
|
-
##
|
|
682
|
-
|
|
683
|
-
The next SDD change should be `harness-runtime-foundation`.
|
|
684
|
-
|
|
685
|
-
Scope:
|
|
686
|
-
|
|
687
|
-
- define schemas for agents, skills, runs, traces, permissions, and adapters
|
|
688
|
-
- add local persistence for these entities where missing
|
|
689
|
-
- add adapter validation/render skeleton
|
|
690
|
-
- add CLI/MCP/TUI interface boundaries for validation, inspection, and guided setup
|
|
691
|
-
- add tests for schema validation, permission decisions, and adapter rendering
|
|
692
|
-
|
|
693
|
-
Out of scope:
|
|
639
|
+
## Future work
|
|
694
640
|
|
|
695
|
-
|
|
696
|
-
- team collaboration
|
|
697
|
-
- web console
|
|
698
|
-
- distributed workers
|
|
699
|
-
- fully autonomous skill mutation
|
|
641
|
+
Planned work that is not yet shipped lives in [Roadmap](./roadmap.md). This document stays focused on the architecture as built; the roadmap is the place to track what is next and what is explicitly deferred.
|