claude-launchpad 0.10.0 → 0.10.1-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +117 -120
  2. package/dist/chunk-7YDBTED2.js +154 -0
  3. package/dist/chunk-7YDBTED2.js.map +1 -0
  4. package/dist/chunk-J5NT4JGE.js +77 -0
  5. package/dist/chunk-J5NT4JGE.js.map +1 -0
  6. package/dist/{chunk-TALTTAMW.js → chunk-JXFTVFPC.js} +43 -3
  7. package/dist/chunk-JXFTVFPC.js.map +1 -0
  8. package/dist/{chunk-4AF3NGNF.js → chunk-YEGOHLE7.js} +3 -3
  9. package/dist/{chunk-JTKRLIEV.js → chunk-ZMSHFAZQ.js} +2 -1
  10. package/dist/cli.js +47 -90
  11. package/dist/cli.js.map +1 -1
  12. package/dist/commands/memory/server.js +39 -75
  13. package/dist/commands/memory/server.js.map +1 -1
  14. package/dist/{context-AGNCZJPC.js → context-SGPGEJV4.js} +4 -4
  15. package/dist/{extract-RPRYPT3Z.js → extract-T32FMLN5.js} +4 -4
  16. package/dist/{install-PSSMUGLO.js → install-OKLYDFBJ.js} +2 -2
  17. package/dist/pull-4VKUDKTB.js +66 -0
  18. package/dist/pull-4VKUDKTB.js.map +1 -0
  19. package/dist/push-WI3ZIPZU.js +89 -0
  20. package/dist/push-WI3ZIPZU.js.map +1 -0
  21. package/dist/{stats-DAUYJ4BE.js → stats-77WLARNA.js} +4 -4
  22. package/dist/{tui-A4TJFNE3.js → tui-YV7AFJFR.js} +3 -3
  23. package/package.json +1 -1
  24. package/dist/chunk-TALTTAMW.js.map +0 -1
  25. /package/dist/{chunk-4AF3NGNF.js.map → chunk-YEGOHLE7.js.map} +0 -0
  26. /package/dist/{chunk-JTKRLIEV.js.map → chunk-ZMSHFAZQ.js.map} +0 -0
  27. /package/dist/{context-AGNCZJPC.js.map → context-SGPGEJV4.js.map} +0 -0
  28. /package/dist/{extract-RPRYPT3Z.js.map → extract-T32FMLN5.js.map} +0 -0
  29. /package/dist/{install-PSSMUGLO.js.map → install-OKLYDFBJ.js.map} +0 -0
  30. /package/dist/{stats-DAUYJ4BE.js.map → stats-77WLARNA.js.map} +0 -0
  31. /package/dist/{tui-A4TJFNE3.js.map → tui-YV7AFJFR.js.map} +0 -0
@@ -70,7 +70,17 @@ var MemoryRepo = class {
70
70
  topInjected: db.prepare(`
71
71
  SELECT id, title, injection_count FROM memories
72
72
  WHERE injection_count > 0 ORDER BY injection_count DESC LIMIT ?
73
- `)
73
+ `),
74
+ upsertSync: db.prepare(`
75
+ INSERT OR REPLACE INTO memories
76
+ (id, type, title, content, context, source, project, tags, importance,
77
+ access_count, injection_count, created_at, updated_at, last_accessed, embedding)
78
+ VALUES (@id, @type, @title, @content, @context, @source, @project, @tags, @importance,
79
+ @accessCount, @injectionCount, @createdAt, @updatedAt, @lastAccessed, NULL)
80
+ `),
81
+ getAllStrictProject: db.prepare(
82
+ "SELECT * FROM memories WHERE project = ? ORDER BY created_at DESC"
83
+ )
74
84
  };
75
85
  }
76
86
  create(input, _embedding = null) {
@@ -207,6 +217,31 @@ var MemoryRepo = class {
207
217
  const rows = this.#stmts.topInjected.all(limit);
208
218
  return rows.map((r) => ({ id: r.id, title: r.title, injectionCount: r.injection_count }));
209
219
  }
220
+ upsertFromSync(row) {
221
+ this.#stmts.upsertSync.run({
222
+ id: row.id,
223
+ type: row.type,
224
+ title: row.title,
225
+ content: row.content,
226
+ context: row.context,
227
+ source: row.source,
228
+ project: row.project,
229
+ tags: JSON.stringify(row.tags),
230
+ importance: row.importance,
231
+ accessCount: row.access_count,
232
+ injectionCount: row.injection_count,
233
+ createdAt: row.created_at,
234
+ updatedAt: row.updated_at,
235
+ lastAccessed: row.last_accessed
236
+ });
237
+ }
238
+ getAllForSync(project) {
239
+ if (project) {
240
+ const rows = this.#stmts.getAllStrictProject.all(project);
241
+ return rows.map(rowToMemory);
242
+ }
243
+ return this.getAll();
244
+ }
210
245
  };
211
246
 
212
247
  // src/commands/memory/storage/relation-repo.ts
@@ -237,7 +272,8 @@ var RelationRepo = class {
237
272
  countByMemory: db.prepare(`
238
273
  SELECT COUNT(*) as count FROM relations WHERE source_id = ? OR target_id = ?
239
274
  `),
240
- count: db.prepare("SELECT COUNT(*) as count FROM relations")
275
+ count: db.prepare("SELECT COUNT(*) as count FROM relations"),
276
+ getAll: db.prepare("SELECT * FROM relations")
241
277
  };
242
278
  }
243
279
  create(sourceId, targetId, relationType) {
@@ -268,6 +304,10 @@ var RelationRepo = class {
268
304
  const row = this.#stmts.count.get();
269
305
  return row.count;
270
306
  }
307
+ getAll() {
308
+ const rows = this.#stmts.getAll.all();
309
+ return rows.map(rowToRelation);
310
+ }
271
311
  };
272
312
 
273
313
  // src/commands/memory/storage/search-repo.ts
@@ -387,4 +427,4 @@ export {
387
427
  RelationRepo,
388
428
  SearchRepo
389
429
  };
390
- //# sourceMappingURL=chunk-TALTTAMW.js.map
430
+ //# sourceMappingURL=chunk-JXFTVFPC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/memory/storage/memory-repo.ts","../src/commands/memory/storage/relation-repo.ts","../src/commands/memory/storage/search-repo.ts"],"sourcesContent":["import type Database from 'better-sqlite3';\nimport type { Memory, MemoryType, MemorySource, StoreInput, SyncMemoryRow } from '../types.js';\nimport { randomUUID } from 'node:crypto';\n\nfunction safeParseTags(raw: string): string[] {\n try {\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? parsed.filter(t => typeof t === 'string') : [];\n } catch {\n return [];\n }\n}\n\n// ── Row shape from SQLite ─────────────────────────────────────\n\ninterface MemoryRow {\n id: string;\n type: string;\n title: string | null;\n content: string;\n context: string | null;\n source: string | null;\n project: string | null;\n tags: string;\n importance: number;\n created_at: string;\n updated_at: string;\n access_count: number;\n last_accessed: string | null;\n injection_count: number;\n embedding: Buffer | null;\n}\n\nfunction rowToMemory(row: MemoryRow): Memory {\n return {\n id: row.id,\n type: row.type as MemoryType,\n title: row.title,\n content: row.content,\n context: row.context,\n source: row.source as MemorySource | null,\n project: row.project,\n tags: safeParseTags(row.tags),\n importance: row.importance,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n accessCount: row.access_count,\n lastAccessed: row.last_accessed,\n injectionCount: row.injection_count,\n };\n}\n\n// ── Repository ────────────────────────────────────────────────\n\nexport class MemoryRepo {\n readonly #stmts;\n readonly db: Database.Database;\n\n constructor(db: Database.Database) {\n this.db = db;\n this.#stmts = {\n insert: db.prepare(`\n INSERT INTO memories (id, type, title, content, context, source, project, tags, importance, created_at, updated_at, embedding)\n VALUES (@id, @type, @title, @content, @context, @source, @project, @tags, @importance, @createdAt, @updatedAt, @embedding)\n `),\n getById: db.prepare('SELECT * FROM memories WHERE id = ?'),\n getAll: db.prepare('SELECT * FROM memories ORDER BY created_at DESC'),\n getAllByProject: db.prepare('SELECT * FROM memories WHERE project = ? OR project IS NULL ORDER BY created_at DESC'),\n getByType: db.prepare('SELECT * FROM memories WHERE type = ? ORDER BY created_at DESC'),\n getByTypeAndProject: db.prepare('SELECT * FROM memories WHERE type = ? AND (project = ? OR project IS NULL) ORDER BY created_at DESC'),\n getRecent: db.prepare('SELECT * FROM memories ORDER BY created_at DESC LIMIT ?'),\n getRecentByProject: db.prepare('SELECT * FROM memories WHERE project = ? OR project IS NULL ORDER BY created_at DESC LIMIT ?'),\n getRecentByTypeAndProject: db.prepare('SELECT * FROM memories WHERE type = ? AND (project = ? OR project IS NULL) ORDER BY created_at DESC LIMIT ?'),\n update: db.prepare(`\n UPDATE memories\n SET title = @title, content = @content, context = @context, tags = @tags,\n importance = @importance, updated_at = @updatedAt, embedding = @embedding\n WHERE id = @id\n `),\n updateImportance: db.prepare('UPDATE memories SET importance = ?, updated_at = ? WHERE id = ?'),\n updateImportanceOnly: db.prepare('UPDATE memories SET importance = ? WHERE id = ?'),\n incrementAccess: db.prepare(`\n UPDATE memories SET access_count = access_count + 1, last_accessed = ? WHERE id = ?\n `),\n incrementInjection: db.prepare(`\n UPDATE memories SET injection_count = injection_count + 1 WHERE id = ?\n `),\n softDelete: db.prepare('UPDATE memories SET importance = 0, updated_at = ? WHERE id = ?'),\n hardDelete: db.prepare('DELETE FROM memories WHERE id = ?'),\n deleteByType: db.prepare('DELETE FROM memories WHERE type = ?'),\n count: db.prepare('SELECT COUNT(*) as count FROM memories'),\n countByProject: db.prepare('SELECT COUNT(*) as count FROM memories WHERE project = ?'),\n countByType: db.prepare('SELECT type, COUNT(*) as count FROM memories GROUP BY type'),\n dateRange: db.prepare('SELECT MIN(created_at) as oldest, MAX(created_at) as newest FROM memories'),\n topInjected: db.prepare(`\n SELECT id, title, injection_count FROM memories\n WHERE injection_count > 0 ORDER BY injection_count DESC LIMIT ?\n `),\n upsertSync: db.prepare(`\n INSERT OR REPLACE INTO memories\n (id, type, title, content, context, source, project, tags, importance,\n access_count, injection_count, created_at, updated_at, last_accessed, embedding)\n VALUES (@id, @type, @title, @content, @context, @source, @project, @tags, @importance,\n @accessCount, @injectionCount, @createdAt, @updatedAt, @lastAccessed, NULL)\n `),\n getAllStrictProject: db.prepare(\n 'SELECT * FROM memories WHERE project = ? ORDER BY created_at DESC'\n ),\n };\n }\n\n create(input: StoreInput, _embedding: Buffer | null = null): Memory {\n const now = new Date().toISOString();\n const id = randomUUID();\n\n const params = {\n id,\n type: input.type,\n title: input.title ?? null,\n content: input.content,\n context: input.context ?? null,\n source: input.source,\n project: input.project ?? null,\n tags: JSON.stringify(input.tags),\n importance: input.importance,\n createdAt: now,\n updatedAt: now,\n embedding: null,\n };\n\n this.#stmts.insert.run(params);\n\n const row: MemoryRow = {\n id,\n type: input.type,\n title: input.title ?? null,\n content: input.content,\n context: input.context ?? null,\n source: input.source,\n project: input.project ?? null,\n tags: JSON.stringify(input.tags),\n importance: input.importance,\n created_at: now,\n updated_at: now,\n access_count: 0,\n last_accessed: null,\n injection_count: 0,\n embedding: null,\n };\n\n return rowToMemory(row);\n }\n\n getById(id: string): Memory | undefined {\n const row = this.#stmts.getById.get(id) as MemoryRow | undefined;\n return row ? rowToMemory(row) : undefined;\n }\n\n getAll(project?: string): readonly Memory[] {\n if (project) {\n const rows = this.#stmts.getAllByProject.all(project) as MemoryRow[];\n return rows.map(rowToMemory);\n }\n const rows = this.#stmts.getAll.all() as MemoryRow[];\n return rows.map(rowToMemory);\n }\n\n getRecent(limit: number, project?: string, type?: MemoryType): readonly Memory[] {\n if (type && project) {\n const rows = this.#stmts.getRecentByTypeAndProject.all(type, project, limit) as MemoryRow[];\n return rows.map(rowToMemory);\n }\n if (project) {\n const rows = this.#stmts.getRecentByProject.all(project, limit) as MemoryRow[];\n return rows.map(rowToMemory);\n }\n const rows = this.#stmts.getRecent.all(limit) as MemoryRow[];\n return rows.map(rowToMemory);\n }\n\n getByType(type: MemoryType, project?: string): readonly Memory[] {\n if (project) {\n const rows = this.#stmts.getByTypeAndProject.all(type, project) as MemoryRow[];\n return rows.map(rowToMemory);\n }\n const rows = this.#stmts.getByType.all(type) as MemoryRow[];\n return rows.map(rowToMemory);\n }\n\n updateContent(id: string, updates: {\n readonly title?: string | null;\n readonly content?: string;\n readonly context?: string | null;\n readonly tags?: readonly string[];\n readonly importance?: number;\n }): boolean {\n const existing = this.getById(id);\n if (!existing) return false;\n\n const now = new Date().toISOString();\n\n const params = {\n id,\n title: updates.title !== undefined ? updates.title : existing.title,\n content: updates.content ?? existing.content,\n context: updates.context !== undefined ? updates.context : existing.context,\n tags: JSON.stringify(updates.tags ?? existing.tags),\n importance: updates.importance ?? existing.importance,\n updatedAt: now,\n embedding: null,\n };\n\n this.#stmts.update.run(params);\n return true;\n }\n\n updateImportance(id: string, importance: number): boolean {\n const now = new Date().toISOString();\n const result = this.#stmts.updateImportance.run(importance, now, id);\n return result.changes > 0;\n }\n\n /** Update importance without touching updated_at - used by decay to avoid resetting the clock. */\n updateImportanceOnly(id: string, importance: number): boolean {\n const result = this.#stmts.updateImportanceOnly.run(importance, id);\n return result.changes > 0;\n }\n\n incrementAccess(id: string): void {\n this.#stmts.incrementAccess.run(new Date().toISOString(), id);\n }\n\n incrementInjection(id: string): void {\n this.#stmts.incrementInjection.run(id);\n }\n\n softDelete(id: string): boolean {\n const result = this.#stmts.softDelete.run(new Date().toISOString(), id);\n return result.changes > 0;\n }\n\n hardDelete(id: string): boolean {\n const result = this.#stmts.hardDelete.run(id);\n return result.changes > 0;\n }\n\n deleteByType(type: MemoryType): number {\n const result = this.#stmts.deleteByType.run(type);\n return result.changes;\n }\n\n count(project?: string): number {\n if (project) {\n const row = this.#stmts.countByProject.get(project) as { count: number };\n return row.count;\n }\n const row = this.#stmts.count.get() as { count: number };\n return row.count;\n }\n\n countByType(): Record<string, number> {\n const rows = this.#stmts.countByType.all() as { type: string; count: number }[];\n return Object.fromEntries(rows.map(r => [r.type, r.count]));\n }\n\n dateRange(): { oldest: string | null; newest: string | null } {\n const row = this.#stmts.dateRange.get() as { oldest: string | null; newest: string | null };\n return { oldest: row.oldest, newest: row.newest };\n }\n\n topInjected(limit: number = 5): readonly { id: string; title: string | null; injectionCount: number }[] {\n const rows = this.#stmts.topInjected.all(limit) as { id: string; title: string | null; injection_count: number }[];\n return rows.map(r => ({ id: r.id, title: r.title, injectionCount: r.injection_count }));\n }\n\n upsertFromSync(row: SyncMemoryRow): void {\n this.#stmts.upsertSync.run({\n id: row.id,\n type: row.type,\n title: row.title,\n content: row.content,\n context: row.context,\n source: row.source,\n project: row.project,\n tags: JSON.stringify(row.tags),\n importance: row.importance,\n accessCount: row.access_count,\n injectionCount: row.injection_count,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n lastAccessed: row.last_accessed,\n });\n }\n\n getAllForSync(project?: string): readonly Memory[] {\n if (project) {\n const rows = this.#stmts.getAllStrictProject.all(project) as MemoryRow[];\n return rows.map(rowToMemory);\n }\n return this.getAll();\n }\n}\n","import type Database from 'better-sqlite3';\nimport type { Relation, RelationType } from '../types.js';\n\ninterface RelationRow {\n source_id: string;\n target_id: string;\n relation_type: string;\n created_at: string;\n}\n\nfunction rowToRelation(row: RelationRow): Relation {\n return {\n sourceId: row.source_id,\n targetId: row.target_id,\n relationType: row.relation_type as RelationType,\n createdAt: row.created_at,\n };\n}\n\nexport class RelationRepo {\n readonly #stmts;\n\n constructor(db: Database.Database) {\n this.#stmts = {\n insert: db.prepare(`\n INSERT OR IGNORE INTO relations (source_id, target_id, relation_type)\n VALUES (?, ?, ?)\n `),\n getBySource: db.prepare('SELECT * FROM relations WHERE source_id = ?'),\n getByTarget: db.prepare('SELECT * FROM relations WHERE target_id = ?'),\n getByMemory: db.prepare(`\n SELECT * FROM relations WHERE source_id = ? OR target_id = ?\n `),\n delete: db.prepare(`\n DELETE FROM relations WHERE source_id = ? AND target_id = ? AND relation_type = ?\n `),\n countByMemory: db.prepare(`\n SELECT COUNT(*) as count FROM relations WHERE source_id = ? OR target_id = ?\n `),\n count: db.prepare('SELECT COUNT(*) as count FROM relations'),\n getAll: db.prepare('SELECT * FROM relations'),\n };\n }\n\n create(sourceId: string, targetId: string, relationType: RelationType): boolean {\n const result = this.#stmts.insert.run(sourceId, targetId, relationType);\n return result.changes > 0;\n }\n\n getBySource(sourceId: string): readonly Relation[] {\n const rows = this.#stmts.getBySource.all(sourceId) as RelationRow[];\n return rows.map(rowToRelation);\n }\n\n getByTarget(targetId: string): readonly Relation[] {\n const rows = this.#stmts.getByTarget.all(targetId) as RelationRow[];\n return rows.map(rowToRelation);\n }\n\n getByMemory(memoryId: string): readonly Relation[] {\n const rows = this.#stmts.getByMemory.all(memoryId, memoryId) as RelationRow[];\n return rows.map(rowToRelation);\n }\n\n delete(sourceId: string, targetId: string, relationType: RelationType): boolean {\n const result = this.#stmts.delete.run(sourceId, targetId, relationType);\n return result.changes > 0;\n }\n\n countByMemory(memoryId: string): number {\n const row = this.#stmts.countByMemory.get(memoryId, memoryId) as { count: number };\n return row.count;\n }\n\n count(): number {\n const row = this.#stmts.count.get() as { count: number };\n return row.count;\n }\n\n getAll(): readonly Relation[] {\n const rows = this.#stmts.getAll.all() as RelationRow[];\n return rows.map(rowToRelation);\n }\n}\n","import type Database from 'better-sqlite3';\nimport type { FtsMatch, MemoryType } from '../types.js';\n\n// ── FTS5 Search ───────────────────────────────────────────────\n\nexport interface FtsSearchOptions {\n readonly query: string;\n readonly limit: number;\n readonly type?: MemoryType;\n readonly minImportance?: number;\n readonly project?: string;\n}\n\n\nexport class SearchRepo {\n readonly #stmts;\n\n constructor(db: Database.Database) {\n // FTS5 search with BM25 ranking (weights: title=5.0, content=1.0, tags=2.0)\n this.#stmts = {\n ftsSearch: db.prepare(`\n SELECT\n m.rowid,\n m.id as memory_id,\n bm25(memories_fts, 5.0, 1.0, 2.0) as rank\n FROM memories_fts f\n JOIN memories m ON m.rowid = f.rowid\n WHERE memories_fts MATCH @query\n ORDER BY rank\n LIMIT @limit\n `),\n ftsSearchByProject: db.prepare(`\n SELECT\n m.rowid,\n m.id as memory_id,\n bm25(memories_fts, 5.0, 1.0, 2.0) as rank\n FROM memories_fts f\n JOIN memories m ON m.rowid = f.rowid\n WHERE memories_fts MATCH @query\n AND (m.project = @project OR m.project IS NULL)\n ORDER BY rank\n LIMIT @limit\n `),\n ftsSearchFiltered: db.prepare(`\n SELECT\n m.rowid,\n m.id as memory_id,\n bm25(memories_fts, 5.0, 1.0, 2.0) as rank\n FROM memories_fts f\n JOIN memories m ON m.rowid = f.rowid\n WHERE memories_fts MATCH @query\n AND m.type = @type\n AND m.importance >= @minImportance\n ORDER BY rank\n LIMIT @limit\n `),\n ftsSearchFilteredByProject: db.prepare(`\n SELECT\n m.rowid,\n m.id as memory_id,\n bm25(memories_fts, 5.0, 1.0, 2.0) as rank\n FROM memories_fts f\n JOIN memories m ON m.rowid = f.rowid\n WHERE memories_fts MATCH @query\n AND m.type = @type\n AND m.importance >= @minImportance\n AND (m.project = @project OR m.project IS NULL)\n ORDER BY rank\n LIMIT @limit\n `),\n };\n }\n\n /**\n * Full-text search using BM25 ranking.\n * Returns matches sorted by relevance (most relevant first).\n */\n searchFts(options: FtsSearchOptions): readonly FtsMatch[] {\n const ftsQuery = toFtsQuery(options.query);\n if (!ftsQuery) return [];\n\n try {\n const hasType = !!options.type;\n const hasProject = !!options.project;\n\n let rows: FtsRow[];\n if (hasType && hasProject) {\n rows = this.#stmts.ftsSearchFilteredByProject.all({\n query: ftsQuery, limit: options.limit,\n type: options.type, minImportance: options.minImportance ?? 0,\n project: options.project,\n }) as FtsRow[];\n } else if (hasType) {\n rows = this.#stmts.ftsSearchFiltered.all({\n query: ftsQuery, limit: options.limit,\n type: options.type, minImportance: options.minImportance ?? 0,\n }) as FtsRow[];\n } else if (hasProject) {\n rows = this.#stmts.ftsSearchByProject.all({\n query: ftsQuery, limit: options.limit,\n project: options.project,\n }) as FtsRow[];\n } else {\n rows = this.#stmts.ftsSearch.all({\n query: ftsQuery, limit: options.limit,\n }) as FtsRow[];\n }\n\n return rows.map(r => ({\n rowid: r.rowid,\n memoryId: r.memory_id,\n rank: r.rank,\n }));\n } catch (err) {\n // FTS5 MATCH throws on invalid query syntax - degrade gracefully\n console.error('[agentic-memory] FTS5 search error:', err instanceof Error ? err.message : err);\n return [];\n }\n }\n\n}\n\n// ── Internal helpers ──────────────────────────────────────────\n\ninterface FtsRow {\n rowid: number;\n memory_id: string;\n rank: number;\n}\n\n/**\n * Convert a natural language query to FTS5 query syntax.\n * Wraps each word in quotes to avoid syntax errors from special characters.\n */\nfunction toFtsQuery(input: string): string | null {\n const words = input\n .replace(/[^\\w\\s]/g, ' ') // strip punctuation\n .split(/\\s+/)\n .filter(w => w.length > 0);\n\n if (words.length === 0) return null;\n\n // Use OR between words for broader recall\n return words.map(w => `\"${w}\"`).join(' OR ');\n}\n"],"mappings":"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","names":["rows","row"]}
@@ -3,14 +3,14 @@ import {
3
3
  MemoryRepo,
4
4
  RelationRepo,
5
5
  SearchRepo
6
- } from "./chunk-TALTTAMW.js";
6
+ } from "./chunk-JXFTVFPC.js";
7
7
  import {
8
8
  closeDatabase,
9
9
  createDatabase,
10
10
  loadConfig,
11
11
  migrate,
12
12
  resolveDataDir
13
- } from "./chunk-JTKRLIEV.js";
13
+ } from "./chunk-ZMSHFAZQ.js";
14
14
 
15
15
  // src/commands/memory/subcommands/init-storage.ts
16
16
  function initStorage(dbPath) {
@@ -32,4 +32,4 @@ function initStorage(dbPath) {
32
32
  export {
33
33
  initStorage
34
34
  };
35
- //# sourceMappingURL=chunk-4AF3NGNF.js.map
35
+ //# sourceMappingURL=chunk-YEGOHLE7.js.map
@@ -278,6 +278,7 @@ function migrate(db) {
278
278
  }
279
279
 
280
280
  export {
281
+ DEFAULT_CONFIG,
281
282
  DEFAULT_DECAY_PARAMS,
282
283
  SCORING_WEIGHTS,
283
284
  INJECTION_WEIGHTS,
@@ -293,4 +294,4 @@ export {
293
294
  closeDatabase,
294
295
  migrate
295
296
  };
296
- //# sourceMappingURL=chunk-JTKRLIEV.js.map
297
+ //# sourceMappingURL=chunk-ZMSHFAZQ.js.map
package/dist/cli.js CHANGED
@@ -591,7 +591,7 @@ Also review .claude/settings.json hooks:
591
591
 
592
592
  // src/commands/init/generators/backlog.ts
593
593
  function generateBacklogMd(options) {
594
- return `# ${options.name} \u2014 Backlog
594
+ return `# ${options.name} - Backlog
595
595
 
596
596
  > Features discussed but deferred. Pick up when relevant.
597
597
  > Priority: P0 = next sprint, P1 = soon, P2 = when relevant.
@@ -1566,26 +1566,29 @@ async function tryFix(issue, root, detected) {
1566
1566
  );
1567
1567
  return entry ? entry.fix(root, detected) : false;
1568
1568
  }
1569
- async function addEnvProtectionHook(root) {
1569
+ async function addHook(root, event, dedupKeyword, entry, successMsg) {
1570
1570
  const settings = await readSettingsJson(root);
1571
1571
  const hooks = settings.hooks ?? {};
1572
- const preToolUse = hooks.PreToolUse ?? [];
1573
- const alreadyHas = preToolUse.some((g) => {
1572
+ const hookList = hooks[event] ?? [];
1573
+ const alreadyHas = hookList.some((g) => {
1574
1574
  const nested = g.hooks;
1575
- return nested?.some((h) => String(h.command ?? "").includes(".env"));
1575
+ return nested?.some((h) => String(h.command ?? "").includes(dedupKeyword));
1576
1576
  });
1577
1577
  if (alreadyHas) return false;
1578
- preToolUse.push({
1578
+ hookList.push(entry);
1579
+ settings.hooks = { ...hooks, [event]: hookList };
1580
+ await writeSettingsJson(root, settings);
1581
+ log.success(successMsg);
1582
+ return true;
1583
+ }
1584
+ async function addEnvProtectionHook(root) {
1585
+ return addHook(root, "PreToolUse", ".env", {
1579
1586
  matcher: "Read|Write|Edit",
1580
1587
  hooks: [{
1581
1588
  type: "command",
1582
1589
  command: `echo "$TOOL_INPUT_FILE_PATH" | grep -qE '\\.(env|env\\..*)$' && ! echo "$TOOL_INPUT_FILE_PATH" | grep -q '.env.example' && echo 'BLOCKED: .env files contain secrets' && exit 1; exit 0`
1583
1590
  }]
1584
- });
1585
- settings.hooks = { ...hooks, PreToolUse: preToolUse };
1586
- await writeSettingsJson(root, settings);
1587
- log.success("Added .env file protection hook (PreToolUse)");
1588
- return true;
1591
+ }, "Added .env file protection hook (PreToolUse)");
1589
1592
  }
1590
1593
  async function addAutoFormatHook(root, detected) {
1591
1594
  if (!detected.language) return false;
@@ -1600,82 +1603,41 @@ async function addAutoFormatHook(root, detected) {
1600
1603
  };
1601
1604
  const config = formatters[detected.language];
1602
1605
  if (!config) return false;
1603
- const settings = await readSettingsJson(root);
1604
- const hooks = settings.hooks ?? {};
1605
- const postToolUse = hooks.PostToolUse ?? [];
1606
- const alreadyHas = postToolUse.some((g) => {
1607
- const nested = g.hooks;
1608
- return nested?.some((h) => String(h.command ?? "").includes("format"));
1609
- });
1610
- if (alreadyHas) return false;
1611
1606
  const extChecks = config.extensions.map((ext) => `[ "$ext" = "${ext}" ]`).join(" || ");
1612
- postToolUse.push({
1607
+ return addHook(root, "PostToolUse", "format", {
1613
1608
  matcher: "Write|Edit",
1614
1609
  hooks: [{
1615
1610
  type: "command",
1616
1611
  command: `ext=\${TOOL_INPUT_FILE_PATH##*.}; (${extChecks}) && ${config.command} "$TOOL_INPUT_FILE_PATH" 2>/dev/null; exit 0`
1617
1612
  }]
1618
- });
1619
- settings.hooks = { ...hooks, PostToolUse: postToolUse };
1620
- await writeSettingsJson(root, settings);
1621
- log.success(`Added auto-format hook (PostToolUse \u2192 ${config.command})`);
1622
- return true;
1613
+ }, `Added auto-format hook (PostToolUse \u2192 ${config.command})`);
1623
1614
  }
1624
1615
  async function addForcePushProtection(root) {
1625
- const settings = await readSettingsJson(root);
1626
- const hooks = settings.hooks ?? {};
1627
- const preToolUse = hooks.PreToolUse ?? [];
1628
- const alreadyHas = preToolUse.some((g) => {
1629
- const nested = g.hooks;
1630
- return nested?.some((h) => String(h.command ?? "").includes("force"));
1631
- });
1632
- if (alreadyHas) return false;
1633
- preToolUse.push({
1616
+ return addHook(root, "PreToolUse", "force", {
1634
1617
  matcher: "Bash",
1635
1618
  hooks: [{
1636
1619
  type: "command",
1637
1620
  command: `echo "$TOOL_INPUT_COMMAND" | grep -qE 'push.*--force|push.*-f' && echo 'WARNING: Force push detected \u2014 this can destroy remote history' && exit 1; exit 0`
1638
1621
  }]
1639
- });
1640
- settings.hooks = { ...hooks, PreToolUse: preToolUse };
1641
- await writeSettingsJson(root, settings);
1642
- log.success("Added force-push protection hook (PreToolUse \u2192 Bash)");
1643
- return true;
1622
+ }, "Added force-push protection hook (PreToolUse \u2192 Bash)");
1644
1623
  }
1645
1624
  async function addPostCompactHook(root) {
1646
- const settings = await readSettingsJson(root);
1647
- const hooks = settings.hooks ?? {};
1648
- const postCompact = hooks.PostCompact ?? [];
1649
- const alreadyHas = postCompact.length > 0;
1650
- if (alreadyHas) return false;
1651
- postCompact.push({
1625
+ return addHook(root, "PostCompact", "TASKS.md", {
1652
1626
  matcher: "",
1653
1627
  hooks: [{
1654
1628
  type: "command",
1655
1629
  command: "cat TASKS.md 2>/dev/null; exit 0"
1656
1630
  }]
1657
- });
1658
- settings.hooks = { ...hooks, PostCompact: postCompact };
1659
- await writeSettingsJson(root, settings);
1660
- log.success("Added PostCompact hook (re-injects TASKS.md after compaction)");
1661
- return true;
1631
+ }, "Added PostCompact hook (re-injects TASKS.md after compaction)");
1662
1632
  }
1663
1633
  async function addSessionStartHook(root) {
1664
- const settings = await readSettingsJson(root);
1665
- const hooks = settings.hooks ?? {};
1666
- const sessionStart = hooks.SessionStart ?? [];
1667
- if (sessionStart.length > 0) return false;
1668
- sessionStart.push({
1634
+ return addHook(root, "SessionStart", "TASKS.md", {
1669
1635
  matcher: "startup|resume",
1670
1636
  hooks: [{
1671
1637
  type: "command",
1672
1638
  command: "cat TASKS.md 2>/dev/null; exit 0"
1673
1639
  }]
1674
- });
1675
- settings.hooks = { ...hooks, SessionStart: sessionStart };
1676
- await writeSettingsJson(root, settings);
1677
- log.success("Added SessionStart hook (injects TASKS.md at startup)");
1678
- return true;
1640
+ }, "Added SessionStart hook (injects TASKS.md at startup)");
1679
1641
  }
1680
1642
  async function migrateAttribution(root) {
1681
1643
  const settings = await readSettingsJson(root);
@@ -1758,7 +1720,7 @@ async function createBacklogMd(root) {
1758
1720
  } catch {
1759
1721
  }
1760
1722
  const name = root.split("/").pop() ?? "Project";
1761
- await writeFile2(backlogPath, `# ${name} \u2014 Backlog
1723
+ await writeFile2(backlogPath, `# ${name} - Backlog
1762
1724
 
1763
1725
  > Features discussed but deferred. Pick up when relevant.
1764
1726
  > Priority: P0 = next sprint, P1 = soon, P2 = when relevant.
@@ -2183,7 +2145,7 @@ async function listYamlFiles(dir) {
2183
2145
  }
2184
2146
 
2185
2147
  // src/commands/eval/runner.ts
2186
- import { mkdir as mkdir3, writeFile as writeFile3, readFile as readFile6, readdir as readdir4, rm, cp, access as access6 } from "fs/promises";
2148
+ import { mkdir as mkdir3, writeFile as writeFile3, readFile as readFile6, readdir as readdir4, rm, cp } from "fs/promises";
2187
2149
  import { join as join8, dirname as dirname3 } from "path";
2188
2150
  import { tmpdir } from "os";
2189
2151
  import { randomUUID } from "crypto";
@@ -2248,27 +2210,19 @@ async function copyProjectConfig(sandboxDir, projectRoot) {
2248
2210
  const claudeDir = join8(projectRoot, ".claude");
2249
2211
  const sandboxClaudeDir = join8(sandboxDir, ".claude");
2250
2212
  const settingsPath = join8(claudeDir, "settings.json");
2251
- if (await fileExistsSafe(settingsPath)) {
2213
+ if (await fileExists(settingsPath)) {
2252
2214
  await mkdir3(sandboxClaudeDir, { recursive: true });
2253
2215
  await cp(settingsPath, join8(sandboxClaudeDir, "settings.json"));
2254
2216
  }
2255
2217
  const rulesDir = join8(claudeDir, "rules");
2256
- if (await fileExistsSafe(rulesDir)) {
2218
+ if (await fileExists(rulesDir)) {
2257
2219
  await cp(rulesDir, join8(sandboxClaudeDir, "rules"), { recursive: true });
2258
2220
  }
2259
2221
  const ignorePath = join8(projectRoot, ".claudeignore");
2260
- if (await fileExistsSafe(ignorePath)) {
2222
+ if (await fileExists(ignorePath)) {
2261
2223
  await cp(ignorePath, join8(sandboxDir, ".claudeignore"));
2262
2224
  }
2263
2225
  }
2264
- async function fileExistsSafe(path) {
2265
- try {
2266
- await access6(path);
2267
- return true;
2268
- } catch {
2269
- return false;
2270
- }
2271
- }
2272
2226
  async function runClaudeInSandbox(cwd, prompt, timeout, model) {
2273
2227
  try {
2274
2228
  const sdk = await import("@anthropic-ai/claude-agent-sdk");
@@ -2347,9 +2301,8 @@ async function evaluateSingleCheck(check, sandboxDir) {
2347
2301
  case "grep":
2348
2302
  return checkGrep(check, sandboxDir);
2349
2303
  case "file-exists":
2350
- return checkFileExists(check, sandboxDir);
2351
2304
  case "file-absent":
2352
- return checkFileAbsent(check, sandboxDir);
2305
+ return checkFilePresence(check, sandboxDir);
2353
2306
  case "max-lines":
2354
2307
  return checkMaxLines(check, sandboxDir);
2355
2308
  case "custom":
@@ -2373,7 +2326,7 @@ async function checkGrep(check, sandboxDir) {
2373
2326
  return check.expect === "absent";
2374
2327
  }
2375
2328
  }
2376
- async function checkFileExists(check, sandboxDir) {
2329
+ async function checkFilePresence(check, sandboxDir) {
2377
2330
  try {
2378
2331
  await readFile6(join8(sandboxDir, check.target));
2379
2332
  return check.expect === "present";
@@ -2381,14 +2334,6 @@ async function checkFileExists(check, sandboxDir) {
2381
2334
  return check.expect === "absent";
2382
2335
  }
2383
2336
  }
2384
- async function checkFileAbsent(check, sandboxDir) {
2385
- try {
2386
- await readFile6(join8(sandboxDir, check.target));
2387
- return check.expect === "absent";
2388
- } catch {
2389
- return check.expect === "present";
2390
- }
2391
- }
2392
2337
  async function checkMaxLines(check, sandboxDir) {
2393
2338
  const maxLines = parseInt(check.pattern ?? "800", 10);
2394
2339
  try {
@@ -2642,7 +2587,7 @@ function createMemoryCommand() {
2642
2587
  }
2643
2588
  const { requireMemoryDeps } = await import("./require-deps-NKRCPVAO.js");
2644
2589
  await requireMemoryDeps();
2645
- const { startTui } = await import("./tui-A4TJFNE3.js");
2590
+ const { startTui } = await import("./tui-YV7AFJFR.js");
2646
2591
  await startTui();
2647
2592
  return;
2648
2593
  }
@@ -2664,25 +2609,25 @@ function createMemoryCommand() {
2664
2609
  log.info("Skipped.");
2665
2610
  return;
2666
2611
  }
2667
- const { runInstall } = await import("./install-PSSMUGLO.js");
2612
+ const { runInstall } = await import("./install-OKLYDFBJ.js");
2668
2613
  await runInstall({});
2669
2614
  } else {
2670
2615
  const { requireMemoryDeps } = await import("./require-deps-NKRCPVAO.js");
2671
2616
  await requireMemoryDeps();
2672
- const { runStats } = await import("./stats-DAUYJ4BE.js");
2617
+ const { runStats } = await import("./stats-77WLARNA.js");
2673
2618
  await runStats({});
2674
2619
  }
2675
2620
  });
2676
2621
  memory.addCommand(
2677
2622
  new Command4("context").description("Load session context (hook handler)").option("--json", "JSON output").action(async (opts) => {
2678
- const { runContext } = await import("./context-AGNCZJPC.js");
2623
+ const { runContext } = await import("./context-SGPGEJV4.js");
2679
2624
  await runContext(opts);
2680
2625
  }).helpCommand(false),
2681
2626
  { hidden: true }
2682
2627
  );
2683
2628
  memory.addCommand(
2684
2629
  new Command4("extract").description("Extract facts from transcript (hook handler)").action(async () => {
2685
- const { runExtract } = await import("./extract-RPRYPT3Z.js");
2630
+ const { runExtract } = await import("./extract-T32FMLN5.js");
2686
2631
  await runExtract();
2687
2632
  }).helpCommand(false),
2688
2633
  { hidden: true }
@@ -2694,11 +2639,23 @@ function createMemoryCommand() {
2694
2639
  }).helpCommand(false),
2695
2640
  { hidden: true }
2696
2641
  );
2642
+ memory.addCommand(
2643
+ new Command4("push").description("Push memories to GitHub Gist").option("--project <name>", "Scope to a specific project").option("-y, --yes", "Skip confirmation prompt").action(async (opts) => {
2644
+ const { runPush } = await import("./push-WI3ZIPZU.js");
2645
+ await runPush(opts);
2646
+ })
2647
+ );
2648
+ memory.addCommand(
2649
+ new Command4("pull").description("Pull memories from GitHub Gist").option("--project <name>", "Scope to a specific project").action(async (opts) => {
2650
+ const { runPull } = await import("./pull-4VKUDKTB.js");
2651
+ await runPull(opts);
2652
+ })
2653
+ );
2697
2654
  return memory;
2698
2655
  }
2699
2656
 
2700
2657
  // src/cli.ts
2701
- var program = new Command5().name("claude-launchpad").description("CLI toolkit that makes Claude Code setups measurably good").version("0.10.0", "-v, --version").action(async () => {
2658
+ var program = new Command5().name("claude-launchpad").description("CLI toolkit that makes Claude Code setups measurably good").version("0.10.1-dev.0", "-v, --version").action(async () => {
2702
2659
  const hasConfig = await fileExists(join11(process.cwd(), "CLAUDE.md")) || await fileExists(join11(process.cwd(), ".claude", "settings.json"));
2703
2660
  if (hasConfig) {
2704
2661
  await program.commands.find((c) => c.name() === "doctor")?.parseAsync([], { from: "user" });