vgxness 1.2.0 → 1.3.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 (70) hide show
  1. package/README.md +7 -6
  2. package/dist/cli/cli-help.js +8 -7
  3. package/dist/cli/commands/index.js +1 -1
  4. package/dist/cli/commands/interactive-entrypoint-dispatcher.js +150 -0
  5. package/dist/cli/commands/setup-dispatcher.js +7 -4
  6. package/dist/cli/dispatcher.js +10 -8
  7. package/dist/cli/index.js +0 -0
  8. package/dist/cli/setup-wizard-renderer.js +1 -1
  9. package/dist/cli/tui/main-menu/index.js +0 -1
  10. package/dist/cli/tui/main-menu/main-menu-controller.js +0 -2
  11. package/dist/cli/tui/main-menu/main-menu-read-model.js +2 -8
  12. package/dist/cli/tui/main-menu/main-menu-render-shape.js +5 -1
  13. package/dist/cli/tui/main-menu/main-menu-state.js +1 -1
  14. package/dist/cli/tui/opentui/code/index.js +210 -0
  15. package/dist/cli/tui/opentui/code/screen.js +107 -0
  16. package/dist/cli/tui/opentui/code/smoke.js +32 -0
  17. package/dist/cli/tui/opentui/main-menu/index.js +3 -0
  18. package/dist/cli/tui/opentui/main-menu/renderer.js +68 -0
  19. package/dist/cli/tui/opentui/main-menu/screen.js +62 -0
  20. package/dist/cli/tui/opentui/main-menu/smoke.js +17 -0
  21. package/dist/cli/tui/opentui/main-menu/view.js +8 -0
  22. package/dist/cli/tui/opentui/setup/index.js +3 -0
  23. package/dist/cli/tui/opentui/setup/renderer.js +87 -0
  24. package/dist/cli/tui/opentui/setup/screen.js +170 -0
  25. package/dist/cli/tui/opentui/setup/smoke.js +42 -0
  26. package/dist/cli/tui/opentui/setup/view.js +12 -0
  27. package/dist/cli/tui/setup/setup-tui-input.js +43 -0
  28. package/dist/cli/tui/setup/setup-tui-read-model.js +1 -1
  29. package/dist/cli/tui/setup/setup-tui-render-shape.js +1 -1
  30. package/dist/cli/tui/setup/setup-tui-view-helpers.js +46 -0
  31. package/dist/cli/tui/visual/index.js +0 -2
  32. package/dist/code/tui/approval-actions.js +33 -0
  33. package/dist/code/tui/prompt-mode.js +11 -0
  34. package/dist/code/tui/runtime-events.js +320 -0
  35. package/dist/sdd/sdd-workflow-service.js +0 -24
  36. package/dist/setup/providers/antigravity-setup-adapter.js +1 -1
  37. package/dist/setup/providers/claude-setup-adapter.js +2 -2
  38. package/dist/setup/providers/custom-setup-adapter.js +1 -1
  39. package/dist/setup/providers/opencode-setup-adapter.js +3 -3
  40. package/dist/setup/setup-lifecycle-service.js +1 -1
  41. package/docs/architecture.md +4 -4
  42. package/docs/cli.md +11 -10
  43. package/docs/funcionamiento-del-sistema.md +6 -7
  44. package/docs/prd.md +4 -4
  45. package/docs/vgxcode.md +76 -0
  46. package/package.json +5 -6
  47. package/dist/cli/commands/dashboard-dispatcher.js +0 -560
  48. package/dist/cli/dashboard-operational-read-models.js +0 -428
  49. package/dist/cli/dashboard-renderer.js +0 -158
  50. package/dist/cli/dashboard-screen-renderers.js +0 -256
  51. package/dist/cli/dashboard-tui-read-model.js +0 -73
  52. package/dist/cli/dashboard-tui-state.js +0 -314
  53. package/dist/cli/interactive-dashboard.js +0 -34
  54. package/dist/cli/tui/dashboard/dashboard-adapter.js +0 -4
  55. package/dist/cli/tui/main-menu/main-menu-app.js +0 -28
  56. package/dist/cli/tui/render-ink-app.js +0 -10
  57. package/dist/cli/tui/setup/screens/applying-screen.js +0 -6
  58. package/dist/cli/tui/setup/screens/cancellation-screen.js +0 -6
  59. package/dist/cli/tui/setup/screens/error-recovery-screen.js +0 -6
  60. package/dist/cli/tui/setup/screens/final-confirmation-screen.js +0 -6
  61. package/dist/cli/tui/setup/screens/opencode-details-screen.js +0 -10
  62. package/dist/cli/tui/setup/screens/plan-review-screen.js +0 -6
  63. package/dist/cli/tui/setup/screens/project-database-screen.js +0 -6
  64. package/dist/cli/tui/setup/screens/provider-screen.js +0 -7
  65. package/dist/cli/tui/setup/screens/result-screen.js +0 -16
  66. package/dist/cli/tui/setup/screens/screen-components.js +0 -103
  67. package/dist/cli/tui/setup/screens/welcome-screen.js +0 -6
  68. package/dist/cli/tui/setup/setup-tui-app.js +0 -113
  69. package/dist/cli/tui/visual/choice-list.js +0 -10
  70. package/dist/cli/tui/visual/layout.js +0 -10
package/README.md CHANGED
@@ -6,7 +6,7 @@ VGXNESS is an installable CLI and MCP control plane for guided AI-agent workflow
6
6
 
7
7
  This package is proprietary software. The npm package ships inspectable JavaScript (`dist/`) so Node can run it, but it is **not open-source licensed** and may not be redistributed unless you have written permission. See [LICENSE](./LICENSE).
8
8
 
9
- OpenCode is the primary supported provider. Other providers remain preview/manual only. The dashboard is read-only/copy-only; provider config writes require explicit CLI confirmation.
9
+ OpenCode is the primary supported provider. Other providers remain preview/manual only. Provider config writes require explicit CLI confirmation.
10
10
 
11
11
  ## Requirements
12
12
 
@@ -162,19 +162,20 @@ vgx setup apply --yes
162
162
 
163
163
  - Preview, status, and plan commands are read-only.
164
164
  - Provider config writes require explicit `--yes` confirmation.
165
- - The dashboard is read-only/copy-only; run copied commands outside the TUI when you choose to act.
165
+ - Setup/status TUI surfaces are preview-oriented; run copied commands explicitly when you choose to act.
166
166
  - SDD artifacts are SQLite-backed through VGXNESS services. Do not create or write `openspec/`.
167
167
  - `vgx sdd accept-artifact` records explicit human-only acceptance; saving a draft never implies acceptance.
168
168
  - OpenCode is the primary supported provider; other providers are preview/manual extension points.
169
169
 
170
- ## Dashboard entrypoint
170
+ ## Main menu entrypoint
171
171
 
172
- Run `vgx` or `vgxness` with no arguments in an interactive terminal to open the operational dashboard. `vgx dashboard interactive` opens the same read-only/copy-only surface. In non-TTY shells, no-args prints static safe guidance and exits 0 without reading dashboard status or inferring project state.
172
+ Run `vgx` or `vgxness` with no arguments in an interactive terminal to open the OpenTUI main menu. In non-TTY shells, no-args prints static safe setup guidance and exits 0 without inferring project state.
173
173
 
174
- For scripts, use the read-only status renderer with an explicit project:
174
+ For scripts, use explicit read-only commands with an explicit project:
175
175
 
176
176
  ```bash
177
- vgx dashboard status --project <name>
177
+ vgx setup status --project <name>
178
+ vgx sdd status --project <name> --change <change>
178
179
  ```
179
180
 
180
181
  For project-local or custom databases, use `--db project-local` or `--db custom --db-path <path>` with setup commands. Existing low-level commands remain available:
@@ -1,6 +1,10 @@
1
1
  export function helpText() {
2
2
  return `Usage: vgxness <area> <command> [flags]
3
3
 
4
+ Global flags:
5
+ --help, -h Show this help.
6
+ --version, -v Print the installed package version.
7
+
4
8
  Areas:
5
9
  init [--project <name>] [--provider opencode|none] [--scope user|project] [--db global|project-local|custom|<path>] [--db-path <path>] [--mode mcp-only|mcp-plus-agents] [--json]
6
10
  setup plan [--project <name>] [--provider opencode|none] [--scope user|project] [--db global|project-local|custom|<path>] [--db-path <path>] [--mode mcp-only|mcp-plus-agents] [--json]
@@ -87,14 +91,11 @@ Areas:
87
91
  subagents list --parent-agent-id <id>
88
92
  subagents get --id <id> | --project <name> --name <name> [--scope project|personal]
89
93
 
90
- dashboard status --project <name> [--change <id>] [--run-id <id>] [--limit <n>]
91
- dashboard interactive [--project <name>] [--change <id>] [--limit <n>]
92
- No args in an interactive TTY opens the same operational dashboard as dashboard interactive.
93
- No args without a TTY prints static safe guidance and exits 0 without reading dashboard status or opening project state.
94
- Dashboard menu: Installation, Status, Agents, Skills, Memory, SDD, Runs, Approvals, Permissions, Settings. Menu: ↑/↓ or j/k, Enter, 1-9/0. Section: b/Esc/Backspace back, r, ?, q.
95
- Dashboardinteractive may launch without --project; Installation remains available and project-scoped checks are deferred while project screens render project-required recovery states.
94
+ No args in an interactive TTY opens the OpenTUI main menu.
95
+ No args without a TTY prints static safe setup guidance and exits 0 without opening project state.
96
+ Setup TUI may launch without --project; Installation remains available and project-scoped checks are deferred while project screens render project-required recovery states.
96
97
  Provider setup support: OpenCode supported primary; Claude preview-only; Antigravity placeholder; Custom/future extension point.
97
- Dashboard status is scriptable/read-only and requires --project. Provider config writes/install/apply are external-only, require explicit confirmation outside the dashboard, and are not run by dashboard flows.
98
+ Provider config writes/install/apply are external-only and require explicit confirmation.
98
99
 
99
100
  sdd status --project <name> --change <id> [--json]
100
101
  sdd next --project <name> --change <id> [--json]
@@ -1,5 +1,5 @@
1
1
  export { runAgentCommand, runSkillCommand, runSubagentCommand } from './agent-skill-dispatcher.js';
2
- export { runCodeCliCommand, runDashboardCommand, runDashboardInteractiveCommand, runDefaultInteractiveEntrypoint } from './dashboard-dispatcher.js';
2
+ export { runCodeCliCommand, runDefaultInteractiveEntrypoint } from './interactive-entrypoint-dispatcher.js';
3
3
  export { runDoctorAliasCommand, runMcpDoctorCommand, runMcpDoctorOpenCodeCommand, runMcpInstallCommand, runMcpSetupCommand } from './mcp-dispatcher.js';
4
4
  export { runMemoryCommand, runMemoryImportCommand, runOpenCodeCommand, runOrchestratorCommand, runSddCommand } from './memory-sdd-dispatcher.js';
5
5
  export { runApprovalsCommand, runPermissionsCommand, runRunsCommand } from './run-permission-dispatcher.js';
@@ -0,0 +1,150 @@
1
+ import { runCodeCommand } from '../../code/cli/code-command.js';
2
+ import { InMemoryRunGateway } from '../../code/runtime/gateways.js';
3
+ import { MemoryServiceCodeGateway } from '../../code/runtime/memory-service-gateway.js';
4
+ import { RunsCodeRunGateway } from '../../code/runtime/runs-code-run-gateway.js';
5
+ import { SddWorkflowGateway } from '../../code/runtime/sdd-workflow-gateway.js';
6
+ import { MemoryService } from '../../memory/memory-service.js';
7
+ import { RunService } from '../../runs/run-service.js';
8
+ import { SddWorkflowService } from '../../sdd/sdd-workflow-service.js';
9
+ import { codeApprovalPolicyFlag, codeMemoryPolicyFlag, codeTranscriptModeFlag, codeVerificationModeFlag, databasePathFor, optionalNumberFlag, optionalStringFlag, } from '../cli-flags.js';
10
+ import { okText, usageFailure, validationFailure } from '../cli-help.js';
11
+ import { openCliDatabase, resultFailure } from '../cli-helpers.js';
12
+ import { canRunInteractiveTui } from '../tui/terminal-capabilities.js';
13
+ import { runSetupTuiCommand } from './setup-dispatcher.js';
14
+ function defaultNoTtyGuidance() {
15
+ return ([
16
+ 'VGXNESS main menu requires a TTY; no provider config was written.',
17
+ 'Next: rerun `vgx` in an interactive terminal, run `vgx init --plan`, or run `vgx setup plan` for read-only installation guidance.',
18
+ ].join('\n') + '\n');
19
+ }
20
+ function guidanceForMainMenuResult(result) {
21
+ if (result.type === 'show-doctor-guidance')
22
+ return ['Doctor / recovery guidance:', '- Read-only status: `vgx setup status`', '- Provider doctor: `vgx mcp doctor opencode`', '- No provider config was written.'].join('\n') + '\n';
23
+ if (result.type === 'show-sdd-guidance')
24
+ return ['SDD / workflow guidance:', '- Use VGXNESS MCP tools from OpenCode for normal SDD progression.', '- Manual status: `vgx sdd status --project <name> --change <change>`', '- No provider config was written.'].join('\n') + '\n';
25
+ if (result.type === 'show-advanced-cli')
26
+ return ['Advanced CLI guidance:', '- Installation preview: `vgx init --plan` or `vgx setup plan`', '- Setup status: `vgx setup status --project <name>`', '- SDD status: `vgx sdd status --project <name> --change <change>`', '- No provider config was written.'].join('\n') + '\n';
27
+ if (result.type === 'exit')
28
+ return 'Exited main menu; no provider config was written.\n';
29
+ return undefined;
30
+ }
31
+ async function renderDefaultMainMenu(environment, onResult) {
32
+ const { renderOpenTuiMainMenu } = await import('../tui/opentui/main-menu/renderer.js');
33
+ await renderOpenTuiMainMenu({
34
+ stdin: environment.stdin,
35
+ stdout: environment.stdout,
36
+ onResult,
37
+ });
38
+ }
39
+ export async function runDefaultInteractiveEntrypointWithMainMenu(environment, input = {}) {
40
+ if (!canRunInteractiveTui(environment.stdin, environment.stdout))
41
+ return okText(defaultNoTtyGuidance());
42
+ let selected;
43
+ await (input.renderMainMenu ?? ((onResult) => renderDefaultMainMenu(environment, onResult)))((result) => {
44
+ selected = result;
45
+ });
46
+ const result = selected ?? { type: 'exit' };
47
+ if (result.type === 'open-setup')
48
+ return (input.setupTui ?? runSetupTuiCommand)(environment);
49
+ return okText(guidanceForMainMenuResult(result) ?? 'No provider config was written.\n');
50
+ }
51
+ function codeApprovalChannelFlag(flags) {
52
+ const value = optionalStringFlag(flags, 'approval-channel');
53
+ if (value === undefined)
54
+ return { ok: true, value: undefined };
55
+ return value === 'stdio' ? { ok: true, value } : validationFailure('--approval-channel must be stdio');
56
+ }
57
+ export async function runDefaultInteractiveEntrypoint(environment) {
58
+ return runDefaultInteractiveEntrypointWithMainMenu(environment);
59
+ }
60
+ export async function runCodeCliCommand(parsed, environment) {
61
+ const [, command] = parsed.positionals;
62
+ if (command !== 'inspect' && command !== 'plan' && command !== 'craft' && command !== 'craft-preview' && command !== 'sdd')
63
+ return usageFailure(`Unknown code command: ${command ?? ''}`.trim());
64
+ const eventsJsonl = parsed.flags['events-jsonl'] === true;
65
+ const approvalChannel = codeApprovalChannelFlag(parsed.flags);
66
+ if (!approvalChannel.ok)
67
+ return resultFailure(approvalChannel);
68
+ if (approvalChannel.value === 'stdio' && (command !== 'craft' || !eventsJsonl))
69
+ return usageFailure('--approval-channel stdio is supported only for code craft --events-jsonl');
70
+ if (eventsJsonl && command !== 'inspect' && command !== 'plan' && command !== 'craft-preview' && approvalChannel.value !== 'stdio')
71
+ return usageFailure('code craft --events-jsonl requires --approval-channel stdio; JSONL without approvals is currently supported only for read-only inspect, plan, and craft-preview');
72
+ const maxSourceBytes = optionalNumberFlag(parsed.flags, 'max-source-bytes');
73
+ if (!maxSourceBytes.ok)
74
+ return resultFailure(maxSourceBytes);
75
+ const output = parsed.flags.json === true || optionalStringFlag(parsed.flags, 'output') === 'json' ? 'json' : 'human';
76
+ const approvalPolicy = codeApprovalPolicyFlag(parsed.flags);
77
+ if (!approvalPolicy.ok)
78
+ return resultFailure(approvalPolicy);
79
+ const verificationMode = codeVerificationModeFlag(parsed.flags);
80
+ if (!verificationMode.ok)
81
+ return resultFailure(verificationMode);
82
+ const transcriptMode = codeTranscriptModeFlag(parsed.flags);
83
+ if (!transcriptMode.ok)
84
+ return resultFailure(transcriptMode);
85
+ const memoryPolicy = codeMemoryPolicyFlag(parsed.flags);
86
+ if (!memoryPolicy.ok)
87
+ return resultFailure(memoryPolicy);
88
+ const provider = optionalStringFlag(parsed.flags, 'provider');
89
+ const model = optionalStringFlag(parsed.flags, 'model');
90
+ if (eventsJsonl) {
91
+ if (approvalChannel.value === 'stdio' && (environment.stdin === undefined || environment.stdout === undefined))
92
+ return resultFailure(validationFailure('--approval-channel stdio requires CLI stdin and stdout streams'));
93
+ return await runCodeCommand({
94
+ command,
95
+ args: parsed.positionals.slice(2),
96
+ cwd: environment.cwd,
97
+ output,
98
+ runGateway: new InMemoryRunGateway(),
99
+ project: optionalStringFlag(parsed.flags, 'project') ?? 'vgxness',
100
+ ...(provider === undefined ? {} : { provider }),
101
+ ...(model === undefined ? {} : { model }),
102
+ stream: parsed.flags.stream === true,
103
+ env: environment.env,
104
+ eventsJsonl,
105
+ memoryPolicy: 'off',
106
+ ...(approvalChannel.value === undefined ? {} : { approvalChannel: approvalChannel.value }),
107
+ ...(approvalChannel.value === 'stdio' ? { approvalDecisionInput: environment.stdin, eventOutput: environment.stdout } : {}),
108
+ ...(maxSourceBytes.value !== undefined ? { maxSourceBytes: maxSourceBytes.value } : {}),
109
+ ...(approvalPolicy.value === undefined ? {} : { approvalPolicy: approvalPolicy.value }),
110
+ ...(verificationMode.value === undefined ? {} : { verificationMode: verificationMode.value }),
111
+ ...(transcriptMode.value === undefined ? {} : { transcriptMode: transcriptMode.value }),
112
+ });
113
+ }
114
+ const selectedDatabasePath = databasePathFor(parsed.flags, environment);
115
+ if (!selectedDatabasePath.ok)
116
+ return resultFailure(selectedDatabasePath);
117
+ const opened = openCliDatabase(selectedDatabasePath.value);
118
+ if (!opened.ok)
119
+ return resultFailure(opened);
120
+ try {
121
+ const runs = new RunService(opened.value);
122
+ const memory = new MemoryService(opened.value);
123
+ return await runCodeCommand({
124
+ command,
125
+ args: parsed.positionals.slice(2),
126
+ cwd: environment.cwd,
127
+ output,
128
+ runGateway: new RunsCodeRunGateway(runs),
129
+ sddGateway: new SddWorkflowGateway(new SddWorkflowService(memory)),
130
+ memoryGateway: new MemoryServiceCodeGateway(memory),
131
+ project: optionalStringFlag(parsed.flags, 'project') ?? 'vgxness',
132
+ ...(provider === undefined ? {} : { provider }),
133
+ ...(model === undefined ? {} : { model }),
134
+ stream: parsed.flags.stream === true,
135
+ env: environment.env,
136
+ eventsJsonl,
137
+ persistArtifact: parsed.flags['save-artifact'] === true || parsed.flags.persist === true,
138
+ ...(maxSourceBytes.value !== undefined ? { maxSourceBytes: maxSourceBytes.value } : {}),
139
+ ...(approvalPolicy.value === undefined ? {} : { approvalPolicy: approvalPolicy.value }),
140
+ ...(verificationMode.value === undefined ? {} : { verificationMode: verificationMode.value }),
141
+ ...(transcriptMode.value === undefined ? {} : { transcriptMode: transcriptMode.value }),
142
+ ...(memoryPolicy.value === undefined ? {} : { memoryPolicy: memoryPolicy.value }),
143
+ });
144
+ }
145
+ finally {
146
+ opened.value.close();
147
+ }
148
+ }
149
+ // Re-export helpers
150
+ export { defaultNoTtyGuidance, };
@@ -1,5 +1,4 @@
1
1
  import { resolve } from 'node:path';
2
- import React from 'react';
3
2
  import { AgentRegistryService } from '../../agents/agent-registry-service.js';
4
3
  import { resolveAgentProfileModel } from '../../agents/profile-model-routing.js';
5
4
  import { installOpenCodeMcpClient } from '../../mcp/client-install-opencode.js';
@@ -14,8 +13,6 @@ import { okText, usageFailure, validationFailure } from '../cli-help.js';
14
13
  import { jsonResult, openCliDatabase, resultFailure } from '../cli-helpers.js';
15
14
  import { renderSetupPlan } from '../setup-plan-renderer.js';
16
15
  import { renderSetupStatus } from '../setup-status-renderer.js';
17
- import { renderInkApp } from '../tui/render-ink-app.js';
18
- import { SetupTuiApp } from '../tui/setup/setup-tui-app.js';
19
16
  import { createDefaultSetupTuiServices } from '../tui/setup/setup-tui-services.js';
20
17
  import { canRunInteractiveTui } from '../tui/terminal-capabilities.js';
21
18
  function renderSetupRollbackApply(result, parsed) {
@@ -361,7 +358,13 @@ export async function runSetupTuiCommand(environment, input) {
361
358
  },
362
359
  };
363
360
  try {
364
- await renderInkApp(React.createElement(SetupTuiApp, { services: createDefaultSetupTuiServices({ lifecycle, cwd: environment.cwd, env: environment.env }), runtime }), { stdin: environment.stdin, stdout: environment.stdout });
361
+ const { renderOpenTuiSetup } = await import('../tui/opentui/setup/index.js');
362
+ await renderOpenTuiSetup({
363
+ services: createDefaultSetupTuiServices({ lifecycle, cwd: environment.cwd, env: environment.env }),
364
+ runtime,
365
+ stdin: environment.stdin,
366
+ stdout: environment.stdout,
367
+ });
365
368
  return { exitCode: 0, stdout: '', stderr: '' };
366
369
  }
367
370
  finally {
@@ -1,11 +1,17 @@
1
1
  // Thin CLI router -- all command handlers live in src/cli/commands/*.ts
2
+ import { createRequire } from 'node:module';
2
3
  import { isWorkflowId } from '../workflows/schema.js';
3
4
  import { databasePathFor, parseArgs, requiredFlag } from './cli-flags.js';
4
5
  import { okText, usageFailure, visibleHelpText } from './cli-help.js';
5
6
  import { openCliDatabase, resultFailure } from './cli-helpers.js';
6
- import { runAgentCommand, runApprovalsCommand, runCodeCliCommand, runDashboardCommand, runDashboardInteractiveCommand, runDefaultInteractiveEntrypoint, runDoctorAliasCommand, runInitCommand, runMcpDoctorCommand, runMcpInstallCommand, runMcpSetupCommand, runMemoryCommand, runMemoryImportCommand, runOpenCodeCommand, runOrchestratorCommand, runPermissionsCommand, runRunsCommand, runSddCommand, runSetupApplyCommand, runSetupLifecycleCommand, runSetupPlanCommand, runSetupRollbackCommand, runSkillCommand, runSubagentCommand, runVerificationPlanCommand, runVerificationReportCommand, runWorkflowExecuteCommand, runWorkflowPreviewCommand, runWorkflowRunCommand, } from './commands/index.js';
7
+ import { runAgentCommand, runApprovalsCommand, runCodeCliCommand, runDefaultInteractiveEntrypoint, runDoctorAliasCommand, runInitCommand, runMcpDoctorCommand, runMcpInstallCommand, runMcpSetupCommand, runMemoryCommand, runMemoryImportCommand, runOpenCodeCommand, runOrchestratorCommand, runPermissionsCommand, runRunsCommand, runSddCommand, runSetupApplyCommand, runSetupLifecycleCommand, runSetupPlanCommand, runSetupRollbackCommand, runSkillCommand, runSubagentCommand, runVerificationPlanCommand, runVerificationReportCommand, runWorkflowExecuteCommand, runWorkflowPreviewCommand, runWorkflowRunCommand, } from './commands/index.js';
7
8
  const _promptBuffers = new WeakMap();
9
+ const require = createRequire(import.meta.url);
10
+ const packageJson = require('../../package.json');
11
+ const cliVersion = typeof packageJson.version === 'string' ? packageJson.version : 'unknown';
8
12
  export function dispatchCli(argv, environment) {
13
+ if (isGlobalVersionRequest(argv))
14
+ return okText(`${cliVersion}\n`);
9
15
  const parsed = parseArgs(argv);
10
16
  const [area, command] = parsed.positionals;
11
17
  if (!area || area === 'help' || area === '--help' || area === '-h')
@@ -69,21 +75,20 @@ export function dispatchCli(argv, environment) {
69
75
  return runOrchestratorCommand(command, parsed, opened.value);
70
76
  if (area === 'opencode')
71
77
  return runOpenCodeCommand(command, parsed, opened.value, environment);
72
- if (area === 'dashboard')
73
- return runDashboardCommand(command, parsed, opened.value, environment, databasePath);
74
78
  return usageFailure(`Unknown command area: ${area}`);
75
79
  }
76
80
  finally {
77
81
  opened.value.close();
78
82
  }
79
83
  }
84
+ function isGlobalVersionRequest(argv) {
85
+ return argv.length === 1 && (argv[0] === '--version' || argv[0] === '-v');
86
+ }
80
87
  export async function dispatchCliAsync(argv, environment) {
81
88
  const parsed = parseArgs(argv);
82
89
  const [area, command] = parsed.positionals;
83
90
  if (argv.length === 0)
84
91
  return runDefaultInteractiveEntrypoint(environment);
85
- if (area === 'dashboard' && command === 'interactive')
86
- return runDashboardInteractiveCommand(parsed, environment);
87
92
  if (area === 'mcp') {
88
93
  if (!command)
89
94
  return usageFailure('Missing command for mcp');
@@ -213,9 +218,6 @@ function validateCommand(area, command) {
213
218
  if (area === 'verification') {
214
219
  return command === 'plan' || command === 'report' ? { ok: true } : { ok: false, message: `Unknown verification command: ${command}` };
215
220
  }
216
- if (area === 'dashboard') {
217
- return command === 'status' ? { ok: true } : { ok: false, message: `Unknown dashboard command: ${command}` };
218
- }
219
221
  if (area === 'code') {
220
222
  return command === 'inspect' || command === 'plan' || command === 'craft' || command === 'sdd'
221
223
  ? { ok: true }
package/dist/cli/index.js CHANGED
File without changes
@@ -58,7 +58,7 @@ function renderFocusedProviderDetails(cards, state) {
58
58
  return [];
59
59
  const targets = focusedCard.targets.length === 0
60
60
  ? 'Targets: none'
61
- : `Targets: ${focusedCard.targets.map((target) => `${target.label} (${target.kind}${target.path === undefined ? '' : `: ${target.path}`}; dashboard-write: no; no-TUI-write)`).join('; ')}`;
61
+ : `Targets: ${focusedCard.targets.map((target) => `${target.label} (${target.kind}${target.path === undefined ? '' : `: ${target.path}`}; TUI-write: no)`).join('; ')}`;
62
62
  const safetyLabels = setupSafetyLabels(focusedCard);
63
63
  return [
64
64
  '',
@@ -1,5 +1,4 @@
1
1
  export * from './main-menu-actions.js';
2
- export * from './main-menu-app.js';
3
2
  export * from './main-menu-controller.js';
4
3
  export * from './main-menu-read-model.js';
5
4
  export * from './main-menu-render-shape.js';
@@ -1,8 +1,6 @@
1
1
  export function resultForMainMenuOption(optionId) {
2
2
  if (optionId === 'setup')
3
3
  return { type: 'open-setup' };
4
- if (optionId === 'dashboard')
5
- return { type: 'open-dashboard' };
6
4
  if (optionId === 'doctor')
7
5
  return { type: 'show-doctor-guidance' };
8
6
  if (optionId === 'sdd')
@@ -1,4 +1,5 @@
1
- import { formatTuiFooter, tuiBadges } from '../visual/index.js';
1
+ import { tuiBadges } from '../visual/badges.js';
2
+ import { formatTuiFooter } from '../visual/footer.js';
2
3
  import { mainMenuOptionIds } from './main-menu-state.js';
3
4
  const optionCopy = {
4
5
  setup: {
@@ -12,13 +13,6 @@ const optionCopy = {
12
13
  'Provider writes require final confirmation; OpenCode is the only automatic provider with explicit consent.',
13
14
  ],
14
15
  },
15
- dashboard: {
16
- label: 'Dashboard',
17
- description: 'Inspect project health, runs, approvals, SDD, agents, and skills.',
18
- badges: [tuiBadges.readOnly],
19
- detailTitle: 'Interactive dashboard',
20
- detailLines: ['Opens the existing read-only dashboard.', 'Dashboard navigation does not apply provider config or write OpenCode setup.'],
21
- },
22
16
  doctor: {
23
17
  label: 'Doctor / recovery',
24
18
  description: 'See health-check and recovery commands.',
@@ -1,4 +1,4 @@
1
- import { choiceLine } from '../visual/index.js';
1
+ import { formatBadges, tuiBadges } from '../visual/badges.js';
2
2
  import { buildMainMenuViewModel } from './main-menu-read-model.js';
3
3
  export function renderMainMenuShape(input) {
4
4
  const state = input.width === undefined ? input.state : { ...input.state, viewport: { ...input.state.viewport, width: input.width } };
@@ -27,3 +27,7 @@ function clampLine(line, width) {
27
27
  return `${line.slice(0, Math.max(0, width - phrase.length - 5)).trim()} ... ${phrase}`.trim();
28
28
  return `${line.slice(0, Math.max(0, width - 1)).trim()}…`;
29
29
  }
30
+ function choiceLine(choice) {
31
+ const badges = formatBadges([...(choice.focused === true ? [tuiBadges.focused] : []), ...choice.badges]);
32
+ return `${choice.focused === true ? '›' : ' '} ${choice.label}${badges.length === 0 ? '' : ` ${badges}`} — ${choice.description}`;
33
+ }
@@ -1,5 +1,5 @@
1
1
  import { createTuiViewport } from '../visual/viewport.js';
2
- export const mainMenuOptionIds = ['setup', 'dashboard', 'doctor', 'sdd', 'advanced-cli', 'exit'];
2
+ export const mainMenuOptionIds = ['setup', 'doctor', 'sdd', 'advanced-cli', 'exit'];
3
3
  export function createMainMenuState(input = {}) {
4
4
  return {
5
5
  focusedOptionId: input.focusedOptionId ?? 'setup',
@@ -0,0 +1,210 @@
1
+ import { createCliRenderer } from '@opentui/core';
2
+ import { spawn } from 'node:child_process';
3
+ import { dirname, resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { ApprovalDecisionWriter, getApprovalActionsState } from '../../../../code/tui/approval-actions.js';
6
+ import { buildSafetyReadModel, parseVgxcodeJsonl, parseVgxcodeJsonlLine } from '../../../../code/tui/runtime-events.js';
7
+ import { parsePromptSubmission, toggleReadOnlyMode } from '../../../../code/tui/prompt-mode.js';
8
+ import { createVgxcodeScreen } from './screen.js';
9
+ async function main() {
10
+ const project = process.argv[2] ?? 'vgxness';
11
+ const parsedStdin = await readPipedRuntimeEvents();
12
+ const state = {
13
+ project,
14
+ prompt: '',
15
+ submittedPrompt: '',
16
+ events: [...parsedStdin.events],
17
+ errors: [...parsedStdin.errors],
18
+ status: parsedStdin.errors.length > 0 ? 'error' : parsedStdin.events.length > 0 ? 'completed' : 'idle',
19
+ source: parsedStdin.events.length > 0 || parsedStdin.errors.length > 0 ? 'stdin' : 'interactive',
20
+ mode: 'inspect',
21
+ childStdinOpen: false,
22
+ decisionWriter: undefined,
23
+ };
24
+ const renderer = await createCliRenderer({
25
+ exitOnCtrlC: true,
26
+ clearOnShutdown: true,
27
+ screenMode: 'alternate-screen',
28
+ consoleMode: 'disabled',
29
+ });
30
+ const render = () => {
31
+ const current = renderer.root.getRenderable('vgxcode-screen');
32
+ if (current)
33
+ renderer.root.remove('vgxcode-screen');
34
+ const safety = buildSafetyReadModel(state.events);
35
+ renderer.root.add(createVgxcodeScreen({
36
+ ...state,
37
+ approvalActionsEnabled: getApprovalActionsState({
38
+ source: state.source,
39
+ status: state.status,
40
+ mode: state.mode,
41
+ childStdinOpen: state.childStdinOpen,
42
+ pendingApprovalCount: safety.pendingApprovalCount,
43
+ ...(safety.latestPendingApproval === undefined ? {} : { latestPendingApproval: safety.latestPendingApproval }),
44
+ }).enabled,
45
+ }));
46
+ renderer.requestRender();
47
+ };
48
+ render();
49
+ if (state.source === 'interactive') {
50
+ renderer.keyInput.on('keypress', (key) => {
51
+ if (key.ctrl && key.name === 'c')
52
+ return;
53
+ if (state.status === 'running') {
54
+ if (key.name === 'a' || key.sequence === 'a')
55
+ writePendingApprovalDecision(state, 'approved', render);
56
+ if (key.name === 'd' || key.sequence === 'd')
57
+ writePendingApprovalDecision(state, 'denied', render);
58
+ return;
59
+ }
60
+ if (key.name === 'tab') {
61
+ state.mode = toggleReadOnlyMode(state.mode);
62
+ render();
63
+ return;
64
+ }
65
+ if (key.name === 'return' || key.name === 'enter') {
66
+ void runReadOnlyBridge(state, render);
67
+ return;
68
+ }
69
+ if (key.name === 'backspace' || key.name === 'delete') {
70
+ state.prompt = state.prompt.slice(0, -1);
71
+ render();
72
+ return;
73
+ }
74
+ const text = printableKeyText(key.sequence);
75
+ if (text !== '') {
76
+ state.prompt += text;
77
+ render();
78
+ }
79
+ });
80
+ }
81
+ }
82
+ async function readPipedRuntimeEvents() {
83
+ if (process.stdin.isTTY)
84
+ return { events: [], errors: [] };
85
+ let input = '';
86
+ for await (const chunk of process.stdin)
87
+ input += String(chunk);
88
+ return parseVgxcodeJsonl(input);
89
+ }
90
+ async function runReadOnlyBridge(state, render) {
91
+ const submission = parsePromptSubmission(state.prompt, state.mode);
92
+ const prompt = submission.prompt;
93
+ if (prompt === '') {
94
+ state.status = 'error';
95
+ state.errors = ['Enter a prompt before running inspect, plan, or craft-preview. Prefix with /plan, /inspect, or /craft-preview; press Tab to switch inspect/plan.'];
96
+ render();
97
+ return;
98
+ }
99
+ state.mode = submission.mode;
100
+ state.submittedPrompt = prompt;
101
+ state.prompt = '';
102
+ state.status = 'running';
103
+ state.errors = [];
104
+ state.events = [];
105
+ state.childStdinOpen = false;
106
+ state.decisionWriter = undefined;
107
+ render();
108
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), '../../../../..');
109
+ const approvalCapableCraft = state.mode === 'craft';
110
+ const child = spawn('npm', commandArgsForMode(state.mode, prompt), {
111
+ cwd: root,
112
+ stdio: approvalCapableCraft ? ['pipe', 'pipe', 'pipe'] : ['ignore', 'pipe', 'pipe'],
113
+ env: process.env,
114
+ });
115
+ state.childStdinOpen = approvalCapableCraft && child.stdin !== null;
116
+ state.decisionWriter = approvalCapableCraft && child.stdin !== null ? new ApprovalDecisionWriter(child.stdin) : undefined;
117
+ if (child.stdout === null || child.stderr === null) {
118
+ state.status = 'error';
119
+ state.childStdinOpen = false;
120
+ state.decisionWriter = undefined;
121
+ state.errors.push('Failed to start root CLI with JSONL output streams.');
122
+ render();
123
+ return;
124
+ }
125
+ child.stdin?.on('close', () => {
126
+ state.childStdinOpen = false;
127
+ render();
128
+ });
129
+ child.stdin?.on('error', () => {
130
+ state.childStdinOpen = false;
131
+ render();
132
+ });
133
+ let lineNumber = 0;
134
+ let stdoutBuffer = '';
135
+ let stderr = '';
136
+ child.stdout.setEncoding('utf8');
137
+ child.stdout.on('data', (chunk) => {
138
+ stdoutBuffer += chunk;
139
+ const lines = stdoutBuffer.split(/\r?\n/);
140
+ stdoutBuffer = lines.pop() ?? '';
141
+ for (const line of lines) {
142
+ lineNumber += 1;
143
+ const parsed = parseVgxcodeJsonlLine(line, lineNumber);
144
+ if (parsed.event)
145
+ state.events.push(parsed.event);
146
+ if (parsed.error)
147
+ state.errors.push(parsed.error);
148
+ }
149
+ render();
150
+ });
151
+ child.stderr.setEncoding('utf8');
152
+ child.stderr.on('data', (chunk) => {
153
+ stderr += chunk;
154
+ });
155
+ child.on('error', (error) => {
156
+ state.status = 'error';
157
+ state.childStdinOpen = false;
158
+ state.errors.push(`Failed to start root CLI: ${error.message}`);
159
+ render();
160
+ });
161
+ child.on('close', (code, signal) => {
162
+ state.childStdinOpen = false;
163
+ state.decisionWriter = undefined;
164
+ if (stdoutBuffer.trim() !== '') {
165
+ lineNumber += 1;
166
+ const parsed = parseVgxcodeJsonlLine(stdoutBuffer, lineNumber);
167
+ if (parsed.event)
168
+ state.events.push(parsed.event);
169
+ if (parsed.error)
170
+ state.errors.push(parsed.error);
171
+ }
172
+ state.status = state.errors.length > 0 ? 'error' : 'completed';
173
+ if (code !== 0 && !(state.mode === 'craft-preview' && code === 3)) {
174
+ const detail = stderr.trim() === '' ? `signal ${signal ?? 'none'}` : stderr.trim();
175
+ state.status = 'error';
176
+ state.errors.push(`Root CLI ${state.mode} failed (exit ${code ?? 'signal'}): ${detail}`);
177
+ }
178
+ render();
179
+ });
180
+ }
181
+ function commandArgsForMode(mode, prompt) {
182
+ const args = ['run', '--silent', 'cli', '--', 'code', mode, prompt, '--events-jsonl'];
183
+ if (mode === 'craft')
184
+ args.push('--approval-channel', 'stdio');
185
+ return args;
186
+ }
187
+ function writePendingApprovalDecision(state, status, render) {
188
+ const model = buildSafetyReadModel(state.events);
189
+ const actions = getApprovalActionsState({
190
+ source: state.source,
191
+ status: state.status,
192
+ mode: state.mode,
193
+ childStdinOpen: state.childStdinOpen,
194
+ pendingApprovalCount: model.pendingApprovalCount,
195
+ ...(model.latestPendingApproval === undefined ? {} : { latestPendingApproval: model.latestPendingApproval }),
196
+ });
197
+ if (!actions.enabled)
198
+ return;
199
+ const wrote = state.decisionWriter?.write(actions.approval, status, status === 'approved' ? 'Approved from vgxcode.' : 'Denied from vgxcode.') ?? false;
200
+ if (!wrote)
201
+ return;
202
+ render();
203
+ }
204
+ function printableKeyText(sequence) {
205
+ if (sequence.length !== 1)
206
+ return '';
207
+ const code = sequence.charCodeAt(0);
208
+ return code >= 32 && code !== 127 ? sequence : '';
209
+ }
210
+ await main();