codebyplan 1.5.1 → 1.9.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 (211) hide show
  1. package/dist/cli.js +4462 -748
  2. package/package.json +5 -1
  3. package/templates/.gitkeep +0 -0
  4. package/templates/README.md +20 -0
  5. package/templates/agents/cbp-cc-executor.md +213 -0
  6. package/templates/agents/cbp-database-agent.md +229 -0
  7. package/templates/agents/cbp-improve-claude.md +245 -0
  8. package/templates/agents/cbp-improve-round.md +284 -0
  9. package/templates/agents/cbp-mechanical-edits.md +111 -0
  10. package/templates/agents/cbp-research.md +282 -0
  11. package/templates/agents/cbp-round-executor.md +604 -0
  12. package/templates/agents/cbp-security-agent.md +134 -0
  13. package/templates/agents/cbp-task-check.md +213 -0
  14. package/templates/agents/cbp-task-planner.md +582 -0
  15. package/templates/agents/cbp-test-e2e-agent.md +363 -0
  16. package/templates/agents/cbp-testing-qa-agent.md +400 -0
  17. package/templates/context/mcp-docs.md +139 -0
  18. package/templates/hooks/README.md +236 -0
  19. package/templates/hooks/cbp-auto-test-hooks.sh +44 -0
  20. package/templates/hooks/cbp-lint-format-on-edit.sh +159 -0
  21. package/templates/hooks/cbp-maestro-yaml-validate.sh +100 -0
  22. package/templates/hooks/cbp-mcp-migration-guard.sh +32 -0
  23. package/templates/hooks/cbp-mcp-round-sync.sh +79 -0
  24. package/templates/hooks/cbp-mcp-worktree-inject.sh +76 -0
  25. package/templates/hooks/cbp-notify.sh +68 -0
  26. package/templates/hooks/cbp-plugin-dispatch.sh +29 -0
  27. package/templates/hooks/cbp-pre-commit-quality-gate.sh +204 -0
  28. package/templates/hooks/cbp-statusline.sh +347 -0
  29. package/templates/hooks/cbp-subagent-statusline.sh +182 -0
  30. package/templates/hooks/cbp-test-coverage-gate.sh +144 -0
  31. package/templates/hooks/cbp-test-hooks.sh +320 -0
  32. package/templates/hooks/hooks.json +85 -0
  33. package/templates/hooks/validate-context-usage.sh +59 -0
  34. package/templates/hooks/validate-git-commit.sh +78 -0
  35. package/templates/hooks/validate-git-stash-deny.sh +32 -0
  36. package/templates/hooks/validate-structure-lengths.sh +57 -0
  37. package/templates/hooks/validate-structure-lib.sh +104 -0
  38. package/templates/hooks/validate-structure-patterns.sh +54 -0
  39. package/templates/hooks/validate-structure-scope.sh +33 -0
  40. package/templates/hooks/validate-structure-smoke.sh +95 -0
  41. package/templates/hooks/validate-structure-templates.sh +34 -0
  42. package/templates/hooks/validate-structure.sh +69 -0
  43. package/templates/rules/.gitkeep +0 -0
  44. package/templates/rules/README.md +47 -0
  45. package/templates/rules/context-file-loading.md +52 -0
  46. package/templates/rules/scope-vocabulary.md +64 -0
  47. package/templates/rules/todo-backend.md +109 -0
  48. package/templates/settings.project.base.json +55 -0
  49. package/templates/settings.user.base.json +25 -0
  50. package/templates/skills/cbp-build-cc-agent/SKILL.md +139 -0
  51. package/templates/skills/cbp-build-cc-agent/examples/read-only-reviewer.md +32 -0
  52. package/templates/skills/cbp-build-cc-agent/examples/with-hooks.md +41 -0
  53. package/templates/skills/cbp-build-cc-agent/examples/with-skills-preload.md +25 -0
  54. package/templates/skills/cbp-build-cc-agent/reference/cbp-quality.md +153 -0
  55. package/templates/skills/cbp-build-cc-agent/reference/frontmatter-fields.md +37 -0
  56. package/templates/skills/cbp-build-cc-agent/reference/permission-modes.md +18 -0
  57. package/templates/skills/cbp-build-cc-agent/scripts/validate-agent.sh +67 -0
  58. package/templates/skills/cbp-build-cc-agent/templates/agent.md +66 -0
  59. package/templates/skills/cbp-build-cc-claude-file/SKILL.md +178 -0
  60. package/templates/skills/cbp-build-cc-claude-file/examples/minimal-project.md +33 -0
  61. package/templates/skills/cbp-build-cc-claude-file/examples/monorepo-with-imports.md +39 -0
  62. package/templates/skills/cbp-build-cc-claude-file/reference/imports.md +72 -0
  63. package/templates/skills/cbp-build-cc-claude-file/reference/what-belongs.md +39 -0
  64. package/templates/skills/cbp-build-cc-claude-file/templates/project-claude-md.md +48 -0
  65. package/templates/skills/cbp-build-cc-claude-file/templates/user-claude-md.md +22 -0
  66. package/templates/skills/cbp-build-cc-memory/SKILL.md +201 -0
  67. package/templates/skills/cbp-build-cc-memory/examples/feedback-memory.md +11 -0
  68. package/templates/skills/cbp-build-cc-memory/examples/project-memory.md +11 -0
  69. package/templates/skills/cbp-build-cc-memory/examples/reference-memory.md +13 -0
  70. package/templates/skills/cbp-build-cc-memory/examples/user-memory.md +14 -0
  71. package/templates/skills/cbp-build-cc-memory/reference/memory-types.md +59 -0
  72. package/templates/skills/cbp-build-cc-memory/reference/when-to-save.md +62 -0
  73. package/templates/skills/cbp-build-cc-memory/templates/MEMORY-index.md +4 -0
  74. package/templates/skills/cbp-build-cc-memory/templates/memory-entry.md +15 -0
  75. package/templates/skills/cbp-build-cc-mode/SKILL.md +99 -0
  76. package/templates/skills/cbp-build-cc-rule/SKILL.md +176 -0
  77. package/templates/skills/cbp-build-cc-rule/examples/global-rule.md +19 -0
  78. package/templates/skills/cbp-build-cc-rule/examples/scoped-rule.md +41 -0
  79. package/templates/skills/cbp-build-cc-rule/reference/paths-patterns.md +48 -0
  80. package/templates/skills/cbp-build-cc-rule/templates/rule.md +32 -0
  81. package/templates/skills/cbp-build-cc-settings/SKILL.md +220 -0
  82. package/templates/skills/cbp-build-cc-settings/examples/hooks-config.json +64 -0
  83. package/templates/skills/cbp-build-cc-settings/examples/permissions-config.json +34 -0
  84. package/templates/skills/cbp-build-cc-settings/examples/sandbox-config.json +42 -0
  85. package/templates/skills/cbp-build-cc-settings/reference/cbp-conventions.md +104 -0
  86. package/templates/skills/cbp-build-cc-settings/reference/permission-rules.md +61 -0
  87. package/templates/skills/cbp-build-cc-settings/reference/scope-precedence.md +73 -0
  88. package/templates/skills/cbp-build-cc-settings/reference/settings-fields.md +166 -0
  89. package/templates/skills/cbp-build-cc-settings/templates/settings.json +23 -0
  90. package/templates/skills/cbp-build-cc-settings/templates/settings.local.json +10 -0
  91. package/templates/skills/cbp-build-cc-skill/SKILL.md +154 -0
  92. package/templates/skills/cbp-build-cc-skill/examples/dynamic-context.md +31 -0
  93. package/templates/skills/cbp-build-cc-skill/examples/fork-skill.md +22 -0
  94. package/templates/skills/cbp-build-cc-skill/examples/knowledge-skill.md +25 -0
  95. package/templates/skills/cbp-build-cc-skill/examples/task-skill.md +29 -0
  96. package/templates/skills/cbp-build-cc-skill/reference/cbp-quality.md +157 -0
  97. package/templates/skills/cbp-build-cc-skill/reference/frontmatter-fields.md +35 -0
  98. package/templates/skills/cbp-build-cc-skill/reference/string-substitutions.md +60 -0
  99. package/templates/skills/cbp-build-cc-skill/scripts/validate-skill.sh +90 -0
  100. package/templates/skills/cbp-build-cc-skill/templates/skill.md +51 -0
  101. package/templates/skills/cbp-checkpoint-check/SKILL.md +156 -0
  102. package/templates/skills/cbp-checkpoint-complete/SKILL.md +109 -0
  103. package/templates/skills/cbp-checkpoint-create/SKILL.md +116 -0
  104. package/templates/skills/cbp-checkpoint-end/SKILL.md +241 -0
  105. package/templates/skills/cbp-checkpoint-plan/SKILL.md +137 -0
  106. package/templates/skills/cbp-checkpoint-plan/reference/alternative-comparison-template.md +54 -0
  107. package/templates/skills/cbp-checkpoint-plan/reference/dep-decision-rubric.md +50 -0
  108. package/templates/skills/cbp-checkpoint-plan/reference/e2e-discovery-probe.md +57 -0
  109. package/templates/skills/cbp-checkpoint-plan/reference/gap-analysis-playbook.md +47 -0
  110. package/templates/skills/cbp-checkpoint-start/SKILL.md +84 -0
  111. package/templates/skills/cbp-checkpoint-update/SKILL.md +115 -0
  112. package/templates/skills/cbp-frontend-a11y/SKILL.md +109 -0
  113. package/templates/skills/cbp-frontend-a11y/reference/aria-roles-states.md +130 -0
  114. package/templates/skills/cbp-frontend-a11y/reference/contrast-visual.md +122 -0
  115. package/templates/skills/cbp-frontend-a11y/reference/keyboard-patterns.md +154 -0
  116. package/templates/skills/cbp-frontend-a11y/reference/semantic-html.md +111 -0
  117. package/templates/skills/cbp-frontend-design/SKILL.md +145 -0
  118. package/templates/skills/cbp-frontend-design/reference/nextjs-scss.md +118 -0
  119. package/templates/skills/cbp-frontend-design/reference/rn-expo.md +101 -0
  120. package/templates/skills/cbp-frontend-design/reference/tauri-react.md +82 -0
  121. package/templates/skills/cbp-frontend-ui/SKILL.md +262 -0
  122. package/templates/skills/cbp-frontend-ui/reference/ui-label-maps.md +42 -0
  123. package/templates/skills/cbp-frontend-ui/reference/ui-layout-patterns.md +105 -0
  124. package/templates/skills/cbp-frontend-ui/reference/variant-defaults.md +149 -0
  125. package/templates/skills/cbp-frontend-ux/SKILL.md +181 -0
  126. package/templates/skills/cbp-git-branch-feat-create/SKILL.md +115 -0
  127. package/templates/skills/cbp-git-commit/SKILL.md +278 -0
  128. package/templates/skills/cbp-git-worktree-create/SKILL.md +226 -0
  129. package/templates/skills/cbp-git-worktree-remove/SKILL.md +145 -0
  130. package/templates/skills/cbp-merge-main/SKILL.md +228 -0
  131. package/templates/skills/cbp-round-check/SKILL.md +104 -0
  132. package/templates/skills/cbp-round-end/SKILL.md +183 -0
  133. package/templates/skills/cbp-round-end/reference/findings-presentation.md +44 -0
  134. package/templates/skills/cbp-round-end/reference/inline-fallback.md +35 -0
  135. package/templates/skills/cbp-round-execute/SKILL.md +211 -0
  136. package/templates/skills/cbp-round-execute/reference/inline-fallback.md +59 -0
  137. package/templates/skills/cbp-round-input/SKILL.md +165 -0
  138. package/templates/skills/cbp-round-start/SKILL.md +222 -0
  139. package/templates/skills/cbp-round-update/SKILL.md +163 -0
  140. package/templates/skills/cbp-session-end/SKILL.md +187 -0
  141. package/templates/skills/cbp-session-start/SKILL.md +155 -0
  142. package/templates/skills/cbp-ship/SKILL.md +332 -0
  143. package/templates/skills/cbp-ship/reference/changesets-overview.md +120 -0
  144. package/templates/skills/cbp-ship/reference/eas-cli-overview.md +60 -0
  145. package/templates/skills/cbp-ship/reference/gh-cli-overview.md +135 -0
  146. package/templates/skills/cbp-ship/reference/gh-cli-shipment-commands.md +283 -0
  147. package/templates/skills/cbp-ship/reference/npm-publish-monorepo.md +252 -0
  148. package/templates/skills/cbp-ship/reference/npm-publish-oidc-trusted.md +157 -0
  149. package/templates/skills/cbp-ship/reference/npm-publish-overview.md +171 -0
  150. package/templates/skills/cbp-ship/reference/preflight-checklist.md +88 -0
  151. package/templates/skills/cbp-ship/reference/railway-nestjs-deployment.md +169 -0
  152. package/templates/skills/cbp-ship/reference/railway-overview.md +120 -0
  153. package/templates/skills/cbp-ship/reference/railway-troubleshooting.md +168 -0
  154. package/templates/skills/cbp-ship/reference/release-please-overview.md +99 -0
  155. package/templates/skills/cbp-ship/reference/surface-expo-eas.md +155 -0
  156. package/templates/skills/cbp-ship/reference/surface-npm.md +180 -0
  157. package/templates/skills/cbp-ship/reference/surface-railway.md +152 -0
  158. package/templates/skills/cbp-ship/reference/surface-supabase.md +178 -0
  159. package/templates/skills/cbp-ship/reference/surface-tauri.md +138 -0
  160. package/templates/skills/cbp-ship/reference/surface-vercel.md +124 -0
  161. package/templates/skills/cbp-ship/reference/surface-vscode-ext.md +144 -0
  162. package/templates/skills/cbp-ship/reference/surfaces.md +60 -0
  163. package/templates/skills/cbp-ship/reference/testflight-automation.md +215 -0
  164. package/templates/skills/cbp-ship/reference/testflight-internal-vs-external.md +69 -0
  165. package/templates/skills/cbp-ship/reference/testflight-overview.md +98 -0
  166. package/templates/skills/cbp-ship/reference/versioning.md +116 -0
  167. package/templates/skills/cbp-ship/scripts/detect-surfaces.sh +217 -0
  168. package/templates/skills/cbp-ship/scripts/verify-expo-eas.sh +35 -0
  169. package/templates/skills/cbp-ship/scripts/verify-npm.sh +21 -0
  170. package/templates/skills/cbp-ship/scripts/verify-railway.sh +41 -0
  171. package/templates/skills/cbp-ship/scripts/verify-supabase.sh +19 -0
  172. package/templates/skills/cbp-ship/scripts/verify-tauri.sh +24 -0
  173. package/templates/skills/cbp-ship/scripts/verify-vercel.sh +32 -0
  174. package/templates/skills/cbp-ship/scripts/verify-vscode-ext.sh +25 -0
  175. package/templates/skills/cbp-ship/templates/eas.json +66 -0
  176. package/templates/skills/cbp-ship/templates/railway.toml +15 -0
  177. package/templates/skills/cbp-ship/templates/release-please-config.json +17 -0
  178. package/templates/skills/cbp-ship/templates/vercel.json +19 -0
  179. package/templates/skills/cbp-ship/templates/vscodeignore +21 -0
  180. package/templates/skills/cbp-ship/templates/workflow-changesets.yml +41 -0
  181. package/templates/skills/cbp-ship/templates/workflow-eas-submit.yml +53 -0
  182. package/templates/skills/cbp-ship/templates/workflow-npm-publish.yml +36 -0
  183. package/templates/skills/cbp-ship/templates/workflow-release-please.yml +21 -0
  184. package/templates/skills/cbp-ship/templates/workflow-tauri-release.yml +69 -0
  185. package/templates/skills/cbp-ship/templates/workflow-vsce-publish.yml +31 -0
  186. package/templates/skills/cbp-ship-configure/SKILL.md +296 -0
  187. package/templates/skills/cbp-ship-configure/reference/expo-mobile.md +204 -0
  188. package/templates/skills/cbp-ship-configure/reference/npm-package.md +165 -0
  189. package/templates/skills/cbp-ship-configure/reference/railway-backend.md +199 -0
  190. package/templates/skills/cbp-ship-configure/reference/supabase.md +200 -0
  191. package/templates/skills/cbp-ship-configure/reference/tauri-desktop.md +181 -0
  192. package/templates/skills/cbp-ship-configure/reference/vercel.md +117 -0
  193. package/templates/skills/cbp-ship-configure/reference/vscode-ext.md +155 -0
  194. package/templates/skills/cbp-ship-main/SKILL.md +65 -0
  195. package/templates/skills/cbp-supabase-branch-check/SKILL.md +337 -0
  196. package/templates/skills/cbp-supabase-branch-check/reference/dag-steps.md +29 -0
  197. package/templates/skills/cbp-supabase-migrate/SKILL.md +314 -0
  198. package/templates/skills/cbp-supabase-migrate/reference/advisor-triage.md +70 -0
  199. package/templates/skills/cbp-supabase-migrate/reference/cli-fallback.md +87 -0
  200. package/templates/skills/cbp-supabase-migrate/reference/preflight-dry-run.md +58 -0
  201. package/templates/skills/cbp-supabase-setup/SKILL.md +239 -0
  202. package/templates/skills/cbp-supabase-setup/reference/branching-setup.md +121 -0
  203. package/templates/skills/cbp-supabase-setup/reference/cli-fallback.md +109 -0
  204. package/templates/skills/cbp-task-check/SKILL.md +166 -0
  205. package/templates/skills/cbp-task-complete/SKILL.md +206 -0
  206. package/templates/skills/cbp-task-complete/reference/checkpoint-done-branching.md +48 -0
  207. package/templates/skills/cbp-task-complete/reference/next-step-heuristic.md +56 -0
  208. package/templates/skills/cbp-task-create/SKILL.md +167 -0
  209. package/templates/skills/cbp-task-start/SKILL.md +239 -0
  210. package/templates/skills/cbp-task-testing/SKILL.md +277 -0
  211. package/templates/skills/cbp-todo/SKILL.md +111 -0
@@ -0,0 +1,283 @@
1
+ # GitHub CLI — Shipment Commands
2
+
3
+ Commands `/cbp-ship` actually runs, organised by surface. Authoritative reference: <https://cli.github.com/manual>. Each section: exact command, flags we use, JSON shape (where applicable), common errors.
4
+
5
+ ## 1. Pull Requests
6
+
7
+ ### `gh pr create` — open a PR
8
+
9
+ ```bash
10
+ gh pr create --base main --head "$BRANCH" \
11
+ --title "$TITLE" --body-file .claude/tmp/pr-body.md --draft
12
+ ```
13
+
14
+ | Flag | Purpose |
15
+ |------|---------|
16
+ | `--base <branch>` | Target. CBP ships `feat/* -> main`; always set explicitly. |
17
+ | `--head <branch>` | Source. Defaults to current; pass explicitly with `--repo`. |
18
+ | `--title` | One-line title. Obey `rules/git-workflow.md` (no Claude attribution). |
19
+ | `--body-file <path>` | Multiline body. Preferred over `--body`. |
20
+ | `--draft` | Open in draft. CBP default for non-trivial PRs. |
21
+ | `--reviewer @me,user,team/slug` | Optional. |
22
+ | `--label`, `--assignee`, `--milestone`, `--project` | Optional metadata. |
23
+ | `--fill` | Use commit subject + body as title/body. |
24
+ | `--repo OWNER/REPO` | Required outside the checkout. |
25
+
26
+ Output: prints the URL on stdout (no JSON). Capture the PR number with `gh pr view --json number`.
27
+
28
+ Common errors:
29
+
30
+ - `A pull request already exists for ...` — branch has an open PR; use `gh pr view`.
31
+ - `must provide --title and --body (or --fill)` — non-TTY without flags.
32
+ - `Head sha can't be blank` — branch not pushed; `git push -u origin <branch>` first.
33
+
34
+ ### `gh pr view` — read PR state
35
+
36
+ ```bash
37
+ gh pr view "$PR_NUMBER" \
38
+ --json number,state,mergeable,mergeStateStatus,headRefOid,baseRefName,reviewDecision,statusCheckRollup,url \
39
+ --jq .
40
+ ```
41
+
42
+ | Field | Type | Use |
43
+ |-------|------|-----|
44
+ | `state` | `OPEN`/`CLOSED`/`MERGED` | Gate further actions |
45
+ | `mergeable` | `MERGEABLE`/`CONFLICTING`/`UNKNOWN` | Pre-merge gate |
46
+ | `mergeStateStatus` | `CLEAN`/`BLOCKED`/`BEHIND`/`DIRTY`/`UNSTABLE`/`HAS_HOOKS` | Why merge is blocked |
47
+ | `headRefOid` | sha | Pin a `--match-head-commit` merge |
48
+ | `reviewDecision` | `APPROVED`/`CHANGES_REQUESTED`/`REVIEW_REQUIRED` | Review gate |
49
+ | `statusCheckRollup` | array | Per-check status; filter with `--jq` |
50
+
51
+ Pass `--json` with no value to discover the full field list.
52
+
53
+ ### `gh pr merge` — merge a PR
54
+
55
+ ```bash
56
+ gh pr merge "$PR_NUMBER" --merge --delete-branch --match-head-commit "$HEAD_OID"
57
+ ```
58
+
59
+ | Flag | Purpose |
60
+ |------|---------|
61
+ | `--merge`/`--squash`/`--rebase` | Strategy. CBP default is `--merge`. `--rebase` often blocked by branch protection. |
62
+ | `--auto` | Auto-merge once required checks pass. |
63
+ | `--delete-branch` (`-d`) | Delete head after merge. Always set for `feat/*`. |
64
+ | `--admin` | Bypass branch protection. Reserved for emergency hotfixes; never default. |
65
+ | `--match-head-commit <oid>` | Refuse merge if HEAD has moved. Use `headRefOid` from `gh pr view`. |
66
+ | `--subject`, `--body` | Override merge commit message. |
67
+
68
+ Common errors:
69
+
70
+ - `Pull request is not mergeable: <reason>` — inspect `mergeStateStatus`. `BLOCKED` = required checks/reviews missing.
71
+ - `auto-merge is not allowed for this repository` — repo disables it; remove `--auto`.
72
+ - HTTP 405 on protected branches without `--admin` — expected; do NOT auto-escalate.
73
+
74
+ ## 2. Releases
75
+
76
+ ### `gh release create`
77
+
78
+ ```bash
79
+ gh release create "$TAG" --title "$TITLE" --notes-file CHANGELOG-fragment.md \
80
+ --target "$COMMIT_SHA" --latest ./dist/*.tar.gz ./dist/*.zip
81
+ ```
82
+
83
+ Argument order: `gh release create <tag> [<asset>...]`. Tag is positional + required.
84
+
85
+ | Flag | Purpose |
86
+ |------|---------|
87
+ | `--title` (`-t`) | Title. Defaults to tag. |
88
+ | `--notes-file` (`-F`) | Notes from file. `-` for stdin. |
89
+ | `--generate-notes` | Auto-generate via API; combinable with `--notes-file` (file content goes above generated). |
90
+ | `--target <branch-or-sha>` | Tag-target if tag does not exist. |
91
+ | `--draft` (`-d`) | Save as draft. CBP default for human review. |
92
+ | `--prerelease` (`-p`) | Prerelease flag. |
93
+ | `--latest` / `--latest=false` | Mark/unmark as Latest. |
94
+ | `--verify-tag` | Refuse if tag does not exist remotely. |
95
+ | `--discussion-category <name>` | Open a discussion. |
96
+ | `--fail-on-no-commits` | Abort if no new commits since last release. |
97
+
98
+ Asset upload: paths after the tag. Custom labels via `'/path/to/file.zip#Display Label'`.
99
+
100
+ Common errors:
101
+
102
+ - `tag already exists` (without `--target`) — local tag points to different sha; `git push --tags` or `git tag -d` + recreate.
103
+ - `validation failed: name has already been taken` — use `gh release edit` or `gh release delete` first.
104
+ - `could not find tag` with `--verify-tag` — push the tag first.
105
+
106
+ ### `gh release view`
107
+
108
+ ```bash
109
+ gh release view "$TAG" --json tagName,name,isDraft,isLatest,isPrerelease,publishedAt,url,assets --jq .
110
+ ```
111
+
112
+ JSON fields: `tagName`, `name`, `body`, `isDraft`, `isLatest`, `isPrerelease`, `publishedAt`, `url`, `tarballUrl`, `zipballUrl`, `assets[].{name,size,downloadCount,url}`.
113
+
114
+ ### `gh release delete`
115
+
116
+ ```bash
117
+ gh release delete "$TAG" --yes --cleanup-tag
118
+ ```
119
+
120
+ `--yes` skips confirmation (required non-TTY). `--cleanup-tag` also deletes the underlying git tag. `release not found` is a soft failure — treat as already-clean.
121
+
122
+ ## 3. Secrets
123
+
124
+ ### `gh secret list`
125
+
126
+ ```bash
127
+ gh secret list --repo "$OWNER/$REPO"
128
+ gh secret list --org "$ORG"
129
+ gh secret list --env "$ENV" --repo "$OWNER/$REPO"
130
+ ```
131
+
132
+ Output is tab-separated `NAME UPDATED`. No `--json`. Secret values are never returned.
133
+
134
+ ### `gh secret set`
135
+
136
+ ```bash
137
+ echo -n "$VALUE" | gh secret set MY_SECRET # repo-level
138
+ gh secret set --env-file .env.production # bulk from dotenv
139
+ echo -n "$VALUE" | gh secret set MY_SECRET --env production # environment
140
+ echo -n "$VALUE" | gh secret set MY_SECRET --org myorg --visibility selected --repos repo1,repo2
141
+ echo -n "$VALUE" | gh secret set MY_SECRET --app dependabot # dependabot
142
+ ```
143
+
144
+ Scope matrix:
145
+
146
+ | Flag combo | Scope | Token scope |
147
+ |------------|-------|-------------|
148
+ | (none) | Repository | `repo` |
149
+ | `--env <name>` | Repository environment (env must exist) | `repo` |
150
+ | `--org <name> --visibility all`/`private` | Org-wide | `admin:org` |
151
+ | `--org <name> --visibility selected --repos a,b` | Org allow-list | `admin:org` |
152
+ | `--app actions` (default) / `dependabot` / `codespaces` | Per-application surface | as above |
153
+
154
+ Input precedence: `--body <value>` > stdin > interactive prompt. Prefer stdin to keep values out of process listings and shell history.
155
+
156
+ Common errors:
157
+
158
+ - `HTTP 404` with `--env` — environment must exist (`gh api repos/{o}/{r}/environments/{name} --method PUT`).
159
+ - `HTTP 422: visibility 'selected' requires --repos` — supply allow-list.
160
+ - `HTTP 403` against `--org` — `gh auth refresh --scopes admin:org`.
161
+
162
+ ## 4. Workflow Runs
163
+
164
+ ### `gh run list`
165
+
166
+ ```bash
167
+ gh run list --workflow ci.yml --branch "$BRANCH" --limit 10 \
168
+ --json databaseId,status,conclusion,headSha,event,createdAt,workflowName,url \
169
+ --jq '.[] | select(.conclusion != "success")'
170
+ ```
171
+
172
+ Flags: `--workflow` (filename/ID/name), `--branch`, `--user`, `--status`, `--event`, `--limit` (default 20), `--created`, `--json`, `-R/--repo`.
173
+
174
+ JSON fields: `attempt`, `conclusion`, `createdAt`, `databaseId`, `displayTitle`, `event`, `headBranch`, `headSha`, `name`, `number`, `startedAt`, `status`, `updatedAt`, `url`, `workflowDatabaseId`, `workflowName`.
175
+
176
+ `status`: `queued`, `in_progress`, `completed`, `requested`, `waiting`. `conclusion` (when `status=completed`): `success`, `failure`, `cancelled`, `skipped`, `timed_out`, `action_required`, `neutral`, `stale`.
177
+
178
+ ### `gh run watch`
179
+
180
+ ```bash
181
+ gh run watch "$RUN_ID" --exit-status --interval 5 --compact
182
+ ```
183
+
184
+ | Flag | Purpose |
185
+ |------|---------|
186
+ | `--exit-status` | Non-zero on failure. Required for `set -e`. |
187
+ | `--interval <sec>` (`-i`) | Poll interval (default 3). Bump to 5-10 for long runs. |
188
+ | `--compact` | Show only failed/relevant steps. |
189
+ | `-R/--repo` | Override repo. |
190
+
191
+ Never omit `RUN_ID` in `/cbp-ship` — without it the command prompts (no TTY).
192
+
193
+ ### `gh run view`
194
+
195
+ ```bash
196
+ gh run view "$RUN_ID" --json status,conclusion,jobs,workflowName,url --jq . # summary
197
+ gh run view "$RUN_ID" --log-failed # failed-step logs
198
+ gh run view --job "$JOB_ID" --log # one job
199
+ gh run view "$RUN_ID" --exit-status # script-friendly
200
+ ```
201
+
202
+ JSON fields: same as `run list` plus `jobs`. `jobs[].{databaseId,name,status,conclusion,steps[].{name,conclusion,number}}` — canonical drill-down from a failed run to the responsible step.
203
+
204
+ Common errors:
205
+
206
+ - `could not find any workflow run with ID <x>` — wrong repo or expired; verify with `gh run list -R <repo>`.
207
+ - `--log-failed` empty while in progress — pair with `gh run watch` first.
208
+ - Logs older than 90 days unavailable (GitHub-side retention).
209
+
210
+ ## 5. Deployments API (Non-Vercel)
211
+
212
+ For non-Vercel deploy targets, `/cbp-ship` records deploys via the GitHub Deployments API using `gh api`. There is no `gh deployment` subcommand.
213
+
214
+ ### Create a deployment
215
+
216
+ ```bash
217
+ gh api "repos/$OWNER/$REPO/deployments" --method POST \
218
+ -f ref="$COMMIT_SHA" -f environment=production -f description="ship: $TAG" \
219
+ -F auto_merge=false -f required_contexts='[]'
220
+ ```
221
+
222
+ - `-f key=value` — string field.
223
+ - `-F key=value` — typed field (`true`/`false`/`null`/integer auto-converted).
224
+ - `required_contexts='[]'` — empty JSON array as a string to skip status-check gating.
225
+ - `--input <file>` — for complex payloads, pass JSON from file.
226
+
227
+ Response (excerpt):
228
+
229
+ ```json
230
+ {
231
+ "id": 12345678,
232
+ "url": "https://api.github.com/repos/.../deployments/12345678",
233
+ "sha": "abc123...",
234
+ "ref": "main",
235
+ "environment": "production",
236
+ "statuses_url": "https://api.github.com/repos/.../deployments/12345678/statuses"
237
+ }
238
+ ```
239
+
240
+ Capture `id` for status follow-ups.
241
+
242
+ ### Update deployment status
243
+
244
+ ```bash
245
+ gh api "repos/$OWNER/$REPO/deployments/$DEPLOYMENT_ID/statuses" --method POST \
246
+ -f state=success -f environment_url="https://app.example.com" \
247
+ -f log_url="https://github.com/.../actions/runs/$RUN_ID" -f description="Live"
248
+ ```
249
+
250
+ `state` values: `error`, `failure`, `inactive`, `in_progress`, `queued`, `pending`, `success`. Only `success` and `inactive` are terminal in the UI.
251
+
252
+ ### List deployments
253
+
254
+ ```bash
255
+ gh api "repos/$OWNER/$REPO/deployments?environment=production&per_page=10" \
256
+ --jq '.[] | {id, sha, created_at, ref}'
257
+ ```
258
+
259
+ Combine `--paginate` for full history, but bound it — Deployments grow unbounded.
260
+
261
+ Common errors:
262
+
263
+ - `HTTP 422: required_contexts is invalid` — pass `'[]'` literal with `-f`.
264
+ - `HTTP 422: Conflict merging <ref>` — `auto_merge` defaulted to true; pass `-F auto_merge=false`.
265
+ - `HTTP 404` — ref does not exist remotely, or token lacks `repo_deployment` (subset of `repo`).
266
+
267
+ ## Quick-Reference: What `/cbp-ship` Calls
268
+
269
+ | Phase | Command |
270
+ |-------|---------|
271
+ | Open PR | `gh pr create --base ... --head ... --title ... --body-file ... --draft` |
272
+ | Wait for checks | `gh run watch $RUN_ID --exit-status --interval 5` |
273
+ | Inspect failure | `gh run view $RUN_ID --log-failed` |
274
+ | Pre-merge gate | `gh pr view $PR --json mergeable,mergeStateStatus,reviewDecision,headRefOid` |
275
+ | Merge | `gh pr merge $PR --merge --delete-branch --match-head-commit $OID` |
276
+ | Cut release | `gh release create $TAG --title ... --notes-file ... --target $SHA --latest` |
277
+ | Record deploy (non-Vercel) | `gh api repos/.../deployments --method POST -f ref=$SHA -f environment=production` |
278
+
279
+ ## Pairs With
280
+
281
+ - [gh-cli-overview.md](./gh-cli-overview.md) — install, auth, config locations
282
+ - `/cbp-ship` skill — orchestrator that sequences these calls
283
+ - `.claude/rules/git-workflow.md` — branch/commit conventions enforced before any of these commands run
@@ -0,0 +1,252 @@
1
+ # Monorepo Publishing — pnpm Workspaces
2
+
3
+ Publishing one or more packages from a pnpm workspace. Workspace layout, `pnpm publish` vs `npm publish`, per-package `publishConfig`, version-management tools (changesets vs release-please), public/private separation, pre-publish gates.
4
+
5
+ ## Workspace Layout
6
+
7
+ A typical pnpm monorepo with mixed published and internal packages:
8
+
9
+ ```
10
+ repo/
11
+ ├── pnpm-workspace.yaml
12
+ ├── package.json # private: true; root tooling only
13
+ ├── packages/
14
+ │ ├── published/
15
+ │ │ ├── cli/ # @codebyplan/cli — published
16
+ │ │ ├── design-tokens/
17
+ │ │ └── auth/
18
+ │ └── internal/
19
+ │ ├── eslint-config/ # @codebyplan/eslint-config — private
20
+ │ └── tsconfig/
21
+ └── apps/
22
+ ├── web/ # not published
23
+ └── backend/
24
+ ```
25
+
26
+ `pnpm-workspace.yaml`:
27
+
28
+ ```yaml
29
+ packages:
30
+ - "packages/*/*"
31
+ - "apps/*"
32
+ ```
33
+
34
+ `packages/published/*` vs `packages/internal/*` is **organisational**. What keeps internal packages off the registry is `"private": true` in their `package.json` — pnpm refuses to publish private packages and skips them silently in `pnpm -r publish`.
35
+
36
+ ## Root `package.json`
37
+
38
+ Always private at the root — never accidentally publish the workspace meta-package:
39
+
40
+ ```json
41
+ {
42
+ "name": "codebyplan-monorepo",
43
+ "private": true,
44
+ "scripts": {
45
+ "build": "turbo run build",
46
+ "test": "turbo run test",
47
+ "lint:packages": "turbo run lint:package",
48
+ "release": "changeset publish"
49
+ }
50
+ }
51
+ ```
52
+
53
+ ## Per-Package `package.json`
54
+
55
+ A published workspace package needs `publishConfig`:
56
+
57
+ ```json
58
+ {
59
+ "name": "@codebyplan/cli",
60
+ "version": "1.4.2",
61
+ "type": "module",
62
+ "exports": {
63
+ ".": {
64
+ "types": "./dist/index.d.ts",
65
+ "default": "./dist/index.js"
66
+ },
67
+ "./package.json": "./package.json"
68
+ },
69
+ "bin": { "codebyplan": "./dist/cli.js" },
70
+ "files": ["dist", "README.md"],
71
+ "engines": { "node": ">=20" },
72
+ "publishConfig": {
73
+ "access": "public",
74
+ "provenance": true
75
+ },
76
+ "repository": {
77
+ "type": "git",
78
+ "url": "git+https://github.com/codebyplan/codebyplan.git",
79
+ "directory": "packages/published/cli"
80
+ }
81
+ }
82
+ ```
83
+
84
+ Notes:
85
+
86
+ - `repository.directory` tells the registry which subfolder this package lives in. Required for provenance attestations to validate.
87
+ - `publishConfig.access: "public"` is required for scoped packages (`@codebyplan/*`). Without it the first publish fails with `402 Payment Required`.
88
+ - `publishConfig.provenance: true` is the package.json equivalent of `--provenance` on the CLI.
89
+
90
+ An internal package just sets `"private": true`:
91
+
92
+ ```json
93
+ {
94
+ "name": "@codebyplan/eslint-config",
95
+ "version": "0.0.0",
96
+ "private": true,
97
+ "main": "./index.js"
98
+ }
99
+ ```
100
+
101
+ ## `pnpm publish` vs `npm publish`
102
+
103
+ | Capability | `pnpm publish` | `npm publish` |
104
+ |------------|----------------|----------------|
105
+ | Recursive (`-r`) — publish all changed packages | Yes | No |
106
+ | `--filter` to scope to a subset | Yes | No |
107
+ | Auto-includes workspace root `LICENSE` | Yes | No |
108
+ | Rewrites `workspace:*` deps to actual versions in tarball | Yes — automatic | No |
109
+ | `--no-git-checks` | Yes | No |
110
+ | OIDC trusted publishing | Yes (npm CLI underneath) | Yes |
111
+
112
+ For a pnpm monorepo, **always use `pnpm publish`** — `npm publish` will leak `workspace:*` protocol strings into the published tarball and consumers will get install errors.
113
+
114
+ `pnpm publish -r` only publishes packages whose `version` is not already on the registry — safe to run repeatedly.
115
+
116
+ ## Version Management — Changesets vs release-please
117
+
118
+ Two dominant tools. Both work with pnpm + GitHub Actions OIDC.
119
+
120
+ ### Changesets
121
+
122
+ Maintainer-friendly — every PR adds a `.changeset/*.md` file declaring intent:
123
+
124
+ ```
125
+ .changeset/funky-tigers-dance.md
126
+ ---
127
+ "@codebyplan/cli": minor
128
+ "@codebyplan/auth": patch
129
+ ---
130
+
131
+ Added --provenance flag to publish command. Auth payload now includes scope.
132
+ ```
133
+
134
+ Workflow:
135
+
136
+ 1. `pnpm changeset` while authoring a PR. Pick affected packages and bump types.
137
+ 2. PR merges to main. Changesets GitHub Action opens (or updates) a "Version Packages" PR that consumes pending changesets, bumps versions, regenerates CHANGELOGs.
138
+ 3. Merging the Version Packages PR triggers `pnpm changeset publish` → calls `pnpm publish -r` for every bumped package.
139
+
140
+ `.changeset/config.json` essentials:
141
+
142
+ ```json
143
+ {
144
+ "changelog": "@changesets/cli/changelog",
145
+ "commit": false,
146
+ "fixed": [],
147
+ "linked": [],
148
+ "access": "public",
149
+ "baseBranch": "main",
150
+ "updateInternalDependencies": "patch",
151
+ "ignore": ["@codebyplan/web", "@codebyplan/backend"]
152
+ }
153
+ ```
154
+
155
+ `ignore` excludes apps that are deployed but never published.
156
+
157
+ Strengths: explicit intent per change; supports linked/fixed package groups; rich CHANGELOG output. Weaknesses: every PR must remember to add a changeset (the bot enforces this).
158
+
159
+ ### release-please
160
+
161
+ Conventional-commit-driven — version is inferred from commit messages (`feat:` → minor, `fix:` → patch, `feat!:` or `BREAKING CHANGE:` → major):
162
+
163
+ 1. Developer writes conventional-commit messages.
164
+ 2. release-please GitHub Action watches main, opens a release PR per package summarising commits since the last tag.
165
+ 3. Merging the release PR creates a git tag, GitHub release, and triggers a separate publish workflow.
166
+
167
+ `release-please-config.json`:
168
+
169
+ ```json
170
+ {
171
+ "packages": {
172
+ "packages/published/cli": { "release-type": "node" },
173
+ "packages/published/auth": { "release-type": "node" }
174
+ },
175
+ "plugins": ["node-workspace"]
176
+ }
177
+ ```
178
+
179
+ The `node-workspace` plugin keeps `workspace:*` deps in sync across the bump.
180
+
181
+ Strengths: zero per-PR ceremony when commit hygiene is enforced. Weaknesses: monorepo config is more involved; granular bump control is harder.
182
+
183
+ ### Picking one
184
+
185
+ - Conventional commits enforced → release-please.
186
+ - Want explicit per-PR intent and rich CHANGELOGs → Changesets.
187
+ - OSS / mixed contributors → Changesets (the bot reminds people).
188
+
189
+ CodeByPlan uses Changesets for `packages/published/*`.
190
+
191
+ ## Pre-Publish Gates
192
+
193
+ Run these in CI before `pnpm publish -r`:
194
+
195
+ | # | Gate | Command |
196
+ |---|------|---------|
197
+ | 1 | Build | `pnpm -r --filter "./packages/published/*" build` |
198
+ | 2 | Test | `pnpm -r --filter "./packages/published/*" test` |
199
+ | 3 | publint | `pnpm -r --filter "./packages/published/*" exec publint` |
200
+ | 4 | attw | `pnpm -r --filter "./packages/published/*" exec attw --pack .` |
201
+ | 5 | Dry-run pack | `pnpm -r --filter "./packages/published/*" publish --dry-run` |
202
+
203
+ [publint](https://publint.dev) catches missing `exports`, broken `main` pointers, type-definition exclusion, ESM/CJS misalignment. [Are The Types Wrong](https://arethetypeswrong.github.io/) validates that the published tarball resolves correctly under both CJS and ESM consumers across conditional-export combinations.
204
+
205
+ The dry-run output lists every file that would be in the tarball plus total size. Compare against `files` in `package.json` — anything outside `files` means the glob is wrong (or `.npmignore` is interfering).
206
+
207
+ `pnpm publish -r` skips already-published versions automatically, but for clarity:
208
+
209
+ ```bash
210
+ pnpm -r --filter "./packages/published/*" exec sh -c \
211
+ 'npm view "$npm_package_name@$npm_package_version" version 2>/dev/null \
212
+ && echo "ALREADY PUBLISHED" && exit 1 || true'
213
+ ```
214
+
215
+ Wire gates 3-4 as a turbo task in each package's `package.json`:
216
+
217
+ ```json
218
+ {
219
+ "scripts": {
220
+ "lint:package": "publint && attw --pack ."
221
+ }
222
+ }
223
+ ```
224
+
225
+ Then in CI before publish: `pnpm turbo run lint:package`.
226
+
227
+ ## Public / Private Separation Recap
228
+
229
+ | Layer | Mechanism | Purpose |
230
+ |-------|-----------|---------|
231
+ | Folder convention | `packages/published/*` vs `packages/internal/*` | Human signal |
232
+ | `private: true` | `package.json` per internal package | Hard block — pnpm refuses to publish |
233
+ | Changesets `ignore` | `.changeset/config.json` | Keeps deployed apps off the version PR |
234
+ | Workspace dep protocol | `"@codebyplan/foo": "workspace:*"` | Internal deps stay internal; pnpm rewrites at publish time |
235
+
236
+ When an internal package is published by mistake:
237
+
238
+ 1. `npm unpublish @scope/pkg@x.y.z` within 72 hours, OR
239
+ 2. `npm deprecate @scope/pkg@"<= x.y.z" "Internal package — do not use"` after that.
240
+
241
+ Then add `"private": true` and ship.
242
+
243
+ ## Sources
244
+
245
+ - [pnpm — publish](https://pnpm.io/cli/publish)
246
+ - [pnpm — workspaces](https://pnpm.io/workspaces)
247
+ - [npm Docs — package.json](https://docs.npmjs.com/cli/v10/configuring-npm/package-json)
248
+ - [Changesets](https://github.com/changesets/changesets)
249
+ - [release-please](https://github.com/googleapis/release-please)
250
+ - [publint](https://publint.dev/)
251
+ - [Are The Types Wrong](https://arethetypeswrong.github.io/)
252
+ - [npm CLI source](https://github.com/npm/cli)
@@ -0,0 +1,157 @@
1
+ # npm OIDC Trusted Publishing
2
+
3
+ Modern publish flow: no NPM_TOKEN, no leak risk. npm trusts a short-lived OIDC token issued by your CI provider. Provenance attestations are generated automatically and recorded in a public transparency ledger (Sigstore).
4
+
5
+ This is the **default publish path for `/cbp-ship`** when the npm-package surface is in play — it's the publish path the orchestrator should reach for first.
6
+
7
+ ## How It Works
8
+
9
+ 1. CI job starts. The CI provider (GitHub Actions, GitLab CI, CircleCI) mints a short-lived OIDC ID token signed with its public key.
10
+ 2. `npm publish` reads the OIDC token from the runner and exchanges it with the npm registry for a one-shot publish credential.
11
+ 3. npm validates the OIDC token's claims — `repository`, `workflow`, `ref`, `aud` — against the trusted-publisher rule registered for the package.
12
+ 4. If the claims match, the publish is accepted. If not, the publish is rejected with `403`.
13
+ 5. (GitHub Actions / GitLab only) npm automatically generates a Sigstore provenance attestation linking the published tarball to the exact commit, workflow file, and runner image that built it. The attestation is logged to the [Rekor transparency ledger](https://search.sigstore.dev/).
14
+
15
+ The OIDC token's lifetime is measured in minutes; it is bound to the workflow run; it cannot be replayed. There is nothing for an attacker to exfiltrate from the runner that survives the job.
16
+
17
+ ## Supported Providers
18
+
19
+ | Provider | OIDC | Auto-provenance |
20
+ |----------|------|-----------------|
21
+ | GitHub Actions (GitHub-hosted runners) | Yes | Yes |
22
+ | GitLab CI/CD (GitLab.com shared runners) | Yes | Yes |
23
+ | CircleCI cloud | Yes | No (must pass `--provenance` manually with extra setup) |
24
+ | Self-hosted runners (any provider) | Not supported | Not supported |
25
+
26
+ Self-hosted runners cannot use trusted publishing today — the npm registry does not yet validate signing certificates from non-cloud OIDC issuers.
27
+
28
+ ## Setup — GitHub Actions
29
+
30
+ ### Step 1 — Register the trusted publisher on npmjs.com
31
+
32
+ 1. Sign in at https://www.npmjs.com.
33
+ 2. Navigate to your package: `npmjs.com/package/<your-pkg>`.
34
+ 3. Settings → **Trusted Publishers** → **Add publisher**.
35
+ 4. Select **GitHub Actions**, then fill in:
36
+ - **Organization or user**: e.g. `codebyplan`
37
+ - **Repository**: e.g. `codebyplan`
38
+ - **Workflow filename**: e.g. `release.yml` (just the filename, not the full path)
39
+ - **Environment** (optional): e.g. `production` — restricts to runs scoped to a GitHub Actions environment
40
+
41
+ If the package does not yet exist on the registry, you must publish it once with a classic token before you can register a trusted publisher (npm requires the package to exist to attach a trust rule).
42
+
43
+ ### Step 2 — Update the workflow
44
+
45
+ Add `id-token: write` to the job's permissions and use a recent npm CLI (≥9.5.0; npm 10+ is recommended).
46
+
47
+ ```yaml
48
+ name: Release
49
+
50
+ on:
51
+ push:
52
+ tags:
53
+ - "v*"
54
+
55
+ jobs:
56
+ publish:
57
+ runs-on: ubuntu-latest
58
+
59
+ permissions:
60
+ contents: read # checkout
61
+ id-token: write # OIDC — required for trusted publishing
62
+ attestations: write # required for provenance attestations
63
+
64
+ steps:
65
+ - uses: actions/checkout@v4
66
+
67
+ - uses: actions/setup-node@v4
68
+ with:
69
+ node-version: "20"
70
+ registry-url: "https://registry.npmjs.org"
71
+
72
+ - uses: pnpm/action-setup@v4
73
+ with:
74
+ version: 10
75
+
76
+ - run: pnpm install --frozen-lockfile
77
+
78
+ - run: pnpm build
79
+
80
+ - run: pnpm test
81
+
82
+ - name: Verify publish (dry run)
83
+ run: pnpm publish --dry-run --access public
84
+
85
+ - name: Lint package shape
86
+ run: |
87
+ pnpm dlx publint
88
+ pnpm dlx @arethetypeswrong/cli --pack
89
+
90
+ - name: Publish to npm
91
+ run: pnpm publish --access public --provenance --no-git-checks
92
+ ```
93
+
94
+ Notes:
95
+
96
+ - **No `NODE_AUTH_TOKEN` env var.** That's the entire point — there is no token to leak. The `setup-node` action wires up `.npmrc` to the registry URL; OIDC handshake happens inside `npm publish`.
97
+ - `--provenance` is **automatic** on GitHub Actions OIDC publishes (npm enables it implicitly), but passing the flag is harmless and makes intent explicit. `publishConfig.provenance: true` in `package.json` is the third equivalent.
98
+ - `--no-git-checks` (pnpm-only) bypasses the "current branch must be the publish branch, working tree must be clean" check. Safe in CI where the runner has a fresh clone at a tag.
99
+
100
+ ## Provenance Attestations
101
+
102
+ A provenance attestation is a JSON document signed by Sigstore that describes:
103
+
104
+ - The source repository URL and the exact commit SHA that was built.
105
+ - The workflow file path and the run ID.
106
+ - The builder identity (e.g. `https://github.com/actions/runner`).
107
+ - The npm package name, version, and tarball SHA.
108
+
109
+ After publish, the npm registry shows a "Provenance" badge on the package page linking to the Sigstore record.
110
+
111
+ ### Verifying attestations as a consumer
112
+
113
+ ```bash
114
+ npm audit signatures
115
+ ```
116
+
117
+ This walks every dependency in the current project, checks the registry signature on each tarball, and validates any provenance attestations. A failure means either the package was tampered with after publish or its signing identity changed unexpectedly.
118
+
119
+ For a single package without installing it:
120
+
121
+ ```bash
122
+ npm view <pkg> --json | jq '.dist.attestations'
123
+ ```
124
+
125
+ Returns the URL of the attestation bundle on the registry; the bundle is a Sigstore-format JSON that can be verified with `cosign` or the `sigstore-js` library.
126
+
127
+ ## Comparison vs Classic NPM_TOKEN
128
+
129
+ | Aspect | Classic NPM_TOKEN | OIDC Trusted Publishing |
130
+ |--------|-------------------|--------------------------|
131
+ | Secret stored in CI | Yes — long-lived token | No |
132
+ | Rotation burden | Manual; forgotten tokens are common | None — every publish mints a fresh credential |
133
+ | Leak blast radius | Token works from anywhere until revoked | Credential expires in minutes; bound to one workflow run |
134
+ | Provenance | Manual `--provenance` + extra setup | Automatic on GitHub Actions / GitLab |
135
+ | Setup complexity | Generate token, paste into CI secrets | One-time npm web UI registration |
136
+ | Works on self-hosted runners | Yes | No (today) |
137
+ | Works locally | Yes | No (no OIDC issuer) |
138
+
139
+ Classic tokens still have a place: local release machines, self-hosted runners, providers npm doesn't trust yet. For everything else, OIDC is the default in 2026.
140
+
141
+ ## Failure Modes
142
+
143
+ | Symptom | Cause | Fix |
144
+ |---------|-------|-----|
145
+ | `403 Forbidden — OIDC publish requires id-token: write` | Workflow missing the permission | Add `permissions.id-token: write` to the job |
146
+ | `403 — trusted publisher mismatch` | Workflow filename, repo, or org doesn't match the rule | Re-check the trusted-publisher rule on npmjs.com matches `${{ github.repository }}` and `${{ github.workflow_ref }}` exactly |
147
+ | `Provenance generation failed: repository field mismatch` | `package.json` `repository.url` doesn't match the GitHub repo URL | Set `"repository": { "type": "git", "url": "git+https://github.com/<org>/<repo>.git" }` (case-sensitive) |
148
+ | Attestations missing on the registry page | Used CircleCI without manual provenance setup | Either accept (CircleCI limitation) or migrate to GitHub Actions / GitLab |
149
+ | `OIDC token unavailable` from a self-hosted runner | Trusted publishing not supported there | Fall back to a granular token, or move the publish job to a GitHub-hosted runner |
150
+
151
+ ## Sources
152
+
153
+ - [npm Docs — Trusted publishers](https://docs.npmjs.com/trusted-publishers)
154
+ - [npm Docs — Generating provenance statements](https://docs.npmjs.com/generating-provenance-statements)
155
+ - [npm Docs — npm audit signatures](https://docs.npmjs.com/cli/v10/commands/npm-audit)
156
+ - [Sigstore](https://www.sigstore.dev/) — signing infrastructure
157
+ - [npm CLI source](https://github.com/npm/cli)