@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,609 @@
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 __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/server/init-entry.ts
27
+ var import_node_child_process = require("node:child_process");
28
+ var fs = __toESM(require("node:fs"));
29
+ var import_node_http = require("node:http");
30
+ var import_node_https = require("node:https");
31
+ var path = __toESM(require("node:path"));
32
+ var readline = __toESM(require("node:readline"));
33
+ var import_node_url = require("node:url");
34
+ var DEFAULT_SERVER_URL = "https://launchsecure-v2.vercel.app";
35
+ var CONFIG_FILENAME = ".launch-secure.cred.config";
36
+ var LEGACY_CONFIG_FILENAME = ".launch-secure.config";
37
+ var ONBOARD_SCRIPT_NAME = "onboard";
38
+ var LAUNCH_KIT_PKG = "@launchsecure/launch-kit";
39
+ var LAUNCH_KIT_TOOLS_GUIDE = `
40
+ Wired in Claude Code (.mcp.json):
41
+ launch-secure \u2014 LS API: work items, comms, secrets, members, board
42
+ launch-chart \u2014 code search + project graph (use instead of grep/glob)
43
+ launch-deck \u2014 visual playground / blast-radius diagrams
44
+ launch-orbit \u2014 git worktree orchestration (branch / merge gates)
45
+ launch-recall \u2014 restore deleted/modified files from shadow git
46
+
47
+ Other tools (run on demand via npx):
48
+ npx launch-pod radar \u2014 webhook listener (LS pings \u2192 terminal/UI)
49
+ npx launch-pod \u2014 full pipeline UI (separate launch-pod login)
50
+ `;
51
+ var PACKAGE_MANAGERS = [
52
+ { name: "pnpm", binary: "pnpm", lockfiles: ["pnpm-lock.yaml"], workspaceFiles: ["pnpm-workspace.yaml"], installArgs: ["install"] },
53
+ { name: "yarn", binary: "yarn", lockfiles: ["yarn.lock"], installArgs: ["install"] },
54
+ { name: "bun", binary: "bun", lockfiles: ["bun.lockb", "bun.lock"], installArgs: ["install"] },
55
+ { name: "npm", binary: "npm", lockfiles: ["package-lock.json"], installArgs: ["install"] }
56
+ ];
57
+ function parseArgs(argv) {
58
+ const args = {
59
+ token: process.env.LS_PAT ?? null,
60
+ orgSlug: null,
61
+ projectSlug: null,
62
+ serverUrl: DEFAULT_SERVER_URL,
63
+ targetDir: null,
64
+ noInstall: false,
65
+ noRecall: false,
66
+ noMigrateSafety: false,
67
+ help: false
68
+ };
69
+ for (const raw of argv) {
70
+ if (raw === "--help" || raw === "-h") {
71
+ args.help = true;
72
+ continue;
73
+ }
74
+ if (raw === "--no-install") {
75
+ args.noInstall = true;
76
+ continue;
77
+ }
78
+ if (raw === "--no-recall") {
79
+ args.noRecall = true;
80
+ continue;
81
+ }
82
+ if (raw === "--no-migrate-safety") {
83
+ args.noMigrateSafety = true;
84
+ continue;
85
+ }
86
+ const eq = raw.indexOf("=");
87
+ if (!raw.startsWith("--") || eq < 0) continue;
88
+ const key = raw.slice(2, eq);
89
+ const val = raw.slice(eq + 1);
90
+ if (key === "token") args.token = val;
91
+ else if (key === "org") args.orgSlug = val;
92
+ else if (key === "project") args.projectSlug = val;
93
+ else if (key === "url") args.serverUrl = val.replace(/\/+$/, "");
94
+ else if (key === "dir") args.targetDir = val;
95
+ }
96
+ return args;
97
+ }
98
+ function printHelp() {
99
+ console.log(`launch-kit init \u2014 bootstrap a LaunchSecure project on this machine
100
+
101
+ Usage:
102
+ npx launch-kit init --token=<pat> --org=<orgSlug> --project=<projectSlug> [options]
103
+
104
+ Required:
105
+ --token=<pat> LaunchSecure PAT (ls_pat_...). Or set LS_PAT env var.
106
+ --org=<orgSlug> Organization slug.
107
+ --project=<projectSlug> Project slug.
108
+
109
+ Options:
110
+ --url=<serverUrl> LaunchSecure base URL (default: ${DEFAULT_SERVER_URL}).
111
+ --dir=<path> Target directory (default: ./<projectSlug>).
112
+ --no-install Skip dependency install step.
113
+ --no-recall Skip launch-recall (shadow git backup) scaffold.
114
+ --no-migrate-safety Skip migrate-safety scaffold (pg_dump-before-migrate
115
+ wrapper + GitHub Action + runbook).
116
+ --help Show this help.
117
+
118
+ What it does:
119
+ 1. Preflight: checks git + node + (optional) gh.
120
+ 2. Calls LaunchSecure to resolve the project + its git remote.
121
+ 3. Clones the repo (prefers gh, falls back to git) \u2014 skipped if dir already
122
+ contains the matching clone.
123
+ 4. Writes .launch-secure.cred.config (gitignored). Auto-migrates any
124
+ legacy .launch-secure.config that contains a PAT.
125
+ 5. Merges .mcp.json with 5 entries (launch-secure / -chart / -deck / -orbit /
126
+ -recall). Preserves your other MCP entries. Auth via headersHelper \u2014
127
+ no secrets written into .mcp.json.
128
+ 6. Detects package manager (packageManager field > lockfile > npm fallback)
129
+ and runs install. Skip with --no-install.
130
+ 7. If package.json declares a "${ONBOARD_SCRIPT_NAME}" script, runs it
131
+ (your repo's hook for codegen / env pull / db setup / etc.).
132
+ 8. Scaffolds launch-recall (shadow git backup). Skip with --no-recall.
133
+ 9. Scaffolds migrate-safety (pg_dump wrapper + GHA backup workflow +
134
+ runbook + .backups/ gitignore line). Skip with --no-migrate-safety.
135
+ `);
136
+ }
137
+ async function prompt(question) {
138
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
139
+ return new Promise((resolve2) => rl.question(question, (answer) => {
140
+ rl.close();
141
+ resolve2(answer.trim());
142
+ }));
143
+ }
144
+ function fail(msg) {
145
+ console.error(`[launch-kit] \u2717 ${msg}`);
146
+ process.exit(1);
147
+ }
148
+ function info(msg) {
149
+ console.log(`[launch-kit] ${msg}`);
150
+ }
151
+ function ok(msg) {
152
+ console.log(`[launch-kit] \u2713 ${msg}`);
153
+ }
154
+ function which(bin) {
155
+ const res = (0, import_node_child_process.spawnSync)(process.platform === "win32" ? "where" : "which", [bin], { encoding: "utf-8" });
156
+ if (res.status !== 0) return null;
157
+ return res.stdout.split(/\r?\n/)[0]?.trim() || null;
158
+ }
159
+ function preflight() {
160
+ const nodeMajor = parseInt(process.versions.node.split(".")[0], 10);
161
+ if (nodeMajor < 18) fail(`Node.js >= 18 required (current: ${process.versions.node}).`);
162
+ if (!which("git")) fail("git not found in PATH. Install git: https://git-scm.com/downloads");
163
+ const hasGh = which("gh") !== null;
164
+ ok(`preflight ok \u2014 node ${process.versions.node}, git present${hasGh ? ", gh present" : ", gh not found (will use git for clone)"}`);
165
+ return { hasGh };
166
+ }
167
+ function callProjectInfo(args) {
168
+ return new Promise((resolve2, reject) => {
169
+ const mcpUrl = new import_node_url.URL("/api/mcp/project", args.serverUrl);
170
+ const body = JSON.stringify({
171
+ jsonrpc: "2.0",
172
+ id: 1,
173
+ method: "tools/call",
174
+ params: {
175
+ name: "project_info",
176
+ arguments: { org_slug: args.orgSlug, project_slug: args.projectSlug }
177
+ }
178
+ });
179
+ const requester = mcpUrl.protocol === "https:" ? import_node_https.request : import_node_http.request;
180
+ const req = requester(
181
+ {
182
+ host: mcpUrl.hostname,
183
+ port: mcpUrl.port || (mcpUrl.protocol === "https:" ? 443 : 80),
184
+ path: mcpUrl.pathname,
185
+ method: "POST",
186
+ headers: {
187
+ "Content-Type": "application/json",
188
+ "Accept": "application/json, text/event-stream",
189
+ "Content-Length": String(Buffer.byteLength(body)),
190
+ "Authorization": `Bearer ${args.token}`,
191
+ "X-Org-Slug": args.orgSlug,
192
+ "X-Project-Slug": args.projectSlug
193
+ }
194
+ },
195
+ (res) => {
196
+ const chunks = [];
197
+ res.on("data", (c) => chunks.push(c));
198
+ res.on("end", () => {
199
+ const text = Buffer.concat(chunks).toString("utf-8");
200
+ if (res.statusCode === 401 || res.statusCode === 403) {
201
+ reject(new Error(`PAT rejected (${res.statusCode}). Check token + that it has access to ${args.orgSlug}/${args.projectSlug}.`));
202
+ return;
203
+ }
204
+ if (res.statusCode && res.statusCode >= 400) {
205
+ reject(new Error(`LaunchSecure responded ${res.statusCode}: ${text.slice(0, 300)}`));
206
+ return;
207
+ }
208
+ let json = text;
209
+ if (text.startsWith("event:") || text.includes("\ndata: ")) {
210
+ for (const line of text.split("\n")) {
211
+ if (line.startsWith("data: ")) {
212
+ json = line.slice(6);
213
+ break;
214
+ }
215
+ }
216
+ }
217
+ try {
218
+ const parsed = JSON.parse(json);
219
+ if (parsed.error) {
220
+ reject(new Error(`project_info error: ${parsed.error.message ?? "unknown"}`));
221
+ return;
222
+ }
223
+ const inner = parsed.result?.content?.[0]?.text;
224
+ if (!inner) {
225
+ reject(new Error("project_info returned no content"));
226
+ return;
227
+ }
228
+ if (inner.startsWith("\u2500\u2500 Error")) {
229
+ const firstLine = inner.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("\u2500\u2500"))[0];
230
+ reject(new Error(`project_info: ${firstLine ?? inner}`));
231
+ return;
232
+ }
233
+ const payload = JSON.parse(inner);
234
+ resolve2({
235
+ orgSlug: payload.org.slug,
236
+ projectSlug: payload.project.slug,
237
+ projectName: payload.project.name,
238
+ repositoryUrl: payload.project.repositoryUrl
239
+ });
240
+ } catch (err) {
241
+ reject(new Error(`Could not parse project_info response: ${err instanceof Error ? err.message : String(err)}`));
242
+ }
243
+ });
244
+ }
245
+ );
246
+ req.on("error", reject);
247
+ req.write(body);
248
+ req.end();
249
+ });
250
+ }
251
+ function gitRemoteUrl(dir) {
252
+ const res = (0, import_node_child_process.spawnSync)("git", ["-C", dir, "config", "--get", "remote.origin.url"], { encoding: "utf-8" });
253
+ if (res.status !== 0) return null;
254
+ return res.stdout.trim() || null;
255
+ }
256
+ function normalizeRepoUrl(url) {
257
+ let u = url.trim().replace(/\.git$/, "").replace(/\/+$/, "");
258
+ const sshMatch = u.match(/^git@([^:]+):(.+)$/);
259
+ if (sshMatch) u = `https://${sshMatch[1]}/${sshMatch[2]}`;
260
+ try {
261
+ const parsed = new import_node_url.URL(u);
262
+ return `${parsed.protocol}//${parsed.host.toLowerCase()}${parsed.pathname}`;
263
+ } catch {
264
+ return u;
265
+ }
266
+ }
267
+ function isGitRepo(dir) {
268
+ return fs.existsSync(path.join(dir, ".git"));
269
+ }
270
+ function dirIsEmpty(dir) {
271
+ if (!fs.existsSync(dir)) return true;
272
+ return fs.readdirSync(dir).length === 0;
273
+ }
274
+ function cloneRepo(repoUrl, targetDir, hasGh) {
275
+ const isGithub = /github\.com/i.test(repoUrl);
276
+ let cmd;
277
+ let args;
278
+ if (hasGh && isGithub) {
279
+ cmd = "gh";
280
+ args = ["repo", "clone", repoUrl, targetDir];
281
+ info(`cloning via gh: ${repoUrl} \u2192 ${targetDir}`);
282
+ } else {
283
+ cmd = "git";
284
+ args = ["clone", repoUrl, targetDir];
285
+ info(`cloning via git: ${repoUrl} \u2192 ${targetDir}`);
286
+ }
287
+ const res = (0, import_node_child_process.spawnSync)(cmd, args, { stdio: "inherit" });
288
+ if (res.status !== 0) {
289
+ fail(
290
+ `Clone failed (${cmd} exited ${res.status}). For private repos make sure your GitHub auth is set up: \`gh auth login\` or an SSH key on your GitHub account.`
291
+ );
292
+ }
293
+ ok(`cloned to ${targetDir}`);
294
+ }
295
+ function writeConfigFile(targetDir, cfg) {
296
+ migrateLegacyCredFile(targetDir);
297
+ const p = path.join(targetDir, CONFIG_FILENAME);
298
+ const existed = fs.existsSync(p);
299
+ fs.writeFileSync(p, JSON.stringify(cfg, null, 2) + "\n", "utf-8");
300
+ try {
301
+ fs.chmodSync(p, 384);
302
+ } catch {
303
+ }
304
+ ok(`${existed ? "updated" : "wrote"} ${CONFIG_FILENAME}`);
305
+ }
306
+ function migrateLegacyCredFile(targetDir) {
307
+ const legacy = path.join(targetDir, LEGACY_CONFIG_FILENAME);
308
+ const dest = path.join(targetDir, CONFIG_FILENAME);
309
+ if (!fs.existsSync(legacy) || fs.existsSync(dest)) return;
310
+ let parsed;
311
+ try {
312
+ parsed = JSON.parse(fs.readFileSync(legacy, "utf-8"));
313
+ } catch {
314
+ return;
315
+ }
316
+ const pat = parsed?.pat;
317
+ if (typeof pat !== "string" || !pat.startsWith("ls_pat_")) return;
318
+ fs.renameSync(legacy, dest);
319
+ removeGitignoreLine(targetDir, LEGACY_CONFIG_FILENAME);
320
+ ok(`migrated legacy ${LEGACY_CONFIG_FILENAME} \u2192 ${CONFIG_FILENAME} (the old name is now reserved for file-backed-config)`);
321
+ }
322
+ function removeGitignoreLine(targetDir, line) {
323
+ const p = path.join(targetDir, ".gitignore");
324
+ if (!fs.existsSync(p)) return;
325
+ const before = fs.readFileSync(p, "utf-8");
326
+ const after = before.split(/\r?\n/).filter((l) => l.trim() !== line).join("\n");
327
+ if (after === before) return;
328
+ fs.writeFileSync(p, after, "utf-8");
329
+ ok(`removed ${line} from .gitignore (now reserved for file-backed-config)`);
330
+ }
331
+ var LAUNCH_SECURE_HEADERS_HELPER = `node -e 'const j=JSON.parse(require("fs").readFileSync(".launch-secure.cred.config","utf-8"));process.stdout.write(JSON.stringify({Authorization:"Bearer "+j.pat,"X-Org-Slug":j.orgSlug,"X-Project-Slug":j.projectSlug}))'`;
332
+ function buildLaunchKitMcpEntries(cfg) {
333
+ return {
334
+ "launch-secure": {
335
+ type: "http",
336
+ url: `${cfg.serverUrl}/api/mcp/project`,
337
+ headersHelper: LAUNCH_SECURE_HEADERS_HELPER
338
+ },
339
+ "launch-chart": {
340
+ command: "npx",
341
+ args: ["-y", "-p", LAUNCH_KIT_PKG, "launch-chart"],
342
+ env: { LAUNCH_CHART_AUTOSERVE: "1" }
343
+ },
344
+ "launch-deck": {
345
+ command: "npx",
346
+ args: ["-y", "-p", LAUNCH_KIT_PKG, "launch-deck"]
347
+ },
348
+ "launch-orbit": {
349
+ command: "npx",
350
+ args: ["-y", "-p", LAUNCH_KIT_PKG, "launch-orbit", "mcp"]
351
+ },
352
+ "launch-recall": {
353
+ command: "npx",
354
+ args: ["-y", "-p", LAUNCH_KIT_PKG, "launch-recall", "mcp"]
355
+ }
356
+ };
357
+ }
358
+ function mergeMcpFile(targetDir, launchKitEntries) {
359
+ const p = path.join(targetDir, ".mcp.json");
360
+ const hadExisting = fs.existsSync(p);
361
+ let existing = {};
362
+ if (hadExisting) {
363
+ try {
364
+ existing = JSON.parse(fs.readFileSync(p, "utf-8"));
365
+ } catch (err) {
366
+ fail(`Could not parse existing .mcp.json: ${err instanceof Error ? err.message : String(err)}`);
367
+ }
368
+ }
369
+ const existingServerCount = Object.keys(existing.mcpServers ?? {}).length;
370
+ const merged = { ...existing, mcpServers: { ...existing.mcpServers ?? {} } };
371
+ for (const [name, entry] of Object.entries(launchKitEntries)) {
372
+ merged.mcpServers[name] = entry;
373
+ }
374
+ fs.writeFileSync(p, JSON.stringify(merged, null, 2) + "\n", "utf-8");
375
+ const action = hadExisting && existingServerCount > 0 ? "merged into" : "wrote";
376
+ ok(`${action} .mcp.json (${Object.keys(launchKitEntries).length} launch-kit entries)`);
377
+ }
378
+ function detectPackageManager(repoDir) {
379
+ const pkgPath = path.join(repoDir, "package.json");
380
+ if (!fs.existsSync(pkgPath)) return null;
381
+ try {
382
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
383
+ if (typeof pkg.packageManager === "string") {
384
+ const name = pkg.packageManager.split("@")[0];
385
+ const match = PACKAGE_MANAGERS.find((p) => p.name === name);
386
+ if (match) return { pm: match, source: `package.json packageManager (${pkg.packageManager})` };
387
+ info(`packageManager field "${pkg.packageManager}" is not recognized \u2014 falling back to lockfile detection`);
388
+ }
389
+ } catch {
390
+ }
391
+ const matches = PACKAGE_MANAGERS.map((pm) => ({ pm, lockfile: pm.lockfiles.find((lf) => fs.existsSync(path.join(repoDir, lf))) ?? null })).filter((m) => m.lockfile !== null);
392
+ if (matches.length === 1) {
393
+ return { pm: matches[0].pm, source: `lockfile ${matches[0].lockfile}` };
394
+ }
395
+ if (matches.length > 1) {
396
+ info(`multiple lockfiles found (${matches.map((m) => m.lockfile).join(", ")}) \u2014 picking ${matches[0].pm.name} by precedence`);
397
+ return { pm: matches[0].pm, source: `lockfile ${matches[0].lockfile} (multiple present)` };
398
+ }
399
+ for (const pm of PACKAGE_MANAGERS) {
400
+ if (pm.workspaceFiles?.some((wf) => fs.existsSync(path.join(repoDir, wf)))) {
401
+ return { pm, source: `workspace file (${pm.workspaceFiles.find((wf) => fs.existsSync(path.join(repoDir, wf)))})` };
402
+ }
403
+ }
404
+ const npm = PACKAGE_MANAGERS.find((p) => p.name === "npm");
405
+ return { pm: npm, source: "default (no signal found)" };
406
+ }
407
+ function runInstall(repoDir, detected) {
408
+ const { pm } = detected;
409
+ if (!which(pm.binary)) {
410
+ fail(
411
+ `${pm.name} not found on PATH. Configs and clone are intact. Install ${pm.name} (try \`corepack enable\` if you have Node \u226516), then run: cd ${path.basename(repoDir)} && ${pm.binary} ${pm.installArgs.join(" ")}`
412
+ );
413
+ }
414
+ info(`running ${pm.binary} ${pm.installArgs.join(" ")} \u2026`);
415
+ const res = (0, import_node_child_process.spawnSync)(pm.binary, pm.installArgs, { cwd: repoDir, stdio: "inherit" });
416
+ if (res.status !== 0) {
417
+ fail(
418
+ `${pm.name} install failed (exit ${res.status}). Configs and clone are intact \u2014 fix the underlying error and retry: cd ${path.basename(repoDir)} && ${pm.binary} ${pm.installArgs.join(" ")}`
419
+ );
420
+ }
421
+ ok(`${pm.name} install complete`);
422
+ }
423
+ function hasOnboardScript(repoDir) {
424
+ const pkgPath = path.join(repoDir, "package.json");
425
+ if (!fs.existsSync(pkgPath)) return false;
426
+ try {
427
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
428
+ return typeof pkg.scripts?.[ONBOARD_SCRIPT_NAME] === "string";
429
+ } catch {
430
+ return false;
431
+ }
432
+ }
433
+ function runRecallInit(repoDir) {
434
+ info(`scaffolding launch-recall (shadow git backup) \u2026`);
435
+ const recallEntry = path.resolve(__dirname, "recall-entry.js");
436
+ const useSibling = fs.existsSync(recallEntry);
437
+ const cmd = useSibling ? process.execPath : "npx";
438
+ const args = useSibling ? [recallEntry, "init"] : ["-y", "-p", LAUNCH_KIT_PKG, "launch-recall", "init"];
439
+ const res = (0, import_node_child_process.spawnSync)(cmd, args, { cwd: repoDir, stdio: "inherit" });
440
+ if (res.status !== 0) {
441
+ info(`\u26A0 launch-recall init failed (exit ${res.status}). Main onboarding is complete \u2014 you can retry later: cd ${path.basename(repoDir)} && npx -y -p ${LAUNCH_KIT_PKG} launch-recall init`);
442
+ return;
443
+ }
444
+ ok(`launch-recall ready (shadow git initialized)`);
445
+ }
446
+ function runOnboard(repoDir, pm) {
447
+ info(`running ${pm.binary} run ${ONBOARD_SCRIPT_NAME} \u2026`);
448
+ const res = (0, import_node_child_process.spawnSync)(pm.binary, ["run", ONBOARD_SCRIPT_NAME], { cwd: repoDir, stdio: "inherit" });
449
+ if (res.status !== 0) {
450
+ fail(
451
+ `${pm.name} run ${ONBOARD_SCRIPT_NAME} failed (exit ${res.status}). Install completed but the onboard script errored. Fix and retry: cd ${path.basename(repoDir)} && ${pm.binary} run ${ONBOARD_SCRIPT_NAME}`
452
+ );
453
+ }
454
+ ok(`${ONBOARD_SCRIPT_NAME} script complete`);
455
+ }
456
+ function ensureGitignoreLine(targetDir, line) {
457
+ const p = path.join(targetDir, ".gitignore");
458
+ let content = fs.existsSync(p) ? fs.readFileSync(p, "utf-8") : "";
459
+ const lines = content.split(/\r?\n/);
460
+ if (lines.some((l) => l.trim() === line)) return;
461
+ if (content.length && !content.endsWith("\n")) content += "\n";
462
+ content += `${line}
463
+ `;
464
+ fs.writeFileSync(p, content, "utf-8");
465
+ ok(`appended ${line} to .gitignore`);
466
+ }
467
+ function copyScaffoldIfMissing(srcPath, destPath, label) {
468
+ if (!fs.existsSync(srcPath)) return "missing-src";
469
+ if (fs.existsSync(destPath)) {
470
+ info(`${label} already present \u2014 leaving alone`);
471
+ return "existed";
472
+ }
473
+ fs.mkdirSync(path.dirname(destPath), { recursive: true });
474
+ fs.copyFileSync(srcPath, destPath);
475
+ try {
476
+ const srcMode = fs.statSync(srcPath).mode;
477
+ fs.chmodSync(destPath, srcMode);
478
+ } catch {
479
+ }
480
+ ok(`wrote ${label}`);
481
+ return "wrote";
482
+ }
483
+ function scaffoldMigrateSafety(targetDir) {
484
+ const scaffoldsRoot = path.resolve(__dirname, "..", "..", "scaffolds", "migrate-safety");
485
+ if (!fs.existsSync(scaffoldsRoot)) {
486
+ info(`\u26A0 migrate-safety scaffolds not found at ${scaffoldsRoot} \u2014 skipping (this is a packaging bug; main onboarding is unaffected)`);
487
+ return;
488
+ }
489
+ const files = [
490
+ {
491
+ src: path.join(scaffoldsRoot, ".github", "workflows", "backup-on-migration.yml"),
492
+ dest: path.join(targetDir, ".github", "workflows", "backup-on-migration.yml"),
493
+ label: ".github/workflows/backup-on-migration.yml"
494
+ },
495
+ {
496
+ src: path.join(scaffoldsRoot, "scripts", "migrate-with-backup.sh"),
497
+ dest: path.join(targetDir, "scripts", "migrate-with-backup.sh"),
498
+ label: "scripts/migrate-with-backup.sh"
499
+ },
500
+ {
501
+ src: path.join(scaffoldsRoot, "docs", "migrations-runbook.md"),
502
+ dest: path.join(targetDir, "docs", "migrations-runbook.md"),
503
+ label: "docs/migrations-runbook.md"
504
+ }
505
+ ];
506
+ info("scaffolding migrate-safety (pg_dump wrapper + GHA backup workflow + runbook) \u2026");
507
+ for (const f of files) copyScaffoldIfMissing(f.src, f.dest, f.label);
508
+ ensureGitignoreLine(targetDir, ".backups/");
509
+ ok("migrate-safety ready \u2014 see docs/migrations-runbook.md for db:migrate wiring + PROD_DATABASE_URL secret setup");
510
+ }
511
+ async function main() {
512
+ const args = parseArgs(process.argv.slice(2));
513
+ if (args.help) {
514
+ printHelp();
515
+ return;
516
+ }
517
+ const subcommand = process.argv[2];
518
+ if (subcommand && subcommand !== "init" && !subcommand.startsWith("--")) {
519
+ fail(`Unknown subcommand "${subcommand}". Only "init" is supported. Run with --help for usage.`);
520
+ }
521
+ if (!args.token) {
522
+ const t = await prompt("LaunchSecure PAT (ls_pat_\u2026): ");
523
+ args.token = t || null;
524
+ }
525
+ if (!args.token) fail("--token (or LS_PAT env) is required.");
526
+ if (!/^ls_pat_/.test(args.token)) fail("Token does not look like a LaunchSecure PAT (expected prefix ls_pat_).");
527
+ if (!args.orgSlug) fail("--org=<orgSlug> is required.");
528
+ if (!args.projectSlug) fail("--project=<projectSlug> is required.");
529
+ const { hasGh } = preflight();
530
+ info(`resolving project ${args.orgSlug}/${args.projectSlug} on ${args.serverUrl} \u2026`);
531
+ let resolved;
532
+ try {
533
+ resolved = await callProjectInfo(args);
534
+ } catch (err) {
535
+ fail(err instanceof Error ? err.message : String(err));
536
+ }
537
+ ok(`resolved "${resolved.projectName}"`);
538
+ if (!resolved.repositoryUrl) {
539
+ fail(
540
+ `Project "${resolved.projectSlug}" has no GitHub repository configured. Connect GitHub at ${args.serverUrl}/${resolved.orgSlug}/projects/${resolved.projectSlug}/settings/integrations, then re-run init.`
541
+ );
542
+ }
543
+ const repoUrl = resolved.repositoryUrl;
544
+ const cwd = process.cwd();
545
+ const targetDir = path.resolve(args.targetDir ?? path.join(cwd, resolved.projectSlug));
546
+ const normalizedRemote = normalizeRepoUrl(repoUrl);
547
+ let skipClone = false;
548
+ if (fs.existsSync(targetDir)) {
549
+ if (isGitRepo(targetDir)) {
550
+ const existingRemote = gitRemoteUrl(targetDir);
551
+ if (existingRemote && normalizeRepoUrl(existingRemote) === normalizedRemote) {
552
+ ok(`${targetDir} is already a clone of ${repoUrl} \u2014 skipping clone, refreshing configs only`);
553
+ skipClone = true;
554
+ } else {
555
+ fail(`${targetDir} is a git repo but its remote (${existingRemote ?? "unknown"}) does not match ${repoUrl}. Refusing to overwrite. Pass --dir=<other-path>.`);
556
+ }
557
+ } else if (!dirIsEmpty(targetDir)) {
558
+ fail(`${targetDir} exists and is not empty (and not a matching git repo). Refusing to clone into it. Pass --dir=<other-path>.`);
559
+ }
560
+ }
561
+ if (!skipClone) cloneRepo(repoUrl, targetDir, hasGh);
562
+ const cfg = {
563
+ pat: args.token,
564
+ orgSlug: resolved.orgSlug,
565
+ projectSlug: resolved.projectSlug,
566
+ serverUrl: args.serverUrl
567
+ };
568
+ writeConfigFile(targetDir, cfg);
569
+ mergeMcpFile(targetDir, buildLaunchKitMcpEntries(cfg));
570
+ ensureGitignoreLine(targetDir, CONFIG_FILENAME);
571
+ let installSkippedReason = null;
572
+ const detected = detectPackageManager(targetDir);
573
+ if (detected) info(`detected package manager: ${detected.pm.name} (${detected.source})`);
574
+ if (args.noInstall) {
575
+ installSkippedReason = "--no-install passed";
576
+ } else if (!detected) {
577
+ installSkippedReason = "no package.json found";
578
+ } else {
579
+ runInstall(targetDir, detected);
580
+ if (hasOnboardScript(targetDir)) runOnboard(targetDir, detected.pm);
581
+ }
582
+ const hasOnboard = hasOnboardScript(targetDir);
583
+ if (!args.noRecall) runRecallInit(targetDir);
584
+ if (!args.noMigrateSafety) scaffoldMigrateSafety(targetDir);
585
+ const relTarget = path.relative(cwd, targetDir) || ".";
586
+ console.log("");
587
+ ok(`done \u2014 ${resolved.projectName} is ready at ${targetDir}`);
588
+ if (installSkippedReason) {
589
+ const installLine = detected ? ` ${detected.pm.binary} ${detected.pm.installArgs.join(" ")}` : ` npm install # or your package manager of choice`;
590
+ const onboardLine = hasOnboard && detected ? `
591
+ ${detected.pm.binary} run ${ONBOARD_SCRIPT_NAME} # project setup hook` : "";
592
+ console.log(`
593
+ Next steps (install skipped: ${installSkippedReason}):
594
+ cd ${relTarget}
595
+ ${installLine}${onboardLine}
596
+ claude # launch Claude Code (5 MCPs wired)
597
+ ${LAUNCH_KIT_TOOLS_GUIDE}`);
598
+ } else {
599
+ console.log(`
600
+ Next steps:
601
+ cd ${relTarget}
602
+ claude # launch Claude Code (5 MCPs wired)
603
+ ${LAUNCH_KIT_TOOLS_GUIDE}`);
604
+ }
605
+ }
606
+ main().catch((err) => {
607
+ console.error(`[launch-kit] unexpected error: ${err instanceof Error ? err.stack ?? err.message : String(err)}`);
608
+ process.exit(1);
609
+ });