@launchsecure/launch-kit 0.0.27 → 0.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/beacon/beacon.mjs +1003 -440
  2. package/dist/beacon/beacon.mjs.map +1 -1
  3. package/dist/beacon/beacon.umd.js +45 -24
  4. package/dist/beacon/beacon.umd.js.map +1 -1
  5. package/dist/beacon/types/capture/events.d.ts +20 -0
  6. package/dist/beacon/types/capture/events.d.ts.map +1 -0
  7. package/dist/beacon/types/element.d.ts +1 -0
  8. package/dist/beacon/types/element.d.ts.map +1 -1
  9. package/dist/beacon/types/index.d.ts +2 -1
  10. package/dist/beacon/types/index.d.ts.map +1 -1
  11. package/dist/beacon/types/monitor/dom.d.ts +13 -0
  12. package/dist/beacon/types/monitor/dom.d.ts.map +1 -0
  13. package/dist/beacon/types/monitor/index.d.ts +19 -0
  14. package/dist/beacon/types/monitor/index.d.ts.map +1 -0
  15. package/dist/beacon/types/monitor/network.d.ts +12 -0
  16. package/dist/beacon/types/monitor/network.d.ts.map +1 -0
  17. package/dist/beacon/types/monitor/transport.d.ts +27 -0
  18. package/dist/beacon/types/monitor/transport.d.ts.map +1 -0
  19. package/dist/beacon/types/monitor/types.d.ts +117 -0
  20. package/dist/beacon/types/monitor/types.d.ts.map +1 -0
  21. package/dist/beacon/types/types.d.ts +10 -0
  22. package/dist/beacon/types/types.d.ts.map +1 -1
  23. package/dist/beacon/types/ui/drawer.d.ts +3 -1
  24. package/dist/beacon/types/ui/drawer.d.ts.map +1 -1
  25. package/dist/beacon/types/ui/monitor-panel.d.ts +19 -0
  26. package/dist/beacon/types/ui/monitor-panel.d.ts.map +1 -0
  27. package/dist/server/beacon-monitor-entry.js +353 -0
  28. package/dist/server/cli.js +50 -2
  29. package/dist/server/council-entry.js +0 -0
  30. package/dist/server/course-entry.js +246 -0
  31. package/dist/server/fb-wizard.js +0 -0
  32. package/dist/server/init-entry.js +394 -64
  33. package/dist/server/orbit-entry.js +187 -24
  34. package/package.json +24 -23
  35. package/scaffolds/ls-marketplace/.claude-plugin/marketplace.json +15 -0
  36. package/scaffolds/ls-marketplace/plugins/ls/.claude-plugin/plugin.json +28 -0
  37. package/scaffolds/ls-marketplace/plugins/ls/commands/activate-beacon.md +216 -0
  38. package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-array.md +92 -0
  39. package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-clear.md +68 -0
  40. package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-pulse.md +80 -0
  41. package/scaffolds/ls-marketplace/plugins/ls/commands/beacon-scan.md +62 -0
  42. package/scaffolds/ls-marketplace/plugins/ls/commands/show-mcp-status.md +109 -0
  43. package/scaffolds/ls-marketplace/plugins/ls/commands/standup.md +177 -0
  44. package/scaffolds/migrate-safety/scripts/migrate-with-backup.sh +0 -0
  45. package/scaffolds/recall-hook/scripts/ensure-recall.sh +69 -0
@@ -923,6 +923,18 @@ function detectPmFromLock(worktreePath) {
923
923
  if ((0, import_node_fs7.existsSync)((0, import_node_path5.join)(worktreePath, "bun.lockb"))) return "bun";
924
924
  return "npm";
925
925
  }
926
+ function combineOutput(stdout, stderr) {
927
+ const out = (stdout ?? "").trimEnd();
928
+ const err2 = (stderr ?? "").trimEnd();
929
+ if (!out && !err2) return "";
930
+ if (!err2) return out + "\n";
931
+ if (!out) return err2 + "\n";
932
+ return `${out}
933
+
934
+ ---STDERR---
935
+ ${err2}
936
+ `;
937
+ }
926
938
  function saveLog(slug, label, content) {
927
939
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
928
940
  const path = (0, import_node_path5.join)(LOG_DIR, `${ts}_${slug}_${label}.log`);
@@ -1007,12 +1019,31 @@ var init_build_lint = __esm({
1007
1019
  env: spawnEnv
1008
1020
  });
1009
1021
  if (installRes.status !== 0) {
1010
- const log = saveLog(ctx.entry.slug, "install", installRes.stderr ?? installRes.stdout ?? "");
1022
+ const log = saveLog(ctx.entry.slug, "install", combineOutput(installRes.stdout, installRes.stderr));
1011
1023
  blockers.push("dependency install failed in ephemeral worktree");
1012
1024
  artifacts.install_log = log;
1013
1025
  return finalize(checkPath, ctx, blockers, artifacts);
1014
1026
  }
1015
1027
  }
1028
+ if ((0, import_node_fs7.existsSync)((0, import_node_path5.join)(checkPath, "prisma", "schema.prisma"))) {
1029
+ const prismaCmd = "npx prisma generate";
1030
+ ctx.logger.step(`[build-lint] ${prismaCmd}`);
1031
+ const prismaRes = (0, import_node_child_process5.spawnSync)("bash", ["-c", `cd ${shellQuote2(checkPath)} && ${prismaCmd}`], {
1032
+ stdio: ["ignore", "pipe", "pipe"],
1033
+ encoding: "utf-8",
1034
+ env: spawnEnv
1035
+ });
1036
+ if (prismaRes.status !== 0) {
1037
+ const log = saveLog(
1038
+ ctx.entry.slug,
1039
+ "prisma-generate",
1040
+ combineOutput(prismaRes.stdout, prismaRes.stderr)
1041
+ );
1042
+ blockers.push(`prisma generate failed (exit ${prismaRes.status}) \u2014 tsc will not be reliable`);
1043
+ artifacts["prisma-generate_log"] = log;
1044
+ return finalize(checkPath, ctx, blockers, artifacts);
1045
+ }
1046
+ }
1016
1047
  const tscMode = config.tsc ?? true;
1017
1048
  if (tscMode !== false) {
1018
1049
  const tscCmd = typeof tscMode === "string" ? tscMode : "npx tsc --noEmit";
@@ -1023,7 +1054,7 @@ var init_build_lint = __esm({
1023
1054
  env: spawnEnv
1024
1055
  });
1025
1056
  if (tscRes.status !== 0) {
1026
- const log = saveLog(ctx.entry.slug, "tsc", tscRes.stdout ?? tscRes.stderr ?? "");
1057
+ const log = saveLog(ctx.entry.slug, "tsc", combineOutput(tscRes.stdout, tscRes.stderr));
1027
1058
  blockers.push(`tsc reported errors (exit ${tscRes.status})`);
1028
1059
  artifacts.tsc_log = log;
1029
1060
  }
@@ -1039,7 +1070,7 @@ var init_build_lint = __esm({
1039
1070
  env: spawnEnv
1040
1071
  });
1041
1072
  if (lintRes.status !== 0) {
1042
- const log = saveLog(ctx.entry.slug, "lint", lintRes.stdout ?? lintRes.stderr ?? "");
1073
+ const log = saveLog(ctx.entry.slug, "lint", combineOutput(lintRes.stdout, lintRes.stderr));
1043
1074
  blockers.push(`lint reported errors (exit ${lintRes.status})`);
1044
1075
  artifacts.lint_log = log;
1045
1076
  }
@@ -1058,7 +1089,7 @@ var init_build_lint = __esm({
1058
1089
  env: spawnEnv
1059
1090
  });
1060
1091
  if (buildRes.status !== 0) {
1061
- const log = saveLog(ctx.entry.slug, "build", buildRes.stdout ?? buildRes.stderr ?? "");
1092
+ const log = saveLog(ctx.entry.slug, "build", combineOutput(buildRes.stdout, buildRes.stderr));
1062
1093
  blockers.push(`build reported errors (exit ${buildRes.status})`);
1063
1094
  artifacts.build_log = log;
1064
1095
  }
@@ -1234,11 +1265,32 @@ var init_gate_registry = __esm({
1234
1265
  });
1235
1266
 
1236
1267
  // src/server/orbit/gate-runner.ts
1237
- async function runMergeGates(manifest, ctx) {
1268
+ async function runMergeGates(manifest, ctx, options = {}) {
1238
1269
  const refs = manifest.gates?.merge ?? DEFAULT_GATES;
1270
+ const skip = new Set(options.skipGates ?? []);
1271
+ if (skip.size > 0) {
1272
+ const declared = new Set(refs.map((r) => r.adapter));
1273
+ for (const id of skip) {
1274
+ if (!declared.has(id)) {
1275
+ ctx.logger.warn(
1276
+ `--skip-gate "${id}" does not match any declared gate (declared: ${[...declared].join(", ")})`
1277
+ );
1278
+ }
1279
+ }
1280
+ const blockedRequired = refs.filter((r) => r.required && skip.has(r.adapter)).map((r) => r.adapter);
1281
+ if (blockedRequired.length > 0) {
1282
+ throw new Error(
1283
+ `cannot skip required gate(s): ${blockedRequired.join(", ")}. Remove "required": true from orbit.json if you really need to bypass.`
1284
+ );
1285
+ }
1286
+ }
1239
1287
  const start = Date.now();
1240
1288
  const results = [];
1241
1289
  for (const ref of refs) {
1290
+ if (skip.has(ref.adapter)) {
1291
+ ctx.logger.info(`[gate] skip ${ref.adapter} (--skip-gate)`);
1292
+ continue;
1293
+ }
1242
1294
  const gate = getMergeGate(ref.adapter);
1243
1295
  const detect = await gate.detect(ctx);
1244
1296
  if (!detect.ok) {
@@ -1288,7 +1340,7 @@ var init_gate_runner = __esm({
1288
1340
  "src/server/orbit/gate-runner.ts"() {
1289
1341
  "use strict";
1290
1342
  init_gate_registry();
1291
- DEFAULT_GATES = [{ adapter: "builtin/mergeability" }];
1343
+ DEFAULT_GATES = [{ adapter: "builtin/mergeability", required: true }];
1292
1344
  }
1293
1345
  });
1294
1346
 
@@ -1321,14 +1373,50 @@ function validateManifest(raw, sourcePath) {
1321
1373
  const worktree = validateWorktreeRef(raw.worktree, sourcePath);
1322
1374
  const resources = validateResourcesArray(raw.resources, sourcePath);
1323
1375
  const gates = validateGates(raw.gates, sourcePath);
1376
+ const profiles = validateProfiles(raw.profiles, resources, sourcePath);
1324
1377
  return {
1325
1378
  version: 1,
1326
1379
  ...envFile !== void 0 ? { envFile } : {},
1327
1380
  worktree,
1328
1381
  resources,
1329
- ...gates !== void 0 ? { gates } : {}
1382
+ ...gates !== void 0 ? { gates } : {},
1383
+ ...profiles !== void 0 ? { profiles } : {}
1330
1384
  };
1331
1385
  }
1386
+ function validateProfiles(raw, resources, sourcePath) {
1387
+ if (raw === void 0) return void 0;
1388
+ if (!isRecord(raw)) throw err("profiles must be an object if present", sourcePath);
1389
+ const known = new Set(resources.map((r) => r.name));
1390
+ const out = {};
1391
+ for (const [name, value] of Object.entries(raw)) {
1392
+ if (!name) throw err("profiles keys must be non-empty strings", sourcePath);
1393
+ if (!isRecord(value)) throw err(`profiles.${name} must be an object`, sourcePath);
1394
+ if (!Array.isArray(value.resources)) {
1395
+ throw err(`profiles.${name}.resources must be an array of resource names`, sourcePath);
1396
+ }
1397
+ const list2 = [];
1398
+ const seen = /* @__PURE__ */ new Set();
1399
+ for (let i = 0; i < value.resources.length; i++) {
1400
+ const r = value.resources[i];
1401
+ if (typeof r !== "string" || !r) {
1402
+ throw err(`profiles.${name}.resources[${i}] must be a non-empty string`, sourcePath);
1403
+ }
1404
+ if (!known.has(r)) {
1405
+ throw err(
1406
+ `profiles.${name}.resources[${i}] "${r}" does not match any resource name (known: ${[...known].join(", ") || "(none)"})`,
1407
+ sourcePath
1408
+ );
1409
+ }
1410
+ if (seen.has(r)) {
1411
+ throw err(`profiles.${name}.resources[${i}] "${r}" duplicated`, sourcePath);
1412
+ }
1413
+ seen.add(r);
1414
+ list2.push(r);
1415
+ }
1416
+ out[name] = { resources: list2 };
1417
+ }
1418
+ return Object.keys(out).length > 0 ? out : void 0;
1419
+ }
1332
1420
  function validateGates(raw, sourcePath) {
1333
1421
  if (raw === void 0) return void 0;
1334
1422
  if (!isRecord(raw)) throw err("gates must be an object if present", sourcePath);
@@ -1351,7 +1439,15 @@ function validateGates(raw, sourcePath) {
1351
1439
  if (config !== void 0 && !isRecord(config)) {
1352
1440
  throw err(`gates.merge[${i}].config must be an object if present`, sourcePath);
1353
1441
  }
1354
- list2.push({ adapter: g.adapter, config });
1442
+ const required = g.required;
1443
+ if (required !== void 0 && typeof required !== "boolean") {
1444
+ throw err(`gates.merge[${i}].required must be a boolean if present`, sourcePath);
1445
+ }
1446
+ list2.push({
1447
+ adapter: g.adapter,
1448
+ config,
1449
+ ...required ? { required: true } : {}
1450
+ });
1355
1451
  }
1356
1452
  out.merge = list2;
1357
1453
  }
@@ -1453,6 +1549,12 @@ async function create(opts) {
1453
1549
  if (lookupWorktree(slug)) {
1454
1550
  throw new Error(`worktree "${slug}" already registered. Run \`launch-orbit drop ${slug}\` first.`);
1455
1551
  }
1552
+ const effectiveResources = resolveProfile(manifest, opts.profile);
1553
+ if (opts.profile) {
1554
+ opts.logger.info(
1555
+ `profile "${opts.profile}" \u2192 resources: ${effectiveResources.map((r) => r.name).join(", ") || "(none)"}`
1556
+ );
1557
+ }
1456
1558
  const worktreeAdapter = getWorktreeAdapter(manifest.worktree.adapter);
1457
1559
  const wtDetect = await worktreeAdapter.detect(ctx);
1458
1560
  if (!wtDetect.ok) throw new Error(`worktree adapter "${worktreeAdapter.id}" unavailable: ${wtDetect.reason}`);
@@ -1474,7 +1576,7 @@ async function create(opts) {
1474
1576
  const resourceStates = {};
1475
1577
  const rewriteFns = {};
1476
1578
  try {
1477
- for (const ref of manifest.resources) {
1579
+ for (const ref of effectiveResources) {
1478
1580
  const adapter = getResourceAdapter(ref.adapter);
1479
1581
  const detect = await adapter.detect(ctx);
1480
1582
  if (!detect.ok) throw new Error(`resource adapter "${adapter.id}" unavailable: ${detect.reason}`);
@@ -1622,7 +1724,7 @@ async function checkMergeable(opts) {
1622
1724
  logger: opts.logger
1623
1725
  };
1624
1726
  opts.logger.step(`running merge gates against target "${opts.target}"`);
1625
- const agg = await runMergeGates(manifest, gateCtx);
1727
+ const agg = await runMergeGates(manifest, gateCtx, { skipGates: opts.skipGates });
1626
1728
  opts.logger.info(formatGateResults(agg.results));
1627
1729
  return {
1628
1730
  slug,
@@ -1639,6 +1741,7 @@ async function merge(opts) {
1639
1741
  const check = await checkMergeable({
1640
1742
  branch: opts.branch,
1641
1743
  target: opts.target,
1744
+ skipGates: opts.skipGates,
1642
1745
  projectRoot: opts.projectRoot,
1643
1746
  logger: opts.logger
1644
1747
  });
@@ -1750,6 +1853,22 @@ function findWorktreeFor(projectRoot, branch) {
1750
1853
  return null;
1751
1854
  }
1752
1855
  }
1856
+ function resolveProfile(manifest, profile) {
1857
+ if (!profile) return manifest.resources;
1858
+ const ref = manifest.profiles?.[profile];
1859
+ if (!ref) {
1860
+ const known = Object.keys(manifest.profiles ?? {});
1861
+ throw new Error(
1862
+ `profile "${profile}" not declared in orbit.json. Available: ${known.length > 0 ? known.join(", ") : "(none \u2014 add a `profiles` block to orbit.json)"}`
1863
+ );
1864
+ }
1865
+ const byName = new Map(manifest.resources.map((r) => [r.name, r]));
1866
+ return ref.resources.map((name) => {
1867
+ const r = byName.get(name);
1868
+ if (!r) throw new Error(`profile "${profile}" references unknown resource "${name}"`);
1869
+ return r;
1870
+ });
1871
+ }
1753
1872
  function loadManifestOrThrow(projectRoot) {
1754
1873
  if (!manifestExists(projectRoot)) {
1755
1874
  throw new Error(
@@ -1832,8 +1951,9 @@ async function handleTool(name, args) {
1832
1951
  case "orbit.create": {
1833
1952
  const branch = String(args.branch ?? "");
1834
1953
  const baseRef = args.baseRef;
1954
+ const profile = args.profile;
1835
1955
  if (!branch) return text({ error: "branch is required" });
1836
- const result = await create({ branch, baseRef, projectRoot, logger });
1956
+ const result = await create({ branch, baseRef, profile, projectRoot, logger });
1837
1957
  return text(result);
1838
1958
  }
1839
1959
  case "orbit.list": {
@@ -1865,7 +1985,9 @@ async function handleTool(name, args) {
1865
1985
  const target = String(args.target ?? "");
1866
1986
  if (!branch) return text({ error: "branch is required" });
1867
1987
  if (!target) return text({ error: "target is required" });
1868
- const result = await checkMergeable({ branch, target, projectRoot, logger });
1988
+ const skipGates = parseSkipGates(args.skipGates);
1989
+ if (skipGates instanceof Error) return text({ error: skipGates.message });
1990
+ const result = await checkMergeable({ branch, target, skipGates, projectRoot, logger });
1869
1991
  return text(result);
1870
1992
  }
1871
1993
  case "orbit.merge": {
@@ -1878,13 +2000,25 @@ async function handleTool(name, args) {
1878
2000
  if (cleanup === void 0) {
1879
2001
  return text({ error: `cleanup must be "full" or "none" (got ${JSON.stringify(cleanupArg)})` });
1880
2002
  }
1881
- const result = await merge({ branch, target, cleanup, projectRoot, logger });
2003
+ const skipGates = parseSkipGates(args.skipGates);
2004
+ if (skipGates instanceof Error) return text({ error: skipGates.message });
2005
+ const result = await merge({ branch, target, cleanup, skipGates, projectRoot, logger });
1882
2006
  return text(result);
1883
2007
  }
1884
2008
  default:
1885
2009
  return text({ error: `unknown tool: ${name}` });
1886
2010
  }
1887
2011
  }
2012
+ function parseSkipGates(raw) {
2013
+ if (raw === void 0 || raw === null) return void 0;
2014
+ if (!Array.isArray(raw)) return new Error("skipGates must be an array of adapter ids");
2015
+ const out = [];
2016
+ for (const v of raw) {
2017
+ if (typeof v !== "string" || !v) return new Error("skipGates entries must be non-empty strings");
2018
+ out.push(v);
2019
+ }
2020
+ return out.length > 0 ? out : void 0;
2021
+ }
1888
2022
  function text(payload) {
1889
2023
  const t = typeof payload === "string" ? payload : JSON.stringify(payload, null, 2);
1890
2024
  return { content: [{ type: "text", text: t }] };
@@ -1973,12 +2107,16 @@ var init_orbit_mcp = __esm({
1973
2107
  TOOLS = [
1974
2108
  {
1975
2109
  name: "orbit.create",
1976
- description: "Create an isolated worktree environment for a branch. Forks the resources declared in orbit.json (database, ports, etc.) so the new worktree is fully independent from the main one.\n\nReturns: { slug, branch, path, envFile, durationMs, resources, nextAction }. The `path` is the absolute worktree directory \u2014 the caller should `cd` into it for subsequent commands. `nextAction` contains the exact shell command to do so.",
2110
+ description: "Create an isolated worktree environment for a branch. Forks the resources declared in orbit.json (database, ports, etc.) so the new worktree is fully independent from the main one.\n\nPass `profile` to fork only a named subset of resources from orbit.json's `profiles` block \u2014 e.g. a frontend-only task can use a `fe` profile that skips the DB clone. Without `profile`, all manifest resources are forked.\n\nReturns: { slug, branch, path, envFile, durationMs, resources, nextAction }. The `path` is the absolute worktree directory \u2014 the caller should `cd` into it for subsequent commands. `nextAction` contains the exact shell command to do so.",
1977
2111
  inputSchema: {
1978
2112
  type: "object",
1979
2113
  properties: {
1980
2114
  branch: { type: "string", description: "Git branch name to create (also the slug source)." },
1981
- baseRef: { type: "string", description: "Optional ref to branch from (default: current HEAD)." }
2115
+ baseRef: { type: "string", description: "Optional ref to branch from (default: current HEAD)." },
2116
+ profile: {
2117
+ type: "string",
2118
+ description: "Optional profile name from orbit.json's `profiles`. Scopes which resources are forked. Omit to fork all resources."
2119
+ }
1982
2120
  },
1983
2121
  required: ["branch"]
1984
2122
  }
@@ -2018,19 +2156,24 @@ var init_orbit_mcp = __esm({
2018
2156
  },
2019
2157
  {
2020
2158
  name: "orbit.check_mergeable",
2021
- description: "Run all configured pre-merge gates against an orbit + target branch WITHOUT actually merging. Used by agents to verify before calling orbit.merge. Default gates: builtin/mergeability (textual conflict check) and any others declared in orbit.json's `gates.merge`.\n\nReturns: { slug, branch, target, passed, gates: [{ gateId, ok, blockers, artifacts }], durationMs }. If `passed` is false, `gates` contains per-gate detail including human-readable blocker lines and paths to artifact logs (tsc output, lint output, etc.).",
2159
+ description: 'Run all configured pre-merge gates against an orbit + target branch WITHOUT actually merging. Used by agents to verify before calling orbit.merge. Default gates: builtin/mergeability (textual conflict check) and any others declared in orbit.json\'s `gates.merge`.\n\nPass `skipGates: ["builtin/build-lint"]` to bypass specific gates by adapter id. Gates marked `required: true` in orbit.json reject skipping and throw \u2014 typically used on the textual conflict check.\n\nReturns: { slug, branch, target, passed, gates: [{ gateId, ok, blockers, artifacts }], durationMs }. If `passed` is false, `gates` contains per-gate detail including human-readable blocker lines and paths to artifact logs (tsc output, lint output, etc.).',
2022
2160
  inputSchema: {
2023
2161
  type: "object",
2024
2162
  properties: {
2025
2163
  branch: { type: "string", description: "The orbit's branch (slug-able)." },
2026
- target: { type: "string", description: "Branch we'd merge INTO." }
2164
+ target: { type: "string", description: "Branch we'd merge INTO." },
2165
+ skipGates: {
2166
+ type: "array",
2167
+ items: { type: "string" },
2168
+ description: 'Adapter ids to skip (e.g. ["builtin/build-lint"]). Throws if any id has `required: true` in orbit.json. Warns on unknown ids without failing.'
2169
+ }
2027
2170
  },
2028
2171
  required: ["branch", "target"]
2029
2172
  }
2030
2173
  },
2031
2174
  {
2032
2175
  name: "orbit.merge",
2033
- description: 'Run gates, and if all pass, perform the merge into the target branch. Default cleanup behavior is `full` \u2014 after a successful merge, the orbit is dropped (DB pg_dump\'d, then DROP DATABASE; worktree removed; branch deleted). Use cleanup="none" to keep the orbit around post-merge.\n\nReturns: { slug, branch, target, merged, mergeSha, gates, cleanedUp, backupPath, durationMs, blockedBy }. If merged is false, blockedBy lists what stopped the merge (gate failures or git errors).',
2176
+ description: 'Run gates, and if all pass, perform the merge into the target branch. Default cleanup behavior is `full` \u2014 after a successful merge, the orbit is dropped (DB pg_dump\'d, then DROP DATABASE; worktree removed; branch deleted). Use cleanup="none" to keep the orbit around post-merge.\n\nPass `skipGates: ["builtin/build-lint"]` to bypass specific gates by adapter id. Gates marked `required: true` in orbit.json reject skipping and throw.\n\nReturns: { slug, branch, target, merged, mergeSha, gates, cleanedUp, backupPath, durationMs, blockedBy }. If merged is false, blockedBy lists what stopped the merge (gate failures or git errors).',
2034
2177
  inputSchema: {
2035
2178
  type: "object",
2036
2179
  properties: {
@@ -2040,6 +2183,11 @@ var init_orbit_mcp = __esm({
2040
2183
  type: "string",
2041
2184
  enum: ["full", "none"],
2042
2185
  description: "Post-merge action. `full` drops the orbit; `none` leaves it intact."
2186
+ },
2187
+ skipGates: {
2188
+ type: "array",
2189
+ items: { type: "string" },
2190
+ description: 'Adapter ids to skip (e.g. ["builtin/build-lint"]). Throws if any id has `required: true` in orbit.json. Warns on unknown ids without failing.'
2043
2191
  }
2044
2192
  },
2045
2193
  required: ["branch", "target"]
@@ -2056,13 +2204,18 @@ init_orchestrator();
2056
2204
  init_logger();
2057
2205
  function parseFlags(argv) {
2058
2206
  const positional = [];
2059
- const flags = { emitCd: false, noBackup: false };
2207
+ const flags = { emitCd: false, noBackup: false, skipGates: [] };
2060
2208
  for (let i = 0; i < argv.length; i++) {
2061
2209
  const a = argv[i];
2062
2210
  if (a === "--emit-cd") flags.emitCd = true;
2063
2211
  else if (a === "--no-backup") flags.noBackup = true;
2064
2212
  else if (a === "--base-ref") flags.baseRef = argv[++i];
2065
- else if (a === "--cleanup") {
2213
+ else if (a === "--profile") flags.profile = argv[++i];
2214
+ else if (a === "--skip-gate") {
2215
+ const id = argv[++i];
2216
+ if (!id) die("--skip-gate requires an adapter id (e.g. --skip-gate builtin/build-lint)");
2217
+ flags.skipGates.push(id);
2218
+ } else if (a === "--cleanup") {
2066
2219
  const v = argv[++i];
2067
2220
  if (v !== "full" && v !== "none") {
2068
2221
  process.stderr.write(`[launch-orbit] --cleanup must be "full" or "none" (got "${v}")
@@ -2097,10 +2250,11 @@ async function main() {
2097
2250
  }
2098
2251
  case "create": {
2099
2252
  const branch = positional[0];
2100
- if (!branch) die("usage: launch-orbit create <branch> [--base-ref <ref>] [--emit-cd]");
2253
+ if (!branch) die("usage: launch-orbit create <branch> [--base-ref <ref>] [--profile <name>] [--emit-cd]");
2101
2254
  const result = await create({
2102
2255
  branch,
2103
2256
  baseRef: flags.baseRef,
2257
+ profile: flags.profile,
2104
2258
  projectRoot,
2105
2259
  logger
2106
2260
  });
@@ -2169,11 +2323,12 @@ ${JSON.stringify(result, null, 2)}
2169
2323
  const branch = positional[0];
2170
2324
  const target = positional[1];
2171
2325
  if (!branch || !target) {
2172
- die("usage: launch-orbit check-merge <branch> <target>");
2326
+ die("usage: launch-orbit check-merge <branch> <target> [--skip-gate <id> ...]");
2173
2327
  }
2174
2328
  const result = await checkMergeable({
2175
2329
  branch,
2176
2330
  target,
2331
+ skipGates: flags.skipGates.length > 0 ? flags.skipGates : void 0,
2177
2332
  projectRoot,
2178
2333
  logger
2179
2334
  });
@@ -2187,12 +2342,13 @@ ${JSON.stringify(result, null, 2)}
2187
2342
  const branch = positional[0];
2188
2343
  const target = positional[1];
2189
2344
  if (!branch || !target) {
2190
- die("usage: launch-orbit merge <branch> <target> [--cleanup full|none]");
2345
+ die("usage: launch-orbit merge <branch> <target> [--cleanup full|none] [--skip-gate <id> ...]");
2191
2346
  }
2192
2347
  const result = await merge({
2193
2348
  branch,
2194
2349
  target,
2195
2350
  cleanup: flags.cleanup ?? "full",
2351
+ skipGates: flags.skipGates.length > 0 ? flags.skipGates : void 0,
2196
2352
  projectRoot,
2197
2353
  logger
2198
2354
  });
@@ -2255,9 +2411,16 @@ function runInit(projectRoot) {
2255
2411
  gates: {
2256
2412
  merge: [
2257
2413
  { adapter: "builtin/clean-tree" },
2258
- { adapter: "builtin/mergeability" },
2414
+ // `required: true` blocks --skip-gate on this one — silently merging a
2415
+ // textual conflict is the obvious footgun. Drop the flag if you really
2416
+ // need to bypass.
2417
+ { adapter: "builtin/mergeability", required: true },
2259
2418
  { adapter: "builtin/build-lint" }
2260
2419
  ]
2420
+ },
2421
+ profiles: {
2422
+ fe: { resources: ["ports"] },
2423
+ full: { resources: ["db", "ports"] }
2261
2424
  }
2262
2425
  };
2263
2426
  (0, import_node_fs11.writeFileSync)(path, JSON.stringify(starter, null, 2) + "\n", "utf-8");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@launchsecure/launch-kit",
3
- "version": "0.0.27",
3
+ "version": "0.0.28",
4
4
  "description": "LaunchSecure toolkit — launch-pod (pipeline), launch-chart (project graph MCP), launch-deck (visual playground MCP), launch-kit-beacon (feedback Web Component), launch-recall (file-watcher backup).",
5
5
  "license": "MIT",
6
6
  "author": "LaunchSecure - AutomateWithUs",
@@ -52,24 +52,9 @@
52
52
  "launch-chart": "./dist/server/graph-mcp-entry.js",
53
53
  "launch-deck": "./dist/server/deck-mcp-entry.js",
54
54
  "launch-recall": "./dist/server/recall-entry.js",
55
- "launch-orbit": "./dist/server/orbit-entry.js"
56
- },
57
- "scripts": {
58
- "build": "pnpm build:client && pnpm build:chart-client && pnpm build:deck-client && pnpm build:council-client && pnpm build:beacon && pnpm build:server",
59
- "build:beacon": "vite build --config vite.beacon.config.ts && tsc -p tsconfig.beacon.json --emitDeclarationOnly --outDir dist/beacon/types",
60
- "test:beacon": "vitest run --config vite.beacon.config.ts",
61
- "test:radar": "vitest run --config vite.radar.config.ts",
62
- "test:chart": "vitest run --config vite.chart.test.config.ts",
63
- "build:deck-client": "vite build --config vite.deck.config.ts",
64
- "build:council-client": "vite build --config vite.council.config.ts",
65
- "build:client": "vite build",
66
- "build:chart-client": "vite build --config vite.chart.config.ts",
67
- "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/parse-worker-entry.ts --bundle --platform=node --target=node18 --outdir=dist/server --external:node-pty --external:ws --external:typescript --external:web-tree-sitter --external:tree-sitter-typescript --external:cloudflared --external:pg --external:pg-native && rm -rf dist/server/public && cp -r ../claude-code-web/src/public dist/server/public && rm -rf dist/server/graph/queries && mkdir -p dist/server/graph && cp -r src/server/graph/queries dist/server/graph/queries",
68
- "dev:client": "vite",
69
- "dev:chart": "pnpm build:server && pnpm build:chart-client && node dist/server/graph-mcp-entry.js serve",
70
- "dev:server": "pnpm build:server && node dist/server/cli.js",
71
- "dev": "pnpm build:server && concurrently -k -n client,server -c cyan,magenta \"vite\" \"node dist/server/cli.js\"",
72
- "prepublishOnly": "pnpm build"
55
+ "launch-orbit": "./dist/server/orbit-entry.js",
56
+ "launch-course": "./dist/server/course-entry.js",
57
+ "launch-beacon": "./dist/server/beacon-monitor-entry.js"
73
58
  },
74
59
  "files": [
75
60
  "dist",
@@ -90,8 +75,6 @@
90
75
  "ws": "^8.18.0"
91
76
  },
92
77
  "devDependencies": {
93
- "@launchsecure/claude-code-web": "workspace:*",
94
- "@launchsecure/ui": "workspace:*",
95
78
  "@types/node": "^20.0.0",
96
79
  "@types/pg": "^8.11.10",
97
80
  "@types/react": "^18.3.12",
@@ -115,6 +98,24 @@
115
98
  "react-router-dom": "^6.28.0",
116
99
  "tailwindcss": "^3.4.19",
117
100
  "vite": "^5.4.11",
118
- "vitest": "^1.6.0"
101
+ "vitest": "^1.6.0",
102
+ "@launchsecure/claude-code-web": "0.0.1",
103
+ "@launchsecure/ui": "0.0.1"
104
+ },
105
+ "scripts": {
106
+ "build": "pnpm build:client && pnpm build:chart-client && pnpm build:deck-client && pnpm build:council-client && pnpm build:beacon && pnpm build:server",
107
+ "build:beacon": "vite build --config vite.beacon.config.ts && tsc -p tsconfig.beacon.json --emitDeclarationOnly --outDir dist/beacon/types",
108
+ "test:beacon": "vitest run --config vite.beacon.config.ts",
109
+ "test:radar": "vitest run --config vite.radar.config.ts",
110
+ "test:chart": "vitest run --config vite.chart.test.config.ts",
111
+ "build:deck-client": "vite build --config vite.deck.config.ts",
112
+ "build:council-client": "vite build --config vite.council.config.ts",
113
+ "build:client": "vite build",
114
+ "build:chart-client": "vite build --config vite.chart.config.ts",
115
+ "build:server": "esbuild src/server/cli.ts src/server/fb-wizard.ts src/server/graph-mcp-entry.ts src/server/chart-serve.ts src/server/deck-mcp-entry.ts src/server/deck-serve.ts src/server/council-entry.ts src/server/council-serve.ts src/server/recall-entry.ts src/server/init-entry.ts src/server/orbit-entry.ts src/server/course-entry.ts src/server/beacon-monitor-entry.ts src/server/parse-worker-entry.ts --bundle --platform=node --target=node18 --outdir=dist/server --external:node-pty --external:ws --external:typescript --external:web-tree-sitter --external:tree-sitter-typescript --external:cloudflared --external:pg --external:pg-native && rm -rf dist/server/public && cp -r ../claude-code-web/src/public dist/server/public && rm -rf dist/server/graph/queries && mkdir -p dist/server/graph && cp -r src/server/graph/queries dist/server/graph/queries",
116
+ "dev:client": "vite",
117
+ "dev:chart": "pnpm build:server && pnpm build:chart-client && node dist/server/graph-mcp-entry.js serve",
118
+ "dev:server": "pnpm build:server && node dist/server/cli.js",
119
+ "dev": "pnpm build:server && concurrently -k -n client,server -c cyan,magenta \"vite\" \"node dist/server/cli.js\""
119
120
  }
120
- }
121
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "launchsecure",
3
+ "description": "LaunchSecure plugins for Claude Code — in-app feedback widget activation, and future LS-namespaced workflows. Distributed locally via launch-kit init today; the same catalog will move to github.com/launchsecure/claude-plugins for centralized updates.",
4
+ "owner": {
5
+ "name": "LaunchSecure — AutomateWithUs",
6
+ "email": "hello@automatewith.us"
7
+ },
8
+ "plugins": [
9
+ {
10
+ "name": "ls",
11
+ "source": "./plugins/ls",
12
+ "description": "LS-namespaced slash commands. Exposes /ls:activate-beacon to wire the launch-kit-beacon in-app feedback widget into the current project."
13
+ }
14
+ ]
15
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "ls",
3
+ "version": "0.1.0",
4
+ "description": "LaunchSecure commands for Claude Code — in-app feedback widget activation, daily standups posted to the Comm Hub, and future LS-namespaced workflows.",
5
+ "author": {
6
+ "name": "LaunchSecure — AutomateWithUs",
7
+ "url": "https://automatewith.us"
8
+ },
9
+ "repository": "https://github.com/launchsecure/launchsecure-v2",
10
+ "license": "MIT",
11
+ "keywords": [
12
+ "launchsecure",
13
+ "launch-kit",
14
+ "launch-kit-beacon",
15
+ "feedback",
16
+ "standup",
17
+ "daily-update"
18
+ ],
19
+ "commands": [
20
+ "./commands/activate-beacon.md",
21
+ "./commands/standup.md",
22
+ "./commands/show-mcp-status.md",
23
+ "./commands/beacon-scan.md",
24
+ "./commands/beacon-pulse.md",
25
+ "./commands/beacon-array.md",
26
+ "./commands/beacon-clear.md"
27
+ ]
28
+ }