@decantr/cli 2.1.2 → 2.3.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 CHANGED
@@ -81,6 +81,8 @@ decantr audit
81
81
  decantr check
82
82
  decantr health --ci --fail-on error
83
83
  decantr studio --port 4319 --host 127.0.0.1
84
+ decantr telemetry status
85
+ decantr telemetry link --enable --org <org-slug>
84
86
  decantr content-health --ci --fail-on error
85
87
  decantr registry summary --namespace @official --json
86
88
  decantr showcase verification --json
@@ -124,7 +126,20 @@ decantr health --json --output decantr-health.json
124
126
  decantr studio --report decantr-health.json
125
127
  ```
126
128
 
127
- If the project has explicitly enabled Decantr CLI telemetry, `new --telemetry`, `init --telemetry`, `check --telemetry`, `health`, and `studio` emit only aggregate product-activation metadata such as lifecycle command outcome, status, score, finding counts, CI failure outcome, Studio usage, and remediation prompt requests. They never upload the health report, finding evidence, local paths, route names, source code, or prompt text.
129
+ If the project has explicitly enabled Decantr CLI telemetry, `new --telemetry`, `init --telemetry`, `analyze`, `check --telemetry`, `health`, and `studio` emit only aggregate product-activation metadata such as lifecycle command outcome, analyze counts, status, score, finding counts, CI failure outcome, Studio usage, and remediation prompt requests. They never upload the health report, finding evidence, local paths, route names, source code, package names, or prompt text.
130
+
131
+ ## Opted-In Telemetry Identity
132
+
133
+ `decantr telemetry` lets users inspect and link the opaque install/project ids used by opted-in CLI telemetry. This is how customer org attribution becomes durable without collecting repository names, local paths, source code, prompts, private package slugs, emails, or secrets.
134
+
135
+ ```bash
136
+ decantr telemetry status
137
+ decantr telemetry status --json
138
+ decantr login --api-key=<key>
139
+ decantr telemetry link --enable --org <org-slug>
140
+ ```
141
+
142
+ `telemetry link` calls the hosted `/v1/me/telemetry-link` endpoint with only opaque ids, optional org slug, and optional label. The API verifies org membership, writes `telemetry_identity_aliases`, clears the actor-resolution cache, audit logs the change, and emits `telemetry.identity_linked`.
128
143
 
129
144
  ## Content Health
130
145
 
package/dist/bin.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-3SULF2JT.js";
2
+ import "./chunk-FSZ6OIAC.js";
3
3
  import "./chunk-WDA4SHIQ.js";
4
- import "./chunk-JYEEXSUX.js";
4
+ import "./chunk-IEW2QFYI.js";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  collectCheckIssues
3
- } from "./chunk-LLQCXOHK.js";
3
+ } from "./chunk-NBJCO4G5.js";
4
4
  import {
5
5
  sendProjectHealthCiFailedTelemetry,
6
6
  sendProjectHealthPromptTelemetry,
7
7
  sendProjectHealthReportTelemetry
8
- } from "./chunk-JYEEXSUX.js";
8
+ } from "./chunk-IEW2QFYI.js";
9
9
 
10
10
  // src/commands/health.ts
11
11
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
@@ -18,15 +18,18 @@ import {
18
18
  import {
19
19
  buildGuardRegistryContext,
20
20
  createDoctrineMap,
21
+ getCliTelemetryIdentityStatus,
22
+ isOptedIn,
21
23
  optIn,
22
24
  scanAmbientContext,
23
25
  scanProjectInteractions,
24
26
  scanRoutes,
25
27
  scanStyling,
28
+ sendAnalyzeCompletedTelemetry,
26
29
  sendCliCommandTelemetry,
27
30
  sendNewProjectCompletedTelemetry,
28
31
  writeDoctrineMap
29
- } from "./chunk-JYEEXSUX.js";
32
+ } from "./chunk-IEW2QFYI.js";
30
33
 
31
34
  // src/index.ts
32
35
  import { existsSync as existsSync26, mkdirSync as mkdirSync12, readdirSync as readdirSync6, readFileSync as readFileSync19, writeFileSync as writeFileSync15 } from "fs";
@@ -2125,7 +2128,8 @@ var RESET2 = "\x1B[0m";
2125
2128
  var GREEN2 = "\x1B[32m";
2126
2129
  var CYAN = "\x1B[36m";
2127
2130
  var YELLOW = "\x1B[33m";
2128
- function cmdAnalyze(projectRoot = process.cwd(), workspace) {
2131
+ async function cmdAnalyze(projectRoot = process.cwd(), workspace) {
2132
+ const startedAt = Date.now();
2129
2133
  console.log(`
2130
2134
  ${BOLD}Analyzing project...${RESET2}
2131
2135
  `);
@@ -2275,6 +2279,22 @@ ${DIM2}Written to:${RESET2} ${outputPath}`);
2275
2279
  ${YELLOW}Next step:${RESET2} Review ${BOLD}.decantr/brownfield-report.md${RESET2}, then run ${BOLD}decantr init --existing --accept-proposal${RESET2} to attach Decantr using the observed proposal.
2276
2280
  `
2277
2281
  );
2282
+ await sendAnalyzeCompletedTelemetry({
2283
+ componentCount: components.componentCount,
2284
+ dependencyCategoryCount: [
2285
+ dependencies.auth,
2286
+ dependencies.db,
2287
+ dependencies.state,
2288
+ dependencies.styling,
2289
+ dependencies.ui
2290
+ ].filter((items) => items.length > 0).length,
2291
+ durationMs: Date.now() - startedAt,
2292
+ pageCount: components.pageCount,
2293
+ projectRoot,
2294
+ routeCount: routes.routes.length,
2295
+ success: true,
2296
+ targetFramework: project.framework
2297
+ });
2278
2298
  }
2279
2299
 
2280
2300
  // src/commands/create.ts
@@ -2948,7 +2968,7 @@ async function cmdMagic(prompt, projectRoot, options) {
2948
2968
  console.log(
2949
2969
  dim(" Running brownfield analysis instead so you can attach Decantr deliberately.\n")
2950
2970
  );
2951
- cmdAnalyze(projectRoot);
2971
+ await cmdAnalyze(projectRoot);
2952
2972
  console.log(
2953
2973
  `${BOLD2}Recommended next step:${RESET4} ${cyan("decantr init --existing --accept-proposal")}`
2954
2974
  );
@@ -4163,28 +4183,162 @@ function resolveDriftEntries(projectRoot, options) {
4163
4183
  }
4164
4184
  }
4165
4185
 
4186
+ // src/commands/telemetry.ts
4187
+ var BOLD5 = "\x1B[1m";
4188
+ var CYAN6 = "\x1B[36m";
4189
+ var DIM11 = "\x1B[2m";
4190
+ var GREEN11 = "\x1B[32m";
4191
+ var RESET11 = "\x1B[0m";
4192
+ var DEFAULT_API_URL = "https://api.decantr.ai/v1";
4193
+ async function cmdTelemetry(args, projectRoot = process.cwd()) {
4194
+ const subcommand = args[0] ?? "status";
4195
+ const options = parseTelemetryOptions(args.slice(1));
4196
+ if (subcommand === "--help" || subcommand === "-h" || subcommand === "help") {
4197
+ printTelemetryHelp();
4198
+ return;
4199
+ }
4200
+ if (subcommand === "status") {
4201
+ printTelemetryStatus(projectRoot, options);
4202
+ return;
4203
+ }
4204
+ if (subcommand === "link") {
4205
+ await linkTelemetryIdentity(projectRoot, options);
4206
+ return;
4207
+ }
4208
+ throw new Error(`Unknown telemetry command: ${subcommand}`);
4209
+ }
4210
+ function printTelemetryHelp() {
4211
+ console.log(`
4212
+ ${BOLD5}decantr telemetry${RESET11} \u2014 Inspect and link privacy-filtered CLI telemetry identity
4213
+
4214
+ ${BOLD5}Usage:${RESET11}
4215
+ decantr telemetry status [--json]
4216
+ decantr telemetry link [--enable] [--org <slug>] [--label <label>] [--api-key <key>] [--api-url <url>]
4217
+
4218
+ ${BOLD5}Examples:${RESET11}
4219
+ decantr init --telemetry
4220
+ decantr telemetry status
4221
+ decantr login --api-key=<your-key>
4222
+ decantr telemetry link --org my-team --label "CI runner"
4223
+ `);
4224
+ }
4225
+ function printTelemetryStatus(projectRoot, options) {
4226
+ const status = getCliTelemetryIdentityStatus(projectRoot, { create: false });
4227
+ if (options.json) {
4228
+ console.log(JSON.stringify(status, null, 2));
4229
+ return;
4230
+ }
4231
+ console.log(`
4232
+ ${BOLD5}Decantr telemetry${RESET11}`);
4233
+ console.log(` Enabled: ${status.enabled ? `${GREEN11}yes${RESET11}` : "no"}`);
4234
+ console.log(` Project: ${status.hasProjectConfig ? status.projectRoot : "not initialized"}`);
4235
+ console.log(` Install ID: ${status.installId ?? `${DIM11}not created yet${RESET11}`}`);
4236
+ console.log(` Project ID: ${status.projectId ?? `${DIM11}not created yet${RESET11}`}`);
4237
+ if (status.enabled) {
4238
+ console.log(
4239
+ DIM11 + "Run `decantr telemetry link` after login to attach these opaque IDs to your Decantr account/org." + RESET11
4240
+ );
4241
+ } else {
4242
+ console.log(DIM11 + "Run `decantr init --telemetry` or `decantr telemetry link --enable` to opt in." + RESET11);
4243
+ }
4244
+ }
4245
+ async function linkTelemetryIdentity(projectRoot, options) {
4246
+ if (options.enable && !isOptedIn(projectRoot)) {
4247
+ optIn(projectRoot);
4248
+ }
4249
+ if (!isOptedIn(projectRoot)) {
4250
+ throw new Error("This project has not opted into telemetry. Re-run with --enable or use `decantr init --telemetry`.");
4251
+ }
4252
+ const identity = getCliTelemetryIdentityStatus(projectRoot, { create: true });
4253
+ if (!identity.installId && !identity.projectId) {
4254
+ throw new Error("No telemetry identity could be created for this project.");
4255
+ }
4256
+ const apiKey = options.apiKey ?? getApiKeyOrToken();
4257
+ if (!apiKey) {
4258
+ throw new Error("Run `decantr login --api-key=<key>` or pass `--api-key <key>` before linking telemetry.");
4259
+ }
4260
+ const apiUrl = trimTrailingSlashes(options.apiUrl ?? process.env.DECANTR_API_URL ?? DEFAULT_API_URL);
4261
+ const response = await fetch(`${apiUrl}/me/telemetry-link`, {
4262
+ method: "POST",
4263
+ headers: {
4264
+ Authorization: `Bearer ${apiKey}`,
4265
+ "Content-Type": "application/json"
4266
+ },
4267
+ body: JSON.stringify({
4268
+ install_id: identity.installId,
4269
+ project_id: identity.projectId,
4270
+ org_slug: options.org,
4271
+ label: options.label
4272
+ })
4273
+ });
4274
+ if (!response.ok) {
4275
+ const body2 = await response.json().catch(() => null);
4276
+ throw new Error(body2?.error ?? `Telemetry link failed with HTTP ${response.status}.`);
4277
+ }
4278
+ const body = await response.json().catch(() => ({ linked: 0 }));
4279
+ console.log(`${GREEN11}Telemetry identity linked.${RESET11}`);
4280
+ console.log(` Linked: ${body.linked ?? 0}`);
4281
+ console.log(` Install ID: ${identity.installId ?? `${DIM11}none${RESET11}`}`);
4282
+ console.log(` Project ID: ${identity.projectId ?? `${DIM11}none${RESET11}`}`);
4283
+ if (options.org) console.log(` Org: ${CYAN6}${options.org}${RESET11}`);
4284
+ console.log(DIM11 + "These opaque IDs now attribute opted-in CLI usage to your Decantr account/org." + RESET11);
4285
+ }
4286
+ function parseTelemetryOptions(args) {
4287
+ const options = {};
4288
+ for (let i = 0; i < args.length; i++) {
4289
+ const arg = args[i];
4290
+ if (arg === "--enable") {
4291
+ options.enable = true;
4292
+ } else if (arg === "--json") {
4293
+ options.json = true;
4294
+ } else if (arg === "--org" && args[i + 1]) {
4295
+ options.org = args[++i];
4296
+ } else if (arg.startsWith("--org=")) {
4297
+ options.org = arg.slice("--org=".length);
4298
+ } else if (arg === "--label" && args[i + 1]) {
4299
+ options.label = args[++i];
4300
+ } else if (arg.startsWith("--label=")) {
4301
+ options.label = arg.slice("--label=".length);
4302
+ } else if (arg === "--api-key" && args[i + 1]) {
4303
+ options.apiKey = args[++i];
4304
+ } else if (arg.startsWith("--api-key=")) {
4305
+ options.apiKey = arg.slice("--api-key=".length);
4306
+ } else if (arg === "--api-url" && args[i + 1]) {
4307
+ options.apiUrl = args[++i];
4308
+ } else if (arg.startsWith("--api-url=")) {
4309
+ options.apiUrl = arg.slice("--api-url=".length);
4310
+ }
4311
+ }
4312
+ return options;
4313
+ }
4314
+ function trimTrailingSlashes(value) {
4315
+ let end = value.length;
4316
+ while (end > 0 && value.charCodeAt(end - 1) === 47) end -= 1;
4317
+ return value.slice(0, end);
4318
+ }
4319
+
4166
4320
  // src/commands/theme-switch.ts
4167
4321
  import { existsSync as existsSync23, readFileSync as readFileSync16, writeFileSync as writeFileSync13 } from "fs";
4168
4322
  import { join as join24 } from "path";
4169
4323
  import { isV4 as isV46 } from "@decantr/essence-spec";
4170
- var GREEN11 = "\x1B[32m";
4324
+ var GREEN12 = "\x1B[32m";
4171
4325
  var RED10 = "\x1B[31m";
4172
4326
  var YELLOW7 = "\x1B[33m";
4173
- var DIM11 = "\x1B[2m";
4174
- var RESET11 = "\x1B[0m";
4327
+ var DIM12 = "\x1B[2m";
4328
+ var RESET12 = "\x1B[0m";
4175
4329
  var VALID_THEME_SHAPES = ["sharp", "rounded", "pill"];
4176
4330
  var VALID_THEME_MODES = ["light", "dark", "auto"];
4177
4331
  async function cmdThemeSwitch(themeName, args, projectRoot = process.cwd()) {
4178
4332
  if (!themeName) {
4179
4333
  console.error(
4180
- `${RED10}Usage: decantr theme switch <themeName> [--shape <s>] [--mode <m>]${RESET11}`
4334
+ `${RED10}Usage: decantr theme switch <themeName> [--shape <s>] [--mode <m>]${RESET12}`
4181
4335
  );
4182
4336
  process.exitCode = 1;
4183
4337
  return;
4184
4338
  }
4185
4339
  const essencePath = join24(projectRoot, "decantr.essence.json");
4186
4340
  if (!existsSync23(essencePath)) {
4187
- console.error(`${RED10}No decantr.essence.json found. Run \`decantr init\` first.${RESET11}`);
4341
+ console.error(`${RED10}No decantr.essence.json found. Run \`decantr init\` first.${RESET12}`);
4188
4342
  process.exitCode = 1;
4189
4343
  return;
4190
4344
  }
@@ -4192,13 +4346,13 @@ async function cmdThemeSwitch(themeName, args, projectRoot = process.cwd()) {
4192
4346
  try {
4193
4347
  parsed = JSON.parse(readFileSync16(essencePath, "utf-8"));
4194
4348
  } catch (e) {
4195
- console.error(`${RED10}Could not read essence: ${e.message}${RESET11}`);
4349
+ console.error(`${RED10}Could not read essence: ${e.message}${RESET12}`);
4196
4350
  process.exitCode = 1;
4197
4351
  return;
4198
4352
  }
4199
4353
  if (!isV46(parsed)) {
4200
4354
  console.error(
4201
- `${RED10}Active workflows require Essence v4.0.0. Run \`decantr migrate --to v4\` first.${RESET11}`
4355
+ `${RED10}Active workflows require Essence v4.0.0. Run \`decantr migrate --to v4\` first.${RESET12}`
4202
4356
  );
4203
4357
  process.exitCode = 1;
4204
4358
  return;
@@ -4220,14 +4374,14 @@ async function cmdThemeSwitch(themeName, args, projectRoot = process.cwd()) {
4220
4374
  }
4221
4375
  if (shape && !VALID_THEME_SHAPES.includes(shape)) {
4222
4376
  console.error(
4223
- `${RED10}Invalid shape "${shape}". Must be one of: ${VALID_THEME_SHAPES.join(", ")}.${RESET11}`
4377
+ `${RED10}Invalid shape "${shape}". Must be one of: ${VALID_THEME_SHAPES.join(", ")}.${RESET12}`
4224
4378
  );
4225
4379
  process.exitCode = 1;
4226
4380
  return;
4227
4381
  }
4228
4382
  if (mode && !VALID_THEME_MODES.includes(mode)) {
4229
4383
  console.error(
4230
- `${RED10}Invalid mode "${mode}". Must be one of: ${VALID_THEME_MODES.join(", ")}.${RESET11}`
4384
+ `${RED10}Invalid mode "${mode}". Must be one of: ${VALID_THEME_MODES.join(", ")}.${RESET12}`
4231
4385
  );
4232
4386
  process.exitCode = 1;
4233
4387
  return;
@@ -4257,27 +4411,27 @@ async function cmdThemeSwitch(themeName, args, projectRoot = process.cwd()) {
4257
4411
  } catch {
4258
4412
  }
4259
4413
  writeFileSync13(essencePath, JSON.stringify(essence, null, 2) + "\n");
4260
- console.log(`${GREEN11}Switched theme: ${oldThemeId} \u2192 ${themeName}${RESET11}`);
4261
- if (shape) console.log(` ${DIM11}Shape: ${shape}${RESET11}`);
4262
- if (mode) console.log(` ${DIM11}Mode: ${mode}${RESET11}`);
4414
+ console.log(`${GREEN12}Switched theme: ${oldThemeId} \u2192 ${themeName}${RESET12}`);
4415
+ if (shape) console.log(` ${DIM12}Shape: ${shape}${RESET12}`);
4416
+ if (mode) console.log(` ${DIM12}Mode: ${mode}${RESET12}`);
4263
4417
  await refreshDerivedFiles(projectRoot, essence, registryClient);
4264
4418
  console.log(
4265
- `${GREEN11}Derived files refreshed (tokens.css, treatments.css, all contexts).${RESET11}`
4419
+ `${GREEN12}Derived files refreshed (tokens.css, treatments.css, all contexts).${RESET12}`
4266
4420
  );
4267
- console.log(`${YELLOW7}Guard will flag code using old tokens. Run \`decantr check\`.${RESET11}`);
4421
+ console.log(`${YELLOW7}Guard will flag code using old tokens. Run \`decantr check\`.${RESET12}`);
4268
4422
  }
4269
4423
 
4270
4424
  // src/prompts.ts
4271
4425
  import { createInterface } from "readline";
4272
- var BOLD5 = "\x1B[1m";
4273
- var DIM12 = "\x1B[2m";
4274
- var RESET12 = "\x1B[0m";
4275
- var GREEN12 = "\x1B[32m";
4426
+ var BOLD6 = "\x1B[1m";
4427
+ var DIM13 = "\x1B[2m";
4428
+ var RESET13 = "\x1B[0m";
4429
+ var GREEN13 = "\x1B[32m";
4276
4430
  var YELLOW8 = "\x1B[33m";
4277
- var CYAN6 = "\x1B[36m";
4431
+ var CYAN7 = "\x1B[36m";
4278
4432
  function ask(question, defaultValue) {
4279
4433
  const rl = createInterface({ input: process.stdin, output: process.stdout });
4280
- const prompt = defaultValue ? `${question} ${DIM12}(${defaultValue})${RESET12}: ` : `${question}: `;
4434
+ const prompt = defaultValue ? `${question} ${DIM13}(${defaultValue})${RESET13}: ` : `${question}: `;
4281
4435
  return new Promise((resolve5) => {
4282
4436
  rl.question(prompt, (answer) => {
4283
4437
  rl.close();
@@ -4287,14 +4441,14 @@ function ask(question, defaultValue) {
4287
4441
  }
4288
4442
  async function select(question, options, defaultIdx = 0, allowOther = false) {
4289
4443
  console.log(`
4290
- ${BOLD5}${question}${RESET12}`);
4444
+ ${BOLD6}${question}${RESET13}`);
4291
4445
  for (let i = 0; i < options.length; i++) {
4292
- const marker = i === defaultIdx ? `${GREEN12}>${RESET12}` : " ";
4293
- const desc = options[i].description ? ` ${DIM12}\u2014 ${options[i].description}${RESET12}` : "";
4446
+ const marker = i === defaultIdx ? `${GREEN13}>${RESET13}` : " ";
4447
+ const desc = options[i].description ? ` ${DIM13}\u2014 ${options[i].description}${RESET13}` : "";
4294
4448
  console.log(` ${marker} ${i + 1}. ${options[i].label}${desc}`);
4295
4449
  }
4296
4450
  if (allowOther) {
4297
- console.log(` ${options.length + 1}. ${DIM12}other (enter custom value)${RESET12}`);
4451
+ console.log(` ${options.length + 1}. ${DIM13}other (enter custom value)${RESET13}`);
4298
4452
  }
4299
4453
  const maxIdx = allowOther ? options.length + 1 : options.length;
4300
4454
  const answer = await ask(`Choose (1-${maxIdx})`, String(defaultIdx + 1));
@@ -4313,11 +4467,11 @@ async function confirm(question, defaultYes = true) {
4313
4467
  }
4314
4468
  function warn(message) {
4315
4469
  console.log(`
4316
- ${YELLOW8} Warning: ${message}${RESET12}`);
4470
+ ${YELLOW8} Warning: ${message}${RESET13}`);
4317
4471
  }
4318
4472
  function showDetection(detected) {
4319
4473
  console.log(`
4320
- ${CYAN6}Detected project configuration:${RESET12}`);
4474
+ ${CYAN7}Detected project configuration:${RESET13}`);
4321
4475
  if (detected.framework !== "unknown") {
4322
4476
  const version = detected.version ? ` ${detected.version}` : "";
4323
4477
  console.log(` Framework: ${detected.framework}${version}`);
@@ -4326,13 +4480,13 @@ ${CYAN6}Detected project configuration:${RESET12}`);
4326
4480
  console.log(` Package manager: ${detected.packageManager}`);
4327
4481
  }
4328
4482
  if (detected.hasTypeScript) {
4329
- console.log(` TypeScript: ${GREEN12}yes${RESET12}`);
4483
+ console.log(` TypeScript: ${GREEN13}yes${RESET13}`);
4330
4484
  }
4331
4485
  if (detected.hasTailwind) {
4332
- console.log(` Tailwind CSS: ${GREEN12}yes${RESET12}`);
4486
+ console.log(` Tailwind CSS: ${GREEN13}yes${RESET13}`);
4333
4487
  }
4334
4488
  if (detected.existingEssence) {
4335
- console.log(` Existing essence: ${YELLOW8}yes${RESET12}`);
4489
+ console.log(` Existing essence: ${YELLOW8}yes${RESET13}`);
4336
4490
  }
4337
4491
  }
4338
4492
  async function runInteractivePrompts(detected, archetypes, blueprints, themes, workflowSeed) {
@@ -4406,7 +4560,7 @@ async function runInteractivePrompts(detected, archetypes, blueprints, themes, w
4406
4560
  warn(`This project appears to be ${detected.framework} but you selected ${target}.`);
4407
4561
  const proceed = await confirm("Continue anyway?", false);
4408
4562
  if (!proceed) {
4409
- console.log(`${DIM12}Using detected framework: ${detected.framework}${RESET12}`);
4563
+ console.log(`${DIM13}Using detected framework: ${detected.framework}${RESET13}`);
4410
4564
  }
4411
4565
  }
4412
4566
  const guardMode = await select(
@@ -4797,29 +4951,29 @@ function resolveWorkspaceInfo(cwd, projectArg) {
4797
4951
  }
4798
4952
 
4799
4953
  // src/index.ts
4800
- var BOLD6 = "\x1B[1m";
4801
- var DIM13 = "\x1B[2m";
4802
- var RESET13 = "\x1B[0m";
4954
+ var BOLD7 = "\x1B[1m";
4955
+ var DIM14 = "\x1B[2m";
4956
+ var RESET14 = "\x1B[0m";
4803
4957
  var RED11 = "\x1B[31m";
4804
- var GREEN13 = "\x1B[32m";
4805
- var CYAN7 = "\x1B[36m";
4958
+ var GREEN14 = "\x1B[32m";
4959
+ var CYAN8 = "\x1B[36m";
4806
4960
  var YELLOW9 = "\x1B[33m";
4807
4961
  function heading2(text) {
4808
4962
  return `
4809
- ${BOLD6}${text}${RESET13}
4963
+ ${BOLD7}${text}${RESET14}
4810
4964
  `;
4811
4965
  }
4812
4966
  function success3(text) {
4813
- return `${GREEN13}${text}${RESET13}`;
4967
+ return `${GREEN14}${text}${RESET14}`;
4814
4968
  }
4815
4969
  function error3(text) {
4816
- return `${RED11}${text}${RESET13}`;
4970
+ return `${RED11}${text}${RESET14}`;
4817
4971
  }
4818
4972
  function dim3(text) {
4819
- return `${DIM13}${text}${RESET13}`;
4973
+ return `${DIM14}${text}${RESET14}`;
4820
4974
  }
4821
4975
  function cyan3(text) {
4822
- return `${CYAN7}${text}${RESET13}`;
4976
+ return `${CYAN8}${text}${RESET14}`;
4823
4977
  }
4824
4978
  function formatIntelligenceSummary(intelligence) {
4825
4979
  if (!intelligence) {
@@ -5542,7 +5696,7 @@ async function printHostedExecutionPackBundle(essencePath, namespace, jsonOutput
5542
5696
  console.log(` Files written: ${writtenContextPaths.length}`);
5543
5697
  }
5544
5698
  console.log("");
5545
- console.log(`${BOLD6}Route Plan:${RESET13}`);
5699
+ console.log(`${BOLD7}Route Plan:${RESET14}`);
5546
5700
  for (const route of typedBundle.scaffold.data.routes) {
5547
5701
  const patterns = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
5548
5702
  const pageLabel = route.sectionId ? `${route.sectionId}/${route.pageId}` : route.pageId;
@@ -5781,7 +5935,7 @@ async function cmdSearch(query, type, sort, recommended, intelligenceSource) {
5781
5935
  }
5782
5936
  console.log(heading2(`${results.length} result(s) for "${query}"`));
5783
5937
  for (const r of results) {
5784
- console.log(` ${cyan3(r.type.padEnd(12))} ${BOLD6}${r.slug}${RESET13}`);
5938
+ console.log(` ${cyan3(r.type.padEnd(12))} ${BOLD7}${r.slug}${RESET14}`);
5785
5939
  console.log(` ${dim3(r.description || "")}`);
5786
5940
  const intelligenceSummary = formatIntelligenceSummary(r.intelligence);
5787
5941
  if (intelligenceSummary) {
@@ -5812,14 +5966,14 @@ async function cmdSuggest(query, type) {
5812
5966
  const exact = results.filter((r) => r.slug.toLowerCase().includes(queryLower));
5813
5967
  const related = results.filter((r) => !r.slug.toLowerCase().includes(queryLower));
5814
5968
  if (exact.length > 0) {
5815
- console.log(`${BOLD6}Direct matches:${RESET13}`);
5969
+ console.log(`${BOLD7}Direct matches:${RESET14}`);
5816
5970
  for (const r of exact.slice(0, 3)) {
5817
5971
  console.log(` ${cyan3(r.slug)} - ${r.description || ""}`);
5818
5972
  }
5819
5973
  console.log("");
5820
5974
  }
5821
5975
  if (related.length > 0) {
5822
- console.log(`${BOLD6}Related:${RESET13}`);
5976
+ console.log(`${BOLD7}Related:${RESET14}`);
5823
5977
  for (const r of related.slice(0, 5)) {
5824
5978
  console.log(` ${cyan3(r.slug)} - ${r.description || ""}`);
5825
5979
  }
@@ -5883,14 +6037,14 @@ async function cmdValidate(path) {
5883
6037
  return;
5884
6038
  }
5885
6039
  const detectedVersion = isV47(essence) ? "v4" : "legacy";
5886
- console.log(`${DIM13}Detected essence version: ${detectedVersion}${RESET13}`);
6040
+ console.log(`${DIM14}Detected essence version: ${detectedVersion}${RESET14}`);
5887
6041
  const result = validateEssence2(essence);
5888
6042
  if (result.valid) {
5889
6043
  console.log(success3(`Essence is valid (${detectedVersion}).`));
5890
6044
  } else {
5891
6045
  console.error(error3("Validation failed:"));
5892
6046
  for (const err of result.errors) {
5893
- console.error(` ${RED11}${err}${RESET13}`);
6047
+ console.error(` ${RED11}${err}${RESET14}`);
5894
6048
  }
5895
6049
  process.exitCode = 1;
5896
6050
  return;
@@ -5911,9 +6065,9 @@ async function cmdValidate(path) {
5911
6065
  console.log(heading2("Guard violations:"));
5912
6066
  for (const v of violations) {
5913
6067
  const vr = v;
5914
- console.log(` ${YELLOW9}[${vr.rule}]${RESET13} ${vr.message}`);
6068
+ console.log(` ${YELLOW9}[${vr.rule}]${RESET14} ${vr.message}`);
5915
6069
  if (vr.suggestion) {
5916
- console.log(` ${DIM13}Suggestion: ${vr.suggestion}${RESET13}`);
6070
+ console.log(` ${DIM14}Suggestion: ${vr.suggestion}${RESET14}`);
5917
6071
  }
5918
6072
  }
5919
6073
  const hasError = violations.some((v) => v.severity === "error");
@@ -5985,9 +6139,9 @@ function enableCliTelemetry(projectRoot) {
5985
6139
  optIn(projectRoot);
5986
6140
  console.log(
5987
6141
  `
5988
- ${CYAN7}Telemetry enabled.${RESET13} Decantr will send privacy-filtered CLI product telemetry for this project.`
6142
+ ${CYAN8}Telemetry enabled.${RESET14} Decantr will send privacy-filtered CLI product telemetry for this project.`
5989
6143
  );
5990
- console.log(`${DIM13}Set "telemetry": false in .decantr/project.json to opt out.${RESET13}`);
6144
+ console.log(`${DIM14}Set "telemetry": false in .decantr/project.json to opt out.${RESET14}`);
5991
6145
  }
5992
6146
  function readCliPackageVersion() {
5993
6147
  const here = dirname4(fileURLToPath2(import.meta.url));
@@ -6109,7 +6263,7 @@ async function applyAcceptedBrownfieldProposal(input) {
6109
6263
  error3("Brownfield proposal produced an invalid Decantr essence. No files were changed.")
6110
6264
  );
6111
6265
  for (const validationError of validation.errors) {
6112
- console.log(` ${RED11}${validationError}${RESET13}`);
6266
+ console.log(` ${RED11}${validationError}${RESET14}`);
6113
6267
  }
6114
6268
  process.exitCode = 1;
6115
6269
  return;
@@ -6200,7 +6354,7 @@ async function cmdInit(args) {
6200
6354
  console.log(dim3(" Found .decantr/init-seed.json brownfield guidance."));
6201
6355
  }
6202
6356
  if (detected.existingEssence && !args.existing) {
6203
- console.log(`${YELLOW9}Warning: decantr.essence.json already exists.${RESET13}`);
6357
+ console.log(`${YELLOW9}Warning: decantr.essence.json already exists.${RESET14}`);
6204
6358
  const overwrite = await confirm("Overwrite existing configuration?", false);
6205
6359
  if (!overwrite) {
6206
6360
  console.log(dim3("Cancelled."));
@@ -6302,7 +6456,7 @@ async function cmdInit(args) {
6302
6456
  } else if (shouldUseRegistry && !apiAvailable) {
6303
6457
  if (!args.blueprint) {
6304
6458
  console.log(`
6305
- ${YELLOW9}You're offline. Scaffolding minimal Decantr project.${RESET13}`);
6459
+ ${YELLOW9}You're offline. Scaffolding minimal Decantr project.${RESET14}`);
6306
6460
  console.log(
6307
6461
  dim3("Run `decantr sync` or `decantr upgrade` when online to pull full registry content.\n")
6308
6462
  );
@@ -6350,7 +6504,7 @@ ${YELLOW9}You're offline. Scaffolding minimal Decantr project.${RESET13}`);
6350
6504
  return;
6351
6505
  }
6352
6506
  console.log(`
6353
- ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET13}`);
6507
+ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET14}`);
6354
6508
  console.log(dim3("Run `decantr upgrade` when online, or visit decantr.ai/registry\n"));
6355
6509
  selectedBlueprint = "default";
6356
6510
  } else if (shouldUseRegistry) {
@@ -6531,7 +6685,7 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET13}`);
6531
6685
  return;
6532
6686
  }
6533
6687
  console.log(
6534
- `${YELLOW9} Warning: Could not fetch blueprint "${options.blueprint}". Using defaults.${RESET13}`
6688
+ `${YELLOW9} Warning: Could not fetch blueprint "${options.blueprint}". Using defaults.${RESET14}`
6535
6689
  );
6536
6690
  }
6537
6691
  } else if (shouldUseRegistry && options.archetype) {
@@ -6546,7 +6700,7 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET13}`);
6546
6700
  return;
6547
6701
  }
6548
6702
  console.log(
6549
- `${YELLOW9} Warning: Could not fetch archetype "${options.archetype}". Using defaults.${RESET13}`
6703
+ `${YELLOW9} Warning: Could not fetch archetype "${options.archetype}". Using defaults.${RESET14}`
6550
6704
  );
6551
6705
  }
6552
6706
  }
@@ -6563,7 +6717,7 @@ ${YELLOW9}You're offline. Scaffolding Decantr default.${RESET13}`);
6563
6717
  return;
6564
6718
  }
6565
6719
  console.log(
6566
- `${YELLOW9} Warning: Could not fetch theme "${options.theme}". Using defaults.${RESET13}`
6720
+ `${YELLOW9} Warning: Could not fetch theme "${options.theme}". Using defaults.${RESET14}`
6567
6721
  );
6568
6722
  }
6569
6723
  }
@@ -6689,7 +6843,7 @@ Validation warnings: ${validation.errors.join(", ")}`));
6689
6843
  };
6690
6844
  const curatedPrompt = generateCuratedPrompt(promptCtx);
6691
6845
  console.log("");
6692
- console.log(`${BOLD6}Prompt for your AI assistant:${RESET13}`);
6846
+ console.log(`${BOLD7}Prompt for your AI assistant:${RESET14}`);
6693
6847
  console.log(dim3("\u2500".repeat(50)));
6694
6848
  console.log("");
6695
6849
  console.log(curatedPrompt);
@@ -6706,7 +6860,7 @@ async function cmdStatus() {
6706
6860
  const projectJsonPath = join27(projectRoot, ".decantr", "project.json");
6707
6861
  console.log(heading2("Decantr Project Status"));
6708
6862
  if (!existsSync26(essencePath)) {
6709
- console.log(`${RED11}No decantr.essence.json found.${RESET13}`);
6863
+ console.log(`${RED11}No decantr.essence.json found.${RESET14}`);
6710
6864
  console.log(dim3('Run "decantr init" to create one.'));
6711
6865
  return;
6712
6866
  }
@@ -6714,11 +6868,11 @@ async function cmdStatus() {
6714
6868
  const essence = JSON.parse(readFileSync19(essencePath, "utf-8"));
6715
6869
  const validation = validateEssence2(essence);
6716
6870
  const essenceVersion = isV47(essence) ? "v4" : "legacy";
6717
- console.log(`${BOLD6}Essence:${RESET13}`);
6871
+ console.log(`${BOLD7}Essence:${RESET14}`);
6718
6872
  if (validation.valid) {
6719
- console.log(` ${GREEN13}Valid${RESET13} (${essenceVersion})`);
6873
+ console.log(` ${GREEN14}Valid${RESET14} (${essenceVersion})`);
6720
6874
  } else {
6721
- console.log(` ${RED11}Invalid: ${validation.errors.join(", ")}${RESET13}`);
6875
+ console.log(` ${RED11}Invalid: ${validation.errors.join(", ")}${RESET14}`);
6722
6876
  }
6723
6877
  if (isV47(essence)) {
6724
6878
  const v4 = essence;
@@ -6726,7 +6880,7 @@ async function cmdStatus() {
6726
6880
  const flatPages = sections.flatMap((section) => section.pages ?? []);
6727
6881
  const resolvedShell = sections.find((section) => section.role === "primary")?.shell || sections[0]?.shell || v4.blueprint.shell || "unknown";
6728
6882
  const resolvedFeatures = v4.blueprint.features ?? [];
6729
- console.log(` ${BOLD6}DNA:${RESET13}`);
6883
+ console.log(` ${BOLD7}DNA:${RESET14}`);
6730
6884
  console.log(` Theme: ${v4.dna.theme.id} (${v4.dna.theme.mode})`);
6731
6885
  console.log(
6732
6886
  ` Spacing: ${v4.dna.spacing.density} density, ${v4.dna.spacing.content_gap} gap`
@@ -6738,42 +6892,42 @@ async function cmdStatus() {
6738
6892
  );
6739
6893
  console.log(` Accessibility: WCAG ${v4.dna.accessibility.wcag_level}`);
6740
6894
  console.log(` Personality: ${v4.dna.personality.join(", ")}`);
6741
- console.log(` ${BOLD6}Blueprint:${RESET13}`);
6895
+ console.log(` ${BOLD7}Blueprint:${RESET14}`);
6742
6896
  console.log(` Shell: ${resolvedShell}`);
6743
6897
  console.log(` Pages: ${flatPages.length}`);
6744
6898
  console.log(` Sections: ${sections.length}`);
6745
6899
  console.log(
6746
6900
  ` Features: ${resolvedFeatures.length > 0 ? resolvedFeatures.join(", ") : "none"}`
6747
6901
  );
6748
- console.log(` ${BOLD6}Meta:${RESET13}`);
6902
+ console.log(` ${BOLD7}Meta:${RESET14}`);
6749
6903
  console.log(` Archetype: ${v4.meta.archetype}`);
6750
6904
  console.log(` Target: ${v4.meta.target}`);
6751
6905
  console.log(
6752
6906
  ` Guard: ${v4.meta.guard.mode} (DNA: ${v4.meta.guard.dna_enforcement}, Blueprint: ${v4.meta.guard.blueprint_enforcement})`
6753
6907
  );
6754
6908
  } else {
6755
- console.log(` ${YELLOW9}Run \`decantr migrate --to v4\` to upgrade this project.${RESET13}`);
6909
+ console.log(` ${YELLOW9}Run \`decantr migrate --to v4\` to upgrade this project.${RESET14}`);
6756
6910
  }
6757
6911
  } catch (e) {
6758
- console.log(` ${RED11}Error reading essence: ${e.message}${RESET13}`);
6912
+ console.log(` ${RED11}Error reading essence: ${e.message}${RESET14}`);
6759
6913
  }
6760
6914
  console.log("");
6761
- console.log(`${BOLD6}Sync Status:${RESET13}`);
6915
+ console.log(`${BOLD7}Sync Status:${RESET14}`);
6762
6916
  if (existsSync26(projectJsonPath)) {
6763
6917
  try {
6764
6918
  const projectJson = JSON.parse(readFileSync19(projectJsonPath, "utf-8"));
6765
6919
  const syncStatus = projectJson.sync?.status || "unknown";
6766
6920
  const lastSync = projectJson.sync?.lastSync || "never";
6767
6921
  const source = projectJson.sync?.registrySource || "unknown";
6768
- const statusColor = syncStatus === "synced" ? GREEN13 : YELLOW9;
6769
- console.log(` Status: ${statusColor}${syncStatus}${RESET13}`);
6922
+ const statusColor = syncStatus === "synced" ? GREEN14 : YELLOW9;
6923
+ console.log(` Status: ${statusColor}${syncStatus}${RESET14}`);
6770
6924
  console.log(` Last sync: ${dim3(lastSync)}`);
6771
6925
  console.log(` Source: ${dim3(source)}`);
6772
6926
  } catch {
6773
- console.log(` ${YELLOW9}Could not read project.json${RESET13}`);
6927
+ console.log(` ${YELLOW9}Could not read project.json${RESET14}`);
6774
6928
  }
6775
6929
  } else {
6776
- console.log(` ${YELLOW9}No .decantr/project.json found${RESET13}`);
6930
+ console.log(` ${YELLOW9}No .decantr/project.json found${RESET14}`);
6777
6931
  console.log(dim3(' Run "decantr init" to create project files.'));
6778
6932
  }
6779
6933
  }
@@ -6786,12 +6940,12 @@ async function cmdSync() {
6786
6940
  console.log(success3("Sync completed successfully."));
6787
6941
  console.log(` Synced: ${result.synced.join(", ")}`);
6788
6942
  if (result.failed.length > 0) {
6789
- console.log(` ${YELLOW9}Failed: ${result.failed.join(", ")}${RESET13}`);
6943
+ console.log(` ${YELLOW9}Failed: ${result.failed.join(", ")}${RESET14}`);
6790
6944
  }
6791
6945
  } else {
6792
- console.log(`${YELLOW9}Could not sync: API unavailable${RESET13}`);
6946
+ console.log(`${YELLOW9}Could not sync: API unavailable${RESET14}`);
6793
6947
  if (result.failed.length > 0) {
6794
- console.log(` ${YELLOW9}Failed: ${result.failed.join(", ")}${RESET13}`);
6948
+ console.log(` ${YELLOW9}Failed: ${result.failed.join(", ")}${RESET14}`);
6795
6949
  }
6796
6950
  }
6797
6951
  }
@@ -6801,15 +6955,15 @@ function printVerificationFindings(findings) {
6801
6955
  return;
6802
6956
  }
6803
6957
  for (const finding of findings) {
6804
- const color = finding.severity === "error" ? RED11 : finding.severity === "warn" ? YELLOW9 : CYAN7;
6958
+ const color = finding.severity === "error" ? RED11 : finding.severity === "warn" ? YELLOW9 : CYAN8;
6805
6959
  console.log(
6806
- ` ${color}[${finding.severity.toUpperCase()}]${RESET13} ${finding.category}: ${finding.message}`
6960
+ ` ${color}[${finding.severity.toUpperCase()}]${RESET14} ${finding.category}: ${finding.message}`
6807
6961
  );
6808
6962
  for (const evidence of finding.evidence) {
6809
- console.log(` ${DIM13}${evidence}${RESET13}`);
6963
+ console.log(` ${DIM14}${evidence}${RESET14}`);
6810
6964
  }
6811
6965
  if (finding.suggestedFix) {
6812
- console.log(` ${DIM13}Fix: ${finding.suggestedFix}${RESET13}`);
6966
+ console.log(` ${DIM14}Fix: ${finding.suggestedFix}${RESET14}`);
6813
6967
  }
6814
6968
  }
6815
6969
  }
@@ -6817,10 +6971,10 @@ function printProjectAuditReport(report) {
6817
6971
  if (report.valid) {
6818
6972
  console.log(success3("Project contract is valid."));
6819
6973
  } else {
6820
- console.log(`${RED11}Project audit found blocking issues.${RESET13}`);
6974
+ console.log(`${RED11}Project audit found blocking issues.${RESET14}`);
6821
6975
  }
6822
6976
  console.log("");
6823
- console.log(`${BOLD6}Summary:${RESET13}`);
6977
+ console.log(`${BOLD7}Summary:${RESET14}`);
6824
6978
  console.log(` Essence version: ${report.summary.essenceVersion ?? "missing"}`);
6825
6979
  console.log(` Pages defined: ${report.summary.pageCount}`);
6826
6980
  console.log(` Pack manifest: ${report.summary.packManifestPresent ? "present" : "missing"}`);
@@ -6849,23 +7003,23 @@ function printProjectAuditReport(report) {
6849
7003
  ` Findings: ${report.summary.errorCount} error(s), ${report.summary.warnCount} warn(s), ${report.summary.infoCount} info`
6850
7004
  );
6851
7005
  console.log("");
6852
- console.log(`${BOLD6}Findings:${RESET13}`);
7006
+ console.log(`${BOLD7}Findings:${RESET14}`);
6853
7007
  printVerificationFindings(report.findings);
6854
7008
  }
6855
7009
  function printFileCritiqueReport(report) {
6856
7010
  console.log(success3(`Critiqued ${report.file}`));
6857
7011
  console.log("");
6858
- console.log(`${BOLD6}Summary:${RESET13}`);
7012
+ console.log(`${BOLD7}Summary:${RESET14}`);
6859
7013
  console.log(` Overall score: ${report.overall}/5`);
6860
7014
  console.log(` Focus areas: ${report.focusAreas.join(", ")}`);
6861
7015
  console.log(` Review pack: ${report.reviewPack ? "present" : "missing"}`);
6862
7016
  console.log("");
6863
- console.log(`${BOLD6}Scores:${RESET13}`);
7017
+ console.log(`${BOLD7}Scores:${RESET14}`);
6864
7018
  for (const score of report.scores) {
6865
7019
  console.log(` ${cyan3(score.category.padEnd(20))} ${score.score}/5 ${dim3(score.details)}`);
6866
7020
  }
6867
7021
  console.log("");
6868
- console.log(`${BOLD6}Findings:${RESET13}`);
7022
+ console.log(`${BOLD7}Findings:${RESET14}`);
6869
7023
  printVerificationFindings(report.findings);
6870
7024
  }
6871
7025
  async function cmdAudit(filePath) {
@@ -6906,7 +7060,7 @@ async function cmdAudit(filePath) {
6906
7060
  console.log(dim3("Project audit completed with advisory findings."));
6907
7061
  }
6908
7062
  } catch (e) {
6909
- console.log(`${RED11}Error: ${e.message}${RESET13}`);
7063
+ console.log(`${RED11}Error: ${e.message}${RESET14}`);
6910
7064
  process.exitCode = 1;
6911
7065
  }
6912
7066
  }
@@ -6915,9 +7069,9 @@ async function cmdTheme(args) {
6915
7069
  const projectRoot = process.cwd();
6916
7070
  if (!subcommand || subcommand === "help") {
6917
7071
  console.log(`
6918
- ${BOLD6}decantr theme${RESET13} \u2014 Manage custom themes
7072
+ ${BOLD7}decantr theme${RESET14} \u2014 Manage custom themes
6919
7073
 
6920
- ${BOLD6}Commands:${RESET13}
7074
+ ${BOLD7}Commands:${RESET14}
6921
7075
  ${cyan3("create")} <name> Create a new custom theme
6922
7076
  ${cyan3("create")} <name> --guided Interactive theme creation
6923
7077
  ${cyan3("list")} List custom themes
@@ -6925,7 +7079,7 @@ ${BOLD6}Commands:${RESET13}
6925
7079
  ${cyan3("delete")} <name> Delete a custom theme
6926
7080
  ${cyan3("import")} <path> Import theme from JSON file
6927
7081
 
6928
- ${BOLD6}Examples:${RESET13}
7082
+ ${BOLD7}Examples:${RESET14}
6929
7083
  decantr theme create mytheme
6930
7084
  decantr theme list
6931
7085
  decantr theme validate mytheme
@@ -6988,7 +7142,7 @@ ${BOLD6}Examples:${RESET13}
6988
7142
  } else {
6989
7143
  console.error(error3("Validation failed:"));
6990
7144
  for (const err of result.errors) {
6991
- console.error(` ${RED11}${err}${RESET13}`);
7145
+ console.error(` ${RED11}${err}${RESET14}`);
6992
7146
  }
6993
7147
  process.exitCode = 1;
6994
7148
  }
@@ -7028,7 +7182,7 @@ ${BOLD6}Examples:${RESET13}
7028
7182
  } else {
7029
7183
  console.error(error3("Import failed:"));
7030
7184
  for (const err of result.errors || []) {
7031
- console.error(` ${RED11}${err}${RESET13}`);
7185
+ console.error(` ${RED11}${err}${RESET14}`);
7032
7186
  }
7033
7187
  process.exitCode = 1;
7034
7188
  }
@@ -7051,9 +7205,9 @@ ${BOLD6}Examples:${RESET13}
7051
7205
  }
7052
7206
  function cmdHelp() {
7053
7207
  console.log(`
7054
- ${BOLD6}decantr${RESET13} \u2014 Design intelligence for AI-generated UI
7208
+ ${BOLD7}decantr${RESET14} \u2014 Design intelligence for AI-generated UI
7055
7209
 
7056
- ${BOLD6}Usage:${RESET13}
7210
+ ${BOLD7}Usage:${RESET14}
7057
7211
  decantr new <name> [--blueprint=X] [--archetype=X] [--theme=X] [--workflow=greenfield] [--adoption=decantr-css] [--telemetry]
7058
7212
  decantr magic <prompt> [--dry-run]
7059
7213
  decantr init [options]
@@ -7078,6 +7232,8 @@ ${BOLD6}Usage:${RESET13}
7078
7232
  decantr health init-ci [--force] [--project <path>] [--fail-on <error|warn|none>] [--cli-version <version|latest>]
7079
7233
  decantr content-health [--json] [--markdown] [--ci]
7080
7234
  decantr studio [--port 4319] [--host 127.0.0.1] [--report decantr-health.json]
7235
+ decantr telemetry status [--json]
7236
+ decantr telemetry link [--enable] [--org <slug>]
7081
7237
  decantr rules preview [--project=<path>]
7082
7238
  decantr rules apply [--project=<path>]
7083
7239
  decantr validate [path]
@@ -7089,7 +7245,7 @@ ${BOLD6}Usage:${RESET13}
7089
7245
  decantr logout
7090
7246
  decantr help
7091
7247
 
7092
- ${BOLD6}Init Options:${RESET13}
7248
+ ${BOLD7}Init Options:${RESET14}
7093
7249
  --blueprint, -b Blueprint ID
7094
7250
  --theme Theme ID
7095
7251
  --mode Color mode: dark | light | auto
@@ -7111,7 +7267,7 @@ ${BOLD6}Init Options:${RESET13}
7111
7267
  --registry Custom registry URL
7112
7268
  --telemetry Opt this project into privacy-filtered CLI product telemetry
7113
7269
 
7114
- ${BOLD6}Commands:${RESET13}
7270
+ ${BOLD7}Commands:${RESET14}
7115
7271
  ${cyan3("new")} Create a new greenfield workspace and bootstrap the available starter adapter
7116
7272
  ${cyan3("magic")} Greenfield-first intent flow; steers existing apps into analyze + init
7117
7273
  ${cyan3("init")} Attach Decantr contract/context files to an existing project or empty workspace
@@ -7136,13 +7292,14 @@ ${BOLD6}Commands:${RESET13}
7136
7292
  ${cyan3("login")} Authenticate with the Decantr registry
7137
7293
  ${cyan3("logout")} Remove stored credentials
7138
7294
  ${cyan3("analyze")} Brownfield entrypoint: scan an existing project and emit attach guidance
7295
+ ${cyan3("telemetry")} Inspect or link this project's opted-in CLI telemetry identity
7139
7296
  ${cyan3("export")} Export design tokens to framework format (shadcn, tailwind, css-vars)
7140
7297
  ${cyan3("registry")} Registry management and intelligence summary
7141
7298
  ${cyan3("rules")} Preview/apply Decantr assistant bridge blocks to repo rule files
7142
7299
  ${cyan3("upgrade")} Check for content updates from registry
7143
7300
  ${cyan3("help")} Show this help
7144
7301
 
7145
- ${BOLD6}Examples:${RESET13}
7302
+ ${BOLD7}Examples:${RESET14}
7146
7303
  decantr new my-app --blueprint=carbon-ai-portal
7147
7304
  decantr magic "AI chatbot with dark cyber theme \u2014 bold and futuristic"
7148
7305
  decantr init
@@ -7162,6 +7319,8 @@ ${BOLD6}Examples:${RESET13}
7162
7319
  decantr content-health --ci --fail-on error
7163
7320
  decantr studio
7164
7321
  decantr studio --report decantr-health.json
7322
+ decantr telemetry status
7323
+ decantr telemetry link --enable --org my-team
7165
7324
  decantr audit
7166
7325
  decantr audit src/pages/HomePage.tsx
7167
7326
  decantr migrate --to v4
@@ -7182,30 +7341,30 @@ ${BOLD6}Examples:${RESET13}
7182
7341
  decantr registry audit-project --namespace @official --dist dist --sources src
7183
7342
  decantr create pattern my-card
7184
7343
 
7185
- ${BOLD6}Workflow Model:${RESET13}
7344
+ ${BOLD7}Workflow Model:${RESET14}
7186
7345
  ${cyan3("Greenfield blueprint")} decantr new my-app --blueprint=X --workflow=greenfield --adoption=decantr-css
7187
7346
  ${cyan3("Greenfield contract")} decantr init --workflow=greenfield --adoption=contract-only
7188
7347
  ${cyan3("Brownfield adoption")} decantr analyze -> decantr init --existing --accept-proposal -> decantr check --brownfield
7189
7348
  ${cyan3("Hybrid composition")} decantr add/remove, decantr theme switch, decantr registry, decantr upgrade
7190
7349
 
7191
- ${BOLD6}Bootstrap adapters:${RESET13}
7350
+ ${BOLD7}Bootstrap adapters:${RESET14}
7192
7351
  Runnable starter adapters: ${cyan3("react-vite")}, ${cyan3("next-app")}
7193
7352
  Unsupported targets resolve through ${cyan3("generic-web")} contract-only mode until their starter adapters land.
7194
7353
  `);
7195
7354
  }
7196
7355
  function cmdRulesHelp() {
7197
7356
  console.log(`
7198
- ${BOLD6}decantr rules${RESET13} \u2014 Preview or apply assistant bridge snippets
7357
+ ${BOLD7}decantr rules${RESET14} \u2014 Preview or apply assistant bridge snippets
7199
7358
 
7200
- ${BOLD6}Usage:${RESET13}
7359
+ ${BOLD7}Usage:${RESET14}
7201
7360
  decantr rules preview [--project=<path>]
7202
7361
  decantr rules apply [--project=<path>]
7203
7362
 
7204
- ${BOLD6}Subcommands:${RESET13}
7363
+ ${BOLD7}Subcommands:${RESET14}
7205
7364
  ${cyan3("preview")} Print target-specific Decantr bridge guidance without mutating rule files
7206
7365
  ${cyan3("apply")} Idempotently write Decantr bridge blocks to supported assistant rule files
7207
7366
 
7208
- ${BOLD6}Examples:${RESET13}
7367
+ ${BOLD7}Examples:${RESET14}
7209
7368
  decantr rules preview
7210
7369
  decantr rules preview --project=apps/web
7211
7370
  decantr rules apply --project=apps/web
@@ -7216,9 +7375,9 @@ function isCommandHelpRequest(args) {
7216
7375
  }
7217
7376
  function cmdHealthHelp() {
7218
7377
  console.log(`
7219
- ${BOLD6}decantr health${RESET13} \u2014 Generate a local Project Health report
7378
+ ${BOLD7}decantr health${RESET14} \u2014 Generate a local Project Health report
7220
7379
 
7221
- ${BOLD6}Usage:${RESET13}
7380
+ ${BOLD7}Usage:${RESET14}
7222
7381
  decantr health [--format text|json|markdown] [--output <file>]
7223
7382
  decantr health --json
7224
7383
  decantr health --markdown
@@ -7226,7 +7385,7 @@ ${BOLD6}Usage:${RESET13}
7226
7385
  decantr health --prompt <finding-id>
7227
7386
  decantr health init-ci [--force] [--project <path>] [--fail-on error|warn|none] [--cli-version <version|latest>]
7228
7387
 
7229
- ${BOLD6}Options:${RESET13}
7388
+ ${BOLD7}Options:${RESET14}
7230
7389
  --format Output format: text, json, or markdown
7231
7390
  --json Emit JSON report
7232
7391
  --markdown Emit markdown report
@@ -7235,7 +7394,7 @@ ${BOLD6}Options:${RESET13}
7235
7394
  --fail-on CI threshold: error, warn, or none
7236
7395
  --prompt Print an AI-ready remediation prompt for a finding
7237
7396
 
7238
- ${BOLD6}Examples:${RESET13}
7397
+ ${BOLD7}Examples:${RESET14}
7239
7398
  decantr health
7240
7399
  decantr health --json
7241
7400
  decantr health --markdown --output decantr-health.md
@@ -7246,16 +7405,16 @@ ${BOLD6}Examples:${RESET13}
7246
7405
  }
7247
7406
  function cmdContentHealthHelp() {
7248
7407
  console.log(`
7249
- ${BOLD6}decantr content-health${RESET13} \u2014 Generate a local registry content health report
7408
+ ${BOLD7}decantr content-health${RESET14} \u2014 Generate a local registry content health report
7250
7409
 
7251
- ${BOLD6}Usage:${RESET13}
7410
+ ${BOLD7}Usage:${RESET14}
7252
7411
  decantr content-health [--format text|json|markdown] [--output <file>]
7253
7412
  decantr content-health --json
7254
7413
  decantr content-health --markdown
7255
7414
  decantr content-health --ci [--fail-on error|warn|none]
7256
7415
  decantr content-health --prompt <finding-id>
7257
7416
 
7258
- ${BOLD6}Options:${RESET13}
7417
+ ${BOLD7}Options:${RESET14}
7259
7418
  --format Output format: text, json, or markdown
7260
7419
  --json Emit JSON report
7261
7420
  --markdown Emit markdown report
@@ -7264,7 +7423,7 @@ ${BOLD6}Options:${RESET13}
7264
7423
  --fail-on CI threshold: error, warn, or none
7265
7424
  --prompt Print an AI-ready remediation prompt for a finding
7266
7425
 
7267
- ${BOLD6}Examples:${RESET13}
7426
+ ${BOLD7}Examples:${RESET14}
7268
7427
  decantr content-health
7269
7428
  decantr content-health --json
7270
7429
  decantr content-health --markdown --output content-health.md
@@ -7273,22 +7432,22 @@ ${BOLD6}Examples:${RESET13}
7273
7432
  }
7274
7433
  function cmdStudioHelp() {
7275
7434
  console.log(`
7276
- ${BOLD6}decantr studio${RESET13} \u2014 Run a local Project Health dashboard
7435
+ ${BOLD7}decantr studio${RESET14} \u2014 Run a local Project Health dashboard
7277
7436
 
7278
- ${BOLD6}Usage:${RESET13}
7437
+ ${BOLD7}Usage:${RESET14}
7279
7438
  decantr studio [--port 4319] [--host 127.0.0.1] [--report decantr-health.json]
7280
7439
 
7281
- ${BOLD6}Options:${RESET13}
7440
+ ${BOLD7}Options:${RESET14}
7282
7441
  --port Local port to bind; defaults to 4319
7283
7442
  --host Local host to bind; defaults to 127.0.0.1
7284
7443
  --report Serve a read-only Project Health JSON artifact instead of scanning the current project
7285
7444
 
7286
- ${BOLD6}Endpoints:${RESET13}
7445
+ ${BOLD7}Endpoints:${RESET14}
7287
7446
  GET /
7288
7447
  GET /api/health
7289
7448
  POST /api/refresh
7290
7449
 
7291
- ${BOLD6}Examples:${RESET13}
7450
+ ${BOLD7}Examples:${RESET14}
7292
7451
  decantr studio
7293
7452
  decantr studio --port 4320
7294
7453
  decantr studio --host 127.0.0.1 --port 4319
@@ -7418,10 +7577,10 @@ async function main() {
7418
7577
  case "heal": {
7419
7578
  if (command === "heal") {
7420
7579
  console.log(
7421
- `${YELLOW9}Note: \`decantr heal\` is deprecated. Use \`decantr check\` instead.${RESET13}`
7580
+ `${YELLOW9}Note: \`decantr heal\` is deprecated. Use \`decantr check\` instead.${RESET14}`
7422
7581
  );
7423
7582
  }
7424
- const { cmdHeal } = await import("./heal-NWQNJ6PU.js");
7583
+ const { cmdHeal } = await import("./heal-M6PRCIIF.js");
7425
7584
  const telemetryFlag = args.includes("--telemetry");
7426
7585
  const brownfieldFlag = args.includes("--brownfield");
7427
7586
  await cmdHeal(process.cwd(), { telemetry: telemetryFlag, brownfield: brownfieldFlag });
@@ -7433,7 +7592,7 @@ async function main() {
7433
7592
  cmdHealthHelp();
7434
7593
  break;
7435
7594
  }
7436
- const { cmdHealth, parseHealthArgs } = await import("./health-WJJ55W3H.js");
7595
+ const { cmdHealth, parseHealthArgs } = await import("./health-EENY3BFS.js");
7437
7596
  await cmdHealth(process.cwd(), parseHealthArgs(args));
7438
7597
  } catch (e) {
7439
7598
  console.error(error3(e.message));
@@ -7461,7 +7620,7 @@ async function main() {
7461
7620
  cmdStudioHelp();
7462
7621
  break;
7463
7622
  }
7464
- const { cmdStudio, parseStudioArgs } = await import("./studio-7XAXWRVN.js");
7623
+ const { cmdStudio, parseStudioArgs } = await import("./studio-TBJPZZHA.js");
7465
7624
  await cmdStudio(process.cwd(), parseStudioArgs(args));
7466
7625
  } catch (e) {
7467
7626
  console.error(error3(e.message));
@@ -7634,6 +7793,10 @@ async function main() {
7634
7793
  console.log(success3("Logged out. Credentials removed."));
7635
7794
  break;
7636
7795
  }
7796
+ case "telemetry": {
7797
+ await cmdTelemetry(args.slice(1), process.cwd());
7798
+ break;
7799
+ }
7637
7800
  case "create": {
7638
7801
  const type = args[1];
7639
7802
  const name = args[2];
@@ -7692,7 +7855,7 @@ async function main() {
7692
7855
  const id = args[3] && !args[3].startsWith("--") ? args[3] : void 0;
7693
7856
  if (!packType || !["manifest", "scaffold", "review", "section", "page", "mutation"].includes(packType)) {
7694
7857
  console.error(
7695
- `${RED11}Usage: decantr registry get-pack <manifest|scaffold|review|section|page|mutation> [id] [--namespace <namespace>] [--json] [--essence <path>] [--write-context]${RESET13}`
7858
+ `${RED11}Usage: decantr registry get-pack <manifest|scaffold|review|section|page|mutation> [id] [--namespace <namespace>] [--json] [--essence <path>] [--write-context]${RESET14}`
7696
7859
  );
7697
7860
  process.exitCode = 1;
7698
7861
  break;
@@ -7720,7 +7883,7 @@ async function main() {
7720
7883
  const sourcePath = args[2] && !args[2].startsWith("--") ? args[2] : void 0;
7721
7884
  if (!sourcePath) {
7722
7885
  console.error(
7723
- `${RED11}Usage: decantr registry critique-file <file> [--namespace <namespace>] [--json] [--essence <path>] [--treatments <path>]${RESET13}`
7886
+ `${RED11}Usage: decantr registry critique-file <file> [--namespace <namespace>] [--json] [--essence <path>] [--treatments <path>]${RESET14}`
7724
7887
  );
7725
7888
  process.exitCode = 1;
7726
7889
  break;
@@ -7745,7 +7908,7 @@ async function main() {
7745
7908
  await printHostedProjectAudit(namespace, jsonOutput, essencePath, distPath, sourcesPath);
7746
7909
  } else {
7747
7910
  console.error(
7748
- `${RED11}Usage: decantr registry mirror [--type <type>] | decantr registry summary [--namespace <namespace>] [--json] | decantr registry compile-packs [path] [--namespace <namespace>] [--json] [--write-context] | decantr registry get-pack <manifest|scaffold|review|section|page|mutation> [id] [--namespace <namespace>] [--json] [--essence <path>] [--write-context] | decantr registry critique-file <file> [--namespace <namespace>] [--json] [--essence <path>] [--treatments <path>] | decantr registry audit-project [--namespace <namespace>] [--json] [--essence <path>] [--dist <path>] [--sources <dir>]${RESET13}`
7911
+ `${RED11}Usage: decantr registry mirror [--type <type>] | decantr registry summary [--namespace <namespace>] [--json] | decantr registry compile-packs [path] [--namespace <namespace>] [--json] [--write-context] | decantr registry get-pack <manifest|scaffold|review|section|page|mutation> [id] [--namespace <namespace>] [--json] [--essence <path>] [--write-context] | decantr registry critique-file <file> [--namespace <namespace>] [--json] [--essence <path>] [--treatments <path>] | decantr registry audit-project [--namespace <namespace>] [--json] [--essence <path>] [--dist <path>] [--sources <dir>]${RESET14}`
7749
7912
  );
7750
7913
  process.exitCode = 1;
7751
7914
  }
@@ -7861,7 +8024,7 @@ async function main() {
7861
8024
  process.exitCode = 1;
7862
8025
  break;
7863
8026
  }
7864
- cmdAnalyze(workspaceInfo.appRoot, workspaceInfo);
8027
+ await cmdAnalyze(workspaceInfo.appRoot, workspaceInfo);
7865
8028
  break;
7866
8029
  }
7867
8030
  case "rules": {
@@ -7925,7 +8088,7 @@ async function main() {
7925
8088
  console.error("");
7926
8089
  console.error(" Example:");
7927
8090
  console.error(
7928
- ` ${CYAN7}decantr magic "AI agent dashboard \u2014 dark, neon, confident"${RESET13}`
8091
+ ` ${CYAN8}decantr magic "AI agent dashboard \u2014 dark, neon, confident"${RESET14}`
7929
8092
  );
7930
8093
  process.exitCode = 1;
7931
8094
  break;
@@ -134,6 +134,30 @@ async function sendProjectHealthReportTelemetry(input) {
134
134
  });
135
135
  }
136
136
  }
137
+ async function sendAnalyzeCompletedTelemetry(input) {
138
+ const projectRoot = input.projectRoot ?? process.cwd();
139
+ const metadata = readProjectTelemetryMetadata(projectRoot);
140
+ const properties = {
141
+ command: "analyze",
142
+ success: input.success,
143
+ durationMs: input.durationMs,
144
+ adoptionMode: metadata.adoptionMode ?? "contract-only",
145
+ componentCount: input.componentCount,
146
+ dependencyCategoryCount: input.dependencyCategoryCount,
147
+ errorCode: input.success ? void 0 : "analyze_failed",
148
+ pageCount: input.pageCount,
149
+ projectScope: metadata.projectScope ?? inferProjectScope(projectRoot),
150
+ routeCount: input.routeCount,
151
+ targetFramework: input.targetFramework,
152
+ workflowMode: metadata.workflowMode ?? "brownfield-attach"
153
+ };
154
+ await captureCliTelemetryEvent({
155
+ args: ["analyze"],
156
+ name: "decantr.analyze.completed",
157
+ projectRoot,
158
+ properties
159
+ });
160
+ }
137
161
  async function sendNewProjectCompletedTelemetry(input) {
138
162
  const projectRoot = input.projectRoot ?? process.cwd();
139
163
  const args = input.args ?? ["new"];
@@ -303,6 +327,19 @@ function collectMetrics(essence, issues) {
303
327
  theme: theme.id ?? "unknown"
304
328
  };
305
329
  }
330
+ function getCliTelemetryIdentityStatus(projectRoot, options = {}) {
331
+ const projectJsonPath = join(projectRoot, ".decantr", "project.json");
332
+ const hasProjectConfig = existsSync(projectJsonPath);
333
+ const identities = options.create ? ensureTelemetryIdentities(projectRoot) : null;
334
+ const projectData = readProjectJson(projectRoot);
335
+ return {
336
+ enabled: projectData?.telemetry === true,
337
+ hasProjectConfig,
338
+ installId: identities?.installId ?? readExistingInstallId(),
339
+ projectId: identities?.projectId ?? readStringProperty(projectData, "telemetryProjectId"),
340
+ projectRoot
341
+ };
342
+ }
306
343
  function ensureTelemetryIdentities(projectRoot) {
307
344
  const installId = getOrCreateInstallId();
308
345
  const projectJsonPath = join(projectRoot, ".decantr", "project.json");
@@ -348,6 +385,16 @@ function getOrCreateInstallId() {
348
385
  return `install_${randomUUID()}`;
349
386
  }
350
387
  }
388
+ function readExistingInstallId() {
389
+ const configPath = join(getConfigDir(), "config.json");
390
+ if (!existsSync(configPath)) return void 0;
391
+ try {
392
+ const data = JSON.parse(readFileSync(configPath, "utf-8"));
393
+ return readStringProperty(data, "telemetryInstallId");
394
+ } catch {
395
+ return void 0;
396
+ }
397
+ }
351
398
  function getConfigDir() {
352
399
  return process.env.DECANTR_CONFIG_DIR || join(homedir(), ".config", "decantr");
353
400
  }
@@ -386,6 +433,10 @@ function readProjectJson(projectRoot) {
386
433
  return null;
387
434
  }
388
435
  }
436
+ function readStringProperty(value, key) {
437
+ const property = value?.[key];
438
+ return typeof property === "string" && property.trim() ? property : void 0;
439
+ }
389
440
  function isRecord(value) {
390
441
  return typeof value === "object" && value !== null && !Array.isArray(value);
391
442
  }
@@ -1650,12 +1701,14 @@ export {
1650
1701
  optIn,
1651
1702
  sendCliCommandTelemetry,
1652
1703
  sendProjectHealthReportTelemetry,
1704
+ sendAnalyzeCompletedTelemetry,
1653
1705
  sendNewProjectCompletedTelemetry,
1654
1706
  sendProjectHealthPromptTelemetry,
1655
1707
  sendProjectHealthCiFailedTelemetry,
1656
1708
  sendStudioStartedTelemetry,
1657
1709
  sendStudioHealthRefreshedTelemetry,
1658
1710
  collectMetrics,
1711
+ getCliTelemetryIdentityStatus,
1659
1712
  buildGuardRegistryContext,
1660
1713
  scanProjectInteractions
1661
1714
  };
@@ -10,7 +10,7 @@ import {
10
10
  scanRoutes,
11
11
  scanStyling,
12
12
  sendGuardMetrics
13
- } from "./chunk-JYEEXSUX.js";
13
+ } from "./chunk-IEW2QFYI.js";
14
14
 
15
15
  // src/commands/heal.ts
16
16
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  cmdHeal,
3
3
  collectCheckIssues
4
- } from "./chunk-LLQCXOHK.js";
5
- import "./chunk-JYEEXSUX.js";
4
+ } from "./chunk-NBJCO4G5.js";
5
+ import "./chunk-IEW2QFYI.js";
6
6
  export {
7
7
  cmdHeal,
8
8
  collectCheckIssues
@@ -8,9 +8,9 @@ import {
8
8
  renderProjectHealthCiWorkflow,
9
9
  shouldFailHealth,
10
10
  writeProjectHealthCiWorkflow
11
- } from "./chunk-DPFORHLL.js";
12
- import "./chunk-LLQCXOHK.js";
13
- import "./chunk-JYEEXSUX.js";
11
+ } from "./chunk-3H3HWDJA.js";
12
+ import "./chunk-NBJCO4G5.js";
13
+ import "./chunk-IEW2QFYI.js";
14
14
  export {
15
15
  cmdHealth,
16
16
  createProjectHealthReport,
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import "./chunk-3SULF2JT.js";
1
+ import "./chunk-FSZ6OIAC.js";
2
2
  import "./chunk-WDA4SHIQ.js";
3
- import "./chunk-JYEEXSUX.js";
3
+ import "./chunk-IEW2QFYI.js";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  createProjectHealthReport
3
- } from "./chunk-DPFORHLL.js";
4
- import "./chunk-LLQCXOHK.js";
3
+ } from "./chunk-3H3HWDJA.js";
4
+ import "./chunk-NBJCO4G5.js";
5
5
  import {
6
6
  sendStudioHealthRefreshedTelemetry,
7
7
  sendStudioStartedTelemetry
8
- } from "./chunk-JYEEXSUX.js";
8
+ } from "./chunk-IEW2QFYI.js";
9
9
 
10
10
  // src/commands/studio.ts
11
11
  import { readFileSync } from "fs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decantr/cli",
3
- "version": "2.1.2",
3
+ "version": "2.3.0",
4
4
  "description": "Decantr CLI - scaffold, audit, inspect Project Health, and maintain Decantr projects from the terminal",
5
5
  "author": "Decantr AI",
6
6
  "license": "MIT",
@@ -32,10 +32,10 @@
32
32
  "dependencies": {
33
33
  "ajv": "^8.20.0",
34
34
  "@decantr/core": "2.0.0",
35
- "@decantr/essence-spec": "2.0.1",
35
+ "@decantr/telemetry": "2.2.0",
36
36
  "@decantr/registry": "2.0.0",
37
37
  "@decantr/verifier": "2.0.0",
38
- "@decantr/telemetry": "2.1.0"
38
+ "@decantr/essence-spec": "2.0.1"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "tsup",