@slats/claude-assets-sync 0.3.2 → 0.3.3

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
@@ -4,7 +4,7 @@ Engine + dispatcher CLI that lets any npm package ship its own Claude Code docs
4
4
 
5
5
  ## Overview
6
6
 
7
- A consumer package declares `claude.assetPath` in `package.json` and runs `claude-build-hashes` during build to emit `dist/claude-hashes.json`. End users run `npx -p @slats/claude-assets-sync inject-claude-settings --package=<name>` and this engine resolves each consumer's metadata, compares its hash manifest against the target `.claude/`, and copies only what is out of date.
7
+ A consumer package declares `claude.assetPath` in `package.json` and runs `claude-build-hashes` during build to emit `dist/claude-hashes.json`. End users run `npx @slats/claude-assets-sync --package=<name>` and this engine resolves each consumer's metadata, compares its hash manifest against the target `.claude/`, and copies only what is out of date.
8
8
 
9
9
  `--package` accepts a scoped name (`@scope/pkg`), an unscoped name (`pkg`), or a **scope alias** (`@scope` with no slash) that fans out to every installed `node_modules/@scope/*` package declaring `claude.assetPath`. Single-target resolution uses `createRequire`; scope-alias enumeration walks ancestor `node_modules/@<scope>/` directories from `cwd` upward and is isolated to `runCli/utils/resolveScopeAlias.ts`.
10
10
 
@@ -21,22 +21,45 @@ yarn add -D @slats/claude-assets-sync
21
21
  ## CLI Surface
22
22
 
23
23
  ```
24
- inject-claude-settings --package=<name> [--scope=user|project] [--dry-run] [--force] [--root=<cwd>]
24
+ <bin> --package=<name> [--scope=user|project] [--dry-run] [--force] [--root=<cwd>]
25
25
  claude-build-hashes
26
26
  ```
27
27
 
28
+ `<bin>` is one of three entry points that all dispatch to the same engine:
29
+
30
+ | Bin | Use when |
31
+ |---|---|
32
+ | `claude-assets-sync` | invoking via `npx` — matches the package's unscoped name so `npx @slats/claude-assets-sync ...` works directly |
33
+ | `inject-claude-settings` | the engine is installed (`yarn add -D` / `npm i -g`) and you prefer a descriptive command name |
34
+ | `claude-build-hashes` | build-time helper for consumer packages (run from `package.json` scripts) |
35
+
28
36
  ### End-user invocation
29
37
 
30
- The engine is not shipped as a runtime dependency of consumers. Always invoke via `npx -p @slats/claude-assets-sync ...`; the package manager fetches and caches the engine on demand.
38
+ The engine is not shipped as a runtime dependency of consumers. The canonical npx form is:
31
39
 
32
40
  ```bash
33
41
  # Single consumer:
34
- npx -p @slats/claude-assets-sync inject-claude-settings --package=@canard/schema-form --scope=user
42
+ npx @slats/claude-assets-sync --package=@canard/schema-form --scope=user
35
43
 
36
44
  # Scope alias — every installed @winglet/* that declares claude.assetPath:
37
- npx -p @slats/claude-assets-sync inject-claude-settings --package=@winglet --scope=user
45
+ npx @slats/claude-assets-sync --package=@winglet --scope=user
38
46
  ```
39
47
 
48
+ The dispatcher walks `node_modules` from the current working directory (or `--root <path>`) up to the filesystem root, so it works as long as the target package is installed somewhere in the host project's hoisting chain.
49
+
50
+ #### Installed CLI (alternative)
51
+
52
+ ```bash
53
+ yarn add -D @slats/claude-assets-sync
54
+ yarn inject-claude-settings --package=@canard/schema-form --scope=user
55
+
56
+ # or globally:
57
+ npm i -g @slats/claude-assets-sync
58
+ inject-claude-settings --package=@canard/schema-form --scope=user
59
+ ```
60
+
61
+ The legacy explicit form `npx -p @slats/claude-assets-sync inject-claude-settings ...` continues to work for backward compatibility.
62
+
40
63
  | Flag | Meaning |
41
64
  |---|---|
42
65
  | `--package <name>` | **Required.** Repeatable/comma-separable. Accepts `@scope/pkg`, `pkg`, or a scope alias `@scope` (fans out to every installed `node_modules/@scope/*` with `claude.assetPath`). |
@@ -89,7 +112,7 @@ Ship the resulting `dist/` (including `claude-hashes.json`) alongside `docs/` wh
89
112
  - The engine is used at two moments only: (1) the consumer's own build, where `claude-build-hashes` produces `dist/claude-hashes.json`, and (2) the end user's one-off `inject-claude-settings` invocation. Neither is runtime behaviour of the consumer library.
90
113
  - Putting the engine in `dependencies` would force every downstream installer of the consumer to pull `commander`, `@inquirer/prompts`, and their transitive trees into their production `node_modules` — dead weight for anyone who never sets up Claude Code assets.
91
114
  - The workspace build chain still resolves `.bin/claude-build-hashes` from `devDependencies` at `yarn install` time; yarn workspaces link devDeps and deps identically for workspace-local builds.
92
- - End users never rely on a hoisted `inject-claude-settings` bin. The canonical invocation is `npx -p @slats/claude-assets-sync inject-claude-settings --package=<THIS>`, which fetches the engine on demand and caches it.
115
+ - End users never rely on a hoisted `inject-claude-settings` bin. The canonical invocation is `npx @slats/claude-assets-sync --package=<THIS>`, which fetches the engine on demand and caches it.
93
116
  - Bundle isolation is enforced by the import graph (`src/**` in the consumer never references the engine), not by dependency-type.
94
117
 
95
118
  ## Authoring `docs/claude/`
@@ -2,9 +2,9 @@
2
2
  "schemaVersion": 1,
3
3
  "package": {
4
4
  "name": "@slats/claude-assets-sync",
5
- "version": "0.3.2"
5
+ "version": "0.3.3"
6
6
  },
7
- "generatedAt": "2026-04-24T17:08:31.012Z",
7
+ "generatedAt": "2026-04-26T11:57:30.854Z",
8
8
  "algorithm": "sha256",
9
9
  "assetRoot": "docs/claude",
10
10
  "files": {
@@ -1,3 +1,4 @@
1
+ import { basename } from 'node:path';
1
2
  import { Command } from 'commander';
2
3
  import { logger } from '../../utils/logger.mjs';
3
4
  import { VERSION } from '../../utils/version.mjs';
@@ -5,10 +6,11 @@ import { renderOrFallback } from './utils/renderOrFallback.mjs';
5
6
  import { resolveTargets } from './utils/resolveTargets.mjs';
6
7
  import { toConsumerPackages } from './utils/toConsumerPackages.mjs';
7
8
 
9
+ const FALLBACK_PROGRAM_NAME = 'inject-claude-settings';
8
10
  async function runCli(argv = process.argv) {
9
11
  const cmd = new Command();
10
12
  cmd
11
- .name('inject-claude-settings')
13
+ .name(deriveProgramName(argv))
12
14
  .description("Inject target consumer(s)' Claude assets into the selected .claude directory")
13
15
  .version(VERSION)
14
16
  .option('--package <name...>', 'Target(s). "@<scope>" = whole npm scope; "@<scope>/<name>" or "<name>" = one package. Repeat the flag or comma-separate values.', collectPackageValues, [])
@@ -52,5 +54,13 @@ function collectPackageValues(value, previous = []) {
52
54
  .filter(Boolean),
53
55
  ];
54
56
  }
57
+ function deriveProgramName(argv) {
58
+ const argv1 = argv[1];
59
+ if (typeof argv1 !== 'string' || argv1.length === 0) {
60
+ return FALLBACK_PROGRAM_NAME;
61
+ }
62
+ const base = basename(argv1).replace(/\.(mjs|cjs|js)$/, '');
63
+ return base.length > 0 ? base : FALLBACK_PROGRAM_NAME;
64
+ }
55
65
 
56
66
  export { runCli };
@@ -13,4 +13,4 @@ export interface ResolvePackageOptions {
13
13
  */
14
14
  skipMissingAsset?: boolean;
15
15
  }
16
- export declare function resolvePackage(name: string, options?: ResolvePackageOptions): Promise<ResolvedMetadata | null>;
16
+ export declare function resolvePackage(name: string, options?: ResolvePackageOptions, originCwd?: string): Promise<ResolvedMetadata | null>;
@@ -2,10 +2,11 @@ import { existsSync, readFileSync } from 'node:fs';
2
2
  import { readFile } from 'node:fs/promises';
3
3
  import { createRequire } from 'node:module';
4
4
  import { dirname, resolve } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
5
6
  import { logger } from '../../../utils/logger.mjs';
6
7
 
7
- async function resolvePackage(name, options = {}) {
8
- const pkgJsonPath = resolvePackageJsonPath(name);
8
+ async function resolvePackage(name, options = {}, originCwd = process.cwd()) {
9
+ const pkgJsonPath = resolvePackageJsonPath(name, originCwd);
9
10
  if (!pkgJsonPath) {
10
11
  logger.error(`cannot resolve package "${name}". Install it in the current project or pass the correct name.`);
11
12
  process.exit(2);
@@ -37,8 +38,14 @@ async function resolvePackage(name, options = {}) {
37
38
  assetPath,
38
39
  };
39
40
  }
40
- function resolvePackageJsonPath(name) {
41
- const require = createRequire(import.meta.url);
41
+ function resolvePackageJsonPath(name, originCwd) {
42
+ const fromCwd = tryResolveFrom(name, resolve(originCwd, '__resolve-base__'));
43
+ if (fromCwd)
44
+ return fromCwd;
45
+ return tryResolveFrom(name, fileURLToPath(import.meta.url));
46
+ }
47
+ function tryResolveFrom(name, baseFilename) {
48
+ const require = createRequire(baseFilename);
42
49
  try {
43
50
  return require.resolve(`${name}/package.json`);
44
51
  }
@@ -23,7 +23,7 @@ async function resolveScopeAlias(scope, rootCwd) {
23
23
  }
24
24
  const resolved = [];
25
25
  for (const name of matchedNames) {
26
- const meta = await resolvePackage(name, { skipMissingAsset: true });
26
+ const meta = await resolvePackage(name, { skipMissingAsset: true }, rootCwd);
27
27
  if (meta)
28
28
  resolved.push(meta);
29
29
  }
@@ -20,9 +20,7 @@ async function resolveTargets(targets, rootCwd) {
20
20
  candidates = await resolveScopeAlias(classification.scope, rootCwd);
21
21
  }
22
22
  else {
23
- const meta = await resolvePackage(classification.name, {
24
- skipMissingAsset: !isSingleTarget,
25
- });
23
+ const meta = await resolvePackage(classification.name, { skipMissingAsset: !isSingleTarget }, rootCwd);
26
24
  candidates = meta ? [meta] : [];
27
25
  }
28
26
  for (const meta of candidates) {
@@ -2,4 +2,4 @@
2
2
  * Current package version from package.json
3
3
  * Automatically synchronized during build process
4
4
  */
5
- export declare const VERSION = "0.3.2";
5
+ export declare const VERSION = "0.3.3";
@@ -1,3 +1,3 @@
1
- const VERSION = '0.3.2';
1
+ const VERSION = '0.3.3';
2
2
 
3
3
  export { VERSION };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slats/claude-assets-sync",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "Shared CLI engine that lets consumer packages inject their Claude docs (skills, rules, commands) into a user's .claude directory via a thin bin/inject-docs wrapper.",
5
5
  "keywords": [
6
6
  "claude",
@@ -36,6 +36,7 @@
36
36
  "module": "dist/index.mjs",
37
37
  "types": "dist/index.d.ts",
38
38
  "bin": {
39
+ "claude-assets-sync": "./bin/inject-claude-settings.mjs",
39
40
  "claude-build-hashes": "./scripts/claude-build-hashes.mjs",
40
41
  "inject-claude-settings": "./bin/inject-claude-settings.mjs"
41
42
  },