@codepassion/skills 1.2.0 → 1.4.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 (34) hide show
  1. package/README.md +6 -1
  2. package/SKILLS.md +6 -1
  3. package/bin/c9n-skills.js +28 -3
  4. package/install.sh +25 -2
  5. package/package.json +1 -1
  6. package/skills/c9n/SKILL.md +279 -0
  7. package/skills/c9n-pipeline/SKILL.md +128 -0
  8. package/skills/c9n-pipeline/templates/Dockerfile.api +49 -0
  9. package/skills/c9n-pipeline/templates/Dockerfile.service +45 -0
  10. package/skills/c9n-pipeline/templates/Dockerfile.web +50 -0
  11. package/skills/c9n-pipeline/templates/dockerignore +18 -0
  12. package/skills/c9n-pipeline/templates/workflows/_build-job.partial.yml +13 -0
  13. package/skills/c9n-pipeline/templates/workflows/_deploy-job.partial.yml +17 -0
  14. package/skills/c9n-pipeline/templates/workflows/_migration-check.partial.yml +16 -0
  15. package/skills/c9n-pipeline/templates/workflows/ci.yml +38 -0
  16. package/skills/c9n-pipeline/templates/workflows/deployment.yml +21 -0
  17. package/skills/c9n-sentry/REFERENCE.md +172 -0
  18. package/skills/c9n-sentry/SKILL.md +53 -0
  19. package/skills/c9n-spec/SKILL.md +92 -0
  20. package/skills/c9n-spec/templates/.github/pull_request_template.md +29 -0
  21. package/skills/c9n-spec/templates/AGENTS.md +179 -0
  22. package/skills/c9n-spec/templates/CLAUDE.md +6 -0
  23. package/skills/c9n-spec/templates/CONTEXT.md +53 -0
  24. package/skills/c9n-spec/templates/DESIGN.md +73 -0
  25. package/skills/c9n-spec/templates/docs/SRS.md +37 -0
  26. package/skills/c9n-spec/templates/docs/adr/0001-spec-driven-iso29110-docs.md +44 -0
  27. package/skills/c9n-spec/templates/docs/adr/_TEMPLATE.md +26 -0
  28. package/skills/c9n-spec/templates/docs/agents/domain.md +43 -0
  29. package/skills/c9n-spec/templates/docs/agents/issue-tracker.md +22 -0
  30. package/skills/c9n-spec/templates/docs/agents/triage-labels.md +27 -0
  31. package/skills/c9n-spec/templates/docs/architecture.md +50 -0
  32. package/skills/c9n-spec/templates/docs/features/_TEMPLATE.md +34 -0
  33. package/skills/c9n-spec/templates/docs/test-plan.md +30 -0
  34. package/skills/c9n-spec/templates/docs/traceability.md +27 -0
package/README.md CHANGED
@@ -8,8 +8,13 @@ Agent skills (SKILL.md) based on the CodePassion (C9N) conventions.
8
8
  npx @codepassion/skills install codex # ~/.codex/skills
9
9
  npx @codepassion/skills install project # ./.agents/skills
10
10
  npx @codepassion/skills install combined # one combined skill
11
+ npx @codepassion/skills install cowork # zip skills for claude.ai/Cowork upload (UI, no API)
11
12
 
12
- Or: ./install.sh {claude|codex|project|combined}
13
+ Or: ./install.sh {claude|codex|project|combined|cowork}
14
+
15
+ claude.ai and Claude Cowork share a personal skill library with no upload API, so `cowork`
16
+ packages upload-ready zips into dist/cowork/ — upload them at
17
+ claude.ai → Settings → Capabilities → Upload skill.
13
18
 
14
19
  ## Edit
15
20
 
package/SKILLS.md CHANGED
@@ -8,8 +8,13 @@ Agent skills (SKILL.md) based on the CodePassion (C9N) conventions.
8
8
  npx @codepassion/skills install codex # ~/.codex/skills
9
9
  npx @codepassion/skills install project # ./.agents/skills
10
10
  npx @codepassion/skills install combined # one combined skill
11
+ npx @codepassion/skills install cowork # zip skills for claude.ai/Cowork upload (UI, no API)
11
12
 
12
- Or: ./install.sh {claude|codex|project|combined}
13
+ Or: ./install.sh {claude|codex|project|combined|cowork}
14
+
15
+ claude.ai and Claude Cowork share a personal skill library with no upload API, so `cowork`
16
+ packages upload-ready zips into dist/cowork/ — upload them at
17
+ claude.ai → Settings → Capabilities → Upload skill.
13
18
 
14
19
  ## Edit
15
20
 
package/bin/c9n-skills.js CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { cp, mkdir, readdir, stat } from 'node:fs/promises'
2
+ import { cp, mkdir, readdir, rm, stat } from 'node:fs/promises'
3
3
  import { existsSync } from 'node:fs'
4
4
  import { homedir } from 'node:os'
5
5
  import { dirname, join, resolve } from 'node:path'
6
6
  import { fileURLToPath } from 'node:url'
7
+ import { execFile } from 'node:child_process'
7
8
  const SRC = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'skills')
8
9
  const [,, cmd='install', target='claude', ...rest] = process.argv
9
10
  if (cmd==='help'||cmd==='--help'||cmd==='-h'){ help(); process.exit(0) }
@@ -12,7 +13,12 @@ const flags={dest:null,force:false,dryRun:false}
12
13
  for (let i=0;i<rest.length;i++){ const a=rest[i]
13
14
  if (a==='--force') flags.force=true
14
15
  else if (a==='--dry-run') flags.dryRun=true
15
- else if (a==='--dest') flags.dest=resolve(rest[++i]??'') }
16
+ else if (a==='--dest'){ const v=rest[++i]
17
+ if (!v){ console.error('--dest requires a value'); process.exit(1) }
18
+ flags.dest=resolve(v) } }
19
+ // cowork: claude.ai / Cowork share a personal skill library with NO upload API —
20
+ // this packages upload-ready zips for manual drag-drop in Settings → Capabilities.
21
+ if (target==='cowork'){ await packageForCowork(flags); process.exit(0) }
16
22
  const dest = flags.dest ?? defaultDest(target)
17
23
  if (!dest){ help(); process.exit(1) }
18
24
  const sources = await resolveSources(target)
@@ -33,4 +39,23 @@ function defaultDest(t){ const h=homedir()
33
39
  if (t==='codex') return join(h,'.codex','skills')
34
40
  if (t==='project') return join(process.cwd(),'.agents','skills')
35
41
  return null }
36
- function help(){ console.log('c9n-skills install [claude|codex|project|combined] [--dest <dir>] [--force] [--dry-run]') }
42
+ async function packageForCowork(flags){
43
+ const ROOT=resolve(SRC,'..')
44
+ // only auto-clean the default output dir — never delete a user-supplied --dest
45
+ const isDefaultOut=!flags.dest
46
+ const out=flags.dest ?? join(ROOT,'dist','cowork')
47
+ const sources=[...await resolveSources('combined'), ...await resolveSources('cowork')]
48
+ if (!sources.length){ console.error('No skills found'); process.exit(1) }
49
+ console.log(`Packaging ${sources.length} skill zip(s) into ${out}${flags.dryRun?' (dry-run)':''}`)
50
+ if (!flags.dryRun){ if (isDefaultOut) await rm(out,{recursive:true,force:true}); await mkdir(out,{recursive:true}) }
51
+ for (const src of sources){ const n=src.split(/[\\/]/).pop()
52
+ console.log(` zip ${n}.zip`)
53
+ if (!flags.dryRun) await zipDir(SRC,n,join(out,`${n}.zip`)) }
54
+ console.log('Done. Upload at claude.ai → Settings → Capabilities → Upload skill (shared with Cowork).')
55
+ console.log('Action skills (c9n-spec, c9n-deliverables) bundle their templates/scripts in the zip.')
56
+ }
57
+ function zipDir(cwd,name,outFile){
58
+ return new Promise((res,rej)=>{ execFile('zip',['-qr',outFile,name],{cwd},(e)=>{
59
+ if (e && e.code==='ENOENT') rej(new Error("'zip' command not found — install zip and retry"))
60
+ else if (e) rej(e); else res() }) }) }
61
+ function help(){ console.log('c9n-skills install [claude|codex|project|combined|cowork] [--dest <dir>] [--force] [--dry-run]') }
package/install.sh CHANGED
@@ -1,13 +1,36 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
- SRC="$(cd "$(dirname "$0")" && pwd)/skills"
3
+ ROOT="$(cd "$(dirname "$0")" && pwd)"
4
+ SRC="$ROOT/skills"
4
5
  MODE="${1:-claude}"
6
+
7
+ # cowork: claude.ai / Cowork share a personal skill library with NO upload API —
8
+ # package upload-ready zips for manual drag-drop in Settings → Capabilities.
9
+ if [ "$MODE" = "cowork" ]; then
10
+ command -v zip >/dev/null || { echo "error: 'zip' not found — install zip and retry"; exit 1; }
11
+ DEST="${TARGET_DIR:-$ROOT/dist/cowork}"
12
+ # only auto-clean the default output dir — never delete a user-supplied TARGET_DIR
13
+ if [ -z "${TARGET_DIR:-}" ]; then rm -rf "$DEST"; fi
14
+ mkdir -p "$DEST"
15
+ count=0
16
+ for d in "$SRC"/c9n "$SRC"/c9n-*; do
17
+ [ -d "$d" ] || continue
18
+ name="$(basename "$d")"
19
+ (cd "$SRC" && zip -qr "$DEST/$name.zip" "$name")
20
+ count=$((count + 1))
21
+ done
22
+ echo "Packaged $count skill zip(s) into $DEST"
23
+ echo "Upload at claude.ai → Settings → Capabilities → Upload skill (shared with Cowork)."
24
+ command -v open >/dev/null && open "$DEST" 2>/dev/null || true
25
+ exit 0
26
+ fi
27
+
5
28
  case "$MODE" in
6
29
  claude) DEST="${TARGET_DIR:-$HOME/.claude/skills}" ;;
7
30
  codex) DEST="${TARGET_DIR:-$HOME/.codex/skills}" ;;
8
31
  project) DEST="${TARGET_DIR:-$PWD/.agents/skills}" ;;
9
32
  combined) DEST="${TARGET_DIR:-$HOME/.claude/skills}" ;;
10
- *) echo "Use: claude | codex | project | combined"; exit 1 ;;
33
+ *) echo "Use: claude | codex | project | combined | cowork"; exit 1 ;;
11
34
  esac
12
35
  mkdir -p "$DEST"
13
36
  if [ "$MODE" = "combined" ]; then cp -R "$SRC/c9n" "$DEST/"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codepassion/skills",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Agent skills (SKILL.md) for CodePassion (C9N) engineering conventions.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -237,3 +237,282 @@ These ALWAYS run, in order — never skip them:
237
237
  - **Redoc/Swagger UI → PDF**: they lazy-render; printing truncates. Use `openapi-to-pdf.mjs`.
238
238
  - See [REFERENCE.md](../c9n-deliverables/REFERENCE.md) for the ISO/IEC 29110 layout and per-artifact recipes.
239
239
 
240
+
241
+
242
+ ---
243
+
244
+ ## Pipeline
245
+
246
+ Bootstraps the Code Passion (C9N) CI/CD pipeline into a target repo so a new project deploys the
247
+ same way as the reference implementation (Smiley): **one image per app, built once at the Alpha
248
+ tag, redeployed to Beta/Production by base tag**, env injected at runtime by Coolify. The heavy
249
+ lifting lives in the org reusable workflows `codepassion-team/cicd/.github/workflows/{build,deploy}.yml`
250
+ — the per-repo files this skill generates just call them. The skill's real value is **Phase 3**:
251
+ the interactive ops checklist that encodes every footgun the Smiley setup hit.
252
+
253
+ Bundled `templates/` are the source skeleton. **Copy them, then substitute tokens** — never edit
254
+ the templates in place for a specific project. The Next.js wiring is an **edit** of files the dev
255
+ already owns, not a file drop (see Phase 2).
256
+
257
+ ## What gets generated
258
+
259
+ | Path | Template | When |
260
+ |------|----------|------|
261
+ | `<app>/Dockerfile` | `Dockerfile.api` | app has `elysia` |
262
+ | `<app>/Dockerfile` | `Dockerfile.web` | app has `next` |
263
+ | `<app>/Dockerfile` | `Dockerfile.service` | any other deployable app (worker, plain Bun) |
264
+ | `.dockerignore` | `dockerignore` | always (repo root) |
265
+ | `.github/workflows/ci.yml` | `workflows/ci.yml` (+ `_migration-check.partial` if DB) | always |
266
+ | `.github/workflows/deployment.yml` | `workflows/deployment.yml` (+ `_build-job`/`_deploy-job` partials) | always |
267
+ | `<web>/next.config.js` | **edit, not copy** | per web app |
268
+ | `<web>/app/layout.tsx` | **edit, not copy** | per web app |
269
+
270
+ ## Workflow
271
+
272
+ Run in order; this is a table of contents — rules are below.
273
+
274
+ 1. **Detect (Phase 1).** Scan `apps/*/package.json`. Classify each: `next`→web, `elysia`→api, else
275
+ →service. Detect DB by **real artifacts** — a migration-generate script in some `package.json`
276
+ AND a migrations dir on disk — capture their actual names; never assume `drizzle:generate` /
277
+ `packages/supabase/migrations`. Read root `package.json` for the Bun version / lockfile name.
278
+ Ask which environments exist (Alpha / Beta / Production). **Print the detected plan and confirm
279
+ before writing anything.**
280
+ 2. **Generate (Phase 2).** Copy templates with tokens substituted. **Diff/confirm per file** — if a
281
+ target exists, show the diff and ask overwrite/skip/merge; never blind-clobber; leave unrelated
282
+ files (a hand-kept `staging.yaml`) alone. Then do the **web edits** (below). Run the web build
283
+ once to prove nothing is baked.
284
+ 3. **Checklist (Phase 3).** Walk the dev interactively through the out-of-repo ops (below). This is
285
+ the deliverable.
286
+ 4. **Verify (Phase 4).** After the dev confirms the ops are done, push a tag (or reuse one), watch
287
+ the Actions run, then `curl` each deployed URL per environment and report pass/fail per app.
288
+
289
+ ## Token substitution
290
+
291
+ | Token | Source |
292
+ |-------|--------|
293
+ | `{{PACKAGE_NAME}}` | `<project>-<appkey>`, e.g. `smiley-web`. Lowercase. This is the ghcr image name and the `package_name` workflow input. |
294
+ | `{{APP_DIR}}` | The app's path, e.g. `apps/web`. |
295
+ | `{{APP_KEY}}` | Short lowercase id unique per app: `api`, `web`, `worker`. Used in job names. |
296
+ | `{{APP_PORT}}` | `3001` api · `3000` web · service's listen port (drop `EXPOSE` for a portless worker). |
297
+ | `{{BUN_VERSION}}` | Pin from root toolchain, e.g. `1.3.10`. Same across all Dockerfiles. |
298
+ | `{{WORKSPACE_MANIFEST_COPIES}}` | **web template only:** one `COPY <app>/package.json ./<app>/package.json` line **per app** — the `deps` stage runs `bun install` before `COPY . .`, so it needs every workspace manifest to resolve the graph. The api/service compile templates copy only the built app's manifest (matches Smiley's api Dockerfile; avoids pulling a sibling app's postinstall without its src). |
299
+ | `{{MIGRATION_CHECK}}` | `_migration-check.partial.yml` with `{{MIGRATION_SCRIPT}}`/`{{MIGRATIONS_DIR}}` filled, **or empty string** if no DB. |
300
+ | `{{ORG_REF}}` | The org reusable-workflow ref — currently `turborepo`. One place; bump here if the org repo retags. |
301
+ | `{{BUILD_JOBS}}` / `{{DEPLOY_JOBS}}` | Stamp `_build-job.partial` once per app (Alpha only) and `_deploy-job.partial` once per app per environment, pruning unused envs. |
302
+ | `{{DEPLOY_NAME}}` | The org `deploy.yml` `name` input → uppercased to `<NAME>_UUID`. **Only `API` and `WEB` are wired** in the org deploy.yml (see Gotchas). |
303
+
304
+ ## Web edits (surgical — not a file drop)
305
+
306
+ For each **web** app, edit the files the dev owns; fail loudly if they can't be parsed rather than
307
+ overwrite:
308
+
309
+ - `next.config.js` — add `output: "standalone"`, `outputFileTracingRoot` (monorepo root), and
310
+ `transpilePackages` for the workspace packages the app imports.
311
+ - `app/layout.tsx` — add `<head><PublicEnvScript /></head>` (`import { PublicEnvScript } from
312
+ "next-runtime-env"`). Client code reads public config with `env("NEXT_PUBLIC_X")`, **not**
313
+ `process.env` (which bakes at build → undefined at runtime).
314
+ - Flag `next-runtime-env` as a dependency to add — **do not run `bun add` yourself**; tell the dev.
315
+ - **Caveat:** runtime values only reach the browser on non-statically-prerendered pages. A page that
316
+ reads runtime public env must opt out of static caching (`export const dynamic = "force-dynamic"`)
317
+ or rely on `<PublicEnvScript>` keeping the document dynamic. Note this so future feature work
318
+ doesn't silently bake a static page.
319
+
320
+ ## Phase 3 — interactive ops checklist (the deliverable)
321
+
322
+ Derived from the actual `codepassion-team/cicd` reusable workflows. Walk these one at a time:
323
+
324
+ **Repo prerequisites**
325
+ - A **`develop` branch must exist** — `build.yml` commits the version bump and does
326
+ `git pull --rebase origin develop` / `push HEAD:develop`. No develop branch → build fails.
327
+ - Tags drive deploys; the exact tag→env mapping is **c9n-semver-deployment** (don't restate it).
328
+
329
+ **Per environment** — create a GitHub **Environment** named exactly `Alpha` / `Beta` / `Production`:
330
+ - Variables: `COOLIFY_RESOURCE_API_UUID`, `COOLIFY_RESOURCE_WEB_UUID` (the Coolify app UUIDs;
331
+ `deploy.yml` resolves `${NAME}_UUID`), and `TURBO_TEAM`.
332
+ - Secrets: `COOLIFY_TOKEN` (Coolify API bearer) and `TURBO_TOKEN`. `GITHUB_TOKEN` is automatic
333
+ (ghcr push uses it; the build job already declares `packages: write`).
334
+
335
+ **Coolify** (one resource per app per env): source = Docker image
336
+ `ghcr.io/codepassion-team/{{PACKAGE_NAME}}:<base_tag>`; copy each resource's UUID into the matching
337
+ env variable above. Caddy `reverse_proxy <app>:<port>` (api `:3001`, web `:3000`).
338
+
339
+ **ghcr** — packages are **private**: give Coolify a registry pull credential (or set package
340
+ visibility), or the image pull silently fails.
341
+
342
+ **Web runtime env in Coolify** (the scars):
343
+ - `PORT=3000` — Next standalone obeys `PORT`; Coolify defaults it to `80`, which 502s behind a
344
+ Caddy `:3000` proxy. This is the single most common web-deploy failure.
345
+ - `HOSTNAME=0.0.0.0`.
346
+ - The app's `NEXT_PUBLIC_*` values — injected here at runtime, not baked into the image.
347
+
348
+ ## Phase 4 — verify
349
+
350
+ Push a `v*.*.*` tag (or reuse one), watch the run (`gh run watch`), then for each app per env
351
+ `curl -so /dev/null -w "%{http_code}"` the deployed URL (e.g. `https://api.<proj>.alpha.c9n.co`,
352
+ `https://<proj>.alpha.c9n.co`) and confirm `200`. Report a clean pass/fail table. Don't call it done
353
+ on a green Actions run alone — the 502 class only shows at the live URL.
354
+
355
+ ## Gotchas
356
+
357
+ - **Combined install mode does not carry `templates/`.** `install.sh combined` copies only the
358
+ generated `skills/c9n` SKILL.md body. Install via `install.sh claude` (or `project`/`codex`) so the
359
+ templates come along — same limitation as `c9n-spec`/`c9n-deliverables`.
360
+ - **Org deploy.yml wires only `API_UUID` + `WEB_UUID`.** A third app type (worker) needs a PR to
361
+ `codepassion-team/cicd` adding `COOLIFY_RESOURCE_<NAME>_UUID` to deploy.yml's `env:` block. Flag
362
+ this; don't generate a deploy job that silently can't resolve its UUID.
363
+ - **Build once, deploy by base tag.** build.yml strips `-alpha`/`-rc.*`; deploy.yml also strips
364
+ `-beta.*`. So `v1.2.3` (Alpha) and a later `v1.2.3-beta.1` release both resolve base tag `v1.2.3`
365
+ and redeploy the same image. Beta/Prod must have an Alpha build of that base version in ghcr first.
366
+ - **`.dockerignore` matters.** Without it, the host's `node_modules` (wrong platform) leaks into the
367
+ build context and clobbers the linux deps → "failed to load next.config". The template ships it.
368
+ - **Don't touch `.env*`.** Generate `.dockerignore` to exclude them; never read or write env files.
369
+
370
+
371
+
372
+ ---
373
+
374
+ ## Sentry
375
+
376
+ ## Sentry — Bun + Elysia + Next.js
377
+
378
+ ## Three landmines (check first)
379
+
380
+ 1. **`@sentry/node` under `bun --compile` fails silently** — valid DSN, zero events. Use `@sentry/elysia` (built on `@sentry/bun`). Never let `@sentry/node` load on the API.
381
+ 2. **Runtime public env** — if the project uses `next-runtime-env` (ADR 0005 pattern), the browser DSN MUST use `env("NEXT_PUBLIC_SENTRY_DSN")` not `process.env`. Server/edge/api use `process.env`. Wrong = undefined DSN in prod = dead Sentry.
382
+ 3. **PDPA / PII** — add `beforeSend` scrubber + `sendDefaultPii: false`. Strip request body/query/cookies, drop auth headers, recursively redact PII keys. See [REFERENCE.md](REFERENCE.md#pdpa-scrubber).
383
+
384
+ ## Quick start
385
+
386
+ ```
387
+ packages/sentry/ ← @repo/sentry (shared)
388
+ src/pdpa.ts ← beforeSend scrubber (shared by all inits)
389
+ src/bun.ts ← initBun() for apps/api
390
+ src/client.ts ← initClient() for browser (uses env() not process.env)
391
+ src/server.ts ← initServer() for Next.js server runtime
392
+ src/edge.ts ← initEdge() for Next.js edge runtime
393
+ package.json ← exports map: ./bun ./client ./server ./edge ./pdpa
394
+ apps/api/src/lib/sentry.ts ← import { initBun } from "@repo/sentry/bun"; initBun()
395
+ apps/api/src/index.ts ← first import: "./lib/sentry.js", then withElysia(new Elysia())
396
+ apps/web/instrumentation.ts ← register() → dynamic import server/edge config
397
+ apps/web/instrumentation-client.ts ← initClient()
398
+ apps/web/sentry.server.config.ts / sentry.edge.config.ts
399
+ apps/web/next.config.js ← withSentryConfig + transpilePackages: ["@repo/sentry"]
400
+ ```
401
+
402
+ ## Env vars
403
+
404
+ | Service | Key | Read by |
405
+ |---------|-----|---------|
406
+ | API (Coolify) | `SENTRY_DSN`, `ENVIRONMENT` | `process.env` |
407
+ | Web (Coolify) | `NEXT_PUBLIC_SENTRY_DSN`, `NEXT_PUBLIC_ENVIRONMENT` | `env()` from next-runtime-env (client) / `process.env` (server/edge) |
408
+
409
+ One Sentry project, same DSN in both vars. Add `initialScope: { tags: { app: "api" } }` to `initBun()` so api vs web events are distinguishable in the shared project.
410
+
411
+ ## Verification checklist
412
+
413
+ - [ ] `bun install --frozen-lockfile` clean
414
+ - [ ] `bun run check-types` (api + web) clean
415
+ - [ ] `bun run build --compile` on API — confirms `@sentry/node` never loaded (the landmine test)
416
+ - [ ] Web Sentry bundle builds green
417
+ - [ ] After deploy: `window.__ENV.NEXT_PUBLIC_SENTRY_DSN` populated in devtools (runtime env proof)
418
+ - [ ] Trigger a test error → event appears in Sentry tagged correct environment
419
+ - [ ] Open event payload — no phone/otp/lat/lng/member_id (PDPA proof)
420
+
421
+ ## Reference
422
+
423
+ See [REFERENCE.md](REFERENCE.md) for full file contents, PDPA scrubber code, and `turbo.json` config.
424
+
425
+
426
+
427
+ ---
428
+
429
+ ## Spec
430
+
431
+ Bootstraps the Code Passion (C9N) spec-driven development doc system into a target repo. Drops
432
+ a self-contained, agent-readable documentation skeleton so a fresh project starts with the same
433
+ approval gates, traceability, and glossary discipline as the reference implementation
434
+ (ddg-jewelry). Fully generic — adapt the placeholders to whatever stack the project uses.
435
+
436
+ Bundled `templates/` are the source skeleton. **Copy them, then substitute tokens** — never edit
437
+ the templates in place for a specific project.
438
+
439
+ ## What gets scaffolded
440
+
441
+ | File | Role |
442
+ |------|------|
443
+ | `AGENTS.md` | **Canonical** agent context: the full ISO/IEC 29110 spec-driven protocol (inlined, so the repo is self-contained) + C9N stack + module patterns. References the sibling `c9n-*` convention skills rather than copying them. |
444
+ | `CLAUDE.md` | Bridge that `@AGENTS.md`-imports the canonical file (so Claude Code's memory auto-loads the protocol; other tools read AGENTS.md directly). |
445
+ | `CONTEXT.md` | Ubiquitous-language glossary skeleton (no implementation detail). |
446
+ | `DESIGN.md` | Visual design-system rules — **only if the repo has a frontend.** |
447
+ | `docs/SRS.md` | Requirements master registry (feature short-codes + system NFRs). |
448
+ | `docs/architecture.md` | Software-design as-is map. |
449
+ | `docs/traceability.md` | Requirement → design → test master index. |
450
+ | `docs/test-plan.md` | Master test strategy. |
451
+ | `docs/adr/0001-spec-driven-iso29110-docs.md` | Seed ADR: why this doc system exists. |
452
+ | `docs/adr/_TEMPLATE.md` | ADR format for future decisions. |
453
+ | `docs/features/_TEMPLATE.md` | Per-feature deep-doc stub (Requirements · Design · Test Plan · Test Cases · Traceability). |
454
+ | `docs/agents/{issue-tracker,triage-labels,domain}.md` | Agent-process docs (GitHub `gh`, triage vocab, domain-doc consumption). |
455
+ | `.github/pull_request_template.md` | The Definition-of-Done merge gate. |
456
+
457
+ ## Workflow
458
+
459
+ Run each step in order; this is a table of contents — the rules are below.
460
+
461
+ 1. **Detect repo shape.** Read the target's root `package.json`/workspaces. Decide:
462
+ monorepo vs single app; **frontend present?** (`apps/web`, or `next`/`astro`/`vite`/`react`
463
+ in any `package.json`) → include `DESIGN.md`. **Backend/API present?** → keep the API
464
+ module-patterns section in AGENTS.md; otherwise tell the user it's deletable.
465
+ 2. **No-clobber.** For every target path, **skip if it already exists** — never overwrite. Collect
466
+ skipped paths for the final report. (Re-running the skill must be safe and idempotent.)
467
+ 3. **Copy templates** into the target repo, substituting tokens (below). Skip `DESIGN.md` if no
468
+ frontend. Skip the API section guidance if no backend.
469
+ 4. **Optional scan — OFF by default.** Only if the user **explicitly** asks to seed from code
470
+ ("scan the codebase", "seed the glossary", "reverse-document"): read the codebase and fill
471
+ - `CONTEXT.md` — first real glossary terms (actors, core entities) from the domain;
472
+ - `docs/architecture.md` — the actual module map + data-model relationships;
473
+ - `docs/features/` — one as-built stub per shipped module (low-confidence, no test cases),
474
+ and register each in `docs/SRS.md` with a fresh short-code.
475
+ Without this request, leave the placeholders untouched.
476
+ 5. **Report.** Print created vs skipped files, then next steps: pick the first feature, write its
477
+ SRS section (Gate 1), get sign-off, then Design + Test Plan (Gate 2) — no production code
478
+ before Gate 2.
479
+
480
+ ## Token substitution
481
+
482
+ When copying, replace these placeholders:
483
+
484
+ | Token | Source |
485
+ |-------|--------|
486
+ | `{{PROJECT_NAME}}` | Target root dir name, or `package.json` `name` (humanized). |
487
+ | `{{YEAR}}` / `{{DATE}}` | Current year / ISO date (for the seed ADR). |
488
+ | `{{DECIDER}}` | Ask, or default to the repo owner. |
489
+ | Stack rows in AGENTS.md | Keep C9N defaults; trim rows the detected stack doesn't use. |
490
+
491
+ ## Conventions (referenced, not copied)
492
+
493
+ The scaffolded `AGENTS.md` points at the sibling C9N skills for engineering conventions — it does
494
+ **not** inline them, so they have a single source of truth:
495
+
496
+ - Commits → `c9n-commit-messages` · Branching → `c9n-git-branching` · PRs → `c9n-pull-requests`
497
+ - Clean code → `c9n-clean-code-typescript` · Lint/types → `c9n-typescript-eslint`
498
+ - Formatting → `c9n-code-formatting` · Review → `c9n-code-review`
499
+
500
+ For C9N-internal repos every dev has these installed, so the references always resolve. If you
501
+ scaffold into a repo where they may not be installed, tell the user to
502
+ `npx @codepassion/skills install claude`.
503
+
504
+ ## Gotchas
505
+
506
+ - **Combined install mode does not carry `templates/`.** `install.sh combined` copies only the
507
+ generated `skills/c9n` SKILL.md body; the bundled templates are absent. Install `c9n-spec` via
508
+ `install.sh claude` (or `project`/`codex`) so the templates come along. Same limitation as
509
+ `c9n-deliverables`' scripts.
510
+ - **AGENTS.md vs CLAUDE.md.** AGENTS.md is the real file. CLAUDE.md exists only so Claude Code's
511
+ memory auto-load reaches the protocol — it does so via the **`@AGENTS.md` import** (Claude Code's
512
+ memory-import syntax), not a markdown link (a link is inert and pulls nothing into context). Don't
513
+ duplicate content into CLAUDE.md. If a target tool doesn't support `@`-imports, it reads AGENTS.md
514
+ directly anyway, so nothing is lost.
515
+ - **Don't grade the stubs.** If you run the optional scan, as-built stubs describe *current*
516
+ behavior at low confidence with **no** test cases — deepen on touch, per the protocol. Don't
517
+ invent acceptance criteria for code you only read.
518
+
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: c9n-pipeline
3
+ description: Bootstrap the C9N CI/CD pipeline INTO a Turborepo/Bun monorepo — generates the per-repo GitHub Actions (CI + tag-driven Deployment that call the org codepassion-team/cicd reusable workflows), per-app Dockerfiles (Elysia→bun-compile, Next.js standalone+runtime-env, generic service), and .dockerignore; then walks the developer through the out-of-repo ops (GitHub Environments, Coolify resources, ghcr pull, the PORT=3000 web footgun) and verifies the live deploy. Use when setting up CI/CD on a new C9N project, "add github actions", "set up deployment", "wire up Coolify", or porting the Smiley pipeline to another repo. Complements c9n-semver-deployment (owns the tag->env mapping) and c9n-spec (scaffolds docs, not pipelines).
4
+ ---
5
+
6
+ Bootstraps the Code Passion (C9N) CI/CD pipeline into a target repo so a new project deploys the
7
+ same way as the reference implementation (Smiley): **one image per app, built once at the Alpha
8
+ tag, redeployed to Beta/Production by base tag**, env injected at runtime by Coolify. The heavy
9
+ lifting lives in the org reusable workflows `codepassion-team/cicd/.github/workflows/{build,deploy}.yml`
10
+ — the per-repo files this skill generates just call them. The skill's real value is **Phase 3**:
11
+ the interactive ops checklist that encodes every footgun the Smiley setup hit.
12
+
13
+ Bundled `templates/` are the source skeleton. **Copy them, then substitute tokens** — never edit
14
+ the templates in place for a specific project. The Next.js wiring is an **edit** of files the dev
15
+ already owns, not a file drop (see Phase 2).
16
+
17
+ ## What gets generated
18
+
19
+ | Path | Template | When |
20
+ |------|----------|------|
21
+ | `<app>/Dockerfile` | `Dockerfile.api` | app has `elysia` |
22
+ | `<app>/Dockerfile` | `Dockerfile.web` | app has `next` |
23
+ | `<app>/Dockerfile` | `Dockerfile.service` | any other deployable app (worker, plain Bun) |
24
+ | `.dockerignore` | `dockerignore` | always (repo root) |
25
+ | `.github/workflows/ci.yml` | `workflows/ci.yml` (+ `_migration-check.partial` if DB) | always |
26
+ | `.github/workflows/deployment.yml` | `workflows/deployment.yml` (+ `_build-job`/`_deploy-job` partials) | always |
27
+ | `<web>/next.config.js` | **edit, not copy** | per web app |
28
+ | `<web>/app/layout.tsx` | **edit, not copy** | per web app |
29
+
30
+ ## Workflow
31
+
32
+ Run in order; this is a table of contents — rules are below.
33
+
34
+ 1. **Detect (Phase 1).** Scan `apps/*/package.json`. Classify each: `next`→web, `elysia`→api, else
35
+ →service. Detect DB by **real artifacts** — a migration-generate script in some `package.json`
36
+ AND a migrations dir on disk — capture their actual names; never assume `drizzle:generate` /
37
+ `packages/supabase/migrations`. Read root `package.json` for the Bun version / lockfile name.
38
+ Ask which environments exist (Alpha / Beta / Production). **Print the detected plan and confirm
39
+ before writing anything.**
40
+ 2. **Generate (Phase 2).** Copy templates with tokens substituted. **Diff/confirm per file** — if a
41
+ target exists, show the diff and ask overwrite/skip/merge; never blind-clobber; leave unrelated
42
+ files (a hand-kept `staging.yaml`) alone. Then do the **web edits** (below). Run the web build
43
+ once to prove nothing is baked.
44
+ 3. **Checklist (Phase 3).** Walk the dev interactively through the out-of-repo ops (below). This is
45
+ the deliverable.
46
+ 4. **Verify (Phase 4).** After the dev confirms the ops are done, push a tag (or reuse one), watch
47
+ the Actions run, then `curl` each deployed URL per environment and report pass/fail per app.
48
+
49
+ ## Token substitution
50
+
51
+ | Token | Source |
52
+ |-------|--------|
53
+ | `{{PACKAGE_NAME}}` | `<project>-<appkey>`, e.g. `smiley-web`. Lowercase. This is the ghcr image name and the `package_name` workflow input. |
54
+ | `{{APP_DIR}}` | The app's path, e.g. `apps/web`. |
55
+ | `{{APP_KEY}}` | Short lowercase id unique per app: `api`, `web`, `worker`. Used in job names. |
56
+ | `{{APP_PORT}}` | `3001` api · `3000` web · service's listen port (drop `EXPOSE` for a portless worker). |
57
+ | `{{BUN_VERSION}}` | Pin from root toolchain, e.g. `1.3.10`. Same across all Dockerfiles. |
58
+ | `{{WORKSPACE_MANIFEST_COPIES}}` | **web template only:** one `COPY <app>/package.json ./<app>/package.json` line **per app** — the `deps` stage runs `bun install` before `COPY . .`, so it needs every workspace manifest to resolve the graph. The api/service compile templates copy only the built app's manifest (matches Smiley's api Dockerfile; avoids pulling a sibling app's postinstall without its src). |
59
+ | `{{MIGRATION_CHECK}}` | `_migration-check.partial.yml` with `{{MIGRATION_SCRIPT}}`/`{{MIGRATIONS_DIR}}` filled, **or empty string** if no DB. |
60
+ | `{{ORG_REF}}` | The org reusable-workflow ref — currently `turborepo`. One place; bump here if the org repo retags. |
61
+ | `{{BUILD_JOBS}}` / `{{DEPLOY_JOBS}}` | Stamp `_build-job.partial` once per app (Alpha only) and `_deploy-job.partial` once per app per environment, pruning unused envs. |
62
+ | `{{DEPLOY_NAME}}` | The org `deploy.yml` `name` input → uppercased to `<NAME>_UUID`. **Only `API` and `WEB` are wired** in the org deploy.yml (see Gotchas). |
63
+
64
+ ## Web edits (surgical — not a file drop)
65
+
66
+ For each **web** app, edit the files the dev owns; fail loudly if they can't be parsed rather than
67
+ overwrite:
68
+
69
+ - `next.config.js` — add `output: "standalone"`, `outputFileTracingRoot` (monorepo root), and
70
+ `transpilePackages` for the workspace packages the app imports.
71
+ - `app/layout.tsx` — add `<head><PublicEnvScript /></head>` (`import { PublicEnvScript } from
72
+ "next-runtime-env"`). Client code reads public config with `env("NEXT_PUBLIC_X")`, **not**
73
+ `process.env` (which bakes at build → undefined at runtime).
74
+ - Flag `next-runtime-env` as a dependency to add — **do not run `bun add` yourself**; tell the dev.
75
+ - **Caveat:** runtime values only reach the browser on non-statically-prerendered pages. A page that
76
+ reads runtime public env must opt out of static caching (`export const dynamic = "force-dynamic"`)
77
+ or rely on `<PublicEnvScript>` keeping the document dynamic. Note this so future feature work
78
+ doesn't silently bake a static page.
79
+
80
+ ## Phase 3 — interactive ops checklist (the deliverable)
81
+
82
+ Derived from the actual `codepassion-team/cicd` reusable workflows. Walk these one at a time:
83
+
84
+ **Repo prerequisites**
85
+ - A **`develop` branch must exist** — `build.yml` commits the version bump and does
86
+ `git pull --rebase origin develop` / `push HEAD:develop`. No develop branch → build fails.
87
+ - Tags drive deploys; the exact tag→env mapping is **c9n-semver-deployment** (don't restate it).
88
+
89
+ **Per environment** — create a GitHub **Environment** named exactly `Alpha` / `Beta` / `Production`:
90
+ - Variables: `COOLIFY_RESOURCE_API_UUID`, `COOLIFY_RESOURCE_WEB_UUID` (the Coolify app UUIDs;
91
+ `deploy.yml` resolves `${NAME}_UUID`), and `TURBO_TEAM`.
92
+ - Secrets: `COOLIFY_TOKEN` (Coolify API bearer) and `TURBO_TOKEN`. `GITHUB_TOKEN` is automatic
93
+ (ghcr push uses it; the build job already declares `packages: write`).
94
+
95
+ **Coolify** (one resource per app per env): source = Docker image
96
+ `ghcr.io/codepassion-team/{{PACKAGE_NAME}}:<base_tag>`; copy each resource's UUID into the matching
97
+ env variable above. Caddy `reverse_proxy <app>:<port>` (api `:3001`, web `:3000`).
98
+
99
+ **ghcr** — packages are **private**: give Coolify a registry pull credential (or set package
100
+ visibility), or the image pull silently fails.
101
+
102
+ **Web runtime env in Coolify** (the scars):
103
+ - `PORT=3000` — Next standalone obeys `PORT`; Coolify defaults it to `80`, which 502s behind a
104
+ Caddy `:3000` proxy. This is the single most common web-deploy failure.
105
+ - `HOSTNAME=0.0.0.0`.
106
+ - The app's `NEXT_PUBLIC_*` values — injected here at runtime, not baked into the image.
107
+
108
+ ## Phase 4 — verify
109
+
110
+ Push a `v*.*.*` tag (or reuse one), watch the run (`gh run watch`), then for each app per env
111
+ `curl -so /dev/null -w "%{http_code}"` the deployed URL (e.g. `https://api.<proj>.alpha.c9n.co`,
112
+ `https://<proj>.alpha.c9n.co`) and confirm `200`. Report a clean pass/fail table. Don't call it done
113
+ on a green Actions run alone — the 502 class only shows at the live URL.
114
+
115
+ ## Gotchas
116
+
117
+ - **Combined install mode does not carry `templates/`.** `install.sh combined` copies only the
118
+ generated `skills/c9n` SKILL.md body. Install via `install.sh claude` (or `project`/`codex`) so the
119
+ templates come along — same limitation as `c9n-spec`/`c9n-deliverables`.
120
+ - **Org deploy.yml wires only `API_UUID` + `WEB_UUID`.** A third app type (worker) needs a PR to
121
+ `codepassion-team/cicd` adding `COOLIFY_RESOURCE_<NAME>_UUID` to deploy.yml's `env:` block. Flag
122
+ this; don't generate a deploy job that silently can't resolve its UUID.
123
+ - **Build once, deploy by base tag.** build.yml strips `-alpha`/`-rc.*`; deploy.yml also strips
124
+ `-beta.*`. So `v1.2.3` (Alpha) and a later `v1.2.3-beta.1` release both resolve base tag `v1.2.3`
125
+ and redeploy the same image. Beta/Prod must have an Alpha build of that base version in ghcr first.
126
+ - **`.dockerignore` matters.** Without it, the host's `node_modules` (wrong platform) leaks into the
127
+ build context and clobbers the linux deps → "failed to load next.config". The template ships it.
128
+ - **Don't touch `.env*`.** Generate `.dockerignore` to exclude them; never read or write env files.
@@ -0,0 +1,49 @@
1
+ # -----------------------------------------------------------------------------
2
+ # {{PACKAGE_NAME}} (Elysia BFF) — compiled to a single Bun binary, run on
3
+ # debian slim. Generated by c9n-pipeline; tune freely after.
4
+ # -----------------------------------------------------------------------------
5
+
6
+ FROM oven/bun:{{BUN_VERSION}} AS build
7
+
8
+ WORKDIR /app
9
+
10
+ # Root manifests + lockfile
11
+ COPY package.json bun.lock ./
12
+
13
+ # This app's manifest + shared packages (compile needs only this member's graph)
14
+ COPY {{APP_DIR}}/package.json ./{{APP_DIR}}/package.json
15
+ COPY packages/ ./packages/
16
+
17
+ # Source needed to compile (copied before install so workspace links resolve)
18
+ COPY {{APP_DIR}}/src ./{{APP_DIR}}/src
19
+
20
+ RUN bun install
21
+
22
+ ENV NODE_ENV=production
23
+
24
+ # Compile the API to a standalone binary from the repo root
25
+ RUN bun build \
26
+ --compile \
27
+ --minify-whitespace \
28
+ --minify-syntax \
29
+ --target=bun \
30
+ --outfile /app/server \
31
+ ./{{APP_DIR}}/src/index.ts
32
+
33
+ # Runtime image — debian slim for compiled-Bun-binary compatibility
34
+ FROM debian:bookworm-slim
35
+
36
+ WORKDIR /app
37
+
38
+ RUN apt-get update && \
39
+ apt-get install -y --no-install-recommends ca-certificates && \
40
+ rm -rf /var/lib/apt/lists/*
41
+
42
+ COPY --from=build /app/server ./server
43
+ RUN chmod +x ./server
44
+
45
+ ENV NODE_ENV=production
46
+
47
+ EXPOSE {{APP_PORT}}
48
+
49
+ CMD ["./server"]
@@ -0,0 +1,45 @@
1
+ # -----------------------------------------------------------------------------
2
+ # {{PACKAGE_NAME}} (generic Bun service / worker) — compiled to a single Bun
3
+ # binary, run on debian slim. Generated by c9n-pipeline.
4
+ # A worker with no HTTP port: drop the EXPOSE line and skip its Coolify domain.
5
+ # NOTE: the org deploy.yml only wires API_UUID + WEB_UUID — a third app type
6
+ # needs a PR to codepassion-team/cicd to add its COOLIFY_RESOURCE_<NAME>_UUID.
7
+ # -----------------------------------------------------------------------------
8
+
9
+ FROM oven/bun:{{BUN_VERSION}} AS build
10
+
11
+ WORKDIR /app
12
+
13
+ COPY package.json bun.lock ./
14
+ COPY {{APP_DIR}}/package.json ./{{APP_DIR}}/package.json
15
+ COPY packages/ ./packages/
16
+ COPY {{APP_DIR}}/src ./{{APP_DIR}}/src
17
+
18
+ RUN bun install
19
+
20
+ ENV NODE_ENV=production
21
+
22
+ RUN bun build \
23
+ --compile \
24
+ --minify-whitespace \
25
+ --minify-syntax \
26
+ --target=bun \
27
+ --outfile /app/server \
28
+ ./{{APP_DIR}}/src/index.ts
29
+
30
+ FROM debian:bookworm-slim
31
+
32
+ WORKDIR /app
33
+
34
+ RUN apt-get update && \
35
+ apt-get install -y --no-install-recommends ca-certificates && \
36
+ rm -rf /var/lib/apt/lists/*
37
+
38
+ COPY --from=build /app/server ./server
39
+ RUN chmod +x ./server
40
+
41
+ ENV NODE_ENV=production
42
+
43
+ EXPOSE {{APP_PORT}}
44
+
45
+ CMD ["./server"]