@decantr/cli 2.9.7 → 2.10.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 +11 -5
- package/dist/bin.js +1 -1
- package/dist/{chunk-66L74ZNJ.js → chunk-HQUTNZKF.js} +303 -18
- package/dist/index.js +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -17,10 +17,10 @@ Or run it without installing:
|
|
|
17
17
|
npx @decantr/cli new my-app --blueprint=esports-hq
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
Use `decantr setup` when you are unsure which path applies. It detects whether the repo is empty, already attached, or a Brownfield app and recommends the
|
|
20
|
+
Use `decantr setup` when you are unsure which path applies. It detects whether the repo is empty, already attached, or a Brownfield app and recommends the right entry path.
|
|
21
21
|
Use `decantr new` for a greenfield workspace in a fresh directory. With a blueprint/archetype it uses the runnable adapter and Decantr CSS; without registry content it creates a contract-only workspace unless you explicitly pass `--adoption=decantr-css`.
|
|
22
22
|
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.
|
|
23
|
-
Use `decantr doctor` when the next step is unclear, `decantr task` before asking an LLM to modify a route, `decantr verify` after the edit, and `decantr ci` in required automation. Use `decantr codify --from-audit` when you want project-owned UI patterns and local rules such as button/card/shell/theme standards to appear in future task context and verification.
|
|
23
|
+
Use `decantr doctor` when the next step is unclear, `decantr task` before asking an LLM to modify a route, `decantr verify` after the edit, and `decantr ci` in required automation. Use `decantr codify --from-audit` when you want project-owned UI patterns and local rules 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 and rules as project authority.
|
|
24
24
|
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.
|
|
25
25
|
Use `decantr init`, `decantr analyze`, `decantr check`, and `decantr health` as advanced primitives when you need direct control over one step.
|
|
26
26
|
|
|
@@ -80,13 +80,13 @@ 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
|
|
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.
|
|
84
84
|
|
|
85
85
|
## What It Does
|
|
86
86
|
|
|
87
87
|
- scaffolds Decantr projects from blueprints, archetypes, or prompts
|
|
88
88
|
- guides users through human workflow commands: setup, adopt, doctor, task, verify, ci, and codify
|
|
89
|
-
- supports explicit workflow lanes: greenfield blueprint, greenfield contract-only, brownfield adoption, and hybrid composition
|
|
89
|
+
- supports explicit workflow lanes: greenfield blueprint, greenfield contract-only, brownfield adoption, Hybrid local law, Hybrid style bridge, Hybrid Decantr CSS, and hybrid composition
|
|
90
90
|
- generates execution-pack context files for AI coding assistants
|
|
91
91
|
- audits projects against Decantr contracts
|
|
92
92
|
- produces local Project Health reports, Evidence Bundles, workspace health, and a localhost Studio dashboard for end-user drift triage
|
|
@@ -96,6 +96,12 @@ Brownfield analysis also writes `.decantr/doctrine-map.json`, a ranked source-pr
|
|
|
96
96
|
- syncs paginated hosted registry content into a full slug-keyed local cache for offline guards and context generation
|
|
97
97
|
- validates, refreshes, and maintains `decantr.essence.json`
|
|
98
98
|
|
|
99
|
+
## Security And Permissions
|
|
100
|
+
|
|
101
|
+
The CLI is intentionally a local project inspector and artifact writer. It reads selected project/workspace files, package manifests, routing/style/config files, `.decantr` artifacts, and Decantr cache/config files. It writes `decantr.essence.json`, `DECANTR.md`, `.decantr/*`, generated context packs, optional CI workflows/snippets, optional style/export files, and auth/telemetry config only when explicitly requested.
|
|
102
|
+
|
|
103
|
+
Telemetry is disabled by default. Hosted registry, hosted pack hydration, hosted critique/audit, and browser evidence are explicit command paths; screenshots and Evidence Bundles stay local unless a hosted workflow is invoked. Release audits prove the installed package with `npm pack --dry-run --json`. See [security permissions](https://decantr.ai/reference/security-permissions.md).
|
|
104
|
+
|
|
99
105
|
## Common Commands
|
|
100
106
|
|
|
101
107
|
```bash
|
|
@@ -142,7 +148,7 @@ decantr showcase verification --json
|
|
|
142
148
|
|
|
143
149
|
`decantr verify` is the workflow command most users should run locally after edits. It delegates to Project Health, can add Brownfield guard validation with `--brownfield`, requires an accepted local pattern pack with `--local-patterns`, scans `.decantr/rules.json` when present, supports workspace mode, and writes evidence to `.decantr/evidence/latest.json` by default when `--evidence` is used.
|
|
144
150
|
|
|
145
|
-
`decantr doctor` explains project/workspace state, adoption mode, generated artifacts, local law, visual evidence, design authority signals, CI wiring, and
|
|
151
|
+
`decantr doctor` explains project/workspace state, adoption mode, adoption lane, generated artifacts, local law, visual evidence, design authority signals, CI wiring, and an ordered next-step queue. It is the command to reach for when an app is in a monorepo, has stale Decantr files, or someone is not sure what Decantr expects next.
|
|
146
152
|
|
|
147
153
|
`decantr setup` is non-mutating orientation. In an attached Brownfield app it reflects whether local law is already accepted, so the recommended verify command includes `--local-patterns` only when the project has that layer.
|
|
148
154
|
|
package/dist/bin.js
CHANGED
|
@@ -3025,7 +3025,8 @@ var RESET2 = "\x1B[0m";
|
|
|
3025
3025
|
var GREEN2 = "\x1B[32m";
|
|
3026
3026
|
var CYAN = "\x1B[36m";
|
|
3027
3027
|
var YELLOW2 = "\x1B[33m";
|
|
3028
|
-
async function cmdAnalyze(projectRoot = process.cwd(), workspace) {
|
|
3028
|
+
async function cmdAnalyze(projectRoot = process.cwd(), workspace, options = {}) {
|
|
3029
|
+
const printNextStep = options.printNextStep ?? true;
|
|
3029
3030
|
const startedAt = Date.now();
|
|
3030
3031
|
console.log(`
|
|
3031
3032
|
${BOLD}Analyzing project...${RESET2}
|
|
@@ -3190,11 +3191,13 @@ ${DIM2}Written to:${RESET2} ${outputPath}`);
|
|
|
3190
3191
|
console.log(`${DIM2}Brownfield intelligence:${RESET2} ${intelligenceArtifacts.intelligencePath}`);
|
|
3191
3192
|
console.log(`${DIM2}Theme inventory:${RESET2} ${intelligenceArtifacts.themeInventoryPath}`);
|
|
3192
3193
|
console.log(`${DIM2}Enrichment backlog:${RESET2} ${intelligenceArtifacts.backlogPath}`);
|
|
3193
|
-
|
|
3194
|
-
|
|
3194
|
+
if (printNextStep) {
|
|
3195
|
+
console.log(
|
|
3196
|
+
`
|
|
3195
3197
|
${YELLOW2}Next step:${RESET2} Review ${BOLD}${reportDisplayPath}${RESET2}, then run ${BOLD}${recommendedAttachCommand}${RESET2} to attach Decantr using the observed proposal.
|
|
3196
3198
|
`
|
|
3197
|
-
|
|
3199
|
+
);
|
|
3200
|
+
}
|
|
3198
3201
|
await sendAnalyzeCompletedTelemetry({
|
|
3199
3202
|
componentCount: components.componentCount,
|
|
3200
3203
|
dependencyCategoryCount: [
|
|
@@ -3281,7 +3284,17 @@ function createBrownfieldCodifyProposal(input) {
|
|
|
3281
3284
|
ruleFiles: input.detected.existingRuleFiles,
|
|
3282
3285
|
routeCount: routes.length
|
|
3283
3286
|
},
|
|
3284
|
-
purpose: "Project-owned Brownfield UI law. Review and edit before accepting; Decantr treats this as authoritative only after it is copied to .decantr/local-patterns.json.",
|
|
3287
|
+
purpose: "Project-owned Brownfield/Hybrid UI law. Review and edit before accepting; Decantr treats this as authoritative only after it is copied to .decantr/local-patterns.json.",
|
|
3288
|
+
hybrid: {
|
|
3289
|
+
intent: "This local pattern pack is the first Hybrid authority layer: it maps Decantr concepts onto project-owned components, tokens, classes, and rules without replacing the app runtime.",
|
|
3290
|
+
authorityPrecedence: [
|
|
3291
|
+
"existing production source",
|
|
3292
|
+
"accepted local patterns and rules",
|
|
3293
|
+
"Decantr Essence V4 contract",
|
|
3294
|
+
"hosted registry patterns and execution packs as optional guidance"
|
|
3295
|
+
],
|
|
3296
|
+
hostedPatternMapping: "Use hosted patterns as vocabulary and review guidance. Before enforcing one, map it to a project-owned component path, class recipe, token recipe, or explicit exception here."
|
|
3297
|
+
},
|
|
3285
3298
|
patterns: [
|
|
3286
3299
|
{
|
|
3287
3300
|
id: "button",
|
|
@@ -3356,13 +3369,15 @@ function createBrownfieldCodifyProposal(input) {
|
|
|
3356
3369
|
"Avoid raw hex/rgb values in component templates unless documented as dynamic data.",
|
|
3357
3370
|
"Avoid static inline styles for reusable visual treatment.",
|
|
3358
3371
|
"When adding a new route, map it to an existing local pattern before inventing a new visual variant.",
|
|
3359
|
-
"When adding a theme variant, update .decantr/theme-inventory.json and this local pattern pack."
|
|
3372
|
+
"When adding a theme variant, update .decantr/theme-inventory.json and this local pattern pack.",
|
|
3373
|
+
"Map hosted Decantr patterns into project-owned local law before making them enforceable."
|
|
3360
3374
|
],
|
|
3361
3375
|
nextSteps: [
|
|
3362
3376
|
"Edit this proposal with real component paths and token/class recipes.",
|
|
3363
3377
|
"Run decantr codify --accept after review.",
|
|
3364
3378
|
"Use decantr task <route> before LLM edits so local law appears in task context.",
|
|
3365
3379
|
"Run decantr verify --brownfield --local-patterns after edits.",
|
|
3380
|
+
"For Hybrid adoption, start with warn-level local rules and raise severities only after the team agrees the law is stable.",
|
|
3366
3381
|
"Wire deterministic project rules into ESLint, Biome, Storybook, visual tests, or CI where Decantr should not guess."
|
|
3367
3382
|
]
|
|
3368
3383
|
};
|
|
@@ -3371,7 +3386,7 @@ function createBrownfieldCodifyProposal(input) {
|
|
|
3371
3386
|
status: "proposal",
|
|
3372
3387
|
generatedAt,
|
|
3373
3388
|
source: input.fromAudit ? "decantr codify --from-audit" : "decantr codify",
|
|
3374
|
-
purpose: "Mechanical Brownfield checks owned by this project. These rules are intentionally local and stack-agnostic; edit before accepting.",
|
|
3389
|
+
purpose: "Mechanical Brownfield/Hybrid checks owned by this project. These rules are intentionally local and stack-agnostic; edit before accepting.",
|
|
3375
3390
|
enforcement: {
|
|
3376
3391
|
defaultSeverity: "warn",
|
|
3377
3392
|
mode: "warn",
|
|
@@ -4462,6 +4477,111 @@ function statusFromIssues(issues, essenceVersion) {
|
|
|
4462
4477
|
}
|
|
4463
4478
|
return "healthy";
|
|
4464
4479
|
}
|
|
4480
|
+
function appendUnique(commands, command) {
|
|
4481
|
+
if (!command) return;
|
|
4482
|
+
if (!commands.includes(command)) commands.push(command);
|
|
4483
|
+
}
|
|
4484
|
+
function deriveAdoptionLane(input) {
|
|
4485
|
+
if (input.workspaceMode) {
|
|
4486
|
+
return {
|
|
4487
|
+
id: "workspace",
|
|
4488
|
+
label: "Workspace overview",
|
|
4489
|
+
sourceAuthority: "Each attached app keeps its own Decantr contract and source authority.",
|
|
4490
|
+
styleAuthority: "Per-app adoption mode",
|
|
4491
|
+
activeAuthorities: ["workspace project list", "attached app contracts"],
|
|
4492
|
+
nextChoice: "Pick an app with --project when you want route-level or source-level guidance."
|
|
4493
|
+
};
|
|
4494
|
+
}
|
|
4495
|
+
if (!input.essenceVersion) {
|
|
4496
|
+
return {
|
|
4497
|
+
id: "unattached",
|
|
4498
|
+
label: "Unattached app",
|
|
4499
|
+
sourceAuthority: "Existing app only",
|
|
4500
|
+
styleAuthority: "Existing app styling system",
|
|
4501
|
+
activeAuthorities: ["source tree"],
|
|
4502
|
+
nextChoice: "Run decantr adopt to attach an observed contract before choosing Brownfield or Hybrid law."
|
|
4503
|
+
};
|
|
4504
|
+
}
|
|
4505
|
+
const hasLocalLaw = input.localPatternsPresent || input.localRulesPresent;
|
|
4506
|
+
if (input.workflowMode === "hybrid-compose") {
|
|
4507
|
+
return {
|
|
4508
|
+
id: "hybrid-compose",
|
|
4509
|
+
label: "Hybrid composition",
|
|
4510
|
+
sourceAuthority: "Existing app plus selected Decantr/local law",
|
|
4511
|
+
styleAuthority: input.adoptionMode === "decantr-css" ? "Decantr CSS runtime is active where adopted" : input.adoptionMode === "style-bridge" ? "Style bridge maps Decantr intent into the app styling system" : "Existing app styling system remains primary",
|
|
4512
|
+
activeAuthorities: [
|
|
4513
|
+
"existing source",
|
|
4514
|
+
"Essence V4 contract",
|
|
4515
|
+
hasLocalLaw ? "accepted local law" : "reviewed Hybrid choices",
|
|
4516
|
+
input.packManifestPresent ? "hosted execution packs" : "optional hosted packs"
|
|
4517
|
+
],
|
|
4518
|
+
nextChoice: "Use task/verify for daily work and keep any hosted pattern adoption mapped into project-owned law."
|
|
4519
|
+
};
|
|
4520
|
+
}
|
|
4521
|
+
if (input.workflowMode === "brownfield-attach") {
|
|
4522
|
+
if (input.adoptionMode === "decantr-css") {
|
|
4523
|
+
return {
|
|
4524
|
+
id: "hybrid-decantr-css",
|
|
4525
|
+
label: "Hybrid with Decantr CSS",
|
|
4526
|
+
sourceAuthority: "Existing app plus explicitly adopted Decantr CSS runtime",
|
|
4527
|
+
styleAuthority: "Decantr CSS runtime is active where adopted",
|
|
4528
|
+
activeAuthorities: ["existing source", "Essence V4 contract", "Decantr CSS runtime"],
|
|
4529
|
+
nextChoice: "Keep Decantr CSS usage explicit and validate route changes with task and verify."
|
|
4530
|
+
};
|
|
4531
|
+
}
|
|
4532
|
+
if (input.adoptionMode === "style-bridge") {
|
|
4533
|
+
return {
|
|
4534
|
+
id: "hybrid-style-bridge",
|
|
4535
|
+
label: "Hybrid style bridge",
|
|
4536
|
+
sourceAuthority: "Existing app plus Decantr intent mapped through a style bridge",
|
|
4537
|
+
styleAuthority: "Style bridge over the existing app styling system",
|
|
4538
|
+
activeAuthorities: ["existing source", "Essence V4 contract", "style bridge"],
|
|
4539
|
+
nextChoice: "Use local law to decide which component families the bridge governs before making it strict."
|
|
4540
|
+
};
|
|
4541
|
+
}
|
|
4542
|
+
if (hasLocalLaw) {
|
|
4543
|
+
return {
|
|
4544
|
+
id: "hybrid-local-law",
|
|
4545
|
+
label: "Hybrid local law",
|
|
4546
|
+
sourceAuthority: "Existing app plus accepted project-owned UI law",
|
|
4547
|
+
styleAuthority: input.designAuthority.length > 0 ? "Existing design authority plus accepted local rules" : "Accepted local rules over the current app styling system",
|
|
4548
|
+
activeAuthorities: [
|
|
4549
|
+
"existing source",
|
|
4550
|
+
"Essence V4 contract",
|
|
4551
|
+
"accepted local patterns/rules",
|
|
4552
|
+
input.packManifestPresent ? "hosted execution packs as guidance" : "optional hosted packs"
|
|
4553
|
+
],
|
|
4554
|
+
nextChoice: "Use task before edits and verify --local-patterns after edits; map hosted patterns into local law before enforcing them."
|
|
4555
|
+
};
|
|
4556
|
+
}
|
|
4557
|
+
return {
|
|
4558
|
+
id: "brownfield-contract-only",
|
|
4559
|
+
label: "Brownfield contract-only",
|
|
4560
|
+
sourceAuthority: "Existing app is authoritative",
|
|
4561
|
+
styleAuthority: "Existing app styling system",
|
|
4562
|
+
activeAuthorities: ["existing source", "Essence V4 contract"],
|
|
4563
|
+
nextChoice: "Stay contract-only for context, or codify local patterns/rules when you want Hybrid drift control."
|
|
4564
|
+
};
|
|
4565
|
+
}
|
|
4566
|
+
if (input.workflowMode === "greenfield-contract-only") {
|
|
4567
|
+
return {
|
|
4568
|
+
id: "greenfield-contract-only",
|
|
4569
|
+
label: "Greenfield contract-only",
|
|
4570
|
+
sourceAuthority: "Essence V4 contract",
|
|
4571
|
+
styleAuthority: "Project-chosen styling system",
|
|
4572
|
+
activeAuthorities: ["Essence V4 contract", "generated context"],
|
|
4573
|
+
nextChoice: "Add local rules or a style bridge only after the runtime conventions are clear."
|
|
4574
|
+
};
|
|
4575
|
+
}
|
|
4576
|
+
return {
|
|
4577
|
+
id: "greenfield-scaffold",
|
|
4578
|
+
label: "Greenfield scaffold",
|
|
4579
|
+
sourceAuthority: "Decantr contract and certified adapter output",
|
|
4580
|
+
styleAuthority: input.adoptionMode === "contract-only" ? "Project-chosen styling system" : "Decantr CSS",
|
|
4581
|
+
activeAuthorities: ["Essence V4 contract", "adapter output", "execution packs"],
|
|
4582
|
+
nextChoice: "Use task and verify to keep generated routes aligned with the compiled contract."
|
|
4583
|
+
};
|
|
4584
|
+
}
|
|
4465
4585
|
function buildDoctorReport(root, args) {
|
|
4466
4586
|
let projectArg;
|
|
4467
4587
|
for (let index = 1; index < args.length; index += 1) {
|
|
@@ -4495,6 +4615,8 @@ function buildDoctorReport(root, args) {
|
|
|
4495
4615
|
const workflowMode = projectJson?.initialized?.workflowMode ?? null;
|
|
4496
4616
|
const adoptionMode = projectJson?.initialized?.adoptionMode ?? null;
|
|
4497
4617
|
const packHydrationOptional = adoptionMode === "contract-only";
|
|
4618
|
+
const localPatternsPresent = existsSync16(localPatternsPath(appRoot));
|
|
4619
|
+
const localRulesPresent = existsSync16(localRulesPath(appRoot));
|
|
4498
4620
|
const missingPackReferences = workspaceMode ? projects.flatMap(
|
|
4499
4621
|
(project) => collectMissingPackManifestFiles(join18(workspaceRoot, project.path)).map(
|
|
4500
4622
|
(missing) => `${project.path}/${missing.relativePath}`
|
|
@@ -4587,7 +4709,7 @@ function buildDoctorReport(root, args) {
|
|
|
4587
4709
|
nextCommand: "decantr registry compile-packs <app-path>/decantr.essence.json --write-context"
|
|
4588
4710
|
});
|
|
4589
4711
|
}
|
|
4590
|
-
if (workflowMode === "brownfield-attach" && !
|
|
4712
|
+
if (workflowMode === "brownfield-attach" && !localPatternsPresent) {
|
|
4591
4713
|
issues.push({
|
|
4592
4714
|
category: "local-law",
|
|
4593
4715
|
severity: "info",
|
|
@@ -4615,7 +4737,49 @@ function buildDoctorReport(root, args) {
|
|
|
4615
4737
|
issues,
|
|
4616
4738
|
workspaceMode || workspaceInfo.requiresProjectSelection ? "4.0.0" : essenceVersion
|
|
4617
4739
|
);
|
|
4618
|
-
const
|
|
4740
|
+
const lane = deriveAdoptionLane({
|
|
4741
|
+
workspaceMode: workspaceMode || workspaceInfo.requiresProjectSelection,
|
|
4742
|
+
essenceVersion,
|
|
4743
|
+
workflowMode,
|
|
4744
|
+
adoptionMode,
|
|
4745
|
+
localPatternsPresent,
|
|
4746
|
+
localRulesPresent,
|
|
4747
|
+
designAuthority,
|
|
4748
|
+
packManifestPresent
|
|
4749
|
+
});
|
|
4750
|
+
const projectFlag = projectPath ? ` --project ${projectPath}` : "";
|
|
4751
|
+
const verifyCommand = workflowMode === "brownfield-attach" ? `decantr verify --brownfield --local-patterns${projectFlag}` : workspaceMode ? "decantr ci --workspace" : `decantr verify${projectFlag}`;
|
|
4752
|
+
const ciCommand = workspaceMode ? "decantr ci --workspace --fail-on error" : `decantr ci${projectFlag} --fail-on error`;
|
|
4753
|
+
const recommendedNextCommands = [];
|
|
4754
|
+
const blockingIssue = issues.find((issue) => issue.category === "workspace" && issue.nextCommand) ?? issues.find(
|
|
4755
|
+
(issue) => (issue.category === "setup" || issue.category === "migration") && issue.nextCommand
|
|
4756
|
+
);
|
|
4757
|
+
if (blockingIssue) {
|
|
4758
|
+
appendUnique(recommendedNextCommands, blockingIssue.nextCommand);
|
|
4759
|
+
} else {
|
|
4760
|
+
appendUnique(
|
|
4761
|
+
recommendedNextCommands,
|
|
4762
|
+
issues.find((issue) => issue.category === "ci" && issue.message.includes("not pinned"))?.nextCommand
|
|
4763
|
+
);
|
|
4764
|
+
if (workflowMode === "brownfield-attach" && !localPatternsPresent) {
|
|
4765
|
+
appendUnique(recommendedNextCommands, `decantr codify --from-audit${projectFlag}`);
|
|
4766
|
+
appendUnique(recommendedNextCommands, `decantr codify --accept${projectFlag}`);
|
|
4767
|
+
}
|
|
4768
|
+
appendUnique(
|
|
4769
|
+
recommendedNextCommands,
|
|
4770
|
+
issues.find((issue) => issue.category === "generated-artifact")?.nextCommand
|
|
4771
|
+
);
|
|
4772
|
+
appendUnique(
|
|
4773
|
+
recommendedNextCommands,
|
|
4774
|
+
issues.find((issue) => issue.category === "ci" && issue.message.includes("No Decantr CI"))?.nextCommand
|
|
4775
|
+
);
|
|
4776
|
+
if (workflowMode === "brownfield-attach") {
|
|
4777
|
+
appendUnique(recommendedNextCommands, `decantr task <route> "<change>"${projectFlag}`);
|
|
4778
|
+
}
|
|
4779
|
+
appendUnique(recommendedNextCommands, verifyCommand);
|
|
4780
|
+
appendUnique(recommendedNextCommands, ciCommand);
|
|
4781
|
+
}
|
|
4782
|
+
const recommendedNextCommand = recommendedNextCommands[0] ?? ciCommand;
|
|
4619
4783
|
return {
|
|
4620
4784
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4621
4785
|
workspaceRoot,
|
|
@@ -4647,16 +4811,18 @@ function buildDoctorReport(root, args) {
|
|
|
4647
4811
|
missingReferencedFiles: missingPackReferences.slice(0, 25)
|
|
4648
4812
|
},
|
|
4649
4813
|
localLaw: {
|
|
4650
|
-
patternsPresent:
|
|
4651
|
-
rulesPresent:
|
|
4814
|
+
patternsPresent: localPatternsPresent,
|
|
4815
|
+
rulesPresent: localRulesPresent
|
|
4652
4816
|
},
|
|
4653
4817
|
visualEvidence: {
|
|
4654
4818
|
manifestPresent: existsSync16(join18(appRoot, ".decantr", "evidence", "visual-manifest.json"))
|
|
4655
4819
|
},
|
|
4820
|
+
lane,
|
|
4656
4821
|
designAuthority,
|
|
4657
4822
|
status,
|
|
4658
4823
|
issues,
|
|
4659
|
-
recommendedNextCommand
|
|
4824
|
+
recommendedNextCommand,
|
|
4825
|
+
recommendedNextCommands
|
|
4660
4826
|
};
|
|
4661
4827
|
}
|
|
4662
4828
|
function colorForStatus(status) {
|
|
@@ -4680,6 +4846,13 @@ function formatDoctorText(report) {
|
|
|
4680
4846
|
` Workflow: ${report.project.workflowMode ?? "unknown"} | adoption ${report.project.adoptionMode ?? "unknown"}`,
|
|
4681
4847
|
` Sync state: ${report.project.syncStatus ?? "unknown"} ${DIM4}(registry/cache state, not generated context freshness)${RESET4}`,
|
|
4682
4848
|
"",
|
|
4849
|
+
`${BOLD3}Adoption Lane:${RESET4}`,
|
|
4850
|
+
` ${report.lane.label}`,
|
|
4851
|
+
` Source authority: ${report.lane.sourceAuthority}`,
|
|
4852
|
+
` Style authority: ${report.lane.styleAuthority}`,
|
|
4853
|
+
` Active: ${report.lane.activeAuthorities.join(", ")}`,
|
|
4854
|
+
` Choice: ${report.lane.nextChoice}`,
|
|
4855
|
+
"",
|
|
4683
4856
|
`${BOLD3}Generated Artifacts:${RESET4}`,
|
|
4684
4857
|
` Context directory: ${report.generatedArtifacts.contextDirPresent ? "present" : "missing"}`,
|
|
4685
4858
|
` Pack manifest: ${report.generatedArtifacts.packManifestPresent ? "present" : "missing"}`,
|
|
@@ -4713,13 +4886,20 @@ function formatDoctorText(report) {
|
|
|
4713
4886
|
if (issue.nextCommand) lines.push(` ${DIM4}${issue.nextCommand}${RESET4}`);
|
|
4714
4887
|
}
|
|
4715
4888
|
}
|
|
4716
|
-
lines.push("", `${BOLD3}Next:${RESET4}
|
|
4889
|
+
lines.push("", `${BOLD3}Next steps:${RESET4}`);
|
|
4890
|
+
for (const [index, command] of report.recommendedNextCommands.entries()) {
|
|
4891
|
+
lines.push(` ${index + 1}. ${command}`);
|
|
4892
|
+
}
|
|
4893
|
+
if (report.recommendedNextCommands.length === 0) {
|
|
4894
|
+
lines.push(` ${report.recommendedNextCommand}`);
|
|
4895
|
+
}
|
|
4896
|
+
lines.push("");
|
|
4717
4897
|
return `${lines.join("\n")}
|
|
4718
4898
|
`;
|
|
4719
4899
|
}
|
|
4720
4900
|
function cmdDoctorHelp() {
|
|
4721
4901
|
console.log(`
|
|
4722
|
-
${BOLD3}decantr doctor${RESET4} \u2014 Explain Decantr state and the next
|
|
4902
|
+
${BOLD3}decantr doctor${RESET4} \u2014 Explain Decantr state and the next commands to run
|
|
4723
4903
|
|
|
4724
4904
|
${BOLD3}Usage:${RESET4}
|
|
4725
4905
|
decantr doctor [--project <path>] [--workspace] [--json]
|
|
@@ -10399,8 +10579,10 @@ async function cmdAdoptWorkflow(args) {
|
|
|
10399
10579
|
return;
|
|
10400
10580
|
}
|
|
10401
10581
|
}
|
|
10402
|
-
await cmdAnalyze(projectRoot, workspaceInfo);
|
|
10582
|
+
await cmdAnalyze(projectRoot, workspaceInfo, { printNextStep: false });
|
|
10403
10583
|
if (process.exitCode && process.exitCode !== 0) return;
|
|
10584
|
+
const initCommand = projectArg ? `decantr init --project ${projectArg} --existing ${proposalFlag}` : `decantr init --existing ${proposalFlag}`;
|
|
10585
|
+
console.log(dim3(`Analysis artifacts written; continuing with ${initCommand}.`));
|
|
10404
10586
|
await cmdInit({
|
|
10405
10587
|
existing: true,
|
|
10406
10588
|
yes: true,
|
|
@@ -10614,6 +10796,81 @@ function readJsonIfPresent(path) {
|
|
|
10614
10796
|
return null;
|
|
10615
10797
|
}
|
|
10616
10798
|
}
|
|
10799
|
+
function mentionsWord(text, term) {
|
|
10800
|
+
const escaped = term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
10801
|
+
return new RegExp(`\\b${escaped}\\b`, "i").test(text);
|
|
10802
|
+
}
|
|
10803
|
+
function createTaskAuthoritySummary(input) {
|
|
10804
|
+
const detected = detectProject(input.projectRoot);
|
|
10805
|
+
const hasLocalLaw = input.hasLocalPatterns || input.hasLocalRules;
|
|
10806
|
+
let lane = "Brownfield contract-only";
|
|
10807
|
+
let sourceAuthority = "Existing app is authoritative; Decantr supplies contract context.";
|
|
10808
|
+
let styleAuthority = "Use the existing styling system.";
|
|
10809
|
+
const activeAuthorities = ["existing source", "Essence V4 contract"];
|
|
10810
|
+
if (input.workflowMode === "hybrid-compose") {
|
|
10811
|
+
lane = "Hybrid composition";
|
|
10812
|
+
sourceAuthority = "Existing app plus selected Decantr/local law are authoritative.";
|
|
10813
|
+
} else if (input.workflowMode === "brownfield-attach" && input.adoptionMode === "decantr-css") {
|
|
10814
|
+
lane = "Hybrid with Decantr CSS";
|
|
10815
|
+
sourceAuthority = "Existing app remains authoritative except where Decantr CSS is explicitly adopted.";
|
|
10816
|
+
styleAuthority = "Decantr CSS runtime is active where adopted.";
|
|
10817
|
+
activeAuthorities.push("Decantr CSS runtime");
|
|
10818
|
+
} else if (input.workflowMode === "brownfield-attach" && input.adoptionMode === "style-bridge") {
|
|
10819
|
+
lane = "Hybrid style bridge";
|
|
10820
|
+
sourceAuthority = "Existing app remains authoritative; Decantr intent maps through the style bridge.";
|
|
10821
|
+
styleAuthority = "Use bridge tokens/classes as a mapping layer onto the app styling system.";
|
|
10822
|
+
activeAuthorities.push("style bridge");
|
|
10823
|
+
} else if (input.workflowMode === "brownfield-attach" && hasLocalLaw) {
|
|
10824
|
+
lane = "Hybrid local law";
|
|
10825
|
+
sourceAuthority = "Existing app plus accepted project-owned UI law are authoritative.";
|
|
10826
|
+
styleAuthority = "Use project-owned components, tokens, classes, and accepted local rules.";
|
|
10827
|
+
} else if (input.workflowMode?.startsWith("greenfield")) {
|
|
10828
|
+
lane = input.workflowMode === "greenfield-contract-only" ? "Greenfield contract-only" : "Greenfield scaffold";
|
|
10829
|
+
sourceAuthority = "Essence V4 and generated context are authoritative.";
|
|
10830
|
+
styleAuthority = input.adoptionMode === "contract-only" ? "Use the project-chosen styling system." : "Use Decantr CSS where generated by the adapter.";
|
|
10831
|
+
}
|
|
10832
|
+
if (hasLocalLaw) activeAuthorities.push("accepted local patterns/rules");
|
|
10833
|
+
if (input.hasPackManifest) activeAuthorities.push("hosted execution packs as guidance");
|
|
10834
|
+
const framework = detected.framework ?? "unknown";
|
|
10835
|
+
const runtimeBoundary = `Detected ${framework}; do not introduce another frontend runtime inside this route unless the task is explicitly a reviewed migration or isolated integration plan.`;
|
|
10836
|
+
const warnings = [];
|
|
10837
|
+
const task = input.taskSummary;
|
|
10838
|
+
const runtimeTerms = [
|
|
10839
|
+
"angular",
|
|
10840
|
+
"vue",
|
|
10841
|
+
"svelte",
|
|
10842
|
+
"solid",
|
|
10843
|
+
"nextjs",
|
|
10844
|
+
"next.js",
|
|
10845
|
+
"react",
|
|
10846
|
+
"bootstrap",
|
|
10847
|
+
"shadcn"
|
|
10848
|
+
];
|
|
10849
|
+
const compatibleMentions = /* @__PURE__ */ new Set();
|
|
10850
|
+
if (framework === "nextjs") compatibleMentions.add("react");
|
|
10851
|
+
if (framework === "react") compatibleMentions.add("react");
|
|
10852
|
+
if (framework === "vue" || framework === "nuxt") compatibleMentions.add("vue");
|
|
10853
|
+
for (const term of runtimeTerms) {
|
|
10854
|
+
if (!mentionsWord(task, term)) continue;
|
|
10855
|
+
if (term === framework || compatibleMentions.has(term)) continue;
|
|
10856
|
+
if ((term === "nextjs" || term === "next.js") && framework === "nextjs") continue;
|
|
10857
|
+
if (term === "shadcn" || term === "bootstrap") {
|
|
10858
|
+
warnings.push(
|
|
10859
|
+
`Requested ${term} should be treated as optional Hybrid guidance unless this project already owns that library or the task explicitly adopts it.`
|
|
10860
|
+
);
|
|
10861
|
+
continue;
|
|
10862
|
+
}
|
|
10863
|
+
warnings.push(
|
|
10864
|
+
`Task mentions ${term}, but the detected runtime is ${framework}. Prefer a dry-run plan or local pattern mapping before adding cross-runtime code.`
|
|
10865
|
+
);
|
|
10866
|
+
}
|
|
10867
|
+
if (input.adoptionMode !== "decantr-css" && (/@decantr\/css/i.test(task) || /\bdecantr css\b/i.test(task) || /\bd-[a-z0-9-]+/i.test(task))) {
|
|
10868
|
+
warnings.push(
|
|
10869
|
+
"This project is not in decantr-css adoption mode. Do not add @decantr/css or d-* classes unless the user explicitly changes adoption mode."
|
|
10870
|
+
);
|
|
10871
|
+
}
|
|
10872
|
+
return { lane, sourceAuthority, styleAuthority, activeAuthorities, runtimeBoundary, warnings };
|
|
10873
|
+
}
|
|
10617
10874
|
async function cmdTaskWorkflow(args) {
|
|
10618
10875
|
const { flags, positional } = parseLooseArgs(args);
|
|
10619
10876
|
const workspaceInfo = resolveWorkflowProject(flags, "task");
|
|
@@ -10657,6 +10914,7 @@ async function cmdTaskWorkflow(args) {
|
|
|
10657
10914
|
const page = section?.pages.find((entry) => entry.id === target.page);
|
|
10658
10915
|
const contextDir = join31(workspaceInfo.appRoot, ".decantr", "context");
|
|
10659
10916
|
const manifest = readJsonIfPresent(join31(contextDir, "pack-manifest.json"));
|
|
10917
|
+
const projectJson = readJsonIfPresent(join31(workspaceInfo.appRoot, ".decantr", "project.json"));
|
|
10660
10918
|
const pagePack = manifest?.pages?.find((entry) => entry.id === target.page);
|
|
10661
10919
|
const sectionPack = manifest?.sections?.find((entry) => entry.id === target.section);
|
|
10662
10920
|
const visualManifest = readJsonIfPresent(join31(workspaceInfo.appRoot, ".decantr", "evidence", "visual-manifest.json"));
|
|
@@ -10672,6 +10930,15 @@ async function cmdTaskWorkflow(args) {
|
|
|
10672
10930
|
const changedSince = flagString(flags, "since");
|
|
10673
10931
|
const currentChangedFiles = changedFiles(workspaceInfo.appRoot, changedSince);
|
|
10674
10932
|
const changedRoutes = routeImpacts(workspaceInfo.appRoot, currentChangedFiles);
|
|
10933
|
+
const authority = createTaskAuthoritySummary({
|
|
10934
|
+
projectRoot: workspaceInfo.appRoot,
|
|
10935
|
+
workflowMode: projectJson?.initialized?.workflowMode ?? null,
|
|
10936
|
+
adoptionMode: projectJson?.initialized?.adoptionMode ?? null,
|
|
10937
|
+
hasLocalPatterns: existsSync29(localPatternPackPath),
|
|
10938
|
+
hasLocalRules: existsSync29(localRuleManifestPath),
|
|
10939
|
+
hasPackManifest: Boolean(manifest),
|
|
10940
|
+
taskSummary
|
|
10941
|
+
});
|
|
10675
10942
|
const context = {
|
|
10676
10943
|
route,
|
|
10677
10944
|
task: taskSummary || null,
|
|
@@ -10689,6 +10956,7 @@ async function cmdTaskWorkflow(args) {
|
|
|
10689
10956
|
existsSync29(localRuleManifestPath) ? displayProjectPath(workspaceInfo, ".decantr/rules.json") : null
|
|
10690
10957
|
].filter(Boolean),
|
|
10691
10958
|
screenshot: screenshot?.startsWith(".decantr/") ? displayProjectPath(workspaceInfo, screenshot) : screenshot ?? null,
|
|
10959
|
+
authority,
|
|
10692
10960
|
localLaw: displayedLocalLaw,
|
|
10693
10961
|
changedFiles: currentChangedFiles,
|
|
10694
10962
|
changedRoutes,
|
|
@@ -10714,6 +10982,16 @@ async function cmdTaskWorkflow(args) {
|
|
|
10714
10982
|
console.log(`${BOLD9}Visual evidence:${RESET16}`);
|
|
10715
10983
|
console.log(` ${cyan3(context.screenshot)}`);
|
|
10716
10984
|
}
|
|
10985
|
+
console.log("");
|
|
10986
|
+
console.log(`${BOLD9}Authority for this task:${RESET16}`);
|
|
10987
|
+
console.log(` Lane: ${context.authority.lane}`);
|
|
10988
|
+
console.log(` Source: ${context.authority.sourceAuthority}`);
|
|
10989
|
+
console.log(` Style: ${context.authority.styleAuthority}`);
|
|
10990
|
+
console.log(` Active: ${context.authority.activeAuthorities.join(", ")}`);
|
|
10991
|
+
console.log(` Runtime: ${context.authority.runtimeBoundary}`);
|
|
10992
|
+
for (const warning of context.authority.warnings) {
|
|
10993
|
+
console.log(` ${YELLOW12}Boundary:${RESET16} ${warning}`);
|
|
10994
|
+
}
|
|
10717
10995
|
if (context.localLaw.patternCount > 0 || context.localLaw.ruleCount > 0) {
|
|
10718
10996
|
console.log("");
|
|
10719
10997
|
console.log(`${BOLD9}Project-owned local law:${RESET16}`);
|
|
@@ -10752,7 +11030,7 @@ async function cmdTaskWorkflow(args) {
|
|
|
10752
11030
|
console.log("");
|
|
10753
11031
|
console.log(`${BOLD9}LLM instruction:${RESET16}`);
|
|
10754
11032
|
console.log(
|
|
10755
|
-
" Preserve the
|
|
11033
|
+
" Preserve the active authority above. Use the route pack, section context, local laws, changed-file impact, and visual evidence as the task contract before changing code."
|
|
10756
11034
|
);
|
|
10757
11035
|
console.log(` After editing, run ${cyan3(context.verifyCommand)}.`);
|
|
10758
11036
|
}
|
|
@@ -10785,6 +11063,11 @@ async function cmdCodifyWorkflow(args) {
|
|
|
10785
11063
|
if (result2.rulesAcceptedPath) {
|
|
10786
11064
|
console.log(success3(`Accepted local rule manifest: ${result2.rulesAcceptedPath}`));
|
|
10787
11065
|
}
|
|
11066
|
+
console.log(
|
|
11067
|
+
dim3(
|
|
11068
|
+
"Hybrid local law is now active: Decantr will treat accepted local patterns and rules as project-owned authority in task and verify flows."
|
|
11069
|
+
)
|
|
11070
|
+
);
|
|
10788
11071
|
console.log(
|
|
10789
11072
|
dim3(
|
|
10790
11073
|
`Run \`${withProject("decantr verify --brownfield --local-patterns", projectArg)}\` after project edits.`
|
|
@@ -10808,12 +11091,14 @@ async function cmdCodifyWorkflow(args) {
|
|
|
10808
11091
|
console.log(success3(`Wrote local rule proposal: ${result.rulesPath}`));
|
|
10809
11092
|
if (fromAudit) {
|
|
10810
11093
|
console.log(
|
|
10811
|
-
dim3(
|
|
11094
|
+
dim3(
|
|
11095
|
+
"Proposal includes source-derived component candidates, starter mechanical rules, and Hybrid authority guidance."
|
|
11096
|
+
)
|
|
10812
11097
|
);
|
|
10813
11098
|
}
|
|
10814
11099
|
console.log(
|
|
10815
11100
|
dim3(
|
|
10816
|
-
`Review both files, add real component paths/token recipes, then run \`${withProject("decantr codify --accept", projectArg)}\`.`
|
|
11101
|
+
`Review both files, add real component paths/token recipes, map hosted pattern ideas into project-owned law, then run \`${withProject("decantr codify --accept", projectArg)}\`.`
|
|
10817
11102
|
)
|
|
10818
11103
|
);
|
|
10819
11104
|
}
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decantr/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.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/registry": "2.2.0",
|
|
53
|
-
"@decantr/verifier": "2.3.3",
|
|
54
52
|
"@decantr/essence-spec": "2.0.1",
|
|
55
|
-
"@decantr/
|
|
53
|
+
"@decantr/registry": "2.2.0",
|
|
54
|
+
"@decantr/telemetry": "2.2.1",
|
|
55
|
+
"@decantr/verifier": "2.3.3"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"build": "tsup",
|