@launchsecure/launch-kit 0.0.35 → 0.0.37
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/chart-client/assets/index-DJrjyXbN.css +1 -0
- package/dist/chart-client/index.html +2 -2
- package/dist/client/assets/index-8eSXr3Ez.css +32 -0
- package/dist/client/index.html +2 -2
- package/dist/council-client/assets/index-4K0t2WrZ.css +1 -0
- package/dist/council-client/index.html +2 -2
- package/dist/deck-client/assets/{_baseUniq-BiVx0WO_.js → _baseUniq-Cn5TyL9s.js} +1 -1
- package/dist/deck-client/assets/{arc-DGMkiEzS.js → arc-D61amKYu.js} +1 -1
- package/dist/deck-client/assets/{architectureDiagram-Q4EWVU46-Y2WRmHtk.js → architectureDiagram-Q4EWVU46-CpKrvC2W.js} +1 -1
- package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-_Lbfu5BQ.js → blockDiagram-DXYQGD6D-Yj5OjxvG.js} +1 -1
- package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-CTqpYTBX.js → c4Diagram-AHTNJAMY-BIR810Tv.js} +1 -1
- package/dist/deck-client/assets/channel-DrJz2x-n.js +1 -0
- package/dist/deck-client/assets/{chunk-4BX2VUAB-liEIbPHs.js → chunk-4BX2VUAB-BeSHwGvx.js} +1 -1
- package/dist/deck-client/assets/{chunk-4TB4RGXK-CCc6lYvL.js → chunk-4TB4RGXK-CCqzsLpg.js} +1 -1
- package/dist/deck-client/assets/{chunk-55IACEB6-D02jJUR2.js → chunk-55IACEB6-CuW_aq4-.js} +1 -1
- package/dist/deck-client/assets/{chunk-EDXVE4YY-BFmGMbLD.js → chunk-EDXVE4YY-Dl35ixYh.js} +1 -1
- package/dist/deck-client/assets/{chunk-FMBD7UC4-6wFLOVcJ.js → chunk-FMBD7UC4-TwreZQTv.js} +1 -1
- package/dist/deck-client/assets/{chunk-OYMX7WX6-Bnr8RiBf.js → chunk-OYMX7WX6-Ahfw8EUo.js} +1 -1
- package/dist/deck-client/assets/{chunk-QZHKN3VN-Ct82MksJ.js → chunk-QZHKN3VN-DlE_zlU-.js} +1 -1
- package/dist/deck-client/assets/{chunk-YZCP3GAM-BXmN1diQ.js → chunk-YZCP3GAM-Dj6QWzSg.js} +1 -1
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-a3tg9w7z.js +1 -0
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-a3tg9w7z.js +1 -0
- package/dist/deck-client/assets/clone-Dd7JBCL5.js +1 -0
- package/dist/deck-client/assets/{cose-bilkent-S5V4N54A-CmQCT-mH.js → cose-bilkent-S5V4N54A-BO1z5aOM.js} +1 -1
- package/dist/deck-client/assets/{dagre-KV5264BT-DDdSa9EX.js → dagre-KV5264BT-DVsw17fE.js} +1 -1
- package/dist/deck-client/assets/{diagram-5BDNPKRD-Bccks2xJ.js → diagram-5BDNPKRD-6jYs7oZk.js} +1 -1
- package/dist/deck-client/assets/{diagram-G4DWMVQ6-CPPNgxmQ.js → diagram-G4DWMVQ6-6DbggeGE.js} +1 -1
- package/dist/deck-client/assets/{diagram-MMDJMWI5-KrD300pS.js → diagram-MMDJMWI5-CQtk1cSU.js} +1 -1
- package/dist/deck-client/assets/{diagram-TYMM5635-DefnLuQf.js → diagram-TYMM5635-BR-gt75b.js} +1 -1
- package/dist/deck-client/assets/{erDiagram-SMLLAGMA-DI9FfnFP.js → erDiagram-SMLLAGMA-C9qMtjdY.js} +1 -1
- package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-twKyd3Fx.js → flowDiagram-DWJPFMVM-CdaPhPYb.js} +1 -1
- package/dist/deck-client/assets/{ganttDiagram-T4ZO3ILL-Wau3jhBr.js → ganttDiagram-T4ZO3ILL-BRsZWUy4.js} +1 -1
- package/dist/deck-client/assets/{gitGraphDiagram-UUTBAWPF-D9GgYXwb.js → gitGraphDiagram-UUTBAWPF-B8Z90jCj.js} +1 -1
- package/dist/deck-client/assets/{graph-BhNLzyXS.js → graph-my2Zphm4.js} +1 -1
- package/dist/deck-client/assets/index-ByqxPEgU.css +1 -0
- package/dist/deck-client/assets/{index-BtQBaQ7s.js → index-DqAoYZwV.js} +43 -42
- package/dist/deck-client/assets/{infoDiagram-42DDH7IO-TylGlSG-.js → infoDiagram-42DDH7IO-Csr9loin.js} +1 -1
- package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-DAT8icpg.js → ishikawaDiagram-UXIWVN3A-HWdvUNFi.js} +1 -1
- package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-D3v_XL72.js → journeyDiagram-VCZTEJTY-CjYHG6EM.js} +1 -1
- package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-DNUOBiNr.js → kanban-definition-6JOO6SKY-CX3JdUu7.js} +1 -1
- package/dist/deck-client/assets/{layout-COfodgwF.js → layout-Bcucv5Gi.js} +1 -1
- package/dist/deck-client/assets/{linear-DmTsuIvK.js → linear-CUGM5FJZ.js} +1 -1
- package/dist/deck-client/assets/{min-BW1F7i1D.js → min-Dw4g5w9z.js} +1 -1
- package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-CErFzKWl.js → mindmap-definition-QFDTVHPH-C8oo61fg.js} +1 -1
- package/dist/deck-client/assets/{pieDiagram-DEJITSTG-DW5F757o.js → pieDiagram-DEJITSTG-D2WYGkq8.js} +1 -1
- package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-B1S2-TfI.js → quadrantDiagram-34T5L4WZ-Vh00GISt.js} +1 -1
- package/dist/deck-client/assets/{requirementDiagram-MS252O5E-BY5BAR-5.js → requirementDiagram-MS252O5E-DxI-DFrN.js} +1 -1
- package/dist/deck-client/assets/{sankeyDiagram-XADWPNL6-CE1Cp9HS.js → sankeyDiagram-XADWPNL6-QgwyjasI.js} +1 -1
- package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-IaHnbKye.js → sequenceDiagram-FGHM5R23-DmOmD5Ni.js} +1 -1
- package/dist/deck-client/assets/{stateDiagram-FHFEXIEX-CwPJm9hU.js → stateDiagram-FHFEXIEX-CRwglGg_.js} +1 -1
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-BvZLEWAA.js +1 -0
- package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-DVFGGSgN.js → timeline-definition-GMOUNBTQ-Dj9YGKOh.js} +1 -1
- package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-C1194MJi.js → vennDiagram-DHZGUBPP-xzIaOzEU.js} +1 -1
- package/dist/deck-client/assets/wardley-RL74JXVD-CEAay09T.js +162 -0
- package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-hpwdFfGj.js → wardleyDiagram-NUSXRM2D-BIYYh-JZ.js} +1 -1
- package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-DYkotwy8.js → xychartDiagram-5P7HB3ND-Cy9EoJCh.js} +1 -1
- package/dist/deck-client/index.html +2 -2
- package/dist/server/cli.js +261 -26
- package/dist/server/council-entry.js +86 -2
- package/dist/server/council-serve.js +81 -2
- package/dist/server/deck-mcp-entry.js +449 -68
- package/dist/server/deck-serve.js +411 -42
- package/dist/server/init-entry.js +732 -237
- package/dist/server/orbit-entry.js +880 -144
- package/dist/server/radar-docker-init-entry.js +371 -37
- package/dist/server/rover-entry.js +108 -20
- package/package.json +1 -1
- package/scaffolds/ls-marketplace/plugins/kit/skills/deploy-check/SKILL.md +5 -0
- package/scaffolds/ls-marketplace/plugins/kit/skills/kickoff/SKILL.md +20 -4
- package/scaffolds/ls-marketplace/plugins/kit/skills/orbit/SKILL.md +27 -7
- package/dist/chart-client/assets/index-DpKO9p0s.css +0 -1
- package/dist/client/assets/index-Dv6dD2zY.css +0 -32
- package/dist/council-client/assets/index-AqQ9Sei6.css +0 -1
- package/dist/deck-client/assets/channel-DB6LxW_l.js +0 -1
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-g944ZyG8.js +0 -1
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-g944ZyG8.js +0 -1
- package/dist/deck-client/assets/clone-DiIRH1pI.js +0 -1
- package/dist/deck-client/assets/index-B-YQq5b5.css +0 -1
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-DQYa2M1q.js +0 -1
- package/dist/deck-client/assets/wardley-RL74JXVD-CHZiUbBa.js +0 -162
- /package/dist/chart-client/assets/{index-DFu2xIrM.js → index-BgUxHxwE.js} +0 -0
- /package/dist/client/assets/{index-Cbw6bVdx.js → index-CUivaQnN.js} +0 -0
- /package/dist/council-client/assets/{index-CAsmGTzg.js → index-DN8HN_5K.js} +0 -0
|
@@ -34,6 +34,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
34
34
|
));
|
|
35
35
|
|
|
36
36
|
// src/server/cf-ingress.ts
|
|
37
|
+
function serviceLabel(s) {
|
|
38
|
+
return s.label ?? s.name;
|
|
39
|
+
}
|
|
37
40
|
async function cf(opts) {
|
|
38
41
|
const res = await fetch(`${CF_API_BASE}${opts.path}`, {
|
|
39
42
|
method: opts.method,
|
|
@@ -58,6 +61,17 @@ async function cf(opts) {
|
|
|
58
61
|
function isNotFound(env) {
|
|
59
62
|
return !env.success && (env.errors ?? []).some((e) => e.code === 7003 || e.code === 1001 || e.code === 81044);
|
|
60
63
|
}
|
|
64
|
+
async function findTunnelByName(input) {
|
|
65
|
+
const q = new URLSearchParams({ name: input.tunnelName, is_deleted: "false" }).toString();
|
|
66
|
+
const res = await cf({
|
|
67
|
+
apiToken: input.apiToken,
|
|
68
|
+
method: "GET",
|
|
69
|
+
path: `/accounts/${input.accountId}/cfd_tunnel?${q}`
|
|
70
|
+
});
|
|
71
|
+
if (!res.success || !Array.isArray(res.result)) return null;
|
|
72
|
+
const live = res.result.find((t) => t.name === input.tunnelName && !t.deleted_at);
|
|
73
|
+
return live?.id ?? null;
|
|
74
|
+
}
|
|
61
75
|
function loadState(path) {
|
|
62
76
|
if (!(0, import_node_fs.existsSync)(path)) return null;
|
|
63
77
|
try {
|
|
@@ -89,16 +103,26 @@ async function ensureTunnel(input, knownTunnelId) {
|
|
|
89
103
|
throw new Error(`[cf] tunnel GET failed: ${JSON.stringify(got.errors)}`);
|
|
90
104
|
}
|
|
91
105
|
}
|
|
106
|
+
const existing = await findTunnelByName(input);
|
|
107
|
+
if (existing) {
|
|
108
|
+
console.log(`[cf] adopted existing tunnel "${input.tunnelName}" (${existing}) \u2014 local state was missing`);
|
|
109
|
+
return existing;
|
|
110
|
+
}
|
|
92
111
|
const created = await cf({
|
|
93
112
|
apiToken: input.apiToken,
|
|
94
113
|
method: "POST",
|
|
95
114
|
path: `/accounts/${input.accountId}/cfd_tunnel`,
|
|
96
115
|
body: { name: input.tunnelName, config_src: "cloudflare" }
|
|
97
116
|
});
|
|
98
|
-
if (
|
|
99
|
-
|
|
117
|
+
if (created.success && created.result) return created.result.id;
|
|
118
|
+
if ((created.errors ?? []).some((e) => e.code === 1013)) {
|
|
119
|
+
const adopted = await findTunnelByName(input);
|
|
120
|
+
if (adopted) {
|
|
121
|
+
console.log(`[cf] tunnel "${input.tunnelName}" already existed (1013) \u2014 adopted ${adopted}`);
|
|
122
|
+
return adopted;
|
|
123
|
+
}
|
|
100
124
|
}
|
|
101
|
-
|
|
125
|
+
throw new Error(`[cf] tunnel create failed: ${JSON.stringify(created.errors)}`);
|
|
102
126
|
}
|
|
103
127
|
async function fetchConnectorToken(input, tunnelId) {
|
|
104
128
|
const res = await cf({
|
|
@@ -113,7 +137,7 @@ async function fetchConnectorToken(input, tunnelId) {
|
|
|
113
137
|
}
|
|
114
138
|
async function setIngressConfig(input, tunnelId) {
|
|
115
139
|
const ingress = input.services.map((s) => ({
|
|
116
|
-
hostname: `${s
|
|
140
|
+
hostname: `${serviceLabel(s)}.${input.zone.name}`,
|
|
117
141
|
service: `http://localhost:${s.port}`
|
|
118
142
|
}));
|
|
119
143
|
ingress.push({ service: "http_status:404" });
|
|
@@ -128,7 +152,7 @@ async function setIngressConfig(input, tunnelId) {
|
|
|
128
152
|
}
|
|
129
153
|
}
|
|
130
154
|
async function ensureDnsRecord(input, tunnelId, service) {
|
|
131
|
-
const fqdn = `${service
|
|
155
|
+
const fqdn = `${serviceLabel(service)}.${input.zone.name}`;
|
|
132
156
|
const target = `${tunnelId}.cfargotunnel.com`;
|
|
133
157
|
const existing = await cf({
|
|
134
158
|
apiToken: input.apiToken,
|
|
@@ -172,7 +196,7 @@ async function provisionIngress(input) {
|
|
|
172
196
|
await setIngressConfig(input, tunnelId);
|
|
173
197
|
await Promise.all(input.services.map((s) => ensureDnsRecord(input, tunnelId, s)));
|
|
174
198
|
const hostnames = {};
|
|
175
|
-
for (const s of input.services) hostnames[s.name] = `${s
|
|
199
|
+
for (const s of input.services) hostnames[s.name] = `${serviceLabel(s)}.${input.zone.name}`;
|
|
176
200
|
return { tunnelId, connectorToken, hostnames };
|
|
177
201
|
}
|
|
178
202
|
var import_node_fs, import_node_path, CF_API_BASE, CF_ERR_DNS_RECORD_EXISTS;
|
|
@@ -222,6 +246,17 @@ async function ensureDocker(opts = {}) {
|
|
|
222
246
|
const initial = detectDocker();
|
|
223
247
|
if (initial.daemonAlive) return initial;
|
|
224
248
|
const platform = process.platform;
|
|
249
|
+
if (initial.cliPresent && canStartDaemon(platform)) {
|
|
250
|
+
if (await confirm(opts, "Docker is installed but its daemon isn't running. Start it now?")) {
|
|
251
|
+
const started = await startDockerDaemon(platform);
|
|
252
|
+
if (started.daemonAlive) return started;
|
|
253
|
+
console.log(
|
|
254
|
+
`
|
|
255
|
+
Docker did not become ready within ${Math.round(DAEMON_START_TIMEOUT_MS / 1e3)}s. Open Docker manually and re-run setup.`
|
|
256
|
+
);
|
|
257
|
+
throw new Error("Docker daemon did not start in time.");
|
|
258
|
+
}
|
|
259
|
+
}
|
|
225
260
|
if (!opts.autoInstall) {
|
|
226
261
|
printManualInstructions(initial, platform);
|
|
227
262
|
throw new Error("Docker is not ready. Install + start it, then re-run `launch-rover setup`.");
|
|
@@ -250,6 +285,40 @@ async function ensureDocker(opts = {}) {
|
|
|
250
285
|
`Auto-install is not supported on ${platform}. Install Docker Desktop manually, then re-run setup.`
|
|
251
286
|
);
|
|
252
287
|
}
|
|
288
|
+
function canStartDaemon(platform) {
|
|
289
|
+
if (platform === "darwin") return true;
|
|
290
|
+
if (platform === "linux") return commandExists("systemctl");
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
async function startDockerDaemon(platform) {
|
|
294
|
+
if (platform === "darwin") {
|
|
295
|
+
runAndPrint("open", ["-a", "Docker"]);
|
|
296
|
+
} else if (platform === "linux") {
|
|
297
|
+
runAndPrint("sudo", ["systemctl", "start", "docker"]);
|
|
298
|
+
} else {
|
|
299
|
+
throw new Error(`Cannot start the Docker daemon on ${platform}.`);
|
|
300
|
+
}
|
|
301
|
+
console.log("Waiting for the Docker daemon to come up\u2026");
|
|
302
|
+
const deadline = DAEMON_START_TIMEOUT_MS;
|
|
303
|
+
let waited = 0;
|
|
304
|
+
while (waited < deadline) {
|
|
305
|
+
await (0, import_promises2.setTimeout)(DAEMON_POLL_INTERVAL_MS);
|
|
306
|
+
waited += DAEMON_POLL_INTERVAL_MS;
|
|
307
|
+
const probe = detectDocker();
|
|
308
|
+
if (probe.daemonAlive) {
|
|
309
|
+
console.log(`Docker daemon is up (after ~${Math.round(waited / 1e3)}s).`);
|
|
310
|
+
return probe;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return detectDocker();
|
|
314
|
+
}
|
|
315
|
+
function commandExists(cmd) {
|
|
316
|
+
const probe = (0, import_node_child_process.spawnSync)(process.platform === "win32" ? "where" : "which", [cmd], {
|
|
317
|
+
stdio: "ignore",
|
|
318
|
+
timeout: 1500
|
|
319
|
+
});
|
|
320
|
+
return probe.status === 0;
|
|
321
|
+
}
|
|
253
322
|
function printManualInstructions(detection, platform) {
|
|
254
323
|
console.log("\n\u2500\u2500 Docker is required to run launch-rover \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
255
324
|
if (!detection.cliPresent) {
|
|
@@ -305,13 +374,16 @@ function runAndPrint(cmd, args) {
|
|
|
305
374
|
throw new Error(`${cmd} exited with code ${result.status ?? "signal"}`);
|
|
306
375
|
}
|
|
307
376
|
}
|
|
308
|
-
var import_node_child_process, import_promises, import_node_process;
|
|
377
|
+
var import_node_child_process, import_promises, import_node_process, import_promises2, DAEMON_START_TIMEOUT_MS, DAEMON_POLL_INTERVAL_MS;
|
|
309
378
|
var init_docker_install = __esm({
|
|
310
379
|
"src/server/rover/docker-install.ts"() {
|
|
311
380
|
"use strict";
|
|
312
381
|
import_node_child_process = require("node:child_process");
|
|
313
382
|
import_promises = require("node:readline/promises");
|
|
314
383
|
import_node_process = require("node:process");
|
|
384
|
+
import_promises2 = require("node:timers/promises");
|
|
385
|
+
DAEMON_START_TIMEOUT_MS = 6e4;
|
|
386
|
+
DAEMON_POLL_INTERVAL_MS = 2e3;
|
|
315
387
|
}
|
|
316
388
|
});
|
|
317
389
|
|
|
@@ -536,7 +608,7 @@ var require_package = __commonJS({
|
|
|
536
608
|
"package.json"(exports2, module2) {
|
|
537
609
|
module2.exports = {
|
|
538
610
|
name: "@launchsecure/launch-kit",
|
|
539
|
-
version: "0.0.
|
|
611
|
+
version: "0.0.37",
|
|
540
612
|
description: "LaunchSecure toolkit \u2014 launch-sequencer (pipeline runner + terminal bridge), launch-radar (feedback webhook receiver), launch-chart (project graph MCP), launch-deck (visual playground MCP), launch-kit-beacon (feedback Web Component), launch-recall (file-watcher backup). launch-pod is the container image these run inside.",
|
|
541
613
|
license: "MIT",
|
|
542
614
|
author: "LaunchSecure - AutomateWithUs",
|
|
@@ -19422,17 +19494,17 @@ function createTunnel(opts) {
|
|
|
19422
19494
|
const exhaustive = opts.provider;
|
|
19423
19495
|
throw new Error(`[tunnel] unknown provider: ${String(exhaustive)}`);
|
|
19424
19496
|
}
|
|
19425
|
-
var import_node_fs3, import_node_events,
|
|
19497
|
+
var import_node_fs3, import_node_events, import_promises3, import_undici, import_cloudflared, dnsResolver, dnsCache, dnsResilientDispatcher, BACKOFF_MIN_MS, BACKOFF_MAX_MS, SELFTEST_INTERVAL_MS, SELFTEST_TIMEOUT_MS, NAMED_FATAL_PATTERNS, CloudflaredTunnel;
|
|
19426
19498
|
var init_tunnel = __esm({
|
|
19427
19499
|
"src/server/tunnel/index.ts"() {
|
|
19428
19500
|
"use strict";
|
|
19429
19501
|
import_node_fs3 = require("node:fs");
|
|
19430
19502
|
import_node_events = require("node:events");
|
|
19431
|
-
|
|
19503
|
+
import_promises3 = require("node:dns/promises");
|
|
19432
19504
|
init_source();
|
|
19433
19505
|
import_undici = __toESM(require_undici());
|
|
19434
19506
|
import_cloudflared = require("cloudflared");
|
|
19435
|
-
dnsResolver = new
|
|
19507
|
+
dnsResolver = new import_promises3.Resolver();
|
|
19436
19508
|
dnsResolver.setServers(["1.1.1.1", "8.8.8.8"]);
|
|
19437
19509
|
dnsCache = new CacheableLookup({ resolver: dnsResolver });
|
|
19438
19510
|
dnsResilientDispatcher = new import_undici.Agent({
|
|
@@ -19689,8 +19761,11 @@ async function podsUp(state, body) {
|
|
|
19689
19761
|
await stopAndRemove(containerName);
|
|
19690
19762
|
const dockerConfigDir = body.skipPull ? null : setupDockerConfig();
|
|
19691
19763
|
try {
|
|
19692
|
-
if (
|
|
19693
|
-
await dockerRun(
|
|
19764
|
+
if (!body.skipPull) {
|
|
19765
|
+
await dockerRun(
|
|
19766
|
+
["pull", image],
|
|
19767
|
+
dockerConfigDir ? { DOCKER_CONFIG: dockerConfigDir } : {}
|
|
19768
|
+
);
|
|
19694
19769
|
}
|
|
19695
19770
|
const runArgs = [
|
|
19696
19771
|
"run",
|
|
@@ -19852,10 +19927,10 @@ async function dockerRun(args, envOverlay = {}) {
|
|
|
19852
19927
|
function setupDockerConfig() {
|
|
19853
19928
|
const token = process.env.GHCR_PULL_TOKEN;
|
|
19854
19929
|
if (!token) {
|
|
19855
|
-
|
|
19856
|
-
"
|
|
19857
|
-
500
|
|
19930
|
+
console.log(
|
|
19931
|
+
"[rover] no GHCR_PULL_TOKEN set \u2014 pulling image anonymously (succeeds only if the image is public)"
|
|
19858
19932
|
);
|
|
19933
|
+
return null;
|
|
19859
19934
|
}
|
|
19860
19935
|
const dir = (0, import_node_fs4.mkdtempSync)((0, import_node_path3.join)((0, import_node_os3.tmpdir)(), "launch-rover-docker-"));
|
|
19861
19936
|
const auth = Buffer.from(`${GHCR_USERNAME}:${token}`, "utf-8").toString("base64");
|
|
@@ -20171,15 +20246,27 @@ function printUsage() {
|
|
|
20171
20246
|
" --name=<host-name> Human-readable rover name (default: hostname)",
|
|
20172
20247
|
" --port=<n> Local daemon port (default: 52749)",
|
|
20173
20248
|
" --auto-install Allow setup to install Docker via brew / get-docker.sh",
|
|
20174
|
-
" --yes / -y Skip confirmation prompts (assume yes)",
|
|
20249
|
+
" --yes / -y Skip confirmation prompts (assume yes). Also auto-starts",
|
|
20250
|
+
" an installed-but-stopped Docker daemon without prompting.",
|
|
20175
20251
|
" --detach Spawn the daemon as a detached process and exit",
|
|
20176
20252
|
""
|
|
20177
20253
|
].join("\n")
|
|
20178
20254
|
);
|
|
20179
20255
|
}
|
|
20256
|
+
function ensureNodeVersion() {
|
|
20257
|
+
const raw = process.versions.node;
|
|
20258
|
+
const major = Number.parseInt(raw.split(".")[0] ?? "", 10);
|
|
20259
|
+
if (!Number.isFinite(major) || major < MIN_NODE_MAJOR) {
|
|
20260
|
+
throw new Error(
|
|
20261
|
+
`launch-rover requires Node >= ${MIN_NODE_MAJOR} (running ${raw}). Upgrade Node, then re-run \`launch-rover setup\`.`
|
|
20262
|
+
);
|
|
20263
|
+
}
|
|
20264
|
+
console.log(`[rover] node ok: v${raw}`);
|
|
20265
|
+
}
|
|
20180
20266
|
async function runSetup(argv) {
|
|
20181
20267
|
const args = parseArgs(argv);
|
|
20182
20268
|
console.log(`[rover] setup org=${args.orgSlug} server=${args.serverUrl}`);
|
|
20269
|
+
ensureNodeVersion();
|
|
20183
20270
|
await ensureDocker({ autoInstall: args.autoInstall, yes: args.yes });
|
|
20184
20271
|
const dockerInfo = detectDocker();
|
|
20185
20272
|
console.log(`[rover] docker ready: ${dockerInfo.version ?? "unknown"}`);
|
|
@@ -20314,7 +20401,7 @@ async function launchDaemon(args, ingress, state) {
|
|
|
20314
20401
|
function runSetupReset(roverId) {
|
|
20315
20402
|
clearRoverState(roverId);
|
|
20316
20403
|
}
|
|
20317
|
-
var import_node_os5, import_node_child_process3, import_node_path5, import_node_fs6, KIT_VERSION, DEFAULT_SERVER, DEFAULT_DAEMON_PORT;
|
|
20404
|
+
var import_node_os5, import_node_child_process3, import_node_path5, import_node_fs6, KIT_VERSION, DEFAULT_SERVER, DEFAULT_DAEMON_PORT, MIN_NODE_MAJOR;
|
|
20318
20405
|
var init_setup = __esm({
|
|
20319
20406
|
"src/server/rover/setup.ts"() {
|
|
20320
20407
|
"use strict";
|
|
@@ -20331,6 +20418,7 @@ var init_setup = __esm({
|
|
|
20331
20418
|
KIT_VERSION = require_package().version;
|
|
20332
20419
|
DEFAULT_SERVER = "https://launchsecure-v2.vercel.app";
|
|
20333
20420
|
DEFAULT_DAEMON_PORT = 52749;
|
|
20421
|
+
MIN_NODE_MAJOR = 18;
|
|
20334
20422
|
}
|
|
20335
20423
|
});
|
|
20336
20424
|
|
|
@@ -20383,7 +20471,7 @@ async function runTeardown(argv) {
|
|
|
20383
20471
|
}
|
|
20384
20472
|
const deadline = Date.now() + 3e3;
|
|
20385
20473
|
while (Date.now() < deadline && isPidAlive(lock.pid)) {
|
|
20386
|
-
await
|
|
20474
|
+
await sleep2(100);
|
|
20387
20475
|
}
|
|
20388
20476
|
if (isPidAlive(lock.pid) && parsed.force) {
|
|
20389
20477
|
console.log("[rover] daemon did not exit, sending SIGKILL");
|
|
@@ -20451,7 +20539,7 @@ function isPidAlive(pid) {
|
|
|
20451
20539
|
return false;
|
|
20452
20540
|
}
|
|
20453
20541
|
}
|
|
20454
|
-
function
|
|
20542
|
+
function sleep2(ms) {
|
|
20455
20543
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
20456
20544
|
}
|
|
20457
20545
|
var import_node_fs7, import_node_os6, import_node_path6;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@launchsecure/launch-kit",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.37",
|
|
4
4
|
"description": "LaunchSecure toolkit — launch-sequencer (pipeline runner + terminal bridge), launch-radar (feedback webhook receiver), launch-chart (project graph MCP), launch-deck (visual playground MCP), launch-kit-beacon (feedback Web Component), launch-recall (file-watcher backup). launch-pod is the container image these run inside.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "LaunchSecure - AutomateWithUs",
|
|
@@ -80,6 +80,11 @@ Run `BUILD_CMD` (the project's real build, so the check matches CI). Skip if `--
|
|
|
80
80
|
|
|
81
81
|
**DB-safety guard (only when the build touches a DB).** If the detected `BUILD_CMD` runs migrations as part of the build (e.g. a Node build script that chains `prisma migrate deploy`, or any build that applies `MIGRATION_TOOL` migrations — detect by scanning the build script / target), then a prod DB connection string must NOT be used. If the relevant connection env (`DATABASE_URL`, `DB_URL`, etc.) looks production-y (matches `neon.tech`, `rds.amazonaws.com`, `supabase.co`, a `vercel.app` proxy host, etc.), refuse the build step: `⚠ build skipped — build applies migrations and DATABASE_URL looks production-y (<host>); re-run with a dev DB or pass --skip=build`. If the build does NOT touch a DB (most stacks), skip this guard.
|
|
82
82
|
|
|
83
|
+
**Build-cache hygiene (avoid stale-cache false positives).** Incremental-cache frameworks reuse a build cache across runs, and a cache left over from a prior build/dev run on a different commit can make a *clean-tree* build fail with a stale-manifest error even though CI (which always builds clean) succeeds. The classic signature is Next.js's `PageNotFoundError: Cannot find module for page: <route>` / `Failed to collect page data for <route>` (often `ENOENT` pointing into `.next/`) — observed on `.well-known/*` and other dynamic routes that build fine in CI. To keep the build check faithful to CI:
|
|
84
|
+
|
|
85
|
+
1. **Clear the framework cache before building** when the project uses a cache-based framework. Remove only the framework's own *regenerable, gitignored* cache dir if it exists — `.next/` (Next.js) · `.nuxt/` + `.output/` (Nuxt) · `.svelte-kit/` (SvelteKit) · `.astro/` (Astro) · `.turbo/` (Turborepo/Turbopack). **Never** remove `dist/`, `build/`, `out/`, `node_modules/`, or anything git-tracked — confirm the dir is gitignored before deleting. Non-cache stacks: skip this, there is nothing to clear.
|
|
86
|
+
2. **Retry-once backstop.** If a build still fails AND the stderr matches a stale-cache signature (`Cannot find module for page`, `Failed to collect page data`, or an `ENOENT` referencing the cache dir), clear the cache (step 1) and re-run `BUILD_CMD` exactly once. Report ✗ only if the clean rebuild also fails; if the rebuild passes, report ✓ and note `(first attempt failed on a stale-cache artifact — passed clean)`. This prevents a regenerable-cache quirk from producing a NO-GO.
|
|
87
|
+
|
|
83
88
|
### 5. schema_drift (only when MIGRATION_TOOL supports it)
|
|
84
89
|
|
|
85
90
|
Schema-vs-migrations consistency, using the tool's own drift command:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Cold-start a session — read the last HANDOFF snapshot, the newest shipping log, the git summary, and (if LS is connected) your open work items, reconcile them, then categorize all pending work (resume in-flight · review/PR feedback · bugs · high-priority · low-hanging fruit · stale/dead · tech-debt/deferred, plus a non-pickable blocked list) and let you choose a list to work from before drilling into a specific item. Read-only; the complement to /kit:handoff. Does NOT branch, commit, or write files unless you confirm a next step.
|
|
2
|
+
description: Cold-start a session — read the last HANDOFF snapshot, the newest shipping log, the git summary, any in-flight orbits/worktrees, and (if LS is connected) your open work items, reconcile them, then categorize all pending work (resume in-flight · review/PR feedback · bugs · high-priority · low-hanging fruit · stale/dead · tech-debt/deferred, plus a non-pickable blocked list) and let you choose a list to work from before drilling into a specific item. Read-only; the complement to /kit:handoff. Does NOT branch, commit, or write files unless you confirm a next step.
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# /kit:kickoff
|
|
@@ -28,7 +28,7 @@ Parse `$ARGUMENTS`:
|
|
|
28
28
|
|
|
29
29
|
## Step 1 — Gather the sources (don't show yet)
|
|
30
30
|
|
|
31
|
-
Pull from
|
|
31
|
+
Pull from five layers in parallel. Each is independently optional; a missing one is noted, not fatal.
|
|
32
32
|
|
|
33
33
|
**1a. The HANDOFF snapshot** — read `doc/handoff/HANDOFF.md` if it exists. Extract its four live sections: **Where we are**, **What's next** (the priority-ordered 1–3), **Open threads**, and the embedded **Kickoff prompt**. This is the human-curated "what's next," highest signal. If the file doesn't exist, note "no HANDOFF yet — first kickoff" and lean on git + work items.
|
|
34
34
|
|
|
@@ -40,6 +40,8 @@ Pull from four layers in parallel. Each is independently optional; a missing one
|
|
|
40
40
|
- `git status --porcelain` — uncommitted work. Filter out generated noise (`.launchsecure/graphs/*.json`, `node_modules`, `dist`, `.next`). What remains is genuine in-flight work — a **"resume this"** candidate.
|
|
41
41
|
- Current branch + whether it's ahead/behind base.
|
|
42
42
|
|
|
43
|
+
> ⚠️ Everything in 1c is scoped to the **current** worktree only. In-flight work in other worktrees/orbits is invisible here — 1e covers it. Do not present a "resume in-flight" list without running 1e first.
|
|
44
|
+
|
|
43
45
|
**1d. Work items (only if LS connected)** — build the backlog candidates:
|
|
44
46
|
- `mcp__launch-secure__work_items_list({ assignee_email: "<git email>", status: "IN_PROGRESS" })` — things you already started; strongest "resume" candidates.
|
|
45
47
|
- `mcp__launch-secure__work_items_list({ assignee_email: "<git email>", status: "TODO" })` — your queued backlog.
|
|
@@ -48,6 +50,15 @@ Pull from four layers in parallel. Each is independently optional; a missing one
|
|
|
48
50
|
|
|
49
51
|
> Use the hosted `launch-secure` MCP by default. Only use `local-launch-secure` if the active course points there (see `/kit:course`). Don't ask for org/project slugs — the `.mcp.json` headers default to the current project.
|
|
50
52
|
|
|
53
|
+
**1e. Orbits / other worktrees** — the in-flight work that 1c structurally can't see. A feature usually lives in its own orbit (isolated worktree + forked DB), so the current tree being clean does NOT mean nothing is in flight. Enumerate + classify them:
|
|
54
|
+
- **Primary: `mcp__launch-orbit__orbit_check({ all: true })`** (if launch-orbit is wired) — sweeps every orbit registered for THIS repo and returns, per orbit, a `recommendation` + two verdicts. This replaces the hand-rolled dirty/ahead/merged math — orbit_check already runs `git status`, the merged-vs-base check, AND the DB/data drop-safety checks, then tells you which orbits still hold work vs. are safe to drop:
|
|
55
|
+
- **not `dropSafe`** (uncommitted changes or commits ahead of base) → a **🔧 Resume in-flight** candidate. Pull the "N dirty, M ahead" label from its `git.clean` / `git.merged` check summaries, and use the report's `actions[]` for what's needed to land it.
|
|
56
|
+
- **`dropSafe`** (merged + clean, nothing to lose) → a **🪦 Stale / dead** candidate ("orbit ‹slug› merged + clean — safe to drop"). Note: `healthy:false` here is usually just the DB being down, not real work — don't promote it to resume on that alone.
|
|
57
|
+
- `mcp__launch-orbit__orbit_list` — use for the stable slug + `age` metadata to label candidates and sort 🔧 Resume in-flight (freshest first).
|
|
58
|
+
- `git worktree list` — also surfaces **bare** worktrees created outside launch-orbit (these won't appear in orbit_check/orbit_list). For each such non-orbit worktree other than the current one, fall back to raw git: `git -C <path> status --porcelain` (uncommitted, filter the generated noise from 1c) + `git -C <path> rev-list --count <base>..HEAD` (commits ahead). Same rule: dirty/ahead → resume; clean + 0-ahead → stale/dead.
|
|
59
|
+
- `orbit_check` / `orbit_list` span **all** projects on the machine — keep only orbits whose `path` is under this repo's root.
|
|
60
|
+
- **Fallback (launch-orbit not wired):** classify every worktree from `git worktree list` with the raw-git pair above — still works, just without the DB/data drop-safety signal.
|
|
61
|
+
|
|
51
62
|
## Step 2 — Reconcile + flag drift
|
|
52
63
|
|
|
53
64
|
Before showing anything, cross-check the sources against each other. This is the honesty pass — the inverse of what `/kit:standup` does for shipped work.
|
|
@@ -65,7 +76,7 @@ Pool every candidate from the reconciled sources (HANDOFF "What's next" + "Open
|
|
|
65
76
|
|
|
66
77
|
Then sort each remaining item into **one** bucket — most-specific bucket wins, in this precedence:
|
|
67
78
|
|
|
68
|
-
1. **🔧 Resume in-flight** — uncommitted local work + `IN_PROGRESS` work items. The strongest "continue what you started" signal; always wins if present.
|
|
79
|
+
1. **🔧 Resume in-flight** — uncommitted local work in the current tree (1c) + **dirty/ahead orbits & other worktrees (1e)** + `IN_PROGRESS` work items. The strongest "continue what you started" signal; always wins if present. Sort orbit/worktree candidates by recency (`age` / most-recently-touched first) — the freshest dirty orbit is usually what you were last on, even if it isn't the current tree.
|
|
69
80
|
2. **👀 Review / PR feedback** — work waiting on *your* action on something already in motion: open PRs you authored or are assigned to review, unresolved review comments, `needs-review`/`changes-requested` tags. Time-sensitive (someone's blocked on you), so it outranks fresh work.
|
|
70
81
|
3. **🐛 Bugs / broken** — defects: work items of type `bug` or tagged `bug`/`defect`/`regression`, and `broken`/`failing`/`revert`/`not patched` follow-up flags from commit bodies. Distinct from feature backlog regardless of priority.
|
|
71
82
|
4. **🔥 High-priority** — items the project marks urgent: a work-item priority/severity field at its top value, tags like `p0`/`p1`/`urgent`, **and** everything in HANDOFF "What's next" (human-curated = high priority by definition), minus anything Step 2 flagged stale.
|
|
@@ -87,6 +98,7 @@ One compact block: orientation header, then each **non-empty** bucket with up to
|
|
|
87
98
|
|
|
88
99
|
🔧 Resume in-flight (N)
|
|
89
100
|
- <item — handle/branch/file · short why>
|
|
101
|
+
- <orbit ‹slug› [branch] · N dirty, M ahead · touched <age>>
|
|
90
102
|
…up to 5… (+N more)
|
|
91
103
|
|
|
92
104
|
👀 Review / PR feedback (N)
|
|
@@ -131,7 +143,10 @@ Readable in under a minute. The 🚧 Blocked list is informational only — neve
|
|
|
131
143
|
Once a specific item is chosen:
|
|
132
144
|
|
|
133
145
|
1. **Produce its kickoff prompt.** If the chosen slice matches the one HANDOFF already wrote a "Kickoff prompt for the next session" for, **reuse that verbatim** in a fenced block. Otherwise synthesize one in the **same shape** handoff uses (Starting **‹item — Title›** · read CLAUDE.md + HANDOFF + relevant Briefs · then the 1–6 design-shape questions: Surface, Fields/inputs, Eligibility, Data path, feature-specific, Out-of-scope). Make it specific to the chosen slice, not generic.
|
|
134
|
-
2. **Offer the setup as a confirmed next step — do not do it unprompted.**
|
|
146
|
+
2. **Offer the setup as a confirmed next step — do not do it unprompted.** Tailor the offer to what was picked:
|
|
147
|
+
- **Picked an existing orbit/worktree (1e candidate):** it already exists — offer to switch into it (`mcp__launch-orbit__orbit_switch` to activate, then work in its `path`), NOT to create a branch. Never re-create an orbit that's already live.
|
|
148
|
+
- **Picked a fresh slice:** offer to branch off the base (`git checkout <base> && git pull && git checkout -b <slice-branch>`) **or** create an isolated orbit (`/kit:orbit`) if the slice wants a forked DB.
|
|
149
|
+
Wait for an explicit "yes" / branch name before running any git or orbit command — this is a state mutation and falls under the per-action confirmation rule.
|
|
135
150
|
3. If the chosen slice is a `TODO` work item and the user confirms starting it, offer (don't auto-run) to move it to `IN_PROGRESS` via `mcp__launch-secure__work_item_move` / `work_item_update`.
|
|
136
151
|
|
|
137
152
|
## Edge cases
|
|
@@ -141,6 +156,7 @@ Once a specific item is chosen:
|
|
|
141
156
|
- **Clean tree, nothing in flight, empty backlog** — say so plainly ("nothing in flight; HANDOFF's next is ‹X›") and just present HANDOFF's "What's next" as the candidates. Don't invent work.
|
|
142
157
|
- **HANDOFF wildly out of sync with git** (many stale "next" items already shipped) — lead with the drift block and suggest the previous session likely forgot `/kit:handoff`; offer to reconstruct current-state from git, but don't write HANDOFF here (that's `/kit:handoff`'s job).
|
|
143
158
|
- **Mid-branch resume** (current branch already ahead of base) — surface "continue this branch" at the top of the 🔧 Resume in-flight list, and skip the branch-creation offer in Step 6 unless the user picks a different item.
|
|
159
|
+
- **In-flight work lives in an orbit, not the current tree** (the common case — current tree clean but a dirty/ahead orbit exists per 1e) — do NOT report "nothing in flight." Lead 🔧 Resume in-flight with the freshest dirty/ahead orbit, and in Step 6 offer `orbit_switch` into it rather than a new branch.
|
|
144
160
|
- **Everything lands in one bucket** (e.g. all candidates are stale) — that's fine; show the single list and skip Stage A (no choice to make), going straight to picking an item.
|
|
145
161
|
|
|
146
162
|
## Notes for the assistant
|
|
@@ -13,17 +13,19 @@ Parse `$ARGUMENTS` — first token is the subcommand:
|
|
|
13
13
|
- **switch <branch>** — activate an orbit: atomically repoint the `current` anchor at its worktree, then operate under that anchor.
|
|
14
14
|
- **deactivate** — clear the `current` anchor so work targets the main checkout again.
|
|
15
15
|
- **doctor** — surface registry inconsistencies.
|
|
16
|
-
- **check <branch>
|
|
16
|
+
- **check <branch>** — `[--all] [--deep] [--base=<ref>] [--json]` — orbit **cleanliness** report: DROP-SAFE + HEALTHY verdicts, recommendation + concrete actions. (Different from `check-merge` — this is "is it safe to delete / sane to keep working in", not "will it merge".)
|
|
17
|
+
- **check-merge <branch> --target=<target-branch>** — run pre-merge gates WITHOUT merging.
|
|
17
18
|
- **merge <branch> --target=<target-branch>** — `[--cleanup=full|none] [--skip-gate=<id>,…]` — run gates + merge.
|
|
18
|
-
- **drop <branch>** — `[--no-backup]` — tear down (backup, drop DB, remove worktree, deregister).
|
|
19
|
+
- **drop <branch>** — `[--no-backup] [--force]` — tear down (drop-safety guard, then backup, drop DB, remove worktree, deregister).
|
|
19
20
|
|
|
20
21
|
Examples:
|
|
21
22
|
- `/kit:orbit create feat-channel-rename --profile=fe`
|
|
22
|
-
- `/kit:orbit check feat-channel-rename --
|
|
23
|
+
- `/kit:orbit check feat-channel-rename` · `/kit:orbit check --all`
|
|
24
|
+
- `/kit:orbit check-merge feat-channel-rename --target=implementation`
|
|
23
25
|
- `/kit:orbit merge feat-channel-rename --target=implementation`
|
|
24
26
|
- `/kit:orbit doctor`
|
|
25
27
|
|
|
26
|
-
## Preflight (runs on create / check / merge)
|
|
28
|
+
## Preflight (runs on create / check-merge / merge)
|
|
27
29
|
|
|
28
30
|
1. Confirm `mcp__launch-orbit__orbit_doctor` returns no critical inconsistencies for THIS project. If it does, surface them and stop — the operator probably wants `/kit:orbit drop <stale-branch>` first.
|
|
29
31
|
2. Confirm `orbit.json` exists at the repo root. If missing, tell the user to add one (point at the orbit docs) and stop.
|
|
@@ -61,7 +63,23 @@ Switching to a *different* orbit just repoints the anchor — re-run `nextAction
|
|
|
61
63
|
|
|
62
64
|
`mcp__launch-orbit__orbit_doctor` → list every issue with a one-line fix (e.g. `orphan-registration` → `/kit:orbit drop <branch>`).
|
|
63
65
|
|
|
64
|
-
### check
|
|
66
|
+
### check (cleanliness)
|
|
67
|
+
|
|
68
|
+
`mcp__launch-orbit__orbit_check(branch)` (or `all: true` to sweep this repo's orbits; `deep: true` adds the prisma schema-drift check) → render the two-verdict report:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
orbit feat-channel-rename [feat-channel-rename] DROP-SAFE: NO HEALTHY: YES
|
|
72
|
+
recommendation: KEEP — unmerged or uncommitted work would be lost on drop.
|
|
73
|
+
Drop-safety: ✗ uncommitted changes · ✗ 3 commits ahead · ✓ no orbit-only rows
|
|
74
|
+
Working-health: ✓ registry · ⚠ env drift · ✓ DB reachable · ✓ ports free
|
|
75
|
+
Actions:
|
|
76
|
+
→ Commit or stash the uncommitted changes …
|
|
77
|
+
→ Merge feat-channel-rename into implementation …
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Use it to answer "can I drop this / is it sane to keep working in" — e.g. before cleaning up stale orbits, or at session start. The report carries a `recommendation` + `actions[]`; surface those verbatim. DB checks skip gracefully if the cluster is down.
|
|
81
|
+
|
|
82
|
+
### check-merge (pre-merge gates)
|
|
65
83
|
|
|
66
84
|
`mcp__launch-orbit__orbit_check_mergeable(branch, target)` → render the gate report:
|
|
67
85
|
|
|
@@ -76,7 +94,7 @@ result: BLOCKED — fix build-lint then retry.
|
|
|
76
94
|
|
|
77
95
|
### merge
|
|
78
96
|
|
|
79
|
-
1. Run `check` first (same report). If `passed: false`, stop with the report — don't attempt the merge.
|
|
97
|
+
1. Run `check-merge` first (same report). If `passed: false`, stop with the report — don't attempt the merge.
|
|
80
98
|
2. If `passed: true`, call `mcp__launch-orbit__orbit_merge(branch, target, cleanup, skipGates)` and surface the result.
|
|
81
99
|
3. Default `cleanup: "full"` (orbit is dropped post-merge after pg_dump). The operator must pass `--cleanup=none` to keep the orbit around.
|
|
82
100
|
4. After a successful merge, recommend `/kit:standup` to surface the change in the next standup, and `/kit:diagram` if it touched the schema.
|
|
@@ -85,11 +103,13 @@ result: BLOCKED — fix build-lint then retry.
|
|
|
85
103
|
|
|
86
104
|
`mcp__launch-orbit__orbit_drop(branch, backup: !args.noBackup)` → idempotent. Surface `backups[]` paths.
|
|
87
105
|
|
|
106
|
+
**Drop-safety guard (default on).** `orbit_drop` runs the drop-safety checks first and THROWS if the orbit still has uncommitted/unmerged work or orbit-only DB data. When that happens, surface the blockers and DON'T pass `force: true` reflexively — relay the verdict, let the operator decide. Only pass `force: true` after they explicitly confirm "drop anyway, I'll lose that work". (Tip: run `/kit:orbit check <branch>` first to see the full report and the recommended actions.)
|
|
107
|
+
|
|
88
108
|
## Conventions encoded here
|
|
89
109
|
|
|
90
110
|
- **No bare `--target=master`.** Default merge target is `implementation` per project CLAUDE.md ("implementation branch is parent for all impl branches"). If the operator types `--target=master`, double-check by asking once.
|
|
91
111
|
- **No `--skip-gate` without a reason in the next message.** Surface a banner: `"Skipping gates: <ids>. Reason?"` — let the operator answer in chat; do NOT proceed silently.
|
|
92
|
-
- **Always run `check` before `merge`.** The skill enforces this — even if the operator runs `merge` directly, the skill runs `check` first.
|
|
112
|
+
- **Always run `check-merge` before `merge`.** The skill enforces this — even if the operator runs `merge` directly, the skill runs `check-merge` first.
|
|
93
113
|
- **Layer awareness.** Pre-flight always prints `detect_project_stack` layers so the operator sees parity (or absence) between chart layers and orbit gate scope.
|
|
94
114
|
|
|
95
115
|
## Constraints
|