@decantr/cli 3.5.4 → 3.6.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.
- package/README.md +4 -2
- package/dist/bin.js +4 -4
- package/dist/{chunk-4WVKE2AQ.js → chunk-24JR4ZNG.js} +76 -3
- package/dist/{chunk-UDBPH25I.js → chunk-56HUEUO3.js} +13 -7
- package/dist/{chunk-GPGBJFMF.js → chunk-TGVBWTDK.js} +30 -15
- package/dist/{chunk-EWUWVQYM.js → chunk-WAGVDMJV.js} +97 -9
- package/dist/{heal-4BQCO4ST.js → heal-2FK63EQE.js} +1 -1
- package/dist/{health-WO2BQYIC.js → health-KUPBUU3B.js} +2 -2
- package/dist/index.js +4 -4
- package/dist/{studio-TXFV3PDM.js → studio-J6Z3AUAR.js} +3 -3
- package/dist/{workspace-G6PN53XI.js → workspace-QAZ3F4VT.js} +3 -3
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Use `decantr scan` when you want a zero-commit Brownfield preview. It reads loca
|
|
|
23
23
|
Use `decantr new` for a greenfield workspace in a fresh directory. With a starter-kit blueprint or archetype it uses the runnable adapter and Decantr CSS; without certified vocabulary content it creates a contract-only workspace unless you explicitly pass `--adoption=decantr-css`.
|
|
24
24
|
Use `decantr adopt` when you already have an app and want Decantr governance without adopting a blueprint. Brownfield attach is proposal-driven: Decantr inventories the app, writes an observed essence proposal, hydrates hosted execution packs when online, and only applies the contract when you explicitly accept or merge it.
|
|
25
25
|
Use `decantr studio` after adoption when you want a local Control Room for routes, findings, evidence, authority, and next actions. Use `decantr doctor` when the next step is unclear, `decantr task <route> "<intent>"` before asking an LLM to modify a route, `decantr verify` after the edit, `decantr resolve` when source and contract disagree, and `decantr ci` in required automation. If runtime source and Decantr context disagree, report the drift instead of guessing; in Brownfield the existing source is observed truth, accepted local law/style bridge is project authority where present, Essence V4 is the structural contract, and hosted packs stay advisory until mapped into local law. Use `decantr graph` when you want the Decantr 3 typed Contract graph, typed graph diff summary, manifest, content-addressed snapshot history, and cache-friendly contract capsule written under `.decantr/graph`; the capsule includes a bounded SourceArtifact path index so agents can discover valid file-impact handles without reading the full snapshot, and `--capsule-source-limit <count>` can tune that index for large repos. Add `--route /feed --task "improve loading" --json` when you want the exact task-ranked route-scoped subgraph an agent should inspect before editing, `--node cmp:button --impact --json` when you need the graph-shaped blast radius for a component, token, rule, finding, or source artifact, or `--file src/app/page.tsx --impact --json` when the agent knows the source file it is about to change. Route and impact ranking use deterministic weighted traversal plus local personalized PageRank and task boosts. Use `--snapshot-id <id>` to inspect a replayable history snapshot and `--compare-to <id> --include-diff-ops --json` to compare the selected/current graph against a prior snapshot. Use `decantr codify --from-audit --style-bridge` when you want project-owned UI patterns, optional `behavior_obligations`, local rules, and token/class bridge mappings such as button/card/shell/theme standards to appear in future task context and verification. Once accepted, that local law is the first Hybrid lane: the app still owns source and styling, but Decantr treats accepted local patterns, behavior obligations, rules, and style bridge mappings as project authority.
|
|
26
|
-
In monorepos, app-scoped commands accept `--project <app-path>`. `setup` shows attach guidance before adoption and the day-two loop after adoption. Hosted pack hydration also follows the essence path: `decantr registry compile-packs apps/web/decantr.essence.json --write-context` writes into `apps/web/.decantr/context`. In contract-only/offline Brownfield, deferred hosted packs are optional context unless a present manifest references missing files.
|
|
26
|
+
In monorepos, app-scoped commands accept `--project <app-path>`. `setup` shows attach guidance before adoption and the day-two loop after adoption. Candidate discovery ranks product UI apps ahead of docs, Storybook, API, MCP helper, workbench, and package surfaces; `decantr workspace list --json` includes rank, category, score, and reason metadata so automation can explain why `apps/web`, `apps/remix`, or `apps/dashboard` was suggested first. Hosted pack hydration also follows the essence path: `decantr registry compile-packs apps/web/decantr.essence.json --write-context` writes into `apps/web/.decantr/context`. In contract-only/offline Brownfield, deferred hosted packs are optional context unless a present manifest references missing files.
|
|
27
27
|
Use `decantr init`, `decantr analyze`, `decantr check`, and `decantr health` as advanced primitives when you need direct control over one step.
|
|
28
28
|
|
|
29
29
|
App-scoped primitives now share the same `--project` posture as the primary workflow commands. From a workspace root, `health`, `status`, `upgrade`, `add`, `remove`, `theme`, `export`, `suggest`, `magic`, `rules`, and `telemetry` target the selected app instead of the root. Task/read paths, local-law summaries, and refresh change summaries are printed as openable workspace paths. Nonexistent project paths fail immediately, and Brownfield adoption refuses component packages unless you intentionally pass `--force-package`.
|
|
@@ -103,12 +103,14 @@ Assistant rule integration is preview-first: `--assistant-bridge=preview` writes
|
|
|
103
103
|
- previews existing apps with read-only Brownfield scan reports
|
|
104
104
|
- guides users through human workflow commands: setup, adopt, doctor, task, verify, ci, and codify
|
|
105
105
|
- supports explicit workflow lanes: greenfield blueprint, greenfield contract-only, brownfield adoption, Hybrid local law, Hybrid style bridge, Hybrid Decantr CSS, and hybrid composition
|
|
106
|
+
- ranks monorepo app candidates with explainable metadata so product UI apps come before docs, Storybook, API, helper packages, and workbench surfaces
|
|
106
107
|
- generates execution-pack context files for AI coding assistants
|
|
107
108
|
- generates typed Contract graph artifacts, replayable snapshot history, graph diffs, manifests, source-file impact context, style-bridge Token nodes, behavior-obligation LocalRule nodes, and `contract-capsule.json` for agent sessions
|
|
108
109
|
- audits projects against Decantr contracts
|
|
109
110
|
- produces local Project Health reports, Evidence Bundles, workspace health, and a localhost Studio dashboard for end-user drift triage
|
|
110
111
|
- audits local vocabulary repositories with Content Health reports for schema, reference, and quality coverage
|
|
111
112
|
- searches the registry and showcase benchmark corpus
|
|
113
|
+
- runs real-world corpus harnesses with timing percentiles, slow-command budgets, root-smoke/app-scoped classification, and stable failure categories
|
|
112
114
|
- filters blueprints through public portfolio sets: `All`, `Featured`, `Certified`, and opt-in `Labs`
|
|
113
115
|
- syncs paginated hosted vocabulary content into a full slug-keyed local cache for offline guards and context generation
|
|
114
116
|
- validates, refreshes, and maintains `decantr.essence.json`
|
|
@@ -184,7 +186,7 @@ decantr showcase verification --json
|
|
|
184
186
|
|
|
185
187
|
`decantr ci` is the blessed non-mutating automation gate. It runs the Project Health surface with adoption-mode-aware local law checks and emits a schema-backed CI report. `decantr ci init` writes root GitHub workflows or portable generic snippets using the detected package manager and pinned local CLI command instead of `@latest`; if the root manifest has not pinned Decantr yet, it prints the exact install command first.
|
|
186
188
|
|
|
187
|
-
`decantr health` remains the advanced project observability primitive. It composes the existing verifier audit, guard checks, brownfield route drift checks, runtime evidence, component reuse drift, accepted style bridge drift, accepted behavior-obligation checks, typed Contract graph freshness, and execution-pack files into a v2 `ProjectHealthReport` with status, score, route summary, pack summary, findings, stable diagnostic codes, typed repair IDs, evidence tier, authority resolution, loop readiness, and AI-ready remediation prompts. The graph freshness slice emits `GRAPH001` / `regenerate-typed-graph` when an attached app has missing, stale, or non-derivable `.decantr/graph` artifacts. The component reuse slice emits `COMP001` / `import-existing-component` when production source locally redeclares a common primitive that already exists as an exported reusable component, and `COMP010` / `replace-raw-control-with-local-component` when production JSX renders raw controls such as `<button>` while the project already owns a reusable primitive. The behavior-obligation slice emits `A11Y010`, `A11Y011`, `INT010`, `INT011`, `INT012`, `INT013`, and `COMP020` for high-confidence dialog/form regressions such as missing accessible names, missing label associations, missing visible destructive consequence copy, missing cancel affordances, missing submitting guards, implicit form button types, or bypassed project-owned interaction primitives. The style bridge slice emits `TOKEN010` / `replace-arbitrary-style-with-bridge-token` when production JSX, common class helpers, hardcoded inline color styles, or hardcoded visual values in CSS/module stylesheets bypass `.decantr/style-bridge.json` after it has been accepted as project-owned style authority. The baseline slice emits `VISUAL010` / `review-visual-baseline-drift` when `--since-baseline` detects changed screenshot hashes. When `.decantr/graph/graph.snapshot.json` exists, each finding is anchored to the most specific graph node Decantr can resolve, and JSON, markdown, text output, repair prompts, and Evidence Bundles carry that anchor. `decantr graph` also writes content-addressed history snapshots under `.decantr/graph/snapshots/` so repeated graph runs can be replayed across an AI edit sequence. When `.decantr/analysis.json` exists, `decantr graph` links observed routes/pages to implementation source artifacts and links exported reusable component declarations to their source files. When browser evidence writes `.decantr/evidence/visual-manifest.json`, `decantr graph` ingests it as local route/page Evidence nodes without uploading screenshots. When `.decantr/evidence/latest.json` exists, `decantr graph` can also materialize saved findings, evidence strings, graph anchors, repair IDs, and referenced repair/read target files as typed graph nodes and edges. When `.decantr/health-baseline-diff.json` exists, baseline changed files become file-level temporal evidence in the graph.
|
|
189
|
+
`decantr health` remains the advanced project observability primitive. It composes the existing verifier audit, guard checks, brownfield route drift checks, runtime evidence, component reuse drift, accepted style bridge drift, accepted behavior-obligation checks, typed Contract graph freshness, and execution-pack files into a v2 `ProjectHealthReport` with status, score, route summary, pack summary, findings, stable diagnostic codes, typed repair IDs, evidence tier, authority resolution, loop readiness, and AI-ready remediation prompts. The graph freshness slice emits `GRAPH001` / `regenerate-typed-graph` when an attached app has missing, stale, or non-derivable `.decantr/graph` artifacts. The component reuse slice emits `COMP001` / `import-existing-component` when production source locally redeclares a common primitive that already exists as an exported reusable component, and `COMP010` / `replace-raw-control-with-local-component` when production JSX renders generic raw controls such as `<button>` or text-like `<input>` while the project already owns a reusable primitive. Specialized inputs such as file, hidden, checkbox, radio, color, range, and Dropzone `getInputProps()` controls are not treated as generic `Input` drift. The behavior-obligation slice emits `A11Y010`, `A11Y011`, `INT010`, `INT011`, `INT012`, `INT013`, and `COMP020` for high-confidence dialog/form regressions such as missing accessible names, missing label associations, missing visible destructive consequence copy, missing cancel affordances, missing submitting guards, implicit form button types, or bypassed project-owned interaction primitives. The style bridge slice emits `TOKEN010` / `replace-arbitrary-style-with-bridge-token` when production JSX, common class helpers, hardcoded inline color styles, or hardcoded visual values in CSS/module stylesheets bypass `.decantr/style-bridge.json` after it has been accepted as project-owned style authority. The baseline slice emits `VISUAL010` / `review-visual-baseline-drift` when `--since-baseline` detects changed screenshot hashes. When `.decantr/graph/graph.snapshot.json` exists, each finding is anchored to the most specific graph node Decantr can resolve, and JSON, markdown, text output, repair prompts, and Evidence Bundles carry that anchor. `decantr graph` also writes content-addressed history snapshots under `.decantr/graph/snapshots/` so repeated graph runs can be replayed across an AI edit sequence. When `.decantr/analysis.json` exists, `decantr graph` links observed routes/pages to implementation source artifacts and links exported reusable component declarations to their source files. When browser evidence writes `.decantr/evidence/visual-manifest.json`, `decantr graph` ingests it as local route/page Evidence nodes without uploading screenshots. When `.decantr/evidence/latest.json` exists, `decantr graph` can also materialize saved findings, evidence strings, graph anchors, repair IDs, and referenced repair/read target files as typed graph nodes and edges. When `.decantr/health-baseline-diff.json` exists, baseline changed files become file-level temporal evidence in the graph.
|
|
188
190
|
|
|
189
191
|
```bash
|
|
190
192
|
decantr verify
|
package/dist/bin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./chunk-
|
|
2
|
+
import "./chunk-TGVBWTDK.js";
|
|
3
3
|
import "./chunk-SIDKK73N.js";
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
4
|
+
import "./chunk-56HUEUO3.js";
|
|
5
|
+
import "./chunk-WAGVDMJV.js";
|
|
6
|
+
import "./chunk-24JR4ZNG.js";
|
|
@@ -328,7 +328,7 @@ function scanAmbientContext(projectRoot) {
|
|
|
328
328
|
|
|
329
329
|
// src/analyzers/routes.ts
|
|
330
330
|
import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
331
|
-
import { join as join2, relative as relative2 } from "path";
|
|
331
|
+
import { extname as extname2, join as join2, relative as relative2 } from "path";
|
|
332
332
|
var SKIP_DIRS2 = /* @__PURE__ */ new Set(["node_modules", ".next", ".git", "api", "_app", "_document"]);
|
|
333
333
|
function shouldSkipDir2(name) {
|
|
334
334
|
return name.startsWith("_") || name.startsWith(".") || SKIP_DIRS2.has(name);
|
|
@@ -460,6 +460,16 @@ var ROUTE_VARIABLE_NAMES = "(?:path|pathname|route|currentPath|currentRoute|loca
|
|
|
460
460
|
var ROUTE_ASSET_EXTENSION_RE = /\.(?:avif|bmp|css|gif|ico|jpeg|jpg|js|json|map|mp4|pdf|png|svg|webp|woff2?)$/i;
|
|
461
461
|
var JSX_ROUTE_PATH_RE = /<(?:Route|[A-Z][\w.]*Route)\b[^>]*\bpath\s*=\s*(?:"([^"]+)"|'([^']+)'|{\s*"([^"]+)"\s*}|{\s*'([^']+)'\s*}|{\s*`([^`]+)`\s*})/g;
|
|
462
462
|
var OBJECT_ROUTE_PATH_RE = /\bpath\s*:\s*(?:"([^"]+)"|'([^']+)'|`([^`]+)`)/g;
|
|
463
|
+
var STATIC_HTML_ENTRY_FILES = [
|
|
464
|
+
"index.html",
|
|
465
|
+
"docs/index.html",
|
|
466
|
+
"src/index.html",
|
|
467
|
+
"public/index.html",
|
|
468
|
+
"dist/index.html"
|
|
469
|
+
];
|
|
470
|
+
var STATIC_HTML_ROUTE_DIRS = ["demos", "examples"];
|
|
471
|
+
var MAX_STATIC_HTML_ROUTES = 80;
|
|
472
|
+
var MAX_STATIC_HTML_CANDIDATES = 1e3;
|
|
463
473
|
function collectRouteCandidateFiles(dir, files, depth = 0) {
|
|
464
474
|
if (depth > 5) return;
|
|
465
475
|
let entries;
|
|
@@ -568,6 +578,65 @@ function collectRouteLiterals(pattern, content, routes) {
|
|
|
568
578
|
}
|
|
569
579
|
return count;
|
|
570
580
|
}
|
|
581
|
+
function htmlRouteFromFile(file) {
|
|
582
|
+
let withoutExt = file.slice(0, -extname2(file).length).replace(/\\/g, "/");
|
|
583
|
+
if (withoutExt.endsWith("/index")) withoutExt = withoutExt.slice(0, -"/index".length);
|
|
584
|
+
if (withoutExt === "index" || withoutExt === "docs" || withoutExt === "src" || withoutExt === "public" || withoutExt === "dist") {
|
|
585
|
+
return "/";
|
|
586
|
+
}
|
|
587
|
+
return `/${withoutExt.split("/").filter(Boolean).join("/")}` || "/";
|
|
588
|
+
}
|
|
589
|
+
function collectStaticHtmlFiles(dir, projectRoot, files, depth = 0) {
|
|
590
|
+
if (depth > 5 || files.length >= MAX_STATIC_HTML_CANDIDATES) return;
|
|
591
|
+
let entries;
|
|
592
|
+
try {
|
|
593
|
+
entries = readdirSync2(dir).sort();
|
|
594
|
+
} catch {
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
for (const entry of entries) {
|
|
598
|
+
if (files.length >= MAX_STATIC_HTML_CANDIDATES) return;
|
|
599
|
+
if (entry.startsWith(".") || entry === "node_modules") continue;
|
|
600
|
+
const fullPath = join2(dir, entry);
|
|
601
|
+
try {
|
|
602
|
+
const stat = statSync2(fullPath);
|
|
603
|
+
if (stat.isDirectory()) {
|
|
604
|
+
collectStaticHtmlFiles(fullPath, projectRoot, files, depth + 1);
|
|
605
|
+
} else if (stat.isFile() && /\.(?:html|htm)$/i.test(entry)) {
|
|
606
|
+
files.push(relative2(projectRoot, fullPath).replace(/\\/g, "/"));
|
|
607
|
+
}
|
|
608
|
+
} catch {
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
function staticHtmlFilePriority(file) {
|
|
613
|
+
const base = file.split("/").pop()?.toLowerCase();
|
|
614
|
+
if (base === "index.html" || base === "index.htm") return 0;
|
|
615
|
+
if (base === "default.html" || base === "default.htm") return 1;
|
|
616
|
+
return 2;
|
|
617
|
+
}
|
|
618
|
+
function scanStaticHtmlRoutes(projectRoot) {
|
|
619
|
+
const routes = /* @__PURE__ */ new Map();
|
|
620
|
+
for (const file of STATIC_HTML_ENTRY_FILES) {
|
|
621
|
+
if (!existsSync2(join2(projectRoot, file))) continue;
|
|
622
|
+
routes.set("/", { path: "/", file, hasLayout: false });
|
|
623
|
+
break;
|
|
624
|
+
}
|
|
625
|
+
for (const dir of STATIC_HTML_ROUTE_DIRS) {
|
|
626
|
+
const fullDir = join2(projectRoot, dir);
|
|
627
|
+
if (!existsSync2(fullDir)) continue;
|
|
628
|
+
const files = [];
|
|
629
|
+
collectStaticHtmlFiles(fullDir, projectRoot, files);
|
|
630
|
+
for (const file of files.sort(
|
|
631
|
+
(a, b) => staticHtmlFilePriority(a) - staticHtmlFilePriority(b) || a.localeCompare(b)
|
|
632
|
+
)) {
|
|
633
|
+
const routePath = htmlRouteFromFile(file);
|
|
634
|
+
if (routes.has(routePath)) continue;
|
|
635
|
+
routes.set(routePath, { path: routePath, file, hasLayout: false });
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
return [...routes.values()].slice(0, MAX_STATIC_HTML_ROUTES);
|
|
639
|
+
}
|
|
571
640
|
function detectPathnameBranchRoutes(content) {
|
|
572
641
|
const routes = /* @__PURE__ */ new Set();
|
|
573
642
|
const comparison = new RegExp(
|
|
@@ -739,6 +808,10 @@ function scanRoutes(projectRoot) {
|
|
|
739
808
|
if (reactRouterRoutes.length > 0) {
|
|
740
809
|
return { strategy: "react-router", routes: reactRouterRoutes };
|
|
741
810
|
}
|
|
811
|
+
const staticHtmlRoutes = scanStaticHtmlRoutes(projectRoot);
|
|
812
|
+
if (staticHtmlRoutes.length > 0) {
|
|
813
|
+
return { strategy: "static-html", routes: staticHtmlRoutes };
|
|
814
|
+
}
|
|
742
815
|
return { strategy: "none", routes: [] };
|
|
743
816
|
}
|
|
744
817
|
|
|
@@ -1534,7 +1607,7 @@ function buildGuardRegistryContext(projectRoot = process.cwd()) {
|
|
|
1534
1607
|
|
|
1535
1608
|
// src/lib/scan-interactions.ts
|
|
1536
1609
|
import { existsSync as existsSync8, readdirSync as readdirSync6, readFileSync as readFileSync8, statSync as statSync4 } from "fs";
|
|
1537
|
-
import { extname as
|
|
1610
|
+
import { extname as extname3, join as join8, relative as relative4 } from "path";
|
|
1538
1611
|
import { verifyInteractionsInSource } from "@decantr/verifier";
|
|
1539
1612
|
var SCAN_EXTENSIONS = /* @__PURE__ */ new Set([".tsx", ".jsx", ".ts", ".js", ".html", ".mdx"]);
|
|
1540
1613
|
var SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
@@ -1586,7 +1659,7 @@ function walkSourceTree(rootDir) {
|
|
|
1586
1659
|
const relativePath = relative4(rootDir, fullPath) || entry;
|
|
1587
1660
|
if (s.isDirectory()) {
|
|
1588
1661
|
walk2(fullPath);
|
|
1589
|
-
} else if (s.isFile() && SCAN_EXTENSIONS.has(
|
|
1662
|
+
} else if (s.isFile() && SCAN_EXTENSIONS.has(extname3(entry))) {
|
|
1590
1663
|
if (s.size > MAX_FILE_SIZE) continue;
|
|
1591
1664
|
if (isNonUiInteractionSource(relativePath)) continue;
|
|
1592
1665
|
try {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createProjectHealthReport,
|
|
3
|
-
|
|
4
|
-
} from "./chunk-
|
|
3
|
+
listWorkspaceAppCandidateDetails
|
|
4
|
+
} from "./chunk-WAGVDMJV.js";
|
|
5
5
|
|
|
6
6
|
// src/commands/workspace.ts
|
|
7
7
|
import { execFileSync } from "child_process";
|
|
@@ -97,10 +97,14 @@ function listWorkspaceProjects(root = process.cwd()) {
|
|
|
97
97
|
}
|
|
98
98
|
function listWorkspaceCandidates(root = process.cwd(), projects = listWorkspaceProjects(root)) {
|
|
99
99
|
const attached = new Set(projects.map((project) => project.path));
|
|
100
|
-
return
|
|
101
|
-
path,
|
|
102
|
-
attached: attached.has(path),
|
|
103
|
-
suggestedAdoptCommand: `decantr adopt --project ${path} --yes
|
|
100
|
+
return listWorkspaceAppCandidateDetails(root).map((candidate, index) => ({
|
|
101
|
+
path: candidate.path,
|
|
102
|
+
attached: attached.has(candidate.path),
|
|
103
|
+
suggestedAdoptCommand: `decantr adopt --project ${candidate.path} --yes`,
|
|
104
|
+
rank: index + 1,
|
|
105
|
+
score: candidate.score,
|
|
106
|
+
category: candidate.category,
|
|
107
|
+
reason: candidate.reason
|
|
104
108
|
}));
|
|
105
109
|
}
|
|
106
110
|
function changedPaths(root, since) {
|
|
@@ -342,7 +346,9 @@ async function cmdWorkspace(workspaceRoot = process.cwd(), args = ["workspace"])
|
|
|
342
346
|
console.log("App candidates:");
|
|
343
347
|
for (const candidate of candidates) {
|
|
344
348
|
const status = candidate.attached ? `${GREEN}attached${RESET}` : `${YELLOW}unattached${RESET}`;
|
|
345
|
-
console.log(
|
|
349
|
+
console.log(
|
|
350
|
+
` #${candidate.rank} ${candidate.path} ${DIM}${status} \xB7 ${candidate.category} \xB7 score ${candidate.score}${RESET}`
|
|
351
|
+
);
|
|
346
352
|
}
|
|
347
353
|
}
|
|
348
354
|
if (unattachedCandidates.length > 0) {
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
listWorkspaceCandidates,
|
|
23
23
|
listWorkspaceProjects,
|
|
24
24
|
shouldFailWorkspaceHealth
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-56HUEUO3.js";
|
|
26
26
|
import {
|
|
27
27
|
acceptBrownfieldLocalLaw,
|
|
28
28
|
acceptStyleBridge,
|
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
writeBrownfieldCodifyProposal,
|
|
53
53
|
writeHostedPatternMappingProposal,
|
|
54
54
|
writeStyleBridgeProposal
|
|
55
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-WAGVDMJV.js";
|
|
56
56
|
import {
|
|
57
57
|
buildGuardRegistryContext,
|
|
58
58
|
createDoctrineMap,
|
|
@@ -69,7 +69,7 @@ import {
|
|
|
69
69
|
sendCliCommandTelemetry,
|
|
70
70
|
sendNewProjectCompletedTelemetry,
|
|
71
71
|
writeDoctrineMap
|
|
72
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-24JR4ZNG.js";
|
|
73
73
|
|
|
74
74
|
// src/index.ts
|
|
75
75
|
import { existsSync as existsSync29, mkdirSync as mkdirSync16, readdirSync as readdirSync8, readFileSync as readFileSync22, writeFileSync as writeFileSync19 } from "fs";
|
|
@@ -2940,6 +2940,21 @@ var RULE_FILES = [
|
|
|
2940
2940
|
".github/copilot-instructions.md",
|
|
2941
2941
|
".windsurfrules"
|
|
2942
2942
|
];
|
|
2943
|
+
var STATIC_HTML_ENTRY_FILES = [
|
|
2944
|
+
"index.html",
|
|
2945
|
+
"docs/index.html",
|
|
2946
|
+
"src/index.html",
|
|
2947
|
+
"public/index.html",
|
|
2948
|
+
"dist/index.html"
|
|
2949
|
+
];
|
|
2950
|
+
var STATIC_HTML_ROUTE_DIRS = ["demos", "examples"];
|
|
2951
|
+
function hasAnyFile(projectRoot, relPaths) {
|
|
2952
|
+
return relPaths.some((relPath) => existsSync10(join12(projectRoot, relPath)));
|
|
2953
|
+
}
|
|
2954
|
+
function hasStaticHtmlSurface(projectRoot) {
|
|
2955
|
+
if (hasAnyFile(projectRoot, STATIC_HTML_ENTRY_FILES)) return true;
|
|
2956
|
+
return STATIC_HTML_ROUTE_DIRS.some((dir) => existsSync10(join12(projectRoot, dir, "index.html")));
|
|
2957
|
+
}
|
|
2943
2958
|
function readPackageJson(dir) {
|
|
2944
2959
|
const path = join12(dir, "package.json");
|
|
2945
2960
|
if (!existsSync10(path)) return null;
|
|
@@ -3045,8 +3060,8 @@ function detectProject(projectRoot = process.cwd()) {
|
|
|
3045
3060
|
} catch {
|
|
3046
3061
|
}
|
|
3047
3062
|
}
|
|
3048
|
-
if (result.framework === "unknown"
|
|
3049
|
-
if (
|
|
3063
|
+
if (result.framework === "unknown") {
|
|
3064
|
+
if (hasStaticHtmlSurface(projectRoot)) {
|
|
3050
3065
|
result.framework = "html";
|
|
3051
3066
|
}
|
|
3052
3067
|
}
|
|
@@ -4251,7 +4266,7 @@ function rel(root, path) {
|
|
|
4251
4266
|
const value = relative4(root, path).replace(/\\/g, "/");
|
|
4252
4267
|
return value || ".";
|
|
4253
4268
|
}
|
|
4254
|
-
function
|
|
4269
|
+
function hasAnyFile2(dir, names) {
|
|
4255
4270
|
for (const name of names) {
|
|
4256
4271
|
const path = join17(dir, name);
|
|
4257
4272
|
if (existsSync15(path)) return name;
|
|
@@ -4280,7 +4295,7 @@ function detectDesignAuthority(workspaceRoot, appRoot) {
|
|
|
4280
4295
|
}
|
|
4281
4296
|
for (const root of [appRoot, workspaceRoot]) {
|
|
4282
4297
|
const label = root === appRoot ? "app" : "workspace";
|
|
4283
|
-
const tailwind =
|
|
4298
|
+
const tailwind = hasAnyFile2(root, [
|
|
4284
4299
|
"tailwind.config.js",
|
|
4285
4300
|
"tailwind.config.ts",
|
|
4286
4301
|
"tailwind.config.mjs",
|
|
@@ -11078,7 +11093,7 @@ async function cmdAdoptWorkflow(args) {
|
|
|
11078
11093
|
await cmdGraph(projectRoot, { displayRoot: process.cwd() });
|
|
11079
11094
|
if (process.exitCode && process.exitCode !== 0) return;
|
|
11080
11095
|
if (runVerify) {
|
|
11081
|
-
const { cmdHealth } = await import("./health-
|
|
11096
|
+
const { cmdHealth } = await import("./health-KUPBUU3B.js");
|
|
11082
11097
|
await cmdHealth(projectRoot, {
|
|
11083
11098
|
browser: runBrowser,
|
|
11084
11099
|
browserBaseUrl: baseUrl,
|
|
@@ -11152,7 +11167,7 @@ async function cmdVerifyWorkflow(args) {
|
|
|
11152
11167
|
return;
|
|
11153
11168
|
}
|
|
11154
11169
|
if (workspaceMode) {
|
|
11155
|
-
const { cmdWorkspace } = await import("./workspace-
|
|
11170
|
+
const { cmdWorkspace } = await import("./workspace-QAZ3F4VT.js");
|
|
11156
11171
|
await cmdWorkspace(process.cwd(), ["workspace", "health", ...withoutWorkflowOnlyFlags(args)]);
|
|
11157
11172
|
return;
|
|
11158
11173
|
}
|
|
@@ -11185,7 +11200,7 @@ async function cmdVerifyWorkflow(args) {
|
|
|
11185
11200
|
}
|
|
11186
11201
|
let guardExitCode;
|
|
11187
11202
|
if (brownfield) {
|
|
11188
|
-
const { cmdHeal, collectCheckIssues } = await import("./heal-
|
|
11203
|
+
const { cmdHeal, collectCheckIssues } = await import("./heal-2FK63EQE.js");
|
|
11189
11204
|
if (quietOutput) {
|
|
11190
11205
|
const result = collectCheckIssues(workspaceInfo.appRoot, { brownfield: true });
|
|
11191
11206
|
guardExitCode = result.issues.some((issue) => issue.type === "error") ? 1 : void 0;
|
|
@@ -11195,7 +11210,7 @@ async function cmdVerifyWorkflow(args) {
|
|
|
11195
11210
|
process.exitCode = void 0;
|
|
11196
11211
|
}
|
|
11197
11212
|
}
|
|
11198
|
-
const { cmdHealth, parseHealthArgs } = await import("./health-
|
|
11213
|
+
const { cmdHealth, parseHealthArgs } = await import("./health-KUPBUU3B.js");
|
|
11199
11214
|
await cmdHealth(workspaceInfo.appRoot, parseHealthArgs(healthArgs));
|
|
11200
11215
|
if (localPatterns) {
|
|
11201
11216
|
const validation = validateLocalLaw(workspaceInfo.appRoot);
|
|
@@ -12832,7 +12847,7 @@ async function main() {
|
|
|
12832
12847
|
`${YELLOW13}Note: \`decantr heal\` is deprecated. Use \`decantr check\` instead.${RESET17}`
|
|
12833
12848
|
);
|
|
12834
12849
|
}
|
|
12835
|
-
const { cmdHeal } = await import("./heal-
|
|
12850
|
+
const { cmdHeal } = await import("./heal-2FK63EQE.js");
|
|
12836
12851
|
const { flags } = parseLooseArgs(args);
|
|
12837
12852
|
const workspaceInfo = resolveWorkflowProject(flags, "check");
|
|
12838
12853
|
if (!workspaceInfo) break;
|
|
@@ -12857,7 +12872,7 @@ async function main() {
|
|
|
12857
12872
|
const { flags } = parseLooseArgs(args);
|
|
12858
12873
|
const workspaceInfo = resolveWorkflowProject(flags, "health");
|
|
12859
12874
|
if (!workspaceInfo) break;
|
|
12860
|
-
const { cmdHealth, parseHealthArgs } = await import("./health-
|
|
12875
|
+
const { cmdHealth, parseHealthArgs } = await import("./health-KUPBUU3B.js");
|
|
12861
12876
|
await cmdHealth(workspaceInfo.appRoot, parseHealthArgs(stripProjectArgs(args)));
|
|
12862
12877
|
} catch (e) {
|
|
12863
12878
|
console.error(error2(e.message));
|
|
@@ -12885,7 +12900,7 @@ async function main() {
|
|
|
12885
12900
|
cmdStudioHelp();
|
|
12886
12901
|
break;
|
|
12887
12902
|
}
|
|
12888
|
-
const { cmdStudio, parseStudioArgs } = await import("./studio-
|
|
12903
|
+
const { cmdStudio, parseStudioArgs } = await import("./studio-J6Z3AUAR.js");
|
|
12889
12904
|
await cmdStudio(process.cwd(), parseStudioArgs(args));
|
|
12890
12905
|
} catch (e) {
|
|
12891
12906
|
console.error(error2(e.message));
|
|
@@ -12899,7 +12914,7 @@ async function main() {
|
|
|
12899
12914
|
cmdWorkspaceHelp();
|
|
12900
12915
|
break;
|
|
12901
12916
|
}
|
|
12902
|
-
const { cmdWorkspace } = await import("./workspace-
|
|
12917
|
+
const { cmdWorkspace } = await import("./workspace-QAZ3F4VT.js");
|
|
12903
12918
|
await cmdWorkspace(process.cwd(), args);
|
|
12904
12919
|
} catch (e) {
|
|
12905
12920
|
console.error(error2(e.message));
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
sendProjectHealthCiFailedTelemetry,
|
|
4
4
|
sendProjectHealthPromptTelemetry,
|
|
5
5
|
sendProjectHealthReportTelemetry
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-24JR4ZNG.js";
|
|
7
7
|
|
|
8
8
|
// src/commands/health.ts
|
|
9
9
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
@@ -60,7 +60,7 @@ function looksLikeApp(dir, options = {}) {
|
|
|
60
60
|
const pkg = readPackageJson(dir);
|
|
61
61
|
const deps = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
62
62
|
const hasFrontendDependency = Boolean(
|
|
63
|
-
deps.react || deps["react-dom"] || deps.next || deps.vue || deps.svelte || deps["@angular/core"] || deps.astro || deps.nuxt
|
|
63
|
+
deps.react || deps["react-dom"] || deps.next || deps["@remix-run/react"] || deps["@remix-run/dev"] || deps.vue || deps.svelte || deps["@angular/core"] || deps.astro || deps.nuxt || deps["solid-js"]
|
|
64
64
|
);
|
|
65
65
|
const hasServerOnlyDependency = Boolean(
|
|
66
66
|
deps.hono || deps.express || deps.fastify || deps.koa || deps["@hapi/hapi"]
|
|
@@ -76,7 +76,86 @@ function looksLikeApp(dir, options = {}) {
|
|
|
76
76
|
if (!allowPackageDeps) return false;
|
|
77
77
|
return hasFrontendDependency;
|
|
78
78
|
}
|
|
79
|
-
function
|
|
79
|
+
function hasConfig(dir) {
|
|
80
|
+
return existsSync(join(dir, "next.config.js")) || existsSync(join(dir, "next.config.ts")) || existsSync(join(dir, "next.config.mjs")) || existsSync(join(dir, "vite.config.ts")) || existsSync(join(dir, "vite.config.js")) || existsSync(join(dir, "angular.json")) || existsSync(join(dir, "svelte.config.js")) || existsSync(join(dir, "svelte.config.ts")) || existsSync(join(dir, "astro.config.mjs")) || existsSync(join(dir, "remix.config.js")) || existsSync(join(dir, "remix.config.mjs")) || existsSync(join(dir, "remix.config.ts"));
|
|
81
|
+
}
|
|
82
|
+
function hasUiSource(dir) {
|
|
83
|
+
return existsSync(join(dir, "src")) || existsSync(join(dir, "app")) || existsSync(join(dir, "pages"));
|
|
84
|
+
}
|
|
85
|
+
function rankWorkspaceAppCandidate(workspaceRoot, base, name) {
|
|
86
|
+
const path = `${base}/${name}`;
|
|
87
|
+
const dir = join(workspaceRoot, path);
|
|
88
|
+
const pkg = readPackageJson(dir);
|
|
89
|
+
const deps = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
90
|
+
const lowerPath = path.toLowerCase();
|
|
91
|
+
const reasons = [];
|
|
92
|
+
let score = base === "apps" ? 25 : -20;
|
|
93
|
+
if (base === "apps") reasons.push("under apps/");
|
|
94
|
+
if (base === "packages") reasons.push("under packages/");
|
|
95
|
+
const frontendDeps = [
|
|
96
|
+
"react",
|
|
97
|
+
"react-dom",
|
|
98
|
+
"next",
|
|
99
|
+
"@remix-run/react",
|
|
100
|
+
"@remix-run/dev",
|
|
101
|
+
"vue",
|
|
102
|
+
"svelte",
|
|
103
|
+
"@angular/core",
|
|
104
|
+
"astro",
|
|
105
|
+
"nuxt",
|
|
106
|
+
"solid-js"
|
|
107
|
+
].filter((dep) => deps[dep]);
|
|
108
|
+
if (frontendDeps.length > 0) {
|
|
109
|
+
score += 30;
|
|
110
|
+
reasons.push(`frontend deps: ${frontendDeps.slice(0, 3).join(", ")}`);
|
|
111
|
+
}
|
|
112
|
+
if (hasConfig(dir)) {
|
|
113
|
+
score += 20;
|
|
114
|
+
reasons.push("frontend config");
|
|
115
|
+
}
|
|
116
|
+
if (hasUiSource(dir)) {
|
|
117
|
+
score += 10;
|
|
118
|
+
reasons.push("UI source tree");
|
|
119
|
+
}
|
|
120
|
+
if (/\b(web|app|dashboard|remix|client|frontend|site|portal)\b/u.test(lowerPath)) {
|
|
121
|
+
score += 25;
|
|
122
|
+
reasons.push("product-app name");
|
|
123
|
+
}
|
|
124
|
+
if (/\b(marketing|admin|console)\b/u.test(lowerPath)) {
|
|
125
|
+
score += 10;
|
|
126
|
+
reasons.push("taskable app surface");
|
|
127
|
+
}
|
|
128
|
+
const negativeSignals = [
|
|
129
|
+
[/\b(docs?|documentation)\b/u, 40, "docs surface"],
|
|
130
|
+
[/\bstorybook\b/u, 45, "Storybook surface"],
|
|
131
|
+
[/\b(api|server|backend|worker|queue)\b/u, 35, "server/API surface"],
|
|
132
|
+
[/\bmcp\b/u, 40, "MCP/helper surface"],
|
|
133
|
+
[/\b(workbench|playground|sandbox|example|demo)\b/u, 30, "workbench/demo surface"],
|
|
134
|
+
[
|
|
135
|
+
/\b(design-system|components?|ui|tokens?|theme|config|eslint|tsconfig)\b/u,
|
|
136
|
+
30,
|
|
137
|
+
"library/config surface"
|
|
138
|
+
]
|
|
139
|
+
];
|
|
140
|
+
for (const [pattern, penalty, reason] of negativeSignals) {
|
|
141
|
+
if (!pattern.test(lowerPath)) continue;
|
|
142
|
+
score -= penalty;
|
|
143
|
+
reasons.push(reason);
|
|
144
|
+
}
|
|
145
|
+
const serverDeps = ["hono", "express", "fastify", "koa", "@hapi/hapi"].filter((dep) => deps[dep]);
|
|
146
|
+
if (serverDeps.length > 0 && frontendDeps.length === 0) {
|
|
147
|
+
score -= 35;
|
|
148
|
+
reasons.push(`server-only deps: ${serverDeps.slice(0, 2).join(", ")}`);
|
|
149
|
+
}
|
|
150
|
+
const category = score >= 65 ? "product-ui" : score >= 30 ? "supporting-ui" : "low-confidence-ui";
|
|
151
|
+
return {
|
|
152
|
+
path,
|
|
153
|
+
score,
|
|
154
|
+
category,
|
|
155
|
+
reason: reasons.join("; ") || "filesystem candidate"
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function listWorkspaceAppDetails(workspaceRoot) {
|
|
80
159
|
const candidates = [];
|
|
81
160
|
for (const base of ["apps", "packages"]) {
|
|
82
161
|
const baseDir = join(workspaceRoot, base);
|
|
@@ -88,14 +167,21 @@ function listWorkspaceApps(workspaceRoot) {
|
|
|
88
167
|
allowSourceDirs: base === "apps",
|
|
89
168
|
allowPackageDeps: base === "apps"
|
|
90
169
|
})) {
|
|
91
|
-
|
|
170
|
+
const ranked = rankWorkspaceAppCandidate(
|
|
171
|
+
workspaceRoot,
|
|
172
|
+
base,
|
|
173
|
+
entry.name
|
|
174
|
+
);
|
|
175
|
+
if (ranked.score <= 0) continue;
|
|
176
|
+
if (base === "packages" && ranked.category !== "product-ui") continue;
|
|
177
|
+
candidates.push(ranked);
|
|
92
178
|
}
|
|
93
179
|
}
|
|
94
180
|
}
|
|
95
|
-
return candidates.sort();
|
|
181
|
+
return candidates.sort((a, b) => b.score - a.score || a.path.localeCompare(b.path));
|
|
96
182
|
}
|
|
97
|
-
function
|
|
98
|
-
return
|
|
183
|
+
function listWorkspaceAppCandidateDetails(workspaceRoot) {
|
|
184
|
+
return listWorkspaceAppDetails(resolve(workspaceRoot));
|
|
99
185
|
}
|
|
100
186
|
function resolveWorkspaceInfo(cwd, projectArg) {
|
|
101
187
|
const absoluteCwd = resolve(cwd);
|
|
@@ -103,7 +189,8 @@ function resolveWorkspaceInfo(cwd, projectArg) {
|
|
|
103
189
|
isAbsolute(projectArg) ? projectArg : join(findWorkspaceRoot(absoluteCwd) ?? absoluteCwd, projectArg)
|
|
104
190
|
) : absoluteCwd;
|
|
105
191
|
const workspaceRoot = projectArg && isAbsolute(projectArg) ? findWorkspaceRoot(appRoot) ?? appRoot : findWorkspaceRoot(absoluteCwd) ?? absoluteCwd;
|
|
106
|
-
const
|
|
192
|
+
const appCandidateDetails = listWorkspaceAppDetails(workspaceRoot);
|
|
193
|
+
const appCandidates = appCandidateDetails.map((candidate) => candidate.path);
|
|
107
194
|
const projectScope = workspaceRoot !== appRoot || appCandidates.length > 0 ? "workspace-app" : "single-app";
|
|
108
195
|
const requiresProjectSelection = !projectArg && workspaceRoot === absoluteCwd && appCandidates.length > 0;
|
|
109
196
|
return {
|
|
@@ -112,6 +199,7 @@ function resolveWorkspaceInfo(cwd, projectArg) {
|
|
|
112
199
|
appRoot,
|
|
113
200
|
projectScope,
|
|
114
201
|
appCandidates,
|
|
202
|
+
appCandidateDetails,
|
|
115
203
|
requiresProjectSelection
|
|
116
204
|
};
|
|
117
205
|
}
|
|
@@ -5438,7 +5526,7 @@ export {
|
|
|
5438
5526
|
acceptStyleBridge,
|
|
5439
5527
|
createStyleBridgeTaskSummary,
|
|
5440
5528
|
styleBridgeMatches,
|
|
5441
|
-
|
|
5529
|
+
listWorkspaceAppCandidateDetails,
|
|
5442
5530
|
resolveWorkspaceInfo,
|
|
5443
5531
|
buildGraphArtifacts,
|
|
5444
5532
|
cmdGraphHelp,
|
|
@@ -13,8 +13,8 @@ import {
|
|
|
13
13
|
renderProjectHealthCiWorkflow,
|
|
14
14
|
shouldFailHealth,
|
|
15
15
|
writeProjectHealthCiWorkflow
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import "./chunk-
|
|
16
|
+
} from "./chunk-WAGVDMJV.js";
|
|
17
|
+
import "./chunk-24JR4ZNG.js";
|
|
18
18
|
export {
|
|
19
19
|
cmdHealth,
|
|
20
20
|
collectDesignTokenEvidence,
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import "./chunk-
|
|
1
|
+
import "./chunk-TGVBWTDK.js";
|
|
2
2
|
import "./chunk-SIDKK73N.js";
|
|
3
|
-
import "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
3
|
+
import "./chunk-56HUEUO3.js";
|
|
4
|
+
import "./chunk-WAGVDMJV.js";
|
|
5
|
+
import "./chunk-24JR4ZNG.js";
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createWorkspaceHealthReport
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-56HUEUO3.js";
|
|
4
4
|
import {
|
|
5
5
|
createProjectHealthReport
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-WAGVDMJV.js";
|
|
7
7
|
import {
|
|
8
8
|
sendStudioHealthRefreshedTelemetry,
|
|
9
9
|
sendStudioStartedTelemetry
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-24JR4ZNG.js";
|
|
11
11
|
|
|
12
12
|
// src/commands/studio.ts
|
|
13
13
|
import { existsSync, readFileSync } from "fs";
|
|
@@ -7,9 +7,9 @@ import {
|
|
|
7
7
|
listWorkspaceProjects,
|
|
8
8
|
parseWorkspaceArgs,
|
|
9
9
|
shouldFailWorkspaceHealth
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-56HUEUO3.js";
|
|
11
|
+
import "./chunk-WAGVDMJV.js";
|
|
12
|
+
import "./chunk-24JR4ZNG.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.
|
|
3
|
+
"version": "3.6.0",
|
|
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/
|
|
53
|
-
"@decantr/core": "3.5.0",
|
|
52
|
+
"@decantr/core": "3.6.0",
|
|
54
53
|
"@decantr/telemetry": "3.4.0",
|
|
55
|
-
"@decantr/
|
|
56
|
-
"@decantr/verifier": "3.
|
|
54
|
+
"@decantr/essence-spec": "3.4.0",
|
|
55
|
+
"@decantr/verifier": "3.6.0",
|
|
56
|
+
"@decantr/registry": "3.4.0"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"build": "tsup",
|