vgxness 0.1.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 (121) hide show
  1. package/LICENSE +9 -0
  2. package/README.md +110 -0
  3. package/dist/agents/agent-activation-service.js +144 -0
  4. package/dist/agents/agent-registry-service.js +46 -0
  5. package/dist/agents/agent-resolver.js +249 -0
  6. package/dist/agents/agent-seed-service.js +146 -0
  7. package/dist/agents/manager-profile-overlay-service.js +34 -0
  8. package/dist/agents/profile-model-routing.js +26 -0
  9. package/dist/agents/renderers/claude-renderer.js +98 -0
  10. package/dist/agents/renderers/index.js +16 -0
  11. package/dist/agents/renderers/json-renderer.js +87 -0
  12. package/dist/agents/renderers/opencode-renderer.js +100 -0
  13. package/dist/agents/renderers/provider-adapter.js +6 -0
  14. package/dist/agents/repositories/agents.js +185 -0
  15. package/dist/agents/repositories/manager-profile-overlays.js +81 -0
  16. package/dist/agents/schema.js +1 -0
  17. package/dist/cli/dashboard-operational-read-models.js +153 -0
  18. package/dist/cli/dashboard-renderer.js +109 -0
  19. package/dist/cli/dashboard-screen-renderers.js +332 -0
  20. package/dist/cli/dashboard-tui-read-model.js +71 -0
  21. package/dist/cli/dashboard-tui-state.js +218 -0
  22. package/dist/cli/dispatcher.js +2880 -0
  23. package/dist/cli/index.js +27 -0
  24. package/dist/cli/interactive-dashboard.js +29 -0
  25. package/dist/cli/mcp-start-path.js +21 -0
  26. package/dist/cli/setup-status-renderer.js +29 -0
  27. package/dist/cli/setup-wizard-read-model.js +56 -0
  28. package/dist/cli/setup-wizard-renderer.js +148 -0
  29. package/dist/cli/setup-wizard-state.js +82 -0
  30. package/dist/cli/tui-render-helpers.js +192 -0
  31. package/dist/export/redaction.js +71 -0
  32. package/dist/harness/tools/agents.js +245 -0
  33. package/dist/harness/tools/memory.js +29 -0
  34. package/dist/mcp/client-install-opencode-contract.js +227 -0
  35. package/dist/mcp/client-install-opencode.js +194 -0
  36. package/dist/mcp/client-setup-preview.js +38 -0
  37. package/dist/mcp/control-plane.js +175 -0
  38. package/dist/mcp/doctor.js +193 -0
  39. package/dist/mcp/index.js +10 -0
  40. package/dist/mcp/opencode-default-agent-config.js +156 -0
  41. package/dist/mcp/opencode-visibility.js +102 -0
  42. package/dist/mcp/schema.js +234 -0
  43. package/dist/mcp/stdio-server.js +56 -0
  44. package/dist/mcp/validation.js +761 -0
  45. package/dist/memory/import/dry-run-planner.js +58 -0
  46. package/dist/memory/import/index.js +3 -0
  47. package/dist/memory/import/observation-writer.js +220 -0
  48. package/dist/memory/import/package.js +178 -0
  49. package/dist/memory/memory-service.js +126 -0
  50. package/dist/memory/repositories/artifacts.js +41 -0
  51. package/dist/memory/repositories/observations.js +133 -0
  52. package/dist/memory/repositories/sessions.js +105 -0
  53. package/dist/memory/repositories/traces.js +58 -0
  54. package/dist/memory/schema.js +1 -0
  55. package/dist/memory/search.js +11 -0
  56. package/dist/memory/sqlite/database.js +97 -0
  57. package/dist/memory/sqlite/migrations/001_initial.sql +128 -0
  58. package/dist/memory/sqlite/migrations/002_observation_revisions.sql +14 -0
  59. package/dist/memory/sqlite/migrations/003_agent_registry.sql +26 -0
  60. package/dist/memory/sqlite/migrations/004_run_runtime.sql +62 -0
  61. package/dist/memory/sqlite/migrations/005_run_approvals.sql +20 -0
  62. package/dist/memory/sqlite/migrations/006_run_operation_attempts.sql +32 -0
  63. package/dist/memory/sqlite/migrations/007_abandoned_operation_attempts.sql +46 -0
  64. package/dist/memory/sqlite/migrations/008_run_execution_plan_events.sql +105 -0
  65. package/dist/memory/sqlite/migrations/009_multiple_operation_attempts.sql +73 -0
  66. package/dist/memory/sqlite/migrations/010_skill_registry.sql +66 -0
  67. package/dist/memory/sqlite/migrations/011_skill_usage_resolution_outcomes.sql +21 -0
  68. package/dist/memory/sqlite/migrations/012_skill_improvement_proposals.sql +37 -0
  69. package/dist/memory/sqlite/migrations/013_skill_evaluation_scenarios.sql +43 -0
  70. package/dist/memory/sqlite/migrations/014_manager_profile_overlays.sql +14 -0
  71. package/dist/memory/storage-paths.js +72 -0
  72. package/dist/orchestrator/natural-language-planner.js +191 -0
  73. package/dist/orchestrator/schema.js +1 -0
  74. package/dist/permissions/index.js +2 -0
  75. package/dist/permissions/policy-evaluator.js +109 -0
  76. package/dist/permissions/schema.js +1 -0
  77. package/dist/providers/opencode/injection-preview.js +134 -0
  78. package/dist/providers/opencode/manager-payload.js +129 -0
  79. package/dist/runs/execution-planning.js +117 -0
  80. package/dist/runs/operation-execution.js +1 -0
  81. package/dist/runs/operation-retry.js +124 -0
  82. package/dist/runs/repositories/runs.js +611 -0
  83. package/dist/runs/run-insights.js +145 -0
  84. package/dist/runs/run-service.js +713 -0
  85. package/dist/runs/run-snapshot-export-service.js +31 -0
  86. package/dist/runs/sandbox-process-execution.js +218 -0
  87. package/dist/runs/sandbox-worktree-planning.js +59 -0
  88. package/dist/runs/schema.js +1 -0
  89. package/dist/sdd/artifact-portability-service.js +118 -0
  90. package/dist/sdd/schema.js +17 -0
  91. package/dist/sdd/sdd-workflow-service.js +217 -0
  92. package/dist/setup/backup-rollback-service.js +76 -0
  93. package/dist/setup/index.js +3 -0
  94. package/dist/setup/providers/antigravity-setup-adapter.js +18 -0
  95. package/dist/setup/providers/claude-setup-adapter.js +30 -0
  96. package/dist/setup/providers/custom-setup-adapter.js +18 -0
  97. package/dist/setup/providers/index.js +6 -0
  98. package/dist/setup/providers/opencode-setup-adapter.js +104 -0
  99. package/dist/setup/providers/provider-setup-adapter.js +15 -0
  100. package/dist/setup/providers/provider-setup-registry.js +11 -0
  101. package/dist/setup/schema.js +1 -0
  102. package/dist/setup/setup-defaults.js +11 -0
  103. package/dist/setup/setup-lifecycle-service.js +175 -0
  104. package/dist/setup/setup-plan.js +105 -0
  105. package/dist/skills/repositories/skill-evaluation-scenarios.js +289 -0
  106. package/dist/skills/repositories/skill-improvement-proposals.js +288 -0
  107. package/dist/skills/repositories/skills.js +430 -0
  108. package/dist/skills/schema.js +1 -0
  109. package/dist/skills/skill-payload.js +94 -0
  110. package/dist/skills/skill-registry-service.js +92 -0
  111. package/dist/skills/skill-resolver.js +191 -0
  112. package/dist/workflows/command-allowlist-adapter.js +70 -0
  113. package/dist/workflows/schema.js +4 -0
  114. package/dist/workflows/workflow-executor.js +345 -0
  115. package/dist/workflows/workflow-registry.js +66 -0
  116. package/docs/architecture.md +698 -0
  117. package/docs/cli.md +741 -0
  118. package/docs/funcionamiento-del-sistema.md +868 -0
  119. package/docs/harness-gap-analysis.md +229 -0
  120. package/docs/prd.md +372 -0
  121. package/package.json +57 -0
@@ -0,0 +1,76 @@
1
+ import { accessSync, constants, copyFileSync, existsSync, mkdirSync, statSync } from 'node:fs';
2
+ import { basename, dirname, join } from 'node:path';
3
+ export function createConfigBackupPath(targetPath, label = 'backup') {
4
+ return `${targetPath}.${label}-${new Date().toISOString().replaceAll(/[-:.]/g, '')}`;
5
+ }
6
+ export function rollbackConfigBackup(input) {
7
+ const backupPath = input.backupPath;
8
+ const readable = validateReadableFile(backupPath, 'backup');
9
+ if (!readable.ok)
10
+ return readable;
11
+ const target = inferRollbackTarget(backupPath);
12
+ if (!target.ok)
13
+ return target;
14
+ try {
15
+ let preRollbackBackupPath;
16
+ if (existsSync(target.value)) {
17
+ const current = validateReadableFile(target.value, 'current target');
18
+ if (!current.ok)
19
+ return current;
20
+ preRollbackBackupPath = createConfigBackupPath(target.value, 'pre-rollback-backup');
21
+ copyFileSync(target.value, preRollbackBackupPath);
22
+ }
23
+ mkdirSync(dirname(target.value), { recursive: true });
24
+ copyFileSync(backupPath, target.value);
25
+ return {
26
+ ok: true,
27
+ value: {
28
+ version: 1,
29
+ kind: 'setup-rollback-result',
30
+ status: 'restored',
31
+ backupPath,
32
+ targetPath: target.value,
33
+ ...(preRollbackBackupPath === undefined ? {} : { preRollbackBackupPath }),
34
+ nextCommands: ['vgx doctor', 'Restart OpenCode and verify the vgxness MCP server is visible.'],
35
+ },
36
+ };
37
+ }
38
+ catch (cause) {
39
+ return { ok: false, error: { code: 'validation_failed', message: `Unable to restore backup ${backupPath} to ${target.value}; original backup was not deleted.`, cause } };
40
+ }
41
+ }
42
+ function validateReadableFile(path, label) {
43
+ try {
44
+ const stat = statSync(path);
45
+ if (!stat.isFile())
46
+ return { ok: false, error: { code: 'validation_failed', message: `${label} path is not a file: ${path}` } };
47
+ accessSync(path, constants.R_OK);
48
+ return { ok: true, value: undefined };
49
+ }
50
+ catch (cause) {
51
+ return { ok: false, error: { code: 'validation_failed', message: `Unable to read ${label} path: ${path}`, cause } };
52
+ }
53
+ }
54
+ function inferRollbackTarget(backupPath) {
55
+ const legacyMarker = '.backup-';
56
+ const legacyIndex = backupPath.lastIndexOf(legacyMarker);
57
+ if (legacyIndex > 0)
58
+ return { ok: true, value: backupPath.slice(0, legacyIndex) };
59
+ const preRollbackMarker = '.pre-rollback-backup-';
60
+ const preRollbackIndex = backupPath.lastIndexOf(preRollbackMarker);
61
+ if (preRollbackIndex > 0)
62
+ return { ok: true, value: backupPath.slice(0, preRollbackIndex) };
63
+ const name = basename(backupPath);
64
+ if (name === 'opencode.json')
65
+ return { ok: true, value: backupPath };
66
+ return {
67
+ ok: false,
68
+ error: {
69
+ code: 'validation_failed',
70
+ message: `Cannot infer rollback target for ${backupPath}. Expected a VGXNESS/OpenCode backup name such as opencode.json.backup-<timestamp>.`,
71
+ },
72
+ };
73
+ }
74
+ export function inferLegacyOpenCodeBackupPath(targetPath, timestamp) {
75
+ return join(dirname(targetPath), `${basename(targetPath)}.backup-${timestamp}`);
76
+ }
@@ -0,0 +1,3 @@
1
+ export * from './schema.js';
2
+ export * from './setup-lifecycle-service.js';
3
+ export * from './providers/index.js';
@@ -0,0 +1,18 @@
1
+ import { noWriteActionSafety } from './provider-setup-adapter.js';
2
+ export const antigravitySetupAdapter = {
3
+ id: 'antigravity',
4
+ displayName: 'Antigravity',
5
+ supportLevel: 'placeholder',
6
+ capabilities: ['manual-guidance'],
7
+ targets: [{ kind: 'manual', label: 'Coming soon manual guidance', writableByDashboard: false }],
8
+ getStatus() {
9
+ return {
10
+ providerId: 'antigravity',
11
+ status: 'coming-soon',
12
+ summary: 'Antigravity setup is a placeholder and is coming soon.',
13
+ evidence: ['No install, doctor, visibility, or write actions are available for Antigravity.'],
14
+ guidance: ['Use this card as a non-mutating placeholder until provider support is implemented.'],
15
+ actions: [{ id: 'antigravity-coming-soon', label: 'Read coming-soon guidance', kind: 'manual-guidance', description: 'Placeholder guidance only; no target path or write action is exposed.', safety: noWriteActionSafety }],
16
+ };
17
+ },
18
+ };
@@ -0,0 +1,30 @@
1
+ import { createMcpClientSetupPreview } from '../../mcp/client-setup-preview.js';
2
+ import { noWriteActionSafety } from './provider-setup-adapter.js';
3
+ export const claudeSetupAdapter = {
4
+ id: 'claude',
5
+ displayName: 'Claude',
6
+ supportLevel: 'preview-only',
7
+ capabilities: ['mcp-preview', 'manual-guidance'],
8
+ targets: [{ kind: 'manual', label: 'Claude MCP config snippet', writableByDashboard: false }],
9
+ getStatus(context) {
10
+ return {
11
+ providerId: 'claude',
12
+ status: 'preview-only',
13
+ summary: 'Claude setup is preview-only with manual MCP guidance.',
14
+ evidence: context.databasePath !== undefined ? ['Claude MCP preview can be generated from the selected database path.'] : ['Claude MCP preview uses a placeholder until a database path is selected.'],
15
+ guidance: ['Copy snippets manually after reviewing them. The dashboard does not install or apply Claude config.'],
16
+ actions: [{ id: 'claude-manual-guidance', label: 'Review Claude MCP snippet', kind: 'manual-guidance', description: 'Manual preview guidance only; no install/apply action is available.', safety: noWriteActionSafety }],
17
+ };
18
+ },
19
+ previewMcp(context) {
20
+ const preview = createMcpClientSetupPreview({ provider: 'claude', databasePath: context.databasePath ?? '<database-path>', ...(context.databasePathSource !== undefined ? { databasePathSource: context.databasePathSource } : {}) });
21
+ return {
22
+ providerId: 'claude',
23
+ label: 'Claude MCP preview',
24
+ readOnly: true,
25
+ writesProviderConfig: false,
26
+ snippets: preview.snippets,
27
+ warnings: preview.warnings,
28
+ };
29
+ },
30
+ };
@@ -0,0 +1,18 @@
1
+ import { noWriteActionSafety } from './provider-setup-adapter.js';
2
+ export const customSetupAdapter = {
3
+ id: 'custom',
4
+ displayName: 'Custom',
5
+ supportLevel: 'extension-point',
6
+ capabilities: ['manual-guidance'],
7
+ targets: [{ kind: 'extension', label: 'Custom provider extension point', writableByDashboard: false }],
8
+ getStatus() {
9
+ return {
10
+ providerId: 'custom',
11
+ status: 'unsupported',
12
+ summary: 'Custom providers are an extension point and default to safe manual guidance.',
13
+ evidence: ['No provider writes, executable calls, or install/apply actions are exposed by default.'],
14
+ guidance: ['Implement a dedicated adapter before exposing provider-specific capabilities.'],
15
+ actions: [{ id: 'custom-extension-guidance', label: 'Review custom provider guidance', kind: 'manual-guidance', description: 'Safe no-write extension guidance.', safety: noWriteActionSafety }],
16
+ };
17
+ },
18
+ };
@@ -0,0 +1,6 @@
1
+ export * from './provider-setup-adapter.js';
2
+ export * from './provider-setup-registry.js';
3
+ export * from './opencode-setup-adapter.js';
4
+ export * from './claude-setup-adapter.js';
5
+ export * from './antigravity-setup-adapter.js';
6
+ export * from './custom-setup-adapter.js';
@@ -0,0 +1,104 @@
1
+ import { planOpenCodeMcpInstall } from '../../mcp/client-install-opencode-contract.js';
2
+ import { createMcpClientSetupPreview } from '../../mcp/client-setup-preview.js';
3
+ import { createOpenCodeMcpVisibilityReport } from '../../mcp/opencode-visibility.js';
4
+ import { externalProviderWriteSafety, noWriteActionSafety } from './provider-setup-adapter.js';
5
+ export const openCodeSetupAdapter = {
6
+ id: 'opencode',
7
+ displayName: 'OpenCode',
8
+ supportLevel: 'supported-primary',
9
+ capabilities: ['mcp-preview', 'mcp-install-plan', 'mcp-install-apply-external', 'agent-preview', 'doctor', 'visibility-check'],
10
+ targets: [
11
+ { kind: 'project-config', label: 'Project OpenCode config', path: '.opencode/opencode.json', writableByDashboard: false },
12
+ { kind: 'user-config', label: 'User OpenCode config', path: '$HOME/.config/opencode/opencode.json', writableByDashboard: false },
13
+ ],
14
+ getStatus(context) {
15
+ const visibility = this.getVisibility?.(context);
16
+ const plan = this.getInstallPlan?.(context);
17
+ return {
18
+ providerId: 'opencode',
19
+ status: visibility?.ready === true ? 'ready' : 'available',
20
+ summary: visibility?.ready === true ? 'OpenCode MCP visibility is ready.' : 'OpenCode is the supported primary provider; install/apply remains external and explicit.',
21
+ evidence: visibility?.evidence ?? ['OpenCode setup preview and install planning are available.'],
22
+ guidance: visibility?.guidance ?? ['Preview setup first, then copy and run external install commands only after review.'],
23
+ actions: plan?.actions ?? [],
24
+ };
25
+ },
26
+ previewMcp(context) {
27
+ const databasePath = databasePathOrPlaceholder(context);
28
+ const preview = createMcpClientSetupPreview({ provider: 'opencode', databasePath, ...(context.databasePathSource !== undefined ? { databasePathSource: context.databasePathSource } : {}) });
29
+ return {
30
+ providerId: 'opencode',
31
+ label: 'OpenCode MCP preview',
32
+ readOnly: true,
33
+ writesProviderConfig: false,
34
+ snippets: preview.snippets,
35
+ warnings: preview.warnings,
36
+ };
37
+ },
38
+ getInstallPlan(context) {
39
+ const databasePath = databasePathOrPlaceholder(context);
40
+ const contract = planOpenCodeMcpInstall({ cwd: context.workspaceRoot, databasePath, ...(context.databasePathSource !== undefined ? { databasePathSource: context.databasePathSource } : {}), ...(context.env !== undefined ? { env: context.env } : {}) });
41
+ const targetPath = 'targetPath' in contract ? contract.targetPath : undefined;
42
+ const actions = contract.status === 'would_install' ? [externalInstallAction(contract.scope, targetPath)] : [];
43
+ return {
44
+ providerId: 'opencode',
45
+ status: contract.status === 'would_install' ? 'available' : 'blocked',
46
+ readOnly: true,
47
+ mutating: false,
48
+ writesProviderConfig: false,
49
+ summary: contract.status === 'would_install' ? `OpenCode ${contract.action} plan is available for external application.` : contract.message,
50
+ ...(targetPath !== undefined ? { targetPath } : {}),
51
+ warnings: contract.warnings,
52
+ actions,
53
+ source: contract,
54
+ };
55
+ },
56
+ getVisibility(context) {
57
+ const report = createOpenCodeMcpVisibilityReport({ cwd: context.workspaceRoot, projectRoot: context.workspaceRoot, env: context.env });
58
+ return {
59
+ providerId: 'opencode',
60
+ readOnly: true,
61
+ writesProviderConfig: false,
62
+ ready: report.ready,
63
+ summary: report.ready ? 'OpenCode can see the vgxness MCP server.' : 'OpenCode MCP visibility is not ready yet.',
64
+ evidence: [
65
+ `Project config exists: ${String(report.targets.project.configExists)}.`,
66
+ `Project config has mcp.vgxness: ${String(report.targets.project.hasVgxnessServer)}.`,
67
+ ],
68
+ guidance: report.guidance,
69
+ source: report,
70
+ };
71
+ },
72
+ };
73
+ function databasePathOrPlaceholder(context) {
74
+ return context.databasePath ?? '<database-path>';
75
+ }
76
+ function externalInstallAction(scope, targetPath) {
77
+ return {
78
+ id: `opencode-mcp-install-${scope}`,
79
+ label: 'Copy external OpenCode install command',
80
+ kind: 'copy-command',
81
+ command: ['vgxness', 'mcp', 'install', 'opencode', '--scope', scope, '--yes'],
82
+ description: 'Copy and run this outside the dashboard only after reviewing the read-only plan.',
83
+ safety: externalProviderWriteSafety(targetPath),
84
+ };
85
+ }
86
+ export function createOpenCodeSetupPreview(context) {
87
+ return openCodeSetupAdapter.previewMcp?.(context) ?? {
88
+ providerId: 'opencode',
89
+ label: 'OpenCode MCP preview unavailable',
90
+ readOnly: true,
91
+ writesProviderConfig: false,
92
+ snippets: [],
93
+ warnings: ['OpenCode MCP preview is unavailable.'],
94
+ };
95
+ }
96
+ export function createOpenCodeManualGuidanceAction() {
97
+ return {
98
+ id: 'opencode-manual-guidance',
99
+ label: 'Review OpenCode manual guidance',
100
+ kind: 'manual-guidance',
101
+ description: 'Review OpenCode setup guidance without writing config.',
102
+ safety: noWriteActionSafety,
103
+ };
104
+ }
@@ -0,0 +1,15 @@
1
+ export const noWriteActionSafety = {
2
+ mutating: false,
3
+ writesProviderConfig: false,
4
+ externalOnly: true,
5
+ requiresExplicitConfirmation: false,
6
+ };
7
+ export function externalProviderWriteSafety(targetPath) {
8
+ return {
9
+ mutating: true,
10
+ writesProviderConfig: true,
11
+ externalOnly: true,
12
+ requiresExplicitConfirmation: true,
13
+ ...(targetPath !== undefined ? { targetPath } : {}),
14
+ };
15
+ }
@@ -0,0 +1,11 @@
1
+ import { antigravitySetupAdapter } from './antigravity-setup-adapter.js';
2
+ import { claudeSetupAdapter } from './claude-setup-adapter.js';
3
+ import { customSetupAdapter } from './custom-setup-adapter.js';
4
+ import { openCodeSetupAdapter } from './opencode-setup-adapter.js';
5
+ export const builtInProviderSetupAdapters = [openCodeSetupAdapter, claudeSetupAdapter, antigravitySetupAdapter, customSetupAdapter];
6
+ export function listProviderSetupAdapters() {
7
+ return builtInProviderSetupAdapters;
8
+ }
9
+ export function getProviderSetupAdapter(id) {
10
+ return builtInProviderSetupAdapters.find((adapter) => adapter.id === id);
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ export const vgxnessSetupDefaults = {
2
+ packageName: 'vgxness',
3
+ minimumNodeMajor: 22,
4
+ defaultProvider: 'opencode',
5
+ primaryProvider: 'opencode',
6
+ defaultDatabaseMode: 'global',
7
+ defaultOpenCodeScope: 'project',
8
+ defaultInstallMode: 'mcp-plus-agents',
9
+ alphaReleaseTag: 'alpha',
10
+ publicLanguage: 'en',
11
+ };
@@ -0,0 +1,175 @@
1
+ import { listProviderSetupAdapters } from './providers/provider-setup-registry.js';
2
+ const agentName = 'vgxness-manager';
3
+ export class SetupLifecycleService {
4
+ dependencies;
5
+ constructor(dependencies) {
6
+ this.dependencies = dependencies;
7
+ }
8
+ getStatus(input) {
9
+ const normalized = { ...input, scope: input.scope ?? 'project' };
10
+ const storeResult = this.dependencies.store(normalized);
11
+ const store = storeStatus(storeResult);
12
+ const project = projectStatus(normalized.project);
13
+ const environment = environmentStatus(normalized.projectRoot, store);
14
+ const mcpInput = store.path === '' ? normalized : { ...normalized, databasePath: store.path };
15
+ const mcp = mcpStatus(this.dependencies.mcp(mcpInput), store.status);
16
+ const defaults = normalized.project === undefined ? deferredDefaultContextStatus(normalized.scope) : defaultContextStatus(this.dependencies.defaultContext({ ...normalized, agentName }), normalized.scope);
17
+ const agents = agentsStatus(defaults, normalized.project !== undefined);
18
+ const providers = providerStatuses(this.dependencies.providers ?? listProviderSetupAdapters(), {
19
+ workspaceRoot: normalized.projectRoot,
20
+ ...(normalized.project === undefined ? {} : { project: normalized.project }),
21
+ ...(store.path === '' ? {} : { databasePath: store.path }),
22
+ });
23
+ const verification = verificationSummary(environment, project, providers, agents, mcp);
24
+ return {
25
+ ok: true,
26
+ value: {
27
+ version: 1,
28
+ kind: 'setup-status',
29
+ ...(input.project === undefined ? {} : { project: input.project }),
30
+ environment,
31
+ projectState: project,
32
+ store,
33
+ mcp,
34
+ defaults,
35
+ providers,
36
+ agents,
37
+ verification,
38
+ nextAction: nextAction(store, mcp, defaults, providers, normalized.project),
39
+ safety: { writesProviderConfig: false, executesProvider: false, localStoreMayBeCreated: true },
40
+ },
41
+ };
42
+ }
43
+ }
44
+ function environmentStatus(workspaceRoot, store) {
45
+ return {
46
+ status: store.status,
47
+ workspaceRoot,
48
+ store,
49
+ summary: store.status === 'ready' ? 'Environment checks are ready.' : `Environment is blocked: ${store.blocker ?? 'local store unavailable'}`,
50
+ };
51
+ }
52
+ function projectStatus(project) {
53
+ if (project === undefined) {
54
+ return {
55
+ status: 'deferred',
56
+ mode: 'none',
57
+ summary: 'No project selected; project-scoped setup checks are deferred, not failed.',
58
+ };
59
+ }
60
+ return {
61
+ status: 'ready',
62
+ mode: 'selected',
63
+ project,
64
+ summary: `Project ${project} is selected for project-scoped setup checks.`,
65
+ };
66
+ }
67
+ function storeStatus(result) {
68
+ if (result.ok)
69
+ return { status: 'ready', path: result.value.path };
70
+ return {
71
+ status: 'blocked',
72
+ path: '',
73
+ blocker: result.error.message,
74
+ recovery: 'Run `npm run cli -- doctor` and verify the configured local memory path is writable.',
75
+ };
76
+ }
77
+ function mcpStatus(result, storeReadiness) {
78
+ if (!result.ok) {
79
+ return { status: 'unknown', evidence: [result.error.message], nextAction: 'Review provider-specific MCP setup guidance.', writesProviderConfig: false };
80
+ }
81
+ if (storeReadiness !== 'ready') {
82
+ return { status: 'unknown', evidence: result.value.evidence, nextAction: 'Review provider-specific MCP setup guidance after the local store is ready.', writesProviderConfig: false };
83
+ }
84
+ return {
85
+ status: result.value.ready ? 'ready' : 'blocked',
86
+ evidence: result.value.evidence,
87
+ ...(result.value.ready ? {} : { nextAction: 'Review provider-specific MCP setup guidance from the provider status collection.' }),
88
+ writesProviderConfig: false,
89
+ };
90
+ }
91
+ function defaultContextStatus(result, scope) {
92
+ if (!result.ok) {
93
+ return {
94
+ status: 'blocked',
95
+ scope,
96
+ agentName,
97
+ blocker: result.error.message,
98
+ nextAction: 'Seed or inspect project agents before running guided workflows.',
99
+ };
100
+ }
101
+ return {
102
+ status: 'ready',
103
+ scope,
104
+ agentName,
105
+ agentId: result.value.agentId,
106
+ providerAdapter: result.value.providerAdapter,
107
+ model: result.value.model,
108
+ ...(result.value.profile === undefined ? {} : { profile: result.value.profile }),
109
+ };
110
+ }
111
+ function deferredDefaultContextStatus(scope) {
112
+ return {
113
+ status: 'unknown',
114
+ scope,
115
+ agentName,
116
+ blocker: 'No project selected; default manager/profile checks are deferred.',
117
+ nextAction: 'Select a project to verify the default manager/profile context.',
118
+ };
119
+ }
120
+ function agentsStatus(defaults, hasProject) {
121
+ return {
122
+ ...defaults,
123
+ projectScoped: true,
124
+ deferred: !hasProject,
125
+ };
126
+ }
127
+ function providerStatuses(adapters, context) {
128
+ return adapters.map((adapter) => {
129
+ const status = adapter.getStatus(context);
130
+ return {
131
+ ...status,
132
+ displayName: adapter.displayName,
133
+ supportLevel: adapter.supportLevel,
134
+ capabilities: adapter.capabilities,
135
+ targets: adapter.targets,
136
+ };
137
+ });
138
+ }
139
+ function verificationSummary(environment, project, providers, agents, mcp) {
140
+ const checks = [
141
+ { id: 'environment', label: 'Environment', status: environment.status, detail: environment.summary },
142
+ { id: 'project', label: 'Project', status: project.status, detail: project.summary },
143
+ { id: 'providers', label: 'Providers', status: providers.some((provider) => provider.supportLevel === 'supported-primary') ? 'ready' : 'unknown', detail: `${providers.length} provider targets are available for read-only setup status.` },
144
+ { id: 'agents', label: 'Agents', status: agents.deferred ? 'deferred' : agents.status, detail: agents.deferred ? agents.nextAction ?? 'Project-scoped agent checks are deferred.' : agents.blocker ?? 'Default manager/profile context checked.' },
145
+ { id: 'verification', label: 'Provider visibility', status: mcp.status, detail: mcp.nextAction ?? mcp.evidence[0] ?? 'Provider visibility check completed.' },
146
+ ];
147
+ const blocking = checks.find((check) => check.status === 'blocked');
148
+ return {
149
+ status: blocking === undefined ? 'ready' : 'blocked',
150
+ summary: blocking === undefined ? 'Setup checks are ready or safely deferred.' : `${blocking.label} is blocked: ${blocking.detail}`,
151
+ checks,
152
+ };
153
+ }
154
+ function nextAction(store, mcp, defaults, providers, project) {
155
+ if (store.status !== 'ready') {
156
+ return { command: 'npm run cli -- doctor', reason: `Local store is blocked: ${store.blocker ?? 'local store is unavailable'}` };
157
+ }
158
+ if (defaults.status !== 'ready') {
159
+ if (defaults.blocker?.startsWith('No project selected') === true) {
160
+ return { command: 'npm run cli -- dashboard interactive --project <project>', reason: defaults.nextAction ?? 'Select a project to verify project-scoped setup checks.' };
161
+ }
162
+ return { command: 'npm run cli -- agents seed --scope project', reason: `Default context is blocked: ${defaults.blocker ?? 'default context is unavailable'}` };
163
+ }
164
+ if (mcp.status !== 'ready') {
165
+ const providerSummary = providers.length === 0 ? 'provider registry returned no setup targets' : `${providers.length} provider setup targets are available for provider-specific guidance`;
166
+ return {
167
+ command: setupStatusCommand(project),
168
+ reason: `Provider MCP status is not ready; review the Providers section for provider-specific preview/install guidance (${providerSummary}).`,
169
+ };
170
+ }
171
+ return { command: 'npm run cli -- runs list', reason: 'Project setup is ready; continue with normal vgxness usage.' };
172
+ }
173
+ function setupStatusCommand(project) {
174
+ return project === undefined ? 'npm run cli -- setup status' : `npm run cli -- setup status --project ${project}`;
175
+ }
@@ -0,0 +1,105 @@
1
+ import { join, resolve } from 'node:path';
2
+ import { resolveMemoryDatabasePath } from '../memory/storage-paths.js';
3
+ import { planOpenCodeMcpInstall } from '../mcp/client-install-opencode-contract.js';
4
+ import { vgxnessSetupDefaults } from './setup-defaults.js';
5
+ export function createSetupPlan(input) {
6
+ const mode = input.databaseMode ?? vgxnessSetupDefaults.defaultDatabaseMode;
7
+ const database = resolveSetupDatabase({ workspaceRoot: input.workspaceRoot, env: input.env, mode, ...(input.databasePath === undefined ? {} : { databasePath: input.databasePath }) });
8
+ if (!database.ok)
9
+ return database;
10
+ const project = input.project?.trim() || basenameFallback(input.workspaceRoot);
11
+ const provider = input.provider ?? vgxnessSetupDefaults.defaultProvider;
12
+ const installMode = input.installMode ?? vgxnessSetupDefaults.defaultInstallMode;
13
+ const scope = input.scope ?? vgxnessSetupDefaults.defaultOpenCodeScope;
14
+ const common = {
15
+ version: 1,
16
+ kind: 'setup-plan',
17
+ project,
18
+ workspaceRoot: input.workspaceRoot,
19
+ db: database.value,
20
+ provider,
21
+ safety: { mutating: false, writesProviderConfig: false, requiresConfirmationForApply: true },
22
+ };
23
+ if (provider === 'none') {
24
+ return {
25
+ ok: true,
26
+ value: {
27
+ ...common,
28
+ status: 'ready',
29
+ actions: [{ id: 'manual-provider', description: 'No provider config will be written; use manual MCP configuration if needed.', mutating: false, backupRequired: false }],
30
+ conflicts: [],
31
+ backupsPlanned: [],
32
+ nextCommands: ['vgxness mcp start', 'vgxness setup status'],
33
+ },
34
+ };
35
+ }
36
+ if (provider !== 'opencode')
37
+ return { ok: false, error: { code: 'validation_failed', message: 'Provider must be opencode or none for the alpha setup plan.' } };
38
+ const opencode = planOpenCodeMcpInstall({
39
+ cwd: input.workspaceRoot,
40
+ databasePath: database.value.path,
41
+ databasePathSource: database.value.planSource,
42
+ scope,
43
+ env: input.env,
44
+ mcpOnly: installMode === 'mcp-only',
45
+ });
46
+ return { ok: true, value: setupPlanFromOpenCode({ ...common, provider: 'opencode', installMode, scope, opencode }) };
47
+ }
48
+ function setupPlanFromOpenCode(input) {
49
+ const db = { mode: input.db.mode, path: input.db.path, source: input.db.source };
50
+ if (input.opencode.status === 'refused') {
51
+ return {
52
+ ...input,
53
+ db,
54
+ status: input.opencode.reason === 'unsupported_jsonc' ? 'manual-required' : 'conflict',
55
+ opencode: { scope: input.scope, action: 'none', ...(input.opencode.targetPath === undefined ? {} : { targetPath: input.opencode.targetPath }), installsAgents: input.opencode.installsAgents, agentNames: input.opencode.agentNames },
56
+ actions: [],
57
+ conflicts: [{ id: input.opencode.reason, severity: 'blocking', message: input.opencode.message, ...(input.opencode.targetPath === undefined ? {} : { targetPath: input.opencode.targetPath }), recovery: 'Inspect the OpenCode config, resolve the conflict, then rerun `vgx setup plan`.' }],
58
+ backupsPlanned: [],
59
+ nextCommands: ['vgx setup plan', 'vgxness mcp doctor opencode'],
60
+ };
61
+ }
62
+ return {
63
+ ...input,
64
+ db,
65
+ status: 'ready',
66
+ opencode: {
67
+ scope: input.scope,
68
+ action: input.opencode.action,
69
+ targetPath: input.opencode.targetPath,
70
+ mcpServer: input.opencode.server,
71
+ installsAgents: input.opencode.installsAgents,
72
+ agentNames: input.opencode.agentNames,
73
+ },
74
+ actions: [
75
+ {
76
+ id: `opencode-${input.opencode.action}`,
77
+ description: `${input.opencode.action === 'create' ? 'Create' : 'Merge'} OpenCode config with mcp.vgxness using vgxness mcp start${input.installMode === 'mcp-plus-agents' ? ' and manager/SDD agents' : ''}.`,
78
+ mutating: false,
79
+ targetPath: input.opencode.targetPath,
80
+ backupRequired: input.opencode.backupRequired,
81
+ },
82
+ ],
83
+ conflicts: [],
84
+ backupsPlanned: input.opencode.backupRequired ? [{ targetPath: input.opencode.targetPath, reason: 'Existing OpenCode config would be backed up before merge.' }] : [],
85
+ nextCommands: ['vgx setup apply --yes', 'vgx doctor', 'Restart OpenCode and verify the vgxness MCP server is visible.'],
86
+ };
87
+ }
88
+ function resolveSetupDatabase(input) {
89
+ if (input.mode === 'global') {
90
+ const resolved = resolveMemoryDatabasePath({ cwd: input.workspaceRoot, env: input.env });
91
+ return resolved.ok ? { ok: true, value: { mode: 'global', path: resolved.value.path, source: resolved.value.source, planSource: resolved.value.source } } : resolved;
92
+ }
93
+ if (input.mode === 'project-local') {
94
+ const path = join(input.workspaceRoot, '.vgx', 'memory.sqlite');
95
+ return { ok: true, value: { mode: 'project-local', path, source: 'project-local', planSource: 'flag' } };
96
+ }
97
+ if (input.databasePath === undefined || input.databasePath.trim().length === 0)
98
+ return { ok: false, error: { code: 'validation_failed', message: '--db custom requires --db-path <path>.' } };
99
+ return { ok: true, value: { mode: 'custom', path: resolve(input.workspaceRoot, input.databasePath), source: 'custom', planSource: 'flag' } };
100
+ }
101
+ function basenameFallback(path) {
102
+ const normalized = path.replace(/[\\/]+$/, '');
103
+ const name = normalized.split(/[\\/]/).pop();
104
+ return name === undefined || name.length === 0 ? 'vgxness' : name;
105
+ }