claude-master-toolkit 0.1.2 → 0.1.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.
Files changed (2) hide show
  1. package/dist/cli.js +313 -3
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -679,8 +679,8 @@ function gitChangedCommand() {
679
679
 
680
680
  // src/cli/commands/find.ts
681
681
  import { execFileSync as execFileSync2 } from "child_process";
682
- function findCommand(query, path) {
683
- const searchPath = path ?? ".";
682
+ function findCommand(query, path3) {
683
+ const searchPath = path3 ?? ".";
684
684
  try {
685
685
  const result = execFileSync2(
686
686
  "rg",
@@ -1751,14 +1751,324 @@ async function dashboardCommand(options) {
1751
1751
  }
1752
1752
  }
1753
1753
 
1754
+ // src/cli/commands/install.ts
1755
+ import fs from "fs";
1756
+ import path from "path";
1757
+ import os from "os";
1758
+ import { execSync as execSync3 } from "child_process";
1759
+ var REPO_DIR = process.cwd();
1760
+ var CLAUDE_DIR = path.join(os.homedir(), ".claude");
1761
+ function say(msg) {
1762
+ console.log(" " + msg);
1763
+ }
1764
+ function head(msg) {
1765
+ console.log("\n== " + msg + " ==\n");
1766
+ }
1767
+ function die(msg) {
1768
+ console.error("\u2717 " + msg);
1769
+ process.exit(1);
1770
+ throw new Error(msg);
1771
+ }
1772
+ function run(cmd) {
1773
+ execSync3(cmd, { stdio: "inherit", cwd: REPO_DIR });
1774
+ }
1775
+ function linkForce(target, link, type) {
1776
+ fs.mkdirSync(path.dirname(link), { recursive: true });
1777
+ if (fs.existsSync(link) || fs.lstatSync(link, { throwIfNoEntry: false })) {
1778
+ fs.unlinkSync(link);
1779
+ }
1780
+ fs.symlinkSync(target, link, type);
1781
+ }
1782
+ async function installClaudeCommand(opts) {
1783
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
1784
+ if (!fs.existsSync(CLAUDE_DIR)) {
1785
+ die("~/.claude not found \u2014 is Claude Code installed?");
1786
+ }
1787
+ const nodeMajor = Number(process.version.split(".")[0].replace("v", ""));
1788
+ if (nodeMajor < 18) die("Node.js 18+ required");
1789
+ const backupDir = path.join(CLAUDE_DIR, "backups", `cmt-${timestamp}`);
1790
+ head("1. Backup");
1791
+ fs.mkdirSync(backupDir, { recursive: true });
1792
+ const claudeMdPath = path.join(CLAUDE_DIR, "CLAUDE.md");
1793
+ if (fs.existsSync(claudeMdPath) && !fs.lstatSync(claudeMdPath).isSymbolicLink()) {
1794
+ fs.copyFileSync(claudeMdPath, path.join(backupDir, "CLAUDE.md"));
1795
+ say("backed up CLAUDE.md");
1796
+ }
1797
+ const settingsPath = path.join(CLAUDE_DIR, "settings.json");
1798
+ if (fs.existsSync(settingsPath)) {
1799
+ fs.copyFileSync(settingsPath, path.join(backupDir, "settings.json"));
1800
+ say("backed up settings.json");
1801
+ }
1802
+ fs.writeFileSync(path.join(backupDir, "repo-path"), REPO_DIR);
1803
+ if (!opts.skipDeps) {
1804
+ head("2. Install dependencies");
1805
+ run("npm install --legacy-peer-deps");
1806
+ say("dependencies installed");
1807
+ }
1808
+ if (!opts.skipBuild) {
1809
+ head("3. Build");
1810
+ run("npm run build");
1811
+ say("build complete");
1812
+ }
1813
+ head("4. Database migrate");
1814
+ run("npx tsx src/server/db/migrate.ts");
1815
+ say("database migrated");
1816
+ head("5. Sync data");
1817
+ const syncScript = `
1818
+ import { syncAll } from './src/server/parser/sync.ts';
1819
+ import { importAllMemories } from './src/server/parser/engram-import.ts';
1820
+
1821
+ (async () => {
1822
+ const sync = await syncAll();
1823
+ const mem = await importAllMemories();
1824
+ console.log(sync.files + ' files, ' + sync.sessions + ' sessions, ' + mem.imported + ' memories');
1825
+ })();
1826
+ `;
1827
+ const tmpFile = path.join(REPO_DIR, ".tmp-sync.ts");
1828
+ fs.writeFileSync(tmpFile, syncScript);
1829
+ try {
1830
+ const output2 = execSync3(`npx tsx ${tmpFile}`, {
1831
+ cwd: REPO_DIR,
1832
+ encoding: "utf-8"
1833
+ });
1834
+ say("synced: " + output2.trim());
1835
+ } catch (e) {
1836
+ fs.unlinkSync(tmpFile);
1837
+ die("sync failed");
1838
+ }
1839
+ fs.unlinkSync(tmpFile);
1840
+ head("6. Symlink CLAUDE.md + hooks");
1841
+ const dist = path.join(REPO_DIR, "claude-dist");
1842
+ linkForce(path.join(dist, "CLAUDE.md"), claudeMdPath, "file");
1843
+ for (const hook of ["session-start.sh", "user-prompt-submit.sh"]) {
1844
+ const src = path.join(dist, "hooks", hook);
1845
+ fs.chmodSync(src, 493);
1846
+ linkForce(src, path.join(CLAUDE_DIR, "hooks", hook), "file");
1847
+ }
1848
+ say("linked CLAUDE.md + hooks (2)");
1849
+ head("7. Symlink skills + agents");
1850
+ const skills = ["sdd-new", "sdd-ff", "sdd-continue", "engram-protocol", "delegate"];
1851
+ for (const skill of skills) {
1852
+ linkForce(
1853
+ path.join(dist, "skills", skill),
1854
+ path.join(CLAUDE_DIR, "skills", skill),
1855
+ "dir"
1856
+ );
1857
+ }
1858
+ linkForce(
1859
+ path.join(dist, "agents", "sdd-orchestrator.md"),
1860
+ path.join(CLAUDE_DIR, "agents", "sdd-orchestrator.md"),
1861
+ "file"
1862
+ );
1863
+ say(`linked skills (${skills.length}) + agents (1)`);
1864
+ head("8. Merge settings");
1865
+ const patchPath = path.join(dist, "settings.patch.json");
1866
+ let base = {};
1867
+ if (fs.existsSync(settingsPath)) {
1868
+ base = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
1869
+ }
1870
+ const patch = JSON.parse(fs.readFileSync(patchPath, "utf-8"));
1871
+ const merged = { ...base, ...patch };
1872
+ fs.writeFileSync(settingsPath, JSON.stringify(merged, null, 2));
1873
+ say("settings merged");
1874
+ head("9. Model preference");
1875
+ const stateDir = path.join(CLAUDE_DIR, "state", "claude-master-toolkit");
1876
+ fs.mkdirSync(stateDir, { recursive: true });
1877
+ const prefFile = path.join(stateDir, "model-preference");
1878
+ if (!fs.existsSync(prefFile)) {
1879
+ fs.writeFileSync(prefFile, "inherit\n");
1880
+ say("model preference \u2192 inherit");
1881
+ } else {
1882
+ say("model preference already set: " + fs.readFileSync(prefFile, "utf-8").trim());
1883
+ }
1884
+ head("10. PATH setup");
1885
+ const binCtk = path.join(REPO_DIR, "bin", "ctk.js");
1886
+ if (fs.existsSync(binCtk)) fs.chmodSync(binCtk, 493);
1887
+ const bashrc = path.join(os.homedir(), ".bashrc");
1888
+ const markerStart = "# >>> claude-master-toolkit >>>";
1889
+ const markerEnd = "# <<< claude-master-toolkit <<<";
1890
+ if (fs.existsSync(bashrc)) {
1891
+ const content = fs.readFileSync(bashrc, "utf-8");
1892
+ if (!content.includes(markerStart)) {
1893
+ fs.appendFileSync(
1894
+ bashrc,
1895
+ `
1896
+ ${markerStart}
1897
+ export PATH="${REPO_DIR}/bin:$PATH"
1898
+ ${markerEnd}
1899
+ `
1900
+ );
1901
+ say("PATH added to ~/.bashrc");
1902
+ } else {
1903
+ say("PATH already configured");
1904
+ }
1905
+ }
1906
+ head("11. Caveman plugin");
1907
+ try {
1908
+ execSync3("command -v claude", { stdio: "ignore" });
1909
+ try {
1910
+ execSync3("claude plugin marketplace add JuliusBrussee/caveman", { stdio: "ignore" });
1911
+ execSync3("claude plugin install caveman@caveman", { stdio: "ignore" });
1912
+ say("caveman plugin configured");
1913
+ } catch {
1914
+ say("caveman install skipped (already installed or failed)");
1915
+ }
1916
+ } catch {
1917
+ say("caveman skipped (claude CLI not found)");
1918
+ }
1919
+ head("\u2713 Installation complete");
1920
+ console.log(`
1921
+ Repo: ${REPO_DIR}
1922
+ Backup: ${backupDir}
1923
+ Config: ~/.claude (symlinked)
1924
+ State: ${stateDir}
1925
+
1926
+ Next:
1927
+ source ~/.bashrc
1928
+ ctk --help
1929
+ ctk dashboard
1930
+ `);
1931
+ }
1932
+
1933
+ // src/cli/commands/uninstall.ts
1934
+ import fs2 from "fs";
1935
+ import path2 from "path";
1936
+ import os2 from "os";
1937
+ import { execSync as execSync4 } from "child_process";
1938
+ var REPO_DIR2 = process.cwd();
1939
+ var CLAUDE_DIR2 = path2.join(os2.homedir(), ".claude");
1940
+ function say2(msg) {
1941
+ console.log(" " + msg);
1942
+ }
1943
+ function head2(msg) {
1944
+ console.log("\n== " + msg + " ==\n");
1945
+ }
1946
+ function removeSymlink(p) {
1947
+ const stat = fs2.lstatSync(p, { throwIfNoEntry: false });
1948
+ if (stat && stat.isSymbolicLink()) {
1949
+ fs2.unlinkSync(p);
1950
+ return true;
1951
+ }
1952
+ return false;
1953
+ }
1954
+ async function uninstallClaudeCommand(opts) {
1955
+ head2("1. Remove symlinks");
1956
+ const links = [
1957
+ path2.join(CLAUDE_DIR2, "CLAUDE.md"),
1958
+ path2.join(CLAUDE_DIR2, "hooks", "session-start.sh"),
1959
+ path2.join(CLAUDE_DIR2, "hooks", "user-prompt-submit.sh"),
1960
+ path2.join(CLAUDE_DIR2, "skills", "sdd-new"),
1961
+ path2.join(CLAUDE_DIR2, "skills", "sdd-ff"),
1962
+ path2.join(CLAUDE_DIR2, "skills", "sdd-continue"),
1963
+ path2.join(CLAUDE_DIR2, "skills", "engram-protocol"),
1964
+ path2.join(CLAUDE_DIR2, "skills", "delegate"),
1965
+ path2.join(CLAUDE_DIR2, "agents", "sdd-orchestrator.md")
1966
+ ];
1967
+ let removed = 0;
1968
+ for (const link of links) if (removeSymlink(link)) removed++;
1969
+ say2(`removed symlinks (${removed}/${links.length})`);
1970
+ head2("2. Clean database + state");
1971
+ const stateDir = path2.join(CLAUDE_DIR2, "state", "claude-master-toolkit");
1972
+ if (fs2.existsSync(stateDir)) {
1973
+ for (const f of ["ctk.sqlite", "ctk.sqlite-journal", "model-preference"]) {
1974
+ const p = path2.join(stateDir, f);
1975
+ if (fs2.existsSync(p)) fs2.unlinkSync(p);
1976
+ }
1977
+ say2("cleaned state dir");
1978
+ } else {
1979
+ say2("no state dir found");
1980
+ }
1981
+ head2("3. Restore latest backup");
1982
+ const backupsDir = path2.join(CLAUDE_DIR2, "backups");
1983
+ if (fs2.existsSync(backupsDir)) {
1984
+ const candidates = fs2.readdirSync(backupsDir).filter((n) => n.startsWith("cmt-")).sort();
1985
+ const latest = candidates[candidates.length - 1];
1986
+ if (latest) {
1987
+ const dir = path2.join(backupsDir, latest);
1988
+ for (const f of ["CLAUDE.md", "settings.json"]) {
1989
+ const src = path2.join(dir, f);
1990
+ if (fs2.existsSync(src)) {
1991
+ fs2.copyFileSync(src, path2.join(CLAUDE_DIR2, f));
1992
+ say2(`restored ${f}`);
1993
+ }
1994
+ }
1995
+ } else {
1996
+ say2("no backup found");
1997
+ }
1998
+ } else {
1999
+ say2("no backups dir");
2000
+ }
2001
+ head2("4. Remove PATH block from ~/.bashrc");
2002
+ const bashrc = path2.join(os2.homedir(), ".bashrc");
2003
+ const markerStart = "# >>> claude-master-toolkit >>>";
2004
+ const markerEnd = "# <<< claude-master-toolkit <<<";
2005
+ if (fs2.existsSync(bashrc)) {
2006
+ const content = fs2.readFileSync(bashrc, "utf-8");
2007
+ const lines = content.split("\n");
2008
+ const si = lines.findIndex((l) => l.includes(markerStart));
2009
+ const ei = lines.findIndex((l) => l.includes(markerEnd));
2010
+ if (si !== -1 && ei !== -1 && ei >= si) {
2011
+ lines.splice(si, ei - si + 1);
2012
+ fs2.writeFileSync(bashrc, lines.join("\n"));
2013
+ say2("removed PATH block");
2014
+ } else {
2015
+ say2("no PATH marker found");
2016
+ }
2017
+ }
2018
+ head2("5. node_modules");
2019
+ if (opts.cleanModules) {
2020
+ const nm = path2.join(REPO_DIR2, "node_modules");
2021
+ const lock = path2.join(REPO_DIR2, "package-lock.json");
2022
+ if (fs2.existsSync(nm)) {
2023
+ fs2.rmSync(nm, { recursive: true, force: true });
2024
+ say2("removed node_modules");
2025
+ }
2026
+ if (fs2.existsSync(lock)) {
2027
+ fs2.unlinkSync(lock);
2028
+ say2("removed package-lock.json");
2029
+ }
2030
+ } else {
2031
+ say2("kept (use --clean-modules to remove)");
2032
+ }
2033
+ head2("6. Caveman plugin");
2034
+ if (opts.removeCaveman) {
2035
+ try {
2036
+ execSync4("command -v claude", { stdio: "ignore" });
2037
+ try {
2038
+ execSync4("claude plugin uninstall caveman@caveman", { stdio: "ignore" });
2039
+ say2("caveman uninstalled");
2040
+ } catch {
2041
+ say2("caveman not installed or failed");
2042
+ }
2043
+ } catch {
2044
+ say2("claude CLI not found");
2045
+ }
2046
+ } else {
2047
+ say2("kept (use --remove-caveman to remove)");
2048
+ }
2049
+ head2("\u2713 Uninstalled");
2050
+ console.log(`
2051
+ claude-master-toolkit removed.
2052
+
2053
+ Backups: ${path2.join(CLAUDE_DIR2, "backups")}
2054
+ Reinstall: ctk install claude
2055
+
2056
+ Reload shell: source ~/.bashrc
2057
+ `);
2058
+ }
2059
+
1754
2060
  // src/cli/index.ts
1755
- var VERSION = "0.1.2";
2061
+ var VERSION = "0.1.3";
1756
2062
  var program = new Command().name("ctk").description(
1757
2063
  "Claude Master Toolkit \u2014 token-efficient CLI + metrics dashboard"
1758
2064
  ).version(VERSION).option("--json", "Output in JSON format (AI-friendly)").hook("preAction", (thisCommand) => {
1759
2065
  const opts = thisCommand.opts();
1760
2066
  if (opts["json"]) setJsonMode(true);
1761
2067
  });
2068
+ var install = program.command("install").description("Install integration stacks (claude, cursor, ...)");
2069
+ install.command("claude").description("Install Claude Code stack into ~/.claude").option("--skip-build", "Skip npm build step").option("--skip-deps", "Skip npm install step").action(installClaudeCommand);
2070
+ var uninstall = program.command("uninstall").description("Uninstall integration stacks");
2071
+ uninstall.command("claude").description("Uninstall Claude Code stack from ~/.claude").option("--clean-modules", "Also remove node_modules + package-lock.json").option("--remove-caveman", "Also uninstall caveman plugin").action(uninstallClaudeCommand);
1762
2072
  program.command("slice <file> <symbol>").description("Extract a symbol block from a file (function/class/type)").action(sliceCommand);
1763
2073
  program.command("model <phase>").description("Print model alias for an SDD phase (respects user preference)").action(modelCommand);
1764
2074
  program.command("model-pref [action] [value]").description("Get/set/clear model selection preference").action(modelPrefCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-master-toolkit",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Token-efficient CLI + metrics dashboard for Claude Code. Measure real cost, cut context bloat, and keep sessions cheap.",
5
5
  "type": "module",
6
6
  "bin": {