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
@@ -1,428 +0,0 @@
1
- import { redactedPlaceholder, redactJsonValue } from '../export/redaction.js';
2
- import { defaultPermissionPolicy } from '../permissions/policy-evaluator.js';
3
- import { verificationChangeTypeList } from '../verification/index.js';
4
- import { listWorkflows } from '../workflows/workflow-registry.js';
5
- import { isSetupReady } from './dashboard-tui-read-model.js';
6
- const noLink = 'no link available';
7
- const verificationPlanCommand = 'npm run cli -- verification plan --type <change-type>';
8
- const verificationPlanCopyActionLabel = 'Copy verification plan command';
9
- const verificationReportCopyActionLabel = 'Copy verification report get command';
10
- export function dashboardCopyAction(label, command, safety = 'read-only') {
11
- return { label, command, safety, copyOnly: true, executesInTui: false };
12
- }
13
- export function verificationPlanGuidanceDetail() {
14
- return [
15
- 'Verification plan guidance: read-only recommendations/manual guidance for a user-selected change type.',
16
- `Copy placeholder command: ${verificationPlanCommand}`,
17
- `Supported change types: ${verificationChangeTypeList}`,
18
- 'Choose the change type explicitly; the dashboard does not infer, rank, prefill, or recommend one from SDD or repository state.',
19
- 'Plans are not verification status, proof, run evidence, checkpoints, SDD readiness, or acceptance.',
20
- ];
21
- }
22
- function verificationPlanCopyAction() {
23
- return dashboardCopyAction(verificationPlanCopyActionLabel, verificationPlanCommand);
24
- }
25
- export function verificationReportCommand(project, change) {
26
- return `npm run cli -- verification report get --project ${project} --change ${change}`;
27
- }
28
- export function verificationReportMetadataDetail(sdd) {
29
- const fallbackTopicKey = `sdd/${sdd.change}/verify`;
30
- const cockpitVerify = sdd.cockpit?.phases.find((phase) => phase.phase === 'verify');
31
- const statusVerify = sdd.status.phases.find((phase) => phase.phase === 'verify');
32
- const verify = cockpitVerify ?? statusVerify;
33
- const present = verify?.present === true;
34
- const topicKey = verify?.topicKey ?? fallbackTopicKey;
35
- if (!present)
36
- return `Verification report artifact: not present · topicKey=${topicKey}`;
37
- const state = verify?.state ?? 'present';
38
- const accepted = verify?.accepted === true ? 'yes' : 'no';
39
- const artifactId = cockpitVerify?.artifact?.artifactId ?? statusVerify?.artifactId ?? 'unavailable';
40
- return `Verification report artifact: present · state=${state} · accepted=${accepted} · topicKey=${topicKey} · artifactId=${artifactId}`;
41
- }
42
- function verificationReportCopyAction(project, change) {
43
- return dashboardCopyAction(verificationReportCopyActionLabel, verificationReportCommand(project, change));
44
- }
45
- export function dashboardSafety() {
46
- return {
47
- readOnly: true,
48
- copyOnly: true,
49
- providerWrites: false,
50
- providerExecution: false,
51
- modifiesDotOpencode: false,
52
- modifiesDotClaude: false,
53
- modifiesOpenSpec: false,
54
- executesWorkflow: false,
55
- executesDoctor: false,
56
- };
57
- }
58
- export function buildDashboardWorkflowsReadModel(input = {}) {
59
- const project = input.project ?? '<project>';
60
- const workflows = listWorkflows();
61
- const items = workflows.map((workflow) => {
62
- const lastKnownRun = (input.runs ?? []).filter((run) => run.workflow === workflow.id).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))[0];
63
- return {
64
- id: workflow.id,
65
- label: workflow.title,
66
- description: workflow.description,
67
- phase: workflow.defaultPhase,
68
- mutability: workflow.mutability,
69
- safetyProfile: workflow.safetyProfile,
70
- requiresSddArtifacts: workflow.requiresSddArtifacts,
71
- availability: 'available',
72
- inputsSummary: workflow.id === 'sdd'
73
- ? '--project and --change for SDD status; --intent for explicit preview outside TUI'
74
- : '--project and --intent for explicit preview outside TUI',
75
- sourceRef: 'src/workflows/workflow-registry.ts',
76
- ...(lastKnownRun === undefined
77
- ? {}
78
- : { lastKnownRun: { id: lastKnownRun.id, status: lastKnownRun.status, phase: lastKnownRun.phase, updatedAt: lastKnownRun.updatedAt } }),
79
- nextActions: [
80
- dashboardCopyAction('Preview workflow metadata', `npm run cli -- ${workflow.id} preview --project ${project} --intent <intent>`),
81
- dashboardCopyAction('Inspect workflow runs', `npm run cli -- runs list --project ${project}`),
82
- ],
83
- };
84
- });
85
- return {
86
- meta: meta('workflows', 'Workflows', 'workflow registry', input.generatedAt),
87
- summary: `${items.length} workflow(s) from registry; actions are copy-only and not executed by the TUI.`,
88
- items,
89
- ...(items.length === 0
90
- ? {
91
- emptyState: {
92
- message: 'No workflows are registered in the workflow registry.',
93
- nextActions: [dashboardCopyAction('Inspect help', 'npm run cli -- --help')],
94
- },
95
- }
96
- : {}),
97
- copyActions: [dashboardCopyAction('Inspect CLI help', 'npm run cli -- --help')],
98
- };
99
- }
100
- export function buildDashboardRunsReadModel(input) {
101
- const items = input.runs.map((run) => buildRunTraceItem(run, input.detailsByRunId?.[run.id], input.insightsByRunId?.[run.id]));
102
- const project = input.project ?? '<project>';
103
- return {
104
- meta: meta('runs', 'Runs', 'run records, details, checkpoints, approvals, operation attempts', input.generatedAt),
105
- summary: `${items.length} visible run(s); ${items.reduce((count, item) => count + item.relatedApprovals.length, 0)} approval(s); ${items.reduce((count, item) => count + item.relatedPreflights.length, 0)} preflight/attempt(s).`,
106
- items,
107
- ...(items.length === 0
108
- ? {
109
- emptyState: {
110
- message: 'No runs are registered or visible for this project.',
111
- nextActions: [dashboardCopyAction('Inspect runs', `npm run cli -- runs list --project ${project}`)],
112
- },
113
- }
114
- : {}),
115
- copyActions: [dashboardCopyAction('Inspect runs', `npm run cli -- runs list --project ${project}`)],
116
- };
117
- }
118
- export function buildDashboardApprovalsReadModel(input) {
119
- const items = input.runs.flatMap((run) => approvalItemsForRun(run, input.detailsByRunId?.[run.id]));
120
- items.sort((a, b) => (a.status === 'pending' ? 0 : 1) - (b.status === 'pending' ? 0 : 1) || (b.createdAt ?? '').localeCompare(a.createdAt ?? ''));
121
- const project = input.project ?? '<project>';
122
- return {
123
- meta: meta('approvals', 'Approvals', 'run approvals and operation attempts', input.generatedAt),
124
- summary: `${items.filter((item) => item.status === 'pending').length} pending; ${items.length} pending/recent approval or preflight item(s).`,
125
- items,
126
- ...(items.length === 0
127
- ? {
128
- emptyState: {
129
- message: 'No pending or recent approvals/preflights are visible for this project.',
130
- nextActions: [dashboardCopyAction('Inspect approvals', `npm run cli -- approvals list --project ${project}`)],
131
- },
132
- }
133
- : {}),
134
- copyActions: [dashboardCopyAction('Inspect approvals', `npm run cli -- approvals list --project ${project}`)],
135
- };
136
- }
137
- export function buildDashboardInstallationReadModel(input) {
138
- const project = input.project ?? '<project>';
139
- const ready = isSetupReady(input.setup);
140
- return {
141
- meta: meta('installation', 'Installation', 'setup lifecycle, provider status, doctor, change-plan preview', input.generatedAt),
142
- lifecycle: {
143
- status: input.setup === undefined ? 'loading' : ready ? 'ready' : 'blocked',
144
- summary: input.setup?.verification.summary ?? 'Setup lifecycle evidence is unavailable.',
145
- ...(input.setup?.nextAction.reason === undefined ? {} : { nextAction: input.setup.nextAction.reason }),
146
- },
147
- setupPlan: {
148
- status: 'ready',
149
- summary: 'Preview-only setup guidance; writes remain in vgx init final confirmation or explicit CLI commands.',
150
- previewCommands: [
151
- dashboardCopyAction('Preview OpenCode setup', 'npm run cli -- mcp setup --provider opencode --preview'),
152
- dashboardCopyAction('Preview OpenCode install plan', 'npm run cli -- mcp install opencode --plan'),
153
- ],
154
- },
155
- ...(input.providerStatus === undefined ? {} : { providerStatus: input.providerStatus }),
156
- ...(input.providerDoctor === undefined ? {} : { providerDoctor: input.providerDoctor }),
157
- ...(input.providerChangePlan === undefined ? {} : { providerChangePlan: input.providerChangePlan }),
158
- copyActions: [
159
- dashboardCopyAction('Inspect dashboard status', `npm run cli -- dashboard status --project ${project}`),
160
- dashboardCopyAction('Open write-capable setup TUI', 'npm run cli -- init', 'manual-review'),
161
- ],
162
- };
163
- }
164
- export function buildDashboardManagementReadModels(input) {
165
- const project = input.project ?? '<project>';
166
- const generatedAt = input.generatedAt;
167
- const agents = input.agentsSkills?.agents.map((agent) => ({
168
- id: agent.id,
169
- title: agent.name,
170
- status: agent.mode,
171
- summary: agent.description ?? 'No description',
172
- detail: [`Scope: ${agent.scope}`, `Provider: ${agent.providerAdapter ?? 'unavailable'}`, `Model: ${agent.model ?? 'unavailable'}`],
173
- copyActions: [dashboardCopyAction('Inspect agent', `npm run cli -- agents get --id ${agent.id}`)],
174
- })) ?? [];
175
- const skills = input.agentsSkills?.skills.map((skill) => ({
176
- id: skill.id,
177
- title: skill.name,
178
- status: skill.scope,
179
- summary: skill.description ?? 'No description',
180
- detail: [`Scope: ${skill.scope}`, 'Skill metadata is displayed read-only.'],
181
- copyActions: [dashboardCopyAction('Inspect skills', `npm run cli -- skills list --project ${project}`)],
182
- })) ?? [];
183
- const memory = input.memoryObservations?.map((observation) => ({
184
- id: observation.id,
185
- title: observation.title,
186
- status: observation.type,
187
- summary: observation.preview,
188
- detail: [
189
- `Project: ${observation.project}`,
190
- `Scope/topic: ${observation.scope}${observation.topicKey === undefined ? '' : ` · ${observation.topicKey}`}`,
191
- `Updated: ${observation.updatedAt}`,
192
- ],
193
- copyActions: [dashboardCopyAction('Inspect memory observation', `npm run cli -- memory get --id ${observation.id}`)],
194
- })) ?? [];
195
- const policy = input.permissionPolicy ?? defaultPermissionPolicy;
196
- const permissions = policy.rules.map((rule) => ({
197
- id: rule.category,
198
- title: rule.category,
199
- status: rule.decision,
200
- summary: rule.reason ?? 'No explicit reason recorded.',
201
- detail: [`Decision: ${rule.decision}`, `Reason: ${rule.reason ?? 'unspecified'}`, 'Dashboard never approves or changes permissions.'],
202
- copyActions: [dashboardCopyAction('Inspect permissions', 'npm run cli -- permissions --help')],
203
- }));
204
- const settings = [
205
- {
206
- id: 'workspace-root',
207
- title: 'Workspace root',
208
- status: 'read-only',
209
- summary: input.workspaceRoot ?? 'unknown',
210
- detail: ['Filesystem paths are displayed only; dashboard does not write provider config.'],
211
- copyActions: [],
212
- },
213
- {
214
- id: 'database-path',
215
- title: 'Database path',
216
- status: 'read-only',
217
- summary: input.databasePath ?? 'default resolution',
218
- detail: ['SQLite path is informational; dashboard read models avoid memory writes.'],
219
- copyActions: [],
220
- },
221
- {
222
- id: 'provider-boundary',
223
- title: 'Provider boundary',
224
- status: 'copy-only',
225
- summary: 'OpenCode setup writes remain in vgx init final confirmation or explicit CLI.',
226
- detail: ['No provider install/write API is called by dashboard rendering or refresh.'],
227
- copyActions: [dashboardCopyAction('Open guided setup explicitly', 'npm run cli -- init', 'manual-review')],
228
- },
229
- ];
230
- return {
231
- agents: management('agents', 'Agents', 'agent registry', agents, project, generatedAt),
232
- skills: management('skills', 'Skills', 'skill registry', skills, project, generatedAt),
233
- memory: management('memory', 'Memory', 'memory observations', memory, project, generatedAt, 'No recent memory observations are visible for this project.'),
234
- sdd: management('sdd', 'SDD', 'SDD artifacts', sddRows(input.sdd, project), project, generatedAt),
235
- runs: management('runs', 'Runs', 'run records', input.runs.map((run) => ({
236
- id: run.id,
237
- title: run.workflow,
238
- status: run.status,
239
- summary: `${run.phase} · ${run.userIntent}`,
240
- detail: [`Agent: ${run.selectedAgentId}`, `Model: ${run.model}`, `Updated: ${run.updatedAt}`],
241
- copyActions: [dashboardCopyAction('Inspect run', `npm run cli -- runs get --id ${run.id}`)],
242
- })), project, generatedAt),
243
- approvals: management('approvals', 'Approvals', 'approvals/preflights', input.approvals?.items.map((item) => ({
244
- id: item.id,
245
- title: item.kind,
246
- status: item.status,
247
- summary: `${item.category} · ${item.operation}`,
248
- detail: [`Reason: ${item.reason}`, `Risk: ${item.riskFlags.join(', ') || 'none'}`, `Run: ${item.runId ?? 'unknown'}`],
249
- copyActions: item.nextActions,
250
- })) ?? [], project, generatedAt),
251
- permissions: management('permissions', 'Permissions', 'permission policy', permissions, project, generatedAt, 'Permission rules are inspect-only here; dashboard never approves or changes permissions.'),
252
- settings: management('settings', 'Settings', 'workspace settings', settings, project, generatedAt, 'Settings are displayed read-only in the dashboard.'),
253
- };
254
- }
255
- export function redactDashboardValue(value) {
256
- return redactJsonValue(value);
257
- }
258
- function meta(screen, title, source, generatedAt = new Date().toISOString()) {
259
- return { screen, title, generatedAt, source, redactionsApplied: [redactedPlaceholder], safety: dashboardSafety() };
260
- }
261
- function management(section, title, source, rows, _project, generatedAt, emptyMessage = `No ${title.toLowerCase()} rows are visible for this project.`) {
262
- return {
263
- meta: meta(section, title, source, generatedAt),
264
- section,
265
- status: rows.length === 0 ? 'empty' : 'ready',
266
- summary: `${rows.length} ${title.toLowerCase()} row(s); Enter shows detail only.`,
267
- rows,
268
- ...(rows.length === 0
269
- ? { emptyState: { message: emptyMessage, nextActions: [dashboardCopyAction(`Inspect ${title}`, `npm run cli -- ${section} --help`)] } }
270
- : {}),
271
- copyActions: [dashboardCopyAction(`Inspect ${title}`, `npm run cli -- ${section} --help`)],
272
- };
273
- }
274
- function sddRows(sdd, project) {
275
- if (sdd === undefined)
276
- return [];
277
- const cockpit = sdd.cockpit;
278
- if (cockpit !== undefined) {
279
- return [
280
- {
281
- id: sdd.change,
282
- title: sdd.change,
283
- status: cockpit.actionablePhase ?? cockpit.next.status,
284
- summary: `actionable=${cockpit.actionablePhase ?? 'none'}; next=${cockpit.next.status}; accepted=${cockpit.acceptedCount}/${cockpit.phases.length}; legacy=${cockpit.legacyCount}`,
285
- detail: [
286
- `Recommended action: ${cockpit.recommendedAction}`,
287
- `Aggregate blockers: ${cockpit.aggregateBlockers.length === 0 ? 'none' : cockpit.aggregateBlockers.map((blocker) => `${blocker.kind}:${blocker.topicKey}`).join(', ')}`,
288
- `Inspect: ${cockpit.inspectCommand}`,
289
- 'Copy/inspect only: Enter shows details; it never runs commands.',
290
- verificationReportMetadataDetail(sdd),
291
- ...verificationPlanGuidanceDetail(),
292
- ],
293
- copyActions: [
294
- dashboardCopyAction('Inspect SDD cockpit', `npm run cli -- sdd cockpit --project ${project} --change ${sdd.change}`),
295
- verificationPlanCopyAction(),
296
- verificationReportCopyAction(project, sdd.change),
297
- ],
298
- },
299
- ];
300
- }
301
- return [
302
- {
303
- id: sdd.change,
304
- title: sdd.change,
305
- status: sdd.status.nextReadyPhase ?? 'no-ready-phase',
306
- summary: `${sdd.status.phases.filter((phase) => phase.present).length}/${sdd.status.phases.length} artifacts present; acceptance is not inferred.`,
307
- detail: [
308
- ...sdd.status.phases.map((phase) => `${phase.present ? 'present' : 'missing'}: ${phase.topicKey}`),
309
- verificationReportMetadataDetail(sdd),
310
- ...verificationPlanGuidanceDetail(),
311
- ],
312
- copyActions: [
313
- dashboardCopyAction('Inspect SDD status', `npm run cli -- sdd status --project ${project} --change ${sdd.change}`),
314
- verificationPlanCopyAction(),
315
- verificationReportCopyAction(project, sdd.change),
316
- ],
317
- },
318
- ];
319
- }
320
- function buildRunTraceItem(run, details, insights) {
321
- const checkpoints = details?.checkpoints ?? [];
322
- const lastCheckpoint = checkpoints.at(-1);
323
- const approvals = details?.approvals ?? [];
324
- const attempts = details?.operationAttempts ?? [];
325
- const relatedApprovals = approvals.map((approval) => {
326
- const attempt = attempts.find((candidate) => candidate.approvalId === approval.id || candidate.decisionEventId === approval.decisionEventId);
327
- return { id: approval.id, status: approval.status, ...(attempt === undefined ? {} : { category: attempt.category, operation: attempt.operation }) };
328
- });
329
- const relatedPreflights = attempts.map((attempt) => ({
330
- id: attempt.id,
331
- status: attempt.status,
332
- category: attempt.category,
333
- operation: attempt.operation,
334
- riskFlags: riskFlags(attempt),
335
- }));
336
- return {
337
- runId: run.id,
338
- project: run.project,
339
- workflow: run.workflow,
340
- phase: run.phase,
341
- status: run.status,
342
- ...(run.outcome === undefined ? {} : { outcome: run.outcome }),
343
- ...(run.outcomeReason === undefined ? {} : { outcomeReason: safeText(run.outcomeReason) }),
344
- selectedAgentId: run.selectedAgentId,
345
- model: run.model,
346
- createdAt: run.createdAt,
347
- updatedAt: run.updatedAt,
348
- ...(run.completedAt === undefined ? {} : { completedAt: run.completedAt }),
349
- ...(lastCheckpoint === undefined
350
- ? {}
351
- : {
352
- lastCheckpoint: {
353
- id: lastCheckpoint.id,
354
- label: lastCheckpoint.label,
355
- createdAt: lastCheckpoint.createdAt,
356
- stateSummary: summarizeJson(lastCheckpoint.state),
357
- },
358
- }),
359
- checkpointsSummary: checkpoints.length === 0 ? noLink : `${checkpoints.length} checkpoint(s); latest ${lastCheckpoint?.label ?? 'unknown'}`,
360
- relatedApprovals,
361
- relatedPreflights,
362
- blockers: insights?.resumePlan.blockers.map((blocker) => `${blocker.code}: ${safeText(blocker.message)}`) ?? [],
363
- latestTimeline: insights?.timeline.slice(-3).map((entry) => `${entry.type}: ${safeText(entry.label)}`) ?? [],
364
- nextActions: [
365
- dashboardCopyAction('Inspect run', `npm run cli -- runs get --id ${run.id}`),
366
- dashboardCopyAction('Inspect timeline', `npm run cli -- runs timeline --id ${run.id}`),
367
- dashboardCopyAction('Inspect resume plan', `npm run cli -- runs resume-inspect --id ${run.id}`),
368
- ],
369
- };
370
- }
371
- function approvalItemsForRun(run, details) {
372
- if (details === undefined)
373
- return [];
374
- const approvalItems = details.approvals.map((approval) => {
375
- const attempt = details.operationAttempts.find((candidate) => candidate.approvalId === approval.id || candidate.decisionEventId === approval.decisionEventId);
376
- return {
377
- id: approval.id,
378
- kind: 'approval',
379
- runId: run.id,
380
- category: attempt?.category ?? 'unknown',
381
- operation: attempt?.operation ?? 'unknown',
382
- status: approval.status,
383
- riskFlags: attempt === undefined ? [] : riskFlags(attempt),
384
- reason: safeText(approval.resolutionReason ?? (approval.status === 'pending' ? 'Awaiting explicit decision outside TUI.' : 'No reason recorded.')),
385
- agent: run.selectedAgentId,
386
- createdAt: approval.requestedAt,
387
- updatedAt: approval.resolvedAt ?? approval.requestedAt,
388
- nextActions: [
389
- dashboardCopyAction('Inspect run approvals', `npm run cli -- runs approvals --run-id ${run.id}`),
390
- dashboardCopyAction('Inspect resume gate', `npm run cli -- runs resume-gate --approval ${approval.id}`),
391
- ],
392
- };
393
- });
394
- const preflightItems = details.operationAttempts.map((attempt) => ({
395
- id: attempt.id,
396
- kind: 'preflight',
397
- runId: run.id,
398
- category: attempt.category,
399
- operation: attempt.operation,
400
- status: attempt.status,
401
- riskFlags: riskFlags(attempt),
402
- reason: summarizeJson(attempt.operationMetadata),
403
- agent: attempt.executorName,
404
- createdAt: attempt.reservedAt,
405
- updatedAt: attempt.updatedAt,
406
- nextActions: [
407
- dashboardCopyAction('Inspect run', `npm run cli -- runs get --id ${run.id}`),
408
- dashboardCopyAction('Evaluate retry', `npm run cli -- runs retry-check --approval ${attempt.approvalId ?? '<approval-id>'}`),
409
- ],
410
- }));
411
- return [...approvalItems, ...preflightItems];
412
- }
413
- function riskFlags(attempt) {
414
- const metadata = redactDashboardValue(attempt.operationMetadata);
415
- if (typeof metadata !== 'object' || metadata === null || Array.isArray(metadata))
416
- return [];
417
- return ['destructive', 'external', 'privileged', 'ambiguous', 'sandboxStrategy']
418
- .filter((key) => Object.hasOwn(metadata, key))
419
- .map((key) => `${key}=${String(metadata[key])}`);
420
- }
421
- function summarizeJson(value) {
422
- const redacted = redactDashboardValue(value);
423
- const rendered = typeof redacted === 'string' ? redacted : JSON.stringify(redacted);
424
- return safeText(rendered).slice(0, 180);
425
- }
426
- function safeText(value) {
427
- return redactJsonValue(value).replaceAll(/\s+/g, ' ').trim();
428
- }
@@ -1,158 +0,0 @@
1
- import { renderSetupStatus, setupNeedsAction } from './setup-status-renderer.js';
2
- export function renderDashboard(view) {
3
- return [
4
- 'VGXNESS Dashboard',
5
- '',
6
- renderProject(view),
7
- '',
8
- renderLatestRuns(view.runs),
9
- '',
10
- renderSelectedRun(view.selectedRun, view.selectedRunInsights),
11
- '',
12
- renderSetup(view.setup),
13
- '',
14
- renderSdd(view.sdd, view.project),
15
- '',
16
- renderNextDecision(view),
17
- '',
18
- renderSafetyBoundary(),
19
- '',
20
- ].join('\n');
21
- }
22
- function renderSetup(setup) {
23
- return setup === undefined
24
- ? ['Setup Status', '- Setup lifecycle status unavailable. Run `setup status` for JSON details.'].join('\n')
25
- : renderSetupStatus(setup);
26
- }
27
- function renderProject(view) {
28
- return ['Project', `- Name: ${view.project}`, `- Store: ${view.databasePath}`].join('\n');
29
- }
30
- function renderLatestRuns(runs) {
31
- if (runs.length === 0)
32
- return ['Latest Runs', '- No runs found for this project.'].join('\n');
33
- return ['Latest Runs', ...runs.map((run) => `- ${run.id}: ${run.status} ${run.workflow}/${run.phase} — ${run.userIntent}`)].join('\n');
34
- }
35
- function renderSelectedRun(run, insights) {
36
- if (run === undefined)
37
- return ['Selected Run', '- No run selected. Pass --run-id <id> for details.'].join('\n');
38
- const lines = [
39
- 'Selected Run',
40
- `- ID: ${run.id}`,
41
- `- Status: ${run.status}`,
42
- `- Intent: ${run.userIntent}`,
43
- `- Events: ${run.events.length}`,
44
- `- Checkpoints: ${run.checkpoints.length}`,
45
- `- Approvals: ${run.approvals.length}`,
46
- `- Operation attempts: ${run.operationAttempts.length}`,
47
- ];
48
- if (insights !== undefined)
49
- lines.push('', renderRunInsights(insights));
50
- return lines.join('\n');
51
- }
52
- function renderRunInsights(insights) {
53
- const lines = [
54
- 'Run Insights',
55
- `- Debug: ${insights.debug.cause} — ${insights.debug.summary}`,
56
- `- Recommended action: ${insights.debug.recommendedAction}`,
57
- `- Resume plan: ${renderResumePlan(insights.resumePlan)}`,
58
- ];
59
- if (insights.resumePlan.blockers.length > 0)
60
- lines.push(`- Blockers: ${insights.resumePlan.blockers.map((blocker) => `${blocker.code} — ${blocker.message}`).join('; ')}`);
61
- const latestTimeline = insights.timeline.slice(-3).map((entry) => `${entry.type}: ${entry.label}`);
62
- if (latestTimeline.length > 0)
63
- lines.push('- Latest timeline:', ...latestTimeline.map((entry) => ` - ${entry}`));
64
- return lines.join('\n');
65
- }
66
- function renderResumePlan(plan) {
67
- if (plan.resumable && plan.latestCheckpoint !== undefined)
68
- return `manual review eligible from checkpoint ${plan.latestCheckpoint.id}`;
69
- return 'blocked or unavailable';
70
- }
71
- function renderSdd(sdd, project) {
72
- if (sdd === undefined)
73
- return ['SDD Change', '- No change selected. Pass --change <id> to inspect workflow state.'].join('\n');
74
- const statusPresent = sdd.status.phases.filter((phase) => phase.present).map((phase) => phase.phase);
75
- if (statusPresent.length === 0) {
76
- return ['SDD Change', `- Change ${sdd.change} cannot be found yet. Pass --change <id> after saving SDD artifacts.`].join('\n');
77
- }
78
- if (sdd.cockpit !== undefined) {
79
- const cockpit = sdd.cockpit;
80
- const present = cockpit.phases.filter((phase) => phase.present).map((phase) => phase.phase);
81
- const missing = cockpit.phases.filter((phase) => !phase.present).map((phase) => phase.phase);
82
- return [
83
- 'SDD Change',
84
- `- Change: ${cockpit.change}`,
85
- `- Present: ${present.join(', ') || 'none'}`,
86
- `- Missing: ${missing.join(', ') || 'none'}`,
87
- `- Actionable phase: ${cockpit.actionablePhase ?? 'none'}`,
88
- `- Next status: ${cockpit.next.status}`,
89
- `- Recommended action: ${cockpit.recommendedAction}`,
90
- `- Accepted: ${cockpit.acceptedCount}/${cockpit.phases.length}`,
91
- `- Legacy artifacts: ${cockpit.legacyCount}`,
92
- `- Blockers: ${cockpit.aggregateBlockers.length === 0 ? 'none' : cockpit.aggregateBlockers.map((blocker) => `${blocker.phase}:${blocker.reason}`).join('; ')}`,
93
- `- ${verificationReportMetadataDetail(sdd)}`,
94
- `- Copy verification report command: ${verificationReportCommand(project, sdd.change)}`,
95
- `- Inspect with: ${cockpit.inspectCommand}`,
96
- ].join('\n');
97
- }
98
- const lines = [
99
- 'SDD Change',
100
- `- Change: ${sdd.change}`,
101
- `- Present: ${statusPresent.join(', ')}`,
102
- `- Missing: ${sdd.status.phases
103
- .filter((phase) => !phase.present)
104
- .map((phase) => phase.phase)
105
- .join(', ') || 'none'}`,
106
- ];
107
- if (sdd.status.nextReadyPhase !== undefined)
108
- lines.push(`- Next ready phase: ${sdd.status.nextReadyPhase}`);
109
- if (sdd.readiness !== undefined && !sdd.readiness.ready)
110
- lines.push(`- Missing prerequisites: ${sdd.readiness.missingArtifactTopicKeys.join(', ')}`);
111
- lines.push(`- ${verificationReportMetadataDetail(sdd)}`);
112
- lines.push(`- Copy verification report command: ${verificationReportCommand(project, sdd.change)}`);
113
- lines.push(`- Inspect with: sdd status --project ${project} --change ${sdd.change}`);
114
- return lines.join('\n');
115
- }
116
- function verificationReportCommand(project, change) {
117
- return `npm run cli -- verification report get --project ${project} --change ${change}`;
118
- }
119
- function verificationReportMetadataDetail(sdd) {
120
- const fallbackTopicKey = `sdd/${sdd.change}/verify`;
121
- const cockpitVerify = sdd.cockpit?.phases.find((phase) => phase.phase === 'verify');
122
- const statusVerify = sdd.status.phases.find((phase) => phase.phase === 'verify');
123
- const verify = cockpitVerify ?? statusVerify;
124
- const present = verify?.present === true;
125
- const topicKey = verify?.topicKey ?? fallbackTopicKey;
126
- if (!present)
127
- return `Verification report artifact: not present · topicKey=${topicKey}`;
128
- const state = verify?.state ?? 'present';
129
- const accepted = verify?.accepted === true ? 'yes' : 'no';
130
- const artifactId = cockpitVerify?.artifact?.artifactId ?? statusVerify?.artifactId ?? 'unavailable';
131
- return `Verification report artifact: present · state=${state} · accepted=${accepted} · topicKey=${topicKey} · artifactId=${artifactId}`;
132
- }
133
- function renderNextDecision(view) {
134
- if (view.setup !== undefined && setupNeedsAction(view.setup)) {
135
- return ['Next Decision', `- Resolve setup first with \`${view.setup.nextAction.command}\`: ${view.setup.nextAction.reason}.`].join('\n');
136
- }
137
- const sddNext = view.sdd?.status.nextReadyPhase;
138
- if (view.sdd?.cockpit !== undefined)
139
- return ['Next Decision', `- ${view.sdd.cockpit.recommendedAction} Inspect: ${view.sdd.cockpit.inspectCommand}.`].join('\n');
140
- const hasSddArtifacts = view.sdd?.status.phases.some((phase) => phase.present) ?? false;
141
- if (view.sdd !== undefined && hasSddArtifacts && sddNext !== undefined) {
142
- return [
143
- 'Next Decision',
144
- `- Continue the next SDD phase in OpenCode; CLI readiness is only a fallback: \`sdd ready --project ${view.project} --change ${view.sdd.change} --phase ${sddNext}\`.`,
145
- ].join('\n');
146
- }
147
- if (view.selectedRun !== undefined)
148
- return ['Next Decision', '- Inspect selected run events, checkpoints, approvals, or operation attempts.'].join('\n');
149
- if (view.runs.length === 0)
150
- return ['Next Decision', '- Create a run or pass --change <id> to inspect SDD state.'].join('\n');
151
- return ['Next Decision', '- Select a run with --run-id <id> or pass --change <id> for workflow readiness.'].join('\n');
152
- }
153
- function renderSafetyBoundary() {
154
- return [
155
- 'Safety Boundary',
156
- '- Read-only dashboard: no preflight, provider execution, shell execution, config writes, workers, sandbox, worktree, or OpenSpec writes.',
157
- ].join('\n');
158
- }