@ecoma-io/archkeep 0.18.0 → 0.18.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/cli.mjs CHANGED
@@ -93,6 +93,11 @@
93
93
  */
94
94
  import { existsSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
95
95
  import { dirname, isAbsolute, join, resolve } from "node:path";
96
+ import { createRequire } from "node:module";
97
+
98
+ const require = createRequire(import.meta.url);
99
+ /*** @type {{name: string, version: string}} */
100
+ const { name: TOOL_NAME, version: TOOL_VERSION } = require("./package.json");
96
101
 
97
102
  import { containmentViolation } from "./src/containment.mjs";
98
103
  import { UsageError } from "./src/errors.mjs";
@@ -2541,9 +2546,10 @@ const DELTA_FLAG_HELP = Object.freeze([
2541
2546
  key: "capture",
2542
2547
  arg: "",
2543
2548
  describe: Object.freeze([
2544
- "Write an evidence snapshot of the current tree",
2549
+ "Print an evidence snapshot of the current tree",
2545
2550
  "(raw import records, graph, coverage, policy",
2546
- "fingerprint) for a later delta run to compare against",
2551
+ "fingerprint) for a later delta run to compare against.",
2552
+ "Without --output, the snapshot goes to stdout.",
2547
2553
  ]),
2548
2554
  }),
2549
2555
  Object.freeze({
@@ -3621,10 +3627,15 @@ export async function runCli(argv, env) {
3621
3627
  // one root-marker read and a clean run pays none.
3622
3628
  const help = () => usage(optionsForUsage(cwd));
3623
3629
 
3630
+ // --help and --version are universal flags, handled before command dispatch.
3624
3631
  if (argv[0] === "--help" || argv[0] === "-h") {
3625
3632
  env.out(help());
3626
3633
  return EXIT.ok;
3627
3634
  }
3635
+ if (argv[0] === "--version" || argv[0] === "-v") {
3636
+ env.out(`${TOOL_NAME} ${TOOL_VERSION}`);
3637
+ return EXIT.ok;
3638
+ }
3628
3639
 
3629
3640
  const [maybeCommand, ...maybeRest] = argv;
3630
3641
  let commandName;
@@ -3635,6 +3646,11 @@ export async function runCli(argv, env) {
3635
3646
  } else if (Object.hasOwn(COMMANDS, maybeCommand)) {
3636
3647
  commandName = maybeCommand;
3637
3648
  rest = maybeRest;
3649
+ // Subcommand --help: show the main help without "unknown option" error.
3650
+ if (rest.includes("--help") || rest.includes("-h")) {
3651
+ env.out(help());
3652
+ return EXIT.ok;
3653
+ }
3638
3654
  } else if (
3639
3655
  maybeCommand !== "" &&
3640
3656
  existsSync(isAbsolute(maybeCommand) ? maybeCommand : join(cwd, maybeCommand))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecoma-io/archkeep",
3
- "version": "0.18.0",
3
+ "version": "0.18.1",
4
4
  "description": "Architecture enforcement for polyglot repositories — dependency graphs and module boundaries for Go, Rust, Python, TypeScript, JavaScript, Vue, Java and Kotlin",
5
5
  "keywords": [
6
6
  "architecture",
@@ -114,6 +114,21 @@ export function parseBaseline(text, path) {
114
114
  );
115
115
  }
116
116
 
117
+ if (!envelope.command) {
118
+ throw new Error(
119
+ `archkeep: the baseline snapshot at '${path}' has no 'command' field — it is not a ` +
120
+ `'graph' envelope. diff requires a graph snapshot (from 'graph --format json'). ` +
121
+ `For delta evidence snapshots use 'delta <snapshot>'.`,
122
+ );
123
+ }
124
+
125
+ if (envelope.command !== "graph") {
126
+ throw new Error(
127
+ `archkeep: the baseline snapshot at '${path}' is a '${envelope.command}' envelope, not a ` +
128
+ `'graph' envelope — diff requires a graph snapshot as its baseline`,
129
+ );
130
+ }
131
+
117
132
  // A consumer that reads a schemaVersion it does not recognise should refuse
118
133
  // to parse the rest (`docs/reference/json-output.md`). This tool IS that
119
134
  // consumer when reading a baseline — a future schema version could change
@@ -127,13 +142,6 @@ export function parseBaseline(text, path) {
127
142
  );
128
143
  }
129
144
 
130
- if (envelope.command !== "graph") {
131
- throw new Error(
132
- `archkeep: the baseline snapshot at '${path}' is a '${envelope.command}' envelope, not a ` +
133
- `'graph' envelope — diff requires a graph snapshot as its baseline`,
134
- );
135
- }
136
-
137
145
  if (!envelope.coverage?.complete) {
138
146
  throw new Error(
139
147
  `archkeep: the baseline snapshot at '${path}' has incomplete coverage — every "removed" ` +
@@ -97,6 +97,7 @@ import {
97
97
  resolveDecisionRef,
98
98
  stripAdrPrefix,
99
99
  } from "../governance/adr-registry.mjs";
100
+ import { isAbsolute, relative, resolve, sep } from "node:path";
100
101
 
101
102
  /**
102
103
  * Parses a `file:line:column` site string into its components.
@@ -354,6 +355,16 @@ export function explainCommand(site, commandContext, config, options = {}) {
354
355
 
355
356
  const parsed = parseSite(site);
356
357
 
358
+ // Normalize the site's sourceFile to a workspace-relative path so it
359
+ // matches the analysis record's sourceFile field (contract.md: workspace-relative).
360
+ // Handles: absolute paths, cwd-relative paths, and backslash separators.
361
+ const rawFile = parsed.sourceFile;
362
+ const normalizedFile = isAbsolute(rawFile)
363
+ ? relative(root, rawFile)
364
+ : relative(root, resolve(root, rawFile));
365
+ // Normalize backslash separators (Windows paths) to forward slashes.
366
+ parsed.sourceFile = sep === "\\" ? normalizedFile.replaceAll("\\", "/") : normalizedFile;
367
+
357
368
  const notAnalyzed = commandContext.analysis.failures
358
369
  .filter(isWholeFileFailure)
359
370
  .map(({ sourceFile, reason }) => ({ file: sourceFile, reason }));
@@ -84,7 +84,9 @@ function loadCatalog(catalogPath, cwd) {
84
84
 
85
85
  if (!existsSync(resolvedPath)) {
86
86
  throw new Error(
87
- `catalog not found at ${catalogPath} — install @ecoma-io/archkeep-rules or use --catalog to point to a catalog.json file`,
87
+ `catalog not found at ${catalogPath} — install @ecoma-io/archkeep-rules ` +
88
+ `(\`npm install -D @ecoma-io/archkeep-rules\`) or use --catalog to point to a ` +
89
+ `catalog.json file`,
88
90
  );
89
91
  }
90
92