vgxness 1.2.1 → 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 +4 -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 +1 -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
@@ -0,0 +1,43 @@
1
+ import { navigationIntentFromInput } from '../keymap.js';
2
+ export function setupTuiActionFromInput(input, key, state) {
3
+ if (key.upArrow === true)
4
+ return { type: 'focus-previous-choice' };
5
+ if (key.downArrow === true)
6
+ return { type: 'focus-next-choice' };
7
+ if (key.escape === true)
8
+ return state.helpVisible ? { type: 'toggle-help' } : { type: 'cancel' };
9
+ if (key.tab === true && key.shift === true)
10
+ return { type: 'back' };
11
+ if (key.tab === true)
12
+ return { type: 'continue' };
13
+ const raw = key.return === true ? '\n' : input;
14
+ const intent = navigationIntentFromInput(raw);
15
+ if (intent === 'cancel')
16
+ return state.helpVisible ? { type: 'toggle-help' } : { type: 'cancel' };
17
+ if (intent === 'help')
18
+ return { type: 'toggle-help' };
19
+ if (intent === 'up')
20
+ return { type: 'focus-previous-choice' };
21
+ if (intent === 'down')
22
+ return { type: 'focus-next-choice' };
23
+ if (intent === 'select')
24
+ return { type: 'select-focused-choice' };
25
+ if (intent === 'back')
26
+ return { type: 'back' };
27
+ if (intent !== 'next')
28
+ return undefined;
29
+ if (key.return === true && isEditableChoiceScreen(state))
30
+ return { type: 'select-focused-choice' };
31
+ if (state.screen === 'plan-review')
32
+ return { type: 'request-apply' };
33
+ if (state.screen === 'final-confirmation')
34
+ return { type: 'confirm-apply' };
35
+ return { type: 'continue' };
36
+ }
37
+ function isEditableChoiceScreen(state) {
38
+ if (state.focusedChoiceId === undefined)
39
+ return false;
40
+ if (state.screen === 'project-database' || state.screen === 'provider')
41
+ return true;
42
+ return state.screen === 'opencode-details' && state.selections.provider === 'opencode';
43
+ }
@@ -1,4 +1,4 @@
1
- import { badgeLabels, compactPath } from './screens/screen-components.js';
1
+ import { badgeLabels, compactPath } from './setup-tui-view-helpers.js';
2
2
  export function buildSetupTuiViewModel(state) {
3
3
  return setupTuiViewModelFromPlan(state.plan, state.status, state);
4
4
  }
@@ -1,4 +1,4 @@
1
- import { badgeLabels, compactPath, formatFooter, setupFooterHints, wrapLabel } from './screens/screen-components.js';
1
+ import { badgeLabels, compactPath, formatFooter, setupFooterHints, wrapLabel } from './setup-tui-view-helpers.js';
2
2
  export function renderSetupTuiShape(input) {
3
3
  const width = input.width ?? 80;
4
4
  const lines = [input.viewModel.title, ...screenLines(input, width)];
@@ -0,0 +1,46 @@
1
+ import { formatTuiFooter } from '../visual/footer.js';
2
+ export const badgeLabels = {
3
+ recommended: '[recommended]',
4
+ selected: '[selected]',
5
+ focused: '[focused]',
6
+ warning: '[warning]',
7
+ error: '[error]',
8
+ writeAfterConfirm: '[will write after confirm]',
9
+ readOnly: '[read-only]',
10
+ manual: '[manual]',
11
+ deferred: '[deferred]',
12
+ cancelled: '[cancelled]',
13
+ pending: '[pending]',
14
+ success: '[success]',
15
+ };
16
+ export const setupFooterHints = {
17
+ continue: { key: 'Enter', label: 'continue' },
18
+ select: { key: 'Enter/Space', label: 'select focused row' },
19
+ finalConfirmation: { key: 'Enter', label: 'final confirmation' },
20
+ confirmApply: { key: 'Enter', label: 'confirm and apply' },
21
+ help: { key: '?/h', label: 'help' },
22
+ back: { key: 'Shift+Tab', label: 'back' },
23
+ cancel: { key: 'q/Esc', label: 'cancel' },
24
+ close: { key: 'q/Esc', label: 'close' },
25
+ };
26
+ export function compactPath(path, width) {
27
+ if (width <= 0 || path.length <= width)
28
+ return path;
29
+ const basename = path.split(/[\\/]/).filter(Boolean).pop() ?? path;
30
+ if (basename.length + 4 >= width)
31
+ return `.../${basename}`;
32
+ const prefixWidth = Math.max(1, width - basename.length - 5);
33
+ return `${path.slice(0, prefixWidth)}.../${basename}`;
34
+ }
35
+ export function wrapLabel(value, width) {
36
+ if (width <= 0 || value.length <= width)
37
+ return value;
38
+ const protectedPhrases = ['final confirmation', 'confirm and apply', 'not installable', 'read-only', 'requires confirmation', 'will write after confirm', 'error', 'cancelled'];
39
+ const phrase = protectedPhrases.find((candidate) => value.includes(candidate));
40
+ if (phrase !== undefined)
41
+ return `${value.slice(0, Math.max(0, width - phrase.length - 5)).trim()} ... ${phrase}`.trim();
42
+ return `${value.slice(0, Math.max(0, width - 1)).trim()}…`;
43
+ }
44
+ export function formatFooter(hints, width) {
45
+ return formatTuiFooter(hints, width);
46
+ }
@@ -1,5 +1,3 @@
1
1
  export * from './badges.js';
2
- export * from './choice-list.js';
3
2
  export * from './footer.js';
4
- export * from './layout.js';
5
3
  export * from './viewport.js';
@@ -0,0 +1,33 @@
1
+ export function getApprovalActionsState(input) {
2
+ if (input.source !== 'interactive' || input.status !== 'running' || input.mode !== 'craft' || !input.childStdinOpen)
3
+ return { enabled: false };
4
+ const approval = input.latestPendingApproval;
5
+ if (input.pendingApprovalCount < 1 || approval?.approvalId === undefined || approval.toolCallId === undefined)
6
+ return { enabled: false };
7
+ return { enabled: true, approval };
8
+ }
9
+ export class ApprovalDecisionWriter {
10
+ #sink;
11
+ #writtenKeys = new Set();
12
+ constructor(sink) {
13
+ this.#sink = sink;
14
+ }
15
+ write(approval, status, reason) {
16
+ if (approval?.approvalId === undefined || approval.toolCallId === undefined)
17
+ return false;
18
+ if (this.#sink.writableEnded === true || this.#sink.destroyed === true)
19
+ return false;
20
+ const key = `${approval.approvalId}:${approval.toolCallId}`;
21
+ if (this.#writtenKeys.has(key))
22
+ return false;
23
+ this.#sink.write(`${JSON.stringify({
24
+ type: 'approval.decision',
25
+ approvalId: approval.approvalId,
26
+ toolCallId: approval.toolCallId,
27
+ status,
28
+ reason,
29
+ })}\n`);
30
+ this.#writtenKeys.add(key);
31
+ return true;
32
+ }
33
+ }
@@ -0,0 +1,11 @@
1
+ export function parsePromptSubmission(input, currentMode) {
2
+ const trimmed = input.trim();
3
+ const prefixMatch = /^(\/(inspect|plan|craft|craft-preview))(?:\s+|$)/iu.exec(trimmed);
4
+ if (!prefixMatch)
5
+ return { mode: currentMode, prompt: trimmed };
6
+ const mode = prefixMatch[2]?.toLowerCase();
7
+ return { mode, prompt: trimmed.slice(prefixMatch[0].length).trim() };
8
+ }
9
+ export function toggleReadOnlyMode(mode) {
10
+ return mode === 'inspect' ? 'plan' : 'inspect';
11
+ }
@@ -0,0 +1,320 @@
1
+ export function sampleEvents(now = new Date('2026-05-26T23:30:00.000Z')) {
2
+ const createdAt = now.toISOString();
3
+ return [
4
+ {
5
+ type: 'session.started',
6
+ projectRoot: 'vgxness',
7
+ userIntent: 'Build the vgxcode OpenTUI shell',
8
+ createdAt,
9
+ },
10
+ {
11
+ type: 'provider.message',
12
+ content: 'I will inspect the runtime event stream and prepare a safe UI shell.',
13
+ toolCallCount: 1,
14
+ createdAt,
15
+ },
16
+ {
17
+ type: 'tool.requested',
18
+ toolName: 'read_file',
19
+ createdAt,
20
+ },
21
+ {
22
+ type: 'tool.completed',
23
+ toolName: 'read_file',
24
+ ok: true,
25
+ changedFiles: [],
26
+ createdAt,
27
+ },
28
+ {
29
+ type: 'verification.completed',
30
+ status: 'not-run',
31
+ reason: 'Static shell preview; no runtime commands executed.',
32
+ createdAt,
33
+ },
34
+ {
35
+ type: 'session.completed',
36
+ status: 'completed',
37
+ outcome: 'success',
38
+ createdAt,
39
+ },
40
+ ];
41
+ }
42
+ export function parseVgxcodeJsonl(input) {
43
+ const events = [];
44
+ const errors = [];
45
+ const lines = input.split(/\r?\n/);
46
+ for (const [index, line] of lines.entries()) {
47
+ const trimmed = line.trim();
48
+ if (!trimmed)
49
+ continue;
50
+ try {
51
+ const parsed = JSON.parse(trimmed);
52
+ if (isVgxcodeEvent(parsed))
53
+ events.push(parsed);
54
+ else
55
+ errors.push(formatUnsupportedRuntimeEvent(parsed, index + 1));
56
+ }
57
+ catch (error) {
58
+ errors.push(`Line ${index + 1}: ${formatJsonlParseError(trimmed, error)}`);
59
+ }
60
+ }
61
+ return { events, errors };
62
+ }
63
+ export function parseVgxcodeJsonlLine(line, lineNumber) {
64
+ const trimmed = line.trim();
65
+ if (!trimmed)
66
+ return {};
67
+ try {
68
+ const parsed = JSON.parse(trimmed);
69
+ if (isVgxcodeEvent(parsed))
70
+ return { event: parsed };
71
+ return { error: formatUnsupportedRuntimeEvent(parsed, lineNumber) };
72
+ }
73
+ catch (error) {
74
+ return { error: `Line ${lineNumber}: ${formatJsonlParseError(trimmed, error)}` };
75
+ }
76
+ }
77
+ function formatJsonlParseError(line, error) {
78
+ const reason = error instanceof Error ? error.message : 'invalid JSON';
79
+ const looksLikeNpmBanner = line.startsWith('>') || line.includes('npm') || line.includes('run-script');
80
+ const snippet = line.length > 80 ? `${line.slice(0, 77)}...` : line;
81
+ return looksLikeNpmBanner
82
+ ? `${reason} (non-JSON output; use npm --silent for the bridge; got: ${snippet})`
83
+ : `${reason} (invalid JSONL; got: ${snippet})`;
84
+ }
85
+ function formatUnsupportedRuntimeEvent(value, lineNumber) {
86
+ const type = isRecord(value) && typeof value.type === 'string' ? value.type : 'missing type';
87
+ return `Line ${lineNumber}: unsupported runtime event (${type}); expected vgxcode read-only JSONL event`;
88
+ }
89
+ export function summarizeEvent(event) {
90
+ switch (event.type) {
91
+ case 'session.started':
92
+ return `◇ Session started — ${event.userIntent}`;
93
+ case 'prompt.accepted':
94
+ return `◇ Prompt accepted — ${event.messageCount} messages, ${event.toolCount} tools`;
95
+ case 'provider.message':
96
+ return `● Assistant — ${event.content}`;
97
+ case 'tool.requested':
98
+ return `▸ Tool requested — ${event.toolName}`;
99
+ case 'approval.pending':
100
+ return `! Approval pending — ${event.toolName}`;
101
+ case 'approval.decided':
102
+ return `! Approval ${event.final} — ${event.toolName}: ${event.reason}`;
103
+ case 'approval.decision_rejected':
104
+ return `! Approval decision rejected — ${event.bindingResult}: ${event.reason}`;
105
+ case 'approval.preview':
106
+ return `! Approval preview — ${event.toolName}: No mutation executed`;
107
+ case 'tool.completed':
108
+ return `${event.ok ? '✓' : '✕'} Tool completed — ${event.toolName}`;
109
+ case 'diff.preview':
110
+ return `◇ Diff preview — ${event.files.length} files, ${event.bodyBytes}/${event.originalBytes} bytes${event.truncated ? ' (truncated)' : ''}`;
111
+ case 'verification.completed':
112
+ return `${event.status === 'passed' ? '✓' : event.status === 'failed' ? '✕' : '·'} Verification — ${event.reason}`;
113
+ case 'session.completed':
114
+ return `◆ Session ${event.status} — ${event.outcome}`;
115
+ case 'session.failed':
116
+ return `✕ Session ${event.status} — ${event.message}`;
117
+ }
118
+ }
119
+ export function buildSafetyReadModel(events) {
120
+ const pendingApprovals = new Map();
121
+ let latestApprovalDecision;
122
+ let latestApprovalPreview;
123
+ const changedFiles = [];
124
+ const seenChangedFiles = new Set();
125
+ let latestDiffPreview;
126
+ for (const event of events) {
127
+ switch (event.type) {
128
+ case 'approval.pending':
129
+ if (event.approvalId === undefined || event.toolCallId === undefined)
130
+ break;
131
+ pendingApprovals.set(approvalKey(event.approvalId, event.toolCallId), {
132
+ ...(event.approvalId === undefined ? {} : { approvalId: event.approvalId }),
133
+ ...(event.toolCallId === undefined ? {} : { toolCallId: event.toolCallId }),
134
+ toolName: event.toolName,
135
+ ...(event.operationSummary === undefined ? {} : { operationSummary: event.operationSummary }),
136
+ ...(event.targetPath === undefined ? {} : { targetPath: event.targetPath }),
137
+ ...(event.risk === undefined ? {} : { risk: event.risk }),
138
+ createdAt: event.createdAt,
139
+ });
140
+ break;
141
+ case 'approval.decided':
142
+ if (event.approvalId !== undefined && event.toolCallId !== undefined)
143
+ pendingApprovals.delete(approvalKey(event.approvalId, event.toolCallId));
144
+ latestApprovalDecision = {
145
+ ...(event.approvalId === undefined ? {} : { approvalId: event.approvalId }),
146
+ ...(event.toolCallId === undefined ? {} : { toolCallId: event.toolCallId }),
147
+ toolName: event.toolName,
148
+ ...(event.status === undefined ? {} : { status: event.status }),
149
+ final: event.final,
150
+ reason: event.reason,
151
+ createdAt: event.createdAt,
152
+ };
153
+ break;
154
+ case 'approval.decision_rejected':
155
+ if (event.approvalId !== undefined && event.toolCallId !== undefined && isResolvingDecisionRejection(event.bindingResult)) {
156
+ pendingApprovals.delete(approvalKey(event.approvalId, event.toolCallId));
157
+ }
158
+ break;
159
+ case 'approval.preview':
160
+ latestApprovalPreview = {
161
+ toolName: event.toolName,
162
+ capability: event.capability,
163
+ ...(event.targetPath === undefined ? {} : { targetPath: event.targetPath }),
164
+ reason: event.reason,
165
+ risk: event.risk,
166
+ createdAt: event.createdAt,
167
+ };
168
+ break;
169
+ case 'tool.completed':
170
+ for (const changedFile of event.changedFiles) {
171
+ if (seenChangedFiles.has(changedFile))
172
+ continue;
173
+ seenChangedFiles.add(changedFile);
174
+ changedFiles.push(changedFile);
175
+ }
176
+ break;
177
+ case 'diff.preview':
178
+ latestDiffPreview = {
179
+ files: event.files,
180
+ body: event.body,
181
+ bodyBytes: event.bodyBytes,
182
+ originalBytes: event.originalBytes,
183
+ truncated: event.truncated,
184
+ redacted: event.redacted,
185
+ hash: event.hash,
186
+ createdAt: event.createdAt,
187
+ };
188
+ for (const file of event.files) {
189
+ if (seenChangedFiles.has(file))
190
+ continue;
191
+ seenChangedFiles.add(file);
192
+ changedFiles.push(file);
193
+ }
194
+ break;
195
+ default:
196
+ break;
197
+ }
198
+ }
199
+ const pendingApprovalValues = [...pendingApprovals.values()];
200
+ const latestPendingApproval = pendingApprovalValues.at(-1);
201
+ return {
202
+ pendingApprovals: pendingApprovalValues,
203
+ pendingApprovalCount: pendingApprovalValues.length,
204
+ ...(latestPendingApproval ? { latestPendingApproval } : {}),
205
+ ...(latestApprovalDecision ? { latestApprovalDecision } : {}),
206
+ ...(latestApprovalPreview ? { latestApprovalPreview } : {}),
207
+ changedFiles,
208
+ diffAvailability: latestDiffPreview !== undefined ? 'preview' : changedFiles.length > 0 ? 'filenames-only' : 'none',
209
+ ...(latestDiffPreview ? { latestDiffPreview } : {}),
210
+ };
211
+ }
212
+ function isVgxcodeEvent(value) {
213
+ if (!isRecord(value) || typeof value.type !== 'string' || typeof value.createdAt !== 'string')
214
+ return false;
215
+ switch (value.type) {
216
+ case 'session.started':
217
+ return typeof value.projectRoot === 'string' && typeof value.userIntent === 'string';
218
+ case 'prompt.accepted':
219
+ return typeof value.messageCount === 'number' && typeof value.toolCount === 'number';
220
+ case 'provider.message':
221
+ return typeof value.content === 'string' && typeof value.toolCallCount === 'number';
222
+ case 'tool.requested':
223
+ return typeof value.toolName === 'string';
224
+ case 'approval.pending':
225
+ return typeof value.toolName === 'string' && isOptionalApprovalContext(value);
226
+ case 'approval.decided':
227
+ return (typeof value.toolName === 'string' &&
228
+ typeof value.reason === 'string' &&
229
+ isApprovalFinal(value.final) &&
230
+ (value.approvalId === undefined || typeof value.approvalId === 'string') &&
231
+ (value.toolCallId === undefined || typeof value.toolCallId === 'string') &&
232
+ (value.status === undefined || isApprovalStatus(value.status)));
233
+ case 'approval.decision_rejected':
234
+ return (typeof value.reason === 'string' &&
235
+ isApprovalDecisionBindingResult(value.bindingResult) &&
236
+ (value.approvalId === undefined || typeof value.approvalId === 'string') &&
237
+ (value.toolCallId === undefined || typeof value.toolCallId === 'string') &&
238
+ (value.status === undefined || value.status === 'approved' || value.status === 'denied'));
239
+ case 'approval.preview':
240
+ return (typeof value.toolCallId === 'string' &&
241
+ typeof value.toolName === 'string' &&
242
+ isCapability(value.capability) &&
243
+ isRecord(value.risk) &&
244
+ typeof value.risk.mutatesWorkspace === 'boolean' &&
245
+ typeof value.risk.requiresApproval === 'boolean' &&
246
+ value.reason === 'preview_only' &&
247
+ (value.targetPath === undefined || typeof value.targetPath === 'string'));
248
+ case 'tool.completed':
249
+ return typeof value.toolName === 'string' && typeof value.ok === 'boolean' && Array.isArray(value.changedFiles) && value.changedFiles.every((changedFile) => typeof changedFile === 'string');
250
+ case 'diff.preview':
251
+ return (typeof value.sourceToolCallId === 'string' &&
252
+ value.sourceToolName === 'git_diff' &&
253
+ value.format === 'unified' &&
254
+ Array.isArray(value.files) &&
255
+ value.files.every((file) => typeof file === 'string') &&
256
+ typeof value.body === 'string' &&
257
+ typeof value.bodyBytes === 'number' &&
258
+ typeof value.originalBytes === 'number' &&
259
+ typeof value.truncated === 'boolean' &&
260
+ typeof value.redacted === 'boolean' &&
261
+ typeof value.hash === 'string');
262
+ case 'verification.completed':
263
+ return isVerificationStatus(value.status) && typeof value.reason === 'string';
264
+ case 'session.completed':
265
+ return isSessionStatus(value.status) && isOutcome(value.outcome);
266
+ case 'session.failed':
267
+ return isFailedSessionStatus(value.status) && typeof value.message === 'string';
268
+ default:
269
+ return false;
270
+ }
271
+ }
272
+ function isRecord(value) {
273
+ return typeof value === 'object' && value !== null;
274
+ }
275
+ function isApprovalFinal(value) {
276
+ return value === 'allow' || value === 'deny' || value === 'blocked';
277
+ }
278
+ function isApprovalStatus(value) {
279
+ return value === 'approved' || value === 'denied' || value === 'blocked' || value === 'cancelled' || value === 'timed_out';
280
+ }
281
+ function isApprovalDecisionBindingResult(value) {
282
+ return value === 'malformed' || value === 'unknown' || value === 'binding_mismatch' || value === 'duplicate' || value === 'stale' || value === 'invalid_status' || value === 'cancelled';
283
+ }
284
+ function isResolvingDecisionRejection(bindingResult) {
285
+ return bindingResult === 'duplicate' || bindingResult === 'stale' || bindingResult === 'cancelled';
286
+ }
287
+ function approvalKey(approvalId, toolCallId) {
288
+ return `${approvalId}:${toolCallId}`;
289
+ }
290
+ function isOptionalApprovalContext(value) {
291
+ return ((value.approvalId === undefined || typeof value.approvalId === 'string') &&
292
+ (value.toolCallId === undefined || typeof value.toolCallId === 'string') &&
293
+ (value.operationSummary === undefined || typeof value.operationSummary === 'string') &&
294
+ (value.targetPath === undefined || typeof value.targetPath === 'string') &&
295
+ (value.risk === undefined || isApprovalRisk(value.risk)));
296
+ }
297
+ function isApprovalRisk(value) {
298
+ return (isRecord(value) &&
299
+ typeof value.mutatesWorkspace === 'boolean' &&
300
+ typeof value.requiresApproval === 'boolean' &&
301
+ (value.destructive === undefined || typeof value.destructive === 'boolean') &&
302
+ (value.external === undefined || typeof value.external === 'boolean') &&
303
+ (value.privileged === undefined || typeof value.privileged === 'boolean') &&
304
+ (value.ambiguous === undefined || typeof value.ambiguous === 'boolean'));
305
+ }
306
+ function isCapability(value) {
307
+ return value === 'read' || value === 'edit' || value === 'shell' || value === 'git' || value === 'network' || value === 'memory' || value === 'sdd' || value === 'run' || value === 'mcp';
308
+ }
309
+ function isVerificationStatus(value) {
310
+ return value === 'not-run' || value === 'passed' || value === 'failed';
311
+ }
312
+ function isSessionStatus(value) {
313
+ return value === 'completed' || value === 'partial' || value === 'blocked' || value === 'failed' || value === 'cancelled';
314
+ }
315
+ function isFailedSessionStatus(value) {
316
+ return value === 'blocked' || value === 'failed' || value === 'cancelled';
317
+ }
318
+ function isOutcome(value) {
319
+ return value === 'success' || value === 'partial' || value === 'failure' || value === 'blocked' || value === 'cancelled';
320
+ }
@@ -38,30 +38,6 @@ export class SddWorkflowService {
38
38
  const phases = this.getPhaseStatuses(validated.value.project, validated.value.change);
39
39
  return statusFromPhases(validated.value.change, phases);
40
40
  }
41
- getDashboardStatus(input) {
42
- const validated = validateProjectAndChange(input.project, input.change);
43
- if (!validated.ok)
44
- return validated;
45
- const phases = this.getPhaseStatusesNoTrace(validated.value.project, validated.value.change);
46
- if (!phases.ok)
47
- return phases;
48
- return statusFromPhases(validated.value.change, phases.value);
49
- }
50
- getPhaseStatusesNoTrace(project, change) {
51
- const statuses = [];
52
- for (const phase of sddPhases) {
53
- const topicKey = sddTopicKey(change, phase);
54
- const artifact = this.memory.getArtifactNoTrace(project, topicKey);
55
- if (!artifact.ok) {
56
- if (artifact.error.code !== 'not_found')
57
- return artifact;
58
- statuses.push({ phase, topicKey, present: false, state: 'missing', accepted: false, legacy: false });
59
- continue;
60
- }
61
- statuses.push(phaseStatusFromArtifact(phase, topicKey, artifact.value));
62
- }
63
- return { ok: true, value: statuses };
64
- }
65
41
  getNext(input) {
66
42
  const validated = validateProjectAndChange(input.project, input.change);
67
43
  if (!validated.ok)
@@ -4,7 +4,7 @@ export const antigravitySetupAdapter = {
4
4
  displayName: 'Antigravity',
5
5
  supportLevel: 'placeholder',
6
6
  capabilities: ['manual-guidance'],
7
- targets: [{ kind: 'manual', label: 'Coming soon manual guidance', writableByDashboard: false }],
7
+ targets: [{ kind: 'manual', label: 'Coming soon manual guidance', writableBySetup: false }],
8
8
  getStatus() {
9
9
  return {
10
10
  providerId: 'antigravity',
@@ -5,7 +5,7 @@ export const claudeSetupAdapter = {
5
5
  displayName: 'Claude',
6
6
  supportLevel: 'preview-only',
7
7
  capabilities: ['mcp-preview', 'manual-guidance'],
8
- targets: [{ kind: 'manual', label: 'Claude MCP config snippet', writableByDashboard: false }],
8
+ targets: [{ kind: 'manual', label: 'Claude MCP config snippet', writableBySetup: false }],
9
9
  getStatus(context) {
10
10
  return {
11
11
  providerId: 'claude',
@@ -14,7 +14,7 @@ export const claudeSetupAdapter = {
14
14
  evidence: context.databasePath !== undefined
15
15
  ? ['Claude MCP preview can be generated from the selected database path.']
16
16
  : ['Claude MCP preview uses a placeholder until a database path is selected.'],
17
- guidance: ['Copy snippets manually after reviewing them. The dashboard does not install or apply Claude config.'],
17
+ guidance: ['Copy snippets manually after reviewing them. The TUI does not install or apply Claude config.'],
18
18
  actions: [
19
19
  {
20
20
  id: 'claude-manual-guidance',
@@ -4,7 +4,7 @@ export const customSetupAdapter = {
4
4
  displayName: 'Custom',
5
5
  supportLevel: 'extension-point',
6
6
  capabilities: ['manual-guidance'],
7
- targets: [{ kind: 'extension', label: 'Custom provider extension point', writableByDashboard: false }],
7
+ targets: [{ kind: 'extension', label: 'Custom provider extension point', writableBySetup: false }],
8
8
  getStatus() {
9
9
  return {
10
10
  providerId: 'custom',
@@ -8,8 +8,8 @@ export const openCodeSetupAdapter = {
8
8
  supportLevel: 'supported-primary',
9
9
  capabilities: ['mcp-preview', 'mcp-install-plan', 'mcp-install-apply-external', 'agent-preview', 'doctor', 'visibility-check'],
10
10
  targets: [
11
- { kind: 'user-config', label: 'User/global OpenCode config (default)', path: '$HOME/.config/opencode/opencode.json', writableByDashboard: false },
12
- { kind: 'project-config', label: 'Project OpenCode config (explicit opt-in)', path: '.opencode/opencode.json', writableByDashboard: false },
11
+ { kind: 'user-config', label: 'User/global OpenCode config (default)', path: '$HOME/.config/opencode/opencode.json', writableBySetup: false },
12
+ { kind: 'project-config', label: 'Project OpenCode config (explicit opt-in)', path: '.opencode/opencode.json', writableBySetup: false },
13
13
  ],
14
14
  getStatus(context) {
15
15
  const visibility = this.getVisibility?.(context);
@@ -161,7 +161,7 @@ function externalInstallAction(scope, targetPath) {
161
161
  label: `Copy external OpenCode ${scope === 'user' ? 'user/global' : 'project'} install command`,
162
162
  kind: 'copy-command',
163
163
  command: ['vgxness', 'mcp', 'install', 'opencode', '--scope', scope, '--yes'],
164
- description: 'Copy and run this outside the dashboard only after reviewing the read-only plan.',
164
+ description: 'Copy and run this outside the TUI only after reviewing the read-only plan.',
165
165
  safety: externalProviderWriteSafety(targetPath),
166
166
  };
167
167
  }
@@ -183,7 +183,7 @@ function nextAction(store, mcp, defaults, providers, project) {
183
183
  if (defaults.status !== 'ready') {
184
184
  if (defaults.blocker?.startsWith('No project selected') === true) {
185
185
  return {
186
- command: 'vgx dashboard interactive --project <project>',
186
+ command: 'vgx setup status --project <project>',
187
187
  reason: defaults.nextAction ?? 'Select a project to verify project-scoped setup checks.',
188
188
  };
189
189
  }
@@ -178,7 +178,7 @@ This plane should remain separate from runtime execution. Rendering OpenCode, Cl
178
178
  |---|---|---|---|
179
179
  | MCP server | AI coding tools and agents | Query/update workflow state, resolve agents/skills, record runs/checkpoints, request approvals. | Bypass permission policy or mutate provider config directly. |
180
180
  | CLI | Human operators, scripts, CI-friendly automation | Initialize, inspect, doctor, sync/render assets, manage MCP installs, inspect SDD/runs/profiles. | Hide dangerous changes or require manual JSON editing for the happy path. |
181
- | TUI | Humans during setup and local operation | Guide installation, show health/status, configure profiles, inspect SDD progress, surface blockers. | Become a decorative dashboard without next actions. |
181
+ | TUI | Humans during setup and local operation | Guide installation, show health/status, surface blockers. | Become decorative without next actions. |
182
182
 
183
183
  ### Natural-language planning seam
184
184
 
@@ -214,7 +214,7 @@ Current MCP tool groups are defined in source by `SUPPORTED_VGX_MCP_TOOL_NAMES`;
214
214
  | Runs | `vgxness_run_start`, `vgxness_run_list`, `vgxness_run_get`, `vgxness_run_preflight`, `vgxness_run_checkpoint`, `vgxness_run_finalize` | Keep execution history, safety preflight, and resumability explicit. |
215
215
  | Resolution/profile/payload | `vgxness_agent_resolve`, `vgxness_agent_activate`, `vgxness_manager_profile_get`, `vgxness_manager_profile_set`, `vgxness_skill_payload`, `vgxness_opencode_manager_payload` | Give agents the correct role, skills, model/profile context, and read-only OpenCode manager previews. Payload/profile preview tools do not execute providers or mutate provider config by default. |
216
216
 
217
- MVP transport should prefer local stdio because it matches common MCP host expectations and avoids opening a network port. A local HTTP transport can be added later for dashboard or background-daemon use cases.
217
+ MVP transport should prefer local stdio because it matches common MCP host expectations and avoids opening a network port. A local HTTP transport can be added later for web-console or background-daemon use cases.
218
218
 
219
219
  ### CLI boundary
220
220
 
@@ -249,7 +249,7 @@ The TUI is the guided human surface. It should optimize for the next meaningful
249
249
  | Doctor | Fix broken config, missing MCP integration, memory issues, and stale assets. |
250
250
  | Settings | Review local preferences and configuration status without silent provider writes. |
251
251
 
252
- Every TUI screen must define loading, empty, error, success, blocked, and permission states. The dashboard TUI is read-only: provider config writes/install/apply are external-only, require explicit confirmation outside the dashboard, and are not run by dashboard flows.
252
+ Every TUI screen must define loading, empty, error, success, blocked, and permission states. TUI status surfaces are read-only: provider config writes/install/apply are external-only, require explicit confirmation outside the TUI, and are not run by preview/status flows.
253
253
 
254
254
  ### Installation flow
255
255
 
@@ -693,6 +693,6 @@ Out of scope:
693
693
 
694
694
  - cloud sync
695
695
  - team collaboration
696
- - web dashboard
696
+ - web console
697
697
  - distributed workers
698
698
  - fully autonomous skill mutation