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.
Files changed (56) hide show
  1. package/README.md +23 -2
  2. package/dist/agents/agent-seed-service.js +10 -0
  3. package/dist/agents/canonical-agent-manifest.js +177 -0
  4. package/dist/agents/canonical-agent-projection.js +164 -0
  5. package/dist/agents/renderers/claude-renderer.js +30 -52
  6. package/dist/cli/bun-bin.js +6 -0
  7. package/dist/cli/cli-flags.js +1 -1
  8. package/dist/cli/cli-help.js +7 -4
  9. package/dist/cli/commands/agent-skill-dispatcher.js +6 -5
  10. package/dist/cli/commands/interactive-entrypoint-dispatcher.js +2 -2
  11. package/dist/cli/commands/mcp-dispatcher.js +75 -3
  12. package/dist/cli/commands/setup-dispatcher.js +9 -0
  13. package/dist/cli/index.js +1 -1
  14. package/dist/cli/tui/main-menu/main-menu-read-model.js +41 -44
  15. package/dist/cli/tui/main-menu/main-menu-render-shape.js +15 -15
  16. package/dist/cli/tui/opentui/main-menu/screen.js +39 -41
  17. package/dist/cli/tui/opentui/main-menu/smoke.js +1 -1
  18. package/dist/cli/tui/opentui/main-menu/view.js +1 -1
  19. package/dist/cli/tui/setup/setup-tui-read-model.js +15 -12
  20. package/dist/governance/governance-report-builder.js +45 -26
  21. package/dist/mcp/claude-code-agent-config.js +95 -0
  22. package/dist/mcp/claude-code-cli.js +71 -0
  23. package/dist/mcp/claude-code-config.js +84 -0
  24. package/dist/mcp/claude-code-project-memory.js +127 -0
  25. package/dist/mcp/claude-code-scope.js +18 -0
  26. package/dist/mcp/client-install-claude-code-contract.js +114 -0
  27. package/dist/mcp/client-install-claude-code.js +136 -0
  28. package/dist/mcp/index.js +8 -0
  29. package/dist/mcp/opencode-default-agent-config.js +7 -113
  30. package/dist/mcp/provider-canonical-agent-manifest.js +39 -0
  31. package/dist/mcp/provider-change-plan.js +109 -1
  32. package/dist/mcp/provider-doctor.js +105 -1
  33. package/dist/mcp/provider-health-types.js +4 -0
  34. package/dist/mcp/provider-status.js +159 -3
  35. package/dist/mcp/schema.js +6 -5
  36. package/dist/mcp/validation.js +1 -1
  37. package/dist/memory/memory-service.js +4 -0
  38. package/dist/sdd/sdd-workflow-service.js +129 -59
  39. package/dist/setup/providers/claude-setup-adapter.js +13 -8
  40. package/dist/setup/setup-plan.js +60 -1
  41. package/docs/architecture.md +55 -113
  42. package/docs/cli.md +90 -2
  43. package/docs/code-runtime.md +218 -0
  44. package/docs/contributing.md +120 -0
  45. package/docs/glossary.md +211 -0
  46. package/docs/mcp.md +144 -0
  47. package/docs/prd.md +23 -26
  48. package/docs/providers.md +150 -0
  49. package/docs/roadmap.md +88 -0
  50. package/docs/safety.md +147 -0
  51. package/docs/storage.md +93 -0
  52. package/package.json +1 -1
  53. package/docs/funcionamiento-del-sistema.md +0 -865
  54. package/docs/harness-gap-analysis.md +0 -243
  55. package/docs/vgxcode.md +0 -87
  56. package/docs/vgxness-code.md +0 -48
@@ -0,0 +1,136 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ import { createManagedProviderConfigBackup } from '../setup/backup-rollback-service.js';
4
+ import { parseClaudeAgentFrontmatter } from './claude-code-agent-config.js';
5
+ import { runClaudeCodeCliCommand } from './claude-code-cli.js';
6
+ import { createClaudeCodeMcpServerConfig, inspectClaudeCodeMcpConfig, mergeClaudeCodeMcpConfig } from './claude-code-config.js';
7
+ import { inspectClaudeProjectMemory, mergeClaudeProjectMemory } from './claude-code-project-memory.js';
8
+ import { expectedClaudeCodeRenderedAgents, planClaudeCodeMcpInstall } from './client-install-claude-code-contract.js';
9
+ export async function installClaudeCodeMcpClient(input) {
10
+ const source = input.databasePathSource ?? 'flag';
11
+ const server = createClaudeCodeMcpServerConfig(input.databasePath, source);
12
+ const plan = planClaudeCodeMcpInstall({ cwd: input.cwd, databasePath: input.databasePath, databasePathSource: source, ...(input.overwriteVgxness !== undefined ? { overwriteVgxness: input.overwriteVgxness } : {}), ...(input.scope !== undefined ? { scope: input.scope } : {}) });
13
+ if (plan.status === 'refused')
14
+ return refusal(plan.reason, plan.message, plan, server, [], []);
15
+ if (!input.confirmed)
16
+ return refusal('confirmation_required', '`mcp install claude` requires explicit --yes before any project config write.', plan, server, [], []);
17
+ if (input.preflight === undefined) {
18
+ return refusal('preflight_failed', 'Claude Code provider config writes require VGXNESS preflight before any project config write.', plan, server, [], []);
19
+ }
20
+ const preflightPaths = unique(plan.targets.flatMap((target) => (isMutatingTarget(target) ? [target.path] : [])));
21
+ if (plan.canonicalClaudeScope !== 'project')
22
+ preflightPaths.unshift(plan.targetPath);
23
+ for (const targetPath of preflightPaths) {
24
+ const preflight = await input.preflight({
25
+ category: 'provider-tool',
26
+ operation: 'write claude project provider config',
27
+ targetPath,
28
+ workspaceRoot: input.cwd,
29
+ providerToolName: 'claude-code',
30
+ ...(input.runId !== undefined ? { runId: input.runId } : {}),
31
+ ...(input.agentId !== undefined ? { agentId: input.agentId } : {}),
32
+ ...(input.phase !== undefined ? { phase: input.phase } : {}),
33
+ });
34
+ if (!preflight.ok)
35
+ return refusal('preflight_failed', preflight.error.message, plan, server, [], []);
36
+ }
37
+ const backups = [];
38
+ const writtenPaths = [];
39
+ if (plan.canonicalClaudeScope !== 'project') {
40
+ if (input.cliRunner === undefined)
41
+ return refusal('preflight_failed', 'Claude CLI MCP registration apply requires an injected runner boundary; read-only plans never execute Claude Code.', plan, server, [], []);
42
+ if (plan.cliCommand === undefined)
43
+ return refusal('preflight_failed', 'Claude CLI MCP registration command is unavailable.', plan, server, [], []);
44
+ const cli = await runClaudeCodeCliCommand(plan.cliCommand, input.cliRunner);
45
+ const cliResult = { exitCode: cli.exitCode, stdout: cli.stdout, stderr: cli.stderr, preview: cli.command.preview };
46
+ if (cli.exitCode !== 0)
47
+ return refusal('preflight_failed', `Claude CLI MCP registration failed with exit code ${cli.exitCode ?? 'unknown'}.`, plan, server, [], [], cliResult);
48
+ return { version: 1, kind: 'mcp-client-install-claude-code', status: 'installed', targetPath: plan.targetPath, writtenPaths: [], backups: [], safety: applySafety(plan), server, warnings: plan.warnings, verificationHints: plan.verificationHints, agentNames: plan.agentNames, overwriteVgxness: plan.overwriteVgxness, cliResult };
49
+ }
50
+ const existingTargets = plan.targets.filter((target) => target.kind !== 'cli-mcp-registration' && (target.action === 'merge' || target.action === 'update-vgxness' || target.action === 'append-managed-block'));
51
+ for (const target of existingTargets) {
52
+ const backup = createBackup(target.path, target.kind === 'project-memory' ? 'project-memory' : 'config');
53
+ if (!backup.ok)
54
+ return refusal('backup_failed', backup.error.message, plan, server, writtenPaths, backups);
55
+ backups.push(toBackupSummary(backup.value));
56
+ }
57
+ const mcpWrite = writeMcpJson(input.cwd, plan, server);
58
+ if (!mcpWrite.ok)
59
+ return refusal('post_write_validation_failed', mcpWrite.error.message, plan, server, writtenPaths, backups);
60
+ writtenPaths.push(mcpWrite.value);
61
+ for (const agent of expectedClaudeCodeRenderedAgents(input.cwd)) {
62
+ const target = plan.targets.find((item) => item.kind === 'agent-file' && item.path === agent.path);
63
+ if (target?.action !== 'create' && target?.action !== 'update-vgxness')
64
+ continue;
65
+ mkdirSync(dirname(agent.path), { recursive: true });
66
+ writeFileSync(agent.path, agent.contents);
67
+ const validation = parseClaudeAgentFrontmatter(readFileSync(agent.path, 'utf8'));
68
+ if (!validation.ok || validation.data.name !== agent.agentName)
69
+ return refusal('post_write_validation_failed', `Claude agent ${agent.agentName} failed post-write validation.`, plan, server, writtenPaths, backups);
70
+ writtenPaths.push(agent.path);
71
+ }
72
+ const projectMemoryWrite = writeProjectMemory(plan);
73
+ if (!projectMemoryWrite.ok)
74
+ return refusal('post_write_validation_failed', projectMemoryWrite.error.message, plan, server, writtenPaths, backups);
75
+ if (projectMemoryWrite.value !== undefined)
76
+ writtenPaths.push(projectMemoryWrite.value);
77
+ return { version: 1, kind: 'mcp-client-install-claude-code', status: 'installed', targetPath: plan.targetPath, writtenPaths, backups, safety: applySafety(plan), server, warnings: plan.warnings, verificationHints: plan.verificationHints, agentNames: plan.agentNames, overwriteVgxness: plan.overwriteVgxness };
78
+ }
79
+ function writeMcpJson(cwd, plan, server) {
80
+ const state = inspectClaudeCodeMcpConfig(cwd);
81
+ if (state.status === 'invalid' || state.status === 'conflicting')
82
+ return { ok: false, error: { code: 'validation_failed', message: state.message } };
83
+ if (plan.targets.find((target) => target.kind === 'mcp-json')?.action === 'create' && existsSync(plan.targetPath))
84
+ return { ok: false, error: { code: 'validation_failed', message: 'Claude .mcp.json appeared after planning; rerun apply to merge safely.' } };
85
+ const merged = mergeClaudeCodeMcpConfig(state.parsed ? state.config : {}, server);
86
+ mkdirSync(dirname(plan.targetPath), { recursive: true });
87
+ writeFileSync(plan.targetPath, `${JSON.stringify(merged, null, 2)}\n`);
88
+ const after = inspectClaudeCodeMcpConfig(cwd);
89
+ return after.status === 'configured' ? { ok: true, value: plan.targetPath } : { ok: false, error: { code: 'validation_failed', message: 'Claude .mcp.json did not validate after write.' } };
90
+ }
91
+ function writeProjectMemory(plan) {
92
+ const target = plan.targets.find((item) => item.kind === 'project-memory');
93
+ if (target === undefined || target.action === 'none')
94
+ return { ok: true, value: undefined };
95
+ if (target.action === 'blocked')
96
+ return { ok: false, error: { code: 'validation_failed', message: target.reason ?? 'Claude project memory is blocked.' } };
97
+ const state = inspectClaudeProjectMemory(dirname(target.path));
98
+ if (state.action !== target.action)
99
+ return { ok: false, error: { code: 'validation_failed', message: 'Claude project memory changed after planning; rerun apply.' } };
100
+ const merged = mergeClaudeProjectMemory(state);
101
+ if (!merged.ok)
102
+ return merged;
103
+ mkdirSync(dirname(target.path), { recursive: true });
104
+ writeFileSync(target.path, merged.value.contents);
105
+ const after = inspectClaudeProjectMemory(dirname(target.path));
106
+ return after.status === 'managed-current' ? { ok: true, value: target.path } : { ok: false, error: { code: 'validation_failed', message: 'Claude project memory did not validate as managed-current after write.' } };
107
+ }
108
+ function createBackup(path, kind) {
109
+ return createManagedProviderConfigBackup({
110
+ targetPath: path,
111
+ provider: 'claude',
112
+ scope: 'project',
113
+ createdByOperation: 'mcp-client-install-claude-code',
114
+ reason: kind === 'project-memory' ? 'pre-project-memory-update-safety' : 'pre-merge-safety',
115
+ description: kind === 'project-memory' ? 'Backup existing Claude Code project memory before appending or updating the VGXNESS managed block.' : 'Backup existing Claude Code project config before merging VGXNESS MCP or agent configuration.',
116
+ });
117
+ }
118
+ function refusal(reason, message, plan, server, writtenPaths, backups, cliResult) {
119
+ return { version: 1, kind: 'mcp-client-install-claude-code', status: 'refused', reason, message, targetPath: plan.targetPath, writtenPaths, backups, safety: { ...plan.safety, operation: 'apply', mutating: false, writesProviderConfig: false }, server, warnings: plan.warnings, verificationHints: plan.verificationHints, agentNames: plan.agentNames, overwriteVgxness: plan.overwriteVgxness, ...(cliResult === undefined ? {} : { cliResult }) };
120
+ }
121
+ function applySafety(plan) {
122
+ return { ...plan.safety, operation: 'apply', mutating: true, writesProviderConfig: true };
123
+ }
124
+ function toBackupSummary(backup) {
125
+ return { kind: 'managed', backupId: backup.backupId, backupPath: backup.backupPath, metadataPath: backup.metadataPath, scope: backup.scope, byteSize: backup.byteSize, contentHash: backup.contentHash };
126
+ }
127
+ function unique(values) {
128
+ return [...new Set(values)];
129
+ }
130
+ function isMutatingTarget(target) {
131
+ if (target.kind === 'cli-mcp-registration')
132
+ return false;
133
+ if (target.action === 'blocked' || target.action === 'none')
134
+ return false;
135
+ return true;
136
+ }
package/dist/mcp/index.js CHANGED
@@ -2,12 +2,20 @@ export * from '../providers/opencode/manager-payload.js';
2
2
  export * from '../verification/index.js';
3
3
  export * from './client-install-opencode.js';
4
4
  export * from './client-install-opencode-contract.js';
5
+ export * from './client-install-claude-code.js';
6
+ export * from './client-install-claude-code-contract.js';
5
7
  export * from './client-setup-preview.js';
8
+ export * from './claude-code-agent-config.js';
9
+ export * from './claude-code-cli.js';
10
+ export * from './claude-code-config.js';
11
+ export * from './claude-code-project-memory.js';
12
+ export * from './claude-code-scope.js';
6
13
  export * from './control-plane.js';
7
14
  export * from './doctor.js';
8
15
  export * from './opencode-visibility.js';
9
16
  export * from './opencode-handoff-preview.js';
10
17
  export * from './provider-change-plan.js';
18
+ export * from './provider-canonical-agent-manifest.js';
11
19
  export * from './provider-doctor.js';
12
20
  export * from './provider-health-types.js';
13
21
  export * from './provider-status.js';
@@ -1,23 +1,11 @@
1
- export const vgxnessOpenCodeDefaultAgent = 'vgxness-manager';
2
- export const vgxnessOpenCodePromptContractVersion = 6;
1
+ import { projectCanonicalAgentManifestToOpenCode, } from '../agents/canonical-agent-projection.js';
2
+ import { canonicalDefaultAgentName, canonicalPromptContractVersion, canonicalSddSubagentNames, createCanonicalOpenCodeSddTaskPermissions, } from '../agents/canonical-agent-manifest.js';
3
+ export const vgxnessOpenCodeDefaultAgent = canonicalDefaultAgentName;
4
+ export const vgxnessOpenCodePromptContractVersion = canonicalPromptContractVersion;
3
5
  export const vgxnessOpenCodeInstructionsPath = 'AGENTS.md';
4
- export const vgxnessOpenCodeSddSubagents = [
5
- 'vgxness-sdd-explore',
6
- 'vgxness-sdd-propose',
7
- 'vgxness-sdd-spec',
8
- 'vgxness-sdd-design',
9
- 'vgxness-sdd-tasks',
10
- 'vgxness-sdd-apply',
11
- 'vgxness-sdd-verify',
12
- 'vgxness-sdd-archive',
13
- 'vgxness-sdd-init',
14
- 'vgxness-sdd-onboard',
15
- ];
6
+ export const vgxnessOpenCodeSddSubagents = canonicalSddSubagentNames;
16
7
  export function createOpenCodeSddTaskPermissions() {
17
- const taskPermissions = { '*': 'deny' };
18
- for (const subagent of vgxnessOpenCodeSddSubagents)
19
- taskPermissions[subagent] = 'allow';
20
- return taskPermissions;
8
+ return createCanonicalOpenCodeSddTaskPermissions();
21
9
  }
22
10
  export function createOpenCodeDefaultAgentInstallPlan(input = {}) {
23
11
  if (input.mcpOnly === true)
@@ -25,99 +13,5 @@ export function createOpenCodeDefaultAgentInstallPlan(input = {}) {
25
13
  return { installsAgents: true, agentNames: [vgxnessOpenCodeDefaultAgent, ...vgxnessOpenCodeSddSubagents], defaultAgent: vgxnessOpenCodeDefaultAgent };
26
14
  }
27
15
  export function createOpenCodeDefaultAgentConfig() {
28
- const agents = {
29
- [vgxnessOpenCodeDefaultAgent]: {
30
- description: 'VGXNESS SDD Manager - coordinates MCP state and SDD sub-agents, avoids inline execution when delegation is safer',
31
- mode: 'primary',
32
- model: 'openai/gpt-5.5',
33
- options: { reasoningEffort: 'high', vgxnessPromptContractVersion: vgxnessOpenCodePromptContractVersion },
34
- permission: { task: createOpenCodeSddTaskPermissions() },
35
- prompt: vgxnessManagerPrompt,
36
- reasoningEffort: 'high',
37
- tools: { bash: true, delegate: true, delegation_list: true, delegation_read: true, edit: true, read: true, write: true },
38
- variant: '',
39
- },
40
- };
41
- const descriptions = {
42
- 'vgxness-sdd-explore': 'Investigate codebase and think through ideas',
43
- 'vgxness-sdd-propose': 'Create change proposals from explorations',
44
- 'vgxness-sdd-spec': 'Write detailed specifications from proposals',
45
- 'vgxness-sdd-design': 'Create technical design from proposals',
46
- 'vgxness-sdd-tasks': 'Break down specs and designs into implementation tasks',
47
- 'vgxness-sdd-apply': 'Implement code changes from task definitions',
48
- 'vgxness-sdd-verify': 'Validate implementation against specs',
49
- 'vgxness-sdd-archive': 'Archive completed change artifacts',
50
- 'vgxness-sdd-init': 'Bootstrap SDD context and project configuration',
51
- 'vgxness-sdd-onboard': 'Guide user through a complete SDD cycle using their real codebase',
52
- };
53
- for (const name of vgxnessOpenCodeSddSubagents) {
54
- agents[name] = {
55
- description: descriptions[name],
56
- hidden: true,
57
- mode: 'subagent',
58
- model: 'openai/gpt-5.5',
59
- options: { vgxnessPromptContractVersion: vgxnessOpenCodePromptContractVersion },
60
- prompt: createSddSubagentPrompt(name),
61
- tools: { bash: true, edit: true, read: true, write: true },
62
- };
63
- }
64
- return { defaultAgent: vgxnessOpenCodeDefaultAgent, agents };
16
+ return projectCanonicalAgentManifestToOpenCode();
65
17
  }
66
- function createSddSubagentPrompt(name) {
67
- const phase = name.replace('vgxness-sdd-', '');
68
- const common = `You are the VGXNESS SDD ${phase} executor, not the orchestrator. Do this phase's work yourself. Do NOT delegate, do NOT call task/delegate, and do NOT launch sub-agents. Use provided SDD artifacts, VGXNESS MCP state, and local repository evidence as needed. Retrieve full artifact content through MCP when the phase requires it; do not expect the manager to paste verbose artifacts into your prompt. Do not depend on external local skill files; this inline contract is sufficient. Preserve unrelated user work, keep output concise, and report evidence.`;
69
- const contracts = {
70
- 'vgxness-sdd-explore': 'Investigate codebase context, constraints, options, and risks. Do not implement code changes. Return findings and recommended next artifacts.',
71
- 'vgxness-sdd-propose': 'Create or refine a focused SDD proposal from exploration evidence. Do not implement code changes. Include scope, tradeoffs, non-goals, and acceptance direction.',
72
- 'vgxness-sdd-spec': 'Write requirements, acceptance criteria, edge cases, and out-of-scope items. Do not implement code changes.',
73
- 'vgxness-sdd-design': 'Create technical design grounded in the repository: affected modules, data shapes, safety, rollout, and verification. Do not implement code changes.',
74
- 'vgxness-sdd-tasks': 'Break approved spec/design into small ordered testable implementation tasks. Do not implement code changes.',
75
- 'vgxness-sdd-apply': 'Implement only assigned SDD tasks. Preserve unrelated dirty files and user work. Use focused tests when authorized. Report changed files, tests/evidence, and residual risks.',
76
- 'vgxness-sdd-verify': 'Validate implementation against SDD artifacts and task acceptance criteria. Prefer focused local tests and evidence review. Do not implement unrelated changes.',
77
- 'vgxness-sdd-archive': 'Archive completed SDD outcome with summary, verification evidence, follow-ups, and residual risks. Confirm before any repository writes beyond the requested archive artifact.',
78
- 'vgxness-sdd-init': 'Bootstrap SDD context and project setup safely. Prefer diagnostics/read-only inspection and require explicit confirmation before writes or provider/global config changes.',
79
- 'vgxness-sdd-onboard': 'Guide the user through the SDD cycle with the smallest missing decision at each step. Keep assumptions explicit and confirm before writes or provider/global config changes.',
80
- };
81
- return `${common}\n\nPhase contract: ${contracts[name]}\n\nSDD governance v1: SDD artifact acceptance is human-only. Do not mark, describe, or persist generated phase output as accepted unless the user explicitly accepts it or an MCP acceptance record shows a human actor. Before advancing phases, use VGXNESS MCP readiness/status tools so draft, rejected, superseded, or legacy artifacts are not treated as accepted prerequisites. For risky VGX-managed side effects, use VGXNESS run preflight/reporting and include runId, phase, and agent context when available. OpenCode native/provider tools are audit-only and non-hard-blocking in governance v1; do not claim they are hard-blocked by OpenCode config.\n\nProvider-native daily progression: daily SDD progression happens inside OpenCode through conversation, VGXNESS MCP, and hidden SDD subagents. Do not instruct the user to run terminal SDD phase commands for normal daily flow. Return phase output, decisions, changed files, and evidence so the manager can persist artifacts and advance through MCP. Preserve confirmation/preflight requirements for apply, verify, init, archive, edits, shell, git, provider-tool, secrets, destructive, privileged, or ambiguous operations.`;
82
- }
83
- const vgxnessManagerPrompt = `# VGXNESS Manager - compact SDD orchestrator
84
-
85
- Bind only to the primary \`vgxness-manager\` agent. Executor agents (for example \`vgxness-sdd-apply\`) use their own prompts.
86
-
87
- ## Role
88
- Coordinate SDD; do not become a monolithic executor. Keep the chat thin, use VGXNESS MCP as durable state, delegate phase work to the smallest exact hidden SDD subagent, then synthesize concise evidence for the user. Be direct, practical, repository-grounded, user-controlled, and willing to challenge unsafe or weak assumptions.
89
-
90
- Coach while coordinating: teach briefly when helpful, explain practical tradeoffs, stay realistic about risk/effort/unknowns, respectfully challenge weak assumptions, keep the user comfortable and in control, and avoid lectures or unnecessary verbosity.
91
-
92
- ## Non-negotiable governance
93
- - SDD artifact acceptance is human-only. Never infer acceptance from generated output, file presence, confidence, or legacy artifacts. Treat draft/rejected/superseded/unaccepted artifacts as not accepted until a human acceptance record exists.
94
- - Before phase advancement, call readiness/status tools: \`vgxness_sdd_status\`/\`vgxness_sdd_next\` and \`vgxness_sdd_ready\` or \`vgxness_sdd_get_readiness\`.
95
- - Before risky VGX-managed side effects (edit, shell/tests, git, network, provider-tool, secrets, external-directory, destructive, privileged, ambiguous), call \`vgxness_run_preflight\` with runId/workflow/phase/agent context when available. If approval/block is required, stop; do not invent approval.
96
- - OpenCode native/provider tools are governance-v1 audit-only/non-hard-blocking. Report warnings; do not say native OpenCode tools are hard-blocked by config.
97
- - Do not mutate provider/global OpenCode config unless explicitly requested. Do not write to \`openspec/\`. Never revert/overwrite unrelated user work. Preserve \`permission.task\` deny-by-default with only exact known SDD subagents allowed.
98
- - Do not publish packages unless explicitly requested.
99
- - Do not change model or reasoning effort.
100
-
101
- ## Provider-native daily flow
102
- Normal SDD progression happens inside OpenCode through conversation, VGXNESS MCP, and hidden SDD subagents. Do not tell users to run terminal SDD phase commands for daily flow. CLI is an escape hatch only for bootstrap, doctor, rollback/recovery, MCP unavailable/setup missing, provider-native repair out of scope, or explicit user request.
103
-
104
- ## MCP playbook
105
- - For starting, resuming, or recovering context: prefer \`vgxness_context_cockpit\` with project + workspace; treat \`vgxness_session_restore\` as one signal inside the cockpit, not authoritative truth. If cockpit is unavailable, use \`vgxness_session_restore\` with project + workspace before inferring state. For ending, pausing, handing off, or compacting: \`vgxness_session_close\` with current session id and actor \`manager\`; if no id, do not invent one, say so and include summary in final response.
106
- - SDD artifacts: list/read with \`vgxness_sdd_get_artifact\`/\`vgxness_sdd_list_artifacts\`; use \`payloadMode: "compact"\` by default for manager-facing reads/lists so the primary context stays clean. Use \`payloadMode: "verbose"\` only when full content is truly required, preferably inside the delegated phase subagent rather than the manager context. Save phase output with \`vgxness_sdd_save_artifact\` only after the appropriate flow. SDD artifacts are not generic memory.
107
- - Acceptance/readiness: confirm explicit human acceptance, use \`vgxness_sdd_accept_artifact\` only for explicit human acceptance, then \`vgxness_sdd_get_readiness\`/readiness tools before reporting state. Use \`vgxness_governance_report\` for readiness, artifact states, preflight posture, and audit warnings before risky/ambiguous transitions and in apply/verify summaries.
108
- - Memory: call \`vgxness_memory_search\`/\`vgxness_memory_get\` when prior work or unclear project context is referenced; call \`vgxness_memory_save\` for durable discoveries, decisions, bug fixes, config, patterns, or preferences; use \`vgxness_memory_update\` only to correct/evolve a known id. Do not duplicate full SDD artifacts as memory.
109
- - Agents/profile/payloads: resolve exact phase with \`vgxness_agent_resolve\`. \`vgxness_agent_activate\`, \`vgxness_opencode_manager_payload\`, and \`vgxness_skill_payload\` prepare/read preview context only; agent_activate does not execute a provider or write provider config. Use \`vgxness_manager_profile_get\` before behavior changes; \`vgxness_manager_profile_set\` requires explicit human authorization.
110
- - Runs: use \`vgxness_run_start\`/\`vgxness_run_list\`/\`vgxness_run_get\` for significant implementation/verification or multi-step delegated work; checkpoint with \`vgxness_run_checkpoint\`, preflight with \`vgxness_run_preflight\`, and close with \`vgxness_run_finalize\`.
111
- - Provider diagnostics: \`vgxness_provider_status\` and \`vgxness_provider_doctor\` are read-only reports.
112
-
113
- ## Minimum flows
114
- - Simple answer: respond inline; memory only if prior context is referenced; no run by default.
115
- - Proposal/spec/design/tasks: status/next -> readiness -> read prerequisites -> resolve exact subagent -> delegate -> persist returned output only according to acceptance/governance.
116
- - Apply/verify: require tasks/apply-progress as appropriate -> read artifacts -> resolve exact subagent -> recover/start run -> preflight writes/shell/git/tests -> delegate -> checkpoint -> save apply-progress/verify -> finalize when clear.
117
- - Config/provider/prompt change: inspect manager/profile or payload first; persistent changes require explicit human authorization.
118
-
119
- ## Delegation thresholds
120
- Inline only small decisions, 1-3 file reads, status commands, and atomic one-file mechanical edits. Delegate broad exploration (4+ files), substantial implementation, writes with analysis/new logic, and execution-heavy verification. Never delegate to unknown agents; use exact SDD phase mapping: explore/propose/spec/design/tasks/apply/verify/archive/init/onboard.
121
-
122
- ## Output
123
- Be concise: what was delegated, what came back, files/decisions changed, evidence/tests, risks, and next step.`;
@@ -0,0 +1,39 @@
1
+ import { canonicalAgentManifest } from '../agents/canonical-agent-manifest.js';
2
+ import { canonicalManifestValidationErrors, canonicalProviderProjection } from '../agents/canonical-agent-projection.js';
3
+ const diagnosticProviders = ['opencode', 'claude'];
4
+ export function buildCanonicalAgentManifestDiagnostic(manifest = canonicalAgentManifest) {
5
+ const validationErrors = canonicalManifestValidationErrors(manifest);
6
+ const managerCount = manifest.agents.filter((agent) => agent.mode === 'agent').length;
7
+ const subagentCount = manifest.agents.filter((agent) => agent.mode === 'subagent').length;
8
+ return {
9
+ status: validationErrors.length === 0 ? 'pass' : 'fail',
10
+ version: manifest.version,
11
+ project: manifest.project,
12
+ scope: manifest.scope,
13
+ defaultAgentName: manifest.defaultAgentName,
14
+ promptContractVersion: manifest.promptContractVersion,
15
+ agentCount: manifest.agents.length,
16
+ managerCount,
17
+ subagentCount,
18
+ validationErrors,
19
+ providerSupport: diagnosticProviders.map((provider) => providerSupportFor(provider, manifest)),
20
+ };
21
+ }
22
+ function providerSupportFor(provider, manifest) {
23
+ try {
24
+ const projection = canonicalProviderProjection(provider, manifest);
25
+ return {
26
+ provider: projection.provider,
27
+ level: projection.level,
28
+ ...(projection.reason === undefined ? {} : { reason: projection.reason }),
29
+ };
30
+ }
31
+ catch {
32
+ const fallback = manifest.agents.find((agent) => agent.providerSupport[provider] !== undefined)?.providerSupport[provider];
33
+ return {
34
+ provider,
35
+ level: fallback?.level ?? 'unsupported',
36
+ ...(fallback?.reason === undefined ? {} : { reason: fallback.reason }),
37
+ };
38
+ }
39
+ }
@@ -1,7 +1,11 @@
1
1
  import { resolveMemoryDatabasePath } from '../memory/storage-paths.js';
2
+ import { planClaudeCodeMcpInstall } from './client-install-claude-code-contract.js';
2
3
  import { planOpenCodeMcpInstall } from './client-install-opencode-contract.js';
3
4
  import { ProviderDoctorService } from './provider-doctor.js';
5
+ import { CLAUDE_USER_GLOBAL_SCOPE_CAPABILITIES, isUserGlobalScope } from './provider-health-types.js';
4
6
  import { ProviderStatusService } from './provider-status.js';
7
+ import { createClaudeCodeCliRegistrationPreview } from './claude-code-cli.js';
8
+ import { resolveClaudeCodeScope } from './claude-code-scope.js';
5
9
  export const providerChangePlanProviders = ['opencode', 'claude', 'antigravity', 'custom'];
6
10
  export const providerChangePlanTypes = ['opencode-mcp-install', 'setup', 'install', 'config-preparation'];
7
11
  const safety = {
@@ -22,8 +26,10 @@ export class ProviderChangePlanService {
22
26
  }
23
27
  getPlan(input) {
24
28
  const normalized = normalizeInput(input);
25
- if (normalized.provider !== 'opencode')
29
+ if (normalized.provider !== 'opencode' && normalized.provider !== 'claude')
26
30
  return { ok: true, value: unsupportedProviderEnvelope(normalized) };
31
+ if (normalized.provider === 'claude')
32
+ return this.getClaudePlan(normalized);
27
33
  const status = (this.deps.providerStatus ?? new ProviderStatusService()).getStatus({
28
34
  project: normalized.project,
29
35
  scope: normalized.scope,
@@ -65,6 +71,60 @@ export class ProviderChangePlanService {
65
71
  value: opencodeEnvelope(normalized, status.value, doctor.value, installPlan, databasePath.value.source),
66
72
  };
67
73
  }
74
+ getClaudePlan(normalized) {
75
+ const status = (this.deps.providerStatus ?? new ProviderStatusService()).getStatus({ project: normalized.project, scope: normalized.scope, providerAdapter: 'claude', workspaceRoot: normalized.workspaceRoot, env: this.deps.env ?? process.env, payloadMode: normalized.payloadMode });
76
+ if (!status.ok)
77
+ return status;
78
+ const doctor = (this.deps.providerDoctor ?? new ProviderDoctorService()).getDoctor({ project: normalized.project, scope: normalized.scope, providerAdapter: 'claude', workspaceRoot: normalized.workspaceRoot, env: this.deps.env ?? process.env, payloadMode: normalized.payloadMode });
79
+ if (!doctor.ok)
80
+ return doctor;
81
+ const claudeScope = resolveClaudeCodeScope(normalized.scope);
82
+ if (!claudeScope.ok)
83
+ return claudeScope;
84
+ if (isUserGlobalScope(normalized.scope) || claudeScope.value.canonical === 'local')
85
+ return { ok: true, value: claudeUserGlobalEnvelope(normalized, status.value, doctor.value, claudeScope.value.canonical, claudeScope.value.warnings) };
86
+ const databasePath = resolveMemoryDatabasePath({ cwd: normalized.workspaceRoot, env: this.deps.env ?? process.env });
87
+ if (!databasePath.ok)
88
+ return { ok: true, value: blockedPlanEnvelope(normalized, status.value, doctor.value, databasePath.error.message) };
89
+ const installPlan = planClaudeCodeMcpInstall({ cwd: normalized.workspaceRoot, databasePath: databasePath.value.path, databasePathSource: databasePath.value.source });
90
+ return { ok: true, value: claudeEnvelope(normalized, status.value, doctor.value, installPlan, databasePath.value.source) };
91
+ }
92
+ }
93
+ function claudeUserGlobalEnvelope(input, status, doctor, canonicalScope = 'user', scopeWarnings = []) {
94
+ const warnings = [
95
+ ...scopeWarnings,
96
+ ...statusWarnings(status),
97
+ ...doctor.recommendations,
98
+ 'Claude private config mutation is intentionally unsupported by VGXNESS; future apply uses Claude CLI argv only after confirmation/preflight.',
99
+ ];
100
+ const envelope = {
101
+ ...baseEnvelope(input),
102
+ supported: true,
103
+ status: 'planned',
104
+ summary: `Read-only Claude ${canonicalScope} change planning completed. VGXNESS will not read or write private Claude config files from this plan. Future apply requires confirmation/preflight and uses Claude CLI argv only: claude mcp add --scope ${canonicalScope} vgxness -- vgxness mcp start.`,
105
+ providerIdentity: { provider: 'claude', adapter: 'claude', support: 'supported' },
106
+ statusSummary: statusSummary(status),
107
+ statusFindings: status.config,
108
+ doctorSummary: doctorSummary(doctor),
109
+ doctorFindings: doctor.checks,
110
+ previewEffects: {
111
+ action: 'none',
112
+ backupRequired: false,
113
+ preservedTopLevelKeys: [],
114
+ cliCommandPreview: createClaudeCodeCliRegistrationPreview(canonicalScope),
115
+ installsAgents: false,
116
+ agentNames: [],
117
+ refusalMessage: 'Read-only plan only; future apply requires confirmation/preflight and does not mutate ~/.claude.json manually.',
118
+ },
119
+ backupRollback: descriptiveBackupPolicy(false),
120
+ confirmations: confirmationPolicy(),
121
+ risks: [
122
+ `VGXNESS cannot verify Claude ${canonicalScope} MCP registration from read-only planning because it intentionally avoids reading private Claude config and does not execute Claude Code.`,
123
+ `Supported Claude read-only capabilities are: ${CLAUDE_USER_GLOBAL_SCOPE_CAPABILITIES.join(', ')}.`,
124
+ ],
125
+ warnings,
126
+ };
127
+ return input.payloadMode === 'verbose' ? { ...envelope, rawSources: { status, doctor } } : envelope;
68
128
  }
69
129
  function normalizeInput(input) {
70
130
  return {
@@ -156,6 +216,28 @@ function opencodeEnvelope(input, status, doctor, installPlan, source) {
156
216
  }
157
217
  : envelope;
158
218
  }
219
+ function claudeEnvelope(input, status, doctor, installPlan, source) {
220
+ const backupRequired = installPlan.status === 'would_install' ? installPlan.backupRequired : false;
221
+ const warnings = [...statusWarnings(status), ...doctor.recommendations, ...installPlan.warnings];
222
+ const envelope = {
223
+ ...baseEnvelope(input),
224
+ supported: true,
225
+ status: installPlan.status === 'refused' ? 'blocked' : 'planned',
226
+ ...(installPlan.status === 'refused' ? { code: 'PLAN_UNAVAILABLE' } : {}),
227
+ summary: installPlan.status === 'refused' ? `Read-only Claude ${input.changeType} planning completed; future install is currently refused: ${installPlan.message}` : `Read-only Claude ${input.changeType} planning completed; future confirmed write would update project .mcp.json, ${installPlan.agentNames.length} agent file(s), and project-root CLAUDE.md as needed.`,
228
+ providerIdentity: { provider: 'claude', adapter: 'claude', support: 'supported' },
229
+ statusSummary: statusSummary(status),
230
+ statusFindings: status.config,
231
+ doctorSummary: doctorSummary(doctor),
232
+ doctorFindings: doctor.checks,
233
+ previewEffects: previewEffectsClaude(installPlan),
234
+ backupRollback: descriptiveBackupPolicy(backupRequired),
235
+ confirmations: confirmationPolicy(),
236
+ risks: risksForClaude(status, doctor, installPlan, source),
237
+ warnings,
238
+ };
239
+ return input.payloadMode === 'verbose' ? { ...envelope, rawSources: { status, doctor, claudeCodeMcpInstallPlan: installPlan } } : envelope;
240
+ }
159
241
  function baseEnvelope(input) {
160
242
  return {
161
243
  version: 1,
@@ -223,6 +305,20 @@ function previewEffects(plan) {
223
305
  ...(plan.defaultAgent === undefined ? {} : { defaultAgent: plan.defaultAgent }),
224
306
  };
225
307
  }
308
+ function previewEffectsClaude(plan) {
309
+ const projectMemory = projectMemoryPreview(plan.targets);
310
+ if (plan.status === 'refused')
311
+ return { action: 'refused', targetPath: plan.targetPath, backupRequired: false, preservedTopLevelKeys: plan.preservedTopLevelKeys, serverCommand: ['vgxness', ...plan.server.args], installsAgents: true, agentNames: plan.agentNames, installPlanStatus: 'refused', refusalReason: plan.reason, refusalMessage: plan.message, ...(projectMemory === undefined ? {} : { projectMemory }) };
312
+ const mcpTarget = plan.targets.find((target) => target.kind === 'mcp-json');
313
+ return { action: mcpTarget?.action === 'create' ? 'would-create' : 'would-merge', targetPath: plan.targetPath, backupRequired: plan.backupRequired, preservedTopLevelKeys: plan.preservedTopLevelKeys, serverCommand: ['vgxness', ...plan.server.args], installsAgents: true, agentNames: plan.agentNames, installPlanStatus: 'would_install', ...(projectMemory === undefined ? {} : { projectMemory }) };
314
+ }
315
+ function projectMemoryPreview(targets) {
316
+ const target = targets.find((item) => item.kind === 'project-memory');
317
+ if (target === undefined)
318
+ return undefined;
319
+ const action = target.action === 'create' ? 'would-create' : target.action === 'append-managed-block' ? 'would-append' : target.action === 'update-managed-block' ? 'would-update-managed-block' : target.action === 'none' ? 'up-to-date' : 'refused';
320
+ return { path: target.path, status: target.status, action, backupRequired: target.backupRequired, ...(target.reason === undefined ? {} : { message: target.reason }) };
321
+ }
226
322
  function descriptiveBackupPolicy(backupRequired) {
227
323
  return {
228
324
  policy: 'descriptive-only',
@@ -255,6 +351,18 @@ function risksForOpenCode(status, doctor, plan, source) {
255
351
  risks.push('Future MCP server command would rely on the global default VGXNESS database path.');
256
352
  return risks;
257
353
  }
354
+ function risksForClaude(status, doctor, plan, source) {
355
+ const risks = ['Claude project .mcp.json and project-root CLAUDE.md may affect collaborators if committed; review before committing.', 'Claude Code runtime MCP approval happens in Claude Code; this plan does not prove runtime connection.'];
356
+ if (status.status !== 'ready')
357
+ risks.push(`Provider status is ${status.status}; future writes should review status findings first.`);
358
+ if (doctor.status !== 'healthy')
359
+ risks.push(`Provider doctor is ${doctor.status}; future writes should review doctor findings first.`);
360
+ if (plan.status === 'refused')
361
+ risks.push(`Future install planner refused the operation: ${plan.reason}.`);
362
+ if (source === 'global-default')
363
+ risks.push('Future MCP server command would rely on the global default VGXNESS database path.');
364
+ return risks;
365
+ }
258
366
  function summaryForOpenCode(input, plan) {
259
367
  if (plan.status === 'refused')
260
368
  return `Read-only OpenCode ${input.changeType} planning completed; future install is currently refused: ${plan.message}`;