@decantr/cli 1.10.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -99,11 +99,12 @@ decantr health --ci --fail-on warn
99
99
  decantr health --prompt <finding-id>
100
100
  decantr health init-ci
101
101
  decantr health init-ci --fail-on warn --cli-version latest --force
102
+ decantr health init-ci --project apps/registry
102
103
  ```
103
104
 
104
105
  Use `--json` for machines and schema validation, `--markdown` for CI summaries, and `--prompt <finding-id>` when you want a scoped remediation prompt for an AI assistant. `--ci --fail-on error` fails only when blocking errors exist; `--ci --fail-on warn` also fails on warnings.
105
106
 
106
- `decantr health init-ci` installs `.github/workflows/decantr-health.yml` for GitHub Actions. The generated workflow installs project dependencies, writes `decantr-health.json`, gates with `decantr health --ci --fail-on error --markdown --output decantr-health.md`, 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.
107
+ `decantr health init-ci` installs `.github/workflows/decantr-health.yml` for GitHub Actions. The generated workflow installs project dependencies, writes `decantr-health.json`, gates with `decantr health --ci --fail-on error --markdown --output decantr-health.md`, 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.
107
108
 
108
109
  `decantr studio` starts a local-only dashboard powered by the same report. It uses Node built-ins only and serves `GET /`, `GET /api/health`, and `POST /api/refresh`.
109
110
 
package/dist/bin.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import "./chunk-FLZVSNB5.js";
2
+ import "./chunk-5RODH77L.js";
3
3
  import "./chunk-USOO77A5.js";
4
4
  import "./chunk-DI2PLOJ6.js";
@@ -6927,7 +6927,7 @@ ${BOLD6}Usage:${RESET13}
6927
6927
  decantr registry get-pack <manifest|scaffold|review|section|page|mutation> [id] [--namespace <namespace>] [--json] [--essence <path>] [--write-context]
6928
6928
  decantr registry critique-file <file> [--namespace <namespace>] [--json] [--essence <path>] [--treatments <path>]
6929
6929
  decantr registry audit-project [--namespace <namespace>] [--json] [--essence <path>] [--dist <path>] [--sources <dir>]
6930
- decantr health init-ci [--force] [--fail-on <error|warn|none>] [--cli-version <version|latest>]
6930
+ decantr health init-ci [--force] [--project <path>] [--fail-on <error|warn|none>] [--cli-version <version|latest>]
6931
6931
  decantr content-health [--json] [--markdown] [--ci]
6932
6932
  decantr rules preview [--project=<path>]
6933
6933
  decantr rules apply [--project=<path>]
@@ -7007,6 +7007,7 @@ ${BOLD6}Examples:${RESET13}
7007
7007
  decantr status
7008
7008
  decantr health
7009
7009
  decantr health init-ci
7010
+ decantr health init-ci --project apps/web
7010
7011
  decantr health --ci --fail-on error
7011
7012
  decantr content-health --ci --fail-on error
7012
7013
  decantr studio
@@ -7190,7 +7191,7 @@ async function main() {
7190
7191
  }
7191
7192
  case "health": {
7192
7193
  try {
7193
- const { cmdHealth, parseHealthArgs } = await import("./health-SIKAOE2Z.js");
7194
+ const { cmdHealth, parseHealthArgs } = await import("./health-3TJYYTX6.js");
7194
7195
  await cmdHealth(process.cwd(), parseHealthArgs(args));
7195
7196
  } catch (e) {
7196
7197
  console.error(error3(e.message));
@@ -7210,7 +7211,7 @@ async function main() {
7210
7211
  }
7211
7212
  case "studio": {
7212
7213
  try {
7213
- const { cmdStudio, parseStudioArgs } = await import("./studio-EQSSNA6D.js");
7214
+ const { cmdStudio, parseStudioArgs } = await import("./studio-7TE7YXFG.js");
7214
7215
  await cmdStudio(process.cwd(), parseStudioArgs(args));
7215
7216
  } catch (e) {
7216
7217
  console.error(error3(e.message));
@@ -92,17 +92,45 @@ function validateArtifactPath(value, flag) {
92
92
  }
93
93
  return normalized;
94
94
  }
95
+ function validateProjectPath(value) {
96
+ if (value === void 0) return void 0;
97
+ const raw = value.trim();
98
+ if (!raw || raw === ".") return void 0;
99
+ const normalized = raw.replace(/^\.\/+/, "").replace(/\/+$/, "");
100
+ if (!normalized || normalized.startsWith("/") || normalized.startsWith("-") || normalized.includes("..") || normalized.includes("\\") || /\s/.test(normalized) || !/^[A-Za-z0-9._@/-]+$/.test(normalized)) {
101
+ throw new Error(
102
+ "Invalid --project value. Use a relative project path without spaces or parent-directory segments."
103
+ );
104
+ }
105
+ const segments = normalized.split("/");
106
+ if (segments.some((segment) => !segment || segment === "." || segment === "..")) {
107
+ throw new Error(
108
+ "Invalid --project value. Use a relative project path without empty or parent-directory segments."
109
+ );
110
+ }
111
+ return normalized;
112
+ }
113
+ function prefixArtifactPath(projectPath, artifactPath) {
114
+ return projectPath ? `${projectPath}/${artifactPath}` : artifactPath;
115
+ }
95
116
  function renderProjectHealthCiWorkflow(options = {}) {
96
117
  const failOn = normalizeHealthFailOn(options.failOn);
118
+ const projectPath = validateProjectPath(options.projectPath);
119
+ const reportPath = validateArtifactPath(
120
+ options.reportPath || DEFAULT_HEALTH_CI_REPORT_PATH,
121
+ "--report-path"
122
+ );
123
+ const jsonPath = validateArtifactPath(options.jsonPath || DEFAULT_HEALTH_CI_JSON_PATH, "--json-path");
97
124
  const template = loadHealthTemplate("decantr-health.workflow.yml.template");
98
125
  return renderTemplate(template, {
99
126
  CLI_PACKAGE: normalizeCliPackageSpecifier(options.cliVersion),
100
127
  FAIL_ON: failOn,
101
- REPORT_PATH: validateArtifactPath(
102
- options.reportPath || DEFAULT_HEALTH_CI_REPORT_PATH,
103
- "--report-path"
104
- ),
105
- JSON_PATH: validateArtifactPath(options.jsonPath || DEFAULT_HEALTH_CI_JSON_PATH, "--json-path")
128
+ PROJECT_WORKING_DIRECTORY: projectPath ? ` working-directory: ${projectPath}
129
+ ` : "",
130
+ REPORT_PATH: reportPath,
131
+ JSON_PATH: jsonPath,
132
+ REPORT_ARTIFACT_PATH: prefixArtifactPath(projectPath, reportPath),
133
+ JSON_ARTIFACT_PATH: prefixArtifactPath(projectPath, jsonPath)
106
134
  });
107
135
  }
108
136
  function writeProjectHealthCiWorkflow(projectRoot, options = {}) {
@@ -118,12 +146,15 @@ function writeProjectHealthCiWorkflow(projectRoot, options = {}) {
118
146
  }
119
147
  mkdirSync(dirname(workflowPath), { recursive: true });
120
148
  writeFileSync(workflowPath, renderProjectHealthCiWorkflow(options), "utf-8");
121
- return {
149
+ const projectPath = validateProjectPath(options.projectPath);
150
+ const result = {
122
151
  path: workflowRelativePath,
123
152
  created: !alreadyExists,
124
153
  cliPackage: normalizeCliPackageSpecifier(options.cliVersion),
125
154
  failOn: normalizeHealthFailOn(options.failOn)
126
155
  };
156
+ if (projectPath) result.projectPath = projectPath;
157
+ return result;
127
158
  }
128
159
  function collectDeclaredRoutes(essence) {
129
160
  if (!essence || typeof essence !== "object") return [];
@@ -490,6 +521,9 @@ async function cmdHealth(projectRoot = process.cwd(), options = {}) {
490
521
  const action = result.created ? "Created" : "Updated";
491
522
  console.log(`${GREEN}${action} Decantr Project Health workflow:${RESET} ${result.path}`);
492
523
  console.log(`${DIM}CLI package: ${result.cliPackage}${RESET}`);
524
+ if (result.projectPath) {
525
+ console.log(`${DIM}Project: ${result.projectPath}${RESET}`);
526
+ }
493
527
  console.log(`${DIM}CI gate: decantr health --ci --fail-on ${result.failOn}${RESET}`);
494
528
  } catch (e) {
495
529
  console.error(`${RED}${e.message}${RESET}`);
@@ -552,9 +586,14 @@ function parseHealthArgs(args) {
552
586
  options.initCi.jsonPath = args[++index];
553
587
  } else if (arg.startsWith("--json-path=")) {
554
588
  options.initCi.jsonPath = arg.split("=")[1];
589
+ } else if (arg === "--project" && args[index + 1]) {
590
+ options.initCi.projectPath = args[++index];
591
+ } else if (arg.startsWith("--project=")) {
592
+ options.initCi.projectPath = arg.split("=")[1];
555
593
  }
556
594
  }
557
595
  normalizeHealthFailOn(options.initCi.failOn);
596
+ validateProjectPath(options.initCi.projectPath);
558
597
  return options;
559
598
  }
560
599
  for (let index = 1; index < args.length; index += 1) {
@@ -8,7 +8,7 @@ import {
8
8
  renderProjectHealthCiWorkflow,
9
9
  shouldFailHealth,
10
10
  writeProjectHealthCiWorkflow
11
- } from "./chunk-K5KCZSEI.js";
11
+ } from "./chunk-6YCFRZZI.js";
12
12
  import "./chunk-RSDCWAHD.js";
13
13
  import "./chunk-DI2PLOJ6.js";
14
14
  export {
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- import "./chunk-FLZVSNB5.js";
1
+ import "./chunk-5RODH77L.js";
2
2
  import "./chunk-USOO77A5.js";
3
3
  import "./chunk-DI2PLOJ6.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createProjectHealthReport
3
- } from "./chunk-K5KCZSEI.js";
3
+ } from "./chunk-6YCFRZZI.js";
4
4
  import "./chunk-RSDCWAHD.js";
5
5
  import "./chunk-DI2PLOJ6.js";
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decantr/cli",
3
- "version": "1.10.0",
3
+ "version": "1.11.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",
@@ -30,12 +30,12 @@
30
30
  "access": "public"
31
31
  },
32
32
  "dependencies": {
33
- "ajv": "^8.18.0",
33
+ "ajv": "^8.20.0",
34
34
  "@decantr/core": "1.0.6",
35
- "@decantr/essence-spec": "1.0.7",
36
35
  "@decantr/registry": "1.1.0",
37
36
  "@decantr/telemetry": "0.1.2",
38
- "@decantr/verifier": "1.1.0"
37
+ "@decantr/verifier": "1.1.1",
38
+ "@decantr/essence-spec": "1.0.8"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "tsup",
@@ -38,14 +38,14 @@ jobs:
38
38
  fi
39
39
 
40
40
  - name: Generate Decantr health JSON
41
- run: npx --yes {{CLI_PACKAGE}} health --json --output {{JSON_PATH}}
41
+ {{PROJECT_WORKING_DIRECTORY}} run: npx --yes {{CLI_PACKAGE}} health --json --output {{JSON_PATH}}
42
42
 
43
43
  - name: Audit Decantr health
44
- run: npx --yes {{CLI_PACKAGE}} health --ci --fail-on {{FAIL_ON}} --markdown --output {{REPORT_PATH}}
44
+ {{PROJECT_WORKING_DIRECTORY}} run: npx --yes {{CLI_PACKAGE}} health --ci --fail-on {{FAIL_ON}} --markdown --output {{REPORT_PATH}}
45
45
 
46
46
  - name: Publish health summary
47
47
  if: always()
48
- shell: bash
48
+ {{PROJECT_WORKING_DIRECTORY}} shell: bash
49
49
  run: |
50
50
  if [ -f {{REPORT_PATH}} ]; then
51
51
  cat {{REPORT_PATH}} >> "$GITHUB_STEP_SUMMARY"
@@ -57,6 +57,6 @@ jobs:
57
57
  with:
58
58
  name: decantr-project-health
59
59
  path: |
60
- {{JSON_PATH}}
61
- {{REPORT_PATH}}
60
+ {{JSON_ARTIFACT_PATH}}
61
+ {{REPORT_ARTIFACT_PATH}}
62
62
  if-no-files-found: ignore