@launchsecure/launch-kit 0.0.26 → 0.0.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/beacon/beacon.mjs +1003 -440
- package/dist/beacon/beacon.mjs.map +1 -1
- package/dist/beacon/beacon.umd.js +45 -24
- package/dist/beacon/beacon.umd.js.map +1 -1
- package/dist/beacon/types/capture/events.d.ts +20 -0
- package/dist/beacon/types/capture/events.d.ts.map +1 -0
- package/dist/beacon/types/element.d.ts +1 -0
- package/dist/beacon/types/element.d.ts.map +1 -1
- package/dist/beacon/types/index.d.ts +2 -1
- package/dist/beacon/types/index.d.ts.map +1 -1
- package/dist/beacon/types/monitor/dom.d.ts +13 -0
- package/dist/beacon/types/monitor/dom.d.ts.map +1 -0
- package/dist/beacon/types/monitor/index.d.ts +19 -0
- package/dist/beacon/types/monitor/index.d.ts.map +1 -0
- package/dist/beacon/types/monitor/network.d.ts +12 -0
- package/dist/beacon/types/monitor/network.d.ts.map +1 -0
- package/dist/beacon/types/monitor/transport.d.ts +27 -0
- package/dist/beacon/types/monitor/transport.d.ts.map +1 -0
- package/dist/beacon/types/monitor/types.d.ts +117 -0
- package/dist/beacon/types/monitor/types.d.ts.map +1 -0
- package/dist/beacon/types/types.d.ts +10 -0
- package/dist/beacon/types/types.d.ts.map +1 -1
- package/dist/beacon/types/ui/drawer.d.ts +3 -1
- package/dist/beacon/types/ui/drawer.d.ts.map +1 -1
- package/dist/beacon/types/ui/monitor-panel.d.ts +19 -0
- package/dist/beacon/types/ui/monitor-panel.d.ts.map +1 -0
- package/dist/chart-client/assets/index-CJ4mgRRF.css +1 -0
- package/dist/chart-client/assets/{index-Bk1hawjD.js → index-Ccy-DpI-.js} +46 -42
- package/dist/chart-client/index.html +2 -2
- package/dist/client/assets/index-DI5qSR_w.css +32 -0
- package/dist/client/assets/index-Dp0_okva.js +294 -0
- package/dist/client/index.html +2 -2
- package/dist/council-client/assets/index-C_-vAM9L.css +1 -0
- package/dist/council-client/index.html +2 -2
- package/dist/deck-client/assets/{_baseUniq-C2xT_eYu.js → _baseUniq-W2JQDmje.js} +1 -1
- package/dist/deck-client/assets/{arc-CmVL9pGd.js → arc-DIBWAId9.js} +1 -1
- package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-BSFgdjve.js → architectureDiagram-Q4EWVU46-CAIRMvJK.js} +1 -1
- package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-DuLzscvP.js → blockDiagram-DXYQGD6D-BeNaNiOi.js} +1 -1
- package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-CfCJB8eY.js → c4Diagram-AHTNJAMY-B9Ozi62h.js} +1 -1
- package/dist/deck-client/assets/channel-CRdozqbp.js +1 -0
- package/dist/deck-client/assets/{chunk-4BX2VUAB-DxmLYTWZ.js → chunk-4BX2VUAB-D7AZ47dt.js} +1 -1
- package/dist/deck-client/assets/{chunk-4TB4RGXK-CCnf7GFE.js → chunk-4TB4RGXK-DnVnNPcI.js} +1 -1
- package/dist/deck-client/assets/{chunk-55IACEB6-Db9DApcj.js → chunk-55IACEB6-UKYs-YNd.js} +1 -1
- package/dist/deck-client/assets/{chunk-EDXVE4YY-DmYDq8ZI.js → chunk-EDXVE4YY-D43b-SKn.js} +1 -1
- package/dist/deck-client/assets/{chunk-FMBD7UC4-BGhUlF20.js → chunk-FMBD7UC4-QzBAoyyW.js} +1 -1
- package/dist/deck-client/assets/{chunk-OYMX7WX6-CpEnicQZ.js → chunk-OYMX7WX6-Cjif4r6W.js} +1 -1
- package/dist/deck-client/assets/{chunk-QZHKN3VN-Doa7LKwf.js → chunk-QZHKN3VN-CqLDirEI.js} +1 -1
- package/dist/deck-client/assets/{chunk-YZCP3GAM-CpkIlH6V.js → chunk-YZCP3GAM-_FQvmMs4.js} +1 -1
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-lIZMp57W.js +1 -0
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-lIZMp57W.js +1 -0
- package/dist/deck-client/assets/clone-BtWeSTyJ.js +1 -0
- package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-Bkh8Bfcb.js → cose-bilkent-S5V4N54A-rfrocesE.js} +1 -1
- package/dist/deck-client/assets/{dagre-KV5264BT-Bp0XpTgH.js → dagre-KV5264BT-Bv_7DJat.js} +1 -1
- package/dist/deck-client/assets/{diagram-5BDNPKRD-ZHiyGYPQ.js → diagram-5BDNPKRD-4F1414G5.js} +1 -1
- package/dist/deck-client/assets/{diagram-G4DWMVQ6-BW-Q8_H5.js → diagram-G4DWMVQ6-C4-Pszqm.js} +1 -1
- package/dist/deck-client/assets/{diagram-MMDJMWI5-6I3LTafu.js → diagram-MMDJMWI5-B647TIx9.js} +1 -1
- package/dist/deck-client/assets/{diagram-TYMM5635-CyM5YK28.js → diagram-TYMM5635-BFAqpezd.js} +1 -1
- package/dist/deck-client/assets/{erDiagram-SMLLAGMA-CjNxVJHk.js → erDiagram-SMLLAGMA-BfBfrJOC.js} +1 -1
- package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-BDQHuAJR.js → flowDiagram-DWJPFMVM-DX9YAYes.js} +1 -1
- package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-B7MnkpbP.js → ganttDiagram-T4ZO3ILL-DCuiy7wF.js} +1 -1
- package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-C9dZAcYD.js → gitGraphDiagram-UUTBAWPF-CGp1IXUh.js} +1 -1
- package/dist/deck-client/assets/{graph-CjdBnzUy.js → graph-B7g8aoxv.js} +1 -1
- package/dist/deck-client/assets/{index-DeIVPW63.js → index-Dg1r-WSN.js} +3 -3
- package/dist/deck-client/assets/index-DsIZ3LqL.css +1 -0
- package/dist/deck-client/assets/{infoDiagram-42DDH7IO-C7d3iRC3.js → infoDiagram-42DDH7IO-L3fahMkF.js} +1 -1
- package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-BcYGKj09.js → ishikawaDiagram-UXIWVN3A-aS_EjWBZ.js} +1 -1
- package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-DqFlRrOL.js → journeyDiagram-VCZTEJTY-djTSQZF9.js} +1 -1
- package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-BJhPp1NR.js → kanban-definition-6JOO6SKY-CcTHo4CM.js} +1 -1
- package/dist/deck-client/assets/{layout-DIeS6GvK.js → layout-mEJiadb7.js} +1 -1
- package/dist/deck-client/assets/{linear-He_yJy5H.js → linear-XgTKqyRu.js} +1 -1
- package/dist/deck-client/assets/{min-DQ6Kx06t.js → min-Ct9jZdpd.js} +1 -1
- package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-sQ62L8T2.js → mindmap-definition-QFDTVHPH-BaFxCGNU.js} +1 -1
- package/dist/deck-client/assets/{pieDiagram-DEJITSTG-BqCWmU2K.js → pieDiagram-DEJITSTG-CIbYYjtw.js} +1 -1
- package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-rQ1TJOoe.js → quadrantDiagram-34T5L4WZ-D9EtCOvh.js} +1 -1
- package/dist/deck-client/assets/{requirementDiagram-MS252O5E-BO2MPBOM.js → requirementDiagram-MS252O5E-xeni9eVG.js} +1 -1
- package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-BgsHEVex.js → sankeyDiagram-XADWPNL6-LYeknz9h.js} +1 -1
- package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-B3j1yMLU.js → sequenceDiagram-FGHM5R23-RDbsKFZf.js} +1 -1
- package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-C8jFlZou.js → stateDiagram-FHFEXIEX-BH1Zjglk.js} +1 -1
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-BrV78NDR.js +1 -0
- package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-tM-qo4Zk.js → timeline-definition-GMOUNBTQ-IFXxKptt.js} +1 -1
- package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-B0-6kOEu.js → vennDiagram-DHZGUBPP-D-sLkQs9.js} +1 -1
- package/dist/deck-client/assets/{wardley-RL74JXVD-HpBk07P-.js → wardley-RL74JXVD-C010F8l4.js} +1 -1
- package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-BkA1NLDE.js → wardleyDiagram-NUSXRM2D-BTjjuDU3.js} +1 -1
- package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-CEKGSuI-.js → xychartDiagram-5P7HB3ND-AYbv92n-.js} +1 -1
- package/dist/deck-client/index.html +2 -2
- package/dist/server/beacon-monitor-entry.js +353 -0
- package/dist/server/chart-serve.js +3836 -3750
- package/dist/server/cli.js +8789 -8219
- package/dist/server/council-entry.js +17 -5
- package/dist/server/council-serve.js +8 -3
- package/dist/server/course-entry.js +246 -0
- package/dist/server/deck-mcp-entry.js +24 -12
- package/dist/server/deck-serve.js +11 -8
- package/dist/server/graph-mcp-entry.js +5005 -4865
- package/dist/server/init-entry.js +939 -0
- package/dist/server/orbit-entry.js +2435 -0
- package/dist/server/parse-worker-entry.js +4721 -0
- package/dist/server/recall-entry.js +356 -18
- package/package.json +11 -4
- package/scaffolds/ls-marketplace/.claude-plugin/marketplace.json +15 -0
- package/scaffolds/ls-marketplace/plugins/ls/.claude-plugin/plugin.json +28 -0
- package/scaffolds/ls-marketplace/plugins/ls/commands/activate-beacon.md +216 -0
- package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-array.md +92 -0
- package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-clear.md +68 -0
- package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-pulse.md +80 -0
- package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-scan.md +62 -0
- package/scaffolds/ls-marketplace/plugins/ls/commands/show-mcp-status.md +109 -0
- package/scaffolds/ls-marketplace/plugins/ls/commands/standup.md +177 -0
- package/scaffolds/migrate-safety/.github/workflows/backup-on-migration.yml +72 -0
- package/scaffolds/migrate-safety/docs/migrations-runbook.md +172 -0
- package/scaffolds/migrate-safety/scripts/migrate-with-backup.sh +294 -0
- package/scaffolds/recall-hook/scripts/ensure-recall.sh +69 -0
- package/dist/chart-client/assets/index-DpaGa3bY.css +0 -1
- package/dist/client/assets/index-Bfel4OQ5.css +0 -32
- package/dist/client/assets/index-eC-WuUWB.js +0 -291
- package/dist/council-client/assets/index-P5kMsT5a.css +0 -1
- package/dist/deck-client/assets/channel-B4aNO8ZB.js +0 -1
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-BHTI0yWz.js +0 -1
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-BHTI0yWz.js +0 -1
- package/dist/deck-client/assets/clone-HduFm7qU.js +0 -1
- package/dist/deck-client/assets/index-LKZDAS9S.css +0 -1
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-BoqepHW0.js +0 -1
- /package/dist/council-client/assets/{index-Cs_MVXHf.js → index-Dt4zWKSj.js} +0 -0
|
@@ -122,8 +122,8 @@ function loadConfig(workTree) {
|
|
|
122
122
|
return { config: { ...DEFAULT_CONFIG }, source: "defaults", path: configPath };
|
|
123
123
|
}
|
|
124
124
|
try {
|
|
125
|
-
const
|
|
126
|
-
const raw = JSON.parse(
|
|
125
|
+
const text2 = fs.readFileSync(configPath, "utf8");
|
|
126
|
+
const raw = JSON.parse(text2);
|
|
127
127
|
return { config: mergeConfig(raw), source: "file", path: configPath };
|
|
128
128
|
} catch (err) {
|
|
129
129
|
process.stderr.write(
|
|
@@ -259,14 +259,346 @@ var init_init = __esm({
|
|
|
259
259
|
}
|
|
260
260
|
});
|
|
261
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
|
+
|
|
262
594
|
// src/server/recall/stop.ts
|
|
263
595
|
var stop_exports = {};
|
|
264
596
|
__export(stop_exports, {
|
|
265
|
-
pidFilePath: () =>
|
|
597
|
+
pidFilePath: () => pidFilePath2,
|
|
266
598
|
runStop: () => runStop,
|
|
267
599
|
stopWatcher: () => stopWatcher
|
|
268
600
|
});
|
|
269
|
-
function
|
|
601
|
+
function pidFilePath2(recallDir) {
|
|
270
602
|
return path4.join(recallDir, "watch.pid");
|
|
271
603
|
}
|
|
272
604
|
function isAlive(pid) {
|
|
@@ -285,7 +617,7 @@ function sleep(ms) {
|
|
|
285
617
|
}
|
|
286
618
|
async function stopWatcher(quiet = false) {
|
|
287
619
|
const { recallDir } = resolveRecallPaths();
|
|
288
|
-
const pf =
|
|
620
|
+
const pf = pidFilePath2(recallDir);
|
|
289
621
|
if (!fs3.existsSync(pf)) {
|
|
290
622
|
if (!quiet) process.stderr.write(`[launch-recall] no PID file at ${pf}
|
|
291
623
|
`);
|
|
@@ -353,7 +685,7 @@ var watch_exports = {};
|
|
|
353
685
|
__export(watch_exports, {
|
|
354
686
|
runWatch: () => runWatch
|
|
355
687
|
});
|
|
356
|
-
function
|
|
688
|
+
function isPidAlive2(pid) {
|
|
357
689
|
try {
|
|
358
690
|
process.kill(pid, 0);
|
|
359
691
|
return true;
|
|
@@ -385,10 +717,10 @@ async function runWatch(args) {
|
|
|
385
717
|
log(`not initialised \u2014 run: launch-recall init`);
|
|
386
718
|
process.exit(1);
|
|
387
719
|
}
|
|
388
|
-
const pf =
|
|
720
|
+
const pf = pidFilePath2(recallDir);
|
|
389
721
|
if (fs4.existsSync(pf)) {
|
|
390
722
|
const existing = Number(fs4.readFileSync(pf, "utf8").trim());
|
|
391
|
-
if (Number.isFinite(existing) &&
|
|
723
|
+
if (Number.isFinite(existing) && isPidAlive2(existing)) {
|
|
392
724
|
log(`watcher already running (pid ${existing}) \u2014 run \`launch-recall stop\` first`);
|
|
393
725
|
process.exit(1);
|
|
394
726
|
}
|
|
@@ -426,7 +758,7 @@ async function runWatch(args) {
|
|
|
426
758
|
return;
|
|
427
759
|
}
|
|
428
760
|
busy = true;
|
|
429
|
-
const add = (0,
|
|
761
|
+
const add = (0, import_node_child_process3.spawn)("git", addArgs, { env, stdio: "ignore" });
|
|
430
762
|
add.on("exit", (addCode) => {
|
|
431
763
|
if (addCode !== 0) {
|
|
432
764
|
busy = false;
|
|
@@ -436,7 +768,7 @@ async function runWatch(args) {
|
|
|
436
768
|
}
|
|
437
769
|
return;
|
|
438
770
|
}
|
|
439
|
-
const check = (0,
|
|
771
|
+
const check = (0, import_node_child_process3.spawn)("git", ["diff", "--cached", "--quiet"], { env, stdio: "ignore" });
|
|
440
772
|
check.on("exit", (checkCode) => {
|
|
441
773
|
if (checkCode === 0) {
|
|
442
774
|
busy = false;
|
|
@@ -446,7 +778,7 @@ async function runWatch(args) {
|
|
|
446
778
|
}
|
|
447
779
|
return;
|
|
448
780
|
}
|
|
449
|
-
const commit = (0,
|
|
781
|
+
const commit = (0, import_node_child_process3.spawn)(
|
|
450
782
|
"git",
|
|
451
783
|
["commit", "-q", "-m", `snap ${stamp()}`],
|
|
452
784
|
{ env, stdio: "ignore" }
|
|
@@ -492,13 +824,13 @@ async function runWatch(args) {
|
|
|
492
824
|
process.exit(1);
|
|
493
825
|
}
|
|
494
826
|
}
|
|
495
|
-
var fs4, fsp,
|
|
827
|
+
var fs4, fsp, import_node_child_process3;
|
|
496
828
|
var init_watch = __esm({
|
|
497
829
|
"src/server/recall/watch.ts"() {
|
|
498
830
|
"use strict";
|
|
499
831
|
fs4 = __toESM(require("node:fs"));
|
|
500
832
|
fsp = __toESM(require("node:fs/promises"));
|
|
501
|
-
|
|
833
|
+
import_node_child_process3 = require("node:child_process");
|
|
502
834
|
init_git();
|
|
503
835
|
init_paths();
|
|
504
836
|
init_config();
|
|
@@ -930,8 +1262,8 @@ function promptYesNo(question) {
|
|
|
930
1262
|
}
|
|
931
1263
|
function removeGitignoreLine(gitignorePath) {
|
|
932
1264
|
if (!fs10.existsSync(gitignorePath)) return "no-file";
|
|
933
|
-
const
|
|
934
|
-
const lines =
|
|
1265
|
+
const text2 = fs10.readFileSync(gitignorePath, "utf8");
|
|
1266
|
+
const lines = text2.split(/\r?\n/);
|
|
935
1267
|
const matches = (l) => {
|
|
936
1268
|
const t = l.trim();
|
|
937
1269
|
return t === `/${RECALL_DIR_NAME}/` || t === `${RECALL_DIR_NAME}/` || t === RECALL_DIR_NAME || t === `/${RECALL_DIR_NAME}` || t === `${RECALL_DIR_NAME}/**`;
|
|
@@ -946,15 +1278,15 @@ async function runUninstall(args) {
|
|
|
946
1278
|
const { workTree, recallDir } = resolveRecallPaths();
|
|
947
1279
|
const configPath = path6.join(workTree, CONFIG_FILENAME);
|
|
948
1280
|
const gitignorePath = path6.join(workTree, ".gitignore");
|
|
949
|
-
const pf =
|
|
1281
|
+
const pf = pidFilePath2(recallDir);
|
|
950
1282
|
const watcherRunning = fs10.existsSync(pf);
|
|
951
1283
|
const shadowPresent = fs10.existsSync(recallDir);
|
|
952
1284
|
const configPresent = fs10.existsSync(configPath);
|
|
953
1285
|
const shadowSize = shadowPresent ? dirSizeBytes(recallDir) : 0;
|
|
954
1286
|
let gitignoreHasLine = false;
|
|
955
1287
|
if (fs10.existsSync(gitignorePath)) {
|
|
956
|
-
const
|
|
957
|
-
gitignoreHasLine =
|
|
1288
|
+
const text2 = fs10.readFileSync(gitignorePath, "utf8");
|
|
1289
|
+
gitignoreHasLine = text2.split(/\r?\n/).some((l) => {
|
|
958
1290
|
const t = l.trim();
|
|
959
1291
|
return t === `/${RECALL_DIR_NAME}/` || t === `${RECALL_DIR_NAME}/` || t === RECALL_DIR_NAME || t === `/${RECALL_DIR_NAME}` || t === `${RECALL_DIR_NAME}/**`;
|
|
960
1292
|
});
|
|
@@ -1034,6 +1366,7 @@ function printUsage() {
|
|
|
1034
1366
|
" init create .recall/repo.git + gitignore entry + default config",
|
|
1035
1367
|
" watch [--debounce <ms>] foreground watcher (defaults from .launch-recall.json)",
|
|
1036
1368
|
" stop terminate the running watcher",
|
|
1369
|
+
" mcp stdio MCP server (spawns watcher as child)",
|
|
1037
1370
|
" log [path] show shadow history (passthrough to git log)",
|
|
1038
1371
|
" restore <path> [--at <ref>] extract a file from a snapshot",
|
|
1039
1372
|
" forget [--keep-last N] [--max-age 30d] [--dry-run]",
|
|
@@ -1059,6 +1392,11 @@ async function main() {
|
|
|
1059
1392
|
await runInit2(argv.slice(1));
|
|
1060
1393
|
return;
|
|
1061
1394
|
}
|
|
1395
|
+
case "mcp": {
|
|
1396
|
+
const { startRecallMcpServer: startRecallMcpServer2 } = await Promise.resolve().then(() => (init_recall_mcp(), recall_mcp_exports));
|
|
1397
|
+
startRecallMcpServer2();
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
1062
1400
|
case "watch": {
|
|
1063
1401
|
const { runWatch: runWatch2 } = await Promise.resolve().then(() => (init_watch(), watch_exports));
|
|
1064
1402
|
await runWatch2(argv.slice(1));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@launchsecure/launch-kit",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.28",
|
|
4
4
|
"description": "LaunchSecure toolkit — launch-pod (pipeline), launch-chart (project graph MCP), launch-deck (visual playground MCP), launch-kit-beacon (feedback Web Component), launch-recall (file-watcher backup).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "LaunchSecure - AutomateWithUs",
|
|
@@ -47,20 +47,26 @@
|
|
|
47
47
|
"access": "public"
|
|
48
48
|
},
|
|
49
49
|
"bin": {
|
|
50
|
+
"launch-kit": "./dist/server/init-entry.js",
|
|
50
51
|
"launch-pod": "./dist/server/cli.js",
|
|
51
52
|
"launch-chart": "./dist/server/graph-mcp-entry.js",
|
|
52
53
|
"launch-deck": "./dist/server/deck-mcp-entry.js",
|
|
53
|
-
"launch-recall": "./dist/server/recall-entry.js"
|
|
54
|
+
"launch-recall": "./dist/server/recall-entry.js",
|
|
55
|
+
"launch-orbit": "./dist/server/orbit-entry.js",
|
|
56
|
+
"launch-course": "./dist/server/course-entry.js",
|
|
57
|
+
"launch-beacon": "./dist/server/beacon-monitor-entry.js"
|
|
54
58
|
},
|
|
55
59
|
"files": [
|
|
56
60
|
"dist",
|
|
57
|
-
"prompts"
|
|
61
|
+
"prompts",
|
|
62
|
+
"scaffolds"
|
|
58
63
|
],
|
|
59
64
|
"dependencies": {
|
|
60
65
|
"cacheable-lookup": "^7.0.0",
|
|
61
66
|
"cloudflared": "^0.7.1",
|
|
62
67
|
"html-to-image": "^1.11.13",
|
|
63
68
|
"node-pty": "^1.2.0-beta.12",
|
|
69
|
+
"pg": "^8.13.0",
|
|
64
70
|
"tree-sitter": "^0.21.1",
|
|
65
71
|
"tree-sitter-typescript": "^0.23.2",
|
|
66
72
|
"typescript": "^5.5.0",
|
|
@@ -70,6 +76,7 @@
|
|
|
70
76
|
},
|
|
71
77
|
"devDependencies": {
|
|
72
78
|
"@types/node": "^20.0.0",
|
|
79
|
+
"@types/pg": "^8.11.10",
|
|
73
80
|
"@types/react": "^18.3.12",
|
|
74
81
|
"@types/react-dom": "^18.3.1",
|
|
75
82
|
"@types/ws": "^8.5.10",
|
|
@@ -105,7 +112,7 @@
|
|
|
105
112
|
"build:council-client": "vite build --config vite.council.config.ts",
|
|
106
113
|
"build:client": "vite build",
|
|
107
114
|
"build:chart-client": "vite build --config vite.chart.config.ts",
|
|
108
|
-
"build:server": "esbuild src/server/cli.ts src/server/fb-wizard.ts src/server/graph-mcp-entry.ts src/server/chart-serve.ts src/server/deck-mcp-entry.ts src/server/deck-serve.ts src/server/council-entry.ts src/server/council-serve.ts src/server/recall-entry.ts --bundle --platform=node --target=node18 --outdir=dist/server --external:node-pty --external:ws --external:typescript --external:web-tree-sitter --external:tree-sitter-typescript --external:cloudflared && rm -rf dist/server/public && cp -r ../claude-code-web/src/public dist/server/public && rm -rf dist/server/graph/queries && mkdir -p dist/server/graph && cp -r src/server/graph/queries dist/server/graph/queries",
|
|
115
|
+
"build:server": "esbuild src/server/cli.ts src/server/fb-wizard.ts src/server/graph-mcp-entry.ts src/server/chart-serve.ts src/server/deck-mcp-entry.ts src/server/deck-serve.ts src/server/council-entry.ts src/server/council-serve.ts src/server/recall-entry.ts src/server/init-entry.ts src/server/orbit-entry.ts src/server/course-entry.ts src/server/beacon-monitor-entry.ts src/server/parse-worker-entry.ts --bundle --platform=node --target=node18 --outdir=dist/server --external:node-pty --external:ws --external:typescript --external:web-tree-sitter --external:tree-sitter-typescript --external:cloudflared --external:pg --external:pg-native && rm -rf dist/server/public && cp -r ../claude-code-web/src/public dist/server/public && rm -rf dist/server/graph/queries && mkdir -p dist/server/graph && cp -r src/server/graph/queries dist/server/graph/queries",
|
|
109
116
|
"dev:client": "vite",
|
|
110
117
|
"dev:chart": "pnpm build:server && pnpm build:chart-client && node dist/server/graph-mcp-entry.js serve",
|
|
111
118
|
"dev:server": "pnpm build:server && node dist/server/cli.js",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "launchsecure",
|
|
3
|
+
"description": "LaunchSecure plugins for Claude Code — in-app feedback widget activation, and future LS-namespaced workflows. Distributed locally via launch-kit init today; the same catalog will move to github.com/launchsecure/claude-plugins for centralized updates.",
|
|
4
|
+
"owner": {
|
|
5
|
+
"name": "LaunchSecure — AutomateWithUs",
|
|
6
|
+
"email": "hello@automatewith.us"
|
|
7
|
+
},
|
|
8
|
+
"plugins": [
|
|
9
|
+
{
|
|
10
|
+
"name": "ls",
|
|
11
|
+
"source": "./plugins/ls",
|
|
12
|
+
"description": "LS-namespaced slash commands. Exposes /ls:activate-beacon to wire the launch-kit-beacon in-app feedback widget into the current project."
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ls",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "LaunchSecure commands for Claude Code — in-app feedback widget activation, daily standups posted to the Comm Hub, and future LS-namespaced workflows.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "LaunchSecure — AutomateWithUs",
|
|
7
|
+
"url": "https://automatewith.us"
|
|
8
|
+
},
|
|
9
|
+
"repository": "https://github.com/launchsecure/launchsecure-v2",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"launchsecure",
|
|
13
|
+
"launch-kit",
|
|
14
|
+
"launch-kit-beacon",
|
|
15
|
+
"feedback",
|
|
16
|
+
"standup",
|
|
17
|
+
"daily-update"
|
|
18
|
+
],
|
|
19
|
+
"commands": [
|
|
20
|
+
"./commands/activate-beacon.md",
|
|
21
|
+
"./commands/standup.md",
|
|
22
|
+
"./commands/show-mcp-status.md",
|
|
23
|
+
"./commands/beacon-scan.md",
|
|
24
|
+
"./commands/beacon-pulse.md",
|
|
25
|
+
"./commands/beacon-array.md",
|
|
26
|
+
"./commands/beacon-clear.md"
|
|
27
|
+
]
|
|
28
|
+
}
|