@decantr/cli 3.4.2 → 3.5.1

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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  cmdHeal,
3
3
  collectCheckIssues
4
- } from "./chunk-2GCVVEQC.js";
4
+ } from "./chunk-PM7DKABI.js";
5
5
  export {
6
6
  cmdHeal,
7
7
  collectCheckIssues
@@ -13,8 +13,8 @@ import {
13
13
  renderProjectHealthCiWorkflow,
14
14
  shouldFailHealth,
15
15
  writeProjectHealthCiWorkflow
16
- } from "./chunk-PQKTJGYL.js";
17
- import "./chunk-2GCVVEQC.js";
16
+ } from "./chunk-EUEQMXN5.js";
17
+ import "./chunk-PM7DKABI.js";
18
18
  export {
19
19
  cmdHealth,
20
20
  collectDesignTokenEvidence,
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import "./chunk-32WTNJQU.js";
1
+ import "./chunk-S3ZDXBRM.js";
2
2
  import "./chunk-SIDKK73N.js";
3
- import "./chunk-BDA6TWV3.js";
4
- import "./chunk-PQKTJGYL.js";
5
- import "./chunk-2GCVVEQC.js";
3
+ import "./chunk-MDQSTAP5.js";
4
+ import "./chunk-EUEQMXN5.js";
5
+ import "./chunk-PM7DKABI.js";
@@ -1,22 +1,23 @@
1
1
  import {
2
2
  createWorkspaceHealthReport
3
- } from "./chunk-BDA6TWV3.js";
3
+ } from "./chunk-MDQSTAP5.js";
4
4
  import {
5
5
  createProjectHealthReport
6
- } from "./chunk-PQKTJGYL.js";
6
+ } from "./chunk-EUEQMXN5.js";
7
7
  import {
8
8
  sendStudioHealthRefreshedTelemetry,
9
9
  sendStudioStartedTelemetry
10
- } from "./chunk-2GCVVEQC.js";
10
+ } from "./chunk-PM7DKABI.js";
11
11
 
12
12
  // src/commands/studio.ts
13
- import { readFileSync } from "fs";
13
+ import { existsSync, readFileSync } from "fs";
14
14
  import { createServer } from "http";
15
- import { isAbsolute, resolve } from "path";
15
+ import { isAbsolute, join, resolve } from "path";
16
+ import { PROJECT_HEALTH_REPORT_V2_SCHEMA_URL } from "@decantr/verifier";
16
17
  var GREEN = "\x1B[32m";
17
18
  var CYAN = "\x1B[36m";
18
19
  var RESET = "\x1B[0m";
19
- var PROJECT_HEALTH_SCHEMA_URL = "https://decantr.ai/schemas/project-health-report.v1.json";
20
+ var PROJECT_HEALTH_SCHEMA_URL = PROJECT_HEALTH_REPORT_V2_SCHEMA_URL;
20
21
  function sendJson(res, status, value) {
21
22
  const body = JSON.stringify(value, null, 2);
22
23
  res.writeHead(status, {
@@ -45,9 +46,9 @@ function isRecord(value) {
45
46
  function readProjectHealthReport(reportPath) {
46
47
  const parsed = JSON.parse(readFileSync(reportPath, "utf-8"));
47
48
  if (!isRecord(parsed) || parsed.$schema !== PROJECT_HEALTH_SCHEMA_URL) {
48
- throw new Error("Report file is not a Decantr Project Health JSON document.");
49
+ throw new Error("Report file is not a Decantr Project Health v2 JSON document.");
49
50
  }
50
- if (typeof parsed.generatedAt !== "string" || typeof parsed.projectRoot !== "string" || !["healthy", "warning", "error"].includes(String(parsed.status)) || typeof parsed.score !== "number" || !isRecord(parsed.summary) || !isRecord(parsed.routes) || !isRecord(parsed.packs) || !isRecord(parsed.ci) || !Array.isArray(parsed.findings)) {
51
+ if (typeof parsed.generatedAt !== "string" || typeof parsed.projectRoot !== "string" || !["healthy", "warning", "error"].includes(String(parsed.status)) || typeof parsed.score !== "number" || !isRecord(parsed.summary) || !isRecord(parsed.routes) || !isRecord(parsed.packs) || !isRecord(parsed.loop) || !isRecord(parsed.authority) || !isRecord(parsed.evidenceTier) || !isRecord(parsed.ci) || !Array.isArray(parsed.findings)) {
51
52
  throw new Error("Report file is not a Decantr Project Health JSON document.");
52
53
  }
53
54
  return parsed;
@@ -1147,6 +1148,294 @@ function workspaceStudioHtml() {
1147
1148
  </body>
1148
1149
  </html>`;
1149
1150
  }
1151
+ function studioControlRoomPayload(report) {
1152
+ return {
1153
+ generatedAt: report.generatedAt,
1154
+ projectRoot: report.projectRoot,
1155
+ status: report.status,
1156
+ score: report.score,
1157
+ summary: report.summary,
1158
+ loop: report.loop,
1159
+ authority: report.authority,
1160
+ evidenceTier: report.evidenceTier,
1161
+ graph: report.graph,
1162
+ blockingFindings: report.findings.filter((finding) => finding.severity === "error" || finding.loopVerdict !== "verified").slice(0, 12).map((finding) => ({
1163
+ id: finding.id,
1164
+ severity: finding.severity,
1165
+ source: finding.source,
1166
+ category: finding.category,
1167
+ message: finding.message,
1168
+ graph: finding.graph,
1169
+ authorityLane: finding.authorityLane,
1170
+ loopVerdict: finding.loopVerdict,
1171
+ commands: finding.repairPlan?.commands ?? finding.remediation.commands
1172
+ }))
1173
+ };
1174
+ }
1175
+ function studioTaskPreview(projectRoot, route, intent) {
1176
+ return {
1177
+ route,
1178
+ intent,
1179
+ command: route && intent ? `decantr task ${JSON.stringify(route)} ${JSON.stringify(intent)}` : 'decantr task <route> "<intent>"',
1180
+ notes: [
1181
+ "Run this before editing a route.",
1182
+ "If runtime source and Decantr context disagree, stop and report drift."
1183
+ ],
1184
+ projectRoot
1185
+ };
1186
+ }
1187
+ function readStudioProofReport(projectRoot) {
1188
+ const candidates = [
1189
+ join(projectRoot, ".decantr", "benchmarks", "proof-field-report.v2.json"),
1190
+ join(projectRoot, ".decantr", "proof-field-report.v2.json"),
1191
+ join(projectRoot, "docs", "benchmarks", "decantr-3-5-proof-field-report.json")
1192
+ ];
1193
+ for (const candidate of candidates) {
1194
+ if (existsSync(candidate)) {
1195
+ return JSON.parse(readFileSync(candidate, "utf-8"));
1196
+ }
1197
+ }
1198
+ return {
1199
+ $schema: "https://decantr.ai/schemas/proof-field-report.v2.json",
1200
+ schemaVersion: 2,
1201
+ status: "missing",
1202
+ message: "No local proof field report was found for this project.",
1203
+ searched: candidates
1204
+ };
1205
+ }
1206
+ function controlRoomHtml(reportMode = false, legacyRenderer) {
1207
+ void legacyRenderer;
1208
+ return `<!doctype html>
1209
+ <html lang="en">
1210
+ <head>
1211
+ <meta charset="utf-8">
1212
+ <meta name="viewport" content="width=device-width, initial-scale=1">
1213
+ <title>Decantr Control Room</title>
1214
+ <style>
1215
+ :root {
1216
+ color-scheme: dark;
1217
+ --bg: #111315;
1218
+ --surface: #181b1f;
1219
+ --surface-2: #20242a;
1220
+ --line: rgba(238, 241, 238, 0.13);
1221
+ --text: #eef1ee;
1222
+ --muted: #a9b1ac;
1223
+ --good: #6ae3a1;
1224
+ --warn: #f4c768;
1225
+ --bad: #ff7782;
1226
+ --accent: #8fd7ff;
1227
+ --ink: #0b0d0f;
1228
+ }
1229
+ * { box-sizing: border-box; }
1230
+ body {
1231
+ margin: 0;
1232
+ background: var(--bg);
1233
+ color: var(--text);
1234
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
1235
+ line-height: 1.45;
1236
+ }
1237
+ button { font: inherit; }
1238
+ code {
1239
+ display: inline-block;
1240
+ max-width: 100%;
1241
+ overflow-wrap: anywhere;
1242
+ border: 1px solid var(--line);
1243
+ background: #0e1012;
1244
+ color: var(--accent);
1245
+ padding: 0.25rem 0.4rem;
1246
+ border-radius: 6px;
1247
+ }
1248
+ .shell { min-height: 100vh; display: grid; grid-template-columns: minmax(220px, 280px) 1fr; }
1249
+ .rail { border-right: 1px solid var(--line); padding: 1rem; background: #14171a; position: sticky; top: 0; height: 100vh; }
1250
+ .brand { display: grid; gap: 0.2rem; margin-bottom: 1rem; }
1251
+ .brand h1 { font-size: 1.15rem; margin: 0; letter-spacing: 0; }
1252
+ .brand p { margin: 0; color: var(--muted); font-size: 0.85rem; }
1253
+ .tabs { display: grid; gap: 0.3rem; }
1254
+ .tab {
1255
+ width: 100%;
1256
+ min-height: 40px;
1257
+ border: 1px solid transparent;
1258
+ background: transparent;
1259
+ color: var(--muted);
1260
+ text-align: left;
1261
+ padding: 0.6rem 0.7rem;
1262
+ border-radius: 7px;
1263
+ cursor: pointer;
1264
+ }
1265
+ .tab.active, .tab:hover { color: var(--text); background: var(--surface-2); border-color: var(--line); }
1266
+ .main { padding: 1rem; display: grid; gap: 1rem; align-content: start; }
1267
+ .topbar { display: flex; justify-content: space-between; gap: 1rem; align-items: center; border-bottom: 1px solid var(--line); padding-bottom: 1rem; }
1268
+ .topbar h2 { margin: 0; font-size: 1.25rem; letter-spacing: 0; }
1269
+ .topbar p { margin: 0.2rem 0 0; color: var(--muted); font-size: 0.9rem; }
1270
+ .refresh {
1271
+ min-height: 38px;
1272
+ border: 1px solid var(--line);
1273
+ background: var(--surface-2);
1274
+ color: var(--text);
1275
+ border-radius: 7px;
1276
+ padding: 0 0.8rem;
1277
+ cursor: pointer;
1278
+ }
1279
+ .grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: 0.8rem; }
1280
+ .panel {
1281
+ grid-column: span 6;
1282
+ border: 1px solid var(--line);
1283
+ background: var(--surface);
1284
+ border-radius: 8px;
1285
+ padding: 0.9rem;
1286
+ min-width: 0;
1287
+ }
1288
+ .panel.wide { grid-column: 1 / -1; }
1289
+ .panel h3 { margin: 0 0 0.6rem; font-size: 0.95rem; letter-spacing: 0; }
1290
+ .metric { display: flex; align-items: baseline; gap: 0.45rem; margin: 0.2rem 0; }
1291
+ .metric strong { font-size: 1.65rem; letter-spacing: 0; }
1292
+ .muted { color: var(--muted); }
1293
+ .status-healthy { color: var(--good); }
1294
+ .status-warning { color: var(--warn); }
1295
+ .status-error, .status-blocked { color: var(--bad); }
1296
+ .list { display: grid; gap: 0.55rem; }
1297
+ .row { border-top: 1px solid var(--line); padding-top: 0.55rem; min-width: 0; }
1298
+ .row:first-child { border-top: 0; padding-top: 0; }
1299
+ .row-title { display: flex; gap: 0.5rem; flex-wrap: wrap; align-items: center; }
1300
+ .pill { border: 1px solid var(--line); border-radius: 999px; padding: 0.1rem 0.45rem; color: var(--muted); font-size: 0.78rem; }
1301
+ .pre { white-space: pre-wrap; overflow-wrap: anywhere; color: var(--muted); margin: 0; }
1302
+ .hide { display: none; }
1303
+ @media (max-width: 820px) {
1304
+ .shell { grid-template-columns: 1fr; }
1305
+ .rail { position: static; height: auto; }
1306
+ .tabs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
1307
+ .panel { grid-column: 1 / -1; }
1308
+ .topbar { align-items: stretch; flex-direction: column; }
1309
+ }
1310
+ </style>
1311
+ </head>
1312
+ <body>
1313
+ <div class="shell">
1314
+ <aside class="rail">
1315
+ <div class="brand">
1316
+ <h1>Decantr Control Room</h1>
1317
+ <p>${reportMode ? "Report artifact" : "Local project"}</p>
1318
+ </div>
1319
+ <nav class="tabs" aria-label="Studio views">
1320
+ <button class="tab active" data-view="control">Control</button>
1321
+ <button class="tab" data-view="routes">Routes</button>
1322
+ <button class="tab" data-view="graph">Graph</button>
1323
+ <button class="tab" data-view="authority">Authority</button>
1324
+ <button class="tab" data-view="evidence">Evidence</button>
1325
+ <button class="tab" data-view="repairs">Repairs</button>
1326
+ <button class="tab" data-view="ci">CI</button>
1327
+ </nav>
1328
+ </aside>
1329
+ <main class="main">
1330
+ <header class="topbar">
1331
+ <div>
1332
+ <h2 id="title">Control</h2>
1333
+ <p id="subtitle">Loading local health state...</p>
1334
+ </div>
1335
+ <button class="refresh" id="refresh" type="button">Refresh</button>
1336
+ </header>
1337
+ <section id="content" class="grid" aria-live="polite"></section>
1338
+ </main>
1339
+ </div>
1340
+ <script>
1341
+ const state = { view: 'control', health: null, control: null, resolve: null, evidence: null, graph: null, proof: null };
1342
+ const esc = (value) => String(value ?? '').replace(/[&<>"']/g, (char) => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[char]));
1343
+ const cls = (status) => 'status-' + String(status || 'warning').replace(/_/g, '-');
1344
+ async function getJson(path, options) {
1345
+ const response = await fetch(path, options);
1346
+ if (!response.ok) throw new Error(await response.text());
1347
+ return response.json();
1348
+ }
1349
+ async function load(refresh = false) {
1350
+ state.health = await getJson(refresh ? '/api/refresh' : '/api/health', refresh ? { method: 'POST' } : undefined);
1351
+ state.control = await getJson('/api/control-room');
1352
+ state.resolve = await getJson('/api/resolve');
1353
+ state.evidence = await getJson('/api/evidence');
1354
+ state.graph = await getJson('/api/graph-impact');
1355
+ state.proof = await getJson('/api/proof');
1356
+ render();
1357
+ }
1358
+ function panel(title, body, wide = false) {
1359
+ return '<article class="panel' + (wide ? ' wide' : '') + '"><h3>' + esc(title) + '</h3>' + body + '</article>';
1360
+ }
1361
+ function rows(items, empty) {
1362
+ if (!items || items.length === 0) return '<p class="muted">' + esc(empty) + '</p>';
1363
+ return '<div class="list">' + items.map((item) => '<div class="row">' + item + '</div>').join('') + '</div>';
1364
+ }
1365
+ function renderControl() {
1366
+ const report = state.health;
1367
+ const loop = report.loop || {};
1368
+ return [
1369
+ panel('Project Health', '<div class="metric"><strong class="' + cls(report.status) + '">' + esc(report.score) + '</strong><span>/100</span></div><p class="muted">' + esc(report.status) + ' | ' + esc(report.summary.findingCount) + ' finding(s)</p>'),
1370
+ panel('Loop State', '<div class="metric"><strong class="' + cls(loop.status) + '">' + esc(loop.state) + '</strong></div><p class="muted">' + esc(loop.nextActions?.[0] || loop.verdict) + '</p>'),
1371
+ panel('Authority Lane', '<p><strong>' + esc(report.authority?.activeLane) + '</strong></p><p class="muted">' + esc(report.authority?.summary) + '</p>', true),
1372
+ panel('Blocking Findings', rows(state.control?.blockingFindings?.map((finding) => '<div class="row-title"><strong>' + esc(finding.id) + '</strong><span class="pill">' + esc(finding.severity) + '</span><span class="pill">' + esc(finding.authorityLane) + '</span></div><p class="muted">' + esc(finding.message) + '</p>'), 'No blocking findings.'), true)
1373
+ ].join('');
1374
+ }
1375
+ function renderRoutes() {
1376
+ const routes = state.health.routes || {};
1377
+ const items = (routes.declared || []).map((route) => '<div class="row-title"><strong>' + esc(route) + '</strong><code>decantr task ' + esc(JSON.stringify(route)) + ' "&lt;intent&gt;"</code></div>');
1378
+ return [
1379
+ panel('Declared Routes', rows(items, 'No declared routes.'), true),
1380
+ panel('Runtime Coverage', '<p class="muted">Checked ' + esc((routes.runtimeChecked || []).length) + ' route(s), matched ' + esc(routes.runtimeMatched) + '.</p><pre class="pre">' + esc((routes.issues || []).join('\\n')) + '</pre>', true)
1381
+ ].join('');
1382
+ }
1383
+ function renderGraph() {
1384
+ const graph = state.graph?.graph || state.health.graph || {};
1385
+ const findings = state.graph?.findings || [];
1386
+ return [
1387
+ panel('Graph Impact', '<p><strong>' + esc(graph.current === false ? 'stale' : graph.ready ? 'ready' : 'missing') + '</strong></p><p class="muted">Snapshot ' + esc(graph.snapshotId || 'none') + ' | sources ' + esc(graph.sourceArtifactCount || 0) + '</p>'),
1388
+ panel('Anchored Findings', rows(findings.map((finding) => '<strong>' + esc(finding.id) + '</strong><p class="muted">' + esc(finding.graph?.node_type) + ' ' + esc(finding.graph?.node_id) + '</p>'), 'No graph-anchored findings.'))
1389
+ ].join('');
1390
+ }
1391
+ function renderAuthority() {
1392
+ const resolution = state.resolve || {};
1393
+ return [
1394
+ panel('Order', rows((resolution.order || []).map((item) => '<strong>' + esc(item.rank) + '. ' + item.label + '</strong><p class="muted">' + esc(item.role) + '</p>'), 'No authority order.'), true),
1395
+ panel('Conflicts', rows((resolution.conflicts || []).map((item) => '<div class="row-title"><strong>' + esc(item.id) + '</strong><span class="pill">' + esc(item.status) + '</span></div><p class="muted">' + esc(item.message) + '</p><pre class="pre">' + esc((item.recommendedActions || []).map((action) => action.kind + (action.command ? ' -> ' + action.command : '')).join('\\n')) + '</pre>'), 'No authority conflicts.'), true)
1396
+ ].join('');
1397
+ }
1398
+ function renderEvidence() {
1399
+ const tier = state.evidence?.evidenceTier || state.health.evidenceTier || {};
1400
+ return [
1401
+ panel('Evidence Tier', '<div class="metric"><strong>' + esc(tier.stage) + '</strong><span>' + esc(tier.confidence?.level) + ' ' + esc(tier.confidence?.score) + '</span></div><p class="muted">' + esc((tier.capabilities || []).join(', ')) + '</p>'),
1402
+ panel('Coverage', '<pre class="pre">' + esc(JSON.stringify(tier.coverage || {}, null, 2)) + '</pre>'),
1403
+ panel('Evidence Findings', rows((state.evidence?.findings || []).map((finding) => '<strong>' + esc(finding.id) + '</strong><p class="muted">' + esc(finding.source) + ' | ' + esc(finding.severity) + '</p>'), 'No evidence findings.'), true)
1404
+ ].join('');
1405
+ }
1406
+ function renderRepairs() {
1407
+ return panel('Repair Plans', rows((state.health.findings || []).map((finding) => '<div class="row-title"><strong>' + esc(finding.id) + '</strong><span class="pill">' + esc(finding.severity) + '</span></div><p class="muted">' + esc(finding.remediation?.summary || finding.message) + '</p><pre class="pre">' + esc((finding.repairPlan?.commands || finding.remediation?.commands || []).join('\\n')) + '</pre>'), 'No repair plans.'), true);
1408
+ }
1409
+ function renderCi() {
1410
+ const proof = state.proof || {};
1411
+ return [
1412
+ panel('CI Command', '<code>' + esc(state.health.ci?.recommendedCommand) + '</code><p class="muted">Loop verify: ' + esc(state.health.loop?.verifyCommand) + '</p>', true),
1413
+ panel('Proof Report', '<pre class="pre">' + esc(JSON.stringify(proof.summary || proof.message || proof, null, 2)) + '</pre>', true)
1414
+ ].join('');
1415
+ }
1416
+ function render() {
1417
+ const titles = { control: 'Control', routes: 'Routes', graph: 'Graph Impact', authority: 'Authority Resolver', evidence: 'Evidence', repairs: 'Repairs', ci: 'CI / Benchmarks' };
1418
+ document.getElementById('title').textContent = titles[state.view] || 'Control';
1419
+ document.getElementById('subtitle').textContent = (state.health?.projectRoot || '') + ' | ' + (state.health?.generatedAt || '');
1420
+ const renderers = { control: renderControl, routes: renderRoutes, graph: renderGraph, authority: renderAuthority, evidence: renderEvidence, repairs: renderRepairs, ci: renderCi };
1421
+ document.getElementById('content').innerHTML = (renderers[state.view] || renderControl)();
1422
+ }
1423
+ for (const button of document.querySelectorAll('.tab')) {
1424
+ button.addEventListener('click', () => {
1425
+ state.view = button.dataset.view;
1426
+ for (const tab of document.querySelectorAll('.tab')) tab.classList.toggle('active', tab === button);
1427
+ render();
1428
+ });
1429
+ }
1430
+ document.getElementById('refresh').addEventListener('click', () => load(true).catch((error) => alert(error.message)));
1431
+ load().catch((error) => {
1432
+ document.getElementById('subtitle').textContent = 'Load failed';
1433
+ document.getElementById('content').innerHTML = panel('Error', '<pre class="pre">' + esc(error.message) + '</pre>', true);
1434
+ });
1435
+ </script>
1436
+ </body>
1437
+ </html>`;
1438
+ }
1150
1439
  function createStudioRequestHandler(projectRoot, options = {}) {
1151
1440
  const reportPath = resolveReportPath(projectRoot, options.report);
1152
1441
  const loadReport = () => reportPath ? readProjectHealthReport(reportPath) : createProjectHealthReport(projectRoot);
@@ -1159,7 +1448,7 @@ function createStudioRequestHandler(projectRoot, options = {}) {
1159
1448
  sendHtml(res, workspaceStudioHtml());
1160
1449
  return;
1161
1450
  }
1162
- sendHtml(res, studioHtml(Boolean(reportPath)));
1451
+ sendHtml(res, controlRoomHtml(Boolean(reportPath), studioHtml));
1163
1452
  return;
1164
1453
  }
1165
1454
  if (options.workspace && req.method === "GET" && url.pathname === "/api/workspace") {
@@ -1174,6 +1463,60 @@ function createStudioRequestHandler(projectRoot, options = {}) {
1174
1463
  sendJson(res, 200, await loadReport());
1175
1464
  return;
1176
1465
  }
1466
+ if (req.method === "GET" && url.pathname === "/api/control-room") {
1467
+ sendJson(res, 200, studioControlRoomPayload(await loadReport()));
1468
+ return;
1469
+ }
1470
+ if (req.method === "GET" && url.pathname === "/api/resolve") {
1471
+ const report = await loadReport();
1472
+ sendJson(res, 200, report.authority);
1473
+ return;
1474
+ }
1475
+ if (req.method === "GET" && url.pathname === "/api/evidence") {
1476
+ const report = await loadReport();
1477
+ sendJson(res, 200, {
1478
+ evidenceTier: report.evidenceTier,
1479
+ findings: report.findings.map((finding) => ({
1480
+ id: finding.id,
1481
+ severity: finding.severity,
1482
+ source: finding.source,
1483
+ graph: finding.graph,
1484
+ evidenceTier: finding.evidenceTier,
1485
+ repairPlan: finding.repairPlan
1486
+ }))
1487
+ });
1488
+ return;
1489
+ }
1490
+ if (req.method === "GET" && url.pathname === "/api/graph-impact") {
1491
+ const report = await loadReport();
1492
+ sendJson(res, 200, {
1493
+ graph: report.graph,
1494
+ loopImpact: report.loop.graphImpact,
1495
+ findings: report.findings.filter((finding) => finding.graph).map((finding) => ({
1496
+ id: finding.id,
1497
+ severity: finding.severity,
1498
+ message: finding.message,
1499
+ graph: finding.graph
1500
+ }))
1501
+ });
1502
+ return;
1503
+ }
1504
+ if (req.method === "GET" && url.pathname === "/api/task-preview") {
1505
+ sendJson(
1506
+ res,
1507
+ 200,
1508
+ studioTaskPreview(
1509
+ projectRoot,
1510
+ url.searchParams.get("route"),
1511
+ url.searchParams.get("intent")
1512
+ )
1513
+ );
1514
+ return;
1515
+ }
1516
+ if (req.method === "GET" && url.pathname === "/api/proof") {
1517
+ sendJson(res, 200, readStudioProofReport(projectRoot));
1518
+ return;
1519
+ }
1177
1520
  if (req.method === "POST" && url.pathname === "/api/refresh") {
1178
1521
  const startedAt = Date.now();
1179
1522
  const report = await loadReport();
@@ -7,9 +7,9 @@ import {
7
7
  listWorkspaceProjects,
8
8
  parseWorkspaceArgs,
9
9
  shouldFailWorkspaceHealth
10
- } from "./chunk-BDA6TWV3.js";
11
- import "./chunk-PQKTJGYL.js";
12
- import "./chunk-2GCVVEQC.js";
10
+ } from "./chunk-MDQSTAP5.js";
11
+ import "./chunk-EUEQMXN5.js";
12
+ import "./chunk-PM7DKABI.js";
13
13
  export {
14
14
  cmdWorkspace,
15
15
  createWorkspaceHealthReport,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decantr/cli",
3
- "version": "3.4.2",
3
+ "version": "3.5.1",
4
4
  "description": "Decantr CLI - adopt, verify, graph, and govern frontend codebases touched by AI agents",
5
5
  "keywords": [
6
6
  "decantr",
@@ -49,11 +49,11 @@
49
49
  },
50
50
  "dependencies": {
51
51
  "ajv": "^8.20.0",
52
- "@decantr/core": "3.4.0",
53
- "@decantr/essence-spec": "3.4.0",
52
+ "@decantr/core": "3.5.0",
54
53
  "@decantr/registry": "3.4.0",
55
54
  "@decantr/telemetry": "3.4.0",
56
- "@decantr/verifier": "3.4.1"
55
+ "@decantr/verifier": "3.5.0",
56
+ "@decantr/essence-spec": "3.4.0"
57
57
  },
58
58
  "scripts": {
59
59
  "build": "tsup",