claude-launchpad 1.2.3 → 1.4.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 (47) hide show
  1. package/README.md +2 -2
  2. package/dist/{chunk-UJP5PJTA.js → chunk-ADZ45KHX.js} +3 -3
  3. package/dist/{chunk-V4NXT4KB.js → chunk-DXDOVWOA.js} +64 -8
  4. package/dist/chunk-DXDOVWOA.js.map +1 -0
  5. package/dist/{chunk-N6X3E5AX.js → chunk-F6SLV2FR.js} +24 -5
  6. package/dist/chunk-F6SLV2FR.js.map +1 -0
  7. package/dist/{chunk-AR64LWGW.js → chunk-RJEPJ4JE.js} +25 -4
  8. package/dist/chunk-RJEPJ4JE.js.map +1 -0
  9. package/dist/{chunk-YXPJDIMK.js → chunk-UQOBOHKN.js} +33 -2
  10. package/dist/chunk-UQOBOHKN.js.map +1 -0
  11. package/dist/{chunk-J765H3HZ.js → chunk-WOC2PKT2.js} +2 -2
  12. package/dist/{chunk-F5PNKQKW.js → chunk-YZ53W47Z.js} +7 -2
  13. package/dist/chunk-YZ53W47Z.js.map +1 -0
  14. package/dist/cli.js +25 -14
  15. package/dist/cli.js.map +1 -1
  16. package/dist/commands/memory/server.js +12 -13
  17. package/dist/commands/memory/server.js.map +1 -1
  18. package/dist/{context-VAXF3EW3.js → context-NCV46PTY.js} +6 -6
  19. package/dist/{install-M3JWBGMK.js → install-DORMJYCU.js} +6 -6
  20. package/dist/install-DORMJYCU.js.map +1 -0
  21. package/dist/{pull-RKFE5ZXV.js → pull-UYLUGILU.js} +45 -20
  22. package/dist/pull-UYLUGILU.js.map +1 -0
  23. package/dist/{push-5ZJNWAE7.js → push-HWXJGSTL.js} +40 -17
  24. package/dist/push-HWXJGSTL.js.map +1 -0
  25. package/dist/{require-deps-QW2IU6I3.js → require-deps-RUXTMQUV.js} +3 -3
  26. package/dist/{stats-NPXPJNBO.js → stats-DFV24AJW.js} +7 -7
  27. package/dist/{sync-clean-JQLVE4WU.js → sync-clean-GFX5F55E.js} +2 -2
  28. package/dist/{sync-status-IYG7ZYC5.js → sync-status-VXMDWDRG.js} +9 -9
  29. package/dist/{tui-56GQMXXT.js → tui-ELOJ37ZA.js} +208 -40
  30. package/dist/tui-ELOJ37ZA.js.map +1 -0
  31. package/package.json +1 -1
  32. package/dist/chunk-AR64LWGW.js.map +0 -1
  33. package/dist/chunk-F5PNKQKW.js.map +0 -1
  34. package/dist/chunk-N6X3E5AX.js.map +0 -1
  35. package/dist/chunk-V4NXT4KB.js.map +0 -1
  36. package/dist/chunk-YXPJDIMK.js.map +0 -1
  37. package/dist/install-M3JWBGMK.js.map +0 -1
  38. package/dist/pull-RKFE5ZXV.js.map +0 -1
  39. package/dist/push-5ZJNWAE7.js.map +0 -1
  40. package/dist/tui-56GQMXXT.js.map +0 -1
  41. /package/dist/{chunk-UJP5PJTA.js.map → chunk-ADZ45KHX.js.map} +0 -0
  42. /package/dist/{chunk-J765H3HZ.js.map → chunk-WOC2PKT2.js.map} +0 -0
  43. /package/dist/{context-VAXF3EW3.js.map → context-NCV46PTY.js.map} +0 -0
  44. /package/dist/{require-deps-QW2IU6I3.js.map → require-deps-RUXTMQUV.js.map} +0 -0
  45. /package/dist/{stats-NPXPJNBO.js.map → stats-DFV24AJW.js.map} +0 -0
  46. /package/dist/{sync-clean-JQLVE4WU.js.map → sync-clean-GFX5F55E.js.map} +0 -0
  47. /package/dist/{sync-status-IYG7ZYC5.js.map → sync-status-VXMDWDRG.js.map} +0 -0
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  mergeFromRemote,
4
4
  parsePayload
5
- } from "./chunk-N6X3E5AX.js";
5
+ } from "./chunk-F6SLV2FR.js";
6
6
  import {
7
7
  assertGhAvailable,
8
8
  filenameToProject,
@@ -11,19 +11,19 @@ import {
11
11
  projectToFilename,
12
12
  readGistFile
13
13
  } from "./chunk-3UJYOWGF.js";
14
- import "./chunk-F5PNKQKW.js";
14
+ import "./chunk-YZ53W47Z.js";
15
15
  import {
16
16
  detectProject
17
17
  } from "./chunk-NAW47BYA.js";
18
18
  import {
19
19
  initStorage
20
- } from "./chunk-UJP5PJTA.js";
21
- import "./chunk-V4NXT4KB.js";
22
- import "./chunk-AR64LWGW.js";
23
- import "./chunk-J765H3HZ.js";
20
+ } from "./chunk-ADZ45KHX.js";
21
+ import "./chunk-DXDOVWOA.js";
22
+ import "./chunk-RJEPJ4JE.js";
23
+ import "./chunk-WOC2PKT2.js";
24
24
  import {
25
25
  log
26
- } from "./chunk-YXPJDIMK.js";
26
+ } from "./chunk-UQOBOHKN.js";
27
27
 
28
28
  // src/commands/memory/subcommands/pull.ts
29
29
  async function runPull(opts) {
@@ -33,7 +33,7 @@ async function runPull(opts) {
33
33
  log.error("No sync gist found. Run `memory push` first.");
34
34
  return;
35
35
  }
36
- const { requireMemoryDeps } = await import("./require-deps-QW2IU6I3.js");
36
+ const { requireMemoryDeps } = await import("./require-deps-RUXTMQUV.js");
37
37
  await requireMemoryDeps();
38
38
  const ctx = initStorage();
39
39
  try {
@@ -72,44 +72,69 @@ function pullAll(ctx, gistId) {
72
72
  log.error("No memory files found in gist.");
73
73
  return;
74
74
  }
75
+ const perProject = [];
76
+ const skipped = [];
75
77
  let totalInserted = 0;
76
78
  let totalUpdated = 0;
77
79
  let totalRelations = 0;
78
80
  for (const filename of projectFiles) {
81
+ const project = filenameToProject(filename);
82
+ if (!project) continue;
83
+ if (ctx.memoryRepo.count(project) === 0) {
84
+ skipped.push(project);
85
+ continue;
86
+ }
79
87
  const payload = parsePayload(readGistFile(gistId, filename));
80
88
  if (!payload) continue;
81
89
  const result = mergeFromRemote(ctx.memoryRepo, ctx.relationRepo, payload);
90
+ perProject.push({ project, result });
82
91
  totalInserted += result.inserted;
83
92
  totalUpdated += result.updated;
84
93
  totalRelations += result.relationsAdded;
85
94
  }
95
+ const changed = perProject.filter((p) => p.result.inserted > 0 || p.result.updated > 0 || p.result.relationsAdded > 0);
96
+ const syncedCount = perProject.length;
86
97
  log.blank();
87
- if (totalInserted === 0 && totalUpdated === 0 && totalRelations === 0) {
88
- log.step("Already in sync");
98
+ if (syncedCount === 0) {
99
+ log.step("No locally set up projects to sync");
100
+ } else if (changed.length === 0) {
101
+ log.step(`Already in sync (${syncedCount} project${syncedCount === 1 ? "" : "s"})`);
89
102
  } else {
90
- log.step("Pull complete");
91
- if (totalInserted > 0) log.info(`Inserted: ${totalInserted}`);
92
- if (totalUpdated > 0) log.info(`Updated: ${totalUpdated}`);
93
- if (totalRelations > 0) log.info(`Relations: ${totalRelations} added`);
103
+ log.step(`Pull complete (${changed.length} of ${syncedCount} projects updated)`);
104
+ for (const { project, result } of changed) {
105
+ const parts = [];
106
+ if (result.inserted > 0) parts.push(`+${result.inserted} new`);
107
+ if (result.updated > 0) parts.push(`~${result.updated} updated`);
108
+ if (result.relationsAdded > 0) parts.push(`+${result.relationsAdded} relations`);
109
+ log.info(`${project.padEnd(30)} ${parts.join(", ")}`);
110
+ }
111
+ if (totalInserted + totalUpdated + totalRelations > 0) {
112
+ log.blank();
113
+ log.info(`Total: +${totalInserted} new, ~${totalUpdated} updated, +${totalRelations} relations`);
114
+ }
115
+ }
116
+ if (skipped.length > 0) {
117
+ log.blank();
118
+ log.info(`Skipped ${skipped.length} project${skipped.length === 1 ? "" : "s"} not set up on this machine:`);
119
+ for (const project of skipped) log.info(` - ${project}`);
120
+ log.info("Run `memory pull` from a project directory to pull that project for the first time.");
94
121
  }
95
- log.info(`Projects: ${projectFiles.length}`);
96
122
  log.blank();
97
123
  }
98
124
  function printResult(result, project) {
99
125
  log.blank();
100
126
  if (result.inserted === 0 && result.updated === 0 && result.relationsAdded === 0) {
101
- log.step("Already in sync");
102
- log.info(`Project: ${project}`);
127
+ log.step(`${project}: already in sync`);
103
128
  } else {
104
- log.step("Pull complete");
105
- log.info(`Project: ${project}`);
129
+ log.step(`${project}: pull complete`);
106
130
  if (result.inserted > 0) log.info(`Inserted: ${result.inserted}`);
107
131
  if (result.updated > 0) log.info(`Updated: ${result.updated}`);
108
132
  if (result.relationsAdded > 0) log.info(`Relations: ${result.relationsAdded} added`);
109
133
  }
134
+ log.info("Tip: run `memory pull --all` to sync every project in this gist.");
110
135
  log.blank();
111
136
  }
112
137
  export {
113
138
  runPull
114
139
  };
115
- //# sourceMappingURL=pull-RKFE5ZXV.js.map
140
+ //# sourceMappingURL=pull-UYLUGILU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/memory/subcommands/pull.ts"],"sourcesContent":["import { log } from '../../../lib/output.js';\nimport { initStorage } from './init-storage.js';\nimport {\n assertGhAvailable,\n loadSyncConfig,\n readGistFile,\n listGistFiles,\n filenameToProject,\n projectToFilename,\n} from '../utils/gist-transport.js';\nimport { mergeFromRemote, parsePayload } from '../utils/sync-merge.js';\nimport type { MergeResult } from '../types.js';\nimport { detectProject } from '../utils/project.js';\n\ninterface PullOpts {\n readonly all?: boolean;\n}\n\nexport async function runPull(opts: PullOpts): Promise<void> {\n assertGhAvailable();\n\n const syncConfig = loadSyncConfig();\n if (!syncConfig) {\n log.error('No sync gist found. Run `memory push` first.');\n return;\n }\n\n const { requireMemoryDeps } = await import('../utils/require-deps.js');\n await requireMemoryDeps();\n const ctx = initStorage();\n\n try {\n if (opts.all) {\n pullAll(ctx, syncConfig.gistId);\n } else {\n pullProject(ctx, syncConfig.gistId);\n }\n } finally {\n ctx.close();\n }\n}\n\nfunction pullProject(ctx: ReturnType<typeof initStorage>, gistId: string): void {\n const project = detectProject(process.cwd());\n if (!project) {\n log.error('Could not detect project. Run from a project directory or use --all.');\n return;\n }\n\n const filename = projectToFilename(project);\n const payload = parsePayload(readGistFile(gistId, filename));\n if (!payload) {\n log.error(`No memories found for project \"${project}\" in gist.`);\n return;\n }\n\n const localCount = ctx.memoryRepo.count(project);\n if (localCount === 0) {\n log.warn(`No local memories for \"${project}\" — creating fresh database from remote.`);\n }\n\n const result = mergeFromRemote(ctx.memoryRepo, ctx.relationRepo, payload);\n printResult(result, project);\n}\n\nfunction pullAll(ctx: ReturnType<typeof initStorage>, gistId: string): void {\n const files = listGistFiles(gistId);\n const projectFiles = files.filter((f) => filenameToProject(f) !== null);\n\n if (projectFiles.length === 0) {\n log.error('No memory files found in gist.');\n return;\n }\n\n const perProject: { project: string; result: MergeResult }[] = [];\n const skipped: string[] = [];\n let totalInserted = 0;\n let totalUpdated = 0;\n let totalRelations = 0;\n\n for (const filename of projectFiles) {\n const project = filenameToProject(filename);\n if (!project) continue;\n\n // Skip projects not set up on this machine (zero local memories).\n // User must `cd` into the project and run `memory pull` to opt-in to a new project.\n if (ctx.memoryRepo.count(project) === 0) {\n skipped.push(project);\n continue;\n }\n\n const payload = parsePayload(readGistFile(gistId, filename));\n if (!payload) continue;\n const result = mergeFromRemote(ctx.memoryRepo, ctx.relationRepo, payload);\n perProject.push({ project, result });\n totalInserted += result.inserted;\n totalUpdated += result.updated;\n totalRelations += result.relationsAdded;\n }\n\n const changed = perProject.filter((p) => p.result.inserted > 0 || p.result.updated > 0 || p.result.relationsAdded > 0);\n const syncedCount = perProject.length;\n\n log.blank();\n if (syncedCount === 0) {\n log.step('No locally set up projects to sync');\n } else if (changed.length === 0) {\n log.step(`Already in sync (${syncedCount} project${syncedCount === 1 ? '' : 's'})`);\n } else {\n log.step(`Pull complete (${changed.length} of ${syncedCount} projects updated)`);\n for (const { project, result } of changed) {\n const parts: string[] = [];\n if (result.inserted > 0) parts.push(`+${result.inserted} new`);\n if (result.updated > 0) parts.push(`~${result.updated} updated`);\n if (result.relationsAdded > 0) parts.push(`+${result.relationsAdded} relations`);\n log.info(`${project.padEnd(30)} ${parts.join(', ')}`);\n }\n if (totalInserted + totalUpdated + totalRelations > 0) {\n log.blank();\n log.info(`Total: +${totalInserted} new, ~${totalUpdated} updated, +${totalRelations} relations`);\n }\n }\n if (skipped.length > 0) {\n log.blank();\n log.info(`Skipped ${skipped.length} project${skipped.length === 1 ? '' : 's'} not set up on this machine:`);\n for (const project of skipped) log.info(` - ${project}`);\n log.info('Run `memory pull` from a project directory to pull that project for the first time.');\n }\n log.blank();\n}\n\nfunction printResult(result: MergeResult, project: string): void {\n log.blank();\n if (result.inserted === 0 && result.updated === 0 && result.relationsAdded === 0) {\n log.step(`${project}: already in sync`);\n } else {\n log.step(`${project}: pull complete`);\n if (result.inserted > 0) log.info(`Inserted: ${result.inserted}`);\n if (result.updated > 0) log.info(`Updated: ${result.updated}`);\n if (result.relationsAdded > 0) log.info(`Relations: ${result.relationsAdded} added`);\n }\n log.info('Tip: run `memory pull --all` to sync every project in this gist.');\n log.blank();\n}\n"],"mappings":"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","names":[]}
@@ -2,8 +2,9 @@
2
2
  import {
3
3
  memoryToSyncRow,
4
4
  mergeFromRemote,
5
- parsePayload
6
- } from "./chunk-N6X3E5AX.js";
5
+ parsePayload,
6
+ tombstoneToSyncRow
7
+ } from "./chunk-F6SLV2FR.js";
7
8
  import {
8
9
  assertGhAvailable,
9
10
  createGist,
@@ -12,30 +13,31 @@ import {
12
13
  readGistFile,
13
14
  updateGistFiles
14
15
  } from "./chunk-3UJYOWGF.js";
15
- import "./chunk-F5PNKQKW.js";
16
+ import "./chunk-YZ53W47Z.js";
16
17
  import {
17
18
  detectProject
18
19
  } from "./chunk-NAW47BYA.js";
19
20
  import {
20
21
  initStorage
21
- } from "./chunk-UJP5PJTA.js";
22
- import "./chunk-V4NXT4KB.js";
23
- import "./chunk-AR64LWGW.js";
24
- import "./chunk-J765H3HZ.js";
22
+ } from "./chunk-ADZ45KHX.js";
23
+ import "./chunk-DXDOVWOA.js";
24
+ import "./chunk-RJEPJ4JE.js";
25
+ import "./chunk-WOC2PKT2.js";
25
26
  import {
26
27
  log
27
- } from "./chunk-YXPJDIMK.js";
28
+ } from "./chunk-UQOBOHKN.js";
28
29
 
29
30
  // src/commands/memory/subcommands/push.ts
30
31
  import { hostname } from "os";
31
32
  import { confirm } from "@inquirer/prompts";
32
- function buildPayload(memories, relations) {
33
+ function buildPayload(memories, relations, tombstones) {
33
34
  return {
34
- version: 1,
35
+ version: 2,
35
36
  machine_id: hostname(),
36
37
  pushed_at: (/* @__PURE__ */ new Date()).toISOString(),
37
38
  memories,
38
- relations
39
+ relations,
40
+ tombstones
39
41
  };
40
42
  }
41
43
  function filterRelations(allRelations, memoryIds) {
@@ -48,7 +50,7 @@ function filterRelations(allRelations, memoryIds) {
48
50
  }
49
51
  async function runPush(opts) {
50
52
  assertGhAvailable();
51
- const { requireMemoryDeps } = await import("./require-deps-QW2IU6I3.js");
53
+ const { requireMemoryDeps } = await import("./require-deps-RUXTMQUV.js");
52
54
  await requireMemoryDeps();
53
55
  const ctx = initStorage();
54
56
  try {
@@ -75,13 +77,18 @@ async function pushProject(ctx, allRelations, syncConfig, opts) {
75
77
  if (remote) mergeFromRemote(ctx.memoryRepo, ctx.relationRepo, remote);
76
78
  }
77
79
  const memories = ctx.memoryRepo.getAllForSync(project);
78
- if (memories.length === 0) {
80
+ const tombstones = ctx.memoryRepo.getTombstonesByProject(project);
81
+ if (memories.length === 0 && tombstones.length === 0) {
79
82
  log.warn(`No memories found for "${project}". Nothing to push.`);
80
83
  return;
81
84
  }
82
85
  const memoryIds = new Set(memories.map((m) => m.id));
83
86
  const relations = filterRelations(allRelations, memoryIds);
84
- const json = JSON.stringify(buildPayload(memories.map(memoryToSyncRow), relations), null, 2);
87
+ const json = JSON.stringify(
88
+ buildPayload(memories.map(memoryToSyncRow), relations, tombstones.map(tombstoneToSyncRow)),
89
+ null,
90
+ 2
91
+ );
85
92
  if (!syncConfig) {
86
93
  if (!opts.yes && !await confirmCreate()) return;
87
94
  createGist(filename, json);
@@ -96,17 +103,23 @@ async function pushProject(ctx, allRelations, syncConfig, opts) {
96
103
  }
97
104
  async function pushAll(ctx, allRelations, syncConfig, opts) {
98
105
  const allMemories = ctx.memoryRepo.getAllForSync();
99
- if (allMemories.length === 0) {
106
+ const allTombstones = ctx.memoryRepo.getAllTombstones();
107
+ if (allMemories.length === 0 && allTombstones.length === 0) {
100
108
  log.warn("No memories found. Nothing to push.");
101
109
  return;
102
110
  }
103
111
  const byProject = groupByProject(allMemories);
112
+ const tombstonesByProject = groupTombstonesByProject(allTombstones);
113
+ for (const project of tombstonesByProject.keys()) {
114
+ if (!byProject.has(project)) byProject.set(project, []);
115
+ }
104
116
  const files = {};
105
117
  for (const [project, memories] of byProject) {
106
118
  const memoryIds = new Set(memories.map((m) => m.id));
107
119
  const relations = filterRelations(allRelations, memoryIds);
120
+ const tombstones = tombstonesByProject.get(project) ?? [];
108
121
  files[projectToFilename(project)] = JSON.stringify(
109
- buildPayload(memories.map(memoryToSyncRow), relations),
122
+ buildPayload(memories.map(memoryToSyncRow), relations, tombstones.map(tombstoneToSyncRow)),
110
123
  null,
111
124
  2
112
125
  );
@@ -137,6 +150,16 @@ function groupByProject(memories) {
137
150
  }
138
151
  return byProject;
139
152
  }
153
+ function groupTombstonesByProject(tombstones) {
154
+ const byProject = /* @__PURE__ */ new Map();
155
+ for (const t of tombstones) {
156
+ const key = t.project ?? "_global";
157
+ const list = byProject.get(key) ?? [];
158
+ list.push(t);
159
+ byProject.set(key, list);
160
+ }
161
+ return byProject;
162
+ }
140
163
  async function confirmCreate() {
141
164
  const proceed = await confirm({
142
165
  message: "Create a private GitHub Gist for memory sync?",
@@ -148,4 +171,4 @@ async function confirmCreate() {
148
171
  export {
149
172
  runPush
150
173
  };
151
- //# sourceMappingURL=push-5ZJNWAE7.js.map
174
+ //# sourceMappingURL=push-HWXJGSTL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/memory/subcommands/push.ts"],"sourcesContent":["import { hostname } from 'node:os';\nimport { confirm } from '@inquirer/prompts';\nimport { log } from '../../../lib/output.js';\nimport { initStorage } from './init-storage.js';\nimport type { StorageContext } from './init-storage.js';\nimport {\n assertGhAvailable,\n loadSyncConfig,\n createGist,\n readGistFile,\n updateGistFiles,\n projectToFilename,\n} from '../utils/gist-transport.js';\nimport { mergeFromRemote, memoryToSyncRow, tombstoneToSyncRow, parsePayload } from '../utils/sync-merge.js';\nimport type { Memory, SyncPayload, SyncRelationRow, SyncTombstone, Tombstone } from '../types.js';\nimport { detectProject } from '../utils/project.js';\n\ninterface PushOpts {\n readonly all?: boolean;\n readonly yes?: boolean;\n}\n\nfunction buildPayload(\n memories: readonly ReturnType<typeof memoryToSyncRow>[],\n relations: readonly SyncRelationRow[],\n tombstones: readonly SyncTombstone[],\n): SyncPayload {\n return {\n version: 2,\n machine_id: hostname(),\n pushed_at: new Date().toISOString(),\n memories,\n relations,\n tombstones,\n };\n}\n\nfunction filterRelations(\n allRelations: readonly { sourceId: string; targetId: string; relationType: string; createdAt: string }[],\n memoryIds: ReadonlySet<string>,\n): readonly SyncRelationRow[] {\n return allRelations\n .filter((r) => memoryIds.has(r.sourceId) && memoryIds.has(r.targetId))\n .map((r) => ({\n source_id: r.sourceId,\n target_id: r.targetId,\n relation_type: r.relationType as SyncRelationRow['relation_type'],\n created_at: r.createdAt,\n }));\n}\n\nexport async function runPush(opts: PushOpts): Promise<void> {\n assertGhAvailable();\n\n const { requireMemoryDeps } = await import('../utils/require-deps.js');\n await requireMemoryDeps();\n const ctx = initStorage();\n\n try {\n const syncConfig = loadSyncConfig();\n const allRelations = ctx.relationRepo.getAll();\n\n if (opts.all) {\n await pushAll(ctx, allRelations, syncConfig, opts);\n } else {\n await pushProject(ctx, allRelations, syncConfig, opts);\n }\n } finally {\n ctx.close();\n }\n}\n\nasync function pushProject(\n ctx: StorageContext,\n allRelations: readonly { sourceId: string; targetId: string; relationType: string; createdAt: string }[],\n syncConfig: { gistId: string } | null,\n opts: PushOpts,\n): Promise<void> {\n const project = detectProject(process.cwd());\n if (!project) {\n log.error('Could not detect project. Run from a project directory or use --all.');\n return;\n }\n\n const filename = projectToFilename(project);\n\n // Pull-before-push for this project\n if (syncConfig) {\n const remote = parsePayload(readGistFile(syncConfig.gistId, filename));\n if (remote) mergeFromRemote(ctx.memoryRepo, ctx.relationRepo, remote);\n }\n\n const memories = ctx.memoryRepo.getAllForSync(project);\n const tombstones = ctx.memoryRepo.getTombstonesByProject(project);\n\n if (memories.length === 0 && tombstones.length === 0) {\n log.warn(`No memories found for \"${project}\". Nothing to push.`);\n return;\n }\n\n const memoryIds = new Set(memories.map((m) => m.id));\n const relations = filterRelations(allRelations, memoryIds);\n const json = JSON.stringify(\n buildPayload(memories.map(memoryToSyncRow), relations, tombstones.map(tombstoneToSyncRow)),\n null,\n 2,\n );\n\n if (!syncConfig) {\n if (!opts.yes && !(await confirmCreate())) return;\n createGist(filename, json);\n } else {\n updateGistFiles(syncConfig.gistId, { [filename]: json });\n }\n\n log.blank();\n log.step('Push complete');\n log.info(`Project: ${project} (${memories.length} memories)`);\n log.info(`Relations: ${relations.length}`);\n log.blank();\n}\n\nasync function pushAll(\n ctx: StorageContext,\n allRelations: readonly { sourceId: string; targetId: string; relationType: string; createdAt: string }[],\n syncConfig: { gistId: string } | null,\n opts: PushOpts,\n): Promise<void> {\n const allMemories = ctx.memoryRepo.getAllForSync();\n const allTombstones = ctx.memoryRepo.getAllTombstones();\n\n if (allMemories.length === 0 && allTombstones.length === 0) {\n log.warn('No memories found. Nothing to push.');\n return;\n }\n\n const byProject = groupByProject(allMemories);\n const tombstonesByProject = groupTombstonesByProject(allTombstones);\n\n // Ensure every project with tombstones-only state still produces a file.\n for (const project of tombstonesByProject.keys()) {\n if (!byProject.has(project)) byProject.set(project, []);\n }\n\n const files: Record<string, string> = {};\n for (const [project, memories] of byProject) {\n const memoryIds = new Set(memories.map((m) => m.id));\n const relations = filterRelations(allRelations, memoryIds);\n const tombstones = tombstonesByProject.get(project) ?? [];\n files[projectToFilename(project)] = JSON.stringify(\n buildPayload(memories.map(memoryToSyncRow), relations, tombstones.map(tombstoneToSyncRow)),\n null,\n 2,\n );\n }\n\n if (!syncConfig) {\n if (!opts.yes && !(await confirmCreate())) return;\n const entries = Object.entries(files);\n const gistId = createGist(entries[0]![0], entries[0]![1]);\n if (entries.length > 1) {\n updateGistFiles(gistId, Object.fromEntries(entries.slice(1)));\n }\n } else {\n updateGistFiles(syncConfig.gistId, files);\n }\n\n log.blank();\n log.step('Push complete');\n log.info(`Projects: ${byProject.size}`);\n log.info(`Memories: ${allMemories.length}`);\n log.blank();\n}\n\nfunction groupByProject(memories: readonly Memory[]): Map<string, Memory[]> {\n const byProject = new Map<string, Memory[]>();\n for (const m of memories) {\n const key = m.project ?? '_global';\n const list = byProject.get(key) ?? [];\n list.push(m);\n byProject.set(key, list);\n }\n return byProject;\n}\n\nfunction groupTombstonesByProject(tombstones: readonly Tombstone[]): Map<string, Tombstone[]> {\n const byProject = new Map<string, Tombstone[]>();\n for (const t of tombstones) {\n const key = t.project ?? '_global';\n const list = byProject.get(key) ?? [];\n list.push(t);\n byProject.set(key, list);\n }\n return byProject;\n}\n\nasync function confirmCreate(): Promise<boolean> {\n const proceed = await confirm({\n message: 'Create a private GitHub Gist for memory sync?',\n default: true,\n });\n if (!proceed) log.info('Skipped.');\n return proceed;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,gBAAgB;AACzB,SAAS,eAAe;AAqBxB,SAAS,aACP,UACA,WACA,YACa;AACb,SAAO;AAAA,IACL,SAAS;AAAA,IACT,YAAY,SAAS;AAAA,IACrB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,gBACP,cACA,WAC4B;AAC5B,SAAO,aACJ,OAAO,CAAC,MAAM,UAAU,IAAI,EAAE,QAAQ,KAAK,UAAU,IAAI,EAAE,QAAQ,CAAC,EACpE,IAAI,CAAC,OAAO;AAAA,IACX,WAAW,EAAE;AAAA,IACb,WAAW,EAAE;AAAA,IACb,eAAe,EAAE;AAAA,IACjB,YAAY,EAAE;AAAA,EAChB,EAAE;AACN;AAEA,eAAsB,QAAQ,MAA+B;AAC3D,oBAAkB;AAElB,QAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,4BAA0B;AACrE,QAAM,kBAAkB;AACxB,QAAM,MAAM,YAAY;AAExB,MAAI;AACF,UAAM,aAAa,eAAe;AAClC,UAAM,eAAe,IAAI,aAAa,OAAO;AAE7C,QAAI,KAAK,KAAK;AACZ,YAAM,QAAQ,KAAK,cAAc,YAAY,IAAI;AAAA,IACnD,OAAO;AACL,YAAM,YAAY,KAAK,cAAc,YAAY,IAAI;AAAA,IACvD;AAAA,EACF,UAAE;AACA,QAAI,MAAM;AAAA,EACZ;AACF;AAEA,eAAe,YACb,KACA,cACA,YACA,MACe;AACf,QAAM,UAAU,cAAc,QAAQ,IAAI,CAAC;AAC3C,MAAI,CAAC,SAAS;AACZ,QAAI,MAAM,sEAAsE;AAChF;AAAA,EACF;AAEA,QAAM,WAAW,kBAAkB,OAAO;AAG1C,MAAI,YAAY;AACd,UAAM,SAAS,aAAa,aAAa,WAAW,QAAQ,QAAQ,CAAC;AACrE,QAAI,OAAQ,iBAAgB,IAAI,YAAY,IAAI,cAAc,MAAM;AAAA,EACtE;AAEA,QAAM,WAAW,IAAI,WAAW,cAAc,OAAO;AACrD,QAAM,aAAa,IAAI,WAAW,uBAAuB,OAAO;AAEhE,MAAI,SAAS,WAAW,KAAK,WAAW,WAAW,GAAG;AACpD,QAAI,KAAK,0BAA0B,OAAO,qBAAqB;AAC/D;AAAA,EACF;AAEA,QAAM,YAAY,IAAI,IAAI,SAAS,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AACnD,QAAM,YAAY,gBAAgB,cAAc,SAAS;AACzD,QAAM,OAAO,KAAK;AAAA,IAChB,aAAa,SAAS,IAAI,eAAe,GAAG,WAAW,WAAW,IAAI,kBAAkB,CAAC;AAAA,IACzF;AAAA,IACA;AAAA,EACF;AAEA,MAAI,CAAC,YAAY;AACf,QAAI,CAAC,KAAK,OAAO,CAAE,MAAM,cAAc,EAAI;AAC3C,eAAW,UAAU,IAAI;AAAA,EAC3B,OAAO;AACL,oBAAgB,WAAW,QAAQ,EAAE,CAAC,QAAQ,GAAG,KAAK,CAAC;AAAA,EACzD;AAEA,MAAI,MAAM;AACV,MAAI,KAAK,eAAe;AACxB,MAAI,KAAK,cAAc,OAAO,KAAK,SAAS,MAAM,YAAY;AAC9D,MAAI,KAAK,cAAc,UAAU,MAAM,EAAE;AACzC,MAAI,MAAM;AACZ;AAEA,eAAe,QACb,KACA,cACA,YACA,MACe;AACf,QAAM,cAAc,IAAI,WAAW,cAAc;AACjD,QAAM,gBAAgB,IAAI,WAAW,iBAAiB;AAEtD,MAAI,YAAY,WAAW,KAAK,cAAc,WAAW,GAAG;AAC1D,QAAI,KAAK,qCAAqC;AAC9C;AAAA,EACF;AAEA,QAAM,YAAY,eAAe,WAAW;AAC5C,QAAM,sBAAsB,yBAAyB,aAAa;AAGlE,aAAW,WAAW,oBAAoB,KAAK,GAAG;AAChD,QAAI,CAAC,UAAU,IAAI,OAAO,EAAG,WAAU,IAAI,SAAS,CAAC,CAAC;AAAA,EACxD;AAEA,QAAM,QAAgC,CAAC;AACvC,aAAW,CAAC,SAAS,QAAQ,KAAK,WAAW;AAC3C,UAAM,YAAY,IAAI,IAAI,SAAS,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AACnD,UAAM,YAAY,gBAAgB,cAAc,SAAS;AACzD,UAAM,aAAa,oBAAoB,IAAI,OAAO,KAAK,CAAC;AACxD,UAAM,kBAAkB,OAAO,CAAC,IAAI,KAAK;AAAA,MACvC,aAAa,SAAS,IAAI,eAAe,GAAG,WAAW,WAAW,IAAI,kBAAkB,CAAC;AAAA,MACzF;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,YAAY;AACf,QAAI,CAAC,KAAK,OAAO,CAAE,MAAM,cAAc,EAAI;AAC3C,UAAM,UAAU,OAAO,QAAQ,KAAK;AACpC,UAAM,SAAS,WAAW,QAAQ,CAAC,EAAG,CAAC,GAAG,QAAQ,CAAC,EAAG,CAAC,CAAC;AACxD,QAAI,QAAQ,SAAS,GAAG;AACtB,sBAAgB,QAAQ,OAAO,YAAY,QAAQ,MAAM,CAAC,CAAC,CAAC;AAAA,IAC9D;AAAA,EACF,OAAO;AACL,oBAAgB,WAAW,QAAQ,KAAK;AAAA,EAC1C;AAEA,MAAI,MAAM;AACV,MAAI,KAAK,eAAe;AACxB,MAAI,KAAK,cAAc,UAAU,IAAI,EAAE;AACvC,MAAI,KAAK,cAAc,YAAY,MAAM,EAAE;AAC3C,MAAI,MAAM;AACZ;AAEA,SAAS,eAAe,UAAoD;AAC1E,QAAM,YAAY,oBAAI,IAAsB;AAC5C,aAAW,KAAK,UAAU;AACxB,UAAM,MAAM,EAAE,WAAW;AACzB,UAAM,OAAO,UAAU,IAAI,GAAG,KAAK,CAAC;AACpC,SAAK,KAAK,CAAC;AACX,cAAU,IAAI,KAAK,IAAI;AAAA,EACzB;AACA,SAAO;AACT;AAEA,SAAS,yBAAyB,YAA4D;AAC5F,QAAM,YAAY,oBAAI,IAAyB;AAC/C,aAAW,KAAK,YAAY;AAC1B,UAAM,MAAM,EAAE,WAAW;AACzB,UAAM,OAAO,UAAU,IAAI,GAAG,KAAK,CAAC;AACpC,SAAK,KAAK,CAAC;AACX,cAAU,IAAI,KAAK,IAAI;AAAA,EACzB;AACA,SAAO;AACT;AAEA,eAAe,gBAAkC;AAC/C,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,SAAS;AAAA,IACT,SAAS;AAAA,EACX,CAAC;AACD,MAAI,CAAC,QAAS,KAAI,KAAK,UAAU;AACjC,SAAO;AACT;","names":[]}
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  cwdRequire,
4
4
  requireMemoryDeps
5
- } from "./chunk-J765H3HZ.js";
6
- import "./chunk-YXPJDIMK.js";
5
+ } from "./chunk-WOC2PKT2.js";
6
+ import "./chunk-UQOBOHKN.js";
7
7
  export {
8
8
  cwdRequire,
9
9
  requireMemoryDeps
10
10
  };
11
- //# sourceMappingURL=require-deps-QW2IU6I3.js.map
11
+ //# sourceMappingURL=require-deps-RUXTMQUV.js.map
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  initStorage
4
- } from "./chunk-UJP5PJTA.js";
5
- import "./chunk-V4NXT4KB.js";
6
- import "./chunk-AR64LWGW.js";
7
- import "./chunk-J765H3HZ.js";
4
+ } from "./chunk-ADZ45KHX.js";
5
+ import "./chunk-DXDOVWOA.js";
6
+ import "./chunk-RJEPJ4JE.js";
7
+ import "./chunk-WOC2PKT2.js";
8
8
  import {
9
9
  log
10
- } from "./chunk-YXPJDIMK.js";
10
+ } from "./chunk-UQOBOHKN.js";
11
11
 
12
12
  // src/commands/memory/subcommands/stats.ts
13
13
  import { existsSync, statSync } from "fs";
@@ -18,7 +18,7 @@ function formatBytes(bytes) {
18
18
  return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
19
19
  }
20
20
  async function runStats(opts) {
21
- const { requireMemoryDeps } = await import("./require-deps-QW2IU6I3.js");
21
+ const { requireMemoryDeps } = await import("./require-deps-RUXTMQUV.js");
22
22
  await requireMemoryDeps();
23
23
  const ctx = initStorage(opts.dbPath);
24
24
  try {
@@ -72,4 +72,4 @@ async function runStats(opts) {
72
72
  export {
73
73
  runStats
74
74
  };
75
- //# sourceMappingURL=stats-NPXPJNBO.js.map
75
+ //# sourceMappingURL=stats-DFV24AJW.js.map
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-3UJYOWGF.js";
10
10
  import {
11
11
  log
12
- } from "./chunk-YXPJDIMK.js";
12
+ } from "./chunk-UQOBOHKN.js";
13
13
 
14
14
  // src/commands/memory/subcommands/sync-clean.ts
15
15
  import { confirm } from "@inquirer/prompts";
@@ -50,4 +50,4 @@ async function runSyncClean(project, opts) {
50
50
  export {
51
51
  runSyncClean
52
52
  };
53
- //# sourceMappingURL=sync-clean-JQLVE4WU.js.map
53
+ //# sourceMappingURL=sync-clean-GFX5F55E.js.map
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  parsePayload
4
- } from "./chunk-N6X3E5AX.js";
4
+ } from "./chunk-F6SLV2FR.js";
5
5
  import {
6
6
  assertGhAvailable,
7
7
  filenameToProject,
@@ -9,16 +9,16 @@ import {
9
9
  loadSyncConfig,
10
10
  readGistFile
11
11
  } from "./chunk-3UJYOWGF.js";
12
- import "./chunk-F5PNKQKW.js";
12
+ import "./chunk-YZ53W47Z.js";
13
13
  import {
14
14
  initStorage
15
- } from "./chunk-UJP5PJTA.js";
16
- import "./chunk-V4NXT4KB.js";
17
- import "./chunk-AR64LWGW.js";
18
- import "./chunk-J765H3HZ.js";
15
+ } from "./chunk-ADZ45KHX.js";
16
+ import "./chunk-DXDOVWOA.js";
17
+ import "./chunk-RJEPJ4JE.js";
18
+ import "./chunk-WOC2PKT2.js";
19
19
  import {
20
20
  log
21
- } from "./chunk-YXPJDIMK.js";
21
+ } from "./chunk-UQOBOHKN.js";
22
22
 
23
23
  // src/commands/memory/subcommands/sync-status.ts
24
24
  async function runSyncStatus() {
@@ -28,7 +28,7 @@ async function runSyncStatus() {
28
28
  log.error("No sync gist found. Run `memory push` first.");
29
29
  return;
30
30
  }
31
- const { requireMemoryDeps } = await import("./require-deps-QW2IU6I3.js");
31
+ const { requireMemoryDeps } = await import("./require-deps-RUXTMQUV.js");
32
32
  await requireMemoryDeps();
33
33
  const ctx = initStorage();
34
34
  try {
@@ -67,4 +67,4 @@ async function runSyncStatus() {
67
67
  export {
68
68
  runSyncStatus
69
69
  };
70
- //# sourceMappingURL=sync-status-IYG7ZYC5.js.map
70
+ //# sourceMappingURL=sync-status-VXMDWDRG.js.map