@m-kopa/launchpad-cli 0.46.2 → 0.47.1
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 +44 -0
- package/dist/cli.js +189 -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,50 @@ 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.1 — 2026-07-08
|
|
10
|
+
|
|
11
|
+
**Fix: no false-success exit code when the inline access-drift apply is aborted
|
|
12
|
+
(PS-2071).** 0.47.0 gated the "✓ Access reconciled" *message* on a real apply,
|
|
13
|
+
but `handleAccessDrift` still returned the apply's raw exit code — and
|
|
14
|
+
`deploy --apply` exits `0` both on a genuine apply and when the operator reviews
|
|
15
|
+
the plan and aborts at its own prompt. So accepting the inline offer and then
|
|
16
|
+
aborting made `launchpad deploy` exit `0` for a no-op — inconsistent with
|
|
17
|
+
declining the offer (which exits `1`) and a false-success signal to any script.
|
|
18
|
+
Now the exit code is gated on the same real-apply signal: an abort maps to `1`
|
|
19
|
+
(nothing changed), while a genuine apply failure's code (`1`/`2`/`4`) passes
|
|
20
|
+
through unchanged. Caught by CodeRabbit review of the 0.47.0 change.
|
|
21
|
+
|
|
22
|
+
## 0.47.0 — 2026-07-07
|
|
23
|
+
|
|
24
|
+
**`launchpad deploy` offers to reconcile access drift inline (PS-2071).** When a
|
|
25
|
+
plain deploy is refused because the manifest's access groups diverge from what
|
|
26
|
+
the gateway enforces, an **interactive terminal** now *offers to reconcile right
|
|
27
|
+
there* — answer `y` and it routes straight into the gated `launchpad deploy
|
|
28
|
+
--apply` flow instead of leaving you at a dead-end error to re-read, find the
|
|
29
|
+
flag, and re-run. This is convenience only: it changes no control. The apply
|
|
30
|
+
still shows the exact plan and prompts again before opening the PR, and the
|
|
31
|
+
byte-equal validator, the second-person `allowed-groups-change` label gate for
|
|
32
|
+
any access-widening change, and Terraform-as-sole-writer all still apply. A
|
|
33
|
+
**non-interactive** session (CI, a pipe, redirected stdin) never auto-runs an
|
|
34
|
+
access change — it prints the manual `--apply` route and exits non-zero so a
|
|
35
|
+
script fails loudly. After a successful reconcile the CLI points you back at
|
|
36
|
+
`launchpad deploy` to ship your content.
|
|
37
|
+
|
|
38
|
+
**`launchpad update` resyncs drifted skills even when the CLI is already current
|
|
39
|
+
(PS-2058).** Previously the skill bundle was re-synced only after an actual CLI
|
|
40
|
+
upgrade, so a machine already on the latest CLI could sit on a stale skill
|
|
41
|
+
bundle indefinitely with no warning. Now, when `update` finds the CLI already
|
|
42
|
+
current, it also checks the installed skill bundle and:
|
|
43
|
+
|
|
44
|
+
- **`launchpad update`** auto-resyncs a drifted bundle (best-effort; the update
|
|
45
|
+
still exits `0` regardless — a skills hiccup never fails it).
|
|
46
|
+
- **`launchpad update --check`** reports the drift as an informational line
|
|
47
|
+
pointing at `launchpad skills update`; exit codes are unchanged (`10` stays
|
|
48
|
+
CLI-update-only).
|
|
49
|
+
|
|
50
|
+
An in-sync bundle stays silent; an absent bundle is left to the passive
|
|
51
|
+
notifier's one-time hint.
|
|
52
|
+
|
|
9
53
|
## 0.46.2 — 2026-07-07
|
|
10
54
|
|
|
11
55
|
Fix: the apply-status poll no longer mistakes the endpoint's own
|
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.1";
|
|
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) {
|
|
@@ -6556,6 +6558,81 @@ function renderManifestError(loaded, io) {
|
|
|
6556
6558
|
}
|
|
6557
6559
|
}
|
|
6558
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
|
+
return exit;
|
|
6618
|
+
}
|
|
6619
|
+
return exit === 0 ? 1 : exit;
|
|
6620
|
+
}
|
|
6621
|
+
|
|
6622
|
+
// src/io/prompt.ts
|
|
6623
|
+
function isInteractive() {
|
|
6624
|
+
return Boolean(process.stdin.isTTY);
|
|
6625
|
+
}
|
|
6626
|
+
async function defaultPrompt2(question) {
|
|
6627
|
+
const { createInterface } = await import("node:readline/promises");
|
|
6628
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
6629
|
+
try {
|
|
6630
|
+
return await rl.question(question);
|
|
6631
|
+
} finally {
|
|
6632
|
+
rl.close();
|
|
6633
|
+
}
|
|
6634
|
+
}
|
|
6635
|
+
|
|
6559
6636
|
// src/deploy/dry-run.ts
|
|
6560
6637
|
import { execFileSync } from "node:child_process";
|
|
6561
6638
|
import { resolve as resolve7 } from "node:path";
|
|
@@ -7402,23 +7479,17 @@ async function runModelADeploy(args) {
|
|
|
7402
7479
|
return 1;
|
|
7403
7480
|
}
|
|
7404
7481
|
if (result.status === 409 && errorCode === "access_drift") {
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
io
|
|
7410
|
-
|
|
7411
|
-
|
|
7412
|
-
|
|
7413
|
-
|
|
7414
|
-
|
|
7415
|
-
|
|
7416
|
-
io.err(" `launchpad deploy` ships content only and cannot change access.");
|
|
7417
|
-
io.err(" Run `launchpad deploy --apply` to reconcile — it opens the gated TF PR");
|
|
7418
|
-
io.err(" (access-widening changes need the allowed-groups-change label from a");
|
|
7419
|
-
io.err(" second person) and waits for the apply verdict. Then redeploy your content.");
|
|
7420
|
-
io.err(" Nothing was committed by this attempt.");
|
|
7421
|
-
return 1;
|
|
7482
|
+
return handleAccessDrift({
|
|
7483
|
+
slug,
|
|
7484
|
+
manifestPath,
|
|
7485
|
+
body,
|
|
7486
|
+
io,
|
|
7487
|
+
deps: {
|
|
7488
|
+
interactive: isInteractive(),
|
|
7489
|
+
prompt: defaultPrompt2,
|
|
7490
|
+
runApply: runDeployApply
|
|
7491
|
+
}
|
|
7492
|
+
});
|
|
7422
7493
|
}
|
|
7423
7494
|
io.err(`launchpad deploy: bot rejected the upload (HTTP ${result.status}, ${errorCode}).`);
|
|
7424
7495
|
if (typeof body.message === "string") {
|
|
@@ -8885,7 +8956,7 @@ var SLUG_REGEX3 = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
|
|
|
8885
8956
|
var initCommand = {
|
|
8886
8957
|
name: "init",
|
|
8887
8958
|
summary: "scaffold a new launchpad.yaml in the current directory",
|
|
8888
|
-
run: (args, io) => runInit(args, io,
|
|
8959
|
+
run: (args, io) => runInit(args, io, defaultPrompt3)
|
|
8889
8960
|
};
|
|
8890
8961
|
async function runInit(args, io, prompt) {
|
|
8891
8962
|
const parsed = parseArgs6(args);
|
|
@@ -9375,7 +9446,7 @@ function ensureGitignoreEntries(path10, entries) {
|
|
|
9375
9446
|
}
|
|
9376
9447
|
return added;
|
|
9377
9448
|
}
|
|
9378
|
-
async function
|
|
9449
|
+
async function defaultPrompt3(question, fallback) {
|
|
9379
9450
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
9380
9451
|
try {
|
|
9381
9452
|
const suffix = fallback !== undefined ? ` [${fallback}]` : "";
|
|
@@ -10056,7 +10127,7 @@ import { stdin as input, stdout as output } from "node:process";
|
|
|
10056
10127
|
var destroyCommand = {
|
|
10057
10128
|
name: "destroy",
|
|
10058
10129
|
summary: "tear down a Launchpad app (owner-only, destructive)",
|
|
10059
|
-
run: (args, io) => runDestroy(args, io,
|
|
10130
|
+
run: (args, io) => runDestroy(args, io, defaultPrompt4, defaultIsTty)
|
|
10060
10131
|
};
|
|
10061
10132
|
var SLUG_RE10 = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
|
|
10062
10133
|
var SLUG_MIN_LENGTH = 3;
|
|
@@ -10288,7 +10359,7 @@ function renderBotError2(status, env, slug, io) {
|
|
|
10288
10359
|
io.err(`launchpad destroy: bot returned HTTP ${status} (error=${errorCode}): ${message}`);
|
|
10289
10360
|
}
|
|
10290
10361
|
}
|
|
10291
|
-
async function
|
|
10362
|
+
async function defaultPrompt4(question) {
|
|
10292
10363
|
const rl = createInterface2({ input, output });
|
|
10293
10364
|
try {
|
|
10294
10365
|
return await rl.question(question);
|
|
@@ -10957,7 +11028,7 @@ async function runRollback(opts, io, deps = {}) {
|
|
|
10957
11028
|
io.out(`Rollback target: ${verifiedSha.slice(0, 12)} (${manifestRelpath})`);
|
|
10958
11029
|
renderDiff(current, parsed.manifest, io);
|
|
10959
11030
|
if (!opts.yes) {
|
|
10960
|
-
const prompt = deps.prompt ??
|
|
11031
|
+
const prompt = deps.prompt ?? defaultPrompt5;
|
|
10961
11032
|
if (!process.stdin.isTTY && deps.prompt === undefined) {
|
|
10962
11033
|
io.err("launchpad rollback: refusing to rollback non-interactively without --yes.");
|
|
10963
11034
|
return 64;
|
|
@@ -11045,7 +11116,7 @@ function formatHostnames(hs) {
|
|
|
11045
11116
|
return hs[0];
|
|
11046
11117
|
return `[${hs.join(", ")}]`;
|
|
11047
11118
|
}
|
|
11048
|
-
async function
|
|
11119
|
+
async function defaultPrompt5(question) {
|
|
11049
11120
|
const rl = createInterface3({ input: process.stdin, output: process.stderr });
|
|
11050
11121
|
try {
|
|
11051
11122
|
return await rl.question(question);
|
|
@@ -11352,7 +11423,7 @@ async function offerRedeploy(slug, opts, io, deps) {
|
|
|
11352
11423
|
const isTty = (deps.isTty ?? (() => process.stdin.isTTY === true))();
|
|
11353
11424
|
let doRedeploy = wantsAuto;
|
|
11354
11425
|
if (!wantsAuto && isTty) {
|
|
11355
|
-
const prompt = deps.prompt ??
|
|
11426
|
+
const prompt = deps.prompt ?? defaultPrompt6;
|
|
11356
11427
|
let answer;
|
|
11357
11428
|
try {
|
|
11358
11429
|
answer = (await prompt(`Redeploy ${slug} now to activate? [y/N] `)).trim().toLowerCase();
|
|
@@ -11373,7 +11444,7 @@ async function offerRedeploy(slug, opts, io, deps) {
|
|
|
11373
11444
|
};
|
|
11374
11445
|
return run({ slug, file: opts.file ?? null, verify: true, verifyTimeoutMs: 180000 }, io, rdeps);
|
|
11375
11446
|
}
|
|
11376
|
-
async function
|
|
11447
|
+
async function defaultPrompt6(question) {
|
|
11377
11448
|
const rl = createInterface4({ input: input2, output: output2 });
|
|
11378
11449
|
try {
|
|
11379
11450
|
return await rl.question(question);
|
|
@@ -12114,9 +12185,45 @@ function printHelp3(io) {
|
|
|
12114
12185
|
|
|
12115
12186
|
// src/commands/skills.ts
|
|
12116
12187
|
import { fileURLToPath } from "node:url";
|
|
12117
|
-
import { dirname as dirname8, join as
|
|
12188
|
+
import { dirname as dirname8, join as join14, resolve as resolve12 } from "node:path";
|
|
12118
12189
|
import { promises as fs6, existsSync as existsSync14 } from "node:fs";
|
|
12190
|
+
|
|
12191
|
+
// src/skills-bundle.ts
|
|
12119
12192
|
import { homedir as homedir2 } from "node:os";
|
|
12193
|
+
import { join as join13 } from "node:path";
|
|
12194
|
+
import { readFileSync as readFileSync18, readdirSync as readdirSync2 } from "node:fs";
|
|
12195
|
+
var SKILL_PREFIX = "launchpad-";
|
|
12196
|
+
function skillsTargetDir() {
|
|
12197
|
+
return process.env.LAUNCHPAD_SKILLS_TARGET_DIR ?? join13(homedir2(), ".claude", "skills");
|
|
12198
|
+
}
|
|
12199
|
+
function readInstalledSkills() {
|
|
12200
|
+
try {
|
|
12201
|
+
const dir = skillsTargetDir();
|
|
12202
|
+
const bundle = readdirSync2(dir, { withFileTypes: true }).find((e) => e.isDirectory() && e.name.startsWith(SKILL_PREFIX));
|
|
12203
|
+
if (!bundle)
|
|
12204
|
+
return { present: false, version: null };
|
|
12205
|
+
return {
|
|
12206
|
+
present: true,
|
|
12207
|
+
version: readSkillVersion(join13(dir, bundle.name, "SKILL.md"))
|
|
12208
|
+
};
|
|
12209
|
+
} catch {
|
|
12210
|
+
return { present: false, version: null };
|
|
12211
|
+
}
|
|
12212
|
+
}
|
|
12213
|
+
function readSkillVersion(path13) {
|
|
12214
|
+
try {
|
|
12215
|
+
const text = readFileSync18(path13, "utf8");
|
|
12216
|
+
const fenceEnd = text.indexOf(`
|
|
12217
|
+
---`, 4);
|
|
12218
|
+
const front = fenceEnd === -1 ? text.slice(0, 1024) : text.slice(0, fenceEnd);
|
|
12219
|
+
const m = /^version:\s*(.+?)\s*$/m.exec(front);
|
|
12220
|
+
return m === null ? null : m[1] ?? null;
|
|
12221
|
+
} catch {
|
|
12222
|
+
return null;
|
|
12223
|
+
}
|
|
12224
|
+
}
|
|
12225
|
+
|
|
12226
|
+
// src/commands/skills.ts
|
|
12120
12227
|
var BUNDLE_PREFIX = "launchpad-";
|
|
12121
12228
|
var BUNDLED_SKILLS = [
|
|
12122
12229
|
"launchpad-onboard",
|
|
@@ -12178,7 +12285,7 @@ function printHelp4(io) {
|
|
|
12178
12285
|
}
|
|
12179
12286
|
function resolveInstallEnv() {
|
|
12180
12287
|
const bundleDir = process.env.LAUNCHPAD_SKILLS_BUNDLE_DIR ?? defaultBundleDir();
|
|
12181
|
-
const userSkillsDir =
|
|
12288
|
+
const userSkillsDir = skillsTargetDir();
|
|
12182
12289
|
return { bundleDir, userSkillsDir };
|
|
12183
12290
|
}
|
|
12184
12291
|
function defaultBundleDir() {
|
|
@@ -12188,7 +12295,7 @@ function defaultBundleDir() {
|
|
|
12188
12295
|
resolve12(here, "..", "..", "skills")
|
|
12189
12296
|
];
|
|
12190
12297
|
for (const c of candidates) {
|
|
12191
|
-
if (existsSync14(
|
|
12298
|
+
if (existsSync14(join14(c, "launchpad-onboard", "SKILL.md"))) {
|
|
12192
12299
|
return c;
|
|
12193
12300
|
}
|
|
12194
12301
|
}
|
|
@@ -12209,12 +12316,12 @@ async function doInstall(io) {
|
|
|
12209
12316
|
}
|
|
12210
12317
|
let installed = 0;
|
|
12211
12318
|
for (const skill of BUNDLED_SKILLS) {
|
|
12212
|
-
const src =
|
|
12319
|
+
const src = join14(env.bundleDir, skill);
|
|
12213
12320
|
if (!await isDir(src)) {
|
|
12214
12321
|
io.err(`launchpad skills install: bundled skill "${skill}" missing from ${env.bundleDir} — package is incomplete.`);
|
|
12215
12322
|
return 1;
|
|
12216
12323
|
}
|
|
12217
|
-
const dest =
|
|
12324
|
+
const dest = join14(env.userSkillsDir, skill);
|
|
12218
12325
|
await fs6.rm(dest, { recursive: true, force: true });
|
|
12219
12326
|
await fs6.cp(src, dest, { recursive: true });
|
|
12220
12327
|
installed++;
|
|
@@ -12239,7 +12346,7 @@ async function doUninstall(io) {
|
|
|
12239
12346
|
continue;
|
|
12240
12347
|
if (!isBundleManaged(entry.name))
|
|
12241
12348
|
continue;
|
|
12242
|
-
const target =
|
|
12349
|
+
const target = join14(env.userSkillsDir, entry.name);
|
|
12243
12350
|
await fs6.rm(target, { recursive: true, force: true });
|
|
12244
12351
|
removed++;
|
|
12245
12352
|
io.out(`✗ removed ${target}`);
|
|
@@ -12262,7 +12369,7 @@ async function doList(io) {
|
|
|
12262
12369
|
}
|
|
12263
12370
|
const width = managedDirs.reduce((n, s) => Math.max(n, s.length), 0);
|
|
12264
12371
|
for (const name of managedDirs) {
|
|
12265
|
-
const skillFile =
|
|
12372
|
+
const skillFile = join14(env.userSkillsDir, name, "SKILL.md");
|
|
12266
12373
|
const version = await readVersion(skillFile);
|
|
12267
12374
|
io.out(` ${name.padEnd(width + 2)}${version ?? "(no version)"}`);
|
|
12268
12375
|
}
|
|
@@ -12297,9 +12404,9 @@ function describe29(e) {
|
|
|
12297
12404
|
import { execFile, spawn as spawn5 } from "node:child_process";
|
|
12298
12405
|
import { promisify } from "node:util";
|
|
12299
12406
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
12300
|
-
import { dirname as dirname9, resolve as resolve13, relative as relative4, isAbsolute as isAbsolute2, join as
|
|
12407
|
+
import { dirname as dirname9, resolve as resolve13, relative as relative4, isAbsolute as isAbsolute2, join as join15 } from "node:path";
|
|
12301
12408
|
import { homedir as homedir3, tmpdir } from "node:os";
|
|
12302
|
-
import { readFileSync as
|
|
12409
|
+
import { readFileSync as readFileSync19, mkdtempSync, writeFileSync as writeFileSync7, rmSync as rmSync2 } from "node:fs";
|
|
12303
12410
|
|
|
12304
12411
|
// src/commands/channel-auth.ts
|
|
12305
12412
|
import { createServer as createServer2 } from "node:http";
|
|
@@ -12420,7 +12527,7 @@ var PKG = "@m-kopa/launchpad-cli";
|
|
|
12420
12527
|
var REGISTRY = "https://registry.npmjs.org";
|
|
12421
12528
|
var CHANNEL_VERSION_URL = "https://get.launchpad.m-kopa.us/version.json";
|
|
12422
12529
|
var CHANNEL_INSTALL_URL = "https://get.launchpad.m-kopa.us";
|
|
12423
|
-
var CHANNEL_MARKER =
|
|
12530
|
+
var CHANNEL_MARKER = join15(homedir3(), ".launchpad", "channel");
|
|
12424
12531
|
var EXIT_UPDATE_AVAILABLE = 10;
|
|
12425
12532
|
var UPGRADE_ARGS = {
|
|
12426
12533
|
npm: ["install", "-g", `${PKG}@latest`],
|
|
@@ -12497,7 +12604,8 @@ function defaultDeps() {
|
|
|
12497
12604
|
detectPm: detectPackageManager2,
|
|
12498
12605
|
runUpgrade,
|
|
12499
12606
|
runChannelInstaller,
|
|
12500
|
-
syncSkills: runSkillsSync
|
|
12607
|
+
syncSkills: runSkillsSync,
|
|
12608
|
+
readInstalledSkills
|
|
12501
12609
|
};
|
|
12502
12610
|
}
|
|
12503
12611
|
async function runSkillsSync() {
|
|
@@ -12520,7 +12628,25 @@ async function runSkillsSync() {
|
|
|
12520
12628
|
async function syncSkillsAfterUpgrade(io, deps) {
|
|
12521
12629
|
const result = await deps.syncSkills();
|
|
12522
12630
|
if (result.ok) {
|
|
12523
|
-
io.out("✓ Claude Code skill bundle re-synced to the
|
|
12631
|
+
io.out("✓ Claude Code skill bundle re-synced to match the CLI.");
|
|
12632
|
+
} else {
|
|
12633
|
+
io.err("Note: couldn't auto-sync the skill bundle — run `launchpad skills update` to finish.");
|
|
12634
|
+
}
|
|
12635
|
+
}
|
|
12636
|
+
async function reconcileSkillBundleWhenCurrent(io, deps, checkOnly) {
|
|
12637
|
+
const skills = deps.readInstalledSkills();
|
|
12638
|
+
if (!skills.present || skills.version === null)
|
|
12639
|
+
return;
|
|
12640
|
+
if (compareVersions(deps.cliVersion, skills.version) !== 1)
|
|
12641
|
+
return;
|
|
12642
|
+
if (checkOnly) {
|
|
12643
|
+
io.out(`Skill bundle ${skills.version} is behind the CLI ${deps.cliVersion} — run \`launchpad skills update\`.`);
|
|
12644
|
+
return;
|
|
12645
|
+
}
|
|
12646
|
+
io.out(`Skill bundle ${skills.version} is behind the CLI ${deps.cliVersion}; re-syncing…`);
|
|
12647
|
+
const result = await deps.syncSkills();
|
|
12648
|
+
if (result.ok) {
|
|
12649
|
+
io.out("✓ Claude Code skill bundle re-synced to match the CLI.");
|
|
12524
12650
|
} else {
|
|
12525
12651
|
io.err("Note: couldn't auto-sync the skill bundle — run `launchpad skills update` to finish.");
|
|
12526
12652
|
}
|
|
@@ -12530,8 +12656,8 @@ async function openSystemBrowser(url) {
|
|
|
12530
12656
|
await execFileAsync(opener, [url]);
|
|
12531
12657
|
}
|
|
12532
12658
|
async function runInstallerScript(script) {
|
|
12533
|
-
const dir = mkdtempSync(
|
|
12534
|
-
const file =
|
|
12659
|
+
const dir = mkdtempSync(join15(tmpdir(), "launchpad-update-"));
|
|
12660
|
+
const file = join15(dir, "install.sh");
|
|
12535
12661
|
try {
|
|
12536
12662
|
writeFileSync7(file, script, { mode: 448 });
|
|
12537
12663
|
return await new Promise((resolvePromise) => {
|
|
@@ -12555,7 +12681,7 @@ function resolveLatestVersion() {
|
|
|
12555
12681
|
}
|
|
12556
12682
|
function detectInstallChannel() {
|
|
12557
12683
|
try {
|
|
12558
|
-
return
|
|
12684
|
+
return readFileSync19(CHANNEL_MARKER, "utf8").trim() === "platform" ? "platform" : "github";
|
|
12559
12685
|
} catch {
|
|
12560
12686
|
return "github";
|
|
12561
12687
|
}
|
|
@@ -12681,6 +12807,7 @@ async function runUpdate(args, io, deps) {
|
|
|
12681
12807
|
const current = deps.cliVersion;
|
|
12682
12808
|
if (compareVersions(current, latest.version) >= 0) {
|
|
12683
12809
|
io.out(`launchpad-cli is up to date (${current}).`);
|
|
12810
|
+
await reconcileSkillBundleWhenCurrent(io, deps, parsed.check);
|
|
12684
12811
|
return 0;
|
|
12685
12812
|
}
|
|
12686
12813
|
if (parsed.check) {
|
|
@@ -12762,7 +12889,7 @@ function printHelp5(io) {
|
|
|
12762
12889
|
}
|
|
12763
12890
|
|
|
12764
12891
|
// src/commands/validate.ts
|
|
12765
|
-
import { existsSync as existsSync15, readFileSync as
|
|
12892
|
+
import { existsSync as existsSync15, readFileSync as readFileSync20, readdirSync as readdirSync3, statSync as statSync2 } from "node:fs";
|
|
12766
12893
|
import { dirname as dirname10, resolve as resolve14 } from "node:path";
|
|
12767
12894
|
var validateCommand = {
|
|
12768
12895
|
name: "validate",
|
|
@@ -12800,7 +12927,7 @@ function checkBoundary(manifestPath, declared) {
|
|
|
12800
12927
|
}
|
|
12801
12928
|
let manifestYaml;
|
|
12802
12929
|
try {
|
|
12803
|
-
manifestYaml =
|
|
12930
|
+
manifestYaml = readFileSync20(manifestPath, "utf8");
|
|
12804
12931
|
} catch {
|
|
12805
12932
|
manifestYaml = null;
|
|
12806
12933
|
}
|
|
@@ -12841,7 +12968,7 @@ function checkStaticServeDir(manifestPath, manifest) {
|
|
|
12841
12968
|
}
|
|
12842
12969
|
let entries;
|
|
12843
12970
|
try {
|
|
12844
|
-
entries =
|
|
12971
|
+
entries = readdirSync3(abs);
|
|
12845
12972
|
} catch (e) {
|
|
12846
12973
|
return [`failed to read static served directory '${serveDir}/': ${describe30(e)}`];
|
|
12847
12974
|
}
|
|
@@ -13715,21 +13842,20 @@ var listOwnersCommand = makeListOwnersCommand();
|
|
|
13715
13842
|
// src/update-notifier.ts
|
|
13716
13843
|
import { spawn as spawn6 } from "node:child_process";
|
|
13717
13844
|
import { homedir as homedir4 } from "node:os";
|
|
13718
|
-
import { join as
|
|
13845
|
+
import { join as join16 } from "node:path";
|
|
13719
13846
|
import {
|
|
13720
13847
|
existsSync as existsSync16,
|
|
13721
13848
|
mkdirSync as mkdirSync4,
|
|
13722
|
-
readFileSync as
|
|
13723
|
-
readdirSync as readdirSync3,
|
|
13849
|
+
readFileSync as readFileSync21,
|
|
13724
13850
|
writeFileSync as writeFileSync8
|
|
13725
13851
|
} from "node:fs";
|
|
13726
13852
|
var INTERNAL_REFRESH_VERB = "__refresh-update-cache";
|
|
13727
13853
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
13728
13854
|
var OPT_OUT_ENV = "LAUNCHPAD_NO_UPDATE_NOTIFIER";
|
|
13729
|
-
var CACHE_FILE =
|
|
13855
|
+
var CACHE_FILE = join16(homedir4(), ".launchpad", "update-check.json");
|
|
13730
13856
|
function readCache2() {
|
|
13731
13857
|
try {
|
|
13732
|
-
const raw = JSON.parse(
|
|
13858
|
+
const raw = JSON.parse(readFileSync21(CACHE_FILE, "utf8"));
|
|
13733
13859
|
if (typeof raw === "object" && raw !== null && typeof raw.checkedAt === "number") {
|
|
13734
13860
|
const latest = raw.latest;
|
|
13735
13861
|
return {
|
|
@@ -13742,39 +13868,12 @@ function readCache2() {
|
|
|
13742
13868
|
}
|
|
13743
13869
|
function writeCache2(state) {
|
|
13744
13870
|
try {
|
|
13745
|
-
mkdirSync4(
|
|
13871
|
+
mkdirSync4(join16(homedir4(), ".launchpad"), { recursive: true });
|
|
13746
13872
|
writeFileSync8(CACHE_FILE, `${JSON.stringify(state)}
|
|
13747
13873
|
`, { mode: 384 });
|
|
13748
13874
|
} catch {}
|
|
13749
13875
|
}
|
|
13750
|
-
var
|
|
13751
|
-
var SKILLS_HINT_MARKER = join15(homedir4(), ".launchpad", "skills-hint-shown");
|
|
13752
|
-
function skillsTargetDir() {
|
|
13753
|
-
return process.env.LAUNCHPAD_SKILLS_TARGET_DIR ?? join15(homedir4(), ".claude", "skills");
|
|
13754
|
-
}
|
|
13755
|
-
function readInstalledSkills() {
|
|
13756
|
-
try {
|
|
13757
|
-
const dir = skillsTargetDir();
|
|
13758
|
-
const bundle = readdirSync3(dir, { withFileTypes: true }).find((e) => e.isDirectory() && e.name.startsWith(SKILL_PREFIX));
|
|
13759
|
-
if (!bundle)
|
|
13760
|
-
return { present: false, version: null };
|
|
13761
|
-
return { present: true, version: readSkillVersion(join15(dir, bundle.name, "SKILL.md")) };
|
|
13762
|
-
} catch {
|
|
13763
|
-
return { present: false, version: null };
|
|
13764
|
-
}
|
|
13765
|
-
}
|
|
13766
|
-
function readSkillVersion(path13) {
|
|
13767
|
-
try {
|
|
13768
|
-
const text = readFileSync20(path13, "utf8");
|
|
13769
|
-
const fenceEnd = text.indexOf(`
|
|
13770
|
-
---`, 4);
|
|
13771
|
-
const front = fenceEnd === -1 ? text.slice(0, 1024) : text.slice(0, fenceEnd);
|
|
13772
|
-
const m = /^version:\s*(.+?)\s*$/m.exec(front);
|
|
13773
|
-
return m === null ? null : m[1] ?? null;
|
|
13774
|
-
} catch {
|
|
13775
|
-
return null;
|
|
13776
|
-
}
|
|
13777
|
-
}
|
|
13876
|
+
var SKILLS_HINT_MARKER = join16(homedir4(), ".launchpad", "skills-hint-shown");
|
|
13778
13877
|
function absentHintShown() {
|
|
13779
13878
|
try {
|
|
13780
13879
|
return existsSync16(SKILLS_HINT_MARKER);
|
|
@@ -13784,7 +13883,7 @@ function absentHintShown() {
|
|
|
13784
13883
|
}
|
|
13785
13884
|
function markAbsentHintShown() {
|
|
13786
13885
|
try {
|
|
13787
|
-
mkdirSync4(
|
|
13886
|
+
mkdirSync4(join16(homedir4(), ".launchpad"), { recursive: true });
|
|
13788
13887
|
writeFileSync8(SKILLS_HINT_MARKER, `${Date.now()}
|
|
13789
13888
|
`, { mode: 384 });
|
|
13790
13889
|
} catch {}
|
|
@@ -13887,8 +13986,8 @@ var refreshUpdateCacheCommand = {
|
|
|
13887
13986
|
// src/telemetry.ts
|
|
13888
13987
|
import { spawn as spawn7 } from "node:child_process";
|
|
13889
13988
|
import { homedir as homedir5 } from "node:os";
|
|
13890
|
-
import { join as
|
|
13891
|
-
import { existsSync as existsSync17, mkdirSync as mkdirSync5, readFileSync as
|
|
13989
|
+
import { join as join17 } from "node:path";
|
|
13990
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync5, readFileSync as readFileSync22, writeFileSync as writeFileSync9 } from "node:fs";
|
|
13892
13991
|
import { randomUUID } from "node:crypto";
|
|
13893
13992
|
var INTERNAL_EMIT_VERB = "__emit-telemetry";
|
|
13894
13993
|
var POSTHOG_HOST = "https://us.i.posthog.com";
|
|
@@ -13896,8 +13995,8 @@ var POSTHOG_PROJECT_KEY = "phc_CYaCuETanWc36TMTiB7cdkdnmPPhFZCkDGmWGLaNkXnb";
|
|
|
13896
13995
|
var CAPTURE_PATH = "/i/v0/e/";
|
|
13897
13996
|
var SEND_TIMEOUT_MS = 3000;
|
|
13898
13997
|
var APP = "launchpad-cli";
|
|
13899
|
-
var DEVICE_ID_FILE =
|
|
13900
|
-
var FIRST_RUN_MARKER =
|
|
13998
|
+
var DEVICE_ID_FILE = join17(homedir5(), ".launchpad", "telemetry-id.json");
|
|
13999
|
+
var FIRST_RUN_MARKER = join17(homedir5(), ".launchpad", "telemetry-notice-shown");
|
|
13901
14000
|
function buildEvent(ctx, identity, nowMs) {
|
|
13902
14001
|
const properties = {
|
|
13903
14002
|
app: APP,
|
|
@@ -13964,14 +14063,14 @@ async function resolveIdentityReal() {
|
|
|
13964
14063
|
}
|
|
13965
14064
|
function getOrCreateDeviceId() {
|
|
13966
14065
|
try {
|
|
13967
|
-
const raw = JSON.parse(
|
|
14066
|
+
const raw = JSON.parse(readFileSync22(DEVICE_ID_FILE, "utf8"));
|
|
13968
14067
|
const id2 = raw?.deviceId;
|
|
13969
14068
|
if (typeof id2 === "string" && id2)
|
|
13970
14069
|
return id2;
|
|
13971
14070
|
} catch {}
|
|
13972
14071
|
const id = randomUUID();
|
|
13973
14072
|
try {
|
|
13974
|
-
mkdirSync5(
|
|
14073
|
+
mkdirSync5(join17(homedir5(), ".launchpad"), { recursive: true });
|
|
13975
14074
|
writeFileSync9(DEVICE_ID_FILE, `${JSON.stringify({ deviceId: id })}
|
|
13976
14075
|
`, {
|
|
13977
14076
|
mode: 384
|
|
@@ -14049,7 +14148,7 @@ function firstRunNoticeShown() {
|
|
|
14049
14148
|
}
|
|
14050
14149
|
function markFirstRunNotice() {
|
|
14051
14150
|
try {
|
|
14052
|
-
mkdirSync5(
|
|
14151
|
+
mkdirSync5(join17(homedir5(), ".launchpad"), { recursive: true });
|
|
14053
14152
|
writeFileSync9(FIRST_RUN_MARKER, `${Date.now()}
|
|
14054
14153
|
`, { mode: 384 });
|
|
14055
14154
|
} catch {}
|
|
@@ -14115,11 +14214,11 @@ import { createInterface as createInterface7 } from "node:readline/promises";
|
|
|
14115
14214
|
import { platform } from "node:os";
|
|
14116
14215
|
|
|
14117
14216
|
// src/report/breadcrumb.ts
|
|
14118
|
-
import { writeFileSync as writeFileSync10, readFileSync as
|
|
14217
|
+
import { writeFileSync as writeFileSync10, readFileSync as readFileSync23, mkdirSync as mkdirSync6 } from "node:fs";
|
|
14119
14218
|
import { homedir as homedir6 } from "node:os";
|
|
14120
|
-
import { join as
|
|
14219
|
+
import { join as join18, dirname as dirname11 } from "node:path";
|
|
14121
14220
|
function breadcrumbPath() {
|
|
14122
|
-
return
|
|
14221
|
+
return join18(homedir6(), ".launchpad", "last-run.json");
|
|
14123
14222
|
}
|
|
14124
14223
|
function writeBreadcrumb(verb, exit) {
|
|
14125
14224
|
try {
|
|
@@ -14130,7 +14229,7 @@ function writeBreadcrumb(verb, exit) {
|
|
|
14130
14229
|
}
|
|
14131
14230
|
function readBreadcrumb() {
|
|
14132
14231
|
try {
|
|
14133
|
-
const raw = JSON.parse(
|
|
14232
|
+
const raw = JSON.parse(readFileSync23(breadcrumbPath(), "utf8"));
|
|
14134
14233
|
if (typeof raw !== "object" || raw === null)
|
|
14135
14234
|
return null;
|
|
14136
14235
|
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,CAoDpB"}
|
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.1";
|
|
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.1",
|
|
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.1
|
|
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.1
|
|
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.1
|
|
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.1
|
|
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.1
|
|
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.1
|
|
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.1
|
|
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.1
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
<!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
|