vigthoria-cli 1.6.54 → 1.6.55

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.
@@ -1556,6 +1556,20 @@ class ChatCommand {
1556
1556
  console.log(chalk_1.default.yellow(`Template Service preview gate: failed${previewGate.error ? ` - ${previewGate.error}` : ''}`));
1557
1557
  }
1558
1558
  }
1559
+ // Show change summary for files touched by the agent
1560
+ if (!this.jsonOutput && !this.directPromptMode && response.changedFiles) {
1561
+ const fileCount = Object.keys(response.changedFiles).length;
1562
+ if (fileCount > 0) {
1563
+ console.log(chalk_1.default.gray(`\nFiles changed: ${fileCount}`));
1564
+ for (const relPath of Object.keys(response.changedFiles).slice(0, 15)) {
1565
+ console.log(chalk_1.default.gray(` ${chalk_1.default.green('+')} ${relPath}`));
1566
+ }
1567
+ if (fileCount > 15) {
1568
+ console.log(chalk_1.default.gray(` ... and ${fileCount - 15} more`));
1569
+ }
1570
+ console.log(chalk_1.default.gray(`Run ${chalk_1.default.cyan('vigthoria preview --diff')} for full visual diffs.`));
1571
+ }
1572
+ }
1559
1573
  this.messages.push({ role: 'assistant', content: response.content || 'V3 agent workflow completed.' });
1560
1574
  watcher?.stop();
1561
1575
  return true;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Vigthoria CLI - Preview Command
3
+ *
4
+ * Local preview server + consolidated visual diffs + Template Service proof gate
5
+ *
6
+ * Usage:
7
+ * vigthoria preview - Preview project in browser
8
+ * vigthoria preview --diff - Show consolidated diff of recent agent changes
9
+ * vigthoria preview --proof - Run Template Service preview gate
10
+ * vigthoria preview -p /path/to/project - Preview specific project
11
+ */
12
+ import { Config } from '../utils/config.js';
13
+ import { Logger } from '../utils/logger.js';
14
+ interface PreviewOptions {
15
+ project: string;
16
+ entry?: string;
17
+ port?: number;
18
+ open?: boolean;
19
+ diff?: boolean;
20
+ proof?: boolean;
21
+ screenshot?: boolean;
22
+ }
23
+ export declare class PreviewCommand {
24
+ private config;
25
+ private logger;
26
+ private api;
27
+ private server;
28
+ constructor(config: Config, logger: Logger);
29
+ run(options: PreviewOptions): Promise<void>;
30
+ /**
31
+ * Detect the HTML entry file in the project
32
+ */
33
+ private detectEntryFile;
34
+ /**
35
+ * Find an available port starting from the given number
36
+ */
37
+ private findAvailablePort;
38
+ /**
39
+ * Start a local HTTP server for preview
40
+ */
41
+ private startServer;
42
+ /**
43
+ * Open URL in default browser
44
+ */
45
+ private openBrowser;
46
+ /**
47
+ * Show consolidated diff of recent agent changes using git
48
+ */
49
+ showConsolidatedDiff(projectPath: string): Promise<void>;
50
+ /**
51
+ * Run Template Service preview gate and persist proof bundle
52
+ */
53
+ private runProofGate;
54
+ }
55
+ export {};
@@ -0,0 +1,478 @@
1
+ "use strict";
2
+ /**
3
+ * Vigthoria CLI - Preview Command
4
+ *
5
+ * Local preview server + consolidated visual diffs + Template Service proof gate
6
+ *
7
+ * Usage:
8
+ * vigthoria preview - Preview project in browser
9
+ * vigthoria preview --diff - Show consolidated diff of recent agent changes
10
+ * vigthoria preview --proof - Run Template Service preview gate
11
+ * vigthoria preview -p /path/to/project - Preview specific project
12
+ */
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
46
+ var __importDefault = (this && this.__importDefault) || function (mod) {
47
+ return (mod && mod.__esModule) ? mod : { "default": mod };
48
+ };
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.PreviewCommand = void 0;
51
+ const chalk_1 = __importDefault(require("chalk"));
52
+ const fs = __importStar(require("fs"));
53
+ const path = __importStar(require("path"));
54
+ const http = __importStar(require("http"));
55
+ const diff_1 = require("diff");
56
+ const logger_js_1 = require("../utils/logger.js");
57
+ const api_js_1 = require("../utils/api.js");
58
+ // Common MIME types for static file serving
59
+ const MIME_TYPES = {
60
+ '.html': 'text/html',
61
+ '.htm': 'text/html',
62
+ '.css': 'text/css',
63
+ '.js': 'application/javascript',
64
+ '.mjs': 'application/javascript',
65
+ '.json': 'application/json',
66
+ '.png': 'image/png',
67
+ '.jpg': 'image/jpeg',
68
+ '.jpeg': 'image/jpeg',
69
+ '.gif': 'image/gif',
70
+ '.svg': 'image/svg+xml',
71
+ '.ico': 'image/x-icon',
72
+ '.webp': 'image/webp',
73
+ '.woff': 'font/woff',
74
+ '.woff2': 'font/woff2',
75
+ '.ttf': 'font/ttf',
76
+ '.eot': 'application/vnd.ms-fontobject',
77
+ '.mp4': 'video/mp4',
78
+ '.webm': 'video/webm',
79
+ '.mp3': 'audio/mpeg',
80
+ '.wav': 'audio/wav',
81
+ '.pdf': 'application/pdf',
82
+ '.xml': 'application/xml',
83
+ '.txt': 'text/plain',
84
+ '.map': 'application/json',
85
+ };
86
+ class PreviewCommand {
87
+ config;
88
+ logger;
89
+ api;
90
+ server = null;
91
+ constructor(config, logger) {
92
+ this.config = config;
93
+ this.logger = logger;
94
+ this.api = new api_js_1.APIClient(config, logger);
95
+ }
96
+ async run(options) {
97
+ const projectPath = path.resolve(options.project || process.cwd());
98
+ if (!fs.existsSync(projectPath) || !fs.statSync(projectPath).isDirectory()) {
99
+ this.logger.error(`Project directory not found: ${projectPath}`);
100
+ this.api.destroy();
101
+ return;
102
+ }
103
+ console.log();
104
+ console.log(chalk_1.default.bold.white(` ${logger_js_1.CH.hLine.repeat(3)} Vigthoria Preview ${logger_js_1.CH.hLine.repeat(40)}`));
105
+ console.log(chalk_1.default.gray(` Project: ${projectPath}`));
106
+ console.log();
107
+ // Show consolidated diff of recent agent changes
108
+ if (options.diff) {
109
+ await this.showConsolidatedDiff(projectPath);
110
+ }
111
+ // Run Template Service preview proof gate
112
+ if (options.proof) {
113
+ await this.runProofGate(projectPath, options.screenshot);
114
+ }
115
+ // If only --diff or --proof without a preview server, exit
116
+ if ((options.diff || options.proof) && options.open === false) {
117
+ this.api.destroy();
118
+ return;
119
+ }
120
+ // Detect entry file
121
+ const entryFile = this.detectEntryFile(projectPath, options.entry);
122
+ if (!entryFile) {
123
+ this.logger.warn('No HTML entry file found. Use --entry <file> to specify one.');
124
+ if (!options.diff && !options.proof) {
125
+ this.api.destroy();
126
+ return;
127
+ }
128
+ this.api.destroy();
129
+ return;
130
+ }
131
+ // Start local preview server
132
+ const port = options.port || (await this.findAvailablePort(3500));
133
+ await this.startServer(projectPath, entryFile, port, options.open !== false);
134
+ }
135
+ /**
136
+ * Detect the HTML entry file in the project
137
+ */
138
+ detectEntryFile(projectPath, specified) {
139
+ if (specified) {
140
+ const fullPath = path.join(projectPath, specified);
141
+ if (fs.existsSync(fullPath))
142
+ return specified;
143
+ this.logger.warn(`Specified entry file not found: ${specified}`);
144
+ return null;
145
+ }
146
+ // Common entry point candidates
147
+ const candidates = [
148
+ 'index.html',
149
+ 'public/index.html',
150
+ 'dist/index.html',
151
+ 'build/index.html',
152
+ 'src/index.html',
153
+ 'out/index.html',
154
+ ];
155
+ for (const candidate of candidates) {
156
+ if (fs.existsSync(path.join(projectPath, candidate))) {
157
+ return candidate;
158
+ }
159
+ }
160
+ // Fallback: find any .html file at root
161
+ try {
162
+ const rootFiles = fs.readdirSync(projectPath);
163
+ const htmlFile = rootFiles.find(f => f.endsWith('.html'));
164
+ if (htmlFile)
165
+ return htmlFile;
166
+ }
167
+ catch { /* ignore */ }
168
+ return null;
169
+ }
170
+ /**
171
+ * Find an available port starting from the given number
172
+ */
173
+ findAvailablePort(startPort) {
174
+ return new Promise((resolve) => {
175
+ const testServer = http.createServer();
176
+ testServer.on('error', () => {
177
+ resolve(this.findAvailablePort(startPort + 1));
178
+ });
179
+ testServer.listen(startPort, () => {
180
+ testServer.close(() => resolve(startPort));
181
+ });
182
+ });
183
+ }
184
+ /**
185
+ * Start a local HTTP server for preview
186
+ */
187
+ async startServer(projectPath, entryFile, port, autoOpen) {
188
+ return new Promise((resolve) => {
189
+ this.server = http.createServer((req, res) => {
190
+ // Sanitize URL to prevent path traversal
191
+ const urlPath = decodeURIComponent(new URL(req.url || '/', `http://localhost:${port}`).pathname);
192
+ const safePath = path.normalize(urlPath).replace(/^(\.\.[/\\])+/, '');
193
+ let filePath = path.join(projectPath, safePath);
194
+ // Ensure the resolved path is within the project directory
195
+ const resolvedPath = path.resolve(filePath);
196
+ if (!resolvedPath.startsWith(path.resolve(projectPath))) {
197
+ res.writeHead(403);
198
+ res.end('Forbidden');
199
+ return;
200
+ }
201
+ // Directory → serve index.html
202
+ if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) {
203
+ filePath = path.join(filePath, 'index.html');
204
+ }
205
+ // SPA fallback
206
+ if (!fs.existsSync(filePath)) {
207
+ const spaFallback = path.join(projectPath, entryFile);
208
+ if (fs.existsSync(spaFallback)) {
209
+ filePath = spaFallback;
210
+ }
211
+ else {
212
+ res.writeHead(404);
213
+ res.end('Not found');
214
+ return;
215
+ }
216
+ }
217
+ const ext = path.extname(filePath).toLowerCase();
218
+ const contentType = MIME_TYPES[ext] || 'application/octet-stream';
219
+ try {
220
+ const content = fs.readFileSync(filePath);
221
+ res.writeHead(200, { 'Content-Type': contentType });
222
+ res.end(content);
223
+ }
224
+ catch {
225
+ res.writeHead(500);
226
+ res.end('Internal server error');
227
+ }
228
+ });
229
+ this.server.listen(port, () => {
230
+ const url = `http://localhost:${port}/${entryFile}`;
231
+ console.log(chalk_1.default.green(` ${logger_js_1.CH.success} Preview server running`));
232
+ console.log(chalk_1.default.gray(` URL: `) + chalk_1.default.cyan.underline(url));
233
+ console.log(chalk_1.default.gray(` Entry: ${entryFile}`));
234
+ console.log(chalk_1.default.gray(` Press Ctrl+C to stop`));
235
+ console.log();
236
+ if (autoOpen) {
237
+ this.openBrowser(url);
238
+ }
239
+ });
240
+ // Handle graceful shutdown
241
+ const shutdown = () => {
242
+ console.log(chalk_1.default.gray('\n Stopping preview server...'));
243
+ this.server?.close();
244
+ this.api.destroy();
245
+ resolve();
246
+ };
247
+ process.on('SIGINT', shutdown);
248
+ process.on('SIGTERM', shutdown);
249
+ });
250
+ }
251
+ /**
252
+ * Open URL in default browser
253
+ */
254
+ openBrowser(url) {
255
+ const { exec } = require('child_process');
256
+ const platform = process.platform;
257
+ const cmd = platform === 'darwin' ? 'open' : platform === 'win32' ? 'start' : 'xdg-open';
258
+ exec(`${cmd} ${url}`, (err) => {
259
+ if (err) {
260
+ this.logger.debug(`Could not auto-open browser: ${err.message}`);
261
+ }
262
+ });
263
+ }
264
+ /**
265
+ * Show consolidated diff of recent agent changes using git
266
+ */
267
+ async showConsolidatedDiff(projectPath) {
268
+ console.log(chalk_1.default.bold.white(` ${logger_js_1.CH.hLine.repeat(3)} Change Summary ${logger_js_1.CH.hLine.repeat(43)}`));
269
+ console.log();
270
+ const proofDir = path.join(projectPath, '.vigthoria', 'proof', 'preview');
271
+ const hasProof = fs.existsSync(proofDir);
272
+ // Try git diff first (most reliable)
273
+ try {
274
+ const { execSync } = require('child_process');
275
+ const isGit = fs.existsSync(path.join(projectPath, '.git'));
276
+ if (isGit) {
277
+ // Get list of changed files (unstaged + staged)
278
+ const statusOutput = execSync('git status --porcelain', { cwd: projectPath, encoding: 'utf-8' }).trim();
279
+ if (!statusOutput) {
280
+ console.log(chalk_1.default.gray(' No changes detected (working tree clean).'));
281
+ console.log();
282
+ return;
283
+ }
284
+ const changedLines = statusOutput.split('\n').filter(Boolean);
285
+ const created = [];
286
+ const modified = [];
287
+ const deleted = [];
288
+ for (const line of changedLines) {
289
+ const status = line.substring(0, 2).trim();
290
+ const filePath = line.substring(3);
291
+ if (status === '??' || status === 'A')
292
+ created.push(filePath);
293
+ else if (status === 'D')
294
+ deleted.push(filePath);
295
+ else
296
+ modified.push(filePath);
297
+ }
298
+ // Summary header
299
+ const total = created.length + modified.length + deleted.length;
300
+ console.log(chalk_1.default.white(` ${total} file${total !== 1 ? 's' : ''} changed:`));
301
+ for (const f of created)
302
+ console.log(chalk_1.default.green(` + ${f}`));
303
+ for (const f of modified)
304
+ console.log(chalk_1.default.yellow(` ~ ${f}`));
305
+ for (const f of deleted)
306
+ console.log(chalk_1.default.red(` - ${f}`));
307
+ console.log();
308
+ // Show unified diffs for modified and created files (limit to keep terminal manageable)
309
+ const diffTargets = [...modified, ...created].slice(0, 20);
310
+ for (const relPath of diffTargets) {
311
+ const absPath = path.join(projectPath, relPath);
312
+ if (!fs.existsSync(absPath))
313
+ continue;
314
+ const ext = path.extname(relPath).toLowerCase();
315
+ const textExts = ['.html', '.css', '.js', '.ts', '.jsx', '.tsx', '.json', '.md', '.yml', '.yaml', '.txt', '.xml', '.svg', '.py', '.sh', '.env'];
316
+ if (!textExts.includes(ext))
317
+ continue;
318
+ try {
319
+ const newContent = fs.readFileSync(absPath, 'utf-8');
320
+ let oldContent = '';
321
+ if (modified.includes(relPath)) {
322
+ try {
323
+ oldContent = execSync(`git show HEAD:${relPath}`, { cwd: projectPath, encoding: 'utf-8' });
324
+ }
325
+ catch {
326
+ // File is new or not tracked
327
+ }
328
+ }
329
+ const patch = (0, diff_1.structuredPatch)(relPath, relPath, oldContent, newContent, 'before', 'after', { context: 3 });
330
+ if (patch.hunks.length === 0)
331
+ continue;
332
+ console.log(chalk_1.default.bold.white(` ${logger_js_1.CH.hLine.repeat(3)} ${relPath} ${logger_js_1.CH.hLine.repeat(Math.max(1, 50 - relPath.length))}`));
333
+ console.log(chalk_1.default.gray(` --- a/${relPath}`));
334
+ console.log(chalk_1.default.gray(` +++ b/${relPath}`));
335
+ for (const hunk of patch.hunks) {
336
+ console.log(chalk_1.default.cyan(` @@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`));
337
+ for (const line of hunk.lines) {
338
+ if (line.startsWith('+')) {
339
+ console.log(chalk_1.default.green(` ${line}`));
340
+ }
341
+ else if (line.startsWith('-')) {
342
+ console.log(chalk_1.default.red(` ${line}`));
343
+ }
344
+ else {
345
+ console.log(chalk_1.default.gray(` ${line}`));
346
+ }
347
+ }
348
+ }
349
+ console.log();
350
+ }
351
+ catch {
352
+ // Skip files that can't be diffed
353
+ }
354
+ }
355
+ if (diffTargets.length < modified.length + created.length) {
356
+ console.log(chalk_1.default.gray(` ... and ${modified.length + created.length - diffTargets.length} more files`));
357
+ console.log();
358
+ }
359
+ return;
360
+ }
361
+ }
362
+ catch {
363
+ // git not available, fall through
364
+ }
365
+ // Fallback: list .vigthoria/proof manifests
366
+ if (hasProof) {
367
+ const manifests = fs.readdirSync(proofDir)
368
+ .filter(f => f.endsWith('.json'))
369
+ .sort()
370
+ .reverse()
371
+ .slice(0, 5);
372
+ if (manifests.length > 0) {
373
+ console.log(chalk_1.default.white(' Recent proof bundles:'));
374
+ for (const m of manifests) {
375
+ try {
376
+ const manifest = JSON.parse(fs.readFileSync(path.join(proofDir, m), 'utf-8'));
377
+ const passed = manifest.previewGate?.passed ? chalk_1.default.green('passed') : chalk_1.default.red('failed');
378
+ console.log(chalk_1.default.gray(` ${manifest.createdAt || m} `) + passed + chalk_1.default.gray(` ${manifest.entryPath || '-'}`));
379
+ }
380
+ catch {
381
+ console.log(chalk_1.default.gray(` ${m}`));
382
+ }
383
+ }
384
+ console.log();
385
+ }
386
+ }
387
+ else {
388
+ console.log(chalk_1.default.gray(' No git history or proof bundles found.'));
389
+ console.log(chalk_1.default.gray(' Run an agent task first to generate changes.'));
390
+ console.log();
391
+ }
392
+ }
393
+ /**
394
+ * Run Template Service preview gate and persist proof bundle
395
+ */
396
+ async runProofGate(projectPath, captureScreenshot) {
397
+ const spinner = (0, logger_js_1.createSpinner)('Running preview proof gate...').start();
398
+ try {
399
+ const result = await this.api.runTemplateServicePreviewGate('', {
400
+ workspacePath: projectPath,
401
+ projectPath: projectPath,
402
+ targetPath: projectPath,
403
+ });
404
+ spinner.stop();
405
+ console.log(chalk_1.default.bold.white(` ${logger_js_1.CH.hLine.repeat(3)} Preview Proof Gate ${logger_js_1.CH.hLine.repeat(39)}`));
406
+ console.log();
407
+ if (!result.required) {
408
+ console.log(chalk_1.default.gray(' Preview gate: not required (no frontend artifacts detected)'));
409
+ console.log();
410
+ return;
411
+ }
412
+ const statusIcon = result.passed ? chalk_1.default.green(logger_js_1.CH.success) : chalk_1.default.red(logger_js_1.CH.error);
413
+ console.log(` ${statusIcon} Preview gate: ${result.passed ? chalk_1.default.green('PASSED') : chalk_1.default.red('FAILED')}`);
414
+ if (result.error) {
415
+ console.log(chalk_1.default.yellow(` Error: ${result.error}`));
416
+ }
417
+ // Show modes
418
+ if (result.modes) {
419
+ const modes = result.modes;
420
+ console.log();
421
+ if (modes.design) {
422
+ const designStatus = modes.design.ready ? chalk_1.default.green('ready') : chalk_1.default.gray('not ready');
423
+ console.log(chalk_1.default.gray(` Design mode: `) + designStatus);
424
+ if (modes.design.devices) {
425
+ console.log(chalk_1.default.gray(` Devices: ${modes.design.devices.join(', ')}`));
426
+ }
427
+ }
428
+ if (modes.live) {
429
+ const liveStatus = modes.live.ready ? chalk_1.default.green('ready') : chalk_1.default.gray('not ready');
430
+ console.log(chalk_1.default.gray(` Live mode: `) + liveStatus);
431
+ if (modes.live.entryPoint) {
432
+ console.log(chalk_1.default.gray(` Entry: ${modes.live.entryPoint}`));
433
+ }
434
+ }
435
+ if (modes.production) {
436
+ const prodStatus = modes.production.ready ? chalk_1.default.green('ready') : chalk_1.default.gray('not ready');
437
+ console.log(chalk_1.default.gray(` Production mode: `) + prodStatus);
438
+ }
439
+ }
440
+ // Show summary
441
+ if (result.summary) {
442
+ console.log();
443
+ const summary = result.summary;
444
+ if (summary.hasViewportMeta !== undefined) {
445
+ console.log(chalk_1.default.gray(` Viewport meta: `) + (summary.hasViewportMeta ? chalk_1.default.green('yes') : chalk_1.default.yellow('missing')));
446
+ }
447
+ if (summary.hasResponsiveSignals !== undefined) {
448
+ console.log(chalk_1.default.gray(` Responsive CSS: `) + (summary.hasResponsiveSignals ? chalk_1.default.green('yes') : chalk_1.default.gray('no')));
449
+ }
450
+ if (summary.hasInteractiveSignals !== undefined) {
451
+ console.log(chalk_1.default.gray(` Interactive JS: `) + (summary.hasInteractiveSignals ? chalk_1.default.green('yes') : chalk_1.default.gray('no')));
452
+ }
453
+ if (typeof summary.sectionCount === 'number') {
454
+ console.log(chalk_1.default.gray(` Section count: ${summary.sectionCount}`));
455
+ }
456
+ }
457
+ // Show artifacts
458
+ if (result.artifacts) {
459
+ console.log();
460
+ if (result.artifacts.manifestPath) {
461
+ console.log(chalk_1.default.gray(` Manifest: ${result.artifacts.manifestPath}`));
462
+ }
463
+ if (result.artifacts.screenshotCaptured && result.artifacts.screenshotPath) {
464
+ console.log(chalk_1.default.gray(` Screenshot: ${result.artifacts.screenshotPath}`));
465
+ }
466
+ if (result.artifacts.previewFileUrl) {
467
+ console.log(chalk_1.default.gray(` File URL: `) + chalk_1.default.cyan.underline(result.artifacts.previewFileUrl));
468
+ }
469
+ }
470
+ console.log();
471
+ }
472
+ catch (error) {
473
+ spinner.stop();
474
+ this.logger.error(`Preview proof gate failed: ${error?.message || String(error)}`);
475
+ }
476
+ }
477
+ }
478
+ exports.PreviewCommand = PreviewCommand;
package/dist/index.js CHANGED
@@ -66,6 +66,7 @@ const repo_js_1 = require("./commands/repo.js");
66
66
  const deploy_js_1 = require("./commands/deploy.js");
67
67
  const bridge_js_1 = require("./commands/bridge.js");
68
68
  const workflow_js_1 = require("./commands/workflow.js");
69
+ const preview_js_1 = require("./commands/preview.js");
69
70
  const config_js_2 = require("./utils/config.js");
70
71
  const logger_js_1 = require("./utils/logger.js");
71
72
  const chalk_1 = __importDefault(require("chalk"));
@@ -661,6 +662,29 @@ async function main() {
661
662
  project: options.project
662
663
  });
663
664
  });
665
+ // ==================== PREVIEW COMMAND ====================
666
+ program
667
+ .command('preview')
668
+ .description('Preview project locally with visual diffs and proof validation')
669
+ .option('-p, --project <path>', 'Project directory path', process.cwd())
670
+ .option('-e, --entry <file>', 'Entry HTML file (auto-detected if omitted)')
671
+ .option('--port <number>', 'Local server port', parseInt)
672
+ .option('--no-open', 'Do not auto-open browser')
673
+ .option('--diff', 'Show consolidated diff of recent agent changes')
674
+ .option('--proof', 'Run Template Service preview gate and persist proof bundle')
675
+ .option('--screenshot', 'Capture screenshot via Puppeteer')
676
+ .action(async (options) => {
677
+ const preview = new preview_js_1.PreviewCommand(config, logger);
678
+ await preview.run({
679
+ project: options.project,
680
+ entry: options.entry,
681
+ port: options.port,
682
+ open: options.open,
683
+ diff: options.diff,
684
+ proof: options.proof,
685
+ screenshot: options.screenshot,
686
+ });
687
+ });
664
688
  // ==================== AUTH COMMANDS ====================
665
689
  // Auth commands
666
690
  program
@@ -39,6 +39,7 @@ export interface V3AgentWorkflowResponse {
39
39
  contextId?: string | null;
40
40
  backendUrl: string;
41
41
  partial?: boolean;
42
+ changedFiles?: Record<string, string>;
42
43
  metadata?: Record<string, unknown>;
43
44
  }
44
45
  export interface FrontendPreviewGateResult {
package/dist/utils/api.js CHANGED
@@ -2831,6 +2831,7 @@ document.addEventListener('DOMContentLoaded', () => {
2831
2831
  contextId: finalContextId,
2832
2832
  backendUrl: baseUrl,
2833
2833
  partial: continuationData.checkpointed === true,
2834
+ changedFiles: continuationData.files || data.files || {},
2834
2835
  metadata: { source: 'v3-agent', mode: 'agent', contextId: finalContextId, continuations, previewGate },
2835
2836
  };
2836
2837
  }
@@ -2849,6 +2850,7 @@ document.addEventListener('DOMContentLoaded', () => {
2849
2850
  taskId: data.task_id || null,
2850
2851
  contextId,
2851
2852
  backendUrl: baseUrl,
2853
+ changedFiles: data.files || {},
2852
2854
  metadata: { source: 'v3-agent', mode: 'agent', contextId, mcpContextId, previewGate },
2853
2855
  };
2854
2856
  }
@@ -2864,6 +2866,7 @@ document.addEventListener('DOMContentLoaded', () => {
2864
2866
  contextId: error.partialData.context_id || requestExecutionContext.contextId || executionContext.contextId || null,
2865
2867
  backendUrl: baseUrl,
2866
2868
  partial: true,
2869
+ changedFiles: error.partialData.files || {},
2867
2870
  metadata: { source: 'v3-agent', mode: 'agent', partial: true, contextId: error.partialData.context_id || requestExecutionContext.contextId || executionContext.contextId || null, mcpContextId: requestExecutionContext.mcpContextId || executionContext.mcpContextId || null, previewGate },
2868
2871
  };
2869
2872
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.6.54",
3
+ "version": "1.6.55",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "main": "dist/index.js",
6
6
  "files": [