@launchsecure/launch-kit 0.0.25 → 0.0.27

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 (132) hide show
  1. package/README.md +50 -0
  2. package/dist/beacon/beacon.mjs +1016 -0
  3. package/dist/beacon/beacon.mjs.map +1 -0
  4. package/dist/beacon/beacon.umd.js +87 -0
  5. package/dist/beacon/beacon.umd.js.map +1 -0
  6. package/dist/beacon/index-DAIDnjfR.mjs +513 -0
  7. package/dist/beacon/index-DAIDnjfR.mjs.map +1 -0
  8. package/dist/beacon/types/capture/element.d.ts +3 -0
  9. package/dist/beacon/types/capture/element.d.ts.map +1 -0
  10. package/dist/beacon/types/capture/framework.d.ts +3 -0
  11. package/dist/beacon/types/capture/framework.d.ts.map +1 -0
  12. package/dist/beacon/types/capture/metadata.d.ts +3 -0
  13. package/dist/beacon/types/capture/metadata.d.ts.map +1 -0
  14. package/dist/beacon/types/capture/overlay.d.ts +7 -0
  15. package/dist/beacon/types/capture/overlay.d.ts.map +1 -0
  16. package/dist/beacon/types/capture/picker.d.ts +12 -0
  17. package/dist/beacon/types/capture/picker.d.ts.map +1 -0
  18. package/dist/beacon/types/capture/screenshot.d.ts +7 -0
  19. package/dist/beacon/types/capture/screenshot.d.ts.map +1 -0
  20. package/dist/beacon/types/capture/selector.d.ts +2 -0
  21. package/dist/beacon/types/capture/selector.d.ts.map +1 -0
  22. package/dist/beacon/types/element.d.ts +50 -0
  23. package/dist/beacon/types/element.d.ts.map +1 -0
  24. package/dist/beacon/types/index.d.ts +4 -0
  25. package/dist/beacon/types/index.d.ts.map +1 -0
  26. package/dist/beacon/types/transport/submit.d.ts +3 -0
  27. package/dist/beacon/types/transport/submit.d.ts.map +1 -0
  28. package/dist/beacon/types/types.d.ts +88 -0
  29. package/dist/beacon/types/types.d.ts.map +1 -0
  30. package/dist/beacon/types/ui/button.d.ts +2 -0
  31. package/dist/beacon/types/ui/button.d.ts.map +1 -0
  32. package/dist/beacon/types/ui/drawer.d.ts +31 -0
  33. package/dist/beacon/types/ui/drawer.d.ts.map +1 -0
  34. package/dist/beacon/types/ui/icons.d.ts +9 -0
  35. package/dist/beacon/types/ui/icons.d.ts.map +1 -0
  36. package/dist/beacon/types/ui/pick-mode-overlay.d.ts +25 -0
  37. package/dist/beacon/types/ui/pick-mode-overlay.d.ts.map +1 -0
  38. package/dist/beacon/types/ui/pin-popover.d.ts +14 -0
  39. package/dist/beacon/types/ui/pin-popover.d.ts.map +1 -0
  40. package/dist/chart-client/assets/index-CJ4mgRRF.css +1 -0
  41. package/dist/chart-client/assets/{index-C8ANseEa.js → index-Ccy-DpI-.js} +82 -73
  42. package/dist/chart-client/index.html +2 -2
  43. package/dist/client/assets/index-DI5qSR_w.css +32 -0
  44. package/dist/client/assets/index-Dp0_okva.js +294 -0
  45. package/dist/client/index.html +2 -2
  46. package/dist/council-client/assets/index-C_-vAM9L.css +1 -0
  47. package/dist/council-client/assets/{index-Dc41S-R2.js → index-Dt4zWKSj.js} +14 -14
  48. package/dist/council-client/index.html +2 -2
  49. package/dist/deck-client/assets/{_baseUniq-2gclQXo7.js → _baseUniq-W2JQDmje.js} +1 -1
  50. package/dist/deck-client/assets/{arc-DcMY5Wm0.js → arc-DIBWAId9.js} +1 -1
  51. package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-B8iirmmJ.js → architectureDiagram-Q4EWVU46-CAIRMvJK.js} +1 -1
  52. package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-B4JBLjmJ.js → blockDiagram-DXYQGD6D-BeNaNiOi.js} +1 -1
  53. package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-CojrJAk8.js → c4Diagram-AHTNJAMY-B9Ozi62h.js} +1 -1
  54. package/dist/deck-client/assets/channel-CRdozqbp.js +1 -0
  55. package/dist/deck-client/assets/{chunk-4BX2VUAB-Bmb_BMDo.js → chunk-4BX2VUAB-D7AZ47dt.js} +1 -1
  56. package/dist/deck-client/assets/{chunk-4TB4RGXK-CumBy8qe.js → chunk-4TB4RGXK-DnVnNPcI.js} +1 -1
  57. package/dist/deck-client/assets/{chunk-55IACEB6-Ka8Hb1wD.js → chunk-55IACEB6-UKYs-YNd.js} +1 -1
  58. package/dist/deck-client/assets/{chunk-EDXVE4YY-B3sIPiQo.js → chunk-EDXVE4YY-D43b-SKn.js} +1 -1
  59. package/dist/deck-client/assets/{chunk-FMBD7UC4-C1tYkaqu.js → chunk-FMBD7UC4-QzBAoyyW.js} +1 -1
  60. package/dist/deck-client/assets/{chunk-OYMX7WX6-D7Wacbky.js → chunk-OYMX7WX6-Cjif4r6W.js} +1 -1
  61. package/dist/deck-client/assets/{chunk-QZHKN3VN-ChXI0vO3.js → chunk-QZHKN3VN-CqLDirEI.js} +1 -1
  62. package/dist/deck-client/assets/{chunk-YZCP3GAM-BXhiqf8u.js → chunk-YZCP3GAM-_FQvmMs4.js} +1 -1
  63. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-lIZMp57W.js +1 -0
  64. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-lIZMp57W.js +1 -0
  65. package/dist/deck-client/assets/clone-BtWeSTyJ.js +1 -0
  66. package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-Bqp3p68D.js → cose-bilkent-S5V4N54A-rfrocesE.js} +1 -1
  67. package/dist/deck-client/assets/{dagre-KV5264BT-BS-rtyhZ.js → dagre-KV5264BT-Bv_7DJat.js} +1 -1
  68. package/dist/deck-client/assets/{diagram-5BDNPKRD-BIrj9YGI.js → diagram-5BDNPKRD-4F1414G5.js} +1 -1
  69. package/dist/deck-client/assets/{diagram-G4DWMVQ6-noHWPIg4.js → diagram-G4DWMVQ6-C4-Pszqm.js} +1 -1
  70. package/dist/deck-client/assets/{diagram-MMDJMWI5-C2qHxvqV.js → diagram-MMDJMWI5-B647TIx9.js} +1 -1
  71. package/dist/deck-client/assets/{diagram-TYMM5635-BytnGQr-.js → diagram-TYMM5635-BFAqpezd.js} +1 -1
  72. package/dist/deck-client/assets/{erDiagram-SMLLAGMA-BfK5m2YQ.js → erDiagram-SMLLAGMA-BfBfrJOC.js} +1 -1
  73. package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-Cq925G1Z.js → flowDiagram-DWJPFMVM-DX9YAYes.js} +1 -1
  74. package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-DhhHPAmj.js → ganttDiagram-T4ZO3ILL-DCuiy7wF.js} +1 -1
  75. package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-B3Lc0h9q.js → gitGraphDiagram-UUTBAWPF-CGp1IXUh.js} +1 -1
  76. package/dist/deck-client/assets/{graph-RTawgVWm.js → graph-B7g8aoxv.js} +1 -1
  77. package/dist/deck-client/assets/{index-BfIfJXmS.js → index-Dg1r-WSN.js} +68 -68
  78. package/dist/deck-client/assets/index-DsIZ3LqL.css +1 -0
  79. package/dist/deck-client/assets/{infoDiagram-42DDH7IO-BlR584kX.js → infoDiagram-42DDH7IO-L3fahMkF.js} +1 -1
  80. package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-DygKoNGY.js → ishikawaDiagram-UXIWVN3A-aS_EjWBZ.js} +1 -1
  81. package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-BnaiYp9N.js → journeyDiagram-VCZTEJTY-djTSQZF9.js} +1 -1
  82. package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-BQBUBzJC.js → kanban-definition-6JOO6SKY-CcTHo4CM.js} +1 -1
  83. package/dist/deck-client/assets/{layout-DeZ8HI1T.js → layout-mEJiadb7.js} +1 -1
  84. package/dist/deck-client/assets/{linear-C6roLi_9.js → linear-XgTKqyRu.js} +1 -1
  85. package/dist/deck-client/assets/{min-CbUksbuI.js → min-Ct9jZdpd.js} +1 -1
  86. package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-iNxV62yN.js → mindmap-definition-QFDTVHPH-BaFxCGNU.js} +1 -1
  87. package/dist/deck-client/assets/{pieDiagram-DEJITSTG-DHVA0jaG.js → pieDiagram-DEJITSTG-CIbYYjtw.js} +1 -1
  88. package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-DBeKKLUQ.js → quadrantDiagram-34T5L4WZ-D9EtCOvh.js} +1 -1
  89. package/dist/deck-client/assets/{requirementDiagram-MS252O5E-CBwITx7p.js → requirementDiagram-MS252O5E-xeni9eVG.js} +1 -1
  90. package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-BtE-1YTU.js → sankeyDiagram-XADWPNL6-LYeknz9h.js} +1 -1
  91. package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-DN96yPP2.js → sequenceDiagram-FGHM5R23-RDbsKFZf.js} +1 -1
  92. package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-VUkKC2uJ.js → stateDiagram-FHFEXIEX-BH1Zjglk.js} +1 -1
  93. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-BrV78NDR.js +1 -0
  94. package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-oUeZhRns.js → timeline-definition-GMOUNBTQ-IFXxKptt.js} +1 -1
  95. package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-D87fK90n.js → vennDiagram-DHZGUBPP-D-sLkQs9.js} +1 -1
  96. package/dist/deck-client/assets/wardley-RL74JXVD-C010F8l4.js +162 -0
  97. package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-Ca_i0QRA.js → wardleyDiagram-NUSXRM2D-BTjjuDU3.js} +1 -1
  98. package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-CUOJVIvq.js → xychartDiagram-5P7HB3ND-AYbv92n-.js} +1 -1
  99. package/dist/deck-client/index.html +2 -2
  100. package/dist/server/chart-serve.js +4524 -3564
  101. package/dist/server/cli.js +27351 -5398
  102. package/dist/server/council-entry.js +17 -5
  103. package/dist/server/council-serve.js +8 -3
  104. package/dist/server/deck-mcp-entry.js +354 -13
  105. package/dist/server/deck-serve.js +298 -7
  106. package/dist/server/graph/queries/classify.scm +8 -0
  107. package/dist/server/graph/queries/exports.scm +7 -0
  108. package/dist/server/graph-mcp-entry.js +5943 -4361
  109. package/dist/server/init-entry.js +609 -0
  110. package/dist/server/orbit-entry.js +2272 -0
  111. package/dist/server/{server/chart-serve.js → parse-worker-entry.js} +1900 -1822
  112. package/dist/server/recall-entry.js +1450 -0
  113. package/package.json +40 -8
  114. package/scaffolds/migrate-safety/.github/workflows/backup-on-migration.yml +72 -0
  115. package/scaffolds/migrate-safety/docs/migrations-runbook.md +172 -0
  116. package/scaffolds/migrate-safety/scripts/migrate-with-backup.sh +294 -0
  117. package/dist/chart-client/assets/index--120d9P9.css +0 -1
  118. package/dist/client/assets/index-Bf8zdL3x.css +0 -32
  119. package/dist/client/assets/index-Ds9UP_cj.js +0 -291
  120. package/dist/council-client/assets/index-CofZh7pS.css +0 -1
  121. package/dist/deck-client/assets/channel-ERh5jKXV.js +0 -1
  122. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-CMi1Gaev.js +0 -1
  123. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-CMi1Gaev.js +0 -1
  124. package/dist/deck-client/assets/clone-DfWhlD4X.js +0 -1
  125. package/dist/deck-client/assets/index-765AIQ9z.css +0 -1
  126. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-CA0IjulK.js +0 -1
  127. package/dist/deck-client/assets/wardley-RL74JXVD-DYbYcpDp.js +0 -162
  128. package/dist/server/deck-server/deck-mcp-entry.js +0 -1789
  129. package/dist/server/deck-server/deck-serve.js +0 -1275
  130. package/dist/server/server/cli.js +0 -13360
  131. package/dist/server/server/fb-wizard.js +0 -136
  132. package/dist/server/server/graph-mcp-entry.js +0 -6776
@@ -0,0 +1,1450 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
12
+ var __export = (target, all) => {
13
+ for (var name in all)
14
+ __defProp(target, name, { get: all[name], enumerable: true });
15
+ };
16
+ var __copyProps = (to, from, except, desc) => {
17
+ if (from && typeof from === "object" || typeof from === "function") {
18
+ for (let key of __getOwnPropNames(from))
19
+ if (!__hasOwnProp.call(to, key) && key !== except)
20
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
21
+ }
22
+ return to;
23
+ };
24
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
25
+ // If the importer is in node compatibility mode or this is not an ESM
26
+ // file that has been converted to a CommonJS file using a Babel-
27
+ // compatible transform (i.e. "__esModule" has not been set), then set
28
+ // "default" to the CommonJS "module.exports" for node compatibility.
29
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
30
+ mod
31
+ ));
32
+
33
+ // src/server/recall/git.ts
34
+ function gitEnv(gitDir, workTree) {
35
+ const env = { ...process.env, GIT_DIR: gitDir };
36
+ if (workTree) env.GIT_WORK_TREE = workTree;
37
+ return env;
38
+ }
39
+ function runGitSync(args, opts) {
40
+ const result = (0, import_node_child_process.spawnSync)("git", args, {
41
+ env: opts.env,
42
+ cwd: opts.cwd,
43
+ encoding: "utf8"
44
+ });
45
+ return {
46
+ status: result.status ?? 1,
47
+ stdout: result.stdout ?? "",
48
+ stderr: result.stderr ?? ""
49
+ };
50
+ }
51
+ function runGit(args, opts) {
52
+ return new Promise((resolve3) => {
53
+ const proc = (0, import_node_child_process.spawn)("git", args, {
54
+ env: opts.env,
55
+ cwd: opts.cwd,
56
+ stdio: opts.stdio ?? "inherit"
57
+ });
58
+ proc.on("exit", (code) => resolve3(code ?? 1));
59
+ proc.on("error", () => resolve3(1));
60
+ });
61
+ }
62
+ var import_node_child_process;
63
+ var init_git = __esm({
64
+ "src/server/recall/git.ts"() {
65
+ "use strict";
66
+ import_node_child_process = require("node:child_process");
67
+ }
68
+ });
69
+
70
+ // src/server/recall/paths.ts
71
+ function resolveRecallPaths(cwd = process.cwd()) {
72
+ const workTree = path.resolve(cwd);
73
+ const recallDir = path.join(workTree, RECALL_DIR_NAME);
74
+ const gitDir = path.join(recallDir, RECALL_REPO_DIRNAME);
75
+ return { workTree, recallDir, gitDir };
76
+ }
77
+ var path, RECALL_DIR_NAME, RECALL_REPO_DIRNAME;
78
+ var init_paths = __esm({
79
+ "src/server/recall/paths.ts"() {
80
+ "use strict";
81
+ path = __toESM(require("node:path"));
82
+ RECALL_DIR_NAME = ".recall";
83
+ RECALL_REPO_DIRNAME = "repo.git";
84
+ }
85
+ });
86
+
87
+ // src/server/recall/config.ts
88
+ function isPlainObject(v) {
89
+ return typeof v === "object" && v !== null && !Array.isArray(v);
90
+ }
91
+ function mergeConfig(raw) {
92
+ if (!isPlainObject(raw)) return { ...DEFAULT_CONFIG };
93
+ const out = {
94
+ debounce: DEFAULT_CONFIG.debounce,
95
+ ignore: [...DEFAULT_CONFIG.ignore],
96
+ retention: { ...DEFAULT_CONFIG.retention }
97
+ };
98
+ if (typeof raw.debounce === "number" && raw.debounce > 0) {
99
+ out.debounce = raw.debounce;
100
+ }
101
+ if (Array.isArray(raw.ignore) && raw.ignore.every((x) => typeof x === "string")) {
102
+ out.ignore = raw.ignore;
103
+ }
104
+ if (isPlainObject(raw.retention)) {
105
+ const r = raw.retention;
106
+ if (r.keepLast === null) {
107
+ out.retention.keepLast = null;
108
+ } else if (typeof r.keepLast === "number" && r.keepLast >= 0) {
109
+ out.retention.keepLast = r.keepLast;
110
+ }
111
+ if (r.maxAgeDays === null) {
112
+ out.retention.maxAgeDays = null;
113
+ } else if (typeof r.maxAgeDays === "number" && r.maxAgeDays >= 0) {
114
+ out.retention.maxAgeDays = r.maxAgeDays;
115
+ }
116
+ }
117
+ return out;
118
+ }
119
+ function loadConfig(workTree) {
120
+ const configPath = path2.join(workTree, CONFIG_FILENAME);
121
+ if (!fs.existsSync(configPath)) {
122
+ return { config: { ...DEFAULT_CONFIG }, source: "defaults", path: configPath };
123
+ }
124
+ try {
125
+ const text2 = fs.readFileSync(configPath, "utf8");
126
+ const raw = JSON.parse(text2);
127
+ return { config: mergeConfig(raw), source: "file", path: configPath };
128
+ } catch (err) {
129
+ process.stderr.write(
130
+ `[launch-recall] warning: failed to parse ${CONFIG_FILENAME} (${err instanceof Error ? err.message : String(err)}) \u2014 using defaults
131
+ `
132
+ );
133
+ return { config: { ...DEFAULT_CONFIG }, source: "defaults", path: configPath };
134
+ }
135
+ }
136
+ function writeDefaultConfig(workTree) {
137
+ const configPath = path2.join(workTree, CONFIG_FILENAME);
138
+ if (fs.existsSync(configPath)) return "present";
139
+ fs.writeFileSync(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n");
140
+ return "created";
141
+ }
142
+ function escapeRegex(s) {
143
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
144
+ }
145
+ function buildIgnoreRegex(ignore) {
146
+ if (ignore.length === 0) return /^\b$/;
147
+ const alt = ignore.map(escapeRegex).join("|");
148
+ return new RegExp(`(^|/)(${alt})(/|$)`);
149
+ }
150
+ function buildPathspecExcludes(ignore) {
151
+ return ignore.map((name) => `:!${name}`);
152
+ }
153
+ var fs, path2, CONFIG_FILENAME, DEFAULT_CONFIG;
154
+ var init_config = __esm({
155
+ "src/server/recall/config.ts"() {
156
+ "use strict";
157
+ fs = __toESM(require("node:fs"));
158
+ path2 = __toESM(require("node:path"));
159
+ CONFIG_FILENAME = ".launch-recall.json";
160
+ DEFAULT_CONFIG = {
161
+ debounce: 3e3,
162
+ ignore: [
163
+ "node_modules",
164
+ ".next",
165
+ "dist",
166
+ "build",
167
+ ".turbo",
168
+ ".git",
169
+ ".recall",
170
+ "coverage"
171
+ ],
172
+ retention: {
173
+ keepLast: 5e3,
174
+ maxAgeDays: 30
175
+ }
176
+ };
177
+ }
178
+ });
179
+
180
+ // src/server/recall/init.ts
181
+ var init_exports = {};
182
+ __export(init_exports, {
183
+ runInit: () => runInit
184
+ });
185
+ function ensureGitignoreEntry(workTree) {
186
+ const gitignorePath = path3.join(workTree, ".gitignore");
187
+ if (!fs2.existsSync(gitignorePath)) return "no-gitignore";
188
+ const contents = fs2.readFileSync(gitignorePath, "utf8");
189
+ const lines = contents.split(/\r?\n/);
190
+ const already = lines.some((l) => {
191
+ const t = l.trim();
192
+ return t === GITIGNORE_LINE || t === `${RECALL_DIR_NAME}/` || t === RECALL_DIR_NAME || t === `/${RECALL_DIR_NAME}` || t === `${RECALL_DIR_NAME}/**`;
193
+ });
194
+ if (already) return "present";
195
+ const needsLeadingNewline = contents.length > 0 && !contents.endsWith("\n");
196
+ fs2.appendFileSync(
197
+ gitignorePath,
198
+ `${needsLeadingNewline ? "\n" : ""}${GITIGNORE_LINE}
199
+ `
200
+ );
201
+ return "added";
202
+ }
203
+ async function runInit(_args) {
204
+ const { workTree, recallDir, gitDir } = resolveRecallPaths();
205
+ if (fs2.existsSync(gitDir)) {
206
+ process.stderr.write(`[launch-recall] already initialised at ${gitDir}
207
+ `);
208
+ } else {
209
+ fs2.mkdirSync(recallDir, { recursive: true });
210
+ const init = runGitSync(["init", "--bare", gitDir], { env: process.env });
211
+ if (init.status !== 0) {
212
+ process.stderr.write(`[launch-recall] git init --bare failed:
213
+ ${init.stderr}`);
214
+ process.exit(1);
215
+ }
216
+ runGitSync(["config", "gc.auto", "256"], { env: gitEnv(gitDir) });
217
+ runGitSync(["config", "core.logAllRefUpdates", "true"], { env: gitEnv(gitDir) });
218
+ process.stderr.write(`[launch-recall] created shadow repo at ${gitDir}
219
+ `);
220
+ }
221
+ const gitignoreResult = ensureGitignoreEntry(workTree);
222
+ switch (gitignoreResult) {
223
+ case "added":
224
+ process.stderr.write(`[launch-recall] added ${GITIGNORE_LINE} to .gitignore
225
+ `);
226
+ break;
227
+ case "present":
228
+ process.stderr.write(`[launch-recall] .gitignore already excludes ${RECALL_DIR_NAME}
229
+ `);
230
+ break;
231
+ case "no-gitignore":
232
+ process.stderr.write(
233
+ `[launch-recall] no .gitignore in ${workTree} \u2014 create one and add "${GITIGNORE_LINE}"
234
+ `
235
+ );
236
+ break;
237
+ }
238
+ const configResult = writeDefaultConfig(workTree);
239
+ if (configResult === "created") {
240
+ process.stderr.write(`[launch-recall] wrote default ${CONFIG_FILENAME}
241
+ `);
242
+ } else {
243
+ process.stderr.write(`[launch-recall] ${CONFIG_FILENAME} already present
244
+ `);
245
+ }
246
+ process.stderr.write(`[launch-recall] ready. start the watcher with: launch-recall watch
247
+ `);
248
+ }
249
+ var fs2, path3, GITIGNORE_LINE;
250
+ var init_init = __esm({
251
+ "src/server/recall/init.ts"() {
252
+ "use strict";
253
+ fs2 = __toESM(require("node:fs"));
254
+ path3 = __toESM(require("node:path"));
255
+ init_git();
256
+ init_paths();
257
+ init_config();
258
+ GITIGNORE_LINE = `/${RECALL_DIR_NAME}/`;
259
+ }
260
+ });
261
+
262
+ // src/server/recall-mcp.ts
263
+ var recall_mcp_exports = {};
264
+ __export(recall_mcp_exports, {
265
+ startRecallMcpServer: () => startRecallMcpServer
266
+ });
267
+ function isPidAlive(pid) {
268
+ try {
269
+ process.kill(pid, 0);
270
+ return true;
271
+ } catch (err) {
272
+ const code = err.code;
273
+ if (code === "ESRCH") return false;
274
+ if (code === "EPERM") return true;
275
+ return false;
276
+ }
277
+ }
278
+ function pidFilePath(recallDir) {
279
+ return `${recallDir}/watch.pid`;
280
+ }
281
+ function existingWatcherPid() {
282
+ const { recallDir } = resolveRecallPaths();
283
+ const pf = pidFilePath(recallDir);
284
+ if (!(0, import_node_fs.existsSync)(pf)) return null;
285
+ const pid = Number((0, import_node_fs.readFileSync)(pf, "utf-8").trim());
286
+ if (!Number.isFinite(pid) || pid <= 0) return null;
287
+ return isPidAlive(pid) ? pid : null;
288
+ }
289
+ function ensureWatcher() {
290
+ const { gitDir, workTree } = resolveRecallPaths();
291
+ if (!(0, import_node_fs.existsSync)(gitDir)) {
292
+ process.stderr.write(
293
+ `[launch-recall mcp] shadow repo missing at ${gitDir} \u2014 run \`launch-recall init\` first. MCP tools will still serve read-only queries, but nothing will be captured.
294
+ `
295
+ );
296
+ return;
297
+ }
298
+ const existing = existingWatcherPid();
299
+ if (existing !== null) {
300
+ process.stderr.write(
301
+ `[launch-recall mcp] external watcher detected (pid ${existing}) \u2014 serving tools only, not spawning a sibling
302
+ `
303
+ );
304
+ return;
305
+ }
306
+ const entry = process.argv[1];
307
+ if (!entry) {
308
+ process.stderr.write(`[launch-recall mcp] cannot resolve entry path from argv \u2014 watcher not spawned
309
+ `);
310
+ return;
311
+ }
312
+ childWatcher = (0, import_node_child_process2.spawn)(process.execPath, [entry, "watch"], {
313
+ cwd: workTree,
314
+ stdio: ["ignore", "ignore", "inherit"],
315
+ detached: false
316
+ });
317
+ process.stderr.write(`[launch-recall mcp] spawned watcher (pid ${childWatcher.pid}) in ${workTree}
318
+ `);
319
+ childWatcher.on("exit", (code, sig) => {
320
+ process.stderr.write(`[launch-recall mcp] child watcher exited (code=${code} sig=${sig})
321
+ `);
322
+ childWatcher = null;
323
+ });
324
+ }
325
+ function killChildWatcher() {
326
+ if (childWatcher && !childWatcher.killed) {
327
+ try {
328
+ childWatcher.kill("SIGTERM");
329
+ } catch {
330
+ }
331
+ }
332
+ }
333
+ function text(payload) {
334
+ const t = typeof payload === "string" ? payload : JSON.stringify(payload, null, 2);
335
+ return { content: [{ type: "text", text: t }] };
336
+ }
337
+ async function handleTool(name, args) {
338
+ switch (name) {
339
+ case "recall.doctor":
340
+ return text(doctorTool());
341
+ case "recall.report":
342
+ return text(reportTool());
343
+ case "recall.status":
344
+ return text(statusTool());
345
+ case "recall.history": {
346
+ const path7 = String(args.path ?? "");
347
+ if (!path7) return text({ error: "path is required" });
348
+ const limit = typeof args.limit === "number" && args.limit > 0 ? Math.floor(args.limit) : 50;
349
+ return text(historyTool(path7, limit));
350
+ }
351
+ default:
352
+ return text({ error: `unknown tool: ${name}` });
353
+ }
354
+ }
355
+ function doctorTool() {
356
+ const { gitDir, recallDir } = resolveRecallPaths();
357
+ const checks = [];
358
+ const shadowOk = (0, import_node_fs.existsSync)(gitDir);
359
+ checks.push({
360
+ name: "shadow_repo",
361
+ ok: shadowOk,
362
+ detail: shadowOk ? gitDir : `missing \u2014 run \`launch-recall init\``
363
+ });
364
+ const pid = existingWatcherPid();
365
+ checks.push({
366
+ name: "watcher_alive",
367
+ ok: pid !== null,
368
+ detail: pid !== null ? `pid ${pid}` : `no live pid in ${pidFilePath(recallDir)}`
369
+ });
370
+ let recentOk = false;
371
+ let recentDetail = "no snapshots yet";
372
+ if (shadowOk) {
373
+ try {
374
+ const out = (0, import_node_child_process2.execFileSync)("git", [`--git-dir=${gitDir}`, "log", "-1", "--format=%aI"], {
375
+ encoding: "utf-8",
376
+ stdio: ["ignore", "pipe", "pipe"]
377
+ }).trim();
378
+ if (out) {
379
+ const ageHours = (Date.now() - new Date(out).getTime()) / 36e5;
380
+ recentOk = ageHours < 24;
381
+ recentDetail = `${out} (${ageHours.toFixed(1)}h ago)`;
382
+ }
383
+ } catch {
384
+ }
385
+ }
386
+ checks.push({ name: "recent_snapshot", ok: recentOk, detail: recentDetail });
387
+ return { ok: checks.every((c) => c.ok), checks };
388
+ }
389
+ function reportTool() {
390
+ const { gitDir, recallDir, workTree } = resolveRecallPaths();
391
+ if (!(0, import_node_fs.existsSync)(gitDir)) return { error: "shadow repo not initialised" };
392
+ let totalSnapshots = 0;
393
+ try {
394
+ const out = (0, import_node_child_process2.execFileSync)("git", [`--git-dir=${gitDir}`, "rev-list", "--count", "HEAD"], {
395
+ encoding: "utf-8",
396
+ stdio: ["ignore", "pipe", "pipe"]
397
+ }).trim();
398
+ totalSnapshots = Number(out) || 0;
399
+ } catch {
400
+ }
401
+ let shadowSizeBytes = 0;
402
+ try {
403
+ const out = (0, import_node_child_process2.execFileSync)("du", ["-sk", recallDir], { encoding: "utf-8" }).trim();
404
+ const kb = Number(out.split(/\s+/)[0]);
405
+ if (Number.isFinite(kb)) shadowSizeBytes = kb * 1024;
406
+ } catch {
407
+ }
408
+ const recentSnapshots = [];
409
+ let lastSnapshot = null;
410
+ try {
411
+ const out = (0, import_node_child_process2.execFileSync)(
412
+ "git",
413
+ [`--git-dir=${gitDir}`, "log", "-10", "--format=%H|%aI|%s"],
414
+ { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }
415
+ );
416
+ for (const line of out.split("\n").filter(Boolean)) {
417
+ const [sha, ts, ...rest] = line.split("|");
418
+ recentSnapshots.push({ sha, ts, message: rest.join("|") });
419
+ }
420
+ if (recentSnapshots.length > 0) lastSnapshot = recentSnapshots[0].ts;
421
+ } catch {
422
+ }
423
+ let config = null;
424
+ const cfgPath = `${workTree}/.launch-recall.json`;
425
+ if ((0, import_node_fs.existsSync)(cfgPath)) {
426
+ try {
427
+ config = JSON.parse((0, import_node_fs.readFileSync)(cfgPath, "utf-8"));
428
+ } catch {
429
+ }
430
+ }
431
+ return { totalSnapshots, shadowSizeBytes, lastSnapshot, recentSnapshots, config, workTree };
432
+ }
433
+ function historyTool(path7, limit) {
434
+ const { gitDir, workTree } = resolveRecallPaths();
435
+ if (!(0, import_node_fs.existsSync)(gitDir)) return { error: "shadow repo not initialised", path: path7, snapshots: [] };
436
+ const rel = path7.startsWith("/") ? path7.replace(`${workTree}/`, "").replace(/^\/+/, "") : path7;
437
+ const snapshots = [];
438
+ try {
439
+ const out = (0, import_node_child_process2.execFileSync)(
440
+ "git",
441
+ [`--git-dir=${gitDir}`, `--work-tree=${workTree}`, "log", `-${limit}`, "--format=%H|%aI|%s", "--", rel],
442
+ { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }
443
+ );
444
+ for (const line of out.split("\n").filter(Boolean)) {
445
+ const [sha, ts, ...rest] = line.split("|");
446
+ snapshots.push({ sha, ts, message: rest.join("|") });
447
+ }
448
+ } catch {
449
+ }
450
+ return { path: rel, snapshots };
451
+ }
452
+ function statusTool() {
453
+ const { gitDir, workTree } = resolveRecallPaths();
454
+ const pid = existingWatcherPid();
455
+ let lastSnapshotAt = null;
456
+ if ((0, import_node_fs.existsSync)(gitDir)) {
457
+ try {
458
+ lastSnapshotAt = (0, import_node_child_process2.execFileSync)("git", [`--git-dir=${gitDir}`, "log", "-1", "--format=%aI"], {
459
+ encoding: "utf-8",
460
+ stdio: ["ignore", "pipe", "pipe"]
461
+ }).trim() || null;
462
+ } catch {
463
+ }
464
+ }
465
+ return {
466
+ running: pid !== null,
467
+ pid,
468
+ lastSnapshotAt,
469
+ watchTree: workTree,
470
+ shadowRepo: gitDir
471
+ };
472
+ }
473
+ function send(msg) {
474
+ process.stdout.write(JSON.stringify(msg) + "\n");
475
+ }
476
+ function sendResponse(id, result) {
477
+ send({ jsonrpc: "2.0", id, result });
478
+ }
479
+ function sendError(id, code, message) {
480
+ send({ jsonrpc: "2.0", id, error: { code, message } });
481
+ }
482
+ async function handleMessage(parsed) {
483
+ const id = parsed.id;
484
+ const method = parsed.method;
485
+ const params = parsed.params ?? {};
486
+ switch (method) {
487
+ case "initialize":
488
+ sendResponse(id ?? null, {
489
+ protocolVersion: "2024-11-05",
490
+ capabilities: { tools: {} },
491
+ serverInfo: SERVER_INFO
492
+ });
493
+ break;
494
+ case "notifications/initialized":
495
+ break;
496
+ case "tools/list":
497
+ sendResponse(id ?? null, { tools: TOOLS });
498
+ break;
499
+ case "tools/call": {
500
+ const toolName = params.name;
501
+ const toolArgs = params.arguments ?? {};
502
+ try {
503
+ const result = await handleTool(toolName, toolArgs);
504
+ sendResponse(id ?? null, result);
505
+ } catch (err) {
506
+ sendError(id ?? null, -32603, `Tool error: ${err.message}`);
507
+ }
508
+ break;
509
+ }
510
+ case "ping":
511
+ sendResponse(id ?? null, {});
512
+ break;
513
+ default:
514
+ if (id !== void 0) sendError(id ?? null, -32601, `Method not found: ${method}`);
515
+ }
516
+ }
517
+ function startRecallMcpServer() {
518
+ process.stderr.write("[launch-recall mcp] starting on stdio\n");
519
+ ensureWatcher();
520
+ const teardown = () => {
521
+ killChildWatcher();
522
+ process.exit(0);
523
+ };
524
+ process.on("SIGTERM", teardown);
525
+ process.on("SIGINT", teardown);
526
+ process.on("exit", killChildWatcher);
527
+ process.stdin.setEncoding("utf-8");
528
+ let buffer = "";
529
+ process.stdin.on("data", (chunk) => {
530
+ buffer += chunk;
531
+ const lines = buffer.split("\n");
532
+ buffer = lines.pop() || "";
533
+ for (const line of lines) {
534
+ const trimmed = line.trim();
535
+ if (!trimmed) continue;
536
+ try {
537
+ handleMessage(JSON.parse(trimmed)).catch((err) => {
538
+ process.stderr.write(`[launch-recall mcp] message error: ${err}
539
+ `);
540
+ });
541
+ } catch (err) {
542
+ process.stderr.write(`[launch-recall mcp] parse error: ${err}
543
+ `);
544
+ }
545
+ }
546
+ });
547
+ process.stdin.on("end", () => {
548
+ process.stderr.write("[launch-recall mcp] stdin closed, exiting\n");
549
+ teardown();
550
+ });
551
+ void import_node_fs.statSync;
552
+ }
553
+ var import_node_child_process2, import_node_fs, SERVER_INFO, TOOLS, childWatcher;
554
+ var init_recall_mcp = __esm({
555
+ "src/server/recall-mcp.ts"() {
556
+ "use strict";
557
+ import_node_child_process2 = require("node:child_process");
558
+ import_node_fs = require("node:fs");
559
+ init_paths();
560
+ SERVER_INFO = { name: "launch-recall", version: "0.0.1" };
561
+ TOOLS = [
562
+ {
563
+ name: "recall.doctor",
564
+ description: "Health check on the launch-recall watcher and its shadow repo.\n\nReturns: { ok, checks: [{ name, ok, detail }] }. Names: shadow_repo (exists + valid), watcher_alive (pid file points at live pid), recent_snapshot (any snapshot within 24h).",
565
+ inputSchema: { type: "object", properties: {} }
566
+ },
567
+ {
568
+ name: "recall.report",
569
+ description: "Project-wide details on what recall has captured.\n\nReturns: { totalSnapshots, shadowSizeBytes, lastSnapshot, recentSnapshots: [{ sha, ts, message }], config: { debounce, ignore, retention } }. recentSnapshots is the last 10.",
570
+ inputSchema: { type: "object", properties: {} }
571
+ },
572
+ {
573
+ name: "recall.history",
574
+ description: "Snapshot history for a specific file or directory. Path is relative to the project root (or an absolute path under it).\n\nReturns: { path, snapshots: [{ sha, ts, message }] }. Empty array if the path was never captured (e.g. ignored, or simply has no churn).",
575
+ inputSchema: {
576
+ type: "object",
577
+ properties: {
578
+ path: { type: "string", description: "File or directory to query." },
579
+ limit: { type: "number", description: "Max snapshots to return (default 50)." }
580
+ },
581
+ required: ["path"]
582
+ }
583
+ },
584
+ {
585
+ name: "recall.status",
586
+ description: "Quick liveness + last-snapshot info. Cheaper than recall.doctor \u2014 no historical inspection.\n\nReturns: { running, pid, lastSnapshotAt, watchTree }.",
587
+ inputSchema: { type: "object", properties: {} }
588
+ }
589
+ ];
590
+ childWatcher = null;
591
+ }
592
+ });
593
+
594
+ // src/server/recall/stop.ts
595
+ var stop_exports = {};
596
+ __export(stop_exports, {
597
+ pidFilePath: () => pidFilePath2,
598
+ runStop: () => runStop,
599
+ stopWatcher: () => stopWatcher
600
+ });
601
+ function pidFilePath2(recallDir) {
602
+ return path4.join(recallDir, "watch.pid");
603
+ }
604
+ function isAlive(pid) {
605
+ try {
606
+ process.kill(pid, 0);
607
+ return true;
608
+ } catch (err) {
609
+ const code = err.code;
610
+ if (code === "ESRCH") return false;
611
+ if (code === "EPERM") return true;
612
+ return false;
613
+ }
614
+ }
615
+ function sleep(ms) {
616
+ return new Promise((r) => setTimeout(r, ms));
617
+ }
618
+ async function stopWatcher(quiet = false) {
619
+ const { recallDir } = resolveRecallPaths();
620
+ const pf = pidFilePath2(recallDir);
621
+ if (!fs3.existsSync(pf)) {
622
+ if (!quiet) process.stderr.write(`[launch-recall] no PID file at ${pf}
623
+ `);
624
+ return { state: "no-pid-file" };
625
+ }
626
+ const raw = fs3.readFileSync(pf, "utf8").trim();
627
+ const pid = Number(raw);
628
+ if (!Number.isFinite(pid) || pid <= 0) {
629
+ if (!quiet) process.stderr.write(`[launch-recall] invalid PID file content: ${raw}
630
+ `);
631
+ fs3.unlinkSync(pf);
632
+ return { state: "kill-failed" };
633
+ }
634
+ if (!isAlive(pid)) {
635
+ if (!quiet) process.stderr.write(`[launch-recall] process ${pid} not running (stale PID file)
636
+ `);
637
+ fs3.unlinkSync(pf);
638
+ return { state: "already-dead", pid };
639
+ }
640
+ try {
641
+ process.kill(pid, "SIGTERM");
642
+ if (!quiet) process.stderr.write(`[launch-recall] sent SIGTERM to ${pid}
643
+ `);
644
+ } catch (err) {
645
+ if (!quiet)
646
+ process.stderr.write(
647
+ `[launch-recall] SIGTERM failed: ${err instanceof Error ? err.message : String(err)}
648
+ `
649
+ );
650
+ return { state: "kill-failed", pid };
651
+ }
652
+ for (let i = 0; i < 8; i++) {
653
+ await sleep(250);
654
+ if (!isAlive(pid)) {
655
+ if (fs3.existsSync(pf)) fs3.unlinkSync(pf);
656
+ if (!quiet) process.stderr.write(`[launch-recall] watcher ${pid} stopped
657
+ `);
658
+ return { state: "stopped", pid };
659
+ }
660
+ }
661
+ try {
662
+ process.kill(pid, "SIGKILL");
663
+ if (!quiet) process.stderr.write(`[launch-recall] sent SIGKILL to ${pid}
664
+ `);
665
+ } catch {
666
+ }
667
+ if (fs3.existsSync(pf)) fs3.unlinkSync(pf);
668
+ return { state: "stopped", pid };
669
+ }
670
+ async function runStop(_args) {
671
+ await stopWatcher(false);
672
+ }
673
+ var fs3, path4;
674
+ var init_stop = __esm({
675
+ "src/server/recall/stop.ts"() {
676
+ "use strict";
677
+ fs3 = __toESM(require("node:fs"));
678
+ path4 = __toESM(require("node:path"));
679
+ init_paths();
680
+ }
681
+ });
682
+
683
+ // src/server/recall/watch.ts
684
+ var watch_exports = {};
685
+ __export(watch_exports, {
686
+ runWatch: () => runWatch
687
+ });
688
+ function isPidAlive2(pid) {
689
+ try {
690
+ process.kill(pid, 0);
691
+ return true;
692
+ } catch (err) {
693
+ const code = err.code;
694
+ if (code === "ESRCH") return false;
695
+ return code === "EPERM";
696
+ }
697
+ }
698
+ function parseDebounceOverride(args) {
699
+ const idx = args.indexOf("--debounce");
700
+ if (idx === -1) return null;
701
+ const raw = args[idx + 1];
702
+ const n = Number(raw);
703
+ if (!Number.isFinite(n) || n <= 0) {
704
+ process.stderr.write(`[launch-recall] invalid --debounce value: ${raw}
705
+ `);
706
+ process.exit(2);
707
+ }
708
+ return n;
709
+ }
710
+ function log(msg) {
711
+ process.stderr.write(`[launch-recall] ${msg}
712
+ `);
713
+ }
714
+ async function runWatch(args) {
715
+ const { workTree, gitDir, recallDir } = resolveRecallPaths();
716
+ if (!fs4.existsSync(gitDir)) {
717
+ log(`not initialised \u2014 run: launch-recall init`);
718
+ process.exit(1);
719
+ }
720
+ const pf = pidFilePath2(recallDir);
721
+ if (fs4.existsSync(pf)) {
722
+ const existing = Number(fs4.readFileSync(pf, "utf8").trim());
723
+ if (Number.isFinite(existing) && isPidAlive2(existing)) {
724
+ log(`watcher already running (pid ${existing}) \u2014 run \`launch-recall stop\` first`);
725
+ process.exit(1);
726
+ }
727
+ try {
728
+ fs4.unlinkSync(pf);
729
+ } catch {
730
+ }
731
+ }
732
+ fs4.writeFileSync(pf, String(process.pid));
733
+ const cleanupPidFile = () => {
734
+ try {
735
+ fs4.unlinkSync(pf);
736
+ } catch {
737
+ }
738
+ };
739
+ const { config, source } = loadConfig(workTree);
740
+ const debounceOverride = parseDebounceOverride(args);
741
+ const debounceMs = debounceOverride ?? config.debounce;
742
+ const ignoreRegex = buildIgnoreRegex(config.ignore);
743
+ const pathspecExcludes = buildPathspecExcludes(config.ignore);
744
+ log(`watching ${workTree}`);
745
+ log(`shadow repo ${gitDir}`);
746
+ log(`config ${source === "file" ? "from .launch-recall.json" : "defaults"}`);
747
+ log(`debounce ${debounceMs}ms`);
748
+ log(`ignoring [${config.ignore.join(", ")}]`);
749
+ let busy = false;
750
+ let queued = false;
751
+ let timer = null;
752
+ const env = gitEnv(gitDir, workTree);
753
+ const addArgs = ["add", "-f", "--", ".", ...pathspecExcludes];
754
+ const stamp = () => (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
755
+ const snap = () => {
756
+ if (busy) {
757
+ queued = true;
758
+ return;
759
+ }
760
+ busy = true;
761
+ const add = (0, import_node_child_process3.spawn)("git", addArgs, { env, stdio: "ignore" });
762
+ add.on("exit", (addCode) => {
763
+ if (addCode !== 0) {
764
+ busy = false;
765
+ if (queued) {
766
+ queued = false;
767
+ snap();
768
+ }
769
+ return;
770
+ }
771
+ const check = (0, import_node_child_process3.spawn)("git", ["diff", "--cached", "--quiet"], { env, stdio: "ignore" });
772
+ check.on("exit", (checkCode) => {
773
+ if (checkCode === 0) {
774
+ busy = false;
775
+ if (queued) {
776
+ queued = false;
777
+ snap();
778
+ }
779
+ return;
780
+ }
781
+ const commit = (0, import_node_child_process3.spawn)(
782
+ "git",
783
+ ["commit", "-q", "-m", `snap ${stamp()}`],
784
+ { env, stdio: "ignore" }
785
+ );
786
+ commit.on("exit", () => {
787
+ busy = false;
788
+ if (queued) {
789
+ queued = false;
790
+ snap();
791
+ }
792
+ });
793
+ });
794
+ });
795
+ };
796
+ const scheduleSnap = () => {
797
+ if (timer) clearTimeout(timer);
798
+ timer = setTimeout(snap, debounceMs);
799
+ };
800
+ const abort = new AbortController();
801
+ const shutdown = (sig, code) => {
802
+ if (sig === "SIGINT") log("stopping");
803
+ abort.abort();
804
+ if (timer) clearTimeout(timer);
805
+ cleanupPidFile();
806
+ process.exit(code);
807
+ };
808
+ process.on("SIGINT", () => shutdown("SIGINT", 0));
809
+ process.on("SIGTERM", () => shutdown("SIGTERM", 0));
810
+ process.on("exit", cleanupPidFile);
811
+ try {
812
+ const watcher = fsp.watch(workTree, { recursive: true, signal: abort.signal });
813
+ for await (const ev of watcher) {
814
+ if (ev.filename && ignoreRegex.test(ev.filename)) continue;
815
+ scheduleSnap();
816
+ }
817
+ } catch (err) {
818
+ if (err.name === "AbortError") {
819
+ cleanupPidFile();
820
+ return;
821
+ }
822
+ log(`watcher error: ${err instanceof Error ? err.message : String(err)}`);
823
+ cleanupPidFile();
824
+ process.exit(1);
825
+ }
826
+ }
827
+ var fs4, fsp, import_node_child_process3;
828
+ var init_watch = __esm({
829
+ "src/server/recall/watch.ts"() {
830
+ "use strict";
831
+ fs4 = __toESM(require("node:fs"));
832
+ fsp = __toESM(require("node:fs/promises"));
833
+ import_node_child_process3 = require("node:child_process");
834
+ init_git();
835
+ init_paths();
836
+ init_config();
837
+ init_stop();
838
+ }
839
+ });
840
+
841
+ // src/server/recall/log.ts
842
+ var log_exports = {};
843
+ __export(log_exports, {
844
+ runLog: () => runLog
845
+ });
846
+ async function runLog(args) {
847
+ const { workTree, gitDir } = resolveRecallPaths();
848
+ if (!fs5.existsSync(gitDir)) {
849
+ process.stderr.write(`[launch-recall] not initialised \u2014 run: launch-recall init
850
+ `);
851
+ process.exit(1);
852
+ }
853
+ const env = gitEnv(gitDir, workTree);
854
+ const gitArgs = ["log", "--oneline", "--decorate"];
855
+ if (args.length > 0) {
856
+ gitArgs.push("--", ...args);
857
+ }
858
+ const code = await runGit(gitArgs, { env });
859
+ process.exit(code);
860
+ }
861
+ var fs5;
862
+ var init_log = __esm({
863
+ "src/server/recall/log.ts"() {
864
+ "use strict";
865
+ fs5 = __toESM(require("node:fs"));
866
+ init_git();
867
+ init_paths();
868
+ }
869
+ });
870
+
871
+ // src/server/recall/restore.ts
872
+ var restore_exports = {};
873
+ __export(restore_exports, {
874
+ runRestore: () => runRestore
875
+ });
876
+ function parseArgs(args) {
877
+ let target = null;
878
+ let at = "HEAD";
879
+ let outPath = null;
880
+ for (let i = 0; i < args.length; i++) {
881
+ const a = args[i];
882
+ if (a === "--at") {
883
+ at = args[++i] ?? "HEAD";
884
+ } else if (a === "--out" || a === "-o") {
885
+ outPath = args[++i] ?? null;
886
+ } else if (!target) {
887
+ target = a;
888
+ }
889
+ }
890
+ return { target, at, outPath };
891
+ }
892
+ async function runRestore(args) {
893
+ const { target, at, outPath } = parseArgs(args);
894
+ if (!target) {
895
+ process.stderr.write(
896
+ "Usage: launch-recall restore <path> [--at <ref>] [--out <path>]\n"
897
+ );
898
+ process.exit(2);
899
+ }
900
+ const { workTree, gitDir } = resolveRecallPaths();
901
+ if (!fs6.existsSync(gitDir)) {
902
+ process.stderr.write(`[launch-recall] not initialised \u2014 run: launch-recall init
903
+ `);
904
+ process.exit(1);
905
+ }
906
+ const env = gitEnv(gitDir, workTree);
907
+ const spec = `${at}:${target}`;
908
+ const result = runGitSync(["show", spec], { env });
909
+ if (result.status !== 0) {
910
+ process.stderr.write(
911
+ `[launch-recall] could not read ${spec} from shadow:
912
+ ${result.stderr}`
913
+ );
914
+ process.exit(result.status);
915
+ }
916
+ const destination = outPath ?? path5.resolve(workTree, target);
917
+ if (outPath) {
918
+ fs6.mkdirSync(path5.dirname(path5.resolve(destination)), { recursive: true });
919
+ }
920
+ if (fs6.existsSync(destination)) {
921
+ process.stderr.write(
922
+ `[launch-recall] refusing to overwrite existing file: ${destination}
923
+ use --out to write to a different location
924
+ `
925
+ );
926
+ process.exit(1);
927
+ }
928
+ fs6.writeFileSync(destination, result.stdout);
929
+ process.stderr.write(`[launch-recall] restored ${spec} \u2192 ${destination}
930
+ `);
931
+ }
932
+ var fs6, path5;
933
+ var init_restore = __esm({
934
+ "src/server/recall/restore.ts"() {
935
+ "use strict";
936
+ fs6 = __toESM(require("node:fs"));
937
+ path5 = __toESM(require("node:path"));
938
+ init_git();
939
+ init_paths();
940
+ }
941
+ });
942
+
943
+ // src/server/recall/forget.ts
944
+ var forget_exports = {};
945
+ __export(forget_exports, {
946
+ runForget: () => runForget
947
+ });
948
+ function parseArgs2(args) {
949
+ const out = {
950
+ keepLast: void 0,
951
+ maxAgeDays: void 0,
952
+ dryRun: false
953
+ };
954
+ for (let i = 0; i < args.length; i++) {
955
+ const a = args[i];
956
+ if (a === "--dry-run" || a === "-n") {
957
+ out.dryRun = true;
958
+ } else if (a === "--keep-last") {
959
+ const raw = args[++i];
960
+ if (raw === "off") {
961
+ out.keepLast = null;
962
+ } else {
963
+ const n = Number(raw);
964
+ if (!Number.isFinite(n) || n < 0) {
965
+ process.stderr.write(`[launch-recall] invalid --keep-last: ${raw}
966
+ `);
967
+ process.exit(2);
968
+ }
969
+ out.keepLast = n;
970
+ }
971
+ } else if (a === "--max-age") {
972
+ const raw = args[++i] ?? "";
973
+ if (raw === "off") {
974
+ out.maxAgeDays = null;
975
+ } else {
976
+ const m = /^(\d+)\s*(d|days?)?$/i.exec(raw);
977
+ if (!m) {
978
+ process.stderr.write(`[launch-recall] invalid --max-age: ${raw} (expected e.g. 30 or 30d)
979
+ `);
980
+ process.exit(2);
981
+ }
982
+ out.maxAgeDays = Number(m[1]);
983
+ }
984
+ } else {
985
+ process.stderr.write(`[launch-recall] unknown forget arg: ${a}
986
+ `);
987
+ process.exit(2);
988
+ }
989
+ }
990
+ return out;
991
+ }
992
+ function resolveRetention(args, configRetention) {
993
+ return {
994
+ keepLast: args.keepLast === void 0 ? configRetention.keepLast : args.keepLast,
995
+ maxAgeDays: args.maxAgeDays === void 0 ? configRetention.maxAgeDays : args.maxAgeDays
996
+ };
997
+ }
998
+ function listCommitsOldestFirst(env) {
999
+ const result = runGitSync(
1000
+ ["log", "--reverse", "--format=%H %ct", "main"],
1001
+ { env }
1002
+ );
1003
+ if (result.status !== 0) {
1004
+ return [];
1005
+ }
1006
+ return result.stdout.split("\n").filter(Boolean).map((line) => {
1007
+ const [sha, ct] = line.split(" ");
1008
+ return { sha, committerEpoch: Number(ct) };
1009
+ });
1010
+ }
1011
+ function pickCutoff(commits, retention) {
1012
+ if (commits.length === 0) return { dropCount: 0, firstKeptIndex: 0 };
1013
+ let firstKept = 0;
1014
+ if (typeof retention.keepLast === "number" && commits.length > retention.keepLast) {
1015
+ firstKept = Math.max(firstKept, commits.length - retention.keepLast);
1016
+ }
1017
+ if (typeof retention.maxAgeDays === "number") {
1018
+ const cutoffEpoch = Math.floor(Date.now() / 1e3) - retention.maxAgeDays * 86400;
1019
+ let idx = commits.findIndex((c) => c.committerEpoch >= cutoffEpoch);
1020
+ if (idx === -1) idx = commits.length;
1021
+ firstKept = Math.max(firstKept, idx);
1022
+ }
1023
+ if (firstKept >= commits.length) {
1024
+ firstKept = commits.length - 1;
1025
+ }
1026
+ return { dropCount: firstKept, firstKeptIndex: firstKept };
1027
+ }
1028
+ function rewriteHistory(env, kept) {
1029
+ if (kept.length === 0) return { error: "no commits to keep" };
1030
+ const first = kept[0];
1031
+ const treeResult = runGitSync(["rev-parse", `${first.sha}^{tree}`], { env });
1032
+ if (treeResult.status !== 0) return { error: `rev-parse tree failed: ${treeResult.stderr}` };
1033
+ const firstTree = treeResult.stdout.trim();
1034
+ const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
1035
+ const newRoot = runGitSync(
1036
+ ["commit-tree", firstTree, "-m", `snap (truncated ${stamp})`],
1037
+ { env }
1038
+ );
1039
+ if (newRoot.status !== 0) return { error: `commit-tree (root) failed: ${newRoot.stderr}` };
1040
+ let parent = newRoot.stdout.trim();
1041
+ for (let i = 1; i < kept.length; i++) {
1042
+ const c = kept[i];
1043
+ const t = runGitSync(["rev-parse", `${c.sha}^{tree}`], { env });
1044
+ if (t.status !== 0) return { error: `rev-parse tree ${c.sha} failed: ${t.stderr}` };
1045
+ const msg = runGitSync(["log", "-1", "--format=%B", c.sha], { env });
1046
+ if (msg.status !== 0) return { error: `log -1 ${c.sha} failed: ${msg.stderr}` };
1047
+ const next = runGitSync(
1048
+ ["commit-tree", t.stdout.trim(), "-p", parent, "-m", msg.stdout.trim() || "snap"],
1049
+ { env }
1050
+ );
1051
+ if (next.status !== 0) return { error: `commit-tree ${c.sha} failed: ${next.stderr}` };
1052
+ parent = next.stdout.trim();
1053
+ }
1054
+ const update = runGitSync(
1055
+ ["update-ref", "refs/heads/main", parent],
1056
+ { env }
1057
+ );
1058
+ if (update.status !== 0) return { error: `update-ref failed: ${update.stderr}` };
1059
+ return { newTipSha: parent };
1060
+ }
1061
+ async function runForget(args) {
1062
+ const parsed = parseArgs2(args);
1063
+ const { workTree, gitDir } = resolveRecallPaths();
1064
+ if (!fs7.existsSync(gitDir)) {
1065
+ process.stderr.write(`[launch-recall] not initialised \u2014 run: launch-recall init
1066
+ `);
1067
+ process.exit(1);
1068
+ }
1069
+ const { config } = loadConfig(workTree);
1070
+ const retention = resolveRetention(parsed, config.retention);
1071
+ if (retention.keepLast === null && retention.maxAgeDays === null) {
1072
+ process.stderr.write(`[launch-recall] no retention rules set \u2014 nothing to do
1073
+ `);
1074
+ return;
1075
+ }
1076
+ const env = gitEnv(gitDir, workTree);
1077
+ const commits = listCommitsOldestFirst(env);
1078
+ if (commits.length === 0) {
1079
+ process.stderr.write(`[launch-recall] no commits in shadow repo
1080
+ `);
1081
+ return;
1082
+ }
1083
+ const { dropCount } = pickCutoff(commits, retention);
1084
+ const kept = commits.slice(dropCount);
1085
+ process.stderr.write(
1086
+ `[launch-recall] retention: keepLast=${retention.keepLast ?? "off"} maxAgeDays=${retention.maxAgeDays ?? "off"}
1087
+ `
1088
+ );
1089
+ process.stderr.write(
1090
+ `[launch-recall] ${commits.length} snapshots total, ${dropCount} to drop, ${kept.length} to keep
1091
+ `
1092
+ );
1093
+ if (dropCount === 0) {
1094
+ process.stderr.write(`[launch-recall] nothing to drop
1095
+ `);
1096
+ return;
1097
+ }
1098
+ if (parsed.dryRun) {
1099
+ process.stderr.write(`[launch-recall] --dry-run \u2014 no changes written
1100
+ `);
1101
+ return;
1102
+ }
1103
+ process.stderr.write(`[launch-recall] rewriting history\u2026
1104
+ `);
1105
+ const result = rewriteHistory(env, kept);
1106
+ if ("error" in result) {
1107
+ process.stderr.write(`[launch-recall] rewrite failed: ${result.error}
1108
+ `);
1109
+ process.exit(1);
1110
+ }
1111
+ process.stderr.write(`[launch-recall] new main tip: ${result.newTipSha.slice(0, 7)}
1112
+ `);
1113
+ process.stderr.write(`[launch-recall] expiring reflog\u2026
1114
+ `);
1115
+ runGitSync(["reflog", "expire", "--expire=now", "--all"], { env });
1116
+ process.stderr.write(`[launch-recall] running gc --prune=now\u2026
1117
+ `);
1118
+ const gc = runGitSync(["gc", "--prune=now", "--quiet"], { env });
1119
+ if (gc.status !== 0) {
1120
+ process.stderr.write(`[launch-recall] gc warning:
1121
+ ${gc.stderr}`);
1122
+ }
1123
+ process.stderr.write(`[launch-recall] done
1124
+ `);
1125
+ }
1126
+ var fs7;
1127
+ var init_forget = __esm({
1128
+ "src/server/recall/forget.ts"() {
1129
+ "use strict";
1130
+ fs7 = __toESM(require("node:fs"));
1131
+ init_git();
1132
+ init_config();
1133
+ init_paths();
1134
+ }
1135
+ });
1136
+
1137
+ // src/server/recall/push.ts
1138
+ var push_exports = {};
1139
+ __export(push_exports, {
1140
+ runPush: () => runPush
1141
+ });
1142
+ async function runPush(args) {
1143
+ const remote = args[0];
1144
+ if (!remote) {
1145
+ process.stderr.write("Usage: launch-recall push <remote-url-or-path>\n");
1146
+ process.exit(2);
1147
+ }
1148
+ const { workTree, gitDir } = resolveRecallPaths();
1149
+ if (!fs8.existsSync(gitDir)) {
1150
+ process.stderr.write(`[launch-recall] not initialised \u2014 run: launch-recall init
1151
+ `);
1152
+ process.exit(1);
1153
+ }
1154
+ const env = gitEnv(gitDir, workTree);
1155
+ const code = await runGit(["push", "--mirror", remote], { env });
1156
+ process.exit(code);
1157
+ }
1158
+ var fs8;
1159
+ var init_push = __esm({
1160
+ "src/server/recall/push.ts"() {
1161
+ "use strict";
1162
+ fs8 = __toESM(require("node:fs"));
1163
+ init_git();
1164
+ init_paths();
1165
+ }
1166
+ });
1167
+
1168
+ // src/server/recall/gc.ts
1169
+ var gc_exports = {};
1170
+ __export(gc_exports, {
1171
+ runGc: () => runGc
1172
+ });
1173
+ async function runGc(_args) {
1174
+ const { workTree, gitDir } = resolveRecallPaths();
1175
+ if (!fs9.existsSync(gitDir)) {
1176
+ process.stderr.write(`[launch-recall] not initialised \u2014 run: launch-recall init
1177
+ `);
1178
+ process.exit(1);
1179
+ }
1180
+ const env = gitEnv(gitDir, workTree);
1181
+ const code = await runGit(["gc", "--auto"], { env });
1182
+ process.exit(code);
1183
+ }
1184
+ var fs9;
1185
+ var init_gc = __esm({
1186
+ "src/server/recall/gc.ts"() {
1187
+ "use strict";
1188
+ fs9 = __toESM(require("node:fs"));
1189
+ init_git();
1190
+ init_paths();
1191
+ }
1192
+ });
1193
+
1194
+ // src/server/recall/uninstall.ts
1195
+ var uninstall_exports = {};
1196
+ __export(uninstall_exports, {
1197
+ runUninstall: () => runUninstall
1198
+ });
1199
+ function parseArgs3(args) {
1200
+ const out = { keepHistory: false, keepConfig: false, force: false };
1201
+ for (const a of args) {
1202
+ if (a === "--keep-history") out.keepHistory = true;
1203
+ else if (a === "--keep-config") out.keepConfig = true;
1204
+ else if (a === "--force" || a === "-y") out.force = true;
1205
+ else {
1206
+ process.stderr.write(`[launch-recall] unknown uninstall arg: ${a}
1207
+ `);
1208
+ process.exit(2);
1209
+ }
1210
+ }
1211
+ return out;
1212
+ }
1213
+ function formatSize(bytes) {
1214
+ if (bytes < 1024) return `${bytes} B`;
1215
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1216
+ if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1217
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
1218
+ }
1219
+ function dirSizeBytes(dir) {
1220
+ if (!fs10.existsSync(dir)) return 0;
1221
+ let total = 0;
1222
+ const stack = [dir];
1223
+ while (stack.length > 0) {
1224
+ const cur = stack.pop();
1225
+ let entries;
1226
+ try {
1227
+ entries = fs10.readdirSync(cur, { withFileTypes: true });
1228
+ } catch {
1229
+ continue;
1230
+ }
1231
+ for (const e of entries) {
1232
+ const full = path6.join(cur, e.name);
1233
+ try {
1234
+ if (e.isDirectory()) {
1235
+ stack.push(full);
1236
+ } else if (e.isFile()) {
1237
+ total += fs10.statSync(full).size;
1238
+ }
1239
+ } catch {
1240
+ }
1241
+ }
1242
+ }
1243
+ return total;
1244
+ }
1245
+ function promptYesNo(question) {
1246
+ return new Promise((resolve3) => {
1247
+ if (!process.stdin.isTTY) {
1248
+ process.stderr.write(
1249
+ `${question} \u2014 non-interactive shell, refusing without --force
1250
+ `
1251
+ );
1252
+ resolve3(false);
1253
+ return;
1254
+ }
1255
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
1256
+ rl.question(`${question} (y/N): `, (answer) => {
1257
+ rl.close();
1258
+ const a = answer.trim().toLowerCase();
1259
+ resolve3(a === "y" || a === "yes");
1260
+ });
1261
+ });
1262
+ }
1263
+ function removeGitignoreLine(gitignorePath) {
1264
+ if (!fs10.existsSync(gitignorePath)) return "no-file";
1265
+ const text2 = fs10.readFileSync(gitignorePath, "utf8");
1266
+ const lines = text2.split(/\r?\n/);
1267
+ const matches = (l) => {
1268
+ const t = l.trim();
1269
+ return t === `/${RECALL_DIR_NAME}/` || t === `${RECALL_DIR_NAME}/` || t === RECALL_DIR_NAME || t === `/${RECALL_DIR_NAME}` || t === `${RECALL_DIR_NAME}/**`;
1270
+ };
1271
+ if (!lines.some(matches)) return "no-line";
1272
+ const kept = lines.filter((l) => !matches(l));
1273
+ fs10.writeFileSync(gitignorePath, kept.join("\n"));
1274
+ return "removed";
1275
+ }
1276
+ async function runUninstall(args) {
1277
+ const parsed = parseArgs3(args);
1278
+ const { workTree, recallDir } = resolveRecallPaths();
1279
+ const configPath = path6.join(workTree, CONFIG_FILENAME);
1280
+ const gitignorePath = path6.join(workTree, ".gitignore");
1281
+ const pf = pidFilePath2(recallDir);
1282
+ const watcherRunning = fs10.existsSync(pf);
1283
+ const shadowPresent = fs10.existsSync(recallDir);
1284
+ const configPresent = fs10.existsSync(configPath);
1285
+ const shadowSize = shadowPresent ? dirSizeBytes(recallDir) : 0;
1286
+ let gitignoreHasLine = false;
1287
+ if (fs10.existsSync(gitignorePath)) {
1288
+ const text2 = fs10.readFileSync(gitignorePath, "utf8");
1289
+ gitignoreHasLine = text2.split(/\r?\n/).some((l) => {
1290
+ const t = l.trim();
1291
+ return t === `/${RECALL_DIR_NAME}/` || t === `${RECALL_DIR_NAME}/` || t === RECALL_DIR_NAME || t === `/${RECALL_DIR_NAME}` || t === `${RECALL_DIR_NAME}/**`;
1292
+ });
1293
+ }
1294
+ const willStopWatcher = watcherRunning;
1295
+ const willRemoveShadow = !parsed.keepHistory && shadowPresent;
1296
+ const willRemoveConfig = !parsed.keepConfig && configPresent;
1297
+ const willPatchGitignore = !parsed.keepConfig && gitignoreHasLine;
1298
+ const plan = [];
1299
+ if (willStopWatcher) plan.push("Stop running watcher");
1300
+ if (willRemoveShadow) plan.push(`Delete ${recallDir} (${formatSize(shadowSize)})`);
1301
+ if (willRemoveConfig) plan.push(`Delete ${configPath}`);
1302
+ if (willPatchGitignore) plan.push(`Remove /${RECALL_DIR_NAME}/ from .gitignore`);
1303
+ if (plan.length === 0) {
1304
+ process.stderr.write("[launch-recall] nothing to uninstall\n");
1305
+ return;
1306
+ }
1307
+ process.stderr.write("[launch-recall] uninstall plan:\n");
1308
+ for (const a of plan) process.stderr.write(` - ${a}
1309
+ `);
1310
+ if (!parsed.force) {
1311
+ const ok = await promptYesNo("Proceed?");
1312
+ if (!ok) {
1313
+ process.stderr.write("[launch-recall] aborted\n");
1314
+ return;
1315
+ }
1316
+ }
1317
+ if (willStopWatcher) {
1318
+ await stopWatcher(false);
1319
+ }
1320
+ if (willRemoveShadow) {
1321
+ fs10.rmSync(recallDir, { recursive: true, force: true });
1322
+ process.stderr.write(`[launch-recall] removed ${recallDir}
1323
+ `);
1324
+ }
1325
+ if (willRemoveConfig) {
1326
+ try {
1327
+ fs10.unlinkSync(configPath);
1328
+ process.stderr.write(`[launch-recall] removed ${configPath}
1329
+ `);
1330
+ } catch {
1331
+ }
1332
+ }
1333
+ if (willPatchGitignore) {
1334
+ const res = removeGitignoreLine(gitignorePath);
1335
+ if (res === "removed") {
1336
+ process.stderr.write(`[launch-recall] removed /${RECALL_DIR_NAME}/ from .gitignore
1337
+ `);
1338
+ }
1339
+ }
1340
+ process.stderr.write("[launch-recall] uninstall complete\n");
1341
+ }
1342
+ var fs10, path6, readline;
1343
+ var init_uninstall = __esm({
1344
+ "src/server/recall/uninstall.ts"() {
1345
+ "use strict";
1346
+ fs10 = __toESM(require("node:fs"));
1347
+ path6 = __toESM(require("node:path"));
1348
+ readline = __toESM(require("node:readline"));
1349
+ init_config();
1350
+ init_paths();
1351
+ init_stop();
1352
+ }
1353
+ });
1354
+
1355
+ // src/server/recall-entry.ts
1356
+ function logStderr(msg) {
1357
+ process.stderr.write(`[launch-recall] ${msg}
1358
+ `);
1359
+ }
1360
+ function printUsage() {
1361
+ process.stdout.write(
1362
+ [
1363
+ "Usage: launch-recall <command> [options]",
1364
+ "",
1365
+ "Commands:",
1366
+ " init create .recall/repo.git + gitignore entry + default config",
1367
+ " watch [--debounce <ms>] foreground watcher (defaults from .launch-recall.json)",
1368
+ " stop terminate the running watcher",
1369
+ " mcp stdio MCP server (spawns watcher as child)",
1370
+ " log [path] show shadow history (passthrough to git log)",
1371
+ " restore <path> [--at <ref>] extract a file from a snapshot",
1372
+ " forget [--keep-last N] [--max-age 30d] [--dry-run]",
1373
+ " prune history per retention rules",
1374
+ " push <remote> mirror shadow to an external bare repo",
1375
+ " gc pack and prune the shadow repo",
1376
+ " uninstall [--keep-history] [--keep-config] [--force]",
1377
+ " stop watcher + remove shadow + config + gitignore line",
1378
+ ""
1379
+ ].join("\n")
1380
+ );
1381
+ }
1382
+ async function main() {
1383
+ const argv = process.argv.slice(2);
1384
+ const subcommand = argv[0];
1385
+ if (!subcommand || subcommand === "--help" || subcommand === "-h") {
1386
+ printUsage();
1387
+ return;
1388
+ }
1389
+ switch (subcommand) {
1390
+ case "init": {
1391
+ const { runInit: runInit2 } = await Promise.resolve().then(() => (init_init(), init_exports));
1392
+ await runInit2(argv.slice(1));
1393
+ return;
1394
+ }
1395
+ case "mcp": {
1396
+ const { startRecallMcpServer: startRecallMcpServer2 } = await Promise.resolve().then(() => (init_recall_mcp(), recall_mcp_exports));
1397
+ startRecallMcpServer2();
1398
+ return;
1399
+ }
1400
+ case "watch": {
1401
+ const { runWatch: runWatch2 } = await Promise.resolve().then(() => (init_watch(), watch_exports));
1402
+ await runWatch2(argv.slice(1));
1403
+ return;
1404
+ }
1405
+ case "stop": {
1406
+ const { runStop: runStop2 } = await Promise.resolve().then(() => (init_stop(), stop_exports));
1407
+ await runStop2(argv.slice(1));
1408
+ return;
1409
+ }
1410
+ case "log": {
1411
+ const { runLog: runLog2 } = await Promise.resolve().then(() => (init_log(), log_exports));
1412
+ await runLog2(argv.slice(1));
1413
+ return;
1414
+ }
1415
+ case "restore": {
1416
+ const { runRestore: runRestore2 } = await Promise.resolve().then(() => (init_restore(), restore_exports));
1417
+ await runRestore2(argv.slice(1));
1418
+ return;
1419
+ }
1420
+ case "forget": {
1421
+ const { runForget: runForget2 } = await Promise.resolve().then(() => (init_forget(), forget_exports));
1422
+ await runForget2(argv.slice(1));
1423
+ return;
1424
+ }
1425
+ case "push": {
1426
+ const { runPush: runPush2 } = await Promise.resolve().then(() => (init_push(), push_exports));
1427
+ await runPush2(argv.slice(1));
1428
+ return;
1429
+ }
1430
+ case "gc": {
1431
+ const { runGc: runGc2 } = await Promise.resolve().then(() => (init_gc(), gc_exports));
1432
+ await runGc2(argv.slice(1));
1433
+ return;
1434
+ }
1435
+ case "uninstall": {
1436
+ const { runUninstall: runUninstall2 } = await Promise.resolve().then(() => (init_uninstall(), uninstall_exports));
1437
+ await runUninstall2(argv.slice(1));
1438
+ return;
1439
+ }
1440
+ default: {
1441
+ logStderr(`unknown command: ${subcommand}`);
1442
+ printUsage();
1443
+ process.exit(2);
1444
+ }
1445
+ }
1446
+ }
1447
+ main().catch((err) => {
1448
+ logStderr(`fatal: ${err instanceof Error ? err.message : String(err)}`);
1449
+ process.exit(1);
1450
+ });