claude-launchpad 0.2.0 → 0.2.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/README.md CHANGED
@@ -13,14 +13,14 @@ npx claude-launchpad
13
13
  ## The Workflow
14
14
 
15
15
  ```bash
16
- npx claude-launchpad init # 1. Auto-detect stack, generate config + hooks
17
- npx claude-launchpad enhance # 2. Claude reads your code, completes CLAUDE.md
18
- npx claude-launchpad # 3. Check your score (42%)
16
+ npx claude-launchpad init # 1. Auto-detect stack, generate config + hooks + .claudeignore
17
+ npx claude-launchpad enhance # 2. Claude reads your code, completes CLAUDE.md
18
+ npx claude-launchpad # 3. Check your score (42%)
19
19
  npx claude-launchpad doctor --fix # 4. Auto-fix everything (→ 86%)
20
- npx claude-launchpad # 5. Confirm the jump
20
+ npx claude-launchpad eval # 5. Prove your config works (89% eval score)
21
21
  ```
22
22
 
23
- > See the [full story on the landing page](https://mboss37.github.io/claude-launchpad/) — a 42% → 86% journey in three commands.
23
+ > See the [full story on the landing page](https://mboss37.github.io/claude-launchpad/) — a 42% → 89% journey.
24
24
 
25
25
  ## Commands
26
26
 
@@ -31,32 +31,32 @@ Runs 7 static analyzers against your `.claude/` directory and `CLAUDE.md`. No AP
31
31
  ```
32
32
  Instruction Budget ━━━━━━━━━━━━━━━━━━━━ 100%
33
33
  CLAUDE.md Quality ━━━━━━━━━━━━━━━━━━━━ 100%
34
- Settings ━━━━━━━━━━━━━━━━━─── 85%
34
+ Settings ━━━━━━━━━━━━━━━━━━━━ 100%
35
35
  Hooks ━━━━━━━━━━━━━━━━━━━━ 100%
36
- Rules ━━━━━━━━━━━━──────── 60%
37
- Permissions ━━━━━━━━━━━━━━━━──── 80%
36
+ Rules ━━━━━━━━━━━━━━━━━━━━ 100%
37
+ Permissions ━━━━━━━━━━━━━━━━━━━━ 100%
38
38
  MCP Servers ━━━━━━━━━━────────── 50%
39
39
 
40
- Overall ━━━━━━━━━━━━━━━━──── 82%
40
+ Overall ━━━━━━━━━━━━━━━━━━━─ 93%
41
41
 
42
- MEDIUM Hooks
43
- No .env file protection hook
44
- Fix: Add a PreToolUse hook that blocks writes to .env files
45
-
46
- LOW Permissions
47
- No force-push protection hook
48
- Fix: Add a PreToolUse hook that warns on `git push --force` commands
42
+ ✓ No issues found. Your configuration looks solid.
49
43
  ```
50
44
 
51
45
  Running bare `claude-launchpad` with no subcommand auto-detects your config and runs doctor.
52
46
 
47
+ **Flags:**
48
+ - `--fix` — Auto-apply deterministic fixes (42% → 86% in one command)
49
+ - `--watch` — Live score that updates every time you save a config file
50
+ - `--json` — JSON output for programmatic use
51
+ - `--min-score <n>` — Exit non-zero if score drops below threshold (for CI)
52
+
53
53
  **What it checks:**
54
54
 
55
55
  | Analyzer | What it catches |
56
56
  |---|---|
57
57
  | **Instruction Budget** | Are you over the ~150 instruction limit where Claude starts ignoring rules? |
58
58
  | **CLAUDE.md Quality** | Missing essential sections, vague instructions ("write good code"), hardcoded secrets |
59
- | **Settings** | Plugin config, permission rules, environment variables |
59
+ | **Settings** | Hooks configured, dangerous tool access without safety nets |
60
60
  | **Hooks** | Missing auto-format, no .env protection, no PreToolUse security gates |
61
61
  | **Rules** | Dead rule files, stale references, empty configs |
62
62
  | **Permissions** | Bash auto-allowed without security hooks, no force-push protection |
@@ -80,6 +80,7 @@ claude-launchpad init
80
80
  ✓ Generated CLAUDE.md
81
81
  ✓ Generated TASKS.md
82
82
  ✓ Generated .claude/settings.json (with hooks)
83
+ ✓ Generated .claudeignore
83
84
  ```
84
85
 
85
86
  **Detects 13 languages:** TypeScript, JavaScript, Python, Go, Ruby, Rust, Dart, PHP, Java, Kotlin, Swift, Elixir, C#
@@ -91,7 +92,8 @@ claude-launchpad init
91
92
  **What you get:**
92
93
  - `CLAUDE.md` with your detected stack, commands, and essential sections
93
94
  - `TASKS.md` for session continuity across Claude sessions
94
- - `.claude/settings.json` with auto-format hooks and .env file protection
95
+ - `.claude/settings.json` with auto-format hooks and .env file protection (merges with existing)
96
+ - `.claudeignore` with language-specific ignore patterns (node_modules, __pycache__, dist, etc.)
95
97
 
96
98
  ### `enhance` — Let Claude finish what init started
97
99
 
@@ -109,23 +111,30 @@ Claude opens, reads your code, and updates CLAUDE.md with:
109
111
 
110
112
  You see Claude working in real-time — same experience as running `claude` yourself.
111
113
 
112
- ### `eval` — Test your config (coming soon)
114
+ ### `eval` — Prove your config works
113
115
 
114
- Runs Claude headless against reproducible scenarios and **scores how well your config actually works**.
116
+ Runs Claude headless against 9 reproducible scenarios using the [Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk) and **scores how well your config actually drives correct behavior**.
115
117
 
116
118
  ```bash
117
- claude-launchpad eval --suite security
119
+ claude-launchpad eval --suite common
118
120
  ```
119
121
 
120
122
  ```
121
123
  ✓ security/sql-injection 10/10 PASS
122
- ✓ security/env-file-protection 10/10 PASS
123
- conventions/error-handling 7/10 WARN
124
- conventions/file-size 4/10 FAIL
125
-
126
- Config Eval Score ━━━━━━━━━━━━━━━━──── 78%
124
+ ✓ security/env-protection 10/10 PASS
125
+ security/secret-exposure 10/10 PASS
126
+ security/input-validation 10/10 PASS
127
+ ✓ conventions/error-handling 10/10 PASS
128
+ conventions/immutability 10/10 PASS
129
+ ✓ conventions/no-hardcoded-values 10/10 PASS
130
+ ✓ conventions/file-size 10/10 PASS
131
+ ✗ workflow/git-conventions 7/10 WARN
132
+
133
+ Config Eval Score ━━━━━━━━━━━━━━━━━━── 89%
127
134
  ```
128
135
 
136
+ Each scenario creates an isolated sandbox, runs Claude with explicit tool permissions, and verifies the output with grep/file assertions. [Write your own scenarios](scenarios/CONTRIBUTING.md) in YAML.
137
+
129
138
  This is the part nobody else has built. Template repos scaffold. Audit tools diagnose. **Nobody tests whether your config actually makes Claude better.** Until now.
130
139
 
131
140
  ## Use in CI
@@ -148,7 +157,7 @@ jobs:
148
157
  - run: npx claude-launchpad@latest doctor --min-score 80 --json
149
158
  ```
150
159
 
151
- Exit code is 1 when score is below the threshold, 0 when it passes. The workflow only triggers when Claude Code config files change.
160
+ Exit code is 1 when score is below the threshold, 0 when it passes.
152
161
 
153
162
  ## Install as a Plugin
154
163
 
@@ -156,7 +165,7 @@ Exit code is 1 when score is below the threshold, 0 when it passes. The workflow
156
165
  claude plugin install claude-launchpad
157
166
  ```
158
167
 
159
- Then use `/doctor` and `/init` directly inside Claude Code.
168
+ Then use `/launchpad:doctor`, `/launchpad:init`, `/launchpad:enhance`, and `/launchpad:eval` directly inside Claude Code. The plugin also nudges you to re-check your score when you edit config files.
160
169
 
161
170
  ## Why this exists
162
171
 
@@ -174,10 +183,11 @@ Claude Launchpad gives you a number. Fix the issues, re-run, watch the number go
174
183
  - **Zero dependencies on third-party Claude plugins.** Generates its own hooks and settings.
175
184
  - **Doctor is free.** No API calls, no secrets, works offline and air-gapped.
176
185
  - **Enhance uses Claude.** Spawns an interactive session to understand your codebase — costs tokens but produces a CLAUDE.md that actually knows your project.
177
- - **Eval costs money.** Runs Claude headless against scenarios — proof that your config works.
186
+ - **Eval uses the Agent SDK.** Runs Claude headless in sandboxes with explicit tool permissions — proof that your config works.
178
187
  - **Works with any stack.** Auto-detects your project. No fixed menu of supported frameworks.
188
+ - **50 tests.** The tool that tests configs is itself well-tested.
179
189
  - **You never clone this repo.** It's a tool you run with `npx`, not a template you fork.
180
190
 
181
191
  ## License
182
192
 
183
- MIT
193
+ MIT — Built by [McLovin](https://github.com/mboss37) (the AI behind [@mboss37](https://github.com/mboss37))
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@
3
3
  // src/cli.ts
4
4
  import { Command as Command5 } from "commander";
5
5
  import { access as access7 } from "fs/promises";
6
- import { join as join8 } from "path";
6
+ import { join as join9 } from "path";
7
7
 
8
8
  // src/commands/init/index.ts
9
9
  import { Command } from "commander";
@@ -947,34 +947,34 @@ async function analyzeSettings(config) {
947
947
  });
948
948
  return { name: "Settings", issues, score: 40 };
949
949
  }
950
- const plugins = config.settings.enabledPlugins;
951
- if (!plugins || Object.keys(plugins).length === 0) {
950
+ const hooks = config.settings.hooks;
951
+ if (!hooks || Object.keys(hooks).length === 0) {
952
952
  issues.push({
953
953
  analyzer: "Settings",
954
- severity: "low",
955
- message: "No plugins enabled",
956
- fix: "Consider enabling plugins for enhanced capabilities"
954
+ severity: "medium",
955
+ message: "settings.json has no hooks configured",
956
+ fix: "Run `claude-launchpad doctor --fix` to generate hooks"
957
957
  });
958
958
  }
959
- const permissions = config.settings.permissions;
960
- if (!permissions) {
959
+ const plugins = config.settings.enabledPlugins;
960
+ if (!plugins || Object.keys(plugins).length === 0) {
961
961
  issues.push({
962
962
  analyzer: "Settings",
963
- severity: "low",
964
- message: "No permission rules configured",
965
- fix: "Add permission rules to control which tools Claude can use automatically"
963
+ severity: "info",
964
+ message: "No plugins enabled \u2014 plugins are optional but can add capabilities"
966
965
  });
967
966
  }
968
- const env = config.settings.env;
969
- if (!env) {
967
+ const allowedTools = config.settings.allowedTools;
968
+ if (allowedTools && allowedTools.length > 0 && config.hooks.length === 0) {
970
969
  issues.push({
971
970
  analyzer: "Settings",
972
- severity: "info",
973
- message: "No environment variables defined in settings",
974
- fix: "Define env vars in settings.json for consistent development environments"
971
+ severity: "medium",
972
+ message: "Tools auto-allowed without any hooks \u2014 no safety net for dangerous operations",
973
+ fix: "Add PreToolUse hooks for security or remove allowedTools to use interactive prompting"
975
974
  });
976
975
  }
977
- const score = Math.max(0, 100 - issues.length * 15);
976
+ const actionableCount = issues.filter((i) => i.severity !== "info").length;
977
+ const score = Math.max(0, 100 - actionableCount * 20);
978
978
  return { name: "Settings", issues, score };
979
979
  }
980
980
 
@@ -1440,9 +1440,93 @@ async function writeSettingsJson(root, settings) {
1440
1440
  await writeFile2(join4(dir, "settings.json"), JSON.stringify(settings, null, 2) + "\n");
1441
1441
  }
1442
1442
 
1443
+ // src/commands/doctor/watcher.ts
1444
+ import { watch } from "fs";
1445
+ import { join as join5 } from "path";
1446
+ async function watchConfig(projectRoot) {
1447
+ const claudeDir = join5(projectRoot, ".claude");
1448
+ const claudeMd = join5(projectRoot, "CLAUDE.md");
1449
+ const claudeignore = join5(projectRoot, ".claudeignore");
1450
+ await runAndDisplay(projectRoot);
1451
+ log.blank();
1452
+ log.info("Watching for changes... (Ctrl+C to stop)");
1453
+ log.blank();
1454
+ let debounce = null;
1455
+ const onChange = () => {
1456
+ if (debounce) clearTimeout(debounce);
1457
+ debounce = setTimeout(async () => {
1458
+ console.clear();
1459
+ await runAndDisplay(projectRoot);
1460
+ log.blank();
1461
+ log.info("Watching for changes... (Ctrl+C to stop)");
1462
+ log.blank();
1463
+ }, 300);
1464
+ };
1465
+ try {
1466
+ watch(claudeDir, { recursive: true }, onChange);
1467
+ } catch {
1468
+ }
1469
+ try {
1470
+ watch(claudeMd, onChange);
1471
+ } catch {
1472
+ }
1473
+ try {
1474
+ watch(claudeignore, onChange);
1475
+ } catch {
1476
+ }
1477
+ await new Promise(() => {
1478
+ });
1479
+ }
1480
+ async function runAndDisplay(projectRoot) {
1481
+ console.log("\x1B[36m\x1B[1m Claude Launchpad\x1B[0m");
1482
+ console.log("\x1B[2m Scaffold \xB7 Diagnose \xB7 Evaluate\x1B[0m");
1483
+ log.blank();
1484
+ const config = await parseClaudeConfig(projectRoot);
1485
+ if (config.claudeMdContent === null && config.settings === null) {
1486
+ log.error("No Claude Code configuration found.");
1487
+ return;
1488
+ }
1489
+ const results = await Promise.all([
1490
+ analyzeBudget(config),
1491
+ analyzeQuality(config),
1492
+ analyzeSettings(config),
1493
+ analyzeHooks(config),
1494
+ analyzeRules(config),
1495
+ analyzePermissions(config),
1496
+ analyzeMcp(config)
1497
+ ]);
1498
+ const overallScore = Math.round(
1499
+ results.reduce((sum, r) => sum + r.score, 0) / results.length
1500
+ );
1501
+ for (const result of results) {
1502
+ printScoreCard(result.name, result.score);
1503
+ }
1504
+ log.blank();
1505
+ printScoreCard("Overall", overallScore);
1506
+ log.blank();
1507
+ const allIssues = results.flatMap((r) => r.issues);
1508
+ const actionable = allIssues.filter((i) => i.severity !== "info");
1509
+ if (actionable.length === 0) {
1510
+ log.success("No issues found. Your configuration looks solid.");
1511
+ return;
1512
+ }
1513
+ const sorted = [...actionable].sort((a, b) => {
1514
+ const order = { critical: 0, high: 1, medium: 2, low: 3, info: 4 };
1515
+ return (order[a.severity] ?? 4) - (order[b.severity] ?? 4);
1516
+ });
1517
+ for (const issue of sorted) {
1518
+ printIssue(issue.severity, issue.analyzer, issue.message, issue.fix);
1519
+ }
1520
+ log.info(`${actionable.length} issue(s) found. Fix critical/high first.`);
1521
+ }
1522
+
1443
1523
  // src/commands/doctor/index.ts
1444
1524
  function createDoctorCommand() {
1445
- return new Command2("doctor").description("Diagnose your Claude Code configuration and report issues").option("-p, --path <path>", "Project root path", process.cwd()).option("--json", "Output as JSON").option("--min-score <n>", "Exit non-zero if overall score is below this threshold (for CI)").option("--fix", "Auto-apply deterministic fixes for detected issues").action(async (opts) => {
1525
+ return new Command2("doctor").description("Diagnose your Claude Code configuration and report issues").option("-p, --path <path>", "Project root path", process.cwd()).option("--json", "Output as JSON").option("--min-score <n>", "Exit non-zero if overall score is below this threshold (for CI)").option("--fix", "Auto-apply deterministic fixes for detected issues").option("--watch", "Watch for config changes and re-run automatically").action(async (opts) => {
1526
+ if (opts.watch) {
1527
+ await watchConfig(opts.path);
1528
+ return;
1529
+ }
1446
1530
  printBanner();
1447
1531
  log.step("Scanning Claude Code configuration...");
1448
1532
  log.blank();
@@ -1530,7 +1614,7 @@ import chalk2 from "chalk";
1530
1614
 
1531
1615
  // src/commands/eval/loader.ts
1532
1616
  import { readFile as readFile6, readdir as readdir2, access as access5 } from "fs/promises";
1533
- import { join as join5, resolve as resolve2, dirname } from "path";
1617
+ import { join as join6, resolve as resolve2, dirname } from "path";
1534
1618
  import { fileURLToPath } from "url";
1535
1619
  import { parse as parseYaml } from "yaml";
1536
1620
 
@@ -1653,7 +1737,7 @@ async function dirExists(path) {
1653
1737
  async function loadScenarios(options) {
1654
1738
  const { suite, customPath } = options;
1655
1739
  const scenarioDir = customPath ? resolve2(customPath) : await findScenariosDir();
1656
- const dirs = suite ? [join5(scenarioDir, suite)] : await getSubdirectories(scenarioDir);
1740
+ const dirs = suite ? [join6(scenarioDir, suite)] : await getSubdirectories(scenarioDir);
1657
1741
  const allDirs = [scenarioDir, ...dirs];
1658
1742
  const scenarios = [];
1659
1743
  for (const dir of allDirs) {
@@ -1675,7 +1759,7 @@ async function loadScenarios(options) {
1675
1759
  async function getSubdirectories(dir) {
1676
1760
  try {
1677
1761
  const entries = await readdir2(dir, { withFileTypes: true });
1678
- return entries.filter((e) => e.isDirectory()).map((e) => join5(dir, e.name));
1762
+ return entries.filter((e) => e.isDirectory()).map((e) => join6(dir, e.name));
1679
1763
  } catch {
1680
1764
  return [];
1681
1765
  }
@@ -1683,7 +1767,7 @@ async function getSubdirectories(dir) {
1683
1767
  async function listYamlFiles(dir) {
1684
1768
  try {
1685
1769
  const entries = await readdir2(dir, { withFileTypes: true });
1686
- return entries.filter((e) => e.isFile() && (e.name.endsWith(".yaml") || e.name.endsWith(".yml"))).map((e) => join5(dir, e.name));
1770
+ return entries.filter((e) => e.isFile() && (e.name.endsWith(".yaml") || e.name.endsWith(".yml"))).map((e) => join6(dir, e.name));
1687
1771
  } catch {
1688
1772
  return [];
1689
1773
  }
@@ -1691,14 +1775,14 @@ async function listYamlFiles(dir) {
1691
1775
 
1692
1776
  // src/commands/eval/runner.ts
1693
1777
  import { mkdir as mkdir3, writeFile as writeFile3, readFile as readFile7, readdir as readdir3, rm } from "fs/promises";
1694
- import { join as join6, dirname as dirname2 } from "path";
1778
+ import { join as join7, dirname as dirname2 } from "path";
1695
1779
  import { tmpdir } from "os";
1696
1780
  import { randomUUID } from "crypto";
1697
1781
  import { execFile } from "child_process";
1698
1782
  import { promisify } from "util";
1699
1783
  var exec = promisify(execFile);
1700
1784
  async function runScenario(scenario, options) {
1701
- const sandboxDir = join6(tmpdir(), `claude-eval-${randomUUID()}`);
1785
+ const sandboxDir = join7(tmpdir(), `claude-eval-${randomUUID()}`);
1702
1786
  try {
1703
1787
  await setupSandbox(sandboxDir, scenario);
1704
1788
  await runClaudeInSandbox(sandboxDir, scenario.prompt, options.timeout);
@@ -1724,13 +1808,13 @@ async function runScenarioWithRetries(scenario, options) {
1724
1808
  async function setupSandbox(sandboxDir, scenario) {
1725
1809
  await mkdir3(sandboxDir, { recursive: true });
1726
1810
  for (const file of scenario.setup.files) {
1727
- const filePath = join6(sandboxDir, file.path);
1811
+ const filePath = join7(sandboxDir, file.path);
1728
1812
  await mkdir3(dirname2(filePath), { recursive: true });
1729
1813
  await writeFile3(filePath, file.content);
1730
1814
  }
1731
1815
  if (scenario.setup.instructions) {
1732
1816
  await writeFile3(
1733
- join6(sandboxDir, "CLAUDE.md"),
1817
+ join7(sandboxDir, "CLAUDE.md"),
1734
1818
  `# Eval Scenario
1735
1819
 
1736
1820
  ${scenario.setup.instructions}
@@ -1843,7 +1927,7 @@ async function evaluateSingleCheck(check, sandboxDir) {
1843
1927
  async function checkGrep(check, sandboxDir) {
1844
1928
  if (!check.pattern) return false;
1845
1929
  try {
1846
- const content = await readFile7(join6(sandboxDir, check.target), "utf-8");
1930
+ const content = await readFile7(join7(sandboxDir, check.target), "utf-8");
1847
1931
  let found;
1848
1932
  try {
1849
1933
  found = new RegExp(check.pattern).test(content);
@@ -1857,7 +1941,7 @@ async function checkGrep(check, sandboxDir) {
1857
1941
  }
1858
1942
  async function checkFileExists(check, sandboxDir) {
1859
1943
  try {
1860
- await readFile7(join6(sandboxDir, check.target));
1944
+ await readFile7(join7(sandboxDir, check.target));
1861
1945
  return check.expect === "present";
1862
1946
  } catch {
1863
1947
  return check.expect === "absent";
@@ -1865,7 +1949,7 @@ async function checkFileExists(check, sandboxDir) {
1865
1949
  }
1866
1950
  async function checkFileAbsent(check, sandboxDir) {
1867
1951
  try {
1868
- await readFile7(join6(sandboxDir, check.target));
1952
+ await readFile7(join7(sandboxDir, check.target));
1869
1953
  return check.expect === "absent";
1870
1954
  } catch {
1871
1955
  return check.expect === "present";
@@ -1874,7 +1958,7 @@ async function checkFileAbsent(check, sandboxDir) {
1874
1958
  async function checkMaxLines(check, sandboxDir) {
1875
1959
  const maxLines = parseInt(check.pattern ?? "800", 10);
1876
1960
  try {
1877
- const files = await listAllFiles(join6(sandboxDir, check.target));
1961
+ const files = await listAllFiles(join7(sandboxDir, check.target));
1878
1962
  for (const file of files) {
1879
1963
  const content = await readFile7(file, "utf-8");
1880
1964
  if (content.split("\n").length > maxLines) {
@@ -1891,7 +1975,7 @@ async function listAllFiles(dir) {
1891
1975
  try {
1892
1976
  const entries = await readdir3(dir, { withFileTypes: true });
1893
1977
  for (const entry of entries) {
1894
- const fullPath = join6(dir, entry.name);
1978
+ const fullPath = join7(dir, entry.name);
1895
1979
  if (entry.isDirectory()) {
1896
1980
  results.push(...await listAllFiles(fullPath));
1897
1981
  } else {
@@ -2017,7 +2101,7 @@ import { Command as Command4 } from "commander";
2017
2101
  import { spawn, execFile as execFile2 } from "child_process";
2018
2102
  import { promisify as promisify2 } from "util";
2019
2103
  import { access as access6 } from "fs/promises";
2020
- import { join as join7 } from "path";
2104
+ import { join as join8 } from "path";
2021
2105
  var execAsync = promisify2(execFile2);
2022
2106
  var ENHANCE_PROMPT = `Read CLAUDE.md and the project's codebase, then update CLAUDE.md to fill in missing or incomplete sections:
2023
2107
 
@@ -2035,7 +2119,7 @@ function createEnhanceCommand() {
2035
2119
  return new Command4("enhance").description("Use Claude to analyze your codebase and complete CLAUDE.md").option("-p, --path <path>", "Project root path", process.cwd()).action(async (opts) => {
2036
2120
  printBanner();
2037
2121
  const root = opts.path;
2038
- const claudeMdPath = join7(root, "CLAUDE.md");
2122
+ const claudeMdPath = join8(root, "CLAUDE.md");
2039
2123
  try {
2040
2124
  await access6(claudeMdPath);
2041
2125
  } catch {
@@ -2064,8 +2148,8 @@ function createEnhanceCommand() {
2064
2148
  }
2065
2149
 
2066
2150
  // src/cli.ts
2067
- var program = new Command5().name("claude-launchpad").description("CLI toolkit that makes Claude Code setups measurably good").version("0.1.0").action(async () => {
2068
- const hasConfig = await fileExists3(join8(process.cwd(), "CLAUDE.md")) || await fileExists3(join8(process.cwd(), ".claude", "settings.json"));
2151
+ var program = new Command5().name("claude-launchpad").description("CLI toolkit that makes Claude Code setups measurably good").version("0.2.2", "-v, --version").action(async () => {
2152
+ const hasConfig = await fileExists3(join9(process.cwd(), "CLAUDE.md")) || await fileExists3(join9(process.cwd(), ".claude", "settings.json"));
2069
2153
  if (hasConfig) {
2070
2154
  await program.commands.find((c) => c.name() === "doctor")?.parseAsync([], { from: "user" });
2071
2155
  } else {