@decantr/cli 2.8.1 → 2.9.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.
- package/README.md +30 -15
- package/dist/bin.js +5 -4
- package/dist/{chunk-KT2ROK2D.js → chunk-34TZXWIF.js} +1858 -1542
- package/dist/{chunk-RKZMHS2K.js → chunk-6UDJDQPT.js} +3021 -2005
- package/dist/{chunk-FV6DGYD7.js → chunk-FKM4OQDF.js} +106 -18
- package/dist/{chunk-V3XAQWKD.js → chunk-RXF7ZYGK.js} +22 -8
- package/dist/{chunk-PAF4PBD3.js → chunk-TMOCTDYY.js} +28 -8
- package/dist/{content-health-QQHBR6XG.js → content-health-4KP2EGTI.js} +27 -10
- package/dist/{heal-ZYD6NVGE.js → heal-2BDT7TR5.js} +1 -2
- package/dist/{health-ETZXWGTW.js → health-Q7XF3I5Z.js} +2 -3
- package/dist/index.js +5 -4
- package/dist/{studio-G3YOU5YF.js → studio-EDQMI6JE.js} +3 -5
- package/dist/{upgrade-U2BTWJJJ.js → upgrade-VON7Y3LG.js} +1 -1
- package/dist/{workspace-U7J3CJY3.js → workspace-JA2RZI6V.js} +3 -5
- package/package.json +4 -4
- package/dist/chunk-3TH5PLFO.js +0 -331
- package/dist/chunk-VE6N3XWG.js +0 -78
|
@@ -1,14 +1,101 @@
|
|
|
1
|
-
import {
|
|
2
|
-
listWorkspaceAppCandidates
|
|
3
|
-
} from "./chunk-VE6N3XWG.js";
|
|
4
1
|
import {
|
|
5
2
|
createProjectHealthReport
|
|
6
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-TMOCTDYY.js";
|
|
7
4
|
|
|
8
5
|
// src/commands/workspace.ts
|
|
9
6
|
import { execFileSync } from "child_process";
|
|
10
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "fs";
|
|
11
|
-
import { dirname, join, relative, resolve } from "path";
|
|
7
|
+
import { existsSync as existsSync2, mkdirSync, readdirSync as readdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
8
|
+
import { dirname as dirname2, join as join2, relative, resolve as resolve2 } from "path";
|
|
9
|
+
|
|
10
|
+
// src/workspace.ts
|
|
11
|
+
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
12
|
+
import { dirname, join, resolve } from "path";
|
|
13
|
+
function readPackageJson(dir) {
|
|
14
|
+
const path = join(dir, "package.json");
|
|
15
|
+
if (!existsSync(path)) return null;
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function hasWorkspaceMarker(dir) {
|
|
23
|
+
if (existsSync(join(dir, "pnpm-workspace.yaml")) || existsSync(join(dir, "turbo.json")) || existsSync(join(dir, "nx.json"))) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
const pkg = readPackageJson(dir);
|
|
27
|
+
return Boolean(pkg?.workspaces);
|
|
28
|
+
}
|
|
29
|
+
function findWorkspaceRoot(startDir) {
|
|
30
|
+
let current = resolve(startDir);
|
|
31
|
+
while (true) {
|
|
32
|
+
if (hasWorkspaceMarker(current)) return current;
|
|
33
|
+
const parent = dirname(current);
|
|
34
|
+
if (parent === current) return null;
|
|
35
|
+
current = parent;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function looksLikeApp(dir, options = {}) {
|
|
39
|
+
const allowSourceDirs = options.allowSourceDirs ?? true;
|
|
40
|
+
const allowPackageDeps = options.allowPackageDeps ?? true;
|
|
41
|
+
const pkg = readPackageJson(dir);
|
|
42
|
+
const deps = { ...pkg?.dependencies, ...pkg?.devDependencies };
|
|
43
|
+
const hasFrontendDependency = Boolean(
|
|
44
|
+
deps.react || deps["react-dom"] || deps.next || deps.vue || deps.svelte || deps["@angular/core"] || deps.astro || deps.nuxt
|
|
45
|
+
);
|
|
46
|
+
const hasServerOnlyDependency = Boolean(
|
|
47
|
+
deps.hono || deps.express || deps.fastify || deps.koa || deps["@hapi/hapi"]
|
|
48
|
+
);
|
|
49
|
+
if (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"))) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
if (allowSourceDirs && (existsSync(join(dir, "src")) || existsSync(join(dir, "app")) || existsSync(join(dir, "pages")))) {
|
|
53
|
+
if (hasFrontendDependency) return true;
|
|
54
|
+
if (hasServerOnlyDependency) return false;
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
if (!allowPackageDeps) return false;
|
|
58
|
+
return hasFrontendDependency;
|
|
59
|
+
}
|
|
60
|
+
function listWorkspaceApps(workspaceRoot) {
|
|
61
|
+
const candidates = [];
|
|
62
|
+
for (const base of ["apps", "packages"]) {
|
|
63
|
+
const baseDir = join(workspaceRoot, base);
|
|
64
|
+
if (!existsSync(baseDir)) continue;
|
|
65
|
+
for (const entry of readdirSync(baseDir, { withFileTypes: true })) {
|
|
66
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
67
|
+
const candidate = join(baseDir, entry.name);
|
|
68
|
+
if (looksLikeApp(candidate, {
|
|
69
|
+
allowSourceDirs: base === "apps",
|
|
70
|
+
allowPackageDeps: base === "apps"
|
|
71
|
+
})) {
|
|
72
|
+
candidates.push(`${base}/${entry.name}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return candidates.sort();
|
|
77
|
+
}
|
|
78
|
+
function listWorkspaceAppCandidates(workspaceRoot) {
|
|
79
|
+
return listWorkspaceApps(resolve(workspaceRoot));
|
|
80
|
+
}
|
|
81
|
+
function resolveWorkspaceInfo(cwd, projectArg) {
|
|
82
|
+
const absoluteCwd = resolve(cwd);
|
|
83
|
+
const workspaceRoot = findWorkspaceRoot(absoluteCwd) ?? absoluteCwd;
|
|
84
|
+
const appRoot = projectArg ? resolve(absoluteCwd, projectArg) : absoluteCwd;
|
|
85
|
+
const appCandidates = listWorkspaceApps(workspaceRoot);
|
|
86
|
+
const projectScope = workspaceRoot !== appRoot || appCandidates.length > 0 ? "workspace-app" : "single-app";
|
|
87
|
+
const requiresProjectSelection = !projectArg && workspaceRoot === absoluteCwd && appCandidates.length > 0;
|
|
88
|
+
return {
|
|
89
|
+
cwd: absoluteCwd,
|
|
90
|
+
workspaceRoot,
|
|
91
|
+
appRoot,
|
|
92
|
+
projectScope,
|
|
93
|
+
appCandidates,
|
|
94
|
+
requiresProjectSelection
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/commands/workspace.ts
|
|
12
99
|
var BOLD = "\x1B[1m";
|
|
13
100
|
var DIM = "\x1B[2m";
|
|
14
101
|
var GREEN = "\x1B[32m";
|
|
@@ -27,12 +114,12 @@ var DEFAULT_IGNORES = /* @__PURE__ */ new Set([
|
|
|
27
114
|
"playwright-report"
|
|
28
115
|
]);
|
|
29
116
|
function workspaceConfigPath(root) {
|
|
30
|
-
return
|
|
117
|
+
return join2(root, ".decantr", "workspace.json");
|
|
31
118
|
}
|
|
32
119
|
function readWorkspaceConfig(root) {
|
|
33
120
|
const path = workspaceConfigPath(root);
|
|
34
|
-
if (!
|
|
35
|
-
return JSON.parse(
|
|
121
|
+
if (!existsSync2(path)) return null;
|
|
122
|
+
return JSON.parse(readFileSync2(path, "utf-8"));
|
|
36
123
|
}
|
|
37
124
|
function normalizeProjectPath(raw) {
|
|
38
125
|
const normalized = raw.replace(/^\.\/+/, "").replace(/\/+$/, "");
|
|
@@ -51,21 +138,21 @@ function discoverProjectPaths(root, config) {
|
|
|
51
138
|
if (depth > 6) return;
|
|
52
139
|
const rel = relative(root, dir).replace(/\\/g, "/");
|
|
53
140
|
if (rel && [...ignored].some((entry) => rel === entry || rel.startsWith(`${entry}/`))) return;
|
|
54
|
-
if (
|
|
141
|
+
if (existsSync2(join2(dir, "decantr.essence.json"))) {
|
|
55
142
|
results.add(rel || ".");
|
|
56
143
|
return;
|
|
57
144
|
}
|
|
58
|
-
for (const entry of
|
|
145
|
+
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
59
146
|
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
60
147
|
if (ignored.has(entry.name)) continue;
|
|
61
|
-
walk(
|
|
148
|
+
walk(join2(dir, entry.name), depth + 1);
|
|
62
149
|
}
|
|
63
150
|
}
|
|
64
151
|
walk(root, 0);
|
|
65
152
|
return [...results].sort();
|
|
66
153
|
}
|
|
67
154
|
function listWorkspaceProjects(root = process.cwd()) {
|
|
68
|
-
const workspaceRoot =
|
|
155
|
+
const workspaceRoot = resolve2(root);
|
|
69
156
|
const config = readWorkspaceConfig(workspaceRoot);
|
|
70
157
|
const byPath = /* @__PURE__ */ new Map();
|
|
71
158
|
for (const project of config?.projects ?? []) {
|
|
@@ -73,7 +160,7 @@ function listWorkspaceProjects(root = process.cwd()) {
|
|
|
73
160
|
byPath.set(path, {
|
|
74
161
|
id: project.id ?? projectIdFromPath(path),
|
|
75
162
|
path,
|
|
76
|
-
absolutePath:
|
|
163
|
+
absolutePath: resolve2(workspaceRoot, path),
|
|
77
164
|
owner: project.owner ?? null,
|
|
78
165
|
tags: project.tags ?? [],
|
|
79
166
|
criticality: project.criticality ?? "normal",
|
|
@@ -86,7 +173,7 @@ function listWorkspaceProjects(root = process.cwd()) {
|
|
|
86
173
|
byPath.set(path, {
|
|
87
174
|
id: projectIdFromPath(path),
|
|
88
175
|
path,
|
|
89
|
-
absolutePath:
|
|
176
|
+
absolutePath: resolve2(workspaceRoot, path),
|
|
90
177
|
owner: null,
|
|
91
178
|
tags: [],
|
|
92
179
|
criticality: "normal",
|
|
@@ -153,7 +240,7 @@ async function mapLimited(items, concurrency, fn) {
|
|
|
153
240
|
return results;
|
|
154
241
|
}
|
|
155
242
|
async function createWorkspaceHealthReport(root = process.cwd(), options = {}) {
|
|
156
|
-
const workspaceRoot =
|
|
243
|
+
const workspaceRoot = resolve2(root);
|
|
157
244
|
const config = readWorkspaceConfig(workspaceRoot);
|
|
158
245
|
const since = options.since ?? "origin/main";
|
|
159
246
|
const changed = options.changedOnly ? changedPaths(workspaceRoot, since) : /* @__PURE__ */ new Set();
|
|
@@ -333,8 +420,8 @@ async function cmdWorkspace(workspaceRoot = process.cwd(), args = ["workspace"])
|
|
|
333
420
|
const payload = options.json ? `${JSON.stringify(report, null, 2)}
|
|
334
421
|
` : options.markdown ? formatWorkspaceHealthMarkdown(report) : formatWorkspaceHealthText(report);
|
|
335
422
|
if (options.output) {
|
|
336
|
-
mkdirSync(
|
|
337
|
-
writeFileSync(
|
|
423
|
+
mkdirSync(dirname2(resolve2(workspaceRoot, options.output)), { recursive: true });
|
|
424
|
+
writeFileSync(resolve2(workspaceRoot, options.output), payload, "utf-8");
|
|
338
425
|
if (!options.ci)
|
|
339
426
|
console.log(`${GREEN}Wrote Decantr workspace health:${RESET} ${options.output}`);
|
|
340
427
|
} else {
|
|
@@ -346,6 +433,7 @@ async function cmdWorkspace(workspaceRoot = process.cwd(), args = ["workspace"])
|
|
|
346
433
|
}
|
|
347
434
|
|
|
348
435
|
export {
|
|
436
|
+
resolveWorkspaceInfo,
|
|
349
437
|
listWorkspaceProjects,
|
|
350
438
|
listWorkspaceCandidates,
|
|
351
439
|
createWorkspaceHealthReport,
|
|
@@ -4989,7 +4989,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
4989
4989
|
themeData,
|
|
4990
4990
|
themeMode: mode,
|
|
4991
4991
|
voiceTone: storedVoice?.tone ? storedVoice.tone.split(".")[0] + "." : void 0,
|
|
4992
|
-
spatialHints: sectionSpatialHints
|
|
4992
|
+
spatialHints: sectionSpatialHints,
|
|
4993
|
+
adoptionMode: effectiveAdoptionMode
|
|
4993
4994
|
});
|
|
4994
4995
|
const sectionContextPath = join2(contextDir, `section-${section.id}.md`);
|
|
4995
4996
|
writeFileSync2(sectionContextPath, contextContent);
|
|
@@ -5345,7 +5346,8 @@ function generateSectionContext(input) {
|
|
|
5345
5346
|
themeHints,
|
|
5346
5347
|
constraints,
|
|
5347
5348
|
shellInfo,
|
|
5348
|
-
spatialHints
|
|
5349
|
+
spatialHints,
|
|
5350
|
+
adoptionMode
|
|
5349
5351
|
} = input;
|
|
5350
5352
|
const lines = [];
|
|
5351
5353
|
lines.push(`# Section: ${section.id}`);
|
|
@@ -5429,9 +5431,15 @@ function generateSectionContext(input) {
|
|
|
5429
5431
|
const decoratorDefs = input.themeData?.decorator_definitions;
|
|
5430
5432
|
const totalDecoratorCount = decoratorDefs && Object.keys(decoratorDefs).length || decorators.length;
|
|
5431
5433
|
if (totalDecoratorCount > 0) {
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5434
|
+
if (adoptionMode === "contract-only") {
|
|
5435
|
+
lines.push(
|
|
5436
|
+
`**Theme intent:** ${totalDecoratorCount} \`${themeName}-*\` decorator reference(s) exist, but this project is contract-only. Translate the intent into the app's current styling system instead of applying Decantr decorator classes directly.`
|
|
5437
|
+
);
|
|
5438
|
+
} else {
|
|
5439
|
+
lines.push(
|
|
5440
|
+
`**Theme decorators:** ${totalDecoratorCount} \`${themeName}-*\` classes \u2014 full Class/Intent/Apply-to table in \`section-${section.id}-pack.md\` (preferred) and DECANTR.md "Decorator Quick Reference". MUST apply.`
|
|
5441
|
+
);
|
|
5442
|
+
}
|
|
5435
5443
|
lines.push("");
|
|
5436
5444
|
}
|
|
5437
5445
|
if (themeHints) {
|
|
@@ -5454,9 +5462,15 @@ function generateSectionContext(input) {
|
|
|
5454
5462
|
}
|
|
5455
5463
|
const themePrefix = themeName.split("-")[0] || themeName;
|
|
5456
5464
|
lines.push("");
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5465
|
+
if (adoptionMode === "contract-only") {
|
|
5466
|
+
lines.push(
|
|
5467
|
+
"Usage: implement this section through the app's existing styling authority (design-system components, Tailwind/Sass/theme tokens, CVA variants, or accepted local rules). Do not add `@decantr/css`, `css(...)`, `d-*` treatments, or Decantr token CSS unless adoption mode changes."
|
|
5468
|
+
);
|
|
5469
|
+
} else {
|
|
5470
|
+
lines.push(
|
|
5471
|
+
`Usage: \`className={css('_flex _col _gap4') + ' d-surface ${themePrefix}-glass'}\` \u2014 atoms via css(), treatments and theme decorators as plain class strings.`
|
|
5472
|
+
);
|
|
5473
|
+
}
|
|
5460
5474
|
lines.push("");
|
|
5461
5475
|
lines.push("---");
|
|
5462
5476
|
lines.push("");
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
collectCheckIssues
|
|
3
|
-
} from "./chunk-3TH5PLFO.js";
|
|
4
|
-
import {
|
|
2
|
+
collectCheckIssues,
|
|
5
3
|
sendProjectHealthCiFailedTelemetry,
|
|
6
4
|
sendProjectHealthPromptTelemetry,
|
|
7
5
|
sendProjectHealthReportTelemetry
|
|
8
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-34TZXWIF.js";
|
|
9
7
|
|
|
10
8
|
// src/commands/health.ts
|
|
11
9
|
import { execFileSync } from "child_process";
|
|
@@ -227,6 +225,26 @@ function sourceFromCheckIssue(issue) {
|
|
|
227
225
|
if (issue.rule.includes("interaction")) return "interaction";
|
|
228
226
|
return "check";
|
|
229
227
|
}
|
|
228
|
+
function normalizeHealthCategory(category, source) {
|
|
229
|
+
const lower = category.toLowerCase();
|
|
230
|
+
if (source === "pack" || lower.includes("execution pack") || lower.includes("review contract") || lower.includes("context")) {
|
|
231
|
+
return "Generated Artifact";
|
|
232
|
+
}
|
|
233
|
+
if (source === "brownfield") return "Brownfield Contract";
|
|
234
|
+
if (source === "design-token" || lower.includes("design-token")) return "Design Token";
|
|
235
|
+
if (lower.includes("accessibility")) return "Accessibility";
|
|
236
|
+
if (source === "runtime") return "Runtime";
|
|
237
|
+
if (source === "browser") return "Visual Evidence";
|
|
238
|
+
if (source === "interaction") return "Interaction";
|
|
239
|
+
if (source === "assertion") return `Contract ${category}`;
|
|
240
|
+
return category;
|
|
241
|
+
}
|
|
242
|
+
function contractAssertionApplies(assertion, metadata) {
|
|
243
|
+
if (assertion.rule === "tokens-file-present" && metadata.adoptionMode === "contract-only") {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
230
248
|
function slugify(value) {
|
|
231
249
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
|
|
232
250
|
}
|
|
@@ -298,13 +316,14 @@ function createHealthFinding(input) {
|
|
|
298
316
|
const idBase = input.baseId || input.rule || `${input.category}-${input.message}`;
|
|
299
317
|
const id = `${input.source}-${slugify(idBase)}`;
|
|
300
318
|
const commands = commandsForFinding(input.source);
|
|
319
|
+
const category = normalizeHealthCategory(input.category, input.source);
|
|
301
320
|
const remediation = {
|
|
302
|
-
summary: input.suggestedFix || `Resolve ${
|
|
321
|
+
summary: input.suggestedFix || `Resolve ${category.toLowerCase()} finding.`,
|
|
303
322
|
commands,
|
|
304
323
|
prompt: buildRemediationPrompt({
|
|
305
324
|
id,
|
|
306
325
|
source: input.source,
|
|
307
|
-
category
|
|
326
|
+
category,
|
|
308
327
|
severity: input.severity,
|
|
309
328
|
message: input.message,
|
|
310
329
|
evidence: input.evidence ?? [],
|
|
@@ -315,7 +334,7 @@ function createHealthFinding(input) {
|
|
|
315
334
|
return {
|
|
316
335
|
id,
|
|
317
336
|
source: input.source,
|
|
318
|
-
category
|
|
337
|
+
category,
|
|
319
338
|
severity: input.severity,
|
|
320
339
|
message: input.message,
|
|
321
340
|
evidence: input.evidence ?? [],
|
|
@@ -787,6 +806,7 @@ async function createProjectHealthReport(projectRoot = process.cwd(), options =
|
|
|
787
806
|
if (!isDuplicateFinding(seen, healthFinding)) findings.push(healthFinding);
|
|
788
807
|
}
|
|
789
808
|
for (const contractAssertion of createContractAssertions(projectRoot, audit)) {
|
|
809
|
+
if (!contractAssertionApplies(contractAssertion, metadata)) continue;
|
|
790
810
|
if (contractAssertion.status !== "failed") continue;
|
|
791
811
|
const healthFinding = createHealthFinding({
|
|
792
812
|
source: "assertion",
|
|
@@ -893,7 +913,7 @@ async function createProjectHealthReport(projectRoot = process.cwd(), options =
|
|
|
893
913
|
generatedAt: typeof manifest?.generatedAt === "string" ? manifest.generatedAt : null
|
|
894
914
|
},
|
|
895
915
|
ci: {
|
|
896
|
-
recommendedCommand: "decantr
|
|
916
|
+
recommendedCommand: "decantr ci --fail-on error",
|
|
897
917
|
failOn: "error"
|
|
898
918
|
},
|
|
899
919
|
findings
|
|
@@ -172,7 +172,9 @@ function statusFromCounts(counts) {
|
|
|
172
172
|
function scoreFromCounts(counts) {
|
|
173
173
|
const warningPenalty = Math.min(counts.warnCount * 2, 75);
|
|
174
174
|
const infoPenalty = Math.min(counts.infoCount * 0.5, 10);
|
|
175
|
-
return Math.round(
|
|
175
|
+
return Math.round(
|
|
176
|
+
Math.max(0, Math.min(100, 100 - counts.errorCount * 15 - warningPenalty - infoPenalty))
|
|
177
|
+
);
|
|
176
178
|
}
|
|
177
179
|
function percentage(count, total) {
|
|
178
180
|
if (total === 0) return 1;
|
|
@@ -728,7 +730,9 @@ async function createContentHealthReport(contentRoot = process.cwd(), options =
|
|
|
728
730
|
category: "Content Root",
|
|
729
731
|
severity: "error",
|
|
730
732
|
message: "No Decantr registry content was found in this directory.",
|
|
731
|
-
evidence: [
|
|
733
|
+
evidence: [
|
|
734
|
+
"Expected one or more of patterns/, themes/, blueprints/, archetypes/, shells/."
|
|
735
|
+
],
|
|
732
736
|
rule: "content-root-empty",
|
|
733
737
|
suggestedFix: "Run this command from a decantr-content style repository.",
|
|
734
738
|
baseId: "content-root-empty"
|
|
@@ -828,11 +832,15 @@ async function createContentHealthReport(contentRoot = process.cwd(), options =
|
|
|
828
832
|
patterns.length
|
|
829
833
|
),
|
|
830
834
|
patternInteractionCoverage: percentage(
|
|
831
|
-
patterns.filter(
|
|
835
|
+
patterns.filter(
|
|
836
|
+
(item) => Array.isArray(item.data.interactions) && item.data.interactions.length > 0
|
|
837
|
+
).length,
|
|
832
838
|
patterns.length
|
|
833
839
|
),
|
|
834
840
|
themeDecoratorCoverage: percentage(
|
|
835
|
-
themes.filter(
|
|
841
|
+
themes.filter(
|
|
842
|
+
(item) => isRecord(item.data.decorators) && Object.keys(item.data.decorators).length > 0
|
|
843
|
+
).length,
|
|
836
844
|
themes.length
|
|
837
845
|
),
|
|
838
846
|
blueprintPersonalityCoverage: percentage(
|
|
@@ -842,7 +850,10 @@ async function createContentHealthReport(contentRoot = process.cwd(), options =
|
|
|
842
850
|
}).length,
|
|
843
851
|
blueprints.length
|
|
844
852
|
),
|
|
845
|
-
blueprintVoiceCoverage: percentage(
|
|
853
|
+
blueprintVoiceCoverage: percentage(
|
|
854
|
+
blueprints.filter((item) => isRecord(item.data.voice)).length,
|
|
855
|
+
blueprints.length
|
|
856
|
+
),
|
|
846
857
|
archetypePageBriefCoverage: percentage(
|
|
847
858
|
archetypes.filter((item) => isRecord(item.data.page_briefs)).length,
|
|
848
859
|
archetypes.length
|
|
@@ -896,10 +907,12 @@ function formatContentHealthText(report) {
|
|
|
896
907
|
);
|
|
897
908
|
if (finding.file) lines.push(` ${DIM}${finding.file}${RESET}`);
|
|
898
909
|
if (finding.suggestedFix) lines.push(` ${DIM}Fix: ${finding.suggestedFix}${RESET}`);
|
|
899
|
-
lines.push(` ${DIM}Prompt: decantr content
|
|
910
|
+
lines.push(` ${DIM}Prompt: decantr content check --prompt ${finding.id}${RESET}`);
|
|
900
911
|
}
|
|
901
912
|
if (report.findings.length > 40) {
|
|
902
|
-
lines.push(
|
|
913
|
+
lines.push(
|
|
914
|
+
` ${DIM}Showing first 40 of ${report.findings.length} findings. Use --json for the full report.${RESET}`
|
|
915
|
+
);
|
|
903
916
|
}
|
|
904
917
|
}
|
|
905
918
|
lines.push("");
|
|
@@ -931,10 +944,14 @@ function formatContentHealthMarkdown(report) {
|
|
|
931
944
|
lines.push("");
|
|
932
945
|
lines.push("## Quality Coverage");
|
|
933
946
|
lines.push("");
|
|
934
|
-
lines.push(
|
|
947
|
+
lines.push(
|
|
948
|
+
`- Pattern visual guidance: ${percentLabel(report.quality.patternVisualBriefCoverage)}`
|
|
949
|
+
);
|
|
935
950
|
lines.push(`- Pattern interactions: ${percentLabel(report.quality.patternInteractionCoverage)}`);
|
|
936
951
|
lines.push(`- Theme decorators: ${percentLabel(report.quality.themeDecoratorCoverage)}`);
|
|
937
|
-
lines.push(
|
|
952
|
+
lines.push(
|
|
953
|
+
`- Blueprint personality: ${percentLabel(report.quality.blueprintPersonalityCoverage)}`
|
|
954
|
+
);
|
|
938
955
|
lines.push(`- Blueprint voice: ${percentLabel(report.quality.blueprintVoiceCoverage)}`);
|
|
939
956
|
lines.push(`- Archetype page briefs: ${percentLabel(report.quality.archetypePageBriefCoverage)}`);
|
|
940
957
|
lines.push("");
|
|
@@ -958,7 +975,7 @@ function formatContentHealthMarkdown(report) {
|
|
|
958
975
|
lines.push("- Evidence:");
|
|
959
976
|
for (const evidence of finding.evidence) lines.push(` - ${evidence}`);
|
|
960
977
|
}
|
|
961
|
-
lines.push(`- Prompt: \`decantr content
|
|
978
|
+
lines.push(`- Prompt: \`decantr content check --prompt ${finding.id}\``);
|
|
962
979
|
lines.push("");
|
|
963
980
|
}
|
|
964
981
|
}
|
|
@@ -10,9 +10,8 @@ import {
|
|
|
10
10
|
renderProjectHealthCiWorkflow,
|
|
11
11
|
shouldFailHealth,
|
|
12
12
|
writeProjectHealthCiWorkflow
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-KT2ROK2D.js";
|
|
13
|
+
} from "./chunk-TMOCTDYY.js";
|
|
14
|
+
import "./chunk-34TZXWIF.js";
|
|
16
15
|
export {
|
|
17
16
|
cmdHealth,
|
|
18
17
|
collectDesignTokenEvidence,
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import "./chunk-
|
|
2
|
-
import "./chunk-
|
|
3
|
-
import "./chunk-
|
|
4
|
-
import "./chunk-
|
|
1
|
+
import "./chunk-6UDJDQPT.js";
|
|
2
|
+
import "./chunk-RXF7ZYGK.js";
|
|
3
|
+
import "./chunk-FKM4OQDF.js";
|
|
4
|
+
import "./chunk-TMOCTDYY.js";
|
|
5
|
+
import "./chunk-34TZXWIF.js";
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createWorkspaceHealthReport
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-VE6N3XWG.js";
|
|
3
|
+
} from "./chunk-FKM4OQDF.js";
|
|
5
4
|
import {
|
|
6
5
|
createProjectHealthReport
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-3TH5PLFO.js";
|
|
6
|
+
} from "./chunk-TMOCTDYY.js";
|
|
9
7
|
import {
|
|
10
8
|
sendStudioHealthRefreshedTelemetry,
|
|
11
9
|
sendStudioStartedTelemetry
|
|
12
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-34TZXWIF.js";
|
|
13
11
|
|
|
14
12
|
// src/commands/studio.ts
|
|
15
13
|
import { readFileSync } from "fs";
|
|
@@ -7,11 +7,9 @@ import {
|
|
|
7
7
|
listWorkspaceProjects,
|
|
8
8
|
parseWorkspaceArgs,
|
|
9
9
|
shouldFailWorkspaceHealth
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-3TH5PLFO.js";
|
|
14
|
-
import "./chunk-KT2ROK2D.js";
|
|
10
|
+
} from "./chunk-FKM4OQDF.js";
|
|
11
|
+
import "./chunk-TMOCTDYY.js";
|
|
12
|
+
import "./chunk-34TZXWIF.js";
|
|
15
13
|
export {
|
|
16
14
|
cmdWorkspace,
|
|
17
15
|
createWorkspaceHealthReport,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decantr/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.9.1",
|
|
4
4
|
"description": "Decantr CLI - scaffold, audit, inspect Project Health, and maintain Decantr projects from the terminal",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"decantr",
|
|
@@ -48,11 +48,11 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"ajv": "^8.20.0",
|
|
51
|
-
"@decantr/core": "2.1.0",
|
|
52
51
|
"@decantr/essence-spec": "2.0.1",
|
|
53
52
|
"@decantr/telemetry": "2.2.1",
|
|
54
|
-
"@decantr/
|
|
55
|
-
"@decantr/
|
|
53
|
+
"@decantr/registry": "2.2.0",
|
|
54
|
+
"@decantr/core": "2.1.0",
|
|
55
|
+
"@decantr/verifier": "2.3.1"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "tsup",
|