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.
- package/README.md +2 -1
- package/dist/{chunk-XL4ZMHA7.js → chunk-COGKNJJB.js} +14 -20
- package/dist/chunk-COGKNJJB.js.map +1 -0
- package/dist/{chunk-XJZZJEXT.js → chunk-H2E7QMF4.js} +2 -2
- package/dist/{chunk-UHZ2B7BE.js → chunk-IY3Z54UK.js} +166 -117
- package/dist/chunk-IY3Z54UK.js.map +1 -0
- package/dist/{chunk-N4GESFQC.js → chunk-RDID5P4K.js} +2 -2
- package/dist/{chunk-VLB5TDFF.js → chunk-THDBKJAV.js} +3 -3
- package/dist/cli.js +124 -32
- package/dist/cli.js.map +1 -1
- package/dist/commands/memory/server.js +3 -3
- package/dist/{context-USZJ232G.js → context-BCTOCTZD.js} +5 -5
- package/dist/{install-FUXDR2HU.js → install-L23C4YWJ.js} +58 -5
- package/dist/install-L23C4YWJ.js.map +1 -0
- package/dist/{pull-IZB5D5FC.js → pull-5KYLJ6TH.js} +7 -7
- package/dist/{push-IUNXSJZ5.js → push-UOVLT5HO.js} +7 -7
- package/dist/{require-deps-Z3CV2I3V.js → require-deps-T2QOGQQ3.js} +3 -3
- package/dist/{stats-FMKE3T6J.js → stats-GL7P24U7.js} +6 -6
- package/dist/{sync-clean-33V4VWRI.js → sync-clean-7RNYS7EH.js} +3 -3
- package/dist/{sync-status-5GMPSUQF.js → sync-status-ULZCMBQJ.js} +14 -9
- package/dist/sync-status-ULZCMBQJ.js.map +1 -0
- package/dist/{tui-7YXGNXCP.js → tui-UGBWF2WT.js} +4 -4
- package/package.json +1 -1
- package/dist/chunk-UHZ2B7BE.js.map +0 -1
- package/dist/chunk-XL4ZMHA7.js.map +0 -1
- package/dist/install-FUXDR2HU.js.map +0 -1
- package/dist/sync-status-5GMPSUQF.js.map +0 -1
- /package/dist/{chunk-XJZZJEXT.js.map → chunk-H2E7QMF4.js.map} +0 -0
- /package/dist/{chunk-N4GESFQC.js.map → chunk-RDID5P4K.js.map} +0 -0
- /package/dist/{chunk-VLB5TDFF.js.map → chunk-THDBKJAV.js.map} +0 -0
- /package/dist/{context-USZJ232G.js.map → context-BCTOCTZD.js.map} +0 -0
- /package/dist/{pull-IZB5D5FC.js.map → pull-5KYLJ6TH.js.map} +0 -0
- /package/dist/{push-IUNXSJZ5.js.map → push-UOVLT5HO.js.map} +0 -0
- /package/dist/{require-deps-Z3CV2I3V.js.map → require-deps-T2QOGQQ3.js.map} +0 -0
- /package/dist/{stats-FMKE3T6J.js.map → stats-GL7P24U7.js.map} +0 -0
- /package/dist/{sync-clean-33V4VWRI.js.map → sync-clean-7RNYS7EH.js.map} +0 -0
- /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-
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
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
|
|
105
|
-
import { join as
|
|
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
|
|
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(
|
|
121
|
-
fileExists(
|
|
122
|
-
readFileOrNull(
|
|
123
|
-
fileExists(
|
|
124
|
-
fileExists(
|
|
125
|
-
fileExists(
|
|
126
|
-
readJsonOrNull(
|
|
127
|
-
fileExists(
|
|
128
|
-
fileExists(
|
|
129
|
-
fileExists(
|
|
130
|
-
fileExists(
|
|
131
|
-
fileExists(
|
|
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(
|
|
213
|
-
fileExists(
|
|
214
|
-
fileExists(
|
|
215
|
-
fileExists(
|
|
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
|
|
633
|
-
import { join as
|
|
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 =
|
|
760
|
+
const mcpJsonPath = join2(root, ".mcp.json");
|
|
759
761
|
try {
|
|
760
|
-
const mcpJson = JSON.parse(await
|
|
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" ?
|
|
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 =
|
|
978
|
+
const ignorePath = join3(root, ".claudeignore");
|
|
971
979
|
let content;
|
|
972
980
|
try {
|
|
973
|
-
content = await
|
|
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
|
|
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 ??
|
|
992
|
+
const claudeMdPath = targetPath ?? join3(root, "CLAUDE.md");
|
|
985
993
|
let existing;
|
|
986
994
|
try {
|
|
987
|
-
existing = await
|
|
995
|
+
existing = await readFile3(claudeMdPath, "utf-8");
|
|
988
996
|
} catch {
|
|
989
997
|
if (!targetPath) return false;
|
|
990
|
-
await
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
1048
|
-
await
|
|
1049
|
-
|
|
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 =
|
|
1070
|
+
const conventionsPath = join3(root, ".claude", "rules", "conventions.md");
|
|
1063
1071
|
let content;
|
|
1064
1072
|
try {
|
|
1065
|
-
content = await
|
|
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
|
|
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 =
|
|
1076
|
-
const skillPath =
|
|
1077
|
-
const globalPath =
|
|
1078
|
-
const legacyProject =
|
|
1079
|
-
const legacyGlobal =
|
|
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
|
|
1082
|
-
await
|
|
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 =
|
|
1088
|
-
const globalPath =
|
|
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
|
|
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-
|
|
1266
|
+
//# sourceMappingURL=chunk-IY3Z54UK.js.map
|