@launchsecure/launch-kit 0.0.31 → 0.0.33

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 (95) hide show
  1. package/dist/beacon/beacon.mjs +550 -521
  2. package/dist/beacon/beacon.mjs.map +1 -1
  3. package/dist/beacon/beacon.umd.js +9 -9
  4. package/dist/beacon/beacon.umd.js.map +1 -1
  5. package/dist/beacon/types/internal/pick-mode-overlay.d.ts.map +1 -1
  6. package/dist/beacon/types/internal/picker.d.ts.map +1 -1
  7. package/dist/beacon/types/internal/pin-popover.d.ts.map +1 -1
  8. package/dist/beacon/types/internal/selector.d.ts.map +1 -1
  9. package/dist/chart-client/assets/{index-B__ARB8k.js → index-DFu2xIrM.js} +2 -2
  10. package/dist/chart-client/assets/index-DpKO9p0s.css +1 -0
  11. package/dist/chart-client/index.html +2 -2
  12. package/dist/client/assets/{index-h8kMzVtG.js → index-Cbw6bVdx.js} +2 -2
  13. package/dist/client/assets/index-Dv6dD2zY.css +32 -0
  14. package/dist/client/index.html +2 -2
  15. package/dist/council-client/assets/index-AqQ9Sei6.css +1 -0
  16. package/dist/council-client/assets/{index-CWaDcsFR.js → index-CAsmGTzg.js} +2 -2
  17. package/dist/council-client/index.html +2 -2
  18. package/dist/deck-client/assets/{_baseUniq-DdHaBFYO.js → _baseUniq-BiVx0WO_.js} +1 -1
  19. package/dist/deck-client/assets/{arc-D98e_18X.js → arc-DGMkiEzS.js} +1 -1
  20. package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-DNFZzh-4.js → architectureDiagram-Q4EWVU46-Y2WRmHtk.js} +1 -1
  21. package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-DeQvGUdX.js → blockDiagram-DXYQGD6D-_Lbfu5BQ.js} +1 -1
  22. package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-B6ekZf1n.js → c4Diagram-AHTNJAMY-CTqpYTBX.js} +1 -1
  23. package/dist/deck-client/assets/channel-DB6LxW_l.js +1 -0
  24. package/dist/deck-client/assets/{chunk-4BX2VUAB-9aDWymq2.js → chunk-4BX2VUAB-liEIbPHs.js} +1 -1
  25. package/dist/deck-client/assets/{chunk-4TB4RGXK-DtKQqaI7.js → chunk-4TB4RGXK-CCc6lYvL.js} +1 -1
  26. package/dist/deck-client/assets/{chunk-55IACEB6-COy9hEae.js → chunk-55IACEB6-D02jJUR2.js} +1 -1
  27. package/dist/deck-client/assets/{chunk-EDXVE4YY-D_f861An.js → chunk-EDXVE4YY-BFmGMbLD.js} +1 -1
  28. package/dist/deck-client/assets/{chunk-FMBD7UC4-CmuA5UKn.js → chunk-FMBD7UC4-6wFLOVcJ.js} +1 -1
  29. package/dist/deck-client/assets/{chunk-OYMX7WX6-vT8z8D-0.js → chunk-OYMX7WX6-Bnr8RiBf.js} +1 -1
  30. package/dist/deck-client/assets/{chunk-QZHKN3VN-CTlwwg-R.js → chunk-QZHKN3VN-Ct82MksJ.js} +1 -1
  31. package/dist/deck-client/assets/{chunk-YZCP3GAM-C44yr620.js → chunk-YZCP3GAM-BXmN1diQ.js} +1 -1
  32. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-g944ZyG8.js +1 -0
  33. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-g944ZyG8.js +1 -0
  34. package/dist/deck-client/assets/clone-DiIRH1pI.js +1 -0
  35. package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-DBB2J2nL.js → cose-bilkent-S5V4N54A-CmQCT-mH.js} +1 -1
  36. package/dist/deck-client/assets/{dagre-KV5264BT-DxDTYbKl.js → dagre-KV5264BT-DDdSa9EX.js} +1 -1
  37. package/dist/deck-client/assets/{diagram-5BDNPKRD-DByWrWd1.js → diagram-5BDNPKRD-Bccks2xJ.js} +1 -1
  38. package/dist/deck-client/assets/{diagram-G4DWMVQ6-B8B6ddMq.js → diagram-G4DWMVQ6-CPPNgxmQ.js} +1 -1
  39. package/dist/deck-client/assets/{diagram-MMDJMWI5-BMUZ2PWK.js → diagram-MMDJMWI5-KrD300pS.js} +1 -1
  40. package/dist/deck-client/assets/{diagram-TYMM5635-Bk9e8BB-.js → diagram-TYMM5635-DefnLuQf.js} +1 -1
  41. package/dist/deck-client/assets/{erDiagram-SMLLAGMA-DcOSwSol.js → erDiagram-SMLLAGMA-DI9FfnFP.js} +1 -1
  42. package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-DI-4BR0F.js → flowDiagram-DWJPFMVM-twKyd3Fx.js} +1 -1
  43. package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-BeZuXBoU.js → ganttDiagram-T4ZO3ILL-Wau3jhBr.js} +1 -1
  44. package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-Bcki__f-.js → gitGraphDiagram-UUTBAWPF-D9GgYXwb.js} +1 -1
  45. package/dist/deck-client/assets/{graph-CifKx6G1.js → graph-BhNLzyXS.js} +1 -1
  46. package/dist/deck-client/assets/index-B-YQq5b5.css +1 -0
  47. package/dist/deck-client/assets/{index-CB-qlwRT.js → index-BtQBaQ7s.js} +76 -76
  48. package/dist/deck-client/assets/{infoDiagram-42DDH7IO-CReN1nFN.js → infoDiagram-42DDH7IO-TylGlSG-.js} +1 -1
  49. package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-CDF_VLN_.js → ishikawaDiagram-UXIWVN3A-DAT8icpg.js} +1 -1
  50. package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-DwgGrNVB.js → journeyDiagram-VCZTEJTY-D3v_XL72.js} +1 -1
  51. package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-DB_zohh5.js → kanban-definition-6JOO6SKY-DNUOBiNr.js} +1 -1
  52. package/dist/deck-client/assets/{layout-DFfX1O3z.js → layout-COfodgwF.js} +1 -1
  53. package/dist/deck-client/assets/{linear-CtKb4EXj.js → linear-DmTsuIvK.js} +1 -1
  54. package/dist/deck-client/assets/{min-DCRRwUZv.js → min-BW1F7i1D.js} +1 -1
  55. package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-D0QBOiFe.js → mindmap-definition-QFDTVHPH-CErFzKWl.js} +1 -1
  56. package/dist/deck-client/assets/{pieDiagram-DEJITSTG-CD-EV5WB.js → pieDiagram-DEJITSTG-DW5F757o.js} +1 -1
  57. package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-B-JXZ8xI.js → quadrantDiagram-34T5L4WZ-B1S2-TfI.js} +1 -1
  58. package/dist/deck-client/assets/{requirementDiagram-MS252O5E-D2_OK5Dp.js → requirementDiagram-MS252O5E-BY5BAR-5.js} +1 -1
  59. package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-BbBJqVSC.js → sankeyDiagram-XADWPNL6-CE1Cp9HS.js} +1 -1
  60. package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-Db8A-Rkk.js → sequenceDiagram-FGHM5R23-IaHnbKye.js} +1 -1
  61. package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-DGJnanjS.js → stateDiagram-FHFEXIEX-CwPJm9hU.js} +1 -1
  62. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-DQYa2M1q.js +1 -0
  63. package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-BRkr6T4w.js → timeline-definition-GMOUNBTQ-DVFGGSgN.js} +1 -1
  64. package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-d0rsTqFo.js → vennDiagram-DHZGUBPP-C1194MJi.js} +1 -1
  65. package/dist/deck-client/assets/{wardley-RL74JXVD-2t7cMqdS.js → wardley-RL74JXVD-CHZiUbBa.js} +1 -1
  66. package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-DzboAsHh.js → wardleyDiagram-NUSXRM2D-hpwdFfGj.js} +1 -1
  67. package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-CgTP9u2V.js → xychartDiagram-5P7HB3ND-DYkotwy8.js} +1 -1
  68. package/dist/deck-client/index.html +2 -2
  69. package/dist/server/cli.js +91 -13
  70. package/dist/server/council-entry.js +0 -0
  71. package/dist/server/deck-mcp-entry.js +83 -40
  72. package/dist/server/deck-serve.js +54 -20
  73. package/dist/server/fb-wizard.js +0 -0
  74. package/dist/server/init-entry.js +952 -290
  75. package/dist/server/radar-docker-init-entry.js +239 -0
  76. package/dist/server/radar-entrypoint-entry.js +99 -0
  77. package/dist/server/radar-teardown-entry.js +477 -0
  78. package/dist/server/recall-entry.js +4 -1
  79. package/package.json +22 -23
  80. package/scaffolds/ls-marketplace/plugins/kit/commands/activate-statusline.md +5 -5
  81. package/scaffolds/ls-marketplace/plugins/kit/skills/diagram/SKILL.md +27 -5
  82. package/scaffolds/ls-marketplace/plugins/kit/skills/ship/SKILL.md +274 -0
  83. package/scaffolds/migrate-safety/scripts/migrate-with-backup.sh +0 -0
  84. package/scaffolds/recall-hook/scripts/ensure-recall.sh +0 -0
  85. package/scaffolds/statusline/statusline-mcp.sh +82 -2
  86. package/scaffolds/statusline/statusline-wrapper.sh +8 -1
  87. package/dist/chart-client/assets/index-CDIhdgWg.css +0 -1
  88. package/dist/client/assets/index-CfW4n40I.css +0 -32
  89. package/dist/council-client/assets/index-CZim6x1u.css +0 -1
  90. package/dist/deck-client/assets/channel-DmR7Tyyt.js +0 -1
  91. package/dist/deck-client/assets/classDiagram-6PBFFD2Q-Bl4ozQWs.js +0 -1
  92. package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-Bl4ozQWs.js +0 -1
  93. package/dist/deck-client/assets/clone-BAy58j24.js +0 -1
  94. package/dist/deck-client/assets/index-BlTlhxFW.css +0 -1
  95. package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-CR7riiab.js +0 -1
@@ -0,0 +1,477 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // src/server/radar-teardown-entry.ts
5
+ var import_node_child_process = require("node:child_process");
6
+ var import_node_fs = require("node:fs");
7
+ var import_node_path = require("node:path");
8
+
9
+ // src/server/recall/config.ts
10
+ var DEFAULT_CONFIG = {
11
+ debounce: 3e3,
12
+ ignore: [
13
+ "node_modules",
14
+ ".next",
15
+ "dist",
16
+ "build",
17
+ ".turbo",
18
+ ".git",
19
+ ".recall",
20
+ "coverage",
21
+ ".pnpm-store",
22
+ ".launchsecure/graphs",
23
+ ".claude/settings.local.json"
24
+ ],
25
+ retention: {
26
+ keepLast: 5e3,
27
+ maxAgeDays: 30
28
+ }
29
+ };
30
+
31
+ // src/server/radar/mcp.ts
32
+ var import_node_https = require("node:https");
33
+ var import_node_http = require("node:http");
34
+ var ProjectMcpClient = class {
35
+ constructor(opts) {
36
+ this.initialized = false;
37
+ this.callId = 1;
38
+ this.mcpUrl = new URL("/api/mcp/project", opts.serverUrl);
39
+ this.headers = {
40
+ "Content-Type": "application/json",
41
+ "Accept": "application/json, text/event-stream",
42
+ "Authorization": `Bearer ${opts.pat}`
43
+ };
44
+ if (opts.orgSlug) this.headers["X-Org-Slug"] = opts.orgSlug;
45
+ if (opts.projectSlug) this.headers["X-Project-Slug"] = opts.projectSlug;
46
+ }
47
+ async call(toolName, args) {
48
+ await this.ensureInitialized();
49
+ const body = JSON.stringify({
50
+ jsonrpc: "2.0",
51
+ id: this.callId++,
52
+ method: "tools/call",
53
+ params: { name: toolName, arguments: args }
54
+ });
55
+ const resp = await this.send(body);
56
+ const parsed = parseBody(resp.body);
57
+ if (parsed.error) {
58
+ throw new Error(`MCP ${toolName} failed: ${parsed.error.message ?? JSON.stringify(parsed.error)}`);
59
+ }
60
+ const text = parsed.result?.content?.[0]?.text;
61
+ if (!text) return parsed.result;
62
+ if (text.startsWith("\u2500\u2500 Error")) {
63
+ const lines = text.split("\n").map((l) => l.trim()).filter(Boolean);
64
+ const reason = lines.find((l) => !l.startsWith("\u2500\u2500")) ?? text;
65
+ throw new Error(`MCP ${toolName} failed: ${reason}`);
66
+ }
67
+ try {
68
+ return JSON.parse(text);
69
+ } catch {
70
+ return text;
71
+ }
72
+ }
73
+ async ensureInitialized() {
74
+ if (this.initialized) return;
75
+ const initBody = JSON.stringify({
76
+ jsonrpc: "2.0",
77
+ id: this.callId++,
78
+ method: "initialize",
79
+ params: {
80
+ protocolVersion: "2025-03-26",
81
+ capabilities: {},
82
+ clientInfo: { name: "launchpod-feedback-agent", version: "0.0.1" }
83
+ }
84
+ });
85
+ const initResp = await this.send(initBody);
86
+ if (initResp.sessionId) this.sessionId = initResp.sessionId;
87
+ const notifBody = JSON.stringify({ jsonrpc: "2.0", method: "notifications/initialized" });
88
+ await this.send(notifBody);
89
+ this.initialized = true;
90
+ }
91
+ send(body) {
92
+ return new Promise((resolve2, reject) => {
93
+ const headers = {
94
+ ...this.headers,
95
+ "Content-Length": String(Buffer.byteLength(body))
96
+ };
97
+ if (this.sessionId) headers["Mcp-Session-Id"] = this.sessionId;
98
+ const requester = this.mcpUrl.protocol === "https:" ? import_node_https.request : import_node_http.request;
99
+ const req = requester(
100
+ {
101
+ host: this.mcpUrl.hostname,
102
+ port: this.mcpUrl.port || (this.mcpUrl.protocol === "https:" ? 443 : 80),
103
+ path: this.mcpUrl.pathname + this.mcpUrl.search,
104
+ method: "POST",
105
+ headers
106
+ },
107
+ (res) => {
108
+ const chunks = [];
109
+ res.on("data", (c) => chunks.push(c));
110
+ res.on("end", () => {
111
+ const text = Buffer.concat(chunks).toString("utf-8");
112
+ if (res.statusCode && res.statusCode >= 400) {
113
+ reject(new Error(`MCP HTTP ${res.statusCode}: ${text}`));
114
+ return;
115
+ }
116
+ const sid = res.headers["mcp-session-id"];
117
+ resolve2({ body: text, sessionId: typeof sid === "string" ? sid : void 0 });
118
+ });
119
+ }
120
+ );
121
+ req.on("error", reject);
122
+ req.write(body);
123
+ req.end();
124
+ });
125
+ }
126
+ };
127
+ function parseBody(text) {
128
+ if (!text) return {};
129
+ if (text.startsWith("event:") || text.includes("\ndata: ")) {
130
+ for (const line of text.split("\n")) {
131
+ if (line.startsWith("data: ")) {
132
+ try {
133
+ return JSON.parse(line.slice(6));
134
+ } catch {
135
+ }
136
+ }
137
+ }
138
+ }
139
+ try {
140
+ return JSON.parse(text);
141
+ } catch {
142
+ return {};
143
+ }
144
+ }
145
+
146
+ // src/server/radar-teardown-entry.ts
147
+ var COMPOSE_FILE = (0, import_node_path.resolve)(process.cwd(), "docker-compose.yml");
148
+ var RADAR_DIR = (0, import_node_path.dirname)(COMPOSE_FILE);
149
+ var COMPOSE_BASE = ["compose", "-f", COMPOSE_FILE];
150
+ var ENV_PATH = (0, import_node_path.join)(RADAR_DIR, ".env");
151
+ function ensureRadarCompose() {
152
+ if (!(0, import_node_fs.existsSync)(COMPOSE_FILE)) {
153
+ console.error(`[teardown] aborting \u2014 no docker-compose.yml at ${COMPOSE_FILE}`);
154
+ console.error(`[teardown] run from packages/cli/docker/radar/ (the directory holding the radar compose).`);
155
+ process.exit(1);
156
+ }
157
+ const text = (0, import_node_fs.readFileSync)(COMPOSE_FILE, "utf-8");
158
+ const hasServices = /(^|\n)services:\s*(\n|$)/.test(text);
159
+ const hasRadar = /\n[ \t]+radar:/.test(text);
160
+ if (!hasServices || !hasRadar) {
161
+ console.error(`[teardown] aborting \u2014 ${COMPOSE_FILE} does not define a 'radar' service.`);
162
+ console.error(`[teardown] this command is only for the launch-pod radar container; refusing to touch this compose project.`);
163
+ process.exit(1);
164
+ }
165
+ }
166
+ function parseArgs(argv) {
167
+ const out = { force: false, removeEnv: false, removeImage: false, help: false };
168
+ for (const a of argv) {
169
+ switch (a) {
170
+ case "--force":
171
+ out.force = true;
172
+ break;
173
+ case "--remove-env":
174
+ out.removeEnv = true;
175
+ break;
176
+ case "--remove-image":
177
+ out.removeImage = true;
178
+ break;
179
+ case "-h":
180
+ case "--help":
181
+ out.help = true;
182
+ break;
183
+ default:
184
+ console.error(`unknown flag: ${a}`);
185
+ process.exit(1);
186
+ }
187
+ }
188
+ return out;
189
+ }
190
+ function printHelp() {
191
+ console.log("usage: launch-pod radar:teardown [--remove-env] [--remove-image] [--force]");
192
+ console.log("");
193
+ console.log(" --remove-env also delete .env (default: keep \u2014 contains live creds)");
194
+ console.log(" --remove-image also remove launchpod-radar:local image");
195
+ console.log(" --force skip the workspace-safety preflight (discards");
196
+ console.log(" uncommitted changes / unpushed commits / in-flight");
197
+ console.log(" analyzer sessions without warning)");
198
+ }
199
+ function sh(cmd, args, opts = {}) {
200
+ const r = (0, import_node_child_process.spawnSync)(cmd, args, { encoding: "utf8", ...opts });
201
+ return {
202
+ status: r.status ?? 1,
203
+ stdout: typeof r.stdout === "string" ? r.stdout : "",
204
+ stderr: typeof r.stderr === "string" ? r.stderr : ""
205
+ };
206
+ }
207
+ function workspaceSh(command) {
208
+ const psQ = sh("docker", [...COMPOSE_BASE, "ps", "-q", "radar"]);
209
+ if (psQ.status === 0 && psQ.stdout.trim()) {
210
+ return sh("docker", [...COMPOSE_BASE, "exec", "-T", "radar", "sh", "-c", command]).stdout;
211
+ }
212
+ return sh("docker", [...COMPOSE_BASE, "run", "--rm", "--no-deps", "--entrypoint", "sh", "radar", "-c", command]).stdout;
213
+ }
214
+ function loadIgnoreSegs() {
215
+ const json = workspaceSh("cat /workspace/.recall/config.json 2>/dev/null");
216
+ if (json.trim()) {
217
+ try {
218
+ const j = JSON.parse(json);
219
+ if (Array.isArray(j.ignore) && j.ignore.every((s) => typeof s === "string")) {
220
+ return j.ignore;
221
+ }
222
+ } catch {
223
+ }
224
+ }
225
+ return [...DEFAULT_CONFIG.ignore];
226
+ }
227
+ function pathIsNoise(p, segs) {
228
+ for (const seg of segs) {
229
+ if (p === seg) return true;
230
+ if (p.startsWith(seg + "/")) return true;
231
+ if (p.endsWith("/" + seg)) return true;
232
+ if (p.includes("/" + seg + "/")) return true;
233
+ }
234
+ return false;
235
+ }
236
+ var SCAFFOLDED_MCP_PATHS = [
237
+ "mcpServers.launch-secure.url"
238
+ ];
239
+ function collectDiffPaths(a, b, prefix, out) {
240
+ if (a === b) return;
241
+ const aIsPrim = a === null || typeof a !== "object";
242
+ const bIsPrim = b === null || typeof b !== "object";
243
+ if (aIsPrim || bIsPrim) {
244
+ if (a !== b) out.push(prefix || "$");
245
+ return;
246
+ }
247
+ const aIsArr = Array.isArray(a);
248
+ const bIsArr = Array.isArray(b);
249
+ if (aIsArr || bIsArr) {
250
+ if (!aIsArr || !bIsArr || a.length !== b.length) {
251
+ out.push(prefix || "$");
252
+ return;
253
+ }
254
+ const aa = a;
255
+ const bb = b;
256
+ for (let i = 0; i < aa.length; i++) {
257
+ collectDiffPaths(aa[i], bb[i], prefix ? `${prefix}[${i}]` : `[${i}]`, out);
258
+ }
259
+ return;
260
+ }
261
+ const ao = a;
262
+ const bo = b;
263
+ for (const k of /* @__PURE__ */ new Set([...Object.keys(ao), ...Object.keys(bo)])) {
264
+ const child = prefix ? `${prefix}.${k}` : k;
265
+ if (!(k in ao) || !(k in bo)) {
266
+ out.push(child);
267
+ continue;
268
+ }
269
+ collectDiffPaths(ao[k], bo[k], child, out);
270
+ }
271
+ }
272
+ function mcpJsonOnlyScaffoldedDiff() {
273
+ const headJson = workspaceSh("git -C /workspace show HEAD:.mcp.json 2>/dev/null");
274
+ const wtJson = workspaceSh("cat /workspace/.mcp.json 2>/dev/null");
275
+ if (!headJson.trim() || !wtJson.trim()) return false;
276
+ let head, wt;
277
+ try {
278
+ head = JSON.parse(headJson);
279
+ wt = JSON.parse(wtJson);
280
+ } catch {
281
+ return false;
282
+ }
283
+ const diffs = [];
284
+ collectDiffPaths(head, wt, "", diffs);
285
+ if (diffs.length === 0) return false;
286
+ return diffs.every((d) => SCAFFOLDED_MCP_PATHS.some((s) => d === s || d.startsWith(s + ".")));
287
+ }
288
+ function filterStatusSignal(raw, modeOnly, segs, scaffoldClean) {
289
+ const out = [];
290
+ for (const rawLine of raw.split("\n")) {
291
+ const line = rawLine.replace(/\r$/, "");
292
+ if (line.length < 3) continue;
293
+ let p = line.slice(3);
294
+ if (line.startsWith("R") || line.startsWith("C")) {
295
+ const arrow = p.indexOf(" -> ");
296
+ if (arrow >= 0) p = p.slice(arrow + 4);
297
+ }
298
+ if (pathIsNoise(p, segs)) continue;
299
+ if (modeOnly.has(p)) continue;
300
+ if (scaffoldClean.has(p)) continue;
301
+ out.push(line);
302
+ }
303
+ return out;
304
+ }
305
+ function probeDocker() {
306
+ const containerRunning = sh("docker", [...COMPOSE_BASE, "ps", "-q", "radar"]).stdout.trim().length > 0;
307
+ const projectName = (sh("docker", [...COMPOSE_BASE, "config", "--format", "json"]).stdout.match(/"name"\s*:\s*"([^"]+)"/) ?? [])[1] ?? "";
308
+ let workspaceVolumeExists = false;
309
+ if (projectName) {
310
+ const vols = sh("docker", ["volume", "ls", "-q", "--filter", `name=^${projectName}_workspace$`]);
311
+ workspaceVolumeExists = vols.status === 0 && vols.stdout.trim().length > 0;
312
+ }
313
+ return { containerRunning, workspaceVolumeExists };
314
+ }
315
+ function preflightWorkspace(args) {
316
+ if (args.force) {
317
+ console.log("[teardown] --force given \u2014 skipping workspace-safety preflight");
318
+ return;
319
+ }
320
+ const state = probeDocker();
321
+ if (!state.containerRunning && !state.workspaceVolumeExists) {
322
+ console.log("[teardown] no radar container or workspace volume present \u2014 nothing to check");
323
+ return;
324
+ }
325
+ console.log("[teardown] checking workspace for unsaved work\u2026");
326
+ const segs = loadIgnoreSegs();
327
+ const numstatRaw = workspaceSh("git -C /workspace diff --numstat 2>/dev/null");
328
+ const modeOnly = /* @__PURE__ */ new Set();
329
+ for (const line of numstatRaw.split("\n")) {
330
+ const parts = line.split(" ");
331
+ if (parts.length >= 3 && parts[0] === "0" && parts[1] === "0") {
332
+ modeOnly.add(parts[2]);
333
+ }
334
+ }
335
+ const rawDirty = workspaceSh("git -C /workspace status --porcelain 2>/dev/null | head -200");
336
+ const scaffoldClean = /* @__PURE__ */ new Set();
337
+ if (mcpJsonOnlyScaffoldedDiff()) scaffoldClean.add(".mcp.json");
338
+ const dirty = filterStatusSignal(rawDirty, modeOnly, segs, scaffoldClean);
339
+ const unpushedRaw = workspaceSh("git -C /workspace rev-list --count @{u}..HEAD 2>/dev/null || echo 0").trim();
340
+ const unpushed = parseInt(unpushedRaw || "0", 10);
341
+ const inflightRaw = workspaceSh(`grep -oE '"state":[[:space:]]*"(analyzing|queued)"' /workspace/.launchpod/radar-pings.json 2>/dev/null | wc -l | tr -d ' '`).trim();
342
+ const inflight = parseInt(inflightRaw || "0", 10);
343
+ let blocked = false;
344
+ if (dirty.length > 0) {
345
+ console.log("[teardown] \u2717 uncommitted changes in /workspace (after recall-ignore filter):");
346
+ for (const line of dirty) console.log(" " + line);
347
+ blocked = true;
348
+ }
349
+ if (unpushed > 0) {
350
+ console.log(`[teardown] \u2717 ${unpushed} unpushed commit(s) ahead of upstream`);
351
+ blocked = true;
352
+ }
353
+ if (inflight > 0) {
354
+ console.log(`[teardown] \u2717 ${inflight} in-flight or queued analyzer session(s) \u2014 output will be lost`);
355
+ blocked = true;
356
+ }
357
+ if (blocked) {
358
+ console.log("");
359
+ console.log("[teardown] aborting \u2014 workspace has unsaved work.");
360
+ console.log(" \u2022 commit + push from inside the container (docker compose exec radar sh), then re-run, OR");
361
+ console.log(" \u2022 re-run with --force to discard the work and tear down anyway.");
362
+ process.exit(2);
363
+ }
364
+ console.log("[teardown] \u2713 workspace is clean (only recall-ignored noise present) \u2014 safe to tear down");
365
+ }
366
+ function parseEnvFile(path) {
367
+ const env = {};
368
+ for (const raw of (0, import_node_fs.readFileSync)(path, "utf8").split("\n")) {
369
+ const line = raw.trim();
370
+ if (!line || line.startsWith("#")) continue;
371
+ const eq = line.indexOf("=");
372
+ if (eq < 0) continue;
373
+ const key = line.slice(0, eq).trim();
374
+ let value = line.slice(eq + 1).trim();
375
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
376
+ value = value.slice(1, -1);
377
+ }
378
+ if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) env[key] = value;
379
+ }
380
+ return env;
381
+ }
382
+ async function releaseWebhook() {
383
+ const psQ = sh("docker", [...COMPOSE_BASE, "ps", "-q", "radar"]);
384
+ if (psQ.status !== 0 || !psQ.stdout.trim()) {
385
+ console.log("[teardown] container not running \u2014 skipping webhook release");
386
+ console.log("[teardown] (any prior registration is now orphaned \u2014 clean up via cloud LS settings UI)");
387
+ return;
388
+ }
389
+ const radarJson = workspaceSh("cat /workspace/.launchpod/radar.json 2>/dev/null");
390
+ let endpointId = "";
391
+ try {
392
+ const j = JSON.parse(radarJson);
393
+ endpointId = j.endpointId ?? j.registration?.endpointId ?? j.webhook?.endpointId ?? "";
394
+ } catch {
395
+ }
396
+ if (!endpointId) {
397
+ console.log("[teardown] no endpoint_id found in /workspace/.launchpod/radar.json \u2014 skipping release");
398
+ return;
399
+ }
400
+ if (!(0, import_node_fs.existsSync)(ENV_PATH)) {
401
+ console.log(`[teardown] no .env found at ${ENV_PATH} \u2014 can't release webhook (manual cleanup needed)`);
402
+ return;
403
+ }
404
+ const env = parseEnvFile(ENV_PATH);
405
+ const pat = env.LS_PAT;
406
+ const orgSlug = env.LS_ORG_SLUG;
407
+ const projectSlug = env.LS_PROJECT_SLUG;
408
+ const serverUrl = env.LS_SERVER_URL || "https://launchsecure-v2.vercel.app";
409
+ if (!pat || !orgSlug || !projectSlug) {
410
+ console.log("[teardown] .env is missing LS_PAT / LS_ORG_SLUG / LS_PROJECT_SLUG \u2014 skipping release");
411
+ return;
412
+ }
413
+ console.log(`[teardown] releasing endpoint ${endpointId} from ${serverUrl}`);
414
+ try {
415
+ const mcp = new ProjectMcpClient({ serverUrl, pat, orgSlug, projectSlug });
416
+ await mcp.call("launchpod_webhook_release", { endpoint_id: endpointId });
417
+ console.log("[teardown] \u2713 webhook released");
418
+ } catch (err) {
419
+ const msg = err instanceof Error ? err.message : String(err);
420
+ console.log(`[teardown] \u26A0 webhook release failed (${msg}) \u2014 delete manually at:`);
421
+ console.log(` ${serverUrl}/${orgSlug}/projects/${projectSlug}/settings`);
422
+ }
423
+ }
424
+ function dockerComposeDown() {
425
+ console.log(`[teardown] docker compose -f ${COMPOSE_FILE} down -v`);
426
+ const down = (0, import_node_child_process.spawnSync)("docker", [...COMPOSE_BASE, "down", "-v"], { encoding: "utf8" });
427
+ const combined = (down.stdout || "") + (down.stderr || "");
428
+ for (const line of combined.split("\n")) {
429
+ if (/Removing|Removed|Stopping|Stopped/.test(line)) console.log(" " + line.trim());
430
+ }
431
+ if (down.status !== 0) {
432
+ console.error(`[teardown] docker compose down -v failed (status ${down.status})`);
433
+ process.exit(down.status ?? 1);
434
+ }
435
+ }
436
+ function cleanupLocalArtifacts(args) {
437
+ if (args.removeImage) {
438
+ console.log("[teardown] removing image launchpod-radar:local");
439
+ const rmi = sh("docker", ["rmi", "launchpod-radar:local"]);
440
+ console.log(rmi.status === 0 ? " removed" : " not present");
441
+ }
442
+ for (const f of (0, import_node_fs.readdirSync)(RADAR_DIR)) {
443
+ if (f.startsWith("launchsecure-launch-kit-") && f.endsWith(".tgz")) {
444
+ console.log("[teardown] removing launch-kit tarball(s)");
445
+ try {
446
+ (0, import_node_fs.unlinkSync)((0, import_node_path.join)(RADAR_DIR, f));
447
+ } catch {
448
+ }
449
+ }
450
+ }
451
+ if (args.removeEnv && (0, import_node_fs.existsSync)(ENV_PATH)) {
452
+ console.log("[teardown] removing .env (--remove-env)");
453
+ try {
454
+ (0, import_node_fs.unlinkSync)(ENV_PATH);
455
+ } catch {
456
+ }
457
+ } else if ((0, import_node_fs.existsSync)(ENV_PATH)) {
458
+ console.log("[teardown] keeping .env (use --remove-env to delete it next time)");
459
+ }
460
+ }
461
+ async function main() {
462
+ const args = parseArgs(process.argv.slice(2));
463
+ if (args.help) {
464
+ printHelp();
465
+ return;
466
+ }
467
+ ensureRadarCompose();
468
+ preflightWorkspace(args);
469
+ await releaseWebhook();
470
+ dockerComposeDown();
471
+ cleanupLocalArtifacts(args);
472
+ console.log("[teardown] done.");
473
+ }
474
+ main().catch((err) => {
475
+ console.error("[teardown] fatal:", err);
476
+ process.exit(1);
477
+ });
@@ -167,7 +167,10 @@ var init_config = __esm({
167
167
  ".turbo",
168
168
  ".git",
169
169
  ".recall",
170
- "coverage"
170
+ "coverage",
171
+ ".pnpm-store",
172
+ ".launchsecure/graphs",
173
+ ".claude/settings.local.json"
171
174
  ],
172
175
  retention: {
173
176
  keepLast: 5e3,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@launchsecure/launch-kit",
3
- "version": "0.0.31",
3
+ "version": "0.0.33",
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",
@@ -56,24 +56,6 @@
56
56
  "launch-course": "./dist/server/course-entry.js",
57
57
  "launch-beacon": "./dist/server/beacon-monitor-entry.js"
58
58
  },
59
- "scripts": {
60
- "build": "pnpm build:client && pnpm build:chart-client && pnpm build:deck-client && pnpm build:council-client && pnpm build:beacon && pnpm build:server",
61
- "build:beacon": "vite build --config vite.beacon.config.ts && tsc -p tsconfig.beacon.json --emitDeclarationOnly --outDir dist/beacon/types",
62
- "test:beacon": "vitest run --config vite.beacon.config.ts",
63
- "test:radar": "vitest run --config vite.radar.config.ts",
64
- "test:chart": "vitest run --config vite.chart.test.config.ts",
65
- "build:deck-client": "vite build --config vite.deck.config.ts",
66
- "build:council-client": "vite build --config vite.council.config.ts",
67
- "build:client": "vite build",
68
- "build:chart-client": "vite build --config vite.chart.config.ts",
69
- "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 --external:pgsql-parser --external:libpg-query && 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",
70
- "dev:client": "vite",
71
- "dev:deck-serve": "cd ../.. && tsx watch packages/cli/src/server/deck-mcp-entry.ts serve",
72
- "dev:chart": "pnpm build:server && pnpm build:chart-client && node dist/server/graph-mcp-entry.js serve",
73
- "dev:server": "pnpm build:server && node dist/server/cli.js",
74
- "dev": "pnpm build:server && concurrently -k -n client,server -c cyan,magenta \"vite\" \"node dist/server/cli.js\"",
75
- "prepublishOnly": "pnpm build"
76
- },
77
59
  "files": [
78
60
  "dist",
79
61
  "prompts",
@@ -94,8 +76,6 @@
94
76
  "ws": "^8.18.0"
95
77
  },
96
78
  "devDependencies": {
97
- "@launchsecure/claude-code-web": "workspace:*",
98
- "@launchsecure/ui": "workspace:*",
99
79
  "@types/node": "^20.0.0",
100
80
  "@types/pg": "^8.11.10",
101
81
  "@types/react": "^18.3.12",
@@ -119,6 +99,25 @@
119
99
  "react-router-dom": "^6.28.0",
120
100
  "tailwindcss": "^3.4.19",
121
101
  "vite": "^5.4.11",
122
- "vitest": "^1.6.0"
102
+ "vitest": "^1.6.0",
103
+ "@launchsecure/claude-code-web": "0.0.1",
104
+ "@launchsecure/ui": "0.0.1"
105
+ },
106
+ "scripts": {
107
+ "build": "pnpm build:client && pnpm build:chart-client && pnpm build:deck-client && pnpm build:council-client && pnpm build:beacon && pnpm build:server",
108
+ "build:beacon": "vite build --config vite.beacon.config.ts && tsc -p tsconfig.beacon.json --emitDeclarationOnly --outDir dist/beacon/types",
109
+ "test:beacon": "vitest run --config vite.beacon.config.ts",
110
+ "test:radar": "vitest run --config vite.radar.config.ts",
111
+ "test:chart": "vitest run --config vite.chart.test.config.ts",
112
+ "build:deck-client": "vite build --config vite.deck.config.ts",
113
+ "build:council-client": "vite build --config vite.council.config.ts",
114
+ "build:client": "vite build",
115
+ "build:chart-client": "vite build --config vite.chart.config.ts",
116
+ "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 src/server/radar-teardown-entry.ts src/server/radar-docker-init-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 --external:pgsql-parser --external:libpg-query && 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",
117
+ "dev:client": "vite",
118
+ "dev:deck-serve": "cd ../.. && tsx watch packages/cli/src/server/deck-mcp-entry.ts serve",
119
+ "dev:chart": "pnpm build:server && pnpm build:chart-client && node dist/server/graph-mcp-entry.js serve",
120
+ "dev:server": "pnpm build:server && node dist/server/cli.js",
121
+ "dev": "pnpm build:server && concurrently -k -n client,server -c cyan,magenta \"vite\" \"node dist/server/cli.js\""
123
122
  }
124
- }
123
+ }
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Wire launch-kit's MCP daemon chips into the user's existing Claude Code statusline. Additive — does not create or replace an existing statusline, only appends colored chips (recall/chart/deck/council) showing each daemon's liveness + last-activity age. Idempotent. Run /kit:deactivate-statusline to undo.
2
+ description: Wire launch-kit's MCP chips into the user's existing Claude Code statusline. Additive — does not create or replace an existing statusline, only appends colored chips (recall/chart/deck/council/secure) showing each daemon's liveness + last-activity age, plus a reachability probe for the hosted launch-secure MCP. Idempotent. Run /kit:deactivate-statusline to undo.
3
3
  ---
4
4
 
5
5
  # /kit:activate-statusline
@@ -21,12 +21,12 @@ Extends the user's existing `~/.claude/settings.json` statusline so MCP daemon h
21
21
  Shell out to the kit binary:
22
22
 
23
23
  ```bash
24
- launch-kit statusline activate # all chips
25
- launch-kit statusline activate --show=recall,chart # only these
26
- launch-kit statusline activate --show=recall,chart,deck,council # explicit all
24
+ launch-kit statusline activate # all chips
25
+ launch-kit statusline activate --show=recall,chart # only these
26
+ launch-kit statusline activate --show=recall,chart,deck,council,secure # explicit all
27
27
  ```
28
28
 
29
- The `--show` flag accepts any comma-separated subset of `recall,chart,deck,council`. Omitting it shows all four. Re-running activate with a different `--show` updates the chip set in place — no need to deactivate first.
29
+ The `--show` flag accepts any comma-separated subset of `recall,chart,deck,council,secure`. Omitting it shows all five. `secure` is a reachability probe (curl, cached ~30s) against the active LaunchSecure profile's `serverUrl` — green when the host responds, red on transport failure, orange when the cred config or curl is missing. The chip label shows the active profile (e.g. `secure(prod)`). Re-running activate with a different `--show` updates the chip set in place — no need to deactivate first.
30
30
 
31
31
  If the published bin isn't on PATH (dev repo, monorepo), fall back to:
32
32
 
@@ -31,7 +31,7 @@ Parse `$ARGUMENTS`:
31
31
  - **--scope=<filter>** — for ERDs, limit to one module or table set (e.g. `--scope=auth` or `--scope=User,Org,Membership`). Default: all tables.
32
32
  - **--session=<id>** — deck session name. Default `diagram-<kind>-<subject-slug>`.
33
33
  - **--dsl=<raw-mermaid>** — bypass auto-generation; push the user's DSL verbatim.
34
- - **--feedback** — push in feedback mode and await response.
34
+ - **--no-feedback** — skip feedback round-trip (rare; only when the caller has already validated the DSL).
35
35
 
36
36
  Examples:
37
37
  - `/kit:diagram flowchart user login flow`
@@ -97,18 +97,38 @@ Rules:
97
97
  - `read_graph(layer: "db")` returns 0 nodes
98
98
  - the chart shows tables but `inspect_node` returns no `columns` (parser misconfiguration)
99
99
 
100
- ## Push to deck
100
+ ## Push to deck — feedback mode is the default
101
+
102
+ **Always use `mode: "feedback"` for diagrams.** Mermaid DSL frequently fails on the first attempt (syntax pitfalls below), and the deck server's auto-error poll is racy (1500ms timeout in `deck-mcp.ts`). Feedback mode gives you two backstops: a synchronous render-error capture *and* explicit user confirmation. Skip only when `--no-feedback` was passed.
101
103
 
102
104
  Call `mcp__launch-deck__deck` with:
103
105
  - `session: <session>`
104
- - `mode: "show"` or `"feedback"` (+ `prompt: "Does this match the schema you expected?"` for ERDs)
106
+ - `mode: "feedback"`
107
+ - `prompt: "Diagram render OK? Reply 'ok' to accept, or paste the error / describe what's wrong."` (ERDs: append "Does this match the schema you expected?")
105
108
  - `blocks: [{ type: "mermaid", label: <kind>-<subject>, content: <mermaid-dsl> }]`
106
109
 
107
- If `--feedback`, call `await_feedback` and surface the response.
110
+ ### Render-error retry loop
111
+
112
+ The deck MCP catches mermaid parse/render errors via the browser's `onMermaidError` → `/api/render-errors` channel and surfaces them on the `deck` tool response. Handle both signal paths:
113
+
114
+ 1. **Synchronous render error** — if the `deck` response is `{ok: false, mermaid_error, failed_source, ...}`, inspect `failed_source` first:
115
+ - If `failed_source` matches the DSL you just pushed → real error. Fix the DSL based on `mermaid_error` and re-push immediately (same session — the tab is already open). Cap at 2 auto-retries before surfacing to the user.
116
+ - If `failed_source` does NOT match what you just pushed → **stale error**. The deck server's `lastRenderError` is a module-level pop register (`consumeRenderError` in `deck-serve.ts`) populated via WebSocket from the browser; if the previous push's render error arrived AFTER its 1500ms MCP poll, it leaks into this push's response. Ignore it and proceed to `await_feedback`.
117
+ 2. **User feedback** — if `deck` returned `ok: true`, call `mcp__launch-deck__await_feedback({ session })`. If the reply is positive ("ok", "looks good", thumbs up), you're done. Otherwise treat the reply as a correction (syntax error pasted, "missing X", "wrong direction") and regenerate. Cap at 2 user-feedback retries before stopping and asking for a clearer instruction.
118
+
119
+ ### Common Mermaid pitfalls (avoid on first attempt)
120
+
121
+ - **No `<br/>` in participant aliases.** `participant Pod as LaunchPod CLI<br/>(agent)` breaks the parser in many Mermaid builds. Use one-line names or a trailing parenthetical.
122
+ - **Multi-line notes are fragile.** `Note over X: line1<br/>line2` is risky inside `rect` blocks. Keep notes one-line, or chain two `Note` statements.
123
+ - **Multiple `else` branches.** `alt … else … else … else … end` works in current Mermaid but failure modes are noisy; prefer 2-branch alts or split into separate alt blocks.
124
+ - **`rect rgb(...)`** uses **space-separated** values, not commas: `rect rgb(230 240 255)` — not `rect rgb(230,240,255)`. Comma form errors silently in some builds.
125
+ - **Special chars in labels** — wrap in double quotes when using `:`, `(`, `,`, or unicode arrows: `A->>B: "step (1): start"`.
126
+ - **Reserved keywords as actor names** — avoid `end`, `note`, `loop`, `alt`, `opt` as participant ids.
127
+ - **ERD column types** must be one word: `string`, `int`, `datetime`, `enum`. No spaces, no parens.
108
128
 
109
129
  ## Open in browser
110
130
 
111
- After the push (and after `await_feedback` if `--feedback`), open the per-session URL so the user lands directly on this diagram:
131
+ After the final successful push (and after `await_feedback` returns positive), open the per-session URL so the user lands directly on this diagram:
112
132
 
113
133
  ```
114
134
  open "<deck-url>/#<session>" # macOS
@@ -150,3 +170,5 @@ Then fall back: render a `flowchart` placeholder explaining "DB layer not availa
150
170
  - **Cap entities at 25.** Past that, Mermaid wraps unreadably; ask the user to narrow scope.
151
171
  - **Mermaid only.** This skill does not draw raster images — Mermaid DSL is the deliverable.
152
172
  - **One block per push.**
173
+ - **Feedback mode by default.** Never push a diagram with `mode: "show"` unless `--no-feedback` was explicitly passed. Diagrams are validated artifacts, not fire-and-forget output.
174
+ - **Retry caps.** 2 auto-retries on synchronous render error + 2 user-feedback retries. Beyond that, hand the broken DSL back to the user with the last error message and stop.