@usevalt/cli 0.6.0 → 0.7.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.
Files changed (2) hide show
  1. package/dist/index.js +94 -36
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -499,16 +499,36 @@ var doctorCommand = new Command5("doctor").description("Check Valt configuration
499
499
  warn("Signing keys: not configured (using ephemeral keys \u2014 certificates will not survive restart)");
500
500
  issues++;
501
501
  }
502
+ const EXPECTED_HOOKS = [
503
+ "SessionStart",
504
+ "PreToolUse",
505
+ "PostToolUse",
506
+ "PostToolUseFailure",
507
+ "SubagentStart",
508
+ "SubagentStop",
509
+ "UserPromptSubmit",
510
+ "SessionEnd"
511
+ ];
502
512
  const hooksPath = `${process.env["HOME"] ?? "~"}/.claude/hooks.json`;
503
513
  try {
504
514
  const { existsSync: exists, readFileSync: read } = await import("fs");
505
515
  if (exists(hooksPath)) {
506
516
  const hooksContent = read(hooksPath, "utf-8");
507
- const hookCount = (hooksContent.match(/valt/g) ?? []).length;
508
- if (hookCount >= 3) {
509
- success(`Claude Code hooks: ${dim(hooksPath)} (${hookCount} hooks)`);
517
+ const hooksConfig = JSON.parse(hooksContent);
518
+ const registeredHooks = hooksConfig.hooks ?? {};
519
+ const presentHooks = EXPECTED_HOOKS.filter((name) => {
520
+ const hookEntries = registeredHooks[name];
521
+ return Array.isArray(hookEntries) && JSON.stringify(hookEntries).includes("valt");
522
+ });
523
+ const missingHooks = EXPECTED_HOOKS.filter((h) => !presentHooks.includes(h));
524
+ if (missingHooks.length === 0) {
525
+ success(`Claude Code hooks: all ${EXPECTED_HOOKS.length} registered`);
526
+ } else if (presentHooks.length === 0) {
527
+ info("Claude Code hooks: not installed. Run `valt setup` to enable.");
510
528
  } else {
511
- warn("Claude Code hooks: partially configured. Run `valt setup`.");
529
+ warn(
530
+ `Claude Code hooks: ${presentHooks.length}/${EXPECTED_HOOKS.length} registered. Missing: ${missingHooks.join(", ")}. Run \`valt setup\` to update.`
531
+ );
512
532
  issues++;
513
533
  }
514
534
  } else {
@@ -1685,11 +1705,12 @@ var hookCommand = new Command13("hook").description("Claude Code hook handlers f
1685
1705
 
1686
1706
  // src/commands/setup.ts
1687
1707
  import { Command as Command14 } from "commander";
1688
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
1708
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync4, unlinkSync as unlinkSync3 } from "fs";
1689
1709
  import { join as join2 } from "path";
1690
1710
  import os3 from "os";
1691
1711
  var CLAUDE_DIR = join2(os3.homedir(), ".claude");
1692
- var HOOKS_FILE = join2(CLAUDE_DIR, "hooks.json");
1712
+ var SETTINGS_FILE = join2(CLAUDE_DIR, "settings.json");
1713
+ var LEGACY_HOOKS_FILE = join2(CLAUDE_DIR, "hooks.json");
1693
1714
  var HOOK_PREFIX = "npx --yes @usevalt/cli";
1694
1715
  function getValtHooks() {
1695
1716
  return {
@@ -1794,17 +1815,17 @@ var setupCommand = new Command14("setup").description("Configure Claude Code hoo
1794
1815
  removeHooks();
1795
1816
  return;
1796
1817
  }
1797
- let config = { hooks: {} };
1798
- if (existsSync4(HOOKS_FILE)) {
1818
+ let config = {};
1819
+ if (existsSync4(SETTINGS_FILE)) {
1799
1820
  try {
1800
- const raw = readFileSync4(HOOKS_FILE, "utf-8");
1821
+ const raw = readFileSync4(SETTINGS_FILE, "utf-8");
1801
1822
  config = JSON.parse(raw);
1802
- if (!config.hooks) config.hooks = {};
1803
1823
  } catch {
1804
- warn("Could not parse existing hooks.json. Creating new one.");
1805
- config = { hooks: {} };
1824
+ warn("Could not parse existing settings.json. Will merge hooks into it.");
1825
+ config = {};
1806
1826
  }
1807
1827
  }
1828
+ if (!config.hooks) config.hooks = {};
1808
1829
  const valtHooks = getValtHooks();
1809
1830
  for (const [event, hooks] of Object.entries(valtHooks)) {
1810
1831
  const existing = config.hooks[event];
@@ -1820,12 +1841,19 @@ var setupCommand = new Command14("setup").description("Configure Claude Code hoo
1820
1841
  config.hooks[event] = hooks;
1821
1842
  }
1822
1843
  }
1823
- writeFileSync4(HOOKS_FILE, JSON.stringify(config, null, 2) + "\n", "utf-8");
1844
+ writeFileSync4(SETTINGS_FILE, JSON.stringify(config, null, 2) + "\n", "utf-8");
1845
+ if (existsSync4(LEGACY_HOOKS_FILE)) {
1846
+ try {
1847
+ removeLegacyHooks();
1848
+ info(`Migrated hooks from legacy hooks.json to settings.json.`);
1849
+ } catch {
1850
+ }
1851
+ }
1824
1852
  success("Claude Code hooks configured.");
1825
1853
  info("");
1826
1854
  info("Every Claude Code session will now be tracked by Valt.");
1827
1855
  info(`Projects are auto-detected from git repo name or folder name.`);
1828
- info(`Hooks written to: ${dim(HOOKS_FILE)}`);
1856
+ info(`Hooks written to: ${dim(SETTINGS_FILE)}`);
1829
1857
  info("");
1830
1858
  info("To remove: valt setup --remove");
1831
1859
  } catch (err) {
@@ -1833,34 +1861,64 @@ var setupCommand = new Command14("setup").description("Configure Claude Code hoo
1833
1861
  process.exit(1);
1834
1862
  }
1835
1863
  });
1836
- function removeHooks() {
1837
- if (!existsSync4(HOOKS_FILE)) {
1838
- info("No hooks.json found. Nothing to remove.");
1839
- return;
1864
+ function removeValtHooksFromConfig(config) {
1865
+ if (!config.hooks) return;
1866
+ for (const event of Object.keys(config.hooks)) {
1867
+ const entries = config.hooks[event];
1868
+ if (!Array.isArray(entries)) continue;
1869
+ config.hooks[event] = entries.filter((h) => {
1870
+ const entry = h;
1871
+ return !entry.hooks?.some((hk) => hk.command?.includes("valt") && hk.command?.includes("hook"));
1872
+ });
1873
+ if (config.hooks[event].length === 0) {
1874
+ delete config.hooks[event];
1875
+ }
1876
+ }
1877
+ if (Object.keys(config.hooks).length === 0) {
1878
+ delete config.hooks;
1840
1879
  }
1880
+ }
1881
+ function removeLegacyHooks() {
1882
+ if (!existsSync4(LEGACY_HOOKS_FILE)) return;
1841
1883
  try {
1842
- const raw = readFileSync4(HOOKS_FILE, "utf-8");
1884
+ const raw = readFileSync4(LEGACY_HOOKS_FILE, "utf-8");
1843
1885
  const config = JSON.parse(raw);
1844
- if (!config.hooks) {
1845
- info("No hooks configured. Nothing to remove.");
1846
- return;
1847
- }
1848
- for (const event of Object.keys(config.hooks)) {
1849
- const entries = config.hooks[event];
1850
- if (!Array.isArray(entries)) continue;
1851
- config.hooks[event] = entries.filter((h) => {
1852
- const entry = h;
1853
- return !entry.hooks?.some((hk) => hk.command?.includes("valt") && hk.command?.includes("hook"));
1854
- });
1855
- if (config.hooks[event].length === 0) {
1856
- delete config.hooks[event];
1886
+ removeValtHooksFromConfig(config);
1887
+ if (!config.hooks || Object.keys(config.hooks).length === 0) {
1888
+ const remaining = Object.keys(config).filter((k) => k !== "hooks");
1889
+ if (remaining.length === 0) {
1890
+ unlinkSync3(LEGACY_HOOKS_FILE);
1891
+ } else {
1892
+ writeFileSync4(LEGACY_HOOKS_FILE, JSON.stringify(config, null, 2) + "\n", "utf-8");
1857
1893
  }
1894
+ } else {
1895
+ writeFileSync4(LEGACY_HOOKS_FILE, JSON.stringify(config, null, 2) + "\n", "utf-8");
1858
1896
  }
1859
- writeFileSync4(HOOKS_FILE, JSON.stringify(config, null, 2) + "\n", "utf-8");
1860
- success("Valt hooks removed from Claude Code.");
1861
1897
  } catch {
1862
- error("Could not parse hooks.json.");
1863
- process.exit(1);
1898
+ }
1899
+ }
1900
+ function removeHooks() {
1901
+ if (existsSync4(SETTINGS_FILE)) {
1902
+ try {
1903
+ const raw = readFileSync4(SETTINGS_FILE, "utf-8");
1904
+ const config = JSON.parse(raw);
1905
+ removeValtHooksFromConfig(config);
1906
+ writeFileSync4(SETTINGS_FILE, JSON.stringify(config, null, 2) + "\n", "utf-8");
1907
+ success("Valt hooks removed from Claude Code settings.");
1908
+ } catch {
1909
+ error("Could not parse settings.json.");
1910
+ process.exit(1);
1911
+ }
1912
+ }
1913
+ if (existsSync4(LEGACY_HOOKS_FILE)) {
1914
+ try {
1915
+ removeLegacyHooks();
1916
+ info("Legacy hooks.json cleaned up.");
1917
+ } catch {
1918
+ }
1919
+ }
1920
+ if (!existsSync4(SETTINGS_FILE) && !existsSync4(LEGACY_HOOKS_FILE)) {
1921
+ info("No hooks configured. Nothing to remove.");
1864
1922
  }
1865
1923
  }
1866
1924
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usevalt/cli",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Valt CLI — trust layer for AI-assisted development",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -39,8 +39,8 @@
39
39
  "tsup": "^8.4.0",
40
40
  "typescript": "^5.7.0",
41
41
  "vitest": "^3.2.0",
42
- "@usevalt/eslint-config": "0.0.0",
43
- "@usevalt/typescript-config": "0.0.0"
42
+ "@usevalt/typescript-config": "0.0.0",
43
+ "@usevalt/eslint-config": "0.0.0"
44
44
  },
45
45
  "scripts": {
46
46
  "build": "tsup",