@m-kopa/launchpad-cli 0.46.1 → 0.47.0
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/CHANGELOG.md +39 -0
- package/dist/cli.js +192 -90
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/update.d.ts +7 -0
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/deploy/access-drift-offer.d.ts +46 -0
- package/dist/deploy/access-drift-offer.d.ts.map +1 -0
- package/dist/deploy/apply.d.ts +8 -0
- package/dist/deploy/apply.d.ts.map +1 -1
- package/dist/io/prompt.d.ts +13 -0
- package/dist/io/prompt.d.ts.map +1 -0
- package/dist/skills-bundle.d.ts +20 -0
- package/dist/skills-bundle.d.ts.map +1 -0
- package/dist/update-notifier.d.ts +3 -13
- package/dist/update-notifier.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +2 -2
- package/skills/launchpad-content-pr/SKILL.md +10 -6
- package/skills/launchpad-deploy/SKILL.md +10 -5
- package/skills/launchpad-deploy-status/SKILL.md +1 -1
- package/skills/launchpad-destroy/SKILL.md +1 -1
- package/skills/launchpad-identity/SKILL.md +1 -1
- package/skills/launchpad-onboard/SKILL.md +1 -1
- package/skills/launchpad-report/SKILL.md +1 -1
- package/skills/launchpad-status/SKILL.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,45 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html);
|
|
7
7
|
pre-1.0 minor bumps may carry breaking changes per ADR 0005.
|
|
8
8
|
|
|
9
|
+
## 0.47.0 — 2026-07-07
|
|
10
|
+
|
|
11
|
+
**`launchpad deploy` offers to reconcile access drift inline (PS-2071).** When a
|
|
12
|
+
plain deploy is refused because the manifest's access groups diverge from what
|
|
13
|
+
the gateway enforces, an **interactive terminal** now *offers to reconcile right
|
|
14
|
+
there* — answer `y` and it routes straight into the gated `launchpad deploy
|
|
15
|
+
--apply` flow instead of leaving you at a dead-end error to re-read, find the
|
|
16
|
+
flag, and re-run. This is convenience only: it changes no control. The apply
|
|
17
|
+
still shows the exact plan and prompts again before opening the PR, and the
|
|
18
|
+
byte-equal validator, the second-person `allowed-groups-change` label gate for
|
|
19
|
+
any access-widening change, and Terraform-as-sole-writer all still apply. A
|
|
20
|
+
**non-interactive** session (CI, a pipe, redirected stdin) never auto-runs an
|
|
21
|
+
access change — it prints the manual `--apply` route and exits non-zero so a
|
|
22
|
+
script fails loudly. After a successful reconcile the CLI points you back at
|
|
23
|
+
`launchpad deploy` to ship your content.
|
|
24
|
+
|
|
25
|
+
**`launchpad update` resyncs drifted skills even when the CLI is already current
|
|
26
|
+
(PS-2058).** Previously the skill bundle was re-synced only after an actual CLI
|
|
27
|
+
upgrade, so a machine already on the latest CLI could sit on a stale skill
|
|
28
|
+
bundle indefinitely with no warning. Now, when `update` finds the CLI already
|
|
29
|
+
current, it also checks the installed skill bundle and:
|
|
30
|
+
|
|
31
|
+
- **`launchpad update`** auto-resyncs a drifted bundle (best-effort; the update
|
|
32
|
+
still exits `0` regardless — a skills hiccup never fails it).
|
|
33
|
+
- **`launchpad update --check`** reports the drift as an informational line
|
|
34
|
+
pointing at `launchpad skills update`; exit codes are unchanged (`10` stays
|
|
35
|
+
CLI-update-only).
|
|
36
|
+
|
|
37
|
+
An in-sync bundle stays silent; an absent bundle is left to the passive
|
|
38
|
+
notifier's one-time hint.
|
|
39
|
+
|
|
40
|
+
## 0.46.2 — 2026-07-07
|
|
41
|
+
|
|
42
|
+
Fix: the apply-status poll no longer mistakes the endpoint's own
|
|
43
|
+
`no_apply_found` 404 (a momentary GitHub PR-list lag right after the
|
|
44
|
+
apply call) for a bot that lacks the endpoint. The poll now retries in
|
|
45
|
+
place; only a genuine route-absent 404 degrades to the legacy
|
|
46
|
+
merge-poll. Surfaced by the first live end-to-end apply.
|
|
47
|
+
|
|
9
48
|
## 0.46.1 — 2026-07-07
|
|
10
49
|
|
|
11
50
|
Copy fixes in the access-drift guidance: consistent voice in the deploy
|
package/dist/cli.js
CHANGED
|
@@ -19,7 +19,7 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
19
19
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
20
20
|
|
|
21
21
|
// src/version.ts
|
|
22
|
-
var CLI_VERSION = "0.
|
|
22
|
+
var CLI_VERSION = "0.47.0";
|
|
23
23
|
|
|
24
24
|
// src/config.ts
|
|
25
25
|
import * as os from "node:os";
|
|
@@ -6262,6 +6262,7 @@ async function runDeployApply(opts, io, deps = {}) {
|
|
|
6262
6262
|
return outcomeToExit(outcome2, undefined, io);
|
|
6263
6263
|
deletePinIfPresent(cfg, slug2, io);
|
|
6264
6264
|
io.out(`✓ Applied ${slug2}.`);
|
|
6265
|
+
deps.onApplied?.();
|
|
6265
6266
|
return 0;
|
|
6266
6267
|
}
|
|
6267
6268
|
const manifestPath = resolvePath(process.cwd(), opts.file ?? "launchpad.yaml");
|
|
@@ -6344,6 +6345,7 @@ async function runDeployApply(opts, io, deps = {}) {
|
|
|
6344
6345
|
deletePinIfPresent(cfg, slug, io);
|
|
6345
6346
|
io.out("");
|
|
6346
6347
|
io.out(`✓ Applied ${slug}. (See ${apply.prUrl} for the merged PR.)`);
|
|
6348
|
+
deps.onApplied?.();
|
|
6347
6349
|
return 0;
|
|
6348
6350
|
}
|
|
6349
6351
|
async function pollUntilApplied(args) {
|
|
@@ -6368,6 +6370,10 @@ async function pollUntilApplied(args) {
|
|
|
6368
6370
|
}, args.fetcher);
|
|
6369
6371
|
} catch (e) {
|
|
6370
6372
|
if (e instanceof NotFoundError) {
|
|
6373
|
+
if (e.message.includes("no_apply_found")) {
|
|
6374
|
+
await sleep(args.pollIntervalSec * 1000);
|
|
6375
|
+
continue;
|
|
6376
|
+
}
|
|
6371
6377
|
breakDots();
|
|
6372
6378
|
args.io.err("! bot has no apply-status endpoint yet — falling back to the coarse merge-poll (upgrade pending bot deploy)");
|
|
6373
6379
|
mode = "legacy";
|
|
@@ -6552,6 +6558,80 @@ function renderManifestError(loaded, io) {
|
|
|
6552
6558
|
}
|
|
6553
6559
|
}
|
|
6554
6560
|
|
|
6561
|
+
// src/deploy/access-drift-offer.ts
|
|
6562
|
+
var asStrings = (v) => Array.isArray(v) ? v.map((x) => String(x)) : [];
|
|
6563
|
+
function renderAccessDriftSummary(slug, body) {
|
|
6564
|
+
const lines = [`launchpad deploy: refused — ACCESS DRIFT on "${slug}".`];
|
|
6565
|
+
const liveOnly = asStrings(body.live_only);
|
|
6566
|
+
const manifestOnly = asStrings(body.manifest_only);
|
|
6567
|
+
if (liveOnly.length > 0) {
|
|
6568
|
+
lines.push(` enforced but NOT declared (over-grant): ${liveOnly.join(", ")}`);
|
|
6569
|
+
}
|
|
6570
|
+
if (manifestOnly.length > 0) {
|
|
6571
|
+
lines.push(` declared but NOT enforced: ${manifestOnly.join(", ")}`);
|
|
6572
|
+
}
|
|
6573
|
+
lines.push("");
|
|
6574
|
+
lines.push(" Your manifest's access groups don't match what the gateway enforces.");
|
|
6575
|
+
lines.push(" `launchpad deploy` ships content only and cannot change access.");
|
|
6576
|
+
return lines;
|
|
6577
|
+
}
|
|
6578
|
+
function renderManualApplyGuidance() {
|
|
6579
|
+
return [
|
|
6580
|
+
" Run `launchpad deploy --apply` to reconcile — it opens the gated TF PR",
|
|
6581
|
+
" (an access-widening change needs the allowed-groups-change label from a",
|
|
6582
|
+
" second person) and waits for the apply verdict. Then redeploy your content.",
|
|
6583
|
+
" Nothing was committed by this attempt."
|
|
6584
|
+
];
|
|
6585
|
+
}
|
|
6586
|
+
function isYes(answer) {
|
|
6587
|
+
const a = answer.trim().toLowerCase();
|
|
6588
|
+
return a === "y" || a === "yes";
|
|
6589
|
+
}
|
|
6590
|
+
async function handleAccessDrift(params) {
|
|
6591
|
+
const { slug, manifestPath, body, io, deps } = params;
|
|
6592
|
+
for (const line of renderAccessDriftSummary(slug, body))
|
|
6593
|
+
io.err(line);
|
|
6594
|
+
if (!deps.interactive) {
|
|
6595
|
+
for (const line of renderManualApplyGuidance())
|
|
6596
|
+
io.err(line);
|
|
6597
|
+
return 1;
|
|
6598
|
+
}
|
|
6599
|
+
io.err("");
|
|
6600
|
+
const answer = await deps.prompt("Reconcile access now via the gated apply PR? You'll review the exact plan next. [y/N] ");
|
|
6601
|
+
if (!isYes(answer)) {
|
|
6602
|
+
io.err("");
|
|
6603
|
+
io.err("Skipped — no access change was made.");
|
|
6604
|
+
for (const line of renderManualApplyGuidance())
|
|
6605
|
+
io.err(line);
|
|
6606
|
+
return 1;
|
|
6607
|
+
}
|
|
6608
|
+
io.out("");
|
|
6609
|
+
io.out("Reconciling access via `launchpad deploy --apply` …");
|
|
6610
|
+
let applied = false;
|
|
6611
|
+
const exit = await deps.runApply({ file: manifestPath, platformRepo: null, rePin: false, yes: false }, io, { onApplied: () => {
|
|
6612
|
+
applied = true;
|
|
6613
|
+
} });
|
|
6614
|
+
if (applied) {
|
|
6615
|
+
io.out("");
|
|
6616
|
+
io.out("✓ Access reconciled. Re-run `launchpad deploy` to ship your content change.");
|
|
6617
|
+
}
|
|
6618
|
+
return exit;
|
|
6619
|
+
}
|
|
6620
|
+
|
|
6621
|
+
// src/io/prompt.ts
|
|
6622
|
+
function isInteractive() {
|
|
6623
|
+
return Boolean(process.stdin.isTTY);
|
|
6624
|
+
}
|
|
6625
|
+
async function defaultPrompt2(question) {
|
|
6626
|
+
const { createInterface } = await import("node:readline/promises");
|
|
6627
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
6628
|
+
try {
|
|
6629
|
+
return await rl.question(question);
|
|
6630
|
+
} finally {
|
|
6631
|
+
rl.close();
|
|
6632
|
+
}
|
|
6633
|
+
}
|
|
6634
|
+
|
|
6555
6635
|
// src/deploy/dry-run.ts
|
|
6556
6636
|
import { execFileSync } from "node:child_process";
|
|
6557
6637
|
import { resolve as resolve7 } from "node:path";
|
|
@@ -7398,23 +7478,17 @@ async function runModelADeploy(args) {
|
|
|
7398
7478
|
return 1;
|
|
7399
7479
|
}
|
|
7400
7480
|
if (result.status === 409 && errorCode === "access_drift") {
|
|
7401
|
-
|
|
7402
|
-
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
io
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
io.err(" `launchpad deploy` ships content only and cannot change access.");
|
|
7413
|
-
io.err(" Run `launchpad deploy --apply` to reconcile — it opens the gated TF PR");
|
|
7414
|
-
io.err(" (access-widening changes need the allowed-groups-change label from a");
|
|
7415
|
-
io.err(" second person) and waits for the apply verdict. Then redeploy your content.");
|
|
7416
|
-
io.err(" Nothing was committed by this attempt.");
|
|
7417
|
-
return 1;
|
|
7481
|
+
return handleAccessDrift({
|
|
7482
|
+
slug,
|
|
7483
|
+
manifestPath,
|
|
7484
|
+
body,
|
|
7485
|
+
io,
|
|
7486
|
+
deps: {
|
|
7487
|
+
interactive: isInteractive(),
|
|
7488
|
+
prompt: defaultPrompt2,
|
|
7489
|
+
runApply: runDeployApply
|
|
7490
|
+
}
|
|
7491
|
+
});
|
|
7418
7492
|
}
|
|
7419
7493
|
io.err(`launchpad deploy: bot rejected the upload (HTTP ${result.status}, ${errorCode}).`);
|
|
7420
7494
|
if (typeof body.message === "string") {
|
|
@@ -8881,7 +8955,7 @@ var SLUG_REGEX3 = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
|
|
|
8881
8955
|
var initCommand = {
|
|
8882
8956
|
name: "init",
|
|
8883
8957
|
summary: "scaffold a new launchpad.yaml in the current directory",
|
|
8884
|
-
run: (args, io) => runInit(args, io,
|
|
8958
|
+
run: (args, io) => runInit(args, io, defaultPrompt3)
|
|
8885
8959
|
};
|
|
8886
8960
|
async function runInit(args, io, prompt) {
|
|
8887
8961
|
const parsed = parseArgs6(args);
|
|
@@ -9371,7 +9445,7 @@ function ensureGitignoreEntries(path10, entries) {
|
|
|
9371
9445
|
}
|
|
9372
9446
|
return added;
|
|
9373
9447
|
}
|
|
9374
|
-
async function
|
|
9448
|
+
async function defaultPrompt3(question, fallback) {
|
|
9375
9449
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
9376
9450
|
try {
|
|
9377
9451
|
const suffix = fallback !== undefined ? ` [${fallback}]` : "";
|
|
@@ -10052,7 +10126,7 @@ import { stdin as input, stdout as output } from "node:process";
|
|
|
10052
10126
|
var destroyCommand = {
|
|
10053
10127
|
name: "destroy",
|
|
10054
10128
|
summary: "tear down a Launchpad app (owner-only, destructive)",
|
|
10055
|
-
run: (args, io) => runDestroy(args, io,
|
|
10129
|
+
run: (args, io) => runDestroy(args, io, defaultPrompt4, defaultIsTty)
|
|
10056
10130
|
};
|
|
10057
10131
|
var SLUG_RE10 = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
|
|
10058
10132
|
var SLUG_MIN_LENGTH = 3;
|
|
@@ -10284,7 +10358,7 @@ function renderBotError2(status, env, slug, io) {
|
|
|
10284
10358
|
io.err(`launchpad destroy: bot returned HTTP ${status} (error=${errorCode}): ${message}`);
|
|
10285
10359
|
}
|
|
10286
10360
|
}
|
|
10287
|
-
async function
|
|
10361
|
+
async function defaultPrompt4(question) {
|
|
10288
10362
|
const rl = createInterface2({ input, output });
|
|
10289
10363
|
try {
|
|
10290
10364
|
return await rl.question(question);
|
|
@@ -10953,7 +11027,7 @@ async function runRollback(opts, io, deps = {}) {
|
|
|
10953
11027
|
io.out(`Rollback target: ${verifiedSha.slice(0, 12)} (${manifestRelpath})`);
|
|
10954
11028
|
renderDiff(current, parsed.manifest, io);
|
|
10955
11029
|
if (!opts.yes) {
|
|
10956
|
-
const prompt = deps.prompt ??
|
|
11030
|
+
const prompt = deps.prompt ?? defaultPrompt5;
|
|
10957
11031
|
if (!process.stdin.isTTY && deps.prompt === undefined) {
|
|
10958
11032
|
io.err("launchpad rollback: refusing to rollback non-interactively without --yes.");
|
|
10959
11033
|
return 64;
|
|
@@ -11041,7 +11115,7 @@ function formatHostnames(hs) {
|
|
|
11041
11115
|
return hs[0];
|
|
11042
11116
|
return `[${hs.join(", ")}]`;
|
|
11043
11117
|
}
|
|
11044
|
-
async function
|
|
11118
|
+
async function defaultPrompt5(question) {
|
|
11045
11119
|
const rl = createInterface3({ input: process.stdin, output: process.stderr });
|
|
11046
11120
|
try {
|
|
11047
11121
|
return await rl.question(question);
|
|
@@ -11348,7 +11422,7 @@ async function offerRedeploy(slug, opts, io, deps) {
|
|
|
11348
11422
|
const isTty = (deps.isTty ?? (() => process.stdin.isTTY === true))();
|
|
11349
11423
|
let doRedeploy = wantsAuto;
|
|
11350
11424
|
if (!wantsAuto && isTty) {
|
|
11351
|
-
const prompt = deps.prompt ??
|
|
11425
|
+
const prompt = deps.prompt ?? defaultPrompt6;
|
|
11352
11426
|
let answer;
|
|
11353
11427
|
try {
|
|
11354
11428
|
answer = (await prompt(`Redeploy ${slug} now to activate? [y/N] `)).trim().toLowerCase();
|
|
@@ -11369,7 +11443,7 @@ async function offerRedeploy(slug, opts, io, deps) {
|
|
|
11369
11443
|
};
|
|
11370
11444
|
return run({ slug, file: opts.file ?? null, verify: true, verifyTimeoutMs: 180000 }, io, rdeps);
|
|
11371
11445
|
}
|
|
11372
|
-
async function
|
|
11446
|
+
async function defaultPrompt6(question) {
|
|
11373
11447
|
const rl = createInterface4({ input: input2, output: output2 });
|
|
11374
11448
|
try {
|
|
11375
11449
|
return await rl.question(question);
|
|
@@ -12110,9 +12184,45 @@ function printHelp3(io) {
|
|
|
12110
12184
|
|
|
12111
12185
|
// src/commands/skills.ts
|
|
12112
12186
|
import { fileURLToPath } from "node:url";
|
|
12113
|
-
import { dirname as dirname8, join as
|
|
12187
|
+
import { dirname as dirname8, join as join14, resolve as resolve12 } from "node:path";
|
|
12114
12188
|
import { promises as fs6, existsSync as existsSync14 } from "node:fs";
|
|
12189
|
+
|
|
12190
|
+
// src/skills-bundle.ts
|
|
12115
12191
|
import { homedir as homedir2 } from "node:os";
|
|
12192
|
+
import { join as join13 } from "node:path";
|
|
12193
|
+
import { readFileSync as readFileSync18, readdirSync as readdirSync2 } from "node:fs";
|
|
12194
|
+
var SKILL_PREFIX = "launchpad-";
|
|
12195
|
+
function skillsTargetDir() {
|
|
12196
|
+
return process.env.LAUNCHPAD_SKILLS_TARGET_DIR ?? join13(homedir2(), ".claude", "skills");
|
|
12197
|
+
}
|
|
12198
|
+
function readInstalledSkills() {
|
|
12199
|
+
try {
|
|
12200
|
+
const dir = skillsTargetDir();
|
|
12201
|
+
const bundle = readdirSync2(dir, { withFileTypes: true }).find((e) => e.isDirectory() && e.name.startsWith(SKILL_PREFIX));
|
|
12202
|
+
if (!bundle)
|
|
12203
|
+
return { present: false, version: null };
|
|
12204
|
+
return {
|
|
12205
|
+
present: true,
|
|
12206
|
+
version: readSkillVersion(join13(dir, bundle.name, "SKILL.md"))
|
|
12207
|
+
};
|
|
12208
|
+
} catch {
|
|
12209
|
+
return { present: false, version: null };
|
|
12210
|
+
}
|
|
12211
|
+
}
|
|
12212
|
+
function readSkillVersion(path13) {
|
|
12213
|
+
try {
|
|
12214
|
+
const text = readFileSync18(path13, "utf8");
|
|
12215
|
+
const fenceEnd = text.indexOf(`
|
|
12216
|
+
---`, 4);
|
|
12217
|
+
const front = fenceEnd === -1 ? text.slice(0, 1024) : text.slice(0, fenceEnd);
|
|
12218
|
+
const m = /^version:\s*(.+?)\s*$/m.exec(front);
|
|
12219
|
+
return m === null ? null : m[1] ?? null;
|
|
12220
|
+
} catch {
|
|
12221
|
+
return null;
|
|
12222
|
+
}
|
|
12223
|
+
}
|
|
12224
|
+
|
|
12225
|
+
// src/commands/skills.ts
|
|
12116
12226
|
var BUNDLE_PREFIX = "launchpad-";
|
|
12117
12227
|
var BUNDLED_SKILLS = [
|
|
12118
12228
|
"launchpad-onboard",
|
|
@@ -12174,7 +12284,7 @@ function printHelp4(io) {
|
|
|
12174
12284
|
}
|
|
12175
12285
|
function resolveInstallEnv() {
|
|
12176
12286
|
const bundleDir = process.env.LAUNCHPAD_SKILLS_BUNDLE_DIR ?? defaultBundleDir();
|
|
12177
|
-
const userSkillsDir =
|
|
12287
|
+
const userSkillsDir = skillsTargetDir();
|
|
12178
12288
|
return { bundleDir, userSkillsDir };
|
|
12179
12289
|
}
|
|
12180
12290
|
function defaultBundleDir() {
|
|
@@ -12184,7 +12294,7 @@ function defaultBundleDir() {
|
|
|
12184
12294
|
resolve12(here, "..", "..", "skills")
|
|
12185
12295
|
];
|
|
12186
12296
|
for (const c of candidates) {
|
|
12187
|
-
if (existsSync14(
|
|
12297
|
+
if (existsSync14(join14(c, "launchpad-onboard", "SKILL.md"))) {
|
|
12188
12298
|
return c;
|
|
12189
12299
|
}
|
|
12190
12300
|
}
|
|
@@ -12205,12 +12315,12 @@ async function doInstall(io) {
|
|
|
12205
12315
|
}
|
|
12206
12316
|
let installed = 0;
|
|
12207
12317
|
for (const skill of BUNDLED_SKILLS) {
|
|
12208
|
-
const src =
|
|
12318
|
+
const src = join14(env.bundleDir, skill);
|
|
12209
12319
|
if (!await isDir(src)) {
|
|
12210
12320
|
io.err(`launchpad skills install: bundled skill "${skill}" missing from ${env.bundleDir} — package is incomplete.`);
|
|
12211
12321
|
return 1;
|
|
12212
12322
|
}
|
|
12213
|
-
const dest =
|
|
12323
|
+
const dest = join14(env.userSkillsDir, skill);
|
|
12214
12324
|
await fs6.rm(dest, { recursive: true, force: true });
|
|
12215
12325
|
await fs6.cp(src, dest, { recursive: true });
|
|
12216
12326
|
installed++;
|
|
@@ -12235,7 +12345,7 @@ async function doUninstall(io) {
|
|
|
12235
12345
|
continue;
|
|
12236
12346
|
if (!isBundleManaged(entry.name))
|
|
12237
12347
|
continue;
|
|
12238
|
-
const target =
|
|
12348
|
+
const target = join14(env.userSkillsDir, entry.name);
|
|
12239
12349
|
await fs6.rm(target, { recursive: true, force: true });
|
|
12240
12350
|
removed++;
|
|
12241
12351
|
io.out(`✗ removed ${target}`);
|
|
@@ -12258,7 +12368,7 @@ async function doList(io) {
|
|
|
12258
12368
|
}
|
|
12259
12369
|
const width = managedDirs.reduce((n, s) => Math.max(n, s.length), 0);
|
|
12260
12370
|
for (const name of managedDirs) {
|
|
12261
|
-
const skillFile =
|
|
12371
|
+
const skillFile = join14(env.userSkillsDir, name, "SKILL.md");
|
|
12262
12372
|
const version = await readVersion(skillFile);
|
|
12263
12373
|
io.out(` ${name.padEnd(width + 2)}${version ?? "(no version)"}`);
|
|
12264
12374
|
}
|
|
@@ -12293,9 +12403,9 @@ function describe29(e) {
|
|
|
12293
12403
|
import { execFile, spawn as spawn5 } from "node:child_process";
|
|
12294
12404
|
import { promisify } from "node:util";
|
|
12295
12405
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
12296
|
-
import { dirname as dirname9, resolve as resolve13, relative as relative4, isAbsolute as isAbsolute2, join as
|
|
12406
|
+
import { dirname as dirname9, resolve as resolve13, relative as relative4, isAbsolute as isAbsolute2, join as join15 } from "node:path";
|
|
12297
12407
|
import { homedir as homedir3, tmpdir } from "node:os";
|
|
12298
|
-
import { readFileSync as
|
|
12408
|
+
import { readFileSync as readFileSync19, mkdtempSync, writeFileSync as writeFileSync7, rmSync as rmSync2 } from "node:fs";
|
|
12299
12409
|
|
|
12300
12410
|
// src/commands/channel-auth.ts
|
|
12301
12411
|
import { createServer as createServer2 } from "node:http";
|
|
@@ -12416,7 +12526,7 @@ var PKG = "@m-kopa/launchpad-cli";
|
|
|
12416
12526
|
var REGISTRY = "https://registry.npmjs.org";
|
|
12417
12527
|
var CHANNEL_VERSION_URL = "https://get.launchpad.m-kopa.us/version.json";
|
|
12418
12528
|
var CHANNEL_INSTALL_URL = "https://get.launchpad.m-kopa.us";
|
|
12419
|
-
var CHANNEL_MARKER =
|
|
12529
|
+
var CHANNEL_MARKER = join15(homedir3(), ".launchpad", "channel");
|
|
12420
12530
|
var EXIT_UPDATE_AVAILABLE = 10;
|
|
12421
12531
|
var UPGRADE_ARGS = {
|
|
12422
12532
|
npm: ["install", "-g", `${PKG}@latest`],
|
|
@@ -12493,7 +12603,8 @@ function defaultDeps() {
|
|
|
12493
12603
|
detectPm: detectPackageManager2,
|
|
12494
12604
|
runUpgrade,
|
|
12495
12605
|
runChannelInstaller,
|
|
12496
|
-
syncSkills: runSkillsSync
|
|
12606
|
+
syncSkills: runSkillsSync,
|
|
12607
|
+
readInstalledSkills
|
|
12497
12608
|
};
|
|
12498
12609
|
}
|
|
12499
12610
|
async function runSkillsSync() {
|
|
@@ -12516,7 +12627,25 @@ async function runSkillsSync() {
|
|
|
12516
12627
|
async function syncSkillsAfterUpgrade(io, deps) {
|
|
12517
12628
|
const result = await deps.syncSkills();
|
|
12518
12629
|
if (result.ok) {
|
|
12519
|
-
io.out("✓ Claude Code skill bundle re-synced to the
|
|
12630
|
+
io.out("✓ Claude Code skill bundle re-synced to match the CLI.");
|
|
12631
|
+
} else {
|
|
12632
|
+
io.err("Note: couldn't auto-sync the skill bundle — run `launchpad skills update` to finish.");
|
|
12633
|
+
}
|
|
12634
|
+
}
|
|
12635
|
+
async function reconcileSkillBundleWhenCurrent(io, deps, checkOnly) {
|
|
12636
|
+
const skills = deps.readInstalledSkills();
|
|
12637
|
+
if (!skills.present || skills.version === null)
|
|
12638
|
+
return;
|
|
12639
|
+
if (compareVersions(deps.cliVersion, skills.version) !== 1)
|
|
12640
|
+
return;
|
|
12641
|
+
if (checkOnly) {
|
|
12642
|
+
io.out(`Skill bundle ${skills.version} is behind the CLI ${deps.cliVersion} — run \`launchpad skills update\`.`);
|
|
12643
|
+
return;
|
|
12644
|
+
}
|
|
12645
|
+
io.out(`Skill bundle ${skills.version} is behind the CLI ${deps.cliVersion}; re-syncing…`);
|
|
12646
|
+
const result = await deps.syncSkills();
|
|
12647
|
+
if (result.ok) {
|
|
12648
|
+
io.out("✓ Claude Code skill bundle re-synced to match the CLI.");
|
|
12520
12649
|
} else {
|
|
12521
12650
|
io.err("Note: couldn't auto-sync the skill bundle — run `launchpad skills update` to finish.");
|
|
12522
12651
|
}
|
|
@@ -12526,8 +12655,8 @@ async function openSystemBrowser(url) {
|
|
|
12526
12655
|
await execFileAsync(opener, [url]);
|
|
12527
12656
|
}
|
|
12528
12657
|
async function runInstallerScript(script) {
|
|
12529
|
-
const dir = mkdtempSync(
|
|
12530
|
-
const file =
|
|
12658
|
+
const dir = mkdtempSync(join15(tmpdir(), "launchpad-update-"));
|
|
12659
|
+
const file = join15(dir, "install.sh");
|
|
12531
12660
|
try {
|
|
12532
12661
|
writeFileSync7(file, script, { mode: 448 });
|
|
12533
12662
|
return await new Promise((resolvePromise) => {
|
|
@@ -12551,7 +12680,7 @@ function resolveLatestVersion() {
|
|
|
12551
12680
|
}
|
|
12552
12681
|
function detectInstallChannel() {
|
|
12553
12682
|
try {
|
|
12554
|
-
return
|
|
12683
|
+
return readFileSync19(CHANNEL_MARKER, "utf8").trim() === "platform" ? "platform" : "github";
|
|
12555
12684
|
} catch {
|
|
12556
12685
|
return "github";
|
|
12557
12686
|
}
|
|
@@ -12677,6 +12806,7 @@ async function runUpdate(args, io, deps) {
|
|
|
12677
12806
|
const current = deps.cliVersion;
|
|
12678
12807
|
if (compareVersions(current, latest.version) >= 0) {
|
|
12679
12808
|
io.out(`launchpad-cli is up to date (${current}).`);
|
|
12809
|
+
await reconcileSkillBundleWhenCurrent(io, deps, parsed.check);
|
|
12680
12810
|
return 0;
|
|
12681
12811
|
}
|
|
12682
12812
|
if (parsed.check) {
|
|
@@ -12758,7 +12888,7 @@ function printHelp5(io) {
|
|
|
12758
12888
|
}
|
|
12759
12889
|
|
|
12760
12890
|
// src/commands/validate.ts
|
|
12761
|
-
import { existsSync as existsSync15, readFileSync as
|
|
12891
|
+
import { existsSync as existsSync15, readFileSync as readFileSync20, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
|
|
12762
12892
|
import { dirname as dirname10, resolve as resolve14 } from "node:path";
|
|
12763
12893
|
var validateCommand = {
|
|
12764
12894
|
name: "validate",
|
|
@@ -12796,7 +12926,7 @@ function checkBoundary(manifestPath, declared) {
|
|
|
12796
12926
|
}
|
|
12797
12927
|
let manifestYaml;
|
|
12798
12928
|
try {
|
|
12799
|
-
manifestYaml =
|
|
12929
|
+
manifestYaml = readFileSync20(manifestPath, "utf8");
|
|
12800
12930
|
} catch {
|
|
12801
12931
|
manifestYaml = null;
|
|
12802
12932
|
}
|
|
@@ -12837,7 +12967,7 @@ function checkStaticServeDir(manifestPath, manifest) {
|
|
|
12837
12967
|
}
|
|
12838
12968
|
let entries;
|
|
12839
12969
|
try {
|
|
12840
|
-
entries =
|
|
12970
|
+
entries = readdirSync3(abs);
|
|
12841
12971
|
} catch (e) {
|
|
12842
12972
|
return [`failed to read static served directory '${serveDir}/': ${describe30(e)}`];
|
|
12843
12973
|
}
|
|
@@ -13711,21 +13841,20 @@ var listOwnersCommand = makeListOwnersCommand();
|
|
|
13711
13841
|
// src/update-notifier.ts
|
|
13712
13842
|
import { spawn as spawn6 } from "node:child_process";
|
|
13713
13843
|
import { homedir as homedir4 } from "node:os";
|
|
13714
|
-
import { join as
|
|
13844
|
+
import { join as join16 } from "node:path";
|
|
13715
13845
|
import {
|
|
13716
13846
|
existsSync as existsSync16,
|
|
13717
13847
|
mkdirSync as mkdirSync4,
|
|
13718
|
-
readFileSync as
|
|
13719
|
-
readdirSync as readdirSync3,
|
|
13848
|
+
readFileSync as readFileSync21,
|
|
13720
13849
|
writeFileSync as writeFileSync8
|
|
13721
13850
|
} from "node:fs";
|
|
13722
13851
|
var INTERNAL_REFRESH_VERB = "__refresh-update-cache";
|
|
13723
13852
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
13724
13853
|
var OPT_OUT_ENV = "LAUNCHPAD_NO_UPDATE_NOTIFIER";
|
|
13725
|
-
var CACHE_FILE =
|
|
13854
|
+
var CACHE_FILE = join16(homedir4(), ".launchpad", "update-check.json");
|
|
13726
13855
|
function readCache2() {
|
|
13727
13856
|
try {
|
|
13728
|
-
const raw = JSON.parse(
|
|
13857
|
+
const raw = JSON.parse(readFileSync21(CACHE_FILE, "utf8"));
|
|
13729
13858
|
if (typeof raw === "object" && raw !== null && typeof raw.checkedAt === "number") {
|
|
13730
13859
|
const latest = raw.latest;
|
|
13731
13860
|
return {
|
|
@@ -13738,39 +13867,12 @@ function readCache2() {
|
|
|
13738
13867
|
}
|
|
13739
13868
|
function writeCache2(state) {
|
|
13740
13869
|
try {
|
|
13741
|
-
mkdirSync4(
|
|
13870
|
+
mkdirSync4(join16(homedir4(), ".launchpad"), { recursive: true });
|
|
13742
13871
|
writeFileSync8(CACHE_FILE, `${JSON.stringify(state)}
|
|
13743
13872
|
`, { mode: 384 });
|
|
13744
13873
|
} catch {}
|
|
13745
13874
|
}
|
|
13746
|
-
var
|
|
13747
|
-
var SKILLS_HINT_MARKER = join15(homedir4(), ".launchpad", "skills-hint-shown");
|
|
13748
|
-
function skillsTargetDir() {
|
|
13749
|
-
return process.env.LAUNCHPAD_SKILLS_TARGET_DIR ?? join15(homedir4(), ".claude", "skills");
|
|
13750
|
-
}
|
|
13751
|
-
function readInstalledSkills() {
|
|
13752
|
-
try {
|
|
13753
|
-
const dir = skillsTargetDir();
|
|
13754
|
-
const bundle = readdirSync3(dir, { withFileTypes: true }).find((e) => e.isDirectory() && e.name.startsWith(SKILL_PREFIX));
|
|
13755
|
-
if (!bundle)
|
|
13756
|
-
return { present: false, version: null };
|
|
13757
|
-
return { present: true, version: readSkillVersion(join15(dir, bundle.name, "SKILL.md")) };
|
|
13758
|
-
} catch {
|
|
13759
|
-
return { present: false, version: null };
|
|
13760
|
-
}
|
|
13761
|
-
}
|
|
13762
|
-
function readSkillVersion(path13) {
|
|
13763
|
-
try {
|
|
13764
|
-
const text = readFileSync20(path13, "utf8");
|
|
13765
|
-
const fenceEnd = text.indexOf(`
|
|
13766
|
-
---`, 4);
|
|
13767
|
-
const front = fenceEnd === -1 ? text.slice(0, 1024) : text.slice(0, fenceEnd);
|
|
13768
|
-
const m = /^version:\s*(.+?)\s*$/m.exec(front);
|
|
13769
|
-
return m === null ? null : m[1] ?? null;
|
|
13770
|
-
} catch {
|
|
13771
|
-
return null;
|
|
13772
|
-
}
|
|
13773
|
-
}
|
|
13875
|
+
var SKILLS_HINT_MARKER = join16(homedir4(), ".launchpad", "skills-hint-shown");
|
|
13774
13876
|
function absentHintShown() {
|
|
13775
13877
|
try {
|
|
13776
13878
|
return existsSync16(SKILLS_HINT_MARKER);
|
|
@@ -13780,7 +13882,7 @@ function absentHintShown() {
|
|
|
13780
13882
|
}
|
|
13781
13883
|
function markAbsentHintShown() {
|
|
13782
13884
|
try {
|
|
13783
|
-
mkdirSync4(
|
|
13885
|
+
mkdirSync4(join16(homedir4(), ".launchpad"), { recursive: true });
|
|
13784
13886
|
writeFileSync8(SKILLS_HINT_MARKER, `${Date.now()}
|
|
13785
13887
|
`, { mode: 384 });
|
|
13786
13888
|
} catch {}
|
|
@@ -13883,8 +13985,8 @@ var refreshUpdateCacheCommand = {
|
|
|
13883
13985
|
// src/telemetry.ts
|
|
13884
13986
|
import { spawn as spawn7 } from "node:child_process";
|
|
13885
13987
|
import { homedir as homedir5 } from "node:os";
|
|
13886
|
-
import { join as
|
|
13887
|
-
import { existsSync as existsSync17, mkdirSync as mkdirSync5, readFileSync as
|
|
13988
|
+
import { join as join17 } from "node:path";
|
|
13989
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync5, readFileSync as readFileSync22, writeFileSync as writeFileSync9 } from "node:fs";
|
|
13888
13990
|
import { randomUUID } from "node:crypto";
|
|
13889
13991
|
var INTERNAL_EMIT_VERB = "__emit-telemetry";
|
|
13890
13992
|
var POSTHOG_HOST = "https://us.i.posthog.com";
|
|
@@ -13892,8 +13994,8 @@ var POSTHOG_PROJECT_KEY = "phc_CYaCuETanWc36TMTiB7cdkdnmPPhFZCkDGmWGLaNkXnb";
|
|
|
13892
13994
|
var CAPTURE_PATH = "/i/v0/e/";
|
|
13893
13995
|
var SEND_TIMEOUT_MS = 3000;
|
|
13894
13996
|
var APP = "launchpad-cli";
|
|
13895
|
-
var DEVICE_ID_FILE =
|
|
13896
|
-
var FIRST_RUN_MARKER =
|
|
13997
|
+
var DEVICE_ID_FILE = join17(homedir5(), ".launchpad", "telemetry-id.json");
|
|
13998
|
+
var FIRST_RUN_MARKER = join17(homedir5(), ".launchpad", "telemetry-notice-shown");
|
|
13897
13999
|
function buildEvent(ctx, identity, nowMs) {
|
|
13898
14000
|
const properties = {
|
|
13899
14001
|
app: APP,
|
|
@@ -13960,14 +14062,14 @@ async function resolveIdentityReal() {
|
|
|
13960
14062
|
}
|
|
13961
14063
|
function getOrCreateDeviceId() {
|
|
13962
14064
|
try {
|
|
13963
|
-
const raw = JSON.parse(
|
|
14065
|
+
const raw = JSON.parse(readFileSync22(DEVICE_ID_FILE, "utf8"));
|
|
13964
14066
|
const id2 = raw?.deviceId;
|
|
13965
14067
|
if (typeof id2 === "string" && id2)
|
|
13966
14068
|
return id2;
|
|
13967
14069
|
} catch {}
|
|
13968
14070
|
const id = randomUUID();
|
|
13969
14071
|
try {
|
|
13970
|
-
mkdirSync5(
|
|
14072
|
+
mkdirSync5(join17(homedir5(), ".launchpad"), { recursive: true });
|
|
13971
14073
|
writeFileSync9(DEVICE_ID_FILE, `${JSON.stringify({ deviceId: id })}
|
|
13972
14074
|
`, {
|
|
13973
14075
|
mode: 384
|
|
@@ -14045,7 +14147,7 @@ function firstRunNoticeShown() {
|
|
|
14045
14147
|
}
|
|
14046
14148
|
function markFirstRunNotice() {
|
|
14047
14149
|
try {
|
|
14048
|
-
mkdirSync5(
|
|
14150
|
+
mkdirSync5(join17(homedir5(), ".launchpad"), { recursive: true });
|
|
14049
14151
|
writeFileSync9(FIRST_RUN_MARKER, `${Date.now()}
|
|
14050
14152
|
`, { mode: 384 });
|
|
14051
14153
|
} catch {}
|
|
@@ -14111,11 +14213,11 @@ import { createInterface as createInterface7 } from "node:readline/promises";
|
|
|
14111
14213
|
import { platform } from "node:os";
|
|
14112
14214
|
|
|
14113
14215
|
// src/report/breadcrumb.ts
|
|
14114
|
-
import { writeFileSync as writeFileSync10, readFileSync as
|
|
14216
|
+
import { writeFileSync as writeFileSync10, readFileSync as readFileSync23, mkdirSync as mkdirSync6 } from "node:fs";
|
|
14115
14217
|
import { homedir as homedir6 } from "node:os";
|
|
14116
|
-
import { join as
|
|
14218
|
+
import { join as join18, dirname as dirname11 } from "node:path";
|
|
14117
14219
|
function breadcrumbPath() {
|
|
14118
|
-
return
|
|
14220
|
+
return join18(homedir6(), ".launchpad", "last-run.json");
|
|
14119
14221
|
}
|
|
14120
14222
|
function writeBreadcrumb(verb, exit) {
|
|
14121
14223
|
try {
|
|
@@ -14126,7 +14228,7 @@ function writeBreadcrumb(verb, exit) {
|
|
|
14126
14228
|
}
|
|
14127
14229
|
function readBreadcrumb() {
|
|
14128
14230
|
try {
|
|
14129
|
-
const raw = JSON.parse(
|
|
14231
|
+
const raw = JSON.parse(readFileSync23(breadcrumbPath(), "utf8"));
|
|
14130
14232
|
if (typeof raw !== "object" || raw === null)
|
|
14131
14233
|
return null;
|
|
14132
14234
|
const r = raw;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAqEA,OAAO,EAGL,4BAA4B,EAC7B,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAqEA,OAAO,EAGL,4BAA4B,EAC7B,MAAM,uBAAuB,CAAC;AAQ/B,OAAO,EAAa,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACnF,OAAO,KAAK,EAAS,OAAO,EAAY,MAAM,kBAAkB,CAAC;AAKjE,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAC;AAEjD,eAAO,MAAM,aAAa,EAAE,OAI3B,CAAC;AAEF,UAAU,UAAU;IAClB,6BAA6B;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAmQD;;;;;;GAMG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAoBpC;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,GAAG,IAAI,CAwBpE;AAkMD,2BAA2B;AAC3B,OAAO,EAAE,4BAA4B,EAAE,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ChannelUpdateResult } from "./channel-auth.js";
|
|
2
|
+
import { type InstalledSkillsState } from "../skills-bundle.js";
|
|
2
3
|
import type { CliIo, Command, ExitCode } from "../dispatcher.js";
|
|
3
4
|
/** Browser-based installer the platform channel directs users to for upgrades. */
|
|
4
5
|
export declare const CHANNEL_INSTALL_URL = "https://get.launchpad.m-kopa.us";
|
|
@@ -96,6 +97,12 @@ export interface UpdateDeps {
|
|
|
96
97
|
* failure). See {@link runSkillsSync}.
|
|
97
98
|
*/
|
|
98
99
|
readonly syncSkills: () => Promise<SkillsSyncResult>;
|
|
100
|
+
/**
|
|
101
|
+
* Read the freshness of the on-disk Claude Code skill bundle. Injected so
|
|
102
|
+
* the already-up-to-date branch's drift check is unit-testable without
|
|
103
|
+
* touching the filesystem. See {@link reconcileSkillBundleWhenCurrent}.
|
|
104
|
+
*/
|
|
105
|
+
readonly readInstalledSkills: () => InstalledSkillsState;
|
|
99
106
|
}
|
|
100
107
|
/** Outcome of the post-upgrade skill-bundle re-sync. */
|
|
101
108
|
export interface SkillsSyncResult {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAuDA,OAAO,EAA4B,KAAK,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACvF,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAWjE,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,oCAAoC,CAAC;AAYrE,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AAEpD,gEAAgE;AAChE,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,UAAU,CAAC;AASnD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,cAAc,GAAG,MAAM,CAGvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,CAEpB;AAED,eAAO,MAAM,aAAa,EAAE,OAI3B,CAAC;AAIF,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAMD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CA4BnE;AAID;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAiBhE;AAID,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAI3D,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,mEAAmE;IACnE,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC;IACzD,QAAQ,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACxD,QAAQ,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7D;;;;;;OAMG;IACH,QAAQ,CAAC,mBAAmB,EAAE,MAAM,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACjE;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { CliIo, ExitCode } from "../dispatcher.js";
|
|
2
|
+
import type { ApplyOptions } from "./apply.js";
|
|
3
|
+
/** The two-direction group diff the bot returns with a 409 `access_drift`. */
|
|
4
|
+
export interface AccessDriftBody {
|
|
5
|
+
readonly live_only?: unknown;
|
|
6
|
+
readonly manifest_only?: unknown;
|
|
7
|
+
}
|
|
8
|
+
export interface AccessDriftDeps {
|
|
9
|
+
/** True when we may prompt (stdin is a TTY). */
|
|
10
|
+
readonly interactive: boolean;
|
|
11
|
+
/** Ask a yes/no question; resolves the raw typed line. */
|
|
12
|
+
readonly prompt: (question: string) => Promise<string>;
|
|
13
|
+
/**
|
|
14
|
+
* Run the gated apply flow. Defaults to `runDeployApply` at the call site.
|
|
15
|
+
* The third arg carries an `onApplied` hook the flow fires only when a
|
|
16
|
+
* terraform apply actually completes — so we never claim success when the
|
|
17
|
+
* operator reviews the plan and aborts (both paths exit 0).
|
|
18
|
+
*/
|
|
19
|
+
readonly runApply: (opts: ApplyOptions, io: CliIo, deps?: {
|
|
20
|
+
readonly onApplied?: () => void;
|
|
21
|
+
}) => Promise<ExitCode>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The drift summary — printed in every case (interactive or not). Names both
|
|
25
|
+
* directions of the divergence so the operator can see exactly what would
|
|
26
|
+
* change before deciding.
|
|
27
|
+
*/
|
|
28
|
+
export declare function renderAccessDriftSummary(slug: string, body: AccessDriftBody): string[];
|
|
29
|
+
/**
|
|
30
|
+
* The manual fallback guidance — printed when the session is non-interactive,
|
|
31
|
+
* or when the operator declines the inline offer.
|
|
32
|
+
*/
|
|
33
|
+
export declare function renderManualApplyGuidance(): string[];
|
|
34
|
+
/**
|
|
35
|
+
* Surface an access-drift refusal and, when interactive, offer to reconcile
|
|
36
|
+
* inline via the gated apply flow. Returns the exit code the deploy command
|
|
37
|
+
* should return.
|
|
38
|
+
*/
|
|
39
|
+
export declare function handleAccessDrift(params: {
|
|
40
|
+
readonly slug: string;
|
|
41
|
+
readonly manifestPath: string;
|
|
42
|
+
readonly body: AccessDriftBody;
|
|
43
|
+
readonly io: CliIo;
|
|
44
|
+
readonly deps: AccessDriftDeps;
|
|
45
|
+
}): Promise<ExitCode>;
|
|
46
|
+
//# sourceMappingURL=access-drift-offer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"access-drift-offer.d.ts","sourceRoot":"","sources":["../../src/deploy/access-drift-offer.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,8EAA8E;AAC9E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,gDAAgD;IAChD,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACvD;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,CACjB,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,KAAK,EACT,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;KAAE,KACvC,OAAO,CAAC,QAAQ,CAAC,CAAC;CACxB;AAKD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,eAAe,GACpB,MAAM,EAAE,CAoBV;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,EAAE,CAOpD;AAQD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;CAChC,GAAG,OAAO,CAAC,QAAQ,CAAC,CA8CpB"}
|
package/dist/deploy/apply.d.ts
CHANGED
|
@@ -27,6 +27,14 @@ export interface ApplyDeps {
|
|
|
27
27
|
readonly fetcher?: typeof fetch;
|
|
28
28
|
/** Polling interval seconds (defaults to 10s; tests use 0). */
|
|
29
29
|
readonly pollIntervalSeconds?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Fired exactly once, when a terraform apply actually COMPLETES in this
|
|
32
|
+
* invocation (not when the operator aborts at the confirmation prompt, and
|
|
33
|
+
* not on a non-applied terminal state). Lets a caller — e.g. the PS-2071
|
|
34
|
+
* inline access-drift offer — distinguish a real reconcile from an abort,
|
|
35
|
+
* both of which exit 0.
|
|
36
|
+
*/
|
|
37
|
+
readonly onApplied?: () => void;
|
|
30
38
|
}
|
|
31
39
|
export declare function runDeployApply(opts: ApplyOptions, io: CliIo, deps?: ApplyDeps): Promise<ExitCode>;
|
|
32
40
|
//# sourceMappingURL=apply.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/deploy/apply.ts"],"names":[],"mappings":"AAqDA,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAU1D,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;iCAC6B;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC;wDACoD;IACpD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB;;;oEAGgE;IAChE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,iEAAiE;IACjE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,8DAA8D;IAC9D,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACxD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC5B,gEAAgE;IAChE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC,+DAA+D;IAC/D,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../src/deploy/apply.ts"],"names":[],"mappings":"AAqDA,OAAO,EAAc,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAU1D,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;iCAC6B;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC;wDACoD;IACpD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB;;;oEAGgE;IAChE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,iEAAiE;IACjE,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,8DAA8D;IAC9D,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACxD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC5B,gEAAgE;IAChE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC,+DAA+D;IAC/D,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC;;;;;;OAMG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACjC;AAwCD,wBAAsB,cAAc,CAClC,IAAI,EAAE,YAAY,EAClB,EAAE,EAAE,KAAK,EACT,IAAI,GAAE,SAAc,GACnB,OAAO,CAAC,QAAQ,CAAC,CAsLnB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* True when we can meaningfully prompt the operator — stdin is an interactive
|
|
3
|
+
* TTY. In a pipe, a CI job, or a redirected stdin this is false, and callers
|
|
4
|
+
* must fall back to a non-interactive path rather than block on a read that
|
|
5
|
+
* will never receive input.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isInteractive(): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Ask a question on stdin/stdout and resolve the raw typed line (untrimmed).
|
|
10
|
+
* The readline interface is always closed, even if the question rejects.
|
|
11
|
+
*/
|
|
12
|
+
export declare function defaultPrompt(question: string): Promise<string>;
|
|
13
|
+
//# sourceMappingURL=prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/io/prompt.ts"],"names":[],"mappings":"AAQA;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQrE"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Prefix every bundled launchpad skill directory shares. */
|
|
2
|
+
export declare const SKILL_PREFIX = "launchpad-";
|
|
3
|
+
/** Freshness of the on-disk Claude Code skill bundle vs the running CLI. */
|
|
4
|
+
export interface InstalledSkillsState {
|
|
5
|
+
/** True if any `launchpad-*` skill is installed under the skills dir. */
|
|
6
|
+
readonly present: boolean;
|
|
7
|
+
/** The bundle's `version:` frontmatter, or null if unreadable. */
|
|
8
|
+
readonly version: string | null;
|
|
9
|
+
}
|
|
10
|
+
/** Where `launchpad skills install` writes the bundle (mirrors skills.ts). */
|
|
11
|
+
export declare function skillsTargetDir(): string;
|
|
12
|
+
/**
|
|
13
|
+
* Synchronously read the installed skill-bundle freshness. Hot-path safe:
|
|
14
|
+
* one `readdir` + at most one small `readFile`, no network. A missing or
|
|
15
|
+
* unreadable skills dir is reported as `{ present: false }` (not an error).
|
|
16
|
+
*/
|
|
17
|
+
export declare function readInstalledSkills(): InstalledSkillsState;
|
|
18
|
+
/** Parse the `version:` frontmatter from a SKILL.md (inside the `---` fence). */
|
|
19
|
+
export declare function readSkillVersion(path: string): string | null;
|
|
20
|
+
//# sourceMappingURL=skills-bundle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills-bundle.d.ts","sourceRoot":"","sources":["../src/skills-bundle.ts"],"names":[],"mappings":"AAiBA,6DAA6D;AAC7D,eAAO,MAAM,YAAY,eAAe,CAAC;AAEzC,4EAA4E;AAC5E,MAAM,WAAW,oBAAoB;IACnC,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,8EAA8E;AAC9E,wBAAgB,eAAe,IAAI,MAAM,CAKxC;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,oBAAoB,CAc1D;AAED,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW5D"}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { CliIo, Command } from "./dispatcher.js";
|
|
2
2
|
import { resolveLatestVersion, type InstallChannel } from "./commands/update.js";
|
|
3
|
+
import { readInstalledSkills, type InstalledSkillsState } from "./skills-bundle.js";
|
|
4
|
+
export { readInstalledSkills };
|
|
5
|
+
export type { InstalledSkillsState };
|
|
3
6
|
/** Internal verb the detached refresh process runs. Hidden from help. */
|
|
4
7
|
export declare const INTERNAL_REFRESH_VERB = "__refresh-update-cache";
|
|
5
8
|
/** Re-check the registry at most once per this window (24h). */
|
|
@@ -15,19 +18,6 @@ export interface CacheState {
|
|
|
15
18
|
export declare function readCache(): CacheState | null;
|
|
16
19
|
/** Write the cache file (0600), creating ~/.launchpad if needed. Best-effort. */
|
|
17
20
|
export declare function writeCache(state: CacheState): void;
|
|
18
|
-
/** Freshness of the on-disk Claude Code skill bundle vs the running CLI. */
|
|
19
|
-
export interface InstalledSkillsState {
|
|
20
|
-
/** True if any `launchpad-*` skill is installed under the skills dir. */
|
|
21
|
-
readonly present: boolean;
|
|
22
|
-
/** The bundle's `version:` frontmatter, or null if unreadable. */
|
|
23
|
-
readonly version: string | null;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Synchronously read the installed skill-bundle freshness. Hot-path safe:
|
|
27
|
-
* one `readdir` + at most one small `readFile`, no network. A missing or
|
|
28
|
-
* unreadable skills dir is reported as `{ present: false }` (not an error).
|
|
29
|
-
*/
|
|
30
|
-
export declare function readInstalledSkills(): InstalledSkillsState;
|
|
31
21
|
/** Has the one-time "skills not installed" hint already been shown? */
|
|
32
22
|
export declare function absentHintShown(): boolean;
|
|
33
23
|
/** Record that the one-time absent hint was shown. Best-effort. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-notifier.d.ts","sourceRoot":"","sources":["../src/update-notifier.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"update-notifier.d.ts","sourceRoot":"","sources":["../src/update-notifier.ts"],"names":[],"mappings":"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"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.
|
|
1
|
+
export declare const CLI_VERSION = "0.47.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@m-kopa/launchpad-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Launchpad CLI
|
|
3
|
+
"version": "0.47.0",
|
|
4
|
+
"description": "Launchpad CLI — clone / deploy / review / merge against Launchpad-managed apps. Talks to the portal-bot endpoints (SCOPE-M-760 / T4).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"launchpad": "./dist/cli.js"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-content-pr
|
|
3
3
|
description: Push a content change to a Launchpad app via `launchpad deploy` and verify it shipped via `launchpad status`. Covers the post-first-deploy iteration loop (edit → deploy → verify) — subsequent deploys commit directly to the app repo's main and the Pages build runs asynchronously, so verification is its own step. Use when someone says "push a content change", "ship an update", "/launchpad-content-pr", "verify my deploy", or after `/launchpad-deploy` reports `done` and they want to follow up with an edit.
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.47.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -193,11 +193,15 @@ group passes (no need to use `deploy --dry-run` as a workaround).
|
|
|
193
193
|
> `access.allowed_entra_groups` and running a plain `launchpad deploy`
|
|
194
194
|
> ships content only — it does **not** reconcile the gateway's access
|
|
195
195
|
> policy, so the change won't take effect (and is refused once the
|
|
196
|
-
> access-drift gate is enforced).
|
|
197
|
-
>
|
|
198
|
-
>
|
|
199
|
-
>
|
|
200
|
-
>
|
|
196
|
+
> access-drift gate is enforced). When that refusal happens in an
|
|
197
|
+
> **interactive terminal**, the CLI now **offers to reconcile inline** —
|
|
198
|
+
> answering `y` routes straight into the gated apply flow (which shows
|
|
199
|
+
> the plan and confirms again before opening the PR). To change a live
|
|
200
|
+
> app's groups the user self-serves the **gated Terraform route**:
|
|
201
|
+
> `launchpad deploy --apply` (preview with `--dry-run`, confirm with
|
|
202
|
+
> `launchpad status`) — or just accept the inline offer. Do **not** tell
|
|
203
|
+
> the user to raise a request with the platform team — `--apply` IS the
|
|
204
|
+
> self-serve route. See `/launchpad-status` and the
|
|
201
205
|
> [auth & groups](https://get.launchpad.m-kopa.us/docs/concepts/auth-groups#changing-access-on-an-existing-app)
|
|
202
206
|
> doc.
|
|
203
207
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-deploy
|
|
3
3
|
description: Walk a Launchpad user through deploying an app from their local working directory (Model A — `launchpad init` + `launchpad deploy`). Wraps the CLI verbs end-to-end: detects the app shape, scaffolds `launchpad.yaml`, resolves the allowed Entra group via `launchpad groups`, bundles the CWD via `launchpad deploy`, and watches the rollout via `launchpad status`. Use when someone says "deploy a new app", "ship my app to Launchpad", "/launchpad-deploy", "I have an app locally — get it on Launchpad", or any variant. Resume/abandon for legacy in-flight provisioning is at the bottom.
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.47.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -235,10 +235,15 @@ Use whichever fits; `--dry-run` additionally previews the Terraform.
|
|
|
235
235
|
|
|
236
236
|
This `--group` flow sets the group at **first deploy**. Changing the
|
|
237
237
|
group on an **existing** app is a different path — a plain `launchpad
|
|
238
|
-
deploy` ships content only and won't move the gateway policy.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
238
|
+
deploy` ships content only and won't move the gateway policy. When a
|
|
239
|
+
plain deploy detects access drift in an **interactive terminal** it now
|
|
240
|
+
**offers to reconcile inline** (answer `y` to route straight into the
|
|
241
|
+
gated apply flow, which shows the plan and confirms again before it
|
|
242
|
+
opens the PR); in a non-interactive session it prints the manual
|
|
243
|
+
`launchpad deploy --apply` route and exits non-zero. Either way the fix
|
|
244
|
+
is the gated Terraform apply — see `/launchpad-content-pr`. Never route
|
|
245
|
+
the user to the platform team for a group change, and never tell them
|
|
246
|
+
access drift is unfixable: the self-serve reconcile is one keystroke.
|
|
242
247
|
|
|
243
248
|
If `launchpad groups list` fails with:
|
|
244
249
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-deploy-status
|
|
3
3
|
description: Show the current provisioning stage + failure reason for a Launchpad app via `launchpad status` (Model A drift + deployment_verified) and `launchpad apps` (lifecycle bucket), or watch provisioning live with `launchpad watch`. Renders the M-892 stage trace for in-flight provisioning, and is the canonical home for `launchpad recover` (repair a terminal-failed app record that is actually live). Use when someone says "what's the status of demo-X", "/launchpad-deploy-status", "is my deploy stuck", "watch my deploy go live", "watch provisioning", "my app says failed but it's serving", or after `/launchpad-deploy` reports a non-`done` terminal stage.
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.47.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-destroy
|
|
3
3
|
description: Tear down a Launchpad app end-to-end via `launchpad destroy` — Cloudflare Pages project, edge-auth wiring (gateway KV/audience entries, or the Access app for `auth: access` apps), custom hostname, platform-repo TF, and the app repo (archive-renamed). Owner-only verb with a two-step destructive confirmation. Use when someone says "destroy this app", "/launchpad-destroy", "tear down `<slug>`", "delete the app", or asks to clean up a smoke-test / orphan / retired app.
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.47.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-identity
|
|
3
3
|
description: Teach an app author how to use the signed-in user's identity inside a Launchpad app — read the gateway-forwarded X-Launchpad-User-Assertion in a Pages Function, VERIFY it with @m-kopa/platform-auth (fail-closed), and show who's logged in (sub/email/name). Use when someone says "who is logged in", "show the current user", "get the user's email in my app", "auth in my launchpad app", "read the user identity", "/launchpad-identity", or is wiring up an /api/me for a gateway-fronted app.
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.47.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-onboard
|
|
3
3
|
description: One-time setup for the Launchpad CLI + Claude Code skill bundle. Verifies the `launchpad` CLI is installed and current, runs `launchpad whoami` to confirm the session is fresh, and checks the bundled skills are installed and in lock-step with the CLI. Idempotent — safe to re-run any time. Use when someone says "set me up for Launchpad", "I just got a new machine and want to use Launchpad", "/launchpad-onboard", or any of the other launchpad-* skills fails on a prereq check.
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.47.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-report
|
|
3
3
|
description: File a bug report or feature request to the Launchpad team's tracker from the CLI. Use when someone reports something broken, hits an error in a launchpad command, or wishes a feature existed — e.g. "this is broken", "report a bug", "can you file that", "I wish launchpad could…", "/launchpad-bug", "/launchpad-feature". Always confirm and show exactly what you'll send before filing; never file silently.
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.47.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: launchpad-status
|
|
3
3
|
description: Show whether a Launchpad app's local launchpad.yaml matches what's deployed, and read the deployed manifest. Wraps `launchpad pull` (fetch deployed YAML) and `launchpad status` (drift report). Use when someone says "is my app in sync", "what's deployed", "show drift", "/launchpad-status", "/launchpad-pull", or after `launchpad deploy` to verify the change landed.
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.47.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|