vgxness 1.5.1 → 1.5.2

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 (42) 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 +146 -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-help.js +3 -0
  8. package/dist/cli/commands/agent-skill-dispatcher.js +6 -5
  9. package/dist/cli/commands/mcp-dispatcher.js +65 -3
  10. package/dist/cli/index.js +1 -1
  11. package/dist/governance/governance-report-builder.js +45 -26
  12. package/dist/mcp/claude-code-agent-config.js +79 -0
  13. package/dist/mcp/claude-code-config.js +84 -0
  14. package/dist/mcp/client-install-claude-code-contract.js +86 -0
  15. package/dist/mcp/client-install-claude-code.js +85 -0
  16. package/dist/mcp/index.js +5 -0
  17. package/dist/mcp/opencode-default-agent-config.js +7 -113
  18. package/dist/mcp/provider-canonical-agent-manifest.js +39 -0
  19. package/dist/mcp/provider-change-plan.js +57 -1
  20. package/dist/mcp/provider-doctor.js +54 -0
  21. package/dist/mcp/provider-status.js +82 -2
  22. package/dist/mcp/schema.js +2 -2
  23. package/dist/mcp/validation.js +1 -1
  24. package/dist/memory/memory-service.js +4 -0
  25. package/dist/sdd/sdd-workflow-service.js +129 -59
  26. package/dist/setup/providers/claude-setup-adapter.js +7 -4
  27. package/docs/architecture.md +54 -112
  28. package/docs/cli.md +53 -0
  29. package/docs/code-runtime.md +218 -0
  30. package/docs/contributing.md +120 -0
  31. package/docs/glossary.md +211 -0
  32. package/docs/mcp.md +144 -0
  33. package/docs/prd.md +23 -26
  34. package/docs/providers.md +123 -0
  35. package/docs/roadmap.md +88 -0
  36. package/docs/safety.md +147 -0
  37. package/docs/storage.md +93 -0
  38. package/package.json +1 -1
  39. package/docs/funcionamiento-del-sistema.md +0 -865
  40. package/docs/harness-gap-analysis.md +0 -243
  41. package/docs/vgxcode.md +0 -87
  42. package/docs/vgxness-code.md +0 -48
@@ -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,4 +1,5 @@
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';
4
5
  import { ProviderStatusService } from './provider-status.js';
@@ -22,8 +23,10 @@ export class ProviderChangePlanService {
22
23
  }
23
24
  getPlan(input) {
24
25
  const normalized = normalizeInput(input);
25
- if (normalized.provider !== 'opencode')
26
+ if (normalized.provider !== 'opencode' && normalized.provider !== 'claude')
26
27
  return { ok: true, value: unsupportedProviderEnvelope(normalized) };
28
+ if (normalized.provider === 'claude')
29
+ return this.getClaudePlan(normalized);
27
30
  const status = (this.deps.providerStatus ?? new ProviderStatusService()).getStatus({
28
31
  project: normalized.project,
29
32
  scope: normalized.scope,
@@ -65,6 +68,19 @@ export class ProviderChangePlanService {
65
68
  value: opencodeEnvelope(normalized, status.value, doctor.value, installPlan, databasePath.value.source),
66
69
  };
67
70
  }
71
+ getClaudePlan(normalized) {
72
+ 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 });
73
+ if (!status.ok)
74
+ return status;
75
+ 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 });
76
+ if (!doctor.ok)
77
+ return doctor;
78
+ const databasePath = resolveMemoryDatabasePath({ cwd: normalized.workspaceRoot, env: this.deps.env ?? process.env });
79
+ if (!databasePath.ok)
80
+ return { ok: true, value: blockedPlanEnvelope(normalized, status.value, doctor.value, databasePath.error.message) };
81
+ const installPlan = planClaudeCodeMcpInstall({ cwd: normalized.workspaceRoot, databasePath: databasePath.value.path, databasePathSource: databasePath.value.source });
82
+ return { ok: true, value: claudeEnvelope(normalized, status.value, doctor.value, installPlan, databasePath.value.source) };
83
+ }
68
84
  }
69
85
  function normalizeInput(input) {
70
86
  return {
@@ -156,6 +172,28 @@ function opencodeEnvelope(input, status, doctor, installPlan, source) {
156
172
  }
157
173
  : envelope;
158
174
  }
175
+ function claudeEnvelope(input, status, doctor, installPlan, source) {
176
+ const backupRequired = installPlan.status === 'would_install' ? installPlan.backupRequired : false;
177
+ const warnings = [...statusWarnings(status), ...doctor.recommendations, ...installPlan.warnings];
178
+ const envelope = {
179
+ ...baseEnvelope(input),
180
+ supported: true,
181
+ status: installPlan.status === 'refused' ? 'blocked' : 'planned',
182
+ ...(installPlan.status === 'refused' ? { code: 'PLAN_UNAVAILABLE' } : {}),
183
+ 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 and ${installPlan.agentNames.length} agent file(s).`,
184
+ providerIdentity: { provider: 'claude', adapter: 'claude', support: 'supported' },
185
+ statusSummary: statusSummary(status),
186
+ statusFindings: status.config,
187
+ doctorSummary: doctorSummary(doctor),
188
+ doctorFindings: doctor.checks,
189
+ previewEffects: previewEffectsClaude(installPlan),
190
+ backupRollback: descriptiveBackupPolicy(backupRequired),
191
+ confirmations: confirmationPolicy(),
192
+ risks: risksForClaude(status, doctor, installPlan, source),
193
+ warnings,
194
+ };
195
+ return input.payloadMode === 'verbose' ? { ...envelope, rawSources: { status, doctor, claudeCodeMcpInstallPlan: installPlan } } : envelope;
196
+ }
159
197
  function baseEnvelope(input) {
160
198
  return {
161
199
  version: 1,
@@ -223,6 +261,12 @@ function previewEffects(plan) {
223
261
  ...(plan.defaultAgent === undefined ? {} : { defaultAgent: plan.defaultAgent }),
224
262
  };
225
263
  }
264
+ function previewEffectsClaude(plan) {
265
+ if (plan.status === 'refused')
266
+ 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 };
267
+ const mcpTarget = plan.targets.find((target) => target.kind === 'mcp-json');
268
+ 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' };
269
+ }
226
270
  function descriptiveBackupPolicy(backupRequired) {
227
271
  return {
228
272
  policy: 'descriptive-only',
@@ -255,6 +299,18 @@ function risksForOpenCode(status, doctor, plan, source) {
255
299
  risks.push('Future MCP server command would rely on the global default VGXNESS database path.');
256
300
  return risks;
257
301
  }
302
+ function risksForClaude(status, doctor, plan, source) {
303
+ const risks = ['Claude project .mcp.json may affect collaborators if committed; review before committing.', 'Claude Code runtime MCP approval happens in Claude Code; this plan does not prove runtime connection.'];
304
+ if (status.status !== 'ready')
305
+ risks.push(`Provider status is ${status.status}; future writes should review status findings first.`);
306
+ if (doctor.status !== 'healthy')
307
+ risks.push(`Provider doctor is ${doctor.status}; future writes should review doctor findings first.`);
308
+ if (plan.status === 'refused')
309
+ risks.push(`Future install planner refused the operation: ${plan.reason}.`);
310
+ if (source === 'global-default')
311
+ risks.push('Future MCP server command would rely on the global default VGXNESS database path.');
312
+ return risks;
313
+ }
258
314
  function summaryForOpenCode(input, plan) {
259
315
  if (plan.status === 'refused')
260
316
  return `Read-only OpenCode ${input.changeType} planning completed; future install is currently refused: ${plan.message}`;
@@ -1,15 +1,25 @@
1
1
  import { existsSync, readFileSync, statSync } from 'node:fs';
2
+ import { inspectClaudeCodeAgents } from './claude-code-agent-config.js';
3
+ import { claudeAdvisoryPaths, inspectClaudeCodeMcpConfig } from './claude-code-config.js';
2
4
  import { vgxnessOpenCodeDefaultAgent, vgxnessOpenCodeSddSubagents } from './opencode-default-agent-config.js';
5
+ import { buildCanonicalAgentManifestDiagnostic } from './provider-canonical-agent-manifest.js';
3
6
  import { normalizeProviderHealthInput, PROVIDER_HEALTH_SAFETY, providerHealthFailure, REQUIRED_PROVIDER_NATIVE_MCP_TOOLS, rollupProviderDoctor, } from './provider-health-types.js';
4
7
  import { inspectOpenCodeConfigPaths } from './provider-status.js';
5
8
  export class ProviderDoctorService {
9
+ deps;
10
+ constructor(deps = {}) {
11
+ this.deps = deps;
12
+ }
6
13
  runDoctor(input = {}) {
7
14
  return this.getDoctor(input);
8
15
  }
9
16
  getDoctor(input = {}) {
10
17
  const normalized = normalizeProviderHealthInput(input);
18
+ if (normalized.providerAdapter === 'claude')
19
+ return this.getClaudeDoctor(normalized);
11
20
  if (normalized.providerAdapter !== 'opencode')
12
21
  return providerHealthFailure(`Unsupported provider adapter: ${normalized.providerAdapter}`);
22
+ const canonicalAgentManifest = (this.deps.canonicalAgentManifestDiagnostic ?? buildCanonicalAgentManifestDiagnostic)();
13
23
  const paths = inspectOpenCodeConfigPaths(normalized.workspaceRoot, normalized.env);
14
24
  const before = snapshotPaths(paths.map((path) => path.path), normalized.workspaceRoot);
15
25
  const readableJson = paths.filter((path) => path.parsed);
@@ -21,6 +31,7 @@ export class ProviderDoctorService {
21
31
  detail: `Workspace root ${existsSync(normalized.workspaceRoot) ? 'exists' : 'does not exist'}: ${normalized.workspaceRoot}`,
22
32
  },
23
33
  { id: 'provider-supported', status: 'pass', detail: 'OpenCode provider adapter selected.' },
34
+ canonicalAgentManifestCheck(canonicalAgentManifest, normalized.payloadMode),
24
35
  configReadableCheck(readableJson.length, paths.filter((path) => path.exists).length),
25
36
  mcpEntryCheck(config),
26
37
  defaultAgentCheck(config),
@@ -71,6 +82,35 @@ export class ProviderDoctorService {
71
82
  },
72
83
  };
73
84
  }
85
+ getClaudeDoctor(normalized) {
86
+ const mcp = inspectClaudeCodeMcpConfig(normalized.workspaceRoot);
87
+ const agents = inspectClaudeCodeAgents(normalized.workspaceRoot);
88
+ const advisoryPaths = claudeAdvisoryPaths(normalized.workspaceRoot);
89
+ const checkedPathList = [mcp.path, agents.directoryPath, ...agents.agents.map((agent) => agent.path), ...advisoryPaths];
90
+ const before = snapshotPaths(checkedPathList, normalized.workspaceRoot);
91
+ const missingAgents = agents.agents.filter((agent) => agent.status === 'missing');
92
+ const badFrontmatter = agents.agents.filter((agent) => agent.frontmatter === 'invalid' || agent.frontmatter === 'missing');
93
+ const badMarkers = agents.agents.filter((agent) => agent.exists && !agent.generatedMarker);
94
+ const memoryFiles = advisoryPaths.filter((path) => existsSync(path));
95
+ const checks = [
96
+ { id: 'workspace-root', status: existsSync(normalized.workspaceRoot) ? 'pass' : 'fail', detail: `Workspace root ${existsSync(normalized.workspaceRoot) ? 'exists' : 'does not exist'}: ${normalized.workspaceRoot}` },
97
+ { id: 'provider-supported', status: 'pass', detail: 'Claude provider adapter selected for project-local .mcp.json and .claude/agents support.' },
98
+ { id: 'claude-project-mcp-readable', status: mcp.status === 'invalid' ? 'fail' : mcp.status === 'missing' ? 'not-configured' : 'pass', detail: mcp.message, ...(mcp.status === 'invalid' ? { remediation: 'Fix malformed .mcp.json before installing VGXNESS Claude support.' } : {}) },
99
+ { id: 'claude-vgxness-mcp-entry', status: mcp.status === 'configured' ? 'pass' : mcp.status === 'conflicting' ? 'fail' : 'not-configured', detail: mcp.message, ...(mcp.status === 'conflicting' ? { remediation: 'Manually reconcile mcpServers.vgxness before applying VGXNESS Claude support.' } : {}) },
100
+ { id: 'claude-agents-directory', status: agents.directoryExists ? 'pass' : 'not-configured', detail: agents.directoryExists ? 'Claude project agents directory exists.' : 'Claude project agents directory is missing; confirmed apply may create it.' },
101
+ { id: 'claude-vgxness-agents', status: missingAgents.length === 0 && agents.agents.every((agent) => agent.status !== 'conflicting' && agent.status !== 'invalid') ? 'pass' : missingAgents.length > 0 ? 'not-configured' : 'fail', detail: missingAgents.length > 0 ? `Missing VGXNESS Claude agents: ${missingAgents.map((agent) => agent.agentName).join(', ')}.` : 'Expected VGXNESS Claude agent targets were inspected.' },
102
+ { id: 'claude-agent-frontmatter', status: badFrontmatter.length === 0 ? 'pass' : 'fail', detail: badFrontmatter.length === 0 ? 'Expected Claude agent frontmatter is valid.' : `Invalid or missing frontmatter for: ${badFrontmatter.map((agent) => agent.agentName).join(', ')}.` },
103
+ { id: 'claude-agent-generated-metadata', status: badMarkers.length === 0 ? 'pass' : 'fail', detail: badMarkers.length === 0 ? 'Existing VGXNESS Claude agent files include generated metadata markers.' : `Missing VGXNESS generated marker for: ${badMarkers.map((agent) => agent.agentName).join(', ')}.` },
104
+ { id: 'claude-project-memory-advisory', status: memoryFiles.length === 0 ? 'pass' : 'warn', detail: memoryFiles.length === 0 ? 'No Claude project memory/settings advisory files were found.' : `Advisory only; VGXNESS will not modify: ${memoryFiles.join(', ')}.` },
105
+ readonlySafetyCheck(before, snapshotPaths(checkedPathList, normalized.workspaceRoot)),
106
+ ];
107
+ const status = rollupProviderDoctor(checks.map((check) => check.status));
108
+ const compactChecksValue = compactChecks(checks, normalized.payloadMode);
109
+ const checkedPaths = normalized.payloadMode === 'verbose' ? checkedPathList : checkedPathList.filter((path) => existsSync(path) || path === mcp.path);
110
+ const failedChecks = checks.filter((check) => check.status === 'fail').map((check) => check.id);
111
+ const recommendations = checks.flatMap((check) => (check.remediation === undefined ? [] : [check.remediation]));
112
+ return { ok: true, value: { version: 1, kind: 'provider-doctor', project: normalized.project, providerAdapter: 'claude', scope: normalized.scope, workspaceRoot: normalized.workspaceRoot, status, payloadMode: normalized.payloadMode, overallStatus: status, checkCount: checks.length, passedCount: checks.filter((check) => check.status === 'pass').length, warningCount: checks.filter((check) => check.status === 'warn' || check.status === 'not-configured').length, errorCount: failedChecks.length, skippedCount: checks.filter((check) => check.status === 'skip').length, failedChecks, summary: summarizeDoctor(status, failedChecks.length, recommendations.length), recommendations, checks: compactChecksValue, checkedPaths, bytes: { originalBytes: Buffer.byteLength(JSON.stringify({ checks, checkedPaths: checkedPathList }), 'utf8'), compactBytes: Buffer.byteLength(JSON.stringify({ checks: compactChecksValue, checkedPaths }), 'utf8') }, verboseAvailable: normalized.payloadMode === 'compact', fullContentRef: `provider-doctor:claude:${normalized.workspaceRoot}`, generatedAt: 'read-only-snapshot', safety: PROVIDER_HEALTH_SAFETY } };
113
+ }
74
114
  }
75
115
  function summarizeDoctor(status, failedCount, recommendationCount) {
76
116
  if (status === 'healthy')
@@ -115,6 +155,20 @@ function configReadableCheck(readableCount, existingCount) {
115
155
  detail: `Found ${existingCount} OpenCode config path(s), ${readableCount} readable JSON config(s). Project config can override user/global config.`,
116
156
  };
117
157
  }
158
+ function canonicalAgentManifestCheck(diagnostic, mode) {
159
+ if (diagnostic.status === 'pass') {
160
+ const detail = mode === 'verbose'
161
+ ? `Canonical agent manifest validates: defaultAgent=${diagnostic.defaultAgentName}, promptContractVersion=${diagnostic.promptContractVersion}, agents=${diagnostic.agentCount}, managers=${diagnostic.managerCount}, subagents=${diagnostic.subagentCount}, providerSupport=${diagnostic.providerSupport.map((support) => `${support.provider}:${support.level}${support.reason === undefined ? '' : ` (${support.reason})`}`).join(', ')}.`
162
+ : `Canonical agent manifest validates: defaultAgent=${diagnostic.defaultAgentName}, promptContractVersion=${diagnostic.promptContractVersion}, agents=${diagnostic.agentCount}, managers=${diagnostic.managerCount}, subagents=${diagnostic.subagentCount}.`;
163
+ return { id: 'canonical-agent-manifest', status: 'pass', detail };
164
+ }
165
+ return {
166
+ id: 'canonical-agent-manifest',
167
+ status: 'fail',
168
+ detail: `Canonical agent manifest validation failed: ${diagnostic.validationErrors.join('; ') || 'unknown validation failure'}.`,
169
+ remediation: 'Fix the canonical agent manifest validation errors before relying on provider projection or provider setup.',
170
+ };
171
+ }
118
172
  function mcpEntryCheck(config) {
119
173
  const mcp = isRecord(config?.mcp) ? config.mcp : undefined;
120
174
  const entry = mcp?.vgxness;
@@ -1,7 +1,10 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
+ import { inspectClaudeCodeAgents } from './claude-code-agent-config.js';
4
+ import { claudeAdvisoryPaths, claudeMcpConfigPathStatus, claudeMcpEntryStatus, inspectClaudeCodeMcpConfig } from './claude-code-config.js';
3
5
  import { resolveOpenCodeMcpInstallTarget } from './client-install-opencode-contract.js';
4
6
  import { vgxnessOpenCodeDefaultAgent, vgxnessOpenCodePromptContractVersion, vgxnessOpenCodeSddSubagents } from './opencode-default-agent-config.js';
7
+ import { buildCanonicalAgentManifestDiagnostic } from './provider-canonical-agent-manifest.js';
5
8
  import { normalizeProviderHealthInput, PROVIDER_HEALTH_SAFETY, providerHealthFailure, REQUIRED_PROVIDER_MCP_TOOLS, rollupProviderHealth, } from './provider-health-types.js';
6
9
  const projectConfigTargets = ['.opencode/opencode.json', 'opencode.json', '.opencode/opencode.jsonc', 'opencode.jsonc'];
7
10
  export class ProviderStatusService {
@@ -11,8 +14,11 @@ export class ProviderStatusService {
11
14
  }
12
15
  getStatus(input = {}) {
13
16
  const normalized = normalizeProviderHealthInput(input);
17
+ if (normalized.providerAdapter === 'claude')
18
+ return this.getClaudeStatus(normalized);
14
19
  if (normalized.providerAdapter !== 'opencode')
15
20
  return providerHealthFailure(`Unsupported provider adapter: ${normalized.providerAdapter}`);
21
+ const canonicalAgentManifest = (this.deps.canonicalAgentManifestDiagnostic ?? buildCanonicalAgentManifestDiagnostic)();
16
22
  const paths = inspectOpenCodeConfigPaths(normalized.workspaceRoot, normalized.env);
17
23
  const mcpEntry = inspectOpenCodeMcpEntry(paths);
18
24
  const providerConfig = readFirstParsedConfig(paths);
@@ -20,7 +26,7 @@ export class ProviderStatusService {
20
26
  const subagentsConfigured = hasConfiguredSubagents(providerConfig);
21
27
  const tools = requiredToolPresence();
22
28
  const configStatus = resolveConfigStatus(paths, mcpEntry, managerConfigured, subagentsConfigured);
23
- const status = rollupProviderHealth([configStatus, ...tools.map((tool) => (tool.present ? 'pass' : 'fail'))]);
29
+ const status = rollupProviderHealth([canonicalAgentManifest.status, configStatus, ...tools.map((tool) => (tool.present ? 'pass' : 'fail'))]);
24
30
  const sdd = normalized.change.length > 0 ? this.readSdd(normalized.project, normalized.change) : undefined;
25
31
  const generatedAt = 'read-only-snapshot';
26
32
  const checkedPaths = normalized.payloadMode === 'verbose'
@@ -28,17 +34,19 @@ export class ProviderStatusService {
28
34
  : paths.filter((path) => path.exists || path.status !== 'not-configured').map((path) => path.path);
29
35
  const verboseShape = {
30
36
  config: { status: configStatus, paths, mcpEntry },
37
+ canonicalAgentManifest,
31
38
  sdd,
32
39
  mcpRequiredTools: tools,
33
40
  };
34
41
  const compactShape = {
35
42
  config: { status: configStatus, paths: compactPaths(paths, 'compact'), mcpEntry: compactMcpEntry(mcpEntry, 'compact') },
43
+ canonicalAgentManifest,
36
44
  sdd: sdd === undefined ? undefined : compactSdd(sdd, 'compact'),
37
45
  mcpRequiredTools: tools,
38
46
  };
39
47
  const originalBytes = Buffer.byteLength(JSON.stringify(verboseShape), 'utf8');
40
48
  const compactBytes = Buffer.byteLength(JSON.stringify(compactShape), 'utf8');
41
- const issueCount = [configStatus, ...tools.map((tool) => (tool.present ? 'pass' : 'fail'))].filter((item) => item === 'fail' || item === 'not-configured').length;
49
+ const issueCount = [canonicalAgentManifest.status, configStatus, ...tools.map((tool) => (tool.present ? 'pass' : 'fail'))].filter((item) => item === 'fail' || item === 'not-configured').length;
42
50
  const warningCount = [configStatus, mcpEntry.status, ...paths.map((path) => path.status)].filter((item) => item === 'warn').length;
43
51
  return {
44
52
  ok: true,
@@ -58,6 +66,7 @@ export class ProviderStatusService {
58
66
  summary: summarizeStatus(status, mcpEntry),
59
67
  nextAction: nextActionFor(status, mcpEntry, sdd?.next),
60
68
  checkedPaths,
69
+ canonicalAgentManifest,
61
70
  config: { status: configStatus, paths: compactPaths(paths, normalized.payloadMode), mcpEntry: compactMcpEntry(mcpEntry, normalized.payloadMode) },
62
71
  ...(sdd === undefined ? {} : { sdd: compactSdd(sdd, normalized.payloadMode) }),
63
72
  mcpRequiredTools: tools,
@@ -77,6 +86,68 @@ export class ProviderStatusService {
77
86
  const next = this.deps.sdd.getNext({ project, change });
78
87
  return { change, ...(status.ok ? { status: status.value } : {}), ...(next.ok ? { next: next.value } : {}) };
79
88
  }
89
+ getClaudeStatus(normalized) {
90
+ const canonicalAgentManifest = (this.deps.canonicalAgentManifestDiagnostic ?? buildCanonicalAgentManifestDiagnostic)();
91
+ const mcpState = inspectClaudeCodeMcpConfig(normalized.workspaceRoot);
92
+ const agents = inspectClaudeCodeAgents(normalized.workspaceRoot);
93
+ const paths = [claudeMcpConfigPathStatus(mcpState)];
94
+ const mcpEntry = claudeMcpEntryStatus(mcpState);
95
+ const agentStatuses = agents.agents.map((agent) => (agent.status === 'managed' ? 'pass' : agent.status === 'missing' ? 'not-configured' : 'fail'));
96
+ const advisory = claudeAdvisoryPaths(normalized.workspaceRoot).filter((path) => existsSync(path));
97
+ const configStatus = claudeConfigHealthStatus([paths[0]?.status ?? 'not-configured', ...agentStatuses]);
98
+ const status = rollupProviderHealth([canonicalAgentManifest.status, configStatus]);
99
+ const sdd = normalized.change.length > 0 ? this.readSdd(normalized.project, normalized.change) : undefined;
100
+ const checkedPaths = normalized.payloadMode === 'verbose' ? [mcpState.path, agents.directoryPath, ...agents.agents.map((agent) => agent.path), ...claudeAdvisoryPaths(normalized.workspaceRoot)] : [mcpState.path, ...agents.agents.filter((agent) => agent.exists || agent.status !== 'missing').map((agent) => agent.path), ...advisory];
101
+ const tools = requiredToolPresence();
102
+ const verboseShape = { config: { status: configStatus, paths, mcpEntry }, canonicalAgentManifest, agents, advisory, sdd, mcpRequiredTools: tools };
103
+ const compactShape = { config: { status: configStatus, paths: compactPaths(paths, 'compact'), mcpEntry: compactMcpEntry(mcpEntry, 'compact') }, canonicalAgentManifest, agentSummary: summarizeClaudeAgents(agents), advisory, sdd: sdd === undefined ? undefined : compactSdd(sdd, 'compact'), mcpRequiredTools: tools };
104
+ const originalBytes = Buffer.byteLength(JSON.stringify(verboseShape), 'utf8');
105
+ const compactBytes = Buffer.byteLength(JSON.stringify(compactShape), 'utf8');
106
+ const issueCount = [canonicalAgentManifest.status, configStatus, ...agentStatuses].filter((item) => item === 'fail' || item === 'not-configured').length;
107
+ const warningCount = advisory.length;
108
+ return {
109
+ ok: true,
110
+ value: {
111
+ version: 1,
112
+ kind: 'provider-status',
113
+ project: normalized.project,
114
+ providerAdapter: 'claude',
115
+ scope: normalized.scope,
116
+ workspaceRoot: normalized.workspaceRoot,
117
+ status,
118
+ payloadMode: normalized.payloadMode,
119
+ overallStatus: status,
120
+ inspectedPaths: checkedPaths,
121
+ issueCount,
122
+ warningCount,
123
+ summary: summarizeClaudeStatus(status, mcpEntry),
124
+ nextAction: nextActionFor(status, mcpEntry, sdd?.next),
125
+ checkedPaths,
126
+ canonicalAgentManifest,
127
+ config: { status: configStatus, paths: compactPaths(paths, normalized.payloadMode), mcpEntry: compactMcpEntry(mcpEntry, normalized.payloadMode) },
128
+ ...(sdd === undefined ? {} : { sdd: compactSdd(sdd, normalized.payloadMode) }),
129
+ mcpRequiredTools: tools,
130
+ originalBytes,
131
+ compactBytes,
132
+ verboseAvailable: normalized.payloadMode === 'compact',
133
+ fullContentRef: `provider-status:claude:${normalized.workspaceRoot}`,
134
+ generatedAt: 'read-only-snapshot',
135
+ safety: PROVIDER_HEALTH_SAFETY,
136
+ },
137
+ };
138
+ }
139
+ }
140
+ function summarizeClaudeAgents(agents) {
141
+ return { expected: agents.agents.length, managed: agents.agents.filter((agent) => agent.status === 'managed').length, missing: agents.agents.filter((agent) => agent.status === 'missing').length, conflicting: agents.agents.filter((agent) => agent.status === 'conflicting' || agent.status === 'invalid').length };
142
+ }
143
+ function claudeConfigHealthStatus(statuses) {
144
+ if (statuses.some((status) => status === 'fail'))
145
+ return 'fail';
146
+ if (statuses.some((status) => status === 'not-configured'))
147
+ return 'not-configured';
148
+ if (statuses.some((status) => status === 'warn'))
149
+ return 'warn';
150
+ return 'pass';
80
151
  }
81
152
  function compactPaths(paths, mode) {
82
153
  if (mode === 'verbose')
@@ -212,6 +283,15 @@ function summarizeStatus(status, mcpEntry) {
212
283
  return 'OpenCode provider config has warnings; no changes were made.';
213
284
  return 'OpenCode provider status check found a blocking issue; no changes were made.';
214
285
  }
286
+ function summarizeClaudeStatus(status, mcpEntry) {
287
+ if (status === 'ready')
288
+ return 'Claude project .mcp.json and VGXNESS agent files are readable and configured.';
289
+ if (status === 'not-configured')
290
+ return mcpEntry.detail;
291
+ if (status === 'warning')
292
+ return 'Claude provider project config has warnings; no changes were made.';
293
+ return 'Claude provider status check found a blocking issue; no changes were made.';
294
+ }
215
295
  function nextActionFor(status, mcpEntry, sddNext) {
216
296
  if (sddNext !== undefined)
217
297
  return { kind: 'advance-sdd', message: 'Continue the next SDD phase inside OpenCode using MCP status/ready/artifact tools and hidden SDD subagents.' };
@@ -430,7 +430,7 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
430
430
  .object({
431
431
  project: z.string().min(1).optional(),
432
432
  scope: z.enum(scopes).optional(),
433
- providerAdapter: z.literal('opencode').optional(),
433
+ providerAdapter: z.enum(['opencode', 'claude']).optional(),
434
434
  workspaceRoot: z.string().min(1).optional(),
435
435
  change: z.string().min(1).optional(),
436
436
  payloadMode: z.enum(payloadModes).optional(),
@@ -440,7 +440,7 @@ export const VGX_MCP_TOOL_INPUT_SCHEMAS = {
440
440
  .object({
441
441
  project: z.string().min(1).optional(),
442
442
  scope: z.enum(scopes).optional(),
443
- providerAdapter: z.literal('opencode').optional(),
443
+ providerAdapter: z.enum(['opencode', 'claude']).optional(),
444
444
  workspaceRoot: z.string().min(1).optional(),
445
445
  expectedPromptContractVersion: z.number().int().positive().optional(),
446
446
  payloadMode: z.enum(payloadModes).optional(),
@@ -187,7 +187,7 @@ function validateProviderHealthInput(input, tool) {
187
187
  return scope;
188
188
  if (scope.value !== undefined)
189
189
  result.scope = scope.value;
190
- const providerAdapter = readOptionalOneOf(record.value, 'providerAdapter', ['opencode'], tool);
190
+ const providerAdapter = readOptionalOneOf(record.value, 'providerAdapter', ['opencode', 'claude'], tool);
191
191
  if (!providerAdapter.ok)
192
192
  return providerAdapter;
193
193
  if (providerAdapter.value !== undefined)
@@ -161,6 +161,10 @@ export class MemoryService {
161
161
  const result = this.artifacts.listByTopicPrefix(project, topicPrefix);
162
162
  return this.record(result, context, { operation: 'artifact.list', targetType: 'artifact', topicKey: topicPrefix });
163
163
  }
164
+ /** Read artifacts for read-only/status projections without writing provenance traces. */
165
+ listArtifactsByTopicPrefixNoTrace(project, topicPrefix) {
166
+ return this.artifacts.listByTopicPrefix(project, topicPrefix);
167
+ }
164
168
  close() {
165
169
  this.database.close();
166
170
  }