contract-driven-delivery 2.0.1 → 2.0.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.0.2] - 2026-04-30
4
+
5
+ ### Added
6
+
7
+ - `npm postinstall` hook: after `npm install -g` or `npm update -g`, skills and
8
+ agents in `~/.claude/` are automatically synced to the newly installed version.
9
+ The sync is a no-op when `cdd-kit init` has never been run (safe for CI and
10
+ local dev installs of the package itself). A backup of any locally modified
11
+ files is created in `~/.claude/.cdd-kit-backup/<timestamp>/` before overwriting,
12
+ matching the existing `cdd-kit update --yes` behaviour.
13
+ - `cdd-kit update --postinstall` flag (internal): quiet mode that implies
14
+ `--yes`, locks provider to `claude`, and silently exits when the skill
15
+ directory is absent. Not intended for direct user invocation.
16
+
17
+ ### Migration note
18
+
19
+ The first `npm update -g contract-driven-delivery` that brings in this version
20
+ will **not** auto-sync (the postinstall hook did not exist in the previously
21
+ installed version). Run `cdd-kit update --yes` once after this upgrade; all
22
+ subsequent upgrades will auto-sync.
23
+
3
24
  ## [2.0.1] - 2026-04-30
4
25
 
5
26
  ### Fixed
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from 'node:child_process';
3
+ import { existsSync } from 'node:fs';
4
+ import { join, dirname } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+
9
+ try {
10
+ const cliPath = join(__dirname, '..', 'dist', 'cli', 'index.js');
11
+ if (!existsSync(cliPath)) process.exit(0);
12
+
13
+ spawnSync(process.execPath, [cliPath, 'update', '--postinstall'], {
14
+ stdio: 'inherit',
15
+ timeout: 30_000,
16
+ });
17
+ process.exit(0);
18
+ } catch {
19
+ process.exit(0);
20
+ }
package/dist/cli/index.js CHANGED
@@ -11673,7 +11673,16 @@ function backupDir(dir, backupDest) {
11673
11673
  walk(dir, backupDest);
11674
11674
  }
11675
11675
  async function update(opts) {
11676
- log.blank();
11676
+ if (opts.postinstall) {
11677
+ if (!existsSync5(join6(SKILLS_HOME, "contract-driven-delivery"))) {
11678
+ return;
11679
+ }
11680
+ opts.yes = true;
11681
+ opts.provider = "claude";
11682
+ }
11683
+ const quiet = !!opts.postinstall;
11684
+ if (!quiet)
11685
+ log.blank();
11677
11686
  const cwd = process.cwd();
11678
11687
  const requestedProvider = opts.provider ?? "auto";
11679
11688
  if (!validateProviderOption(requestedProvider)) {
@@ -11689,28 +11698,32 @@ async function update(opts) {
11689
11698
  const toAdd = toWrite.filter((e) => e.action === "add");
11690
11699
  const toOver = toWrite.filter((e) => e.action === "overwrite");
11691
11700
  const toSkip = [...agentDiff, ...skillDiff].filter((e) => e.action === "skip");
11692
- log.info(`Provider: ${provider}`);
11693
- if (updateClaudeAssets) {
11694
- log.info(`Dry-run diff \u2014 agents: ${AGENTS_HOME}`);
11695
- log.info(`Dry-run diff \u2014 skill: ${skillDest}`);
11696
- } else {
11697
- log.info("Codex provider has no global cdd-kit assets to update.");
11698
- log.info("Project files are preserved; run cdd-kit init --local-only --provider codex to add missing local guidance.");
11701
+ if (!quiet) {
11702
+ log.info(`Provider: ${provider}`);
11703
+ if (updateClaudeAssets) {
11704
+ log.info(`Dry-run diff \u2014 agents: ${AGENTS_HOME}`);
11705
+ log.info(`Dry-run diff \u2014 skill: ${skillDest}`);
11706
+ } else {
11707
+ log.info("Codex provider has no global cdd-kit assets to update.");
11708
+ log.info("Project files are preserved; run cdd-kit init --local-only --provider codex to add missing local guidance.");
11709
+ }
11710
+ log.blank();
11711
+ if (toAdd.length)
11712
+ log.info(` + ${toAdd.length} file(s) would be added`);
11713
+ if (toOver.length)
11714
+ log.warn(` ~ ${toOver.length} file(s) would be overwritten (user edits lost without backup)`);
11715
+ if (toSkip.length)
11716
+ log.dim(` ${toSkip.length} file(s) unchanged (skipped)`);
11699
11717
  }
11700
- log.blank();
11701
- if (toAdd.length)
11702
- log.info(` + ${toAdd.length} file(s) would be added`);
11703
- if (toOver.length)
11704
- log.warn(` ~ ${toOver.length} file(s) would be overwritten (user edits lost without backup)`);
11705
- if (toSkip.length)
11706
- log.dim(` ${toSkip.length} file(s) unchanged (skipped)`);
11707
11718
  if (toWrite.length === 0) {
11708
- log.blank();
11709
- log.ok("Already up to date \u2014 nothing to write.");
11710
- log.blank();
11719
+ if (!quiet) {
11720
+ log.blank();
11721
+ log.ok("Already up to date \u2014 nothing to write.");
11722
+ log.blank();
11723
+ }
11711
11724
  return;
11712
11725
  }
11713
- if (!opts.yes) {
11726
+ if (!quiet && !opts.yes) {
11714
11727
  log.blank();
11715
11728
  log.info("Run with --yes to apply changes. Example:");
11716
11729
  log.dim(" cdd-kit update --yes");
@@ -11719,25 +11732,41 @@ async function update(opts) {
11719
11732
  }
11720
11733
  const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
11721
11734
  const backupRoot = join6(homedir2(), ".claude", ".cdd-kit-backup", timestamp2);
11722
- log.blank();
11723
- log.info(`Backing up to ${backupRoot} \u2026`);
11735
+ if (!quiet) {
11736
+ log.blank();
11737
+ log.info(`Backing up to ${backupRoot} \u2026`);
11738
+ }
11724
11739
  backupDir(AGENTS_HOME, join6(backupRoot, "agents"));
11725
11740
  backupDir(skillDest, join6(backupRoot, "skill"));
11726
- log.ok(`Backup complete: ${backupRoot}`);
11727
- log.blank();
11741
+ if (!quiet)
11742
+ log.ok(`Backup complete: ${backupRoot}`);
11743
+ if (!quiet)
11744
+ log.blank();
11745
+ let totalSynced = 0;
11728
11746
  if (updateClaudeAssets) {
11729
- log.info(`Updating agents \u2192 ${AGENTS_HOME}`);
11747
+ if (!quiet)
11748
+ log.info(`Updating agents \u2192 ${AGENTS_HOME}`);
11730
11749
  const agentCount = applyDir(agentDiff);
11731
- log.ok(`${agentCount} agent file(s) updated.`);
11732
- log.info(`Updating skill \u2192 ${skillDest}`);
11750
+ if (!quiet)
11751
+ log.ok(`${agentCount} agent file(s) updated.`);
11752
+ totalSynced += agentCount;
11753
+ if (!quiet)
11754
+ log.info(`Updating skill \u2192 ${skillDest}`);
11733
11755
  const skillCount = applyDir(skillDiff);
11734
- log.ok(`${skillCount} skill file(s) updated.`);
11756
+ if (!quiet)
11757
+ log.ok(`${skillCount} skill file(s) updated.`);
11758
+ totalSynced += skillCount;
11759
+ }
11760
+ if (quiet) {
11761
+ if (totalSynced > 0)
11762
+ log.ok(`cdd-kit: synced ${totalSynced} file(s) to ~/.claude/`);
11763
+ } else {
11764
+ log.blank();
11765
+ log.info("Project files (contracts/, specs/, tests/, ci/) were not changed.");
11766
+ log.ok("Update complete.");
11767
+ log.info(`Backup saved to: ${backupRoot}`);
11768
+ log.blank();
11735
11769
  }
11736
- log.blank();
11737
- log.info("Project files (contracts/, specs/, tests/, ci/) were not changed.");
11738
- log.ok("Update complete.");
11739
- log.info(`Backup saved to: ${backupRoot}`);
11740
- log.blank();
11741
11770
  }
11742
11771
 
11743
11772
  // src/commands/new-change.ts
@@ -12657,7 +12686,7 @@ program.command("init").description(
12657
12686
  provider: opts.provider
12658
12687
  })
12659
12688
  );
12660
- program.command("update").description("Update provider assets for the current project (does not overwrite project guidance files)").option("--yes", "Apply changes (default is dry-run)", false).option("--provider <provider>", "Provider adapter to update: auto, claude, codex, or both", "auto").action((opts) => update({ yes: opts.yes, provider: opts.provider }));
12689
+ program.command("update").description("Update provider assets for the current project (does not overwrite project guidance files)").option("--yes", "Apply changes (default is dry-run)", false).option("--provider <provider>", "Provider adapter to update: auto, claude, codex, or both", "auto").option("--postinstall", "Internal: invoked by npm postinstall; no-op if cdd has not been init-ed", false).action((opts) => update({ yes: opts.yes, provider: opts.provider, postinstall: opts.postinstall }));
12661
12690
  program.command("doctor").description("Inspect cdd-kit repo health, provider guidance, and context index freshness").option("--strict", "Treat warnings as errors", false).option("--json", "Print a machine-readable health report", false).option("--provider <provider>", "Provider adapter to inspect: auto, claude, codex, or both", "auto").option("--fix", "Auto-resolve safe warnings (stale context indexes, missing role bindings)", false).action(async (opts) => {
12662
12691
  const { doctor: doctor2 } = await Promise.resolve().then(() => (init_doctor(), doctor_exports));
12663
12692
  await doctor2({ strict: opts.strict, json: opts.json, provider: opts.provider, fix: opts.fix });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "contract-driven-delivery",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "description": "Contract-driven delivery kit for AI coding agents with deterministic context indexes, manifest-backed read-scope governance, and orchestrated contracts-first delivery.",
5
5
  "keywords": [
6
6
  "contract-driven",
@@ -40,7 +40,8 @@
40
40
  "build": "node build.js",
41
41
  "test": "vitest run",
42
42
  "test:watch": "vitest",
43
- "prepublishOnly": "node build.js && vitest run"
43
+ "prepublishOnly": "node build.js && vitest run",
44
+ "postinstall": "node bin/postinstall.js"
44
45
  },
45
46
  "engines": {
46
47
  "node": ">=18.0.0"