@decantr/cli 2.10.0 → 2.11.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 +2 -1
- package/dist/bin.js +3 -3
- package/dist/{chunk-WPP3JEMC.js → chunk-5WPRTVNA.js} +2 -2
- package/dist/{chunk-HQUTNZKF.js → chunk-ET4BG746.js} +170 -40
- package/dist/{chunk-QDMXZSVK.js → chunk-NUNUW5IS.js} +1 -1
- package/dist/{health-VAYRQVTF.js → health-ADHNLMX3.js} +1 -1
- package/dist/index.js +3 -3
- package/dist/{studio-WPETOZRW.js → studio-GZFCCVSO.js} +2 -2
- package/dist/{workspace-PBO3BJRL.js → workspace-YYCRXJNB.js} +2 -2
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -80,7 +80,7 @@ pnpm exec decantr ci init --project apps/web
|
|
|
80
80
|
|
|
81
81
|
Assistant rule integration is preview-first: `--assistant-bridge=preview` writes `.decantr/context/assistant-bridge.md`, `decantr rules preview` prints the bridge, and `--assistant-bridge=apply` or `decantr rules apply` mutates supported rule files with idempotent marked blocks.
|
|
82
82
|
|
|
83
|
-
Brownfield analysis also writes `.decantr/doctrine-map.json`, a ranked source-precedence map across security/data, architecture, design-system, workflow/CI, feature/business, assistant-specific, stale, and unsafe-to-cite evidence. It also writes `.decantr/brownfield-intelligence.json`, `.decantr/theme-inventory.json`, and `.decantr/enrichment-backlog.md`. The proposal groups routes into observed semantic domains such as auth, RBAC, billing, reporting, facilities, settings, and public surfaces across Next App/Pages Router, React Router, Angular Router, SvelteKit, Vue Router, and Nuxt file routes. Existing styling systems such as Tailwind, Bootstrap, MUI, Chakra, plain CSS, and Decantr CSS are observed as evidence instead of replaced. Theme variants are observed in the theme inventory without changing Essence V4. `decantr codify --from-audit` proposes `.decantr/local-patterns.proposal.json` and `.decantr/rules.proposal.json` with Hybrid authority guidance; after review, `decantr codify --accept` promotes `.decantr/local-patterns.json` and `.decantr/rules.json`. `decantr doctor` reports whether the app is contract-only, Hybrid local law, style bridge, Decantr CSS, or Hybrid composition. `decantr task` prints that authority block and warns before mixing runtimes or adding Decantr CSS to a non-Decantr-CSS app. `decantr verify --brownfield --local-patterns` uses the Brownfield guard layer plus accepted local law to flag actionable missing doctrine coverage, unsafe context, missing assistant bridges, style drift, raw local-rule violations, and unsafe defaults without treating current database migrations as stale docs.
|
|
83
|
+
Brownfield analysis also writes `.decantr/doctrine-map.json`, a ranked source-precedence map across security/data, architecture, design-system, workflow/CI, feature/business, assistant-specific, stale, and unsafe-to-cite evidence. It also writes `.decantr/brownfield-intelligence.json`, `.decantr/theme-inventory.json`, and `.decantr/enrichment-backlog.md`. The proposal groups routes into observed semantic domains such as auth, RBAC, billing, reporting, facilities, settings, and public surfaces across Next App/Pages Router, React Router, Angular Router, SvelteKit, Vue Router, and Nuxt file routes. Existing styling systems such as Tailwind, Bootstrap, MUI, Chakra, plain CSS, and Decantr CSS are observed as evidence instead of replaced. Theme variants are observed in the theme inventory without changing Essence V4. `decantr codify --from-audit` proposes `.decantr/local-patterns.proposal.json` and `.decantr/rules.proposal.json` with Hybrid authority guidance and source-derived button/card class hints; after review, `decantr codify --accept` promotes `.decantr/local-patterns.json` and `.decantr/rules.json`. `decantr doctor` reports whether the app is contract-only, Hybrid local law, style bridge, Decantr CSS, or Hybrid composition. `decantr task` prints that authority block and warns before mixing runtimes or adding Decantr CSS to a non-Decantr-CSS app. `decantr suggest --from-code` surfaces accepted local patterns from the app root or selected `--project`, and `decantr ci` prints accepted local-rule findings with file/line evidence. `decantr verify --brownfield --local-patterns` uses the Brownfield guard layer plus accepted local law to flag actionable missing doctrine coverage, unsafe context, missing assistant bridges, style drift, raw local-rule violations, and unsafe defaults without treating current database migrations as stale docs.
|
|
84
84
|
|
|
85
85
|
## What It Does
|
|
86
86
|
|
|
@@ -117,6 +117,7 @@ decantr task /feed "add saved recipe actions"
|
|
|
117
117
|
decantr verify --brownfield --local-patterns
|
|
118
118
|
decantr verify --base-url http://localhost:3000 --evidence
|
|
119
119
|
decantr ci --project apps/web
|
|
120
|
+
decantr ci --project apps/web --fail-on warn
|
|
120
121
|
decantr ci --workspace --changed --since origin/main
|
|
121
122
|
decantr ci init --project apps/web
|
|
122
123
|
decantr init --existing --blueprint=esports-hq
|
package/dist/bin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./chunk-
|
|
2
|
+
import "./chunk-ET4BG746.js";
|
|
3
3
|
import "./chunk-IQZCIMQJ.js";
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
import "./chunk-NUNUW5IS.js";
|
|
5
|
+
import "./chunk-5WPRTVNA.js";
|
|
6
6
|
import "./chunk-4SZ4SEKT.js";
|
|
@@ -713,7 +713,7 @@ async function collectBrowserVerification(projectRoot, options, declaredRoutes)
|
|
|
713
713
|
message: "Browser verification was requested, but Playwright is not installed in this project.",
|
|
714
714
|
evidence: ["Expected dependency: playwright or @playwright/test"],
|
|
715
715
|
rule: "browser-playwright-missing",
|
|
716
|
-
suggestedFix: "Install Playwright in the project or
|
|
716
|
+
suggestedFix: "Install Playwright in the project or omit browser/base-url evidence for static-only health checks.",
|
|
717
717
|
baseId: "playwright-missing"
|
|
718
718
|
});
|
|
719
719
|
return {
|
|
@@ -1404,7 +1404,7 @@ async function cmdHealth(projectRoot = process.cwd(), options = {}) {
|
|
|
1404
1404
|
const reason = evidenceBundle.browser.findings[0] ?? "Playwright is not available to Decantr in this project.";
|
|
1405
1405
|
console.log(`${YELLOW}Browser evidence unavailable:${RESET} ${reason}`);
|
|
1406
1406
|
console.log(
|
|
1407
|
-
`${DIM}Static evidence was still written. Install Playwright or
|
|
1407
|
+
`${DIM}Static evidence was still written. Install Playwright or omit --browser/--base-url evidence if screenshots are not needed.${RESET}`
|
|
1408
1408
|
);
|
|
1409
1409
|
}
|
|
1410
1410
|
}
|
|
@@ -22,14 +22,14 @@ import {
|
|
|
22
22
|
listWorkspaceCandidates,
|
|
23
23
|
listWorkspaceProjects,
|
|
24
24
|
shouldFailWorkspaceHealth
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-NUNUW5IS.js";
|
|
26
26
|
import {
|
|
27
27
|
createProjectHealthReport,
|
|
28
28
|
formatProjectHealthMarkdown,
|
|
29
29
|
formatProjectHealthText,
|
|
30
30
|
resolveWorkspaceInfo,
|
|
31
31
|
shouldFailHealth
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-5WPRTVNA.js";
|
|
33
33
|
import {
|
|
34
34
|
buildGuardRegistryContext,
|
|
35
35
|
createDoctrineMap,
|
|
@@ -2549,6 +2549,17 @@ function collectCssFiles(projectRoot) {
|
|
|
2549
2549
|
function selectorFromMatch(raw) {
|
|
2550
2550
|
return raw.replace(/\s+/g, " ").trim().slice(0, 120);
|
|
2551
2551
|
}
|
|
2552
|
+
function isThemeVariantClass(id, selector) {
|
|
2553
|
+
if (/^(switcher|toggle|selector|picker|menu|button|btn|control|provider|container|panel|card|icon|label|group|actions?)$/i.test(
|
|
2554
|
+
id
|
|
2555
|
+
)) {
|
|
2556
|
+
return false;
|
|
2557
|
+
}
|
|
2558
|
+
const selectors = selector.split(",").map((entry) => entry.trim());
|
|
2559
|
+
return selectors.some(
|
|
2560
|
+
(entry) => /^(?::root|html|body)?(?:\.[\w-]+)*\.theme-[a-z0-9-]+(?:\s|$|:|\[|\.)/i.test(entry)
|
|
2561
|
+
);
|
|
2562
|
+
}
|
|
2552
2563
|
function createThemeInventory(projectRoot, styling) {
|
|
2553
2564
|
const files = collectCssFiles(projectRoot);
|
|
2554
2565
|
const variantMap = /* @__PURE__ */ new Map();
|
|
@@ -2582,8 +2593,12 @@ function createThemeInventory(projectRoot, styling) {
|
|
|
2582
2593
|
const themeClass = selector.match(/\.theme-([a-z0-9-]+)/i);
|
|
2583
2594
|
if (/dark/i.test(selector)) id = "dark";
|
|
2584
2595
|
if (dataTheme?.[1]) id = dataTheme[1].toLowerCase();
|
|
2585
|
-
if (themeClass?.[1])
|
|
2586
|
-
|
|
2596
|
+
if (themeClass?.[1]) {
|
|
2597
|
+
const classId = themeClass[1].toLowerCase();
|
|
2598
|
+
if (!isThemeVariantClass(classId, selector)) continue;
|
|
2599
|
+
id = classId;
|
|
2600
|
+
}
|
|
2601
|
+
if (/^:root(?:\s|$|,)/.test(selector)) id = "base";
|
|
2587
2602
|
ensureVariant(id, selector, rel2);
|
|
2588
2603
|
}
|
|
2589
2604
|
for (const [id, entry] of variantMap) {
|
|
@@ -2617,6 +2632,7 @@ function createThemeInventory(projectRoot, styling) {
|
|
|
2617
2632
|
}
|
|
2618
2633
|
const variants = [...variantMap.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
2619
2634
|
const modes = variants.map((variant) => variant.id);
|
|
2635
|
+
const darkModeDetected = styling.darkMode || modes.includes("dark") || variants.some((variant) => /dark/i.test(variant.id));
|
|
2620
2636
|
if (variants.length > 2) {
|
|
2621
2637
|
notes.push(
|
|
2622
2638
|
"Multiple theme variants were observed. Essence V4 remains unchanged; variants are reported here for task-time context."
|
|
@@ -2627,7 +2643,7 @@ function createThemeInventory(projectRoot, styling) {
|
|
|
2627
2643
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2628
2644
|
localOnly: true,
|
|
2629
2645
|
stylingApproach: styling.approach,
|
|
2630
|
-
darkModeDetected
|
|
2646
|
+
darkModeDetected,
|
|
2631
2647
|
modes,
|
|
2632
2648
|
variants,
|
|
2633
2649
|
tokens: {
|
|
@@ -3309,7 +3325,8 @@ function createBrownfieldCodifyProposal(input) {
|
|
|
3309
3325
|
],
|
|
3310
3326
|
componentPaths: evidence.buttonComponents,
|
|
3311
3327
|
decide: "Define primary, secondary, tertiary, destructive, icon-only, disabled, and loading button variants from this app.",
|
|
3312
|
-
|
|
3328
|
+
classHints: evidence.buttonClassHints,
|
|
3329
|
+
evidence: evidence.buttonComponents.length ? evidence.buttonComponents : evidence.buttonClassHints.length ? evidence.buttonClassHints : [
|
|
3313
3330
|
"No obvious Button wrapper found yet. Add the project-owned wrapper path before strict enforcement."
|
|
3314
3331
|
],
|
|
3315
3332
|
forbiddenAlternatives: ["New one-off button variants without updating this manifest."]
|
|
@@ -3635,11 +3652,27 @@ function summarizeSourceEvidence(projectRoot, files) {
|
|
|
3635
3652
|
formComponents: byName(["input", "field", "form", "select", "textarea"]),
|
|
3636
3653
|
shellComponents,
|
|
3637
3654
|
themeComponents,
|
|
3655
|
+
buttonClassHints: collectClassHints(projectRoot, files, [
|
|
3656
|
+
"button",
|
|
3657
|
+
"btn",
|
|
3658
|
+
"action",
|
|
3659
|
+
"primary",
|
|
3660
|
+
"secondary",
|
|
3661
|
+
"tertiary",
|
|
3662
|
+
"ghost",
|
|
3663
|
+
"link"
|
|
3664
|
+
]),
|
|
3638
3665
|
cardClassHints: collectClassHints(projectRoot, files, ["card", "panel", "surface", "tile"])
|
|
3639
3666
|
};
|
|
3640
3667
|
}
|
|
3641
3668
|
function collectClassHints(projectRoot, files, terms) {
|
|
3642
3669
|
const hints = /* @__PURE__ */ new Map();
|
|
3670
|
+
const wantsButton = terms.some(
|
|
3671
|
+
(term) => /^(button|btn|action|primary|secondary|tertiary|ghost|link)$/i.test(term)
|
|
3672
|
+
);
|
|
3673
|
+
const wantsSurface = terms.some((term) => /^(card|panel|surface|tile)$/i.test(term));
|
|
3674
|
+
const buttonSignal = /button|btn|action|primary|secondary|tertiary|ghost|link|destructive|icon/i;
|
|
3675
|
+
const surfaceSignal = /card|panel|surface|tile|rounded|shadow|border|bg-|p-\d|px-|py-/i;
|
|
3643
3676
|
for (const file of files) {
|
|
3644
3677
|
if (!UI_TEMPLATE_EXTENSIONS.has(extname(file.absolute))) continue;
|
|
3645
3678
|
const content = readFileSync11(join15(projectRoot, file.relative), "utf-8");
|
|
@@ -3647,7 +3680,8 @@ function collectClassHints(projectRoot, files, terms) {
|
|
|
3647
3680
|
const matches = content.matchAll(/\bclass(?:Name)?\s*=\s*["'`]([^"'`]+)["'`]/g);
|
|
3648
3681
|
for (const match of matches) {
|
|
3649
3682
|
const value = match[1].trim();
|
|
3650
|
-
|
|
3683
|
+
const keep = wantsButton && buttonSignal.test(value) || wantsSurface && surfaceSignal.test(value) || !wantsButton && !wantsSurface && (buttonSignal.test(value) || surfaceSignal.test(value));
|
|
3684
|
+
if (!keep) continue;
|
|
3651
3685
|
hints.set(value, (hints.get(value) ?? 0) + 1);
|
|
3652
3686
|
}
|
|
3653
3687
|
}
|
|
@@ -3845,6 +3879,16 @@ function summarizeLocalLaw(projectRoot) {
|
|
|
3845
3879
|
checked: validation.patternPackPresent || validation.ruleManifestPresent,
|
|
3846
3880
|
patternsPresent: validation.patternPackPresent,
|
|
3847
3881
|
rulesPresent: validation.ruleManifestPresent,
|
|
3882
|
+
warnings: validation.warnings,
|
|
3883
|
+
findings: validation.findings.map((finding) => ({
|
|
3884
|
+
ruleId: finding.ruleId,
|
|
3885
|
+
severity: finding.severity,
|
|
3886
|
+
file: finding.file,
|
|
3887
|
+
line: finding.line,
|
|
3888
|
+
column: finding.column,
|
|
3889
|
+
message: finding.message,
|
|
3890
|
+
suggestedFix: finding.suggestedFix
|
|
3891
|
+
})),
|
|
3848
3892
|
errorCount: validation.findings.filter((finding) => finding.severity === "error").length,
|
|
3849
3893
|
warnCount: validation.findings.filter((finding) => finding.severity === "warn").length + validation.warnings.length
|
|
3850
3894
|
};
|
|
@@ -3854,6 +3898,74 @@ function localLawFails(summary, failOn) {
|
|
|
3854
3898
|
if (summary.errorCount > 0) return true;
|
|
3855
3899
|
return failOn === "warn" && summary.warnCount > 0;
|
|
3856
3900
|
}
|
|
3901
|
+
function projectCiStatus(health, localLaw) {
|
|
3902
|
+
if (health.status === "error" || localLaw.errorCount > 0) return "error";
|
|
3903
|
+
if (health.status === "warning" || localLaw.warnCount > 0) return "warning";
|
|
3904
|
+
return health.status;
|
|
3905
|
+
}
|
|
3906
|
+
function formatLocalLawText(summary, health) {
|
|
3907
|
+
const lines = ["", `${BOLD2}Project-owned local law:${RESET3}`];
|
|
3908
|
+
if (!summary.checked) {
|
|
3909
|
+
const isBrownfield = health.summary.workflowMode === "brownfield-attach";
|
|
3910
|
+
lines.push(
|
|
3911
|
+
isBrownfield ? " Not accepted yet. Run `decantr codify --from-audit`, review, then `decantr codify --accept`." : " Not active for this project."
|
|
3912
|
+
);
|
|
3913
|
+
return `${lines.join("\n")}
|
|
3914
|
+
`;
|
|
3915
|
+
}
|
|
3916
|
+
lines.push(
|
|
3917
|
+
` Patterns: ${summary.patternsPresent ? "present" : "missing"} | Rules: ${summary.rulesPresent ? "present" : "missing"}`
|
|
3918
|
+
);
|
|
3919
|
+
lines.push(` Findings: ${summary.errorCount} error(s), ${summary.warnCount} warning(s)`);
|
|
3920
|
+
for (const warning of summary.warnings.slice(0, 5)) {
|
|
3921
|
+
lines.push(` ${DIM3}[WARN] ${warning}${RESET3}`);
|
|
3922
|
+
}
|
|
3923
|
+
for (const finding of summary.findings.slice(0, 8)) {
|
|
3924
|
+
const label = finding.severity.toUpperCase();
|
|
3925
|
+
lines.push(
|
|
3926
|
+
` [${label}] ${finding.ruleId} ${finding.file}:${finding.line}:${finding.column} ${finding.message}`
|
|
3927
|
+
);
|
|
3928
|
+
lines.push(` ${DIM3}${finding.suggestedFix}${RESET3}`);
|
|
3929
|
+
}
|
|
3930
|
+
if (summary.findings.length > 8) {
|
|
3931
|
+
lines.push(` ${DIM3}...${summary.findings.length - 8} more local-law finding(s)${RESET3}`);
|
|
3932
|
+
}
|
|
3933
|
+
return `${lines.join("\n")}
|
|
3934
|
+
`;
|
|
3935
|
+
}
|
|
3936
|
+
function formatLocalLawMarkdown(summary, health) {
|
|
3937
|
+
const lines = ["## Project-Owned Local Law", ""];
|
|
3938
|
+
if (!summary.checked) {
|
|
3939
|
+
if (health.summary.workflowMode !== "brownfield-attach") {
|
|
3940
|
+
lines.push("Local law is not active for this project.");
|
|
3941
|
+
return lines.join("\n");
|
|
3942
|
+
}
|
|
3943
|
+
lines.push(
|
|
3944
|
+
"Local law has not been accepted yet. Run `decantr codify --from-audit`, review the proposal, then run `decantr codify --accept`."
|
|
3945
|
+
);
|
|
3946
|
+
return lines.join("\n");
|
|
3947
|
+
}
|
|
3948
|
+
lines.push(
|
|
3949
|
+
`Patterns: **${summary.patternsPresent ? "present" : "missing"}** \xB7 Rules: **${summary.rulesPresent ? "present" : "missing"}**`
|
|
3950
|
+
);
|
|
3951
|
+
lines.push("");
|
|
3952
|
+
lines.push(`Findings: **${summary.errorCount} error(s), ${summary.warnCount} warning(s)**`);
|
|
3953
|
+
if (summary.warnings.length > 0) {
|
|
3954
|
+
lines.push("");
|
|
3955
|
+
for (const warning of summary.warnings.slice(0, 5)) {
|
|
3956
|
+
lines.push(`- Warning: ${warning}`);
|
|
3957
|
+
}
|
|
3958
|
+
}
|
|
3959
|
+
if (summary.findings.length > 0) {
|
|
3960
|
+
lines.push("");
|
|
3961
|
+
for (const finding of summary.findings.slice(0, 12)) {
|
|
3962
|
+
lines.push(
|
|
3963
|
+
`- \`${finding.severity}\` \`${finding.ruleId}\` at \`${finding.file}:${finding.line}:${finding.column}\`: ${finding.message}`
|
|
3964
|
+
);
|
|
3965
|
+
}
|
|
3966
|
+
}
|
|
3967
|
+
return lines.join("\n");
|
|
3968
|
+
}
|
|
3857
3969
|
function formatProjectCiMarkdown(report) {
|
|
3858
3970
|
const lines = [
|
|
3859
3971
|
"# Decantr CI",
|
|
@@ -3864,7 +3976,9 @@ function formatProjectCiMarkdown(report) {
|
|
|
3864
3976
|
`- Fail on: \`${report.failOn}\``,
|
|
3865
3977
|
`- Local law: ${report.localLaw.checked ? `${report.localLaw.errorCount} error(s), ${report.localLaw.warnCount} warning(s)` : "not accepted yet"}`,
|
|
3866
3978
|
"",
|
|
3867
|
-
formatProjectHealthMarkdown(report.health)
|
|
3979
|
+
formatProjectHealthMarkdown(report.health),
|
|
3980
|
+
"",
|
|
3981
|
+
formatLocalLawMarkdown(report.localLaw, report.health)
|
|
3868
3982
|
];
|
|
3869
3983
|
return `${lines.join("\n")}
|
|
3870
3984
|
`;
|
|
@@ -4068,7 +4182,7 @@ async function runProjectCi(root, options) {
|
|
|
4068
4182
|
mode: "project",
|
|
4069
4183
|
projectPath,
|
|
4070
4184
|
failOn,
|
|
4071
|
-
status: health
|
|
4185
|
+
status: projectCiStatus(health, localLaw),
|
|
4072
4186
|
health,
|
|
4073
4187
|
localLaw
|
|
4074
4188
|
};
|
|
@@ -4080,7 +4194,8 @@ async function runProjectCi(root, options) {
|
|
|
4080
4194
|
if (!options.output && !options.markdownOutput) {
|
|
4081
4195
|
if (options.json) process.stdout.write(json);
|
|
4082
4196
|
else if (options.markdown) process.stdout.write(markdown);
|
|
4083
|
-
else
|
|
4197
|
+
else
|
|
4198
|
+
process.stdout.write(`${formatProjectHealthText(health)}${formatLocalLawText(localLaw, health)}`);
|
|
4084
4199
|
}
|
|
4085
4200
|
return shouldFailHealth(health, failOn) || localLawFails(localLaw, failOn) ? 1 : 0;
|
|
4086
4201
|
}
|
|
@@ -8803,7 +8918,7 @@ function localPatternMatches(projectRoot, query) {
|
|
|
8803
8918
|
if (!projectRoot) return [];
|
|
8804
8919
|
const pack = readLocalPatternPack(projectRoot);
|
|
8805
8920
|
const patterns = Array.isArray(pack?.patterns) ? pack.patterns : [];
|
|
8806
|
-
const queryTerms = query.toLowerCase().split(/[^a-z0-9]+/).filter((term) => term.length > 1);
|
|
8921
|
+
const queryTerms = query.toLowerCase().split(/[^a-z0-9]+/).filter((term) => term.length > 1).flatMap((term) => term.endsWith("s") && term.length > 3 ? [term, term.slice(0, -1)] : [term]);
|
|
8807
8922
|
if (queryTerms.length === 0) return [];
|
|
8808
8923
|
return patterns.map((pattern) => {
|
|
8809
8924
|
const id = typeof pattern.id === "string" ? pattern.id : "local-pattern";
|
|
@@ -8815,7 +8930,11 @@ function localPatternMatches(projectRoot, query) {
|
|
|
8815
8930
|
role,
|
|
8816
8931
|
typeof pattern.decide === "string" ? pattern.decide : null,
|
|
8817
8932
|
...Array.isArray(pattern.appliesTo) ? pattern.appliesTo : [],
|
|
8818
|
-
...Array.isArray(pattern.componentPaths) ? pattern.componentPaths : []
|
|
8933
|
+
...Array.isArray(pattern.componentPaths) ? pattern.componentPaths : [],
|
|
8934
|
+
...Array.isArray(pattern.tokenHints) ? pattern.tokenHints : [],
|
|
8935
|
+
...Array.isArray(pattern.classHints) ? pattern.classHints : [],
|
|
8936
|
+
...Array.isArray(pattern.evidence) ? pattern.evidence : [],
|
|
8937
|
+
...Array.isArray(pattern.forbiddenAlternatives) ? pattern.forbiddenAlternatives : []
|
|
8819
8938
|
].filter((entry) => typeof entry === "string").join(" ").toLowerCase();
|
|
8820
8939
|
const score = queryTerms.reduce((sum, term) => sum + (haystack.includes(term) ? 1 : 0), 0);
|
|
8821
8940
|
return { id, label, role, score };
|
|
@@ -8849,6 +8968,7 @@ async function loadPatternDiscoveryCandidates(registryClient) {
|
|
|
8849
8968
|
}
|
|
8850
8969
|
async function cmdSuggest(query, options = {}) {
|
|
8851
8970
|
const searchType = options.type || "pattern";
|
|
8971
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
8852
8972
|
if (searchType !== "pattern" && searchType !== "patterns") {
|
|
8853
8973
|
const apiClient = getAPIClient();
|
|
8854
8974
|
try {
|
|
@@ -8869,9 +8989,10 @@ async function cmdSuggest(query, options = {}) {
|
|
|
8869
8989
|
}
|
|
8870
8990
|
}
|
|
8871
8991
|
const registryClient = new RegistryClient({
|
|
8872
|
-
cacheDir: join31(
|
|
8992
|
+
cacheDir: join31(projectRoot, ".decantr", "cache")
|
|
8873
8993
|
});
|
|
8874
|
-
const code = options.fromCode || options.file ? readSuggestCodeContext(
|
|
8994
|
+
const code = options.fromCode || options.file ? readSuggestCodeContext(projectRoot, options.route, options.file) : "";
|
|
8995
|
+
const localMatches = localPatternMatches(projectRoot, [query, code].filter(Boolean).join("\n"));
|
|
8875
8996
|
const candidates = await loadPatternDiscoveryCandidates(registryClient);
|
|
8876
8997
|
const matches = rankPatternCandidates(
|
|
8877
8998
|
{
|
|
@@ -8882,7 +9003,7 @@ async function cmdSuggest(query, options = {}) {
|
|
|
8882
9003
|
},
|
|
8883
9004
|
candidates
|
|
8884
9005
|
);
|
|
8885
|
-
if (matches.length === 0) {
|
|
9006
|
+
if (matches.length === 0 && localMatches.length === 0) {
|
|
8886
9007
|
console.log(dim3(`No pattern suggestions for "${query}"`));
|
|
8887
9008
|
console.log("");
|
|
8888
9009
|
console.log("Try:");
|
|
@@ -8902,10 +9023,6 @@ async function cmdSuggest(query, options = {}) {
|
|
|
8902
9023
|
`Pattern suggestions for "${query}"${contextBits.length > 0 ? ` (${contextBits.join(", ")})` : ""}`
|
|
8903
9024
|
)
|
|
8904
9025
|
);
|
|
8905
|
-
const localMatches = localPatternMatches(
|
|
8906
|
-
options.projectRoot,
|
|
8907
|
-
[query, code].filter(Boolean).join("\n")
|
|
8908
|
-
);
|
|
8909
9026
|
if (localMatches.length > 0) {
|
|
8910
9027
|
console.log(`${BOLD9}Project-owned local law:${RESET16}`);
|
|
8911
9028
|
for (const match of localMatches) {
|
|
@@ -8915,6 +9032,12 @@ async function cmdSuggest(query, options = {}) {
|
|
|
8915
9032
|
console.log("");
|
|
8916
9033
|
console.log(`${BOLD9}Registry patterns:${RESET16}`);
|
|
8917
9034
|
}
|
|
9035
|
+
if (matches.length === 0) {
|
|
9036
|
+
console.log(dim3("No hosted/bundled registry patterns matched this query."));
|
|
9037
|
+
console.log("");
|
|
9038
|
+
console.log(dim3('Use local law first, or run "decantr list patterns" to browse registry options.'));
|
|
9039
|
+
return;
|
|
9040
|
+
}
|
|
8918
9041
|
for (const match of matches.slice(0, 8)) {
|
|
8919
9042
|
const candidate = match.candidate;
|
|
8920
9043
|
const slug = candidate.slug || candidate.id;
|
|
@@ -9313,18 +9436,23 @@ async function applyAcceptedBrownfieldProposal(input) {
|
|
|
9313
9436
|
if (refreshResult.contextFiles.length > 8) {
|
|
9314
9437
|
console.log(` ${dim3(`(+${refreshResult.contextFiles.length - 8} more)`)}`);
|
|
9315
9438
|
}
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
9319
|
-
|
|
9320
|
-
|
|
9321
|
-
|
|
9322
|
-
|
|
9323
|
-
|
|
9324
|
-
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9439
|
+
if (!input.suppressNextSteps) {
|
|
9440
|
+
console.log("");
|
|
9441
|
+
console.log(" Next steps:");
|
|
9442
|
+
console.log(
|
|
9443
|
+
` 1. Run ${cyan3(withProject("decantr doctor", projectLabel))} to explain adoption state and the next command`
|
|
9444
|
+
);
|
|
9445
|
+
console.log(
|
|
9446
|
+
` 2. Run ${cyan3(withProject("decantr codify --from-audit", projectLabel))} when you are ready to propose project-owned UI law`
|
|
9447
|
+
);
|
|
9448
|
+
console.log(
|
|
9449
|
+
` 3. Use ${cyan3(withProject('decantr task / "change summary"', projectLabel))} before LLM edits`
|
|
9450
|
+
);
|
|
9451
|
+
console.log(
|
|
9452
|
+
` 4. Run ${cyan3(withProject("decantr verify --brownfield", projectLabel))} after edits`
|
|
9453
|
+
);
|
|
9454
|
+
console.log("");
|
|
9455
|
+
}
|
|
9328
9456
|
}
|
|
9329
9457
|
async function cmdInit(args) {
|
|
9330
9458
|
const workspaceInfo = resolveWorkspaceInfo(process.cwd(), args.project);
|
|
@@ -9372,7 +9500,8 @@ async function cmdInit(args) {
|
|
|
9372
9500
|
detected,
|
|
9373
9501
|
workspaceInfo,
|
|
9374
9502
|
mode: proposalMode,
|
|
9375
|
-
assistantBridge: policy.assistantBridge
|
|
9503
|
+
assistantBridge: policy.assistantBridge,
|
|
9504
|
+
suppressNextSteps: args.internalSuppressNextSteps
|
|
9376
9505
|
});
|
|
9377
9506
|
if (args.telemetry) enableCliTelemetry(projectRoot);
|
|
9378
9507
|
writeArtifactReadme(projectRoot);
|
|
@@ -10591,7 +10720,8 @@ async function cmdAdoptWorkflow(args) {
|
|
|
10591
10720
|
"merge-proposal": proposalFlag === "--merge-proposal",
|
|
10592
10721
|
"replace-essence": proposalFlag === "--replace-essence",
|
|
10593
10722
|
"assistant-bridge": assistantBridge,
|
|
10594
|
-
telemetry: flagBoolean(flags, "telemetry")
|
|
10723
|
+
telemetry: flagBoolean(flags, "telemetry"),
|
|
10724
|
+
internalSuppressNextSteps: true
|
|
10595
10725
|
});
|
|
10596
10726
|
if (process.exitCode && process.exitCode !== 0) return;
|
|
10597
10727
|
if (hydratePacks) {
|
|
@@ -10620,7 +10750,7 @@ async function cmdAdoptWorkflow(args) {
|
|
|
10620
10750
|
console.log(dim3("Skipping hosted pack hydration in offline mode."));
|
|
10621
10751
|
}
|
|
10622
10752
|
if (runVerify) {
|
|
10623
|
-
const { cmdHealth } = await import("./health-
|
|
10753
|
+
const { cmdHealth } = await import("./health-ADHNLMX3.js");
|
|
10624
10754
|
await cmdHealth(projectRoot, {
|
|
10625
10755
|
browser: runBrowser,
|
|
10626
10756
|
browserBaseUrl: baseUrl,
|
|
@@ -10691,7 +10821,7 @@ async function cmdVerifyWorkflow(args) {
|
|
|
10691
10821
|
return;
|
|
10692
10822
|
}
|
|
10693
10823
|
if (workspaceMode) {
|
|
10694
|
-
const { cmdWorkspace } = await import("./workspace-
|
|
10824
|
+
const { cmdWorkspace } = await import("./workspace-YYCRXJNB.js");
|
|
10695
10825
|
await cmdWorkspace(process.cwd(), ["workspace", "health", ...withoutWorkflowOnlyFlags(args)]);
|
|
10696
10826
|
return;
|
|
10697
10827
|
}
|
|
@@ -10734,7 +10864,7 @@ async function cmdVerifyWorkflow(args) {
|
|
|
10734
10864
|
process.exitCode = void 0;
|
|
10735
10865
|
}
|
|
10736
10866
|
}
|
|
10737
|
-
const { cmdHealth, parseHealthArgs } = await import("./health-
|
|
10867
|
+
const { cmdHealth, parseHealthArgs } = await import("./health-ADHNLMX3.js");
|
|
10738
10868
|
await cmdHealth(workspaceInfo.appRoot, parseHealthArgs(healthArgs));
|
|
10739
10869
|
if (localPatterns) {
|
|
10740
10870
|
const validation = validateLocalLaw(workspaceInfo.appRoot);
|
|
@@ -11845,7 +11975,7 @@ async function main() {
|
|
|
11845
11975
|
const { flags } = parseLooseArgs(args);
|
|
11846
11976
|
const workspaceInfo = resolveWorkflowProject(flags, "health");
|
|
11847
11977
|
if (!workspaceInfo) break;
|
|
11848
|
-
const { cmdHealth, parseHealthArgs } = await import("./health-
|
|
11978
|
+
const { cmdHealth, parseHealthArgs } = await import("./health-ADHNLMX3.js");
|
|
11849
11979
|
await cmdHealth(workspaceInfo.appRoot, parseHealthArgs(stripProjectArgs(args)));
|
|
11850
11980
|
} catch (e) {
|
|
11851
11981
|
console.error(error2(e.message));
|
|
@@ -11873,7 +12003,7 @@ async function main() {
|
|
|
11873
12003
|
cmdStudioHelp();
|
|
11874
12004
|
break;
|
|
11875
12005
|
}
|
|
11876
|
-
const { cmdStudio, parseStudioArgs } = await import("./studio-
|
|
12006
|
+
const { cmdStudio, parseStudioArgs } = await import("./studio-GZFCCVSO.js");
|
|
11877
12007
|
await cmdStudio(process.cwd(), parseStudioArgs(args));
|
|
11878
12008
|
} catch (e) {
|
|
11879
12009
|
console.error(error2(e.message));
|
|
@@ -11887,7 +12017,7 @@ async function main() {
|
|
|
11887
12017
|
cmdWorkspaceHelp();
|
|
11888
12018
|
break;
|
|
11889
12019
|
}
|
|
11890
|
-
const { cmdWorkspace } = await import("./workspace-
|
|
12020
|
+
const { cmdWorkspace } = await import("./workspace-YYCRXJNB.js");
|
|
11891
12021
|
await cmdWorkspace(process.cwd(), args);
|
|
11892
12022
|
} catch (e) {
|
|
11893
12023
|
console.error(error2(e.message));
|
|
@@ -12004,7 +12134,7 @@ async function main() {
|
|
|
12004
12134
|
route,
|
|
12005
12135
|
file,
|
|
12006
12136
|
fromCode,
|
|
12007
|
-
projectRoot: workspaceInfo?.appRoot
|
|
12137
|
+
projectRoot: workspaceInfo?.appRoot ?? process.cwd()
|
|
12008
12138
|
});
|
|
12009
12139
|
break;
|
|
12010
12140
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createWorkspaceHealthReport
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NUNUW5IS.js";
|
|
4
4
|
import {
|
|
5
5
|
createProjectHealthReport
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-5WPRTVNA.js";
|
|
7
7
|
import {
|
|
8
8
|
sendStudioHealthRefreshedTelemetry,
|
|
9
9
|
sendStudioStartedTelemetry
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
listWorkspaceProjects,
|
|
8
8
|
parseWorkspaceArgs,
|
|
9
9
|
shouldFailWorkspaceHealth
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-NUNUW5IS.js";
|
|
11
|
+
import "./chunk-5WPRTVNA.js";
|
|
12
12
|
import "./chunk-4SZ4SEKT.js";
|
|
13
13
|
export {
|
|
14
14
|
cmdWorkspace,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decantr/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.11.0",
|
|
4
4
|
"description": "Decantr CLI - scaffold, audit, inspect Project Health, and maintain Decantr projects from the terminal",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"decantr",
|
|
@@ -49,10 +49,10 @@
|
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"ajv": "^8.20.0",
|
|
51
51
|
"@decantr/core": "2.1.0",
|
|
52
|
-
"@decantr/essence-spec": "2.0.1",
|
|
53
|
-
"@decantr/registry": "2.2.0",
|
|
54
52
|
"@decantr/telemetry": "2.2.1",
|
|
55
|
-
"@decantr/
|
|
53
|
+
"@decantr/essence-spec": "2.0.1",
|
|
54
|
+
"@decantr/verifier": "2.4.0",
|
|
55
|
+
"@decantr/registry": "2.2.0"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "tsup",
|