@decantr/cli 2.7.0 → 2.8.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 CHANGED
@@ -20,7 +20,7 @@ npx @decantr/cli new my-app --blueprint=esports-hq
20
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 next command.
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, and only applies it when you explicitly accept or merge it.
23
- Use `decantr task` before asking an LLM to modify a route, and `decantr verify` after the edit. Use `decantr codify` when you want project-owned UI patterns such as local button/card/shell standards to appear in future task context.
23
+ Use `decantr task` before asking an LLM to modify a route, and `decantr verify` after the edit. 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.
24
24
  Use `decantr init`, `decantr analyze`, `decantr check`, and `decantr health` as advanced primitives when you need direct control over one step.
25
25
 
26
26
  Current starter adapter availability:
@@ -38,11 +38,11 @@ Explicit workflow/adoption flags:
38
38
 
39
39
  ```bash
40
40
  decantr setup
41
- decantr adopt --base-url http://localhost:3000 --evidence --yes
42
- decantr task /feed "add saved recipe actions"
43
- decantr verify --brownfield
44
- decantr codify
41
+ decantr adopt --yes
42
+ decantr codify --from-audit
45
43
  decantr codify --accept
44
+ decantr task /feed "add saved recipe actions"
45
+ decantr verify --brownfield --local-patterns
46
46
  decantr init --workflow=greenfield --adoption=contract-only
47
47
  decantr analyze
48
48
  decantr init --existing --accept-proposal
@@ -61,11 +61,18 @@ Adoption modes:
61
61
  - `style-bridge` writes bridge tokens/files that map Decantr intent onto an existing style system without requiring `@decantr/css`.
62
62
  - `decantr-css` writes the full Decantr CSS files and runtime guidance.
63
63
 
64
- Monorepos store both `workspaceRoot` and `appRoot`. In non-interactive workspace-root runs with multiple app candidates, pass `--project=<path>` so Decantr attaches to the intended app.
64
+ Monorepos store both `workspaceRoot` and `appRoot`. Install Decantr at the workspace root if that is where dependencies are managed, but attach Decantr to an app root with `--project=<path>`.
65
+
66
+ ```bash
67
+ pnpm add -D -w @decantr/cli
68
+ pnpm exec decantr setup
69
+ pnpm exec decantr workspace list
70
+ pnpm exec decantr adopt --project apps/web --yes
71
+ ```
65
72
 
66
73
  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.
67
74
 
68
- 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 verify --brownfield` uses the Brownfield guard layer to flag actionable missing doctrine coverage, unsafe context, missing assistant bridges, style drift, and unsafe defaults without treating current database migrations as stale docs.
75
+ 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`; after review, `decantr codify --accept` promotes `.decantr/local-patterns.json` and `.decantr/rules.json`. `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.
69
76
 
70
77
  ## What It Does
71
78
 
@@ -86,10 +93,13 @@ Brownfield analysis also writes `.decantr/doctrine-map.json`, a ranked source-pr
86
93
  ```bash
87
94
  decantr setup
88
95
  decantr new my-app --blueprint=esports-hq
89
- decantr adopt --base-url http://localhost:3000 --evidence --yes
96
+ decantr adopt --yes
97
+ decantr adopt --project apps/web --yes
98
+ decantr codify --from-audit
99
+ decantr codify --accept
90
100
  decantr task /feed "add saved recipe actions"
91
101
  decantr verify --brownfield --local-patterns
92
- decantr codify
102
+ decantr verify --base-url http://localhost:3000 --evidence
93
103
  decantr init --existing --blueprint=esports-hq
94
104
  decantr init --workflow=greenfield --adoption=contract-only
95
105
  decantr rules preview
@@ -114,13 +124,14 @@ decantr showcase verification --json
114
124
 
115
125
  ## Project Health And Studio
116
126
 
117
- `decantr verify` is the workflow command most users should run locally and in CI. It delegates to Project Health, can add Brownfield guard validation with `--brownfield`, requires an accepted local pattern pack with `--local-patterns`, supports workspace mode, and writes evidence to `.decantr/evidence/latest.json` by default when `--evidence` is used.
127
+ `decantr verify` is the workflow command most users should run locally and in CI. 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.
118
128
 
119
129
  `decantr health` remains the advanced project observability primitive. It composes the existing verifier audit, guard checks, brownfield route drift checks, runtime evidence, and execution-pack files into a `ProjectHealthReport` with a status, score, route summary, pack summary, findings, and AI-ready remediation prompts.
120
130
 
121
131
  ```bash
122
132
  decantr verify
123
133
  decantr verify --brownfield --local-patterns
134
+ decantr verify --brownfield --local-patterns --fail-on warn
124
135
  decantr verify --base-url http://localhost:3000 --evidence
125
136
  decantr verify --since-baseline
126
137
  decantr verify init-ci --project apps/registry
@@ -149,7 +160,7 @@ Use `--json` for machines and schema validation, `--markdown` for CI summaries,
149
160
 
150
161
  `decantr verify init-ci` installs `.github/workflows/decantr-health.yml` for GitHub Actions. The generated workflow installs project dependencies, writes JSON/markdown health artifacts, gates with the Project Health CI command, appends the markdown report to the GitHub step summary, and uploads both files as artifacts. Use `--force` to replace an existing workflow, `--fail-on warn` for stricter repositories, or `--cli-version <version|latest>` to pin the package used by CI. In monorepos, add `--project <path>` from the repository root; dependency install stays at the root while health runs inside the app contract and uploads artifacts from that project path. Use `--workspace` to generate an aggregate gate that runs `decantr workspace health` from the repository root and uploads `.decantr/workspace-health.json` plus `.decantr/workspace-health.md`.
151
162
 
152
- `decantr workspace` is the monorepo reliability namespace. It discovers Decantr projects from `.decantr/workspace.json` or by finding `decantr.essence.json` files, runs projects with deterministic ordering, concurrency, per-project timeout, failure isolation, and aggregate JSON, and can limit a run to changed projects:
163
+ `decantr workspace` is the monorepo reliability namespace. Before attach, `workspace list` shows app candidates. After attach, it also discovers Decantr projects from `.decantr/workspace.json` or by finding `decantr.essence.json` files. Workspace health runs projects with deterministic ordering, concurrency, per-project timeout, failure isolation, and aggregate JSON, and can limit a run to changed projects:
153
164
 
154
165
  ```bash
155
166
  decantr workspace list
package/dist/bin.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-ZYHR3BGT.js";
2
+ import "./chunk-RKZMHS2K.js";
3
3
  import "./chunk-V3XAQWKD.js";
4
+ import "./chunk-VE6N3XWG.js";
4
5
  import "./chunk-KT2ROK2D.js";
@@ -1,3 +1,6 @@
1
+ import {
2
+ listWorkspaceAppCandidates
3
+ } from "./chunk-VE6N3XWG.js";
1
4
  import {
2
5
  createProjectHealthReport
3
6
  } from "./chunk-PAF4PBD3.js";
@@ -93,6 +96,14 @@ function listWorkspaceProjects(root = process.cwd()) {
93
96
  }
94
97
  return [...byPath.values()].sort((a, b) => a.path.localeCompare(b.path));
95
98
  }
99
+ function listWorkspaceCandidates(root = process.cwd(), projects = listWorkspaceProjects(root)) {
100
+ const attached = new Set(projects.map((project) => project.path));
101
+ return listWorkspaceAppCandidates(root).map((path) => ({
102
+ path,
103
+ attached: attached.has(path),
104
+ suggestedAdoptCommand: `decantr adopt --project ${path} --yes`
105
+ }));
106
+ }
96
107
  function changedPaths(root, since) {
97
108
  try {
98
109
  const output = execFileSync("git", ["diff", "--name-only", since, "--"], {
@@ -100,7 +111,9 @@ function changedPaths(root, since) {
100
111
  encoding: "utf-8",
101
112
  stdio: ["ignore", "pipe", "ignore"]
102
113
  });
103
- return new Set(output.split("\n").map((line) => line.trim()).filter(Boolean));
114
+ return new Set(
115
+ output.split("\n").map((line) => line.trim()).filter(Boolean)
116
+ );
104
117
  } catch {
105
118
  return /* @__PURE__ */ new Set();
106
119
  }
@@ -116,7 +129,10 @@ function projectChanged(project, changed) {
116
129
  async function withTimeout(promise, timeoutMs, label) {
117
130
  let timeout;
118
131
  const timer = new Promise((_, reject) => {
119
- timeout = setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)), timeoutMs);
132
+ timeout = setTimeout(
133
+ () => reject(new Error(`${label} timed out after ${timeoutMs}ms`)),
134
+ timeoutMs
135
+ );
120
136
  });
121
137
  try {
122
138
  return await Promise.race([promise, timer]);
@@ -265,9 +281,11 @@ function parseWorkspaceArgs(args) {
265
281
  else if (arg.startsWith("--since=")) options.since = arg.split("=")[1];
266
282
  else if (arg === "--output" && args[index + 1]) options.output = args[++index];
267
283
  else if (arg.startsWith("--output=")) options.output = arg.split("=")[1];
268
- else if (arg === "--fail-on" && args[index + 1]) options.failOn = parseHealthFailOn(args[++index]);
284
+ else if (arg === "--fail-on" && args[index + 1])
285
+ options.failOn = parseHealthFailOn(args[++index]);
269
286
  else if (arg.startsWith("--fail-on=")) options.failOn = parseHealthFailOn(arg.split("=")[1]);
270
- else if (arg === "--concurrency" && args[index + 1]) options.concurrency = Number(args[++index]);
287
+ else if (arg === "--concurrency" && args[index + 1])
288
+ options.concurrency = Number(args[++index]);
271
289
  else if (arg.startsWith("--concurrency=")) options.concurrency = Number(arg.split("=")[1]);
272
290
  else if (arg === "--timeout-ms" && args[index + 1]) options.timeoutMs = Number(args[++index]);
273
291
  else if (arg.startsWith("--timeout-ms=")) options.timeoutMs = Number(arg.split("=")[1]);
@@ -278,15 +296,36 @@ async function cmdWorkspace(workspaceRoot = process.cwd(), args = ["workspace"])
278
296
  const options = parseWorkspaceArgs(args);
279
297
  if (options.subcommand === "list") {
280
298
  const projects = listWorkspaceProjects(workspaceRoot);
281
- const payload2 = `${JSON.stringify({ projects }, null, 2)}
299
+ const candidates = listWorkspaceCandidates(workspaceRoot, projects);
300
+ const unattachedCandidates = candidates.filter((candidate) => !candidate.attached);
301
+ const payload2 = `${JSON.stringify({ projects, candidates }, null, 2)}
282
302
  `;
283
303
  if (options.json) {
284
304
  process.stdout.write(payload2);
285
305
  return;
286
306
  }
287
307
  console.log(`${BOLD}Decantr workspace projects${RESET}`);
288
- for (const project of projects) {
289
- console.log(`${project.path} ${DIM}${project.source}${RESET}`);
308
+ console.log("");
309
+ console.log("Attached Decantr projects:");
310
+ if (projects.length === 0) {
311
+ console.log(` ${DIM}(none yet)${RESET}`);
312
+ } else {
313
+ for (const project of projects) {
314
+ console.log(` ${project.path} ${DIM}${project.source}${RESET}`);
315
+ }
316
+ }
317
+ if (candidates.length > 0) {
318
+ console.log("");
319
+ console.log("App candidates:");
320
+ for (const candidate of candidates) {
321
+ const status = candidate.attached ? `${GREEN}attached${RESET}` : `${YELLOW}unattached${RESET}`;
322
+ console.log(` ${candidate.path} ${DIM}${status}${RESET}`);
323
+ }
324
+ }
325
+ if (unattachedCandidates.length > 0) {
326
+ console.log("");
327
+ console.log("Start by attaching one app:");
328
+ console.log(` ${unattachedCandidates[0].suggestedAdoptCommand}`);
290
329
  }
291
330
  return;
292
331
  }
@@ -296,7 +335,8 @@ async function cmdWorkspace(workspaceRoot = process.cwd(), args = ["workspace"])
296
335
  if (options.output) {
297
336
  mkdirSync(dirname(resolve(workspaceRoot, options.output)), { recursive: true });
298
337
  writeFileSync(resolve(workspaceRoot, options.output), payload, "utf-8");
299
- if (!options.ci) console.log(`${GREEN}Wrote Decantr workspace health:${RESET} ${options.output}`);
338
+ if (!options.ci)
339
+ console.log(`${GREEN}Wrote Decantr workspace health:${RESET} ${options.output}`);
300
340
  } else {
301
341
  process.stdout.write(payload);
302
342
  }
@@ -307,6 +347,7 @@ async function cmdWorkspace(workspaceRoot = process.cwd(), args = ["workspace"])
307
347
 
308
348
  export {
309
349
  listWorkspaceProjects,
350
+ listWorkspaceCandidates,
310
351
  createWorkspaceHealthReport,
311
352
  formatWorkspaceHealthText,
312
353
  formatWorkspaceHealthMarkdown,