@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,263 @@
1
+ ---
2
+ name: launchpad-status
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.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-status
32
+
33
+ Read-only verbs. Zero local platform-repo / terraform / GitHub
34
+ credentials needed. Both go through the bot's `/manifest/state`
35
+ endpoint; the bot reads launchpad-platform's TF state to find the
36
+ last-applied manifest sha, then fetches launchpad.yaml from the app
37
+ repo at that sha. The CLI is a thin client.
38
+
39
+ ## When to use which verb
40
+
41
+ - **`launchpad pull <slug>`** — read the deployed `launchpad.yaml`.
42
+ Use when you want to see what's actually running, or when you need
43
+ a starting point for editing a manifest.
44
+ - **`launchpad status [<slug>] [--strict] [--json]`** — compare local
45
+ `./launchpad.yaml` against what's deployed and report drift. Use
46
+ before `launchpad deploy` to see what will change, or after to
47
+ confirm the change landed.
48
+
49
+ If the operator is asking "did my deploy work" → `launchpad status`
50
+ inside the app repo's clone is the answer.
51
+
52
+ If they're asking "what was actually deployed" → `launchpad pull` is
53
+ the answer.
54
+
55
+ ## Pre-flight
56
+
57
+ Both verbs need a current Cf Access session. If `~/.launchpad/session.json`
58
+ is missing or expired, surface the run-login hint:
59
+
60
+ ```bash
61
+ launchpad whoami
62
+ # If this fails: launchpad login
63
+ ```
64
+
65
+ The app must exist in the registry (`launchpad apps` lists yours).
66
+ Both verbs are owner/editor-scoped — non-members get HTTP 403 with
67
+ "not authorised for app X (you must be an owner or editor)".
68
+
69
+ ## Slug resolution
70
+
71
+ Both verbs accept the slug three ways, with this precedence:
72
+
73
+ 1. **`--slug <slug>`** — explicit, wins over everything.
74
+ 2. **Positional argument** — `launchpad pull horizon-clone`.
75
+ 3. **Current directory inference** — if cwd is named
76
+ `launchpad-app-<slug>/`, the slug is extracted automatically.
77
+ This is the default when you've `launchpad clone`d an app.
78
+
79
+ `--file <path>` on `launchpad status` only changes which **local**
80
+ manifest is read; it does NOT change slug resolution.
81
+
82
+ ## `launchpad pull`
83
+
84
+ ```bash
85
+ # Write deployed launchpad.yaml to stdout
86
+ launchpad pull horizon
87
+
88
+ # Write to a file
89
+ launchpad pull horizon --out /tmp/deployed.yaml
90
+
91
+ # Inside an app-repo clone — slug inferred from cwd
92
+ launchpad clone horizon
93
+ cd launchpad-app-horizon
94
+ launchpad pull
95
+ ```
96
+
97
+ Exit codes:
98
+
99
+ - **0** — manifest written successfully.
100
+ - **1** — error (no deployed manifest yet, 401, 403, 404, network).
101
+
102
+ ## `launchpad status`
103
+
104
+ Reads `./launchpad.yaml` by default, compares to the deployed
105
+ manifest, reports state.
106
+
107
+ ### Three possible states
108
+
109
+ - **`in sync`** — local and deployed are equivalent. The
110
+ equivalence relation is normalised-AST: whitespace, comments, and
111
+ key ordering do NOT count as drift. Schema defaults are honoured
112
+ (absent key with a default == explicit default).
113
+ - **`drift: <field list>`** — local and deployed differ on at least
114
+ one field in the v1 closed set:
115
+ `metadata.name`, `metadata.team`, `metadata.owner`,
116
+ `metadata.description`, `deployment.type`,
117
+ `access.allowed_entra_group`, `hostnames[0]`, `build.command`,
118
+ `build.destination_dir`, `build.root_dir`, `production_env.*`.
119
+ - **`no deployed manifest yet`** — bot reports no
120
+ `output "<slug>_manifest_sha"`. Run `launchpad deploy` first.
121
+
122
+ ### Three-state surfacing
123
+
124
+ Beyond drift, status also surfaces the relationship between the app
125
+ repo's main HEAD sha and the last-applied manifest sha. If HEAD is
126
+ ahead of deployed:
127
+
128
+ - With an open PR → "an apply may be queued (see PR #N)"
129
+ - Without an open PR → "main is ahead of deployed"
130
+
131
+ This is the load-bearing answer to "is my change going to land"
132
+ without polling tf-apply.
133
+
134
+ ### Exit codes
135
+
136
+ - **0** — in sync, OR drift in default (report-only) mode.
137
+ - **1** — drift, when `--strict` is set.
138
+ - **2** — error (missing local manifest, network, auth, etc.).
139
+
140
+ The default is report-only so casual interactive use never surprises
141
+ the operator. For CI guards, use `--strict`:
142
+
143
+ ```bash
144
+ # Interactive (default): show drift, exit 0
145
+ launchpad status horizon
146
+
147
+ # CI guard: fail the script if local differs from deployed
148
+ launchpad status --strict horizon && launchpad deploy
149
+ ```
150
+
151
+ ### --json output
152
+
153
+ For downstream scripts (M-1187 destroy, M-1189 update-skill, ad-hoc
154
+ CI):
155
+
156
+ ```bash
157
+ launchpad status horizon --json
158
+ ```
159
+
160
+ ```json
161
+ {
162
+ "state": "drift",
163
+ "slug": "horizon",
164
+ "deployedSha": "<40-hex>",
165
+ "headSha": "<40-hex>",
166
+ "hasOpenPr": true,
167
+ "openPrNumber": 42,
168
+ "driftFields": ["metadata.owner", "production_env.API_BASE"],
169
+ "driftDetails": [
170
+ { "path": "metadata.owner", "local": "alice@m-kopa.com", "deployed": "bob@m-kopa.com" },
171
+ { "path": "production_env.API_BASE", "local": "https://new", "deployed": "https://old" }
172
+ ]
173
+ }
174
+ ```
175
+
176
+ ### production_env secret-shape warning
177
+
178
+ If any `production_env.<KEY>` value matches a basic secret-shape
179
+ heuristic — both a known prefix (`sk-`, `ghp_`, `gho_`, `ghu_`, `AKIA`,
180
+ `xoxb-`, `xoxa-`, `xoxp-`) AND length ≥ 32 in a base64-shaped
181
+ character set — status warns on stderr:
182
+
183
+ ```text
184
+ launchpad status: production_env.OPENAI_KEY looks like a secret.
185
+ production_env is non-secret by contract; move secrets to the
186
+ secrets bindings path. (This is informational; status will continue.)
187
+ ```
188
+
189
+ Warning, not block. If you see this: open `launchpad.yaml`, move the
190
+ secret out of `production_env:` and into the `secrets:` bindings
191
+ section (which gets written via `wrangler secret put`, never via
192
+ git).
193
+
194
+ ## Common error patterns + fixes
195
+
196
+ ### "session expired, run `launchpad login`"
197
+
198
+ The Cf Access token is gone or stale. Run `launchpad login` once;
199
+ both verbs work after that for the next ~24 hours (Cf Access session
200
+ length).
201
+
202
+ ### "not authorised for app X (you must be an owner or editor)"
203
+
204
+ You're not on the app's `owner` or `editor` list, or in the
205
+ break-glass admin group. Either:
206
+
207
+ - Ask the app's current owner to add you as an editor via the portal
208
+ dashboard.
209
+ - If you genuinely need break-glass access for this incident, talk to
210
+ platform-team.
211
+
212
+ ### "no deployed manifest for X yet"
213
+
214
+ The app exists in the registry but `tf-apply` hasn't run yet (the
215
+ first deploy may still be queuing) or the apply failed. Check:
216
+
217
+ ```bash
218
+ launchpad apps # confirm lifecycle is `live`
219
+ # If lifecycle is `provisioning` or `failed`, use:
220
+ /launchpad-deploy-status # diagnose the M-892 stage trace
221
+ ```
222
+
223
+ ### "drift: hostnames[0]"
224
+
225
+ The hostname changed in your local manifest but the deployed app
226
+ still has the old hostname. This is a manifest-side change — run
227
+ `launchpad deploy` to roll it out. (Hostname changes also require
228
+ edge-auth + cert re-issuance — the gateway hostname/cert for an
229
+ `auth: gateway` app, or the Cf Access app for an `auth: access` one —
230
+ which the bot handles.)
231
+
232
+ ### "drift: access.allowed_entra_group"
233
+
234
+ Group binding changed in your local manifest. This is the
235
+ **load-bearing** drift — it controls who can access the app. After
236
+ `launchpad deploy`, the resulting PR will require the
237
+ `allowed-groups-change: true` label from a non-author human reviewer
238
+ (AC-S4) before it can auto-merge.
239
+
240
+ ## Related skills
241
+
242
+ - **`/launchpad-deploy`** — provision a new app (uses `launchpad
243
+ create` not `deploy`; see skill for naming history).
244
+ - **`/launchpad-deploy-status`** — interrogate provisioning state
245
+ (M-892 stages). Use during initial provisioning; once the app is
246
+ `live`, `launchpad status` is the daily-use tool.
247
+ - **`/launchpad-content-pr`** — push first content to a freshly
248
+ provisioned app. After the first content lands, `launchpad
249
+ status` is how you check ongoing deploys.
250
+
251
+ ## Anti-patterns
252
+
253
+ - **Don't** try `--platform-repo /path/to/clone` — that flag was
254
+ removed in M-1188. Both verbs are bot-relayed; the operator never
255
+ needs a local platform-repo clone.
256
+ - **Don't** parse the prose output of `launchpad status` in CI
257
+ scripts. Use `--json` and `--strict`.
258
+ - **Don't** rely on exit-1 firing without `--strict`. The default
259
+ behaviour is report-only.
260
+
261
+ ## Version
262
+
263
+ This skill ships in launchpad-cli v0.13.0+ (M-1188 release).
@@ -0,0 +1,155 @@
1
+ # marquee-share-skill
2
+
3
+ A Claude Code skill that uploads an AI-chat HTML artefact to
4
+ [Marquee](https://marquee.launchpad.m-kopa.us) `POST /api/uploads`,
5
+ attributed to the **real signed-in user** (not a service token).
6
+
7
+ This directory is a **self-contained Node program**, isolated from the
8
+ Marquee Cloudflare Pages app at the repo root — it has its own
9
+ `package.json`, `tsconfig.json`, ESLint and Vitest config. It is not
10
+ part of the Pages build and does not deploy with the site.
11
+
12
+ The skill runs from a **pre-built, dependency-free bundle**,
13
+ `dist/cli.js`, which needs only Node 22+ — no `tsx`, no runtime
14
+ `node_modules`. `dist/cli.js` is a **committed build artefact**
15
+ (`npm run build` regenerates it). This matters because the skill is
16
+ distributed by file-copy: the Launchpad CLI skills bundle copies
17
+ `skill/` to `~/.claude/skills/marquee-share/` *without* `node_modules`,
18
+ so the built bundle must be present in the tree.
19
+
20
+ The skill is complete: a Claude Code [`SKILL.md`](./SKILL.md) drives a
21
+ CLI that authenticates the user, wraps an AI-chat result in an
22
+ M-KOPA-branded HTML document, uploads it, and returns a shareable
23
+ `view_url`.
24
+
25
+ ## How it works
26
+
27
+ A user says *"share this via Marquee"* in a Claude Code session.
28
+ Claude renders the current result to an inner-content HTML fragment
29
+ and runs the `share` command. The command wraps that fragment in the
30
+ branded template, uploads the document to Marquee, and prints the
31
+ `view_url`, which Claude returns to the user.
32
+
33
+ ```text
34
+ Claude (renders content HTML)
35
+ └─ marquee-share share src/cli.ts
36
+ ├─ renderBrandedDocument src/render/template.ts (M-KOPA shell)
37
+ ├─ getValidToken src/auth/index.ts (PKCE session)
38
+ └─ POST /api/uploads src/upload/index.ts → view_url
39
+ ```
40
+
41
+ ## Install (personal)
42
+
43
+ To use this skill in your own Claude Code, link it into your personal
44
+ skills directory. Claude Code discovers skills by reading `SKILL.md`
45
+ from each subdirectory of `~/.claude/skills/`; the install script
46
+ symlinks `~/.claude/skills/marquee-share` at this `skill/` directory.
47
+
48
+ ```sh
49
+ cd skill
50
+ ./install.sh # symlink the skill into ~/.claude/skills/
51
+ ```
52
+
53
+ No `npm install` is needed to *use* the skill — it runs from the
54
+ committed, dependency-free `dist/cli.js` bundle. (`npm install` is only
55
+ needed to *develop* the skill — see Development below.)
56
+
57
+ `install.sh` is idempotent — re-running it is a no-op once the link is
58
+ in place. Because it is a symlink, the skill you run always reflects
59
+ this checkout: pull the branch and the skill updates with it. Restart
60
+ Claude Code (or open a new session) to pick up the skill.
61
+
62
+ To remove it:
63
+
64
+ ```sh
65
+ cd skill
66
+ ./install.sh --uninstall
67
+ ```
68
+
69
+ This is the **personal-developer** install path. M-KOPA-wide
70
+ distribution goes through the Launchpad CLI skills bundle, which
71
+ mirrors this directory as its canonical source.
72
+
73
+ ## CLI
74
+
75
+ The skill ships a single command, `marquee-share`, with three
76
+ subcommands. Run the built bundle from this directory:
77
+
78
+ ```sh
79
+ node dist/cli.js login # one-time browser sign-in
80
+ node dist/cli.js logout # clear the cached session
81
+ node dist/cli.js share [file] # wrap + upload; prints the view_url
82
+ ```
83
+
84
+ `share` reads a complete inner-content HTML fragment from `[file]` or,
85
+ when no file is given, from stdin. `--title "<text>"` sets the
86
+ document title. On success it prints **only** the `view_url` to
87
+ stdout; diagnostics go to stderr. On a first run with no session it
88
+ triggers the browser login automatically, then proceeds. The bearer
89
+ token is never printed or logged.
90
+
91
+ `src/cli.ts` is the entrypoint source — a thin dispatcher that wires
92
+ the auth and upload layers together. Its `run()` core takes every side
93
+ effect as an injected dependency, so `tests/cli.test.ts` exercises it
94
+ with fakes (no network, no browser, no real session file).
95
+
96
+ `npm run build` bundles `src/cli.ts` and its imports into a single
97
+ `dist/cli.js` with `bun build --target node`. The skill has zero
98
+ runtime dependencies (auth and upload use only Node built-ins), so the
99
+ bundle is fully self-contained. `dist/cli.js` carries a
100
+ `#!/usr/bin/env node` shebang and is what the installed skill runs.
101
+
102
+ Requires Node `>=22`. The shipped skill runs the built `dist/cli.js`
103
+ under plain Node — no `tsx`, no `node_modules`. `tsx` is a
104
+ devDependency, used only by the `typecheck`/`test` loop below.
105
+
106
+ ## Auth layer
107
+
108
+ `src/auth/` is a vendored copy of the Launchpad CLI's Cloudflare
109
+ Access Managed-OAuth / PKCE flow — see
110
+ [`src/auth/PROVENANCE.md`](./src/auth/PROVENANCE.md) for the source
111
+ commit and the Marquee-specific adaptations.
112
+
113
+ The public surface is `src/auth/index.ts`:
114
+
115
+ | Export | Purpose |
116
+ |--------|---------|
117
+ | `login()` | One-time interactive browser PKCE login; persists a session. |
118
+ | `logout()` | Clears the cached session. |
119
+ | `getValidToken()` | Returns a still-valid access token — reuses a cached session while fresh, silently refreshes on expiry, re-logins when the grant has expired. |
120
+
121
+ ### Security properties
122
+
123
+ - **PKCE public client** — no client secret is generated, received,
124
+ or stored anywhere in the shipped skill.
125
+ - **Session cached at `~/.marquee/session.json`, mode `0600`**;
126
+ parent dir `0700`. Override the path with `MARQUEE_SESSION_PATH`
127
+ (used by tests). Override the Marquee base URL with
128
+ `MARQUEE_RESOURCE_URL`.
129
+ - **The credential is never logged, echoed, or committed.** The auth
130
+ layer prints only non-secret diagnostics (auth URLs, the session
131
+ file *path*). `tests/no-token-leak.test.ts` enforces this.
132
+ - **No client-side JWT verification.** The skill is an OAuth client:
133
+ it presents the token; Marquee's Pages Functions verify it. (See
134
+ the repo's `ANTI-PATTERNS.md`.)
135
+
136
+ ## Development
137
+
138
+ Developing the skill requires [**Bun**](https://bun.sh) installed on
139
+ your `PATH` — `npm run build` invokes `bun build` to produce the
140
+ dependency-free `dist/cli.js` bundle. Bun is a build-time prerequisite
141
+ only; the shipped skill itself runs under plain Node 22+.
142
+
143
+ ```sh
144
+ cd skill
145
+ npm install
146
+ npm run typecheck
147
+ npm run lint
148
+ npm test
149
+ npm run build # regenerate dist/cli.js — commit the result
150
+ ```
151
+
152
+ `dist/cli.js` is a committed build artefact. Any change under `src/`
153
+ must be followed by `npm run build`, and the regenerated `dist/cli.js`
154
+ committed alongside the source change — the skill is distributed by
155
+ file-copy, so a stale or missing bundle ships a stale or broken skill.
@@ -0,0 +1,94 @@
1
+ ---
2
+ name: marquee-share
3
+ description: >-
4
+ Publish the current AI-chat result to Marquee as an M-KOPA-branded,
5
+ shareable web page and return its view URL. Use when the user says
6
+ "share this via Marquee", "share via marquee", "publish this to
7
+ Marquee", "put this on Marquee", or otherwise asks to share or
8
+ publish the current result as a Marquee link.
9
+ ---
10
+
11
+ # Marquee share
12
+
13
+ Turn the current chat result into a self-contained, M-KOPA-branded HTML
14
+ page hosted on [Marquee](https://marquee.launchpad.m-kopa.us) and give
15
+ the user back a shareable `view_url`. The page is attributed to the
16
+ real signed-in user (Cloudflare Access), not a service account.
17
+
18
+ ## When to use
19
+
20
+ Invoke when the user asks to share or publish the current result to
21
+ Marquee — e.g. "share this via Marquee", "publish this to Marquee".
22
+
23
+ ## How to share
24
+
25
+ All commands run from the `skill/` directory and invoke the bundled
26
+ CLI directly with Node — `node dist/cli.js …`. `dist/cli.js` is a
27
+ self-contained build that needs only Node 22+ on PATH; it has no
28
+ runtime dependencies and does not require `npm install` or
29
+ `node_modules/`.
30
+
31
+ 1. **Render the result to inner-content HTML.** Take whatever you just
32
+ produced for the user — prose, markdown, a code block, a table, a
33
+ short report — and write it out as clean, valid **HTML fragment**
34
+ (the inner body content only). Guidance:
35
+ - Headings → `<h2>`/`<h3>`; paragraphs → `<p>`; lists → `<ul>`/`<ol>`.
36
+ - Code → `<pre><code>…</code></pre>`; tables → real `<table>` markup.
37
+ - Escape any literal `<`, `>`, `&` that are meant as text.
38
+ - Write **only the content** — do **not** add `<html>`, `<head>`,
39
+ `<body>`, `<style>`, or `<script>`. Do **not** add your own
40
+ colours, fonts, or layout CSS.
41
+ - The white background and M-KOPA green/black branding are applied
42
+ for you by the skill's branded template — the `share` command
43
+ wraps your content in a complete, self-contained M-KOPA document.
44
+
45
+ 2. **Run the `share` command**, passing the HTML fragment on stdin and
46
+ a short human title:
47
+
48
+ ```sh
49
+ node dist/cli.js share --title "Quarterly summary" < content.html
50
+ ```
51
+
52
+ or pipe the HTML directly:
53
+
54
+ ```sh
55
+ printf '%s' "$CONTENT_HTML" | node dist/cli.js share --title "Quarterly summary"
56
+ ```
57
+
58
+ The `share` command prints **only** the resulting `view_url` to
59
+ stdout. Capture that single line.
60
+
61
+ 3. **Return the `view_url`** to the user as the shareable link. Do not
62
+ print anything else from the command's output as the result.
63
+
64
+ ## One-time browser sign-in
65
+
66
+ The first time `share` runs on a machine with no Marquee session, it
67
+ opens a browser for a one-time Cloudflare Access sign-in, then
68
+ continues automatically — no separate step needed. The user can also
69
+ sign in ahead of time:
70
+
71
+ ```sh
72
+ node dist/cli.js login
73
+ ```
74
+
75
+ The session is cached locally (`~/.marquee/session.json`, mode `0600`)
76
+ and refreshed silently. The skill never prints or logs the token.
77
+
78
+ To sign out:
79
+
80
+ ```sh
81
+ node dist/cli.js logout
82
+ ```
83
+
84
+ ## Notes
85
+
86
+ - Requires Node `>=22`. No `npm install` is needed to run the skill —
87
+ `dist/cli.js` is a pre-built, dependency-free bundle.
88
+ - This skill is installed by symlinking `~/.claude/skills/marquee-share`
89
+ at the repo's `skill/` directory — see `skill/install.sh` and
90
+ `skill/README.md` for the install/uninstall steps.
91
+ - `dist/cli.js` is a committed build artefact. After changing anything
92
+ under `src/`, regenerate it with `npm run build` in `skill/`.
93
+ - If `share` exits non-zero, it prints a short `error: …` line on
94
+ stderr — report that to the user; do not retry blindly.
@@ -0,0 +1,27 @@
1
+ # Vendored skill — DO NOT EDIT BY HAND
2
+
3
+ This directory is a **mirror** of the `marquee-share` skill. Its
4
+ canonical source is the `M-KOPA/marquee` repository, under `skill/`.
5
+
6
+ It is vendored here so `launchpad skills install` can copy a working
7
+ skill into `~/.claude/skills/` straight from the published
8
+ `@m-kopa/launchpad-cli` npm package.
9
+
10
+ ## Provenance
11
+
12
+ - source repo: `M-KOPA/marquee`
13
+ - source path: `skill/`
14
+ - source commit: `eb8261a621244667edc23d80850d5c39c1b54ee4`
15
+ - synced by: `packages/launchpad-cli/scripts/sync-marquee-share-skill.sh`
16
+
17
+ ## Updating
18
+
19
+ Do **not** edit files in this directory directly — changes will be
20
+ overwritten and will drift from the canonical source. To pull a new
21
+ version of the skill:
22
+
23
+ ```sh
24
+ packages/launchpad-cli/scripts/sync-marquee-share-skill.sh <marquee-checkout> [ref]
25
+ ```
26
+
27
+ then review the diff and commit. Run this at marquee-share release time.