claude-launchpad 1.7.2 → 1.8.1

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 (37) hide show
  1. package/README.md +2 -1
  2. package/dist/{chunk-XL4ZMHA7.js → chunk-COGKNJJB.js} +14 -20
  3. package/dist/chunk-COGKNJJB.js.map +1 -0
  4. package/dist/{chunk-XJZZJEXT.js → chunk-H2E7QMF4.js} +2 -2
  5. package/dist/{chunk-UHZ2B7BE.js → chunk-IY3Z54UK.js} +166 -117
  6. package/dist/chunk-IY3Z54UK.js.map +1 -0
  7. package/dist/{chunk-N4GESFQC.js → chunk-RDID5P4K.js} +2 -2
  8. package/dist/{chunk-VLB5TDFF.js → chunk-THDBKJAV.js} +3 -3
  9. package/dist/cli.js +124 -32
  10. package/dist/cli.js.map +1 -1
  11. package/dist/commands/memory/server.js +3 -3
  12. package/dist/{context-USZJ232G.js → context-BCTOCTZD.js} +5 -5
  13. package/dist/{install-FUXDR2HU.js → install-L23C4YWJ.js} +58 -5
  14. package/dist/install-L23C4YWJ.js.map +1 -0
  15. package/dist/{pull-IZB5D5FC.js → pull-5KYLJ6TH.js} +7 -7
  16. package/dist/{push-IUNXSJZ5.js → push-UOVLT5HO.js} +7 -7
  17. package/dist/{require-deps-Z3CV2I3V.js → require-deps-T2QOGQQ3.js} +3 -3
  18. package/dist/{stats-FMKE3T6J.js → stats-GL7P24U7.js} +6 -6
  19. package/dist/{sync-clean-33V4VWRI.js → sync-clean-7RNYS7EH.js} +3 -3
  20. package/dist/{sync-status-5GMPSUQF.js → sync-status-ULZCMBQJ.js} +14 -9
  21. package/dist/sync-status-ULZCMBQJ.js.map +1 -0
  22. package/dist/{tui-7YXGNXCP.js → tui-UGBWF2WT.js} +4 -4
  23. package/package.json +1 -1
  24. package/dist/chunk-UHZ2B7BE.js.map +0 -1
  25. package/dist/chunk-XL4ZMHA7.js.map +0 -1
  26. package/dist/install-FUXDR2HU.js.map +0 -1
  27. package/dist/sync-status-5GMPSUQF.js.map +0 -1
  28. /package/dist/{chunk-XJZZJEXT.js.map → chunk-H2E7QMF4.js.map} +0 -0
  29. /package/dist/{chunk-N4GESFQC.js.map → chunk-RDID5P4K.js.map} +0 -0
  30. /package/dist/{chunk-VLB5TDFF.js.map → chunk-THDBKJAV.js.map} +0 -0
  31. /package/dist/{context-USZJ232G.js.map → context-BCTOCTZD.js.map} +0 -0
  32. /package/dist/{pull-IZB5D5FC.js.map → pull-5KYLJ6TH.js.map} +0 -0
  33. /package/dist/{push-IUNXSJZ5.js.map → push-UOVLT5HO.js.map} +0 -0
  34. /package/dist/{require-deps-Z3CV2I3V.js.map → require-deps-T2QOGQQ3.js.map} +0 -0
  35. /package/dist/{stats-FMKE3T6J.js.map → stats-GL7P24U7.js.map} +0 -0
  36. /package/dist/{sync-clean-33V4VWRI.js.map → sync-clean-7RNYS7EH.js.map} +0 -0
  37. /package/dist/{tui-7YXGNXCP.js.map → tui-UGBWF2WT.js.map} +0 -0
package/README.md CHANGED
@@ -208,7 +208,7 @@ Claude's built-in memory resets per machine. Launchpad gives each project persis
208
208
  claude-launchpad memory
209
209
  ```
210
210
 
211
- If memory is not installed, it runs interactive setup. If installed, it shows stats. Requires native deps first: `npm install better-sqlite3 sqlite-vec`.
211
+ If memory is not installed, it runs interactive setup. If fully installed (SessionStart hook **and** MCP server registered), it shows stats. If partially installed, it points you at `memory install` to fix the missing piece. Requires native deps first: `npm install better-sqlite3 sqlite-vec`.
212
212
 
213
213
  During setup, you choose where memory config lives:
214
214
 
@@ -226,6 +226,7 @@ Data stays in `~/.agentic-memory/memory.db`. Sync requires the [GitHub CLI](http
226
226
  | Flag / Subcommand | What it does |
227
227
  |---|---|
228
228
  | `--dashboard` | Opens the interactive TUI dashboard |
229
+ | `install` | Explicitly (re-)run the install flow — useful after a partial/broken setup |
229
230
  | `push` | Push current project's memories to a private GitHub Gist |
230
231
  | `pull` | Pull current project's memories from a private GitHub Gist |
231
232
  | `push --all` | Push all projects |
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  log
4
- } from "./chunk-UHZ2B7BE.js";
4
+ } from "./chunk-IY3Z54UK.js";
5
5
 
6
6
  // src/commands/memory/utils/gist-transport.ts
7
7
  import { execSync } from "child_process";
@@ -141,26 +141,20 @@ function createGist(filename, content) {
141
141
  }
142
142
  }
143
143
  function readGistFile(gistId, filename) {
144
- try {
145
- const escapedFilename = JSON.stringify(filename);
146
- return execSync(
147
- `gh api "/gists/${gistId}" --jq '.files[${escapedFilename}].content'`,
148
- { ...EXEC_OPTS, stdio: ["pipe", "pipe", "pipe"] }
149
- ).trimEnd();
150
- } catch {
151
- return null;
152
- }
144
+ const escapedFilename = JSON.stringify(filename);
145
+ const output = execSync(
146
+ `gh api "/gists/${gistId}" --jq '.files[${escapedFilename}].content'`,
147
+ { ...EXEC_OPTS, stdio: ["pipe", "pipe", "pipe"] }
148
+ ).trimEnd();
149
+ if (output === "null" || output === "") return null;
150
+ return output;
153
151
  }
154
152
  function listGistFiles(gistId) {
155
- try {
156
- const output = execSync(
157
- `gh api "/gists/${gistId}" --jq '.files | keys[]'`,
158
- { ...EXEC_OPTS, stdio: ["pipe", "pipe", "pipe"] }
159
- );
160
- return output.trim().split("\n").filter(Boolean);
161
- } catch {
162
- return [];
163
- }
153
+ const output = execSync(
154
+ `gh api "/gists/${gistId}" --jq '.files | keys[]'`,
155
+ { ...EXEC_OPTS, stdio: ["pipe", "pipe", "pipe"] }
156
+ );
157
+ return output.trim().split("\n").filter(Boolean);
164
158
  }
165
159
  function deleteGistFile(gistId, filename) {
166
160
  const payload = { files: { [filename]: null } };
@@ -211,4 +205,4 @@ export {
211
205
  deleteGistFile,
212
206
  updateGistFiles
213
207
  };
214
- //# sourceMappingURL=chunk-XL4ZMHA7.js.map
208
+ //# sourceMappingURL=chunk-COGKNJJB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/memory/utils/gist-transport.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { readFileSync, writeFileSync, unlinkSync, mkdirSync } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport { tmpdir } from 'node:os';\nimport { homedir } from 'node:os';\nimport { log } from '../../../lib/output.js';\n\nexport interface SyncConfig {\n readonly gistId: string;\n}\n\nconst EXEC_OPTS = { encoding: 'utf-8' as const, timeout: 30_000 };\nconst GIST_DESCRIPTION = 'agentic-memory sync';\nconst SYNC_CONFIG_FILE = 'sync-config.json';\nconst DATA_DIR = join(homedir(), '.agentic-memory');\n\nfunction syncConfigPath(): string {\n return join(DATA_DIR, SYNC_CONFIG_FILE);\n}\n\nfunction slugify(project: string): string {\n return project.replace(/[^a-zA-Z0-9._-]/g, '-');\n}\n\nexport function projectToFilename(project: string): string {\n if (!project) throw new Error('Project name cannot be empty');\n return `memories-${slugify(project)}.json`;\n}\n\nexport function filenameToProject(filename: string): string | null {\n const match = filename.match(/^memories-(.+)\\.json$/);\n return match?.[1] ?? null;\n}\n\n\nexport function assertGhAvailable(): void {\n try {\n execSync('gh --version', { ...EXEC_OPTS, stdio: 'pipe' });\n } catch {\n throw new Error(\n 'Memory sync requires the GitHub CLI.\\n' +\n 'Install: https://cli.github.com/\\n' +\n 'Then run: gh auth login'\n );\n }\n try {\n execSync('gh auth status', { ...EXEC_OPTS, stdio: 'pipe' });\n } catch {\n throw new Error(\n 'gh is installed but not authenticated.\\n' +\n 'Run: gh auth login'\n );\n }\n}\n\n/**\n * Read sync config from disk only — no network discovery.\n * Safe to call from lightweight contexts like doctor analyzers.\n */\nexport function readSyncConfig(): SyncConfig | null {\n try {\n const raw = readFileSync(syncConfigPath(), 'utf-8');\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n if (typeof parsed.gistId === 'string' && /^[a-f0-9]+$/.test(parsed.gistId)) {\n return { gistId: parsed.gistId };\n }\n } catch { /* no config file */ }\n return null;\n}\n\nexport function loadSyncConfig(): SyncConfig | null {\n const stored = readSyncConfig();\n if (stored) {\n const status = probeGist(stored.gistId);\n if (status === 'alive') return stored;\n if (status === 'missing') {\n log.warn(`Sync gist ${stored.gistId} no longer exists. Re-discovering...`);\n clearSyncConfig();\n // fall through to discovery\n } else {\n // unknown error (network, auth, rate limit) — trust stored config\n return stored;\n }\n }\n\n const discovered = discoverSyncGist();\n if (discovered) {\n saveSyncConfig({ gistId: discovered });\n if (stored) log.success(`Reconnected to sync gist ${discovered}`);\n return { gistId: discovered };\n }\n return null;\n}\n\nfunction probeGist(gistId: string): 'alive' | 'missing' | 'unknown' {\n try {\n execSync(\n `gh api \"/gists/${gistId}\" --silent`,\n { ...EXEC_OPTS, stdio: ['pipe', 'pipe', 'pipe'] },\n );\n return 'alive';\n } catch (err) {\n const stderr = (err as { stderr?: Buffer | string }).stderr?.toString() ?? '';\n const msg = err instanceof Error ? err.message : String(err);\n if (/HTTP 404/i.test(stderr) || /HTTP 404/i.test(msg) || /Not Found/i.test(stderr)) {\n return 'missing';\n }\n return 'unknown';\n }\n}\n\nfunction clearSyncConfig(): void {\n try { unlinkSync(syncConfigPath()); } catch { /* already gone */ }\n}\n\nfunction discoverSyncGist(): string | null {\n try {\n const output = execSync(\n 'gh gist list --limit 100',\n { ...EXEC_OPTS, stdio: ['pipe', 'pipe', 'pipe'] },\n );\n for (const line of output.split('\\n')) {\n const cols = line.split('\\t');\n if (cols[1]?.trim() === GIST_DESCRIPTION) {\n const gistId = cols[0]?.trim();\n if (gistId && /^[a-f0-9]+$/.test(gistId)) return gistId;\n }\n }\n } catch { /* gh list failed */ }\n return null;\n}\n\nexport function saveSyncConfig(config: SyncConfig): void {\n const filePath = syncConfigPath();\n mkdirSync(dirname(filePath), { recursive: true });\n writeFileSync(filePath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n\nexport function createGist(filename: string, content: string): string {\n const safeFilename = slugify(filename.replace(/\\.json$/, '')) + '.json';\n const tmpFile = join(tmpdir(), safeFilename);\n try {\n writeFileSync(tmpFile, content, 'utf-8');\n const result = execSync(\n `gh gist create \"${tmpFile}\" --desc \"${GIST_DESCRIPTION}\" --public=false`,\n { ...EXEC_OPTS, stdio: ['pipe', 'pipe', 'pipe'] },\n ).trim();\n const gistId = result.split('/').pop()?.trim() ?? '';\n if (!gistId || !/^[a-f0-9]+$/.test(gistId)) {\n throw new Error(`Failed to parse gist ID from: ${result}`);\n }\n saveSyncConfig({ gistId });\n return gistId;\n } finally {\n try { unlinkSync(tmpFile); } catch { /* ignore */ }\n }\n}\n\n/**\n * Read a file from a gist.\n * Returns null when the gist exists but does not contain this file (legitimate first push).\n * Throws on transport failures (network, auth, 404 on the gist itself, rate limits).\n */\nexport function readGistFile(gistId: string, filename: string): string | null {\n const escapedFilename = JSON.stringify(filename);\n const output = execSync(\n `gh api \"/gists/${gistId}\" --jq '.files[${escapedFilename}].content'`,\n { ...EXEC_OPTS, stdio: ['pipe', 'pipe', 'pipe'] },\n ).trimEnd();\n // jq returns the literal string \"null\" when the requested field is absent.\n if (output === 'null' || output === '') return null;\n return output;\n}\n\n/**\n * List filenames in a gist.\n * Throws on transport failures. Returns [] only when the gist is legitimately empty.\n */\nexport function listGistFiles(gistId: string): readonly string[] {\n const output = execSync(\n `gh api \"/gists/${gistId}\" --jq '.files | keys[]'`,\n { ...EXEC_OPTS, stdio: ['pipe', 'pipe', 'pipe'] },\n );\n return output.trim().split('\\n').filter(Boolean);\n}\n\nexport function deleteGistFile(gistId: string, filename: string): void {\n const payload = { files: { [filename]: null } };\n const tmpFile = join(tmpdir(), `gist-delete-${Date.now()}.json`);\n try {\n writeFileSync(tmpFile, JSON.stringify(payload), 'utf-8');\n execSync(\n `gh api --method PATCH \"/gists/${gistId}\" --input \"${tmpFile}\"`,\n { ...EXEC_OPTS, stdio: ['pipe', 'pipe', 'pipe'] },\n );\n } finally {\n try { unlinkSync(tmpFile); } catch { /* ignore */ }\n }\n}\n\nexport function updateGistFiles(\n gistId: string,\n files: Record<string, string>,\n): void {\n const payload = {\n files: Object.fromEntries(\n Object.entries(files).map(([name, content]) => [name, { content }]),\n ),\n };\n const tmpFile = join(tmpdir(), `gist-patch-${Date.now()}.json`);\n try {\n writeFileSync(tmpFile, JSON.stringify(payload), 'utf-8');\n execSync(\n `gh api --method PATCH \"/gists/${gistId}\" --input \"${tmpFile}\"`,\n { ...EXEC_OPTS, stdio: ['pipe', 'pipe', 'pipe'] },\n );\n } finally {\n try { unlinkSync(tmpFile); } catch { /* ignore */ }\n }\n}\n"],"mappings":";;;;;;AAAA,SAAS,gBAAgB;AACzB,SAAS,cAAc,eAAe,YAAY,iBAAiB;AACnE,SAAS,MAAM,eAAe;AAC9B,SAAS,cAAc;AACvB,SAAS,eAAe;AAOxB,IAAM,YAAY,EAAE,UAAU,SAAkB,SAAS,IAAO;AAChE,IAAM,mBAAmB;AACzB,IAAM,mBAAmB;AACzB,IAAM,WAAW,KAAK,QAAQ,GAAG,iBAAiB;AAElD,SAAS,iBAAyB;AAChC,SAAO,KAAK,UAAU,gBAAgB;AACxC;AAEA,SAAS,QAAQ,SAAyB;AACxC,SAAO,QAAQ,QAAQ,oBAAoB,GAAG;AAChD;AAEO,SAAS,kBAAkB,SAAyB;AACzD,MAAI,CAAC,QAAS,OAAM,IAAI,MAAM,8BAA8B;AAC5D,SAAO,YAAY,QAAQ,OAAO,CAAC;AACrC;AAEO,SAAS,kBAAkB,UAAiC;AACjE,QAAM,QAAQ,SAAS,MAAM,uBAAuB;AACpD,SAAO,QAAQ,CAAC,KAAK;AACvB;AAGO,SAAS,oBAA0B;AACxC,MAAI;AACF,aAAS,gBAAgB,EAAE,GAAG,WAAW,OAAO,OAAO,CAAC;AAAA,EAC1D,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AACA,MAAI;AACF,aAAS,kBAAkB,EAAE,GAAG,WAAW,OAAO,OAAO,CAAC;AAAA,EAC5D,QAAQ;AACN,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AACF;AAMO,SAAS,iBAAoC;AAClD,MAAI;AACF,UAAM,MAAM,aAAa,eAAe,GAAG,OAAO;AAClD,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,OAAO,OAAO,WAAW,YAAY,cAAc,KAAK,OAAO,MAAM,GAAG;AAC1E,aAAO,EAAE,QAAQ,OAAO,OAAO;AAAA,IACjC;AAAA,EACF,QAAQ;AAAA,EAAuB;AAC/B,SAAO;AACT;AAEO,SAAS,iBAAoC;AAClD,QAAM,SAAS,eAAe;AAC9B,MAAI,QAAQ;AACV,UAAM,SAAS,UAAU,OAAO,MAAM;AACtC,QAAI,WAAW,QAAS,QAAO;AAC/B,QAAI,WAAW,WAAW;AACxB,UAAI,KAAK,aAAa,OAAO,MAAM,sCAAsC;AACzE,sBAAgB;AAAA,IAElB,OAAO;AAEL,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,aAAa,iBAAiB;AACpC,MAAI,YAAY;AACd,mBAAe,EAAE,QAAQ,WAAW,CAAC;AACrC,QAAI,OAAQ,KAAI,QAAQ,4BAA4B,UAAU,EAAE;AAChE,WAAO,EAAE,QAAQ,WAAW;AAAA,EAC9B;AACA,SAAO;AACT;AAEA,SAAS,UAAU,QAAiD;AAClE,MAAI;AACF;AAAA,MACE,kBAAkB,MAAM;AAAA,MACxB,EAAE,GAAG,WAAW,OAAO,CAAC,QAAQ,QAAQ,MAAM,EAAE;AAAA,IAClD;AACA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,UAAM,SAAU,IAAqC,QAAQ,SAAS,KAAK;AAC3E,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,QAAI,YAAY,KAAK,MAAM,KAAK,YAAY,KAAK,GAAG,KAAK,aAAa,KAAK,MAAM,GAAG;AAClF,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACF;AAEA,SAAS,kBAAwB;AAC/B,MAAI;AAAE,eAAW,eAAe,CAAC;AAAA,EAAG,QAAQ;AAAA,EAAqB;AACnE;AAEA,SAAS,mBAAkC;AACzC,MAAI;AACF,UAAM,SAAS;AAAA,MACb;AAAA,MACA,EAAE,GAAG,WAAW,OAAO,CAAC,QAAQ,QAAQ,MAAM,EAAE;AAAA,IAClD;AACA,eAAW,QAAQ,OAAO,MAAM,IAAI,GAAG;AACrC,YAAM,OAAO,KAAK,MAAM,GAAI;AAC5B,UAAI,KAAK,CAAC,GAAG,KAAK,MAAM,kBAAkB;AACxC,cAAM,SAAS,KAAK,CAAC,GAAG,KAAK;AAC7B,YAAI,UAAU,cAAc,KAAK,MAAM,EAAG,QAAO;AAAA,MACnD;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAAuB;AAC/B,SAAO;AACT;AAEO,SAAS,eAAe,QAA0B;AACvD,QAAM,WAAW,eAAe;AAChC,YAAU,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAChD,gBAAc,UAAU,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,MAAM,OAAO;AACzE;AAEO,SAAS,WAAW,UAAkB,SAAyB;AACpE,QAAM,eAAe,QAAQ,SAAS,QAAQ,WAAW,EAAE,CAAC,IAAI;AAChE,QAAM,UAAU,KAAK,OAAO,GAAG,YAAY;AAC3C,MAAI;AACF,kBAAc,SAAS,SAAS,OAAO;AACvC,UAAM,SAAS;AAAA,MACb,mBAAmB,OAAO,aAAa,gBAAgB;AAAA,MACvD,EAAE,GAAG,WAAW,OAAO,CAAC,QAAQ,QAAQ,MAAM,EAAE;AAAA,IAClD,EAAE,KAAK;AACP,UAAM,SAAS,OAAO,MAAM,GAAG,EAAE,IAAI,GAAG,KAAK,KAAK;AAClD,QAAI,CAAC,UAAU,CAAC,cAAc,KAAK,MAAM,GAAG;AAC1C,YAAM,IAAI,MAAM,iCAAiC,MAAM,EAAE;AAAA,IAC3D;AACA,mBAAe,EAAE,OAAO,CAAC;AACzB,WAAO;AAAA,EACT,UAAE;AACA,QAAI;AAAE,iBAAW,OAAO;AAAA,IAAG,QAAQ;AAAA,IAAe;AAAA,EACpD;AACF;AAOO,SAAS,aAAa,QAAgB,UAAiC;AAC5E,QAAM,kBAAkB,KAAK,UAAU,QAAQ;AAC/C,QAAM,SAAS;AAAA,IACb,kBAAkB,MAAM,kBAAkB,eAAe;AAAA,IACzD,EAAE,GAAG,WAAW,OAAO,CAAC,QAAQ,QAAQ,MAAM,EAAE;AAAA,EAClD,EAAE,QAAQ;AAEV,MAAI,WAAW,UAAU,WAAW,GAAI,QAAO;AAC/C,SAAO;AACT;AAMO,SAAS,cAAc,QAAmC;AAC/D,QAAM,SAAS;AAAA,IACb,kBAAkB,MAAM;AAAA,IACxB,EAAE,GAAG,WAAW,OAAO,CAAC,QAAQ,QAAQ,MAAM,EAAE;AAAA,EAClD;AACA,SAAO,OAAO,KAAK,EAAE,MAAM,IAAI,EAAE,OAAO,OAAO;AACjD;AAEO,SAAS,eAAe,QAAgB,UAAwB;AACrE,QAAM,UAAU,EAAE,OAAO,EAAE,CAAC,QAAQ,GAAG,KAAK,EAAE;AAC9C,QAAM,UAAU,KAAK,OAAO,GAAG,eAAe,KAAK,IAAI,CAAC,OAAO;AAC/D,MAAI;AACF,kBAAc,SAAS,KAAK,UAAU,OAAO,GAAG,OAAO;AACvD;AAAA,MACE,iCAAiC,MAAM,cAAc,OAAO;AAAA,MAC5D,EAAE,GAAG,WAAW,OAAO,CAAC,QAAQ,QAAQ,MAAM,EAAE;AAAA,IAClD;AAAA,EACF,UAAE;AACA,QAAI;AAAE,iBAAW,OAAO;AAAA,IAAG,QAAQ;AAAA,IAAe;AAAA,EACpD;AACF;AAEO,SAAS,gBACd,QACA,OACM;AACN,QAAM,UAAU;AAAA,IACd,OAAO,OAAO;AAAA,MACZ,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,MAAM,OAAO,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AAAA,IACpE;AAAA,EACF;AACA,QAAM,UAAU,KAAK,OAAO,GAAG,cAAc,KAAK,IAAI,CAAC,OAAO;AAC9D,MAAI;AACF,kBAAc,SAAS,KAAK,UAAU,OAAO,GAAG,OAAO;AACvD;AAAA,MACE,iCAAiC,MAAM,cAAc,OAAO;AAAA,MAC5D,EAAE,GAAG,WAAW,OAAO,CAAC,QAAQ,QAAQ,MAAM,EAAE;AAAA,IAClD;AAAA,EACF,UAAE;AACA,QAAI;AAAE,iBAAW,OAAO;AAAA,IAAG,QAAQ;AAAA,IAAe;AAAA,EACpD;AACF;","names":[]}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  log
4
- } from "./chunk-UHZ2B7BE.js";
4
+ } from "./chunk-IY3Z54UK.js";
5
5
 
6
6
  // src/commands/memory/utils/require-deps.ts
7
7
  import { createRequire } from "module";
@@ -44,4 +44,4 @@ export {
44
44
  cwdRequire,
45
45
  requireMemoryDeps
46
46
  };
47
- //# sourceMappingURL=chunk-XJZZJEXT.js.map
47
+ //# sourceMappingURL=chunk-H2E7QMF4.js.map
@@ -32,77 +32,15 @@ async function readJsonOrNull(path) {
32
32
  }
33
33
 
34
34
  // src/lib/settings.ts
35
- import { readFile as readFile2, writeFile, mkdir } from "fs/promises";
36
- import { join } from "path";
37
- async function readSettingsJson(root) {
38
- const path = join(root, ".claude", "settings.json");
39
- try {
40
- const content = await readFile2(path, "utf-8");
41
- return JSON.parse(content);
42
- } catch {
43
- return {};
44
- }
45
- }
46
- async function writeSettingsJson(root, settings) {
47
- const dir = join(root, ".claude");
48
- await mkdir(dir, { recursive: true });
49
- await writeFile(join(dir, "settings.json"), JSON.stringify(settings, null, 2) + "\n");
50
- }
51
- async function readSettingsLocalJson(root) {
52
- const path = join(root, ".claude", "settings.local.json");
53
- try {
54
- const content = await readFile2(path, "utf-8");
55
- return JSON.parse(content);
56
- } catch {
57
- return {};
58
- }
59
- }
60
- async function writeSettingsLocalJson(root, settings) {
61
- const dir = join(root, ".claude");
62
- await mkdir(dir, { recursive: true });
63
- await writeFile(join(dir, "settings.local.json"), JSON.stringify(settings, null, 2) + "\n");
64
- }
65
-
66
- // src/lib/memory-placement.ts
67
- import { select } from "@inquirer/prompts";
68
- function hasMemoryPermissions(settings) {
69
- const permissions = settings.permissions;
70
- const allow = permissions?.allow ?? [];
71
- return allow.some((p) => p.includes("agentic-memory"));
72
- }
73
- async function getMemoryPlacement(root, skipPrompt = false) {
74
- const local = await readSettingsLocalJson(root);
75
- const persisted = local.memoryPlacement;
76
- if (persisted === "shared" || persisted === "local") {
77
- return persisted;
78
- }
79
- if (hasMemoryPermissions(local)) {
80
- await writeSettingsLocalJson(root, { ...local, memoryPlacement: "local" });
81
- return "local";
82
- }
83
- const shared = await readSettingsJson(root);
84
- if (hasMemoryPermissions(shared)) {
85
- await writeSettingsLocalJson(root, { ...local, memoryPlacement: "shared" });
86
- return "shared";
87
- }
88
- if (skipPrompt) return "shared";
89
- const choice = await select({
90
- message: "Where should memory config go?",
91
- choices: [
92
- { value: "shared", name: "Shared (team sees it) \u2014 CLAUDE.md + settings.json" },
93
- { value: "local", name: "Local (only you) \u2014 .claude/CLAUDE.md + settings.local.json" }
94
- ]
95
- });
96
- await writeSettingsLocalJson(root, { ...local, memoryPlacement: choice });
97
- return choice;
98
- }
35
+ import { readFile as readFile4, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
36
+ import { join as join4 } from "path";
99
37
 
100
38
  // src/lib/output.ts
101
39
  import chalk from "chalk";
102
40
 
103
41
  // src/commands/doctor/fixer.ts
104
- import { readFile as readFile4, writeFile as writeFile2, mkdir as mkdir2, access as access2 } from "fs/promises";
105
- import { join as join4 } from "path";
42
+ import { readFile as readFile3, writeFile, mkdir, access as access2 } from "fs/promises";
43
+ import { join as join3 } from "path";
106
44
  import { homedir } from "os";
107
45
 
108
46
  // src/lib/sections.ts
@@ -113,22 +51,22 @@ var OFF_LIMITS_CONTENT = "- Never hardcode secrets \u2014 use environment variab
113
51
  var SKILL_AUTHORING_CONTENT = 'When creating Claude Code skills (.claude/skills/*/SKILL.md):\n\n- Keep SKILL.md under 500 lines \u2014 move reference material to supporting files in the same directory\n- Front-load description (first 250 chars shown in listings) with TRIGGER when / DO NOT TRIGGER when clauses\n- Add allowed-tools in frontmatter to restrict tool access (e.g. Read, Glob, Grep for read-only skills)\n- Add argument-hint in frontmatter showing the expected input format (use $ARGUMENTS or $0, $1 for dynamic input)\n- Set disable-model-invocation: true for skills with side effects (deploy, send messages)\n- Structure as phases: Research, Plan, Execute, Verify with "Done when:" success criteria per phase\n- Handle edge cases and preconditions before execution';
114
52
 
115
53
  // src/lib/detect.ts
116
- import { join as join2, basename } from "path";
54
+ import { join, basename } from "path";
117
55
  async function detectProject(root) {
118
56
  const name = basename(root);
119
57
  const [pkgJson, goMod, pyProject, gemfile, cargo, pubspec, composerJson, pomXml, buildGradleGroovy, buildGradleKts, packageSwift, mixExs, csproj, lockfiles] = await Promise.all([
120
- readJsonOrNull(join2(root, "package.json")),
121
- fileExists(join2(root, "go.mod")),
122
- readFileOrNull(join2(root, "pyproject.toml")),
123
- fileExists(join2(root, "Gemfile")),
124
- fileExists(join2(root, "Cargo.toml")),
125
- fileExists(join2(root, "pubspec.yaml")),
126
- readJsonOrNull(join2(root, "composer.json")),
127
- fileExists(join2(root, "pom.xml")),
128
- fileExists(join2(root, "build.gradle")),
129
- fileExists(join2(root, "build.gradle.kts")),
130
- fileExists(join2(root, "Package.swift")),
131
- fileExists(join2(root, "mix.exs")),
58
+ readJsonOrNull(join(root, "package.json")),
59
+ fileExists(join(root, "go.mod")),
60
+ readFileOrNull(join(root, "pyproject.toml")),
61
+ fileExists(join(root, "Gemfile")),
62
+ fileExists(join(root, "Cargo.toml")),
63
+ fileExists(join(root, "pubspec.yaml")),
64
+ readJsonOrNull(join(root, "composer.json")),
65
+ fileExists(join(root, "pom.xml")),
66
+ fileExists(join(root, "build.gradle")),
67
+ fileExists(join(root, "build.gradle.kts")),
68
+ fileExists(join(root, "Package.swift")),
69
+ fileExists(join(root, "mix.exs")),
132
70
  globExists(root, "*.csproj"),
133
71
  detectLockfiles(root)
134
72
  ]);
@@ -209,10 +147,10 @@ function detectFramework(m) {
209
147
  }
210
148
  async function detectLockfiles(root) {
211
149
  const [pnpmLock, yarnLock, bunLock, npmLock] = await Promise.all([
212
- fileExists(join2(root, "pnpm-lock.yaml")),
213
- fileExists(join2(root, "yarn.lock")),
214
- fileExists(join2(root, "bun.lockb")),
215
- fileExists(join2(root, "package-lock.json"))
150
+ fileExists(join(root, "pnpm-lock.yaml")),
151
+ fileExists(join(root, "yarn.lock")),
152
+ fileExists(join(root, "bun.lockb")),
153
+ fileExists(join(root, "package-lock.json"))
216
154
  ]);
217
155
  return { pnpmLock, yarnLock, bunLock, npmLock };
218
156
  }
@@ -619,6 +557,40 @@ function generateEnhanceSkill() {
619
557
  ].join("\n");
620
558
  }
621
559
 
560
+ // src/lib/memory-placement.ts
561
+ import { select } from "@inquirer/prompts";
562
+ function hasMemoryPermissions(settings) {
563
+ const permissions = settings.permissions;
564
+ const allow = permissions?.allow ?? [];
565
+ return allow.some((p) => p.includes("agentic-memory"));
566
+ }
567
+ async function getMemoryPlacement(root, skipPrompt = false) {
568
+ const local = await readSettingsLocalJson(root) ?? {};
569
+ const persisted = local.memoryPlacement;
570
+ if (persisted === "shared" || persisted === "local") {
571
+ return persisted;
572
+ }
573
+ if (hasMemoryPermissions(local)) {
574
+ await writeSettingsLocalJson(root, { ...local, memoryPlacement: "local" });
575
+ return "local";
576
+ }
577
+ const shared = await readSettingsJson(root) ?? {};
578
+ if (hasMemoryPermissions(shared)) {
579
+ await writeSettingsLocalJson(root, { ...local, memoryPlacement: "shared" });
580
+ return "shared";
581
+ }
582
+ if (skipPrompt) return "shared";
583
+ const choice = await select({
584
+ message: "Where should memory config go?",
585
+ choices: [
586
+ { value: "shared", name: "Shared (team sees it) \u2014 CLAUDE.md + settings.json" },
587
+ { value: "local", name: "Local (only you) \u2014 .claude/CLAUDE.md + settings.local.json" }
588
+ ]
589
+ });
590
+ await writeSettingsLocalJson(root, { ...local, memoryPlacement: choice });
591
+ return choice;
592
+ }
593
+
622
594
  // src/lib/stub-marker.ts
623
595
  var LP_STUB_OPEN = "<!-- LP-STUB: ai-recommended -->";
624
596
  var LP_STUB_CLOSE = "<!-- /LP-STUB -->";
@@ -629,12 +601,13 @@ ${LP_STUB_CLOSE}`;
629
601
  }
630
602
 
631
603
  // src/commands/doctor/fixer-memory.ts
632
- import { readFile as readFile3 } from "fs/promises";
633
- import { join as join3 } from "path";
604
+ import { readFile as readFile2 } from "fs/promises";
605
+ import { join as join2 } from "path";
634
606
  async function addPlacementHook(root, placement, event, dedupKeyword, entry, prepend, successMsg) {
635
607
  const read = placement === "local" ? readSettingsLocalJson : readSettingsJson;
636
608
  const write = placement === "local" ? writeSettingsLocalJson : writeSettingsJson;
637
609
  const settings = await read(root);
610
+ if (settings === null) return false;
638
611
  const hooks = settings.hooks ?? {};
639
612
  const hookList = hooks[event] ?? [];
640
613
  const alreadyHas = hookList.some((g) => {
@@ -652,6 +625,7 @@ async function disableAutoMemory(root, placement) {
652
625
  const read = placement === "local" ? readSettingsLocalJson : readSettingsJson;
653
626
  const write = placement === "local" ? writeSettingsLocalJson : writeSettingsJson;
654
627
  const settings = await read(root);
628
+ if (settings === null) return false;
655
629
  if (settings.autoMemoryEnabled === false) return false;
656
630
  const updated = { ...settings, autoMemoryEnabled: false };
657
631
  await write(root, updated);
@@ -663,6 +637,7 @@ async function addMemoryToolPermissions(root, placement) {
663
637
  const read = placement === "local" ? readSettingsLocalJson : readSettingsJson;
664
638
  const write = placement === "local" ? writeSettingsLocalJson : writeSettingsJson;
665
639
  const settings = await read(root);
640
+ if (settings === null) return false;
666
641
  const permissions = settings.permissions ?? {};
667
642
  const allow = permissions.allow ?? [];
668
643
  const tools = [
@@ -701,6 +676,7 @@ async function upgradeStaleSessionEndPushHook(root) {
701
676
  const read = placement === "local" ? readSettingsLocalJson : readSettingsJson;
702
677
  const write = placement === "local" ? writeSettingsLocalJson : writeSettingsJson;
703
678
  const settings = await read(root);
679
+ if (settings === null) continue;
704
680
  const hooks = settings.hooks;
705
681
  const sessionEnd = hooks?.SessionEnd;
706
682
  if (!sessionEnd) continue;
@@ -727,6 +703,7 @@ async function upgradeStaleSessionEndPushHook(root) {
727
703
  }
728
704
  async function removeStaleStopHook(root) {
729
705
  const settings = await readSettingsJson(root);
706
+ if (settings === null) return false;
730
707
  const hooks = settings.hooks;
731
708
  if (!hooks?.Stop) return false;
732
709
  const stopHooks = hooks.Stop;
@@ -743,21 +720,46 @@ async function removeStaleStopHook(root) {
743
720
  log.success("Removed deprecated Stop hook (memory extract)");
744
721
  return true;
745
722
  }
723
+ async function addMemoryToAllowedMcpServers(root) {
724
+ let changed = false;
725
+ for (const placement of ["shared", "local"]) {
726
+ const read = placement === "local" ? readSettingsLocalJson : readSettingsJson;
727
+ const write = placement === "local" ? writeSettingsLocalJson : writeSettingsJson;
728
+ const settings = await read(root);
729
+ if (settings === null) continue;
730
+ const existing = settings.allowedMcpServers;
731
+ if (!Array.isArray(existing)) continue;
732
+ const list = existing;
733
+ const hasMemory = list.some((e) => e && typeof e === "object" && e.serverName === "agentic-memory");
734
+ if (hasMemory) continue;
735
+ const updated = {
736
+ ...settings,
737
+ allowedMcpServers: [{ serverName: "agentic-memory" }, ...list]
738
+ };
739
+ await write(root, updated);
740
+ const target = placement === "local" ? "settings.local.json" : "settings.json";
741
+ log.success(`Added agentic-memory to allowedMcpServers in ${target}`);
742
+ changed = true;
743
+ }
744
+ return changed;
745
+ }
746
746
  async function addAllowedMcpServers(root, placement) {
747
747
  const read = placement === "local" ? readSettingsLocalJson : readSettingsJson;
748
748
  const write = placement === "local" ? writeSettingsLocalJson : writeSettingsJson;
749
749
  const settings = await read(root);
750
+ if (settings === null) return false;
750
751
  if (settings.allowedMcpServers) return false;
751
752
  const other = placement === "local" ? await readSettingsJson(root) : await readSettingsLocalJson(root);
753
+ if (other === null) return false;
752
754
  if (other.allowedMcpServers) return false;
753
755
  const serverNames = /* @__PURE__ */ new Set();
754
756
  const settingsServers = settings.mcpServers;
755
757
  if (settingsServers && typeof settingsServers === "object") {
756
758
  for (const name of Object.keys(settingsServers)) serverNames.add(name);
757
759
  }
758
- const mcpJsonPath = join3(root, ".mcp.json");
760
+ const mcpJsonPath = join2(root, ".mcp.json");
759
761
  try {
760
- const mcpJson = JSON.parse(await readFile3(mcpJsonPath, "utf-8"));
762
+ const mcpJson = JSON.parse(await readFile2(mcpJsonPath, "utf-8"));
761
763
  const mcpServers = mcpJson.mcpServers;
762
764
  if (mcpServers && typeof mcpServers === "object") {
763
765
  for (const name of Object.keys(mcpServers)) serverNames.add(name);
@@ -836,12 +838,13 @@ var FIX_TABLE = [
836
838
  { analyzer: "Memory", match: "autoMemoryEnabled not disabled", fix: (root, _det, placement) => disableAutoMemory(root, placement) },
837
839
  { analyzer: "Memory", match: "MCP tool permission", fix: (root, _det, placement) => addMemoryToolPermissions(root, placement) },
838
840
  { analyzer: "MCP", match: "no allowedMcpServers", fix: (root, _det, placement) => addAllowedMcpServers(root, placement) },
841
+ { analyzer: "Memory", match: "allowedMcpServers is set but does not include agentic-memory", fix: (root) => addMemoryToAllowedMcpServers(root) },
839
842
  { analyzer: "Memory", match: "SessionStart hook to auto-pull", fix: (root, _det, placement) => addSessionStartPullHook(root, placement) },
840
843
  { analyzer: "Memory", match: "SessionEnd hook to auto-push", fix: (root, _det, placement) => addSessionEndPushHook(root, placement) },
841
844
  { analyzer: "Memory", match: "SessionEnd push hook is not nohup-wrapped", fix: (root) => upgradeStaleSessionEndPushHook(root) },
842
845
  { analyzer: "Memory", match: "CLAUDE.md missing memory guidance", fix: (root, _det, placement) => {
843
846
  const content = "Use agentic-memory to persist knowledge across sessions:\n- Memories are automatically injected at session start\n- STORE IMMEDIATELY when: a dependency strategy changes, an architecture decision is made, a convention is established, a bug pattern is discovered, or a feature is killed/added\n- Use memory_search before memory_store to check for duplicates\n- NEVER store credentials, API keys, tokens, or secrets in memories";
844
- const target = placement === "local" ? join4(root, ".claude", "CLAUDE.md") : void 0;
847
+ const target = placement === "local" ? join3(root, ".claude", "CLAUDE.md") : void 0;
845
848
  return addClaudeMdSection(root, "## Memory", wrapStub(content), target);
846
849
  } }
847
850
  ];
@@ -858,6 +861,7 @@ async function tryFix(issue, root, detected, placement) {
858
861
  }
859
862
  async function addHook(root, event, dedupKeyword, entry, successMsg) {
860
863
  const settings = await readSettingsJson(root);
864
+ if (settings === null) return false;
861
865
  const hooks = settings.hooks ?? {};
862
866
  const hookList = hooks[event] ?? [];
863
867
  const alreadyHas = hookList.some((g) => {
@@ -931,6 +935,7 @@ async function addSessionStartHook(root) {
931
935
  }
932
936
  async function migrateAttribution(root) {
933
937
  const settings = await readSettingsJson(root);
938
+ if (settings === null) return false;
934
939
  if (settings.includeCoAuthoredBy === void 0) return false;
935
940
  const { includeCoAuthoredBy: _, ...rest } = settings;
936
941
  const updated = { ...rest, attribution: { commit: "", pr: "" } };
@@ -940,6 +945,7 @@ async function migrateAttribution(root) {
940
945
  }
941
946
  async function addCredentialDenyRules(root) {
942
947
  const settings = await readSettingsJson(root);
948
+ if (settings === null) return false;
943
949
  const permissions = settings.permissions ?? {};
944
950
  const deny = permissions.deny ?? [];
945
951
  const toAdd = ["Read(~/.ssh/*)", "Read(~/.aws/*)", "Read(~/.npmrc)"];
@@ -952,6 +958,7 @@ async function addCredentialDenyRules(root) {
952
958
  }
953
959
  async function addBypassDisable(root) {
954
960
  const settings = await readSettingsJson(root);
961
+ if (settings === null) return false;
955
962
  if (settings.disableBypassPermissionsMode === "disable") return false;
956
963
  const updated = { ...settings, disableBypassPermissionsMode: "disable" };
957
964
  await writeSettingsJson(root, updated);
@@ -960,6 +967,7 @@ async function addBypassDisable(root) {
960
967
  }
961
968
  async function removeSandboxSettings(root) {
962
969
  const settings = await readSettingsJson(root);
970
+ if (settings === null) return false;
963
971
  if (settings.sandbox === void 0) return false;
964
972
  const { sandbox: _sandbox, ...rest } = settings;
965
973
  await writeSettingsJson(root, rest);
@@ -967,27 +975,27 @@ async function removeSandboxSettings(root) {
967
975
  return true;
968
976
  }
969
977
  async function addEnvToClaudeignore(root) {
970
- const ignorePath = join4(root, ".claudeignore");
978
+ const ignorePath = join3(root, ".claudeignore");
971
979
  let content;
972
980
  try {
973
- content = await readFile4(ignorePath, "utf-8");
981
+ content = await readFile3(ignorePath, "utf-8");
974
982
  } catch {
975
983
  return false;
976
984
  }
977
985
  const lines = content.split("\n").map((l) => l.trim());
978
986
  if (lines.some((l) => l === ".env" || l === ".env.*" || l === ".env*")) return false;
979
- await writeFile2(ignorePath, content.trimEnd() + "\n.env\n.env.*\n");
987
+ await writeFile(ignorePath, content.trimEnd() + "\n.env\n.env.*\n");
980
988
  log.success("Added .env to .claudeignore");
981
989
  return true;
982
990
  }
983
991
  async function addClaudeMdSection(root, heading, content, targetPath) {
984
- const claudeMdPath = targetPath ?? join4(root, "CLAUDE.md");
992
+ const claudeMdPath = targetPath ?? join3(root, "CLAUDE.md");
985
993
  let existing;
986
994
  try {
987
- existing = await readFile4(claudeMdPath, "utf-8");
995
+ existing = await readFile3(claudeMdPath, "utf-8");
988
996
  } catch {
989
997
  if (!targetPath) return false;
990
- await mkdir2(join4(root, ".claude"), { recursive: true });
998
+ await mkdir(join3(root, ".claude"), { recursive: true });
991
999
  existing = "# Local Claude Config\n";
992
1000
  }
993
1001
  if (existing.includes(heading)) return false;
@@ -999,20 +1007,20 @@ ${content}
999
1007
 
1000
1008
  `;
1001
1009
  const updated = existing.slice(0, insertAt) + section + existing.slice(insertAt);
1002
- await writeFile2(claudeMdPath, updated);
1010
+ await writeFile(claudeMdPath, updated);
1003
1011
  const label = targetPath ? ".claude/CLAUDE.md" : "CLAUDE.md";
1004
1012
  log.success(`Added "${heading}" section to ${label}`);
1005
1013
  return true;
1006
1014
  }
1007
1015
  async function createBacklogMd(root) {
1008
- const backlogPath = join4(root, "BACKLOG.md");
1016
+ const backlogPath = join3(root, "BACKLOG.md");
1009
1017
  try {
1010
1018
  await access2(backlogPath);
1011
1019
  return false;
1012
1020
  } catch {
1013
1021
  }
1014
1022
  const name = root.split("/").pop() ?? "Project";
1015
- await writeFile2(backlogPath, `# ${name} - Backlog
1023
+ await writeFile(backlogPath, `# ${name} - Backlog
1016
1024
 
1017
1025
  > Features discussed but deferred. Pick up when relevant.
1018
1026
  > Priority: P0 = next sprint, P1 = soon, P2 = when relevant.
@@ -1021,14 +1029,14 @@ async function createBacklogMd(root) {
1021
1029
  return true;
1022
1030
  }
1023
1031
  async function createClaudeignore(root, detected) {
1024
- const ignorePath = join4(root, ".claudeignore");
1032
+ const ignorePath = join3(root, ".claudeignore");
1025
1033
  try {
1026
1034
  await access2(ignorePath);
1027
1035
  return false;
1028
1036
  } catch {
1029
1037
  }
1030
1038
  const content = generateClaudeignore(detected);
1031
- await writeFile2(ignorePath, content);
1039
+ await writeFile(ignorePath, content);
1032
1040
  log.success("Generated .claudeignore with language-specific ignore patterns");
1033
1041
  return true;
1034
1042
  }
@@ -1038,15 +1046,15 @@ var SKILL_AUTHORING_SECTION = `
1038
1046
  ${SKILL_AUTHORING_CONTENT}
1039
1047
  `;
1040
1048
  async function createStarterRules(root) {
1041
- const rulesDir = join4(root, ".claude", "rules");
1049
+ const rulesDir = join3(root, ".claude", "rules");
1042
1050
  try {
1043
1051
  await access2(rulesDir);
1044
1052
  return false;
1045
1053
  } catch {
1046
1054
  }
1047
- await mkdir2(rulesDir, { recursive: true });
1048
- await writeFile2(
1049
- join4(rulesDir, "conventions.md"),
1055
+ await mkdir(rulesDir, { recursive: true });
1056
+ await writeFile(
1057
+ join3(rulesDir, "conventions.md"),
1050
1058
  `# Project Conventions
1051
1059
 
1052
1060
  - Use conventional commits (feat:, fix:, docs:, refactor:, test:, chore:)
@@ -1059,36 +1067,36 @@ ${SKILL_AUTHORING_SECTION}`
1059
1067
  return true;
1060
1068
  }
1061
1069
  async function addSkillAuthoringConventions(root) {
1062
- const conventionsPath = join4(root, ".claude", "rules", "conventions.md");
1070
+ const conventionsPath = join3(root, ".claude", "rules", "conventions.md");
1063
1071
  let content;
1064
1072
  try {
1065
- content = await readFile4(conventionsPath, "utf-8");
1073
+ content = await readFile3(conventionsPath, "utf-8");
1066
1074
  } catch {
1067
1075
  return false;
1068
1076
  }
1069
1077
  if (/^##\s+Skill\s+Authoring/im.test(content)) return false;
1070
- await writeFile2(conventionsPath, content.trimEnd() + "\n" + SKILL_AUTHORING_SECTION);
1078
+ await writeFile(conventionsPath, content.trimEnd() + "\n" + SKILL_AUTHORING_SECTION);
1071
1079
  log.success("Added Skill Authoring section to .claude/rules/conventions.md");
1072
1080
  return true;
1073
1081
  }
1074
1082
  async function createEnhanceSkill(root) {
1075
- const skillDir = join4(root, ".claude", "skills", "lp-enhance");
1076
- const skillPath = join4(skillDir, "SKILL.md");
1077
- const globalPath = join4(homedir(), ".claude", "skills", "lp-enhance", "SKILL.md");
1078
- const legacyProject = join4(root, ".claude", "commands", "lp-enhance.md");
1079
- const legacyGlobal = join4(homedir(), ".claude", "commands", "lp-enhance.md");
1083
+ const skillDir = join3(root, ".claude", "skills", "lp-enhance");
1084
+ const skillPath = join3(skillDir, "SKILL.md");
1085
+ const globalPath = join3(homedir(), ".claude", "skills", "lp-enhance", "SKILL.md");
1086
+ const legacyProject = join3(root, ".claude", "commands", "lp-enhance.md");
1087
+ const legacyGlobal = join3(homedir(), ".claude", "commands", "lp-enhance.md");
1080
1088
  if (await fileExists(skillPath) || await fileExists(globalPath) || await fileExists(legacyProject) || await fileExists(legacyGlobal)) return false;
1081
- await mkdir2(skillDir, { recursive: true });
1082
- await writeFile2(skillPath, generateEnhanceSkill());
1089
+ await mkdir(skillDir, { recursive: true });
1090
+ await writeFile(skillPath, generateEnhanceSkill());
1083
1091
  log.success("Generated /lp-enhance skill (.claude/skills/lp-enhance/)");
1084
1092
  return true;
1085
1093
  }
1086
1094
  async function updateEnhanceSkill(root) {
1087
- const projectPath = join4(root, ".claude", "skills", "lp-enhance", "SKILL.md");
1088
- const globalPath = join4(homedir(), ".claude", "skills", "lp-enhance", "SKILL.md");
1095
+ const projectPath = join3(root, ".claude", "skills", "lp-enhance", "SKILL.md");
1096
+ const globalPath = join3(homedir(), ".claude", "skills", "lp-enhance", "SKILL.md");
1089
1097
  const targetPath = await fileExists(projectPath) ? projectPath : await fileExists(globalPath) ? globalPath : null;
1090
1098
  if (!targetPath) return false;
1091
- await writeFile2(targetPath, generateEnhanceSkill());
1099
+ await writeFile(targetPath, generateEnhanceSkill());
1092
1100
  log.success("Updated /lp-enhance skill to latest version");
1093
1101
  return true;
1094
1102
  }
@@ -1117,10 +1125,16 @@ var colors = {
1117
1125
  return map[sev](` ${sev.toUpperCase()} `);
1118
1126
  }
1119
1127
  };
1128
+ var warnedKeys = /* @__PURE__ */ new Set();
1120
1129
  var log = {
1121
1130
  success: (msg) => console.log(` ${chalk.green("\u2713")} ${msg}`),
1122
1131
  error: (msg) => console.log(` ${chalk.red("\u2717")} ${msg}`),
1123
1132
  warn: (msg) => console.log(` ${chalk.yellow("!")} ${msg}`),
1133
+ warnOnce: (key, msg) => {
1134
+ if (warnedKeys.has(key)) return;
1135
+ warnedKeys.add(key);
1136
+ console.log(` ${chalk.yellow("!")} ${msg}`);
1137
+ },
1124
1138
  step: (msg) => console.log(` ${chalk.cyan("\u2192")} ${msg}`),
1125
1139
  info: (msg) => console.log(` ${chalk.dim("\xB7")} ${msg}`),
1126
1140
  blank: () => console.log()
@@ -1189,6 +1203,41 @@ function renderDoctorReport(results, options) {
1189
1203
  return { overallScore, actionableCount: actionable.length };
1190
1204
  }
1191
1205
 
1206
+ // src/lib/settings.ts
1207
+ async function readJsonFile(path) {
1208
+ let raw;
1209
+ try {
1210
+ raw = await readFile4(path, "utf-8");
1211
+ } catch (err) {
1212
+ const code = err.code;
1213
+ if (code === "ENOENT") return {};
1214
+ log.warnOnce(`read:${path}`, `Could not read ${path}: ${err.message}`);
1215
+ return null;
1216
+ }
1217
+ try {
1218
+ return JSON.parse(raw);
1219
+ } catch (err) {
1220
+ log.warnOnce(`parse:${path}`, `${path} is not valid JSON: ${err.message}. Treating as unreadable to avoid clobbering it.`);
1221
+ return null;
1222
+ }
1223
+ }
1224
+ async function readSettingsJson(root) {
1225
+ return readJsonFile(join4(root, ".claude", "settings.json"));
1226
+ }
1227
+ async function writeSettingsJson(root, settings) {
1228
+ const dir = join4(root, ".claude");
1229
+ await mkdir2(dir, { recursive: true });
1230
+ await writeFile2(join4(dir, "settings.json"), JSON.stringify(settings, null, 2) + "\n");
1231
+ }
1232
+ async function readSettingsLocalJson(root) {
1233
+ return readJsonFile(join4(root, ".claude", "settings.local.json"));
1234
+ }
1235
+ async function writeSettingsLocalJson(root, settings) {
1236
+ const dir = join4(root, ".claude");
1237
+ await mkdir2(dir, { recursive: true });
1238
+ await writeFile2(join4(dir, "settings.local.json"), JSON.stringify(settings, null, 2) + "\n");
1239
+ }
1240
+
1192
1241
  export {
1193
1242
  __export,
1194
1243
  SESSION_START_CONTENT,
@@ -1214,4 +1263,4 @@ export {
1214
1263
  printScoreCard,
1215
1264
  renderDoctorReport
1216
1265
  };
1217
- //# sourceMappingURL=chunk-UHZ2B7BE.js.map
1266
+ //# sourceMappingURL=chunk-IY3Z54UK.js.map