@m-kopa/launchpad-cli 0.23.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.
Files changed (191) hide show
  1. package/CHANGELOG.md +854 -0
  2. package/README.md +109 -0
  3. package/dist/auth/browser.d.ts +18 -0
  4. package/dist/auth/browser.d.ts.map +1 -0
  5. package/dist/auth/callback-server.d.ts +24 -0
  6. package/dist/auth/callback-server.d.ts.map +1 -0
  7. package/dist/auth/discovery.d.ts +25 -0
  8. package/dist/auth/discovery.d.ts.map +1 -0
  9. package/dist/auth/flow.d.ts +39 -0
  10. package/dist/auth/flow.d.ts.map +1 -0
  11. package/dist/auth/jwt.d.ts +27 -0
  12. package/dist/auth/jwt.d.ts.map +1 -0
  13. package/dist/auth/pkce.d.ts +26 -0
  14. package/dist/auth/pkce.d.ts.map +1 -0
  15. package/dist/auth/registration.d.ts +8 -0
  16. package/dist/auth/registration.d.ts.map +1 -0
  17. package/dist/auth/session.d.ts +54 -0
  18. package/dist/auth/session.d.ts.map +1 -0
  19. package/dist/auth/token.d.ts +37 -0
  20. package/dist/auth/token.d.ts.map +1 -0
  21. package/dist/bundle/cron-bundle.d.ts +77 -0
  22. package/dist/bundle/cron-bundle.d.ts.map +1 -0
  23. package/dist/bundle/cwd-walker.d.ts +43 -0
  24. package/dist/bundle/cwd-walker.d.ts.map +1 -0
  25. package/dist/bundle/orchestrate.d.ts +51 -0
  26. package/dist/bundle/orchestrate.d.ts.map +1 -0
  27. package/dist/bundle/upload.d.ts +66 -0
  28. package/dist/bundle/upload.d.ts.map +1 -0
  29. package/dist/cli.d.ts +3 -0
  30. package/dist/cli.d.ts.map +1 -0
  31. package/dist/cli.js +9757 -0
  32. package/dist/clone/git-init.d.ts +18 -0
  33. package/dist/clone/git-init.d.ts.map +1 -0
  34. package/dist/clone/tar-extract.d.ts +59 -0
  35. package/dist/clone/tar-extract.d.ts.map +1 -0
  36. package/dist/commands/apps.d.ts +14 -0
  37. package/dist/commands/apps.d.ts.map +1 -0
  38. package/dist/commands/channel-auth.d.ts +31 -0
  39. package/dist/commands/channel-auth.d.ts.map +1 -0
  40. package/dist/commands/clone.d.ts +3 -0
  41. package/dist/commands/clone.d.ts.map +1 -0
  42. package/dist/commands/create.d.ts +27 -0
  43. package/dist/commands/create.d.ts.map +1 -0
  44. package/dist/commands/deploy-flags.d.ts +75 -0
  45. package/dist/commands/deploy-flags.d.ts.map +1 -0
  46. package/dist/commands/deploy-modes.d.ts +59 -0
  47. package/dist/commands/deploy-modes.d.ts.map +1 -0
  48. package/dist/commands/deploy.d.ts +29 -0
  49. package/dist/commands/deploy.d.ts.map +1 -0
  50. package/dist/commands/destroy.d.ts +14 -0
  51. package/dist/commands/destroy.d.ts.map +1 -0
  52. package/dist/commands/envvars.d.ts +28 -0
  53. package/dist/commands/envvars.d.ts.map +1 -0
  54. package/dist/commands/generate.d.ts +3 -0
  55. package/dist/commands/generate.d.ts.map +1 -0
  56. package/dist/commands/groups-whoami.d.ts +3 -0
  57. package/dist/commands/groups-whoami.d.ts.map +1 -0
  58. package/dist/commands/groups.d.ts +3 -0
  59. package/dist/commands/groups.d.ts.map +1 -0
  60. package/dist/commands/init.d.ts +44 -0
  61. package/dist/commands/init.d.ts.map +1 -0
  62. package/dist/commands/login.d.ts +3 -0
  63. package/dist/commands/login.d.ts.map +1 -0
  64. package/dist/commands/logout.d.ts +3 -0
  65. package/dist/commands/logout.d.ts.map +1 -0
  66. package/dist/commands/logs.d.ts +16 -0
  67. package/dist/commands/logs.d.ts.map +1 -0
  68. package/dist/commands/merge.d.ts +29 -0
  69. package/dist/commands/merge.d.ts.map +1 -0
  70. package/dist/commands/plan.d.ts +3 -0
  71. package/dist/commands/plan.d.ts.map +1 -0
  72. package/dist/commands/pull.d.ts +12 -0
  73. package/dist/commands/pull.d.ts.map +1 -0
  74. package/dist/commands/review.d.ts +22 -0
  75. package/dist/commands/review.d.ts.map +1 -0
  76. package/dist/commands/rollback.d.ts +3 -0
  77. package/dist/commands/rollback.d.ts.map +1 -0
  78. package/dist/commands/secrets-template.d.ts +3 -0
  79. package/dist/commands/secrets-template.d.ts.map +1 -0
  80. package/dist/commands/secrets.d.ts +3 -0
  81. package/dist/commands/secrets.d.ts.map +1 -0
  82. package/dist/commands/skills.d.ts +13 -0
  83. package/dist/commands/skills.d.ts.map +1 -0
  84. package/dist/commands/status.d.ts +54 -0
  85. package/dist/commands/status.d.ts.map +1 -0
  86. package/dist/commands/update.d.ts +114 -0
  87. package/dist/commands/update.d.ts.map +1 -0
  88. package/dist/commands/validate.d.ts +3 -0
  89. package/dist/commands/validate.d.ts.map +1 -0
  90. package/dist/commands/whoami.d.ts +3 -0
  91. package/dist/commands/whoami.d.ts.map +1 -0
  92. package/dist/config.d.ts +11 -0
  93. package/dist/config.d.ts.map +1 -0
  94. package/dist/deploy/apply.d.ts +29 -0
  95. package/dist/deploy/apply.d.ts.map +1 -0
  96. package/dist/deploy/dry-run.d.ts +13 -0
  97. package/dist/deploy/dry-run.d.ts.map +1 -0
  98. package/dist/deploy/git-files.d.ts +33 -0
  99. package/dist/deploy/git-files.d.ts.map +1 -0
  100. package/dist/deploy/group-pin.d.ts +66 -0
  101. package/dist/deploy/group-pin.d.ts.map +1 -0
  102. package/dist/deploy/manifest-state.d.ts +20 -0
  103. package/dist/deploy/manifest-state.d.ts.map +1 -0
  104. package/dist/deploy/manifest-status.d.ts +11 -0
  105. package/dist/deploy/manifest-status.d.ts.map +1 -0
  106. package/dist/deploy/resolve.d.ts +53 -0
  107. package/dist/deploy/resolve.d.ts.map +1 -0
  108. package/dist/deploy/rollback.d.ts +23 -0
  109. package/dist/deploy/rollback.d.ts.map +1 -0
  110. package/dist/deploy/runner.d.ts +29 -0
  111. package/dist/deploy/runner.d.ts.map +1 -0
  112. package/dist/deploy/stage-exit-codes.d.ts +41 -0
  113. package/dist/deploy/stage-exit-codes.d.ts.map +1 -0
  114. package/dist/deploy/status-polling.d.ts +37 -0
  115. package/dist/deploy/status-polling.d.ts.map +1 -0
  116. package/dist/deploy/tar-pack.d.ts +22 -0
  117. package/dist/deploy/tar-pack.d.ts.map +1 -0
  118. package/dist/detect/index.d.ts +53 -0
  119. package/dist/detect/index.d.ts.map +1 -0
  120. package/dist/dispatcher.d.ts +30 -0
  121. package/dist/dispatcher.d.ts.map +1 -0
  122. package/dist/groups/client.d.ts +62 -0
  123. package/dist/groups/client.d.ts.map +1 -0
  124. package/dist/http/api-client.d.ts +33 -0
  125. package/dist/http/api-client.d.ts.map +1 -0
  126. package/dist/http/errors.d.ts +31 -0
  127. package/dist/http/errors.d.ts.map +1 -0
  128. package/dist/manifest/load.d.ts +38 -0
  129. package/dist/manifest/load.d.ts.map +1 -0
  130. package/dist/manifest/schema.d.ts +3 -0
  131. package/dist/manifest/schema.d.ts.map +1 -0
  132. package/dist/postinstall.d.ts +3 -0
  133. package/dist/postinstall.d.ts.map +1 -0
  134. package/dist/postinstall.js +37 -0
  135. package/dist/secrets/env-parse.d.ts +19 -0
  136. package/dist/secrets/env-parse.d.ts.map +1 -0
  137. package/dist/secrets/push.d.ts +13 -0
  138. package/dist/secrets/push.d.ts.map +1 -0
  139. package/dist/secrets/set.d.ts +19 -0
  140. package/dist/secrets/set.d.ts.map +1 -0
  141. package/dist/secrets/status.d.ts +19 -0
  142. package/dist/secrets/status.d.ts.map +1 -0
  143. package/dist/types/api.d.ts +112 -0
  144. package/dist/types/api.d.ts.map +1 -0
  145. package/dist/update-notifier.d.ts +69 -0
  146. package/dist/update-notifier.d.ts.map +1 -0
  147. package/dist/version.d.ts +2 -0
  148. package/dist/version.d.ts.map +1 -0
  149. package/package.json +62 -0
  150. package/skills/README.md +100 -0
  151. package/skills/_partials/shell-contract.md +42 -0
  152. package/skills/launchpad-content-pr/SKILL.md +255 -0
  153. package/skills/launchpad-deploy/SKILL.md +415 -0
  154. package/skills/launchpad-deploy-status/SKILL.md +231 -0
  155. package/skills/launchpad-destroy/SKILL.md +317 -0
  156. package/skills/launchpad-onboard/SKILL.md +179 -0
  157. package/skills/launchpad-status/SKILL.md +263 -0
  158. package/skills/marquee-share/README.md +155 -0
  159. package/skills/marquee-share/SKILL.md +94 -0
  160. package/skills/marquee-share/SYNC.md +27 -0
  161. package/skills/marquee-share/dist/cli.js +896 -0
  162. package/skills/marquee-share/eslint.config.mjs +71 -0
  163. package/skills/marquee-share/install.sh +103 -0
  164. package/skills/marquee-share/package-lock.json +3946 -0
  165. package/skills/marquee-share/package.json +30 -0
  166. package/skills/marquee-share/src/auth/PROVENANCE.md +103 -0
  167. package/skills/marquee-share/src/auth/browser.ts +75 -0
  168. package/skills/marquee-share/src/auth/callback-server.ts +171 -0
  169. package/skills/marquee-share/src/auth/discovery.ts +171 -0
  170. package/skills/marquee-share/src/auth/flow.ts +262 -0
  171. package/skills/marquee-share/src/auth/index.ts +171 -0
  172. package/skills/marquee-share/src/auth/jwt.ts +77 -0
  173. package/skills/marquee-share/src/auth/pkce.ts +79 -0
  174. package/skills/marquee-share/src/auth/registration.ts +87 -0
  175. package/skills/marquee-share/src/auth/session.ts +205 -0
  176. package/skills/marquee-share/src/auth/token.ts +162 -0
  177. package/skills/marquee-share/src/cli.ts +246 -0
  178. package/skills/marquee-share/src/config.ts +101 -0
  179. package/skills/marquee-share/src/render/template.ts +171 -0
  180. package/skills/marquee-share/src/upload/index.ts +11 -0
  181. package/skills/marquee-share/src/upload/upload.ts +191 -0
  182. package/skills/marquee-share/tests/cli.test.ts +281 -0
  183. package/skills/marquee-share/tests/config.test.ts +119 -0
  184. package/skills/marquee-share/tests/flow.test.ts +356 -0
  185. package/skills/marquee-share/tests/no-token-leak.test.ts +240 -0
  186. package/skills/marquee-share/tests/pkce.test.ts +121 -0
  187. package/skills/marquee-share/tests/session.test.ts +173 -0
  188. package/skills/marquee-share/tests/template.test.ts +170 -0
  189. package/skills/marquee-share/tests/upload.test.ts +311 -0
  190. package/skills/marquee-share/tsconfig.json +23 -0
  191. package/skills/marquee-share/vitest.config.ts +15 -0
@@ -0,0 +1,69 @@
1
+ import type { CliIo, Command } from "./dispatcher.js";
2
+ import { resolveLatestVersion, type InstallChannel } from "./commands/update.js";
3
+ /** Internal verb the detached refresh process runs. Hidden from help. */
4
+ export declare const INTERNAL_REFRESH_VERB = "__refresh-update-cache";
5
+ /** Re-check the registry at most once per this window (24h). */
6
+ export declare const CHECK_INTERVAL_MS: number;
7
+ /** Persisted shape of {@link CACHE_FILE}. */
8
+ export interface CacheState {
9
+ /** `Date.now()` at the last completed refresh (success OR failure). */
10
+ readonly checkedAt: number;
11
+ /** Latest version last seen, or null if never successfully fetched. */
12
+ readonly latest: string | null;
13
+ }
14
+ /** Read + parse the cache file; null on any error (missing/corrupt). */
15
+ export declare function readCache(): CacheState | null;
16
+ /** Write the cache file (0600), creating ~/.launchpad if needed. Best-effort. */
17
+ export declare function writeCache(state: CacheState): void;
18
+ /** Context derived from the running process, passed in for testability. */
19
+ export interface NotifyContext {
20
+ readonly argv: readonly string[];
21
+ readonly env: NodeJS.ProcessEnv;
22
+ readonly stderrIsTTY: boolean;
23
+ }
24
+ /** Injectable surface so the core is testable without fs/clock/spawn. */
25
+ export interface NotifierDeps {
26
+ readonly cliVersion: string;
27
+ readonly channel: () => InstallChannel;
28
+ readonly readCache: () => CacheState | null;
29
+ readonly now: () => number;
30
+ /** Kick off the detached background refresh (no-op in tests). */
31
+ readonly spawnRefresh: () => void;
32
+ }
33
+ /**
34
+ * Should the notifier stay silent entirely (no notice, no refresh)?
35
+ * Keeps the CLI quiet in every non-interactive / machine context.
36
+ */
37
+ export declare function isSuppressed(ctx: NotifyContext): boolean;
38
+ /** The one-line, channel-appropriate notice. */
39
+ export declare function noticeLine(current: string, latest: string, channel: InstallChannel): string;
40
+ /**
41
+ * Core notifier: print a cached notice if a newer version is known, and
42
+ * trigger a background refresh when the cache is stale. Synchronous and
43
+ * allocation-light on the hot path; the only async work (the network)
44
+ * happens in the detached process `spawnRefresh` launches.
45
+ */
46
+ export declare function maybeNotify(io: CliIo, ctx: NotifyContext, deps: NotifierDeps): void;
47
+ /**
48
+ * Entry point for the bin wrapper, invoked AFTER the command resolves.
49
+ * Builds the real deps from `process.*` and runs the core, fully
50
+ * guarded so a notifier fault can never affect the command's result.
51
+ */
52
+ export declare function notifyAfterCommand(io: CliIo, argv: readonly string[], cliPath?: string | undefined): void;
53
+ /** Injectable surface for {@link refreshUpdateCache}. */
54
+ export interface RefreshDeps {
55
+ readonly resolveLatestVersion: typeof resolveLatestVersion;
56
+ readonly readCache: () => CacheState | null;
57
+ readonly writeCache: (s: CacheState) => void;
58
+ readonly now: () => number;
59
+ }
60
+ /**
61
+ * Refresh the cached latest-version marker. Always bumps `checkedAt`
62
+ * (success OR failure) so a flaky/offline registry backs off for the
63
+ * full interval rather than re-spawning on every command; preserves the
64
+ * previously-seen `latest` on failure.
65
+ */
66
+ export declare function refreshUpdateCache(deps?: RefreshDeps): Promise<void>;
67
+ /** Hidden command: the detached background refresh. Not shown in help. */
68
+ export declare const refreshUpdateCacheCommand: Command;
69
+ //# sourceMappingURL=update-notifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-notifier.d.ts","sourceRoot":"","sources":["../src/update-notifier.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAIL,oBAAoB,EACpB,KAAK,cAAc,EACpB,MAAM,sBAAsB,CAAC;AAE9B,yEAAyE;AACzE,eAAO,MAAM,qBAAqB,2BAA2B,CAAC;AAE9D,gEAAgE;AAChE,eAAO,MAAM,iBAAiB,QAAsB,CAAC;AAQrD,6CAA6C;AAC7C,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,uEAAuE;IACvE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,wEAAwE;AACxE,wBAAgB,SAAS,IAAI,UAAU,GAAG,IAAI,CAkB7C;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAOlD;AAID,2EAA2E;AAC3E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAED,yEAAyE;AACzE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,MAAM,cAAc,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,MAAM,UAAU,GAAG,IAAI,CAAC;IAC5C,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,YAAY,EAAE,MAAM,IAAI,CAAC;CACnC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAQxD;AAED,gDAAgD;AAChD,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,cAAc,GACtB,MAAM,CAMR;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,EAAE,EAAE,KAAK,EACT,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,YAAY,GACjB,IAAI,CAcN;AAwBD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,KAAK,EACT,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,GAAE,MAAM,GAAG,SAA2B,GAC5C,IAAI,CAoBN;AAID,yDAAyD;AACzD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,oBAAoB,EAAE,OAAO,oBAAoB,CAAC;IAC3D,QAAQ,CAAC,SAAS,EAAE,MAAM,UAAU,GAAG,IAAI,CAAC;IAC5C,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAC;IAC7C,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;CAC5B;AASD;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,GAAE,WAA6B,GAClC,OAAO,CAAC,IAAI,CAAC,CASf;AAED,0EAA0E;AAC1E,eAAO,MAAM,yBAAyB,EAAE,OAQvC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const CLI_VERSION = "0.23.0";
2
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,WAAW,WAAW,CAAC"}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@m-kopa/launchpad-cli",
3
+ "version": "0.23.0",
4
+ "description": "Launchpad CLI — clone / deploy / review / merge against Launchpad-managed apps. Talks to the portal-bot endpoints (SCOPE-M-760 / T4).",
5
+ "type": "module",
6
+ "bin": {
7
+ "launchpad": "./dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "skills",
12
+ "README.md",
13
+ "CHANGELOG.md",
14
+ "LICENSE"
15
+ ],
16
+ "engines": {
17
+ "node": ">=20"
18
+ },
19
+ "publishConfig": {
20
+ "registry": "https://registry.npmjs.org",
21
+ "access": "public"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://github.com/M-KOPA/launchpad-platform.git",
26
+ "directory": "packages/launchpad-cli"
27
+ },
28
+ "homepage": "https://github.com/M-KOPA/launchpad-platform/tree/main/packages/launchpad-cli#readme",
29
+ "license": "UNLICENSED",
30
+ "dependencies": {
31
+ "esbuild": "0.27.3",
32
+ "yaml": "2.9.0",
33
+ "zod": "4.4.3"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "22.18.10",
37
+ "@typescript-eslint/eslint-plugin": "8.46.4",
38
+ "@typescript-eslint/parser": "8.46.4",
39
+ "@vitest/coverage-istanbul": "4.1.5",
40
+ "eslint": "9.39.4",
41
+ "typescript": "5.9.3",
42
+ "vitest": "4.1.5",
43
+ "@m-kopa/launchpad-engine": "workspace:*"
44
+ },
45
+ "scripts": {
46
+ "build": "rm -rf dist && bun build src/cli.ts src/postinstall.ts --target=node --outdir=dist --external yaml --external zod --external esbuild --external '@m-kopa/platform-auth' && chmod +x dist/cli.js && tsc -p tsconfig.build.json --emitDeclarationOnly --declarationDir dist",
47
+ "postinstall": "node dist/postinstall.js",
48
+ "test": "vitest run",
49
+ "test:watch": "vitest",
50
+ "lint": "eslint src tests",
51
+ "typecheck": "tsc --noEmit",
52
+ "check:version-sync": "bash scripts/check-version-sync.sh",
53
+ "check:installer-syntax": "bash scripts/check-installer-syntax.sh",
54
+ "check:skill-contract": "bash scripts/sync-skill-contract.sh --check",
55
+ "check:skill-bash-dialect": "bash scripts/check-skill-bash-dialect.sh",
56
+ "check:skill-bash-parse": "bash scripts/check-skill-bash-parse.sh",
57
+ "check:skills": "bun run check:skill-contract && bun run check:skill-bash-dialect && bun run check:skill-bash-parse",
58
+ "sync:skill-contract": "bash scripts/sync-skill-contract.sh write",
59
+ "version:patch": "npm version patch --no-git-tag-version",
60
+ "version:minor": "npm version minor --no-git-tag-version"
61
+ }
62
+ }
@@ -0,0 +1,100 @@
1
+ # Launchpad Claude Code skill bundle
2
+
3
+ This directory ships the Claude Code slash-commands that
4
+ `launchpad skills install` copies into `~/.claude/skills/`.
5
+
6
+ ```text
7
+ skills/
8
+ ├── _partials/
9
+ │ └── shell-contract.md # single source of truth for the
10
+ │ # Shell Contract block at the top
11
+ │ # of every launchpad-* SKILL.md
12
+ ├── launchpad-onboard/SKILL.md
13
+ ├── launchpad-deploy/SKILL.md
14
+ ├── launchpad-deploy-status/SKILL.md
15
+ ├── launchpad-content-pr/SKILL.md
16
+ ├── launchpad-status/SKILL.md
17
+ ├── launchpad-destroy/SKILL.md
18
+ └── marquee-share/ # vendored from M-KOPA/marquee, do
19
+ # not hand-edit — managed by
20
+ # scripts/sync-marquee-share-skill.sh
21
+ ```
22
+
23
+ ## Cross-platform shell contract
24
+
25
+ Claude Code's `Bash` tool **always** runs `bash`, on every OS — on
26
+ Windows it resolves to Git for Windows / MSYS bash. Skills that
27
+ contain bash blocks must be unambiguously bash, because models will
28
+ otherwise sometimes transliterate to PowerShell on Windows and the
29
+ bash tool will then fail with `/usr/bin/bash: syntax error`.
30
+
31
+ To make this explicit, each `launchpad-*` SKILL.md starts with a
32
+ **Shell contract** preamble that tells the agent not to translate:
33
+
34
+ ```markdown
35
+ <!-- BEGIN shell-contract (managed by …) -->
36
+ ## Shell contract — read this first
37
+
38
+ Every fenced `bash` block below MUST be sent to the `Bash` tool
39
+ verbatim. Do not rewrite into PowerShell, cmd, …
40
+ <!-- END shell-contract -->
41
+ ```
42
+
43
+ The canonical body lives at `_partials/shell-contract.md`. **Do not
44
+ edit the copies in the four launchpad-* SKILL.md files directly** —
45
+ edit the partial, then run:
46
+
47
+ ```bash
48
+ bun run sync:skill-contract
49
+ ```
50
+
51
+ CI (`bun run check:skills`) fails if any SKILL.md drifts from the
52
+ partial, contains a PowerShell token inside a bash fence, or fails
53
+ `bash -n` parse.
54
+
55
+ ## Versioning
56
+
57
+ The `version:` frontmatter line in each `launchpad-*/SKILL.md` is
58
+ **stamped** from `package.json` by `scripts/sync-skill-contract.sh`.
59
+ Do not hand-bump it.
60
+
61
+ Workflow:
62
+
63
+ 1. Bump `package.json` (`bun run version:patch` / `:minor`) and
64
+ `src/version.ts`'s `CLI_VERSION`.
65
+ 2. Run `bun run sync:skill-contract` — this rewrites each SKILL.md's
66
+ `version:` line and re-stamps the Shell Contract block.
67
+ 3. Commit the result.
68
+
69
+ CI gate `bun run check:skills` will reject the PR if step 2 was
70
+ skipped (the rendered SKILL.md disagrees with the partial or the
71
+ package.json version).
72
+
73
+ ## Authoring guidance
74
+
75
+ When you write or edit a `launchpad-*` SKILL.md, follow these rules.
76
+ They keep the bash blocks portable to Git Bash on Windows without
77
+ forking the skill into per-OS versions.
78
+
79
+ | Do | Don't |
80
+ |---|---|
81
+ | `"$HOME/.launchpad/session.json"` | `~/.launchpad/session.json` (parses fine, but visually nudges the agent toward `$env:USERPROFILE`) |
82
+ | `"$TOKEN"`, `"$BOT"` (quoted) | bare `$TOKEN`, `$BOT` |
83
+ | `${TMPDIR:-/tmp}/foo` | `/tmp/foo` (Git Bash needs `$TMPDIR`) |
84
+ | `case "$(uname -s)" in …` | per-OS prose ("on macOS … on Linux …") |
85
+ | `[ -n "$VAR" ] \|\| { echo …; exit 1; }` for nullable inputs | unchecked `$(jq -r .field file)` pipes |
86
+ | `gh auth status --hostname github.com` | `gh auth status` (lenient about which host) |
87
+
88
+ Forbidden tokens (CI grep blocks these inside ```bash``` fences):
89
+ `Test-Path`, `Get-Content`, `Get-ChildItem`, `Where-Object`,
90
+ `Write-Output`, `Write-Host`, `$env:`, `Set-Location`, `New-Item`,
91
+ `Remove-Item`. Update the list in `scripts/check-skill-bash-dialect.sh`
92
+ and `tests/skill-contract.test.ts` together if you need to extend it.
93
+
94
+ ## Why not ship a PowerShell version of each skill?
95
+
96
+ The audience is Claude Code users, and Claude Code's `Bash` tool runs
97
+ bash. There is no audience for a PowerShell version of these skills.
98
+ Per-OS forks would also double the maintenance burden and risk drift
99
+ between variants. One skill, one shell, OS-branched inside bash with
100
+ `case "$(uname -s)"` where it actually matters.
@@ -0,0 +1,42 @@
1
+ <!--
2
+ shell-contract.md — single source of truth for the "Shell contract"
3
+ preamble that every launchpad-* skill must carry verbatim.
4
+
5
+ Why this exists: Claude Code's `Bash` tool always invokes `bash`,
6
+ even on Windows (where it resolves to Git for Windows / MSYS bash).
7
+ Without an explicit contract, models will sometimes transliterate
8
+ the fenced bash blocks into PowerShell on Windows ("$env:USERPROFILE",
9
+ Test-Path, Get-Content, etc.), which then fails with a confusing
10
+ `/usr/bin/bash: syntax error` because the bash tool is still what
11
+ runs the command.
12
+
13
+ The block below is short and imperative on purpose — models follow
14
+ brief rules far better than verbose ones. Edit cautiously; CI
15
+ asserts that the rendered block in each SKILL.md is byte-identical
16
+ to this file (scripts/check-skill-contract.sh).
17
+
18
+ Do NOT edit the launchpad-*/SKILL.md copies of this block directly.
19
+ Edit this file, then run `bun run sync:skill-contract` to re-stamp
20
+ the four launchpad-* skills.
21
+ -->
22
+ ## Shell contract — read this first
23
+
24
+ Every fenced `bash` block below MUST be sent to the `Bash` tool **verbatim**.
25
+ Do not rewrite into PowerShell, cmd, zsh-isms, or "equivalent" forms.
26
+
27
+ - macOS / Linux: the `Bash` tool runs system bash.
28
+ - Windows: the `Bash` tool runs Git for Windows (MSYS) bash. `$HOME`,
29
+ forward slashes, `test -f`, `command -v`, heredocs, and `[[ … ]]` all
30
+ work. There is no reason to translate to `Test-Path`, `$env:USERPROFILE`,
31
+ `Get-Content`, `Where-Object`, `Get-ChildItem`, or backslash paths —
32
+ doing so will fail with `/usr/bin/bash: syntax error`.
33
+
34
+ If a step genuinely needs OS branching, branch *inside* bash:
35
+
36
+ ```bash
37
+ case "$(uname -s)" in
38
+ Darwin) : ;;
39
+ Linux) : ;;
40
+ MINGW*|MSYS*|CYGWIN*) : ;;
41
+ esac
42
+ ```
@@ -0,0 +1,255 @@
1
+ ---
2
+ name: launchpad-content-pr
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) and the stack-fit pre-flight that the bot enforces server-side. 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.23.0
5
+ ---
6
+
7
+ <!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
8
+ ## Shell contract — read this first
9
+
10
+ Every fenced `bash` block below MUST be sent to the `Bash` tool **verbatim**.
11
+ Do not rewrite into PowerShell, cmd, zsh-isms, or "equivalent" forms.
12
+
13
+ - macOS / Linux: the `Bash` tool runs system bash.
14
+ - Windows: the `Bash` tool runs Git for Windows (MSYS) bash. `$HOME`,
15
+ forward slashes, `test -f`, `command -v`, heredocs, and `[[ … ]]` all
16
+ work. There is no reason to translate to `Test-Path`, `$env:USERPROFILE`,
17
+ `Get-Content`, `Where-Object`, `Get-ChildItem`, or backslash paths —
18
+ doing so will fail with `/usr/bin/bash: syntax error`.
19
+
20
+ If a step genuinely needs OS branching, branch *inside* bash:
21
+
22
+ ```bash
23
+ case "$(uname -s)" in
24
+ Darwin) : ;;
25
+ Linux) : ;;
26
+ MINGW*|MSYS*|CYGWIN*) : ;;
27
+ esac
28
+ ```
29
+ <!-- END shell-contract -->
30
+
31
+ # /launchpad-content-pr
32
+
33
+ Push a content change to an already-provisioned Launchpad app, then
34
+ verify it shipped.
35
+
36
+ Under Model A the first deploy and the first content are the same
37
+ event — `launchpad init` + `launchpad deploy` from the user's CWD
38
+ opens a PR that contains their working tree, the bot waits for the
39
+ Cloudflare Pages deployment to come up green (`deployment_verified`
40
+ lifecycle gate), and the lifecycle flips to `live`. There is no
41
+ separate "now push content" step.
42
+
43
+ This skill is therefore the **iteration** companion to
44
+ `/launchpad-deploy`: once an app is live, how do you ship the next
45
+ change? It is also the canonical place to find the stack-fit
46
+ pre-flight (the rules the bot enforces server-side on every bundle).
47
+
48
+ ## Pre-flight
49
+
50
+ ```bash
51
+ launchpad whoami
52
+ ```
53
+
54
+ If that fails, run `/launchpad-onboard` first. You also need to be
55
+ an owner or editor on the slug — `launchpad apps` lists every app
56
+ the caller can see along with the role per app.
57
+
58
+ ## Confirm the app is live
59
+
60
+ ```bash
61
+ launchpad apps
62
+ ```
63
+
64
+ Look for the slug in the `live` lifecycle bucket. If it's in
65
+ `provisioning` / `failed` / `destroying` / `destroyed`, the
66
+ iteration loop is not the right tool — route to
67
+ `/launchpad-deploy-status` (in-flight) or `/launchpad-destroy`
68
+ (teardown) instead.
69
+
70
+ ## Edit and ship
71
+
72
+ The iteration loop is two verbs:
73
+
74
+ ```bash
75
+ # 1. Edit your working tree.
76
+
77
+ # 2. Ship it.
78
+ launchpad deploy --message "<one-line description>"
79
+ ```
80
+
81
+ The CLI bundles the CWD (using `git ls-files -co --exclude-standard`
82
+ where available; falling back to a pure-FS walker honouring
83
+ `.gitignore` and a default-ignore set), gzips it, uploads to the
84
+ bot, and the bot opens a content PR on `launchpad-app-<slug>`.
85
+ Cloudflare Pages auto-deploys on merge to `main`; the bot waits for
86
+ the deploy to come up green via the `deployment_verified` lifecycle
87
+ gate before reporting `done`.
88
+
89
+ Common flags:
90
+
91
+ - **`--slug <slug>`** — explicit override. Defaults to the slug from
92
+ `./launchpad.yaml`, or the cwd-name parse if you're in a
93
+ `launchpad-app-<slug>/` clone.
94
+ - **`--message <text>`** — threaded as the PR description. Useful
95
+ for change logs and audit trails.
96
+ - **`--file <path>`** — point at a non-default manifest path.
97
+
98
+ Exit codes:
99
+
100
+ - **0** — content PR opened + Cloudflare Pages deployment verified.
101
+ - **non-zero** — see `/launchpad-deploy-status <slug>` for the
102
+ failure reason.
103
+
104
+ ## Stack-fit — what the bot enforces server-side
105
+
106
+ The bot rejects bundles that contain shapes the Cloudflare Pages +
107
+ Workers runtime cannot host. The validation runs on every
108
+ `launchpad deploy`; you do not need to pre-flight it locally, but
109
+ **knowing what's enforced saves the round-trip when a bundle is
110
+ going to fail**.
111
+
112
+ The accepted Launchpad stack is the single source of truth in:
113
+
114
+ - `launchpad-platform/ARCHITECTURE.md § Tech Stack`
115
+ - `launchpad-platform/PATTERNS.md § Approved Libraries`
116
+ - `launchpad-platform/ANTI-PATTERNS.md`
117
+
118
+ ### Forbidden runtime dependencies
119
+
120
+ The bot rejects bundles whose `package.json` declares any of these
121
+ runtime dependencies — they do not work in the Workers runtime
122
+ without a non-trivial port:
123
+
124
+ | Forbidden | Replacement on Launchpad |
125
+ |---|---|
126
+ | `fastify` / `express` / `koa` / `hapi` / `@nestjs/*` | `hono` mounted at `functions/api/[[path]].ts` — the only server framework (PATTERNS.md). |
127
+ | `better-sqlite3` / native `sqlite3` | Cloudflare D1 binding (`wrangler.toml [[d1_databases]]`). |
128
+ | `pg` / `mysql2` / `mongodb` / `mongoose` / `redis` / `ioredis` | Neon (Postgres over HTTP) or another HTTP-driver backend. Native TCP drivers do not work in the Workers runtime. |
129
+ | `dotenv` | `c.env.*` bindings; `wrangler secret put` (or `launchpad envvars` / `launchpad secrets push`) for secrets. |
130
+ | `pm2` / `forever` / `nodemon` | Cloudflare Cron Triggers (`wrangler.toml [triggers] crons`). |
131
+
132
+ ### Forbidden top-level files
133
+
134
+ The bot rejects bundles whose root contains any of these (on non-
135
+ container app-types):
136
+
137
+ ```
138
+ Dockerfile · docker-compose.yml · docker-compose.yaml · nginx.conf
139
+ pm2.config.js · ecosystem.config.js · Procfile
140
+ ```
141
+
142
+ Their presence is the smoking gun that the app was copied from a
143
+ long-running-server stack without removing the legacy infra.
144
+
145
+ ### Forbidden source patterns
146
+
147
+ The bot's secret-scan + build-command policy rejects bundles where
148
+ the source tree contains:
149
+
150
+ - `process.env.X` / `process.env['X']` reads — switch to `c.env.*`
151
+ bindings.
152
+ - `setInterval` / `setTimeout` daemons — Workers do not run between
153
+ requests; use Cron Triggers for periodic work.
154
+ - High-signal secret patterns (AWS access keys, GitHub PATs / OAuth
155
+ / app tokens, Slack tokens, SSH/RSA/EC/PGP keys, generic
156
+ `api_key` shapes). These never belong in a bundle — push them
157
+ through `launchpad secrets push` instead.
158
+
159
+ ### `react+api` requires `nodejs_compat`
160
+
161
+ The bot checks that `wrangler.toml` declares
162
+ `compatibility_flags = ["nodejs_compat"]` for any `react+api` app
163
+ (ADR-0011 carve-out).
164
+
165
+ ### Verifying locally before you ship
166
+
167
+ The CLI ships an offline validator that catches the schema-level
168
+ issues without uploading anything:
169
+
170
+ ```bash
171
+ launchpad validate
172
+ launchpad plan
173
+ ```
174
+
175
+ Neither verb talks to the bot. Catch what you can locally; the bot
176
+ catches the rest server-side.
177
+
178
+ ## Verify after merge
179
+
180
+ ```bash
181
+ launchpad status <slug>
182
+ ```
183
+
184
+ Three possible states (see `/launchpad-status` for the canonical
185
+ reference):
186
+
187
+ - **`in sync`** — local matches deployed; the deploy landed and
188
+ `deployment_verified` is green. You're done.
189
+ - **`drift: <fields>`** — your local manifest diverges from what was
190
+ deployed. Either re-run `launchpad deploy` to ship the local, or
191
+ `launchpad pull <slug> --out launchpad.yaml` to bring the local
192
+ into line with deployed.
193
+ - **`no deployed manifest yet`** — the deploy is still in flight or
194
+ failed. Re-check in a minute, or run `/launchpad-deploy-status
195
+ <slug>`.
196
+
197
+ You can also point a browser at `https://<slug>.launchpad.m-kopa.us`
198
+ once status reports `in sync`. An SSO gate sits in front of the app:
199
+ for a gateway-fronted app (the default, `auth: gateway`) expect a
200
+ redirect to the Entra-OIDC gateway (Microsoft sign-in) on the first
201
+ request; for an `auth: access` app expect a redirect to
202
+ `*.cloudflareaccess.com`. Either way you land on your app after sign-in.
203
+
204
+ If the URL serves the wrong thing:
205
+
206
+ - **`200 OK` with no SSO redirect** → the edge gate is missing or
207
+ open. Check the app's auth (gateway KV entry, or the Access policy
208
+ via the portal admin UI); the
209
+ access-lockout runbook lives in
210
+ `launchpad-platform/docs/runbooks/access-lockout.md`.
211
+ - **`404`** → Pages deploy hasn't finished, or build failed. Run
212
+ `launchpad status <slug> --json` to see the most recent error.
213
+ - **`5xx` from Cloudflare** → wait + retry; cert issuance can lag a
214
+ few minutes on a brand-new hostname.
215
+
216
+ ## Ongoing-deploy verbs
217
+
218
+ Once you've shipped first content, the daily-use verbs are:
219
+
220
+ - **`launchpad status`** (`/launchpad-status`) — is my local
221
+ `launchpad.yaml` in sync with what's deployed?
222
+ - **`launchpad pull <slug>`** (`/launchpad-status`) — read the
223
+ currently-deployed `launchpad.yaml`.
224
+ - **`launchpad deploy`** — package the working tree + open an
225
+ update PR via the bot.
226
+ - **`launchpad envvars`** — list / set / remove non-secret
227
+ production env vars.
228
+ - **`launchpad secrets push`** — push secrets (never via git).
229
+ - **`launchpad logs <slug>`** — recent Cloudflare Pages deployment
230
+ history.
231
+ - **`launchpad rollback`** — revert manifest to a prior git SHA +
232
+ re-apply.
233
+
234
+ ## Don'ts
235
+
236
+ - Do **not** shell out to `gh`, `jq`, `curl`, or `git` from this
237
+ skill. Every step is a `launchpad` verb. External users without
238
+ M-KOPA GitHub access need this skill to work end-to-end on the
239
+ CLI alone.
240
+ - Do **not** open content PRs by hand against the app repo — the
241
+ bot's bootstrap ruleset gates auto-merge, and a hand-rolled PR
242
+ bypasses the bundle scan, secret-scan, and build-command policy.
243
+ Use `launchpad deploy`.
244
+ - Do **not** treat the stack-fit pre-flight as something the user
245
+ re-implements locally. The bot enforces it server-side on every
246
+ bundle; the playbook describes what's enforced, not how to
247
+ re-implement it.
248
+ - Do **not** force-merge if the bot's PR checks are red. The
249
+ bundle-policy / secret-scan / build-command checks are detecting
250
+ real things; surface the failure verbatim and let the user fix
251
+ the bundle.
252
+ - Do **not** edit `launchpad.yaml`'s `production_env:` block to
253
+ contain secret values. That block is non-secret by contract;
254
+ `launchpad status` warns when a value looks like a secret. Use
255
+ `launchpad secrets push` for the real secret path.