@jenga-ai/agent 1.2.3 → 1.3.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 (45) hide show
  1. package/README.md +4 -1
  2. package/agents/developer.md +18 -0
  3. package/agents/scrum-master.md +1 -0
  4. package/agents/tester.md +18 -0
  5. package/hooks/on_session_end.sh +27 -0
  6. package/lib/commands/init.js +23 -68
  7. package/lib/generate-copilot-instructions.js +142 -0
  8. package/package.json +18 -17
  9. package/scripts/postinstall.js +21 -0
  10. package/skills/commit/SKILL.md +11 -1
  11. package/skills/dev-done/SKILL.md +46 -0
  12. package/skills/dev-done/scripts/classify-commit-outcome.sh +114 -0
  13. package/skills/init/SKILL.md +7 -6
  14. package/skills/init/assets/scope-thresholds_template.json +7 -0
  15. package/skills/init/scripts/init.sh +6 -0
  16. package/skills/publish/SKILL.md +66 -0
  17. package/skills/publish/adapters/npm-ci.md +34 -0
  18. package/skills/publish/adapters/npm.md +18 -0
  19. package/skills/publish/assets/ci-contract.md +27 -0
  20. package/skills/publish/assets/publish.example.json +27 -0
  21. package/skills/publish/schemas/publish.schema.json +20 -0
  22. package/skills/publish/scripts/npm_ci_pipeline.sh +29 -0
  23. package/skills/publish/scripts/npm_stage_inspect.sh +829 -0
  24. package/skills/publish/scripts/npm_stage_pipeline.sh +427 -0
  25. package/skills/publish/scripts/publish_common.sh +16 -0
  26. package/skills/publish/scripts/show_history.sh +12 -5
  27. package/skills/publish/scripts/validate_npm_stage_env.sh +184 -0
  28. package/skills/publish/scripts/write_ledger_entry.sh +92 -2
  29. package/skills/reconcile/SKILL.md +121 -11
  30. package/skills/reconcile/assets/report_format.md +17 -0
  31. package/skills/reconcile/scripts/resolve-reconcile-scope.sh +489 -0
  32. package/skills/uncharted/SKILL.md +200 -21
  33. package/skills/uncharted/scripts/directory-triage.sh +342 -0
  34. package/skills/uncharted/scripts/elicitation-state.sh +457 -0
  35. package/templates/SCRUM_BOARD_SCHEMA.md +57 -0
  36. package/templates/agent-context.md.tpl +23 -11
  37. package/templates/copilot-instructions.md.tpl +21 -11
  38. package/mcp/router/README.md +0 -19
  39. package/mcp/router/embedder.js +0 -23
  40. package/mcp/router/index.js +0 -204
  41. package/mcp/router/matcher.js +0 -87
  42. package/mcp/router/package-lock.json +0 -1048
  43. package/mcp/router/package.json +0 -11
  44. package/mcp/router/skill-index.js +0 -104
  45. package/skills/route/SKILL.md +0 -180
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env bash
2
+ # ---------------------------------------------------------------------------
3
+ # skills/dev-done/scripts/classify-commit-outcome.sh
4
+ #
5
+ # Deterministic classifier backing `/dev-done` (story E42_S04, task
6
+ # E42_S04_T01). `/dev-done` chains `/commit <scope-id>` then `/self-sync`,
7
+ # but must NOT proceed to `/self-sync` when `/commit` halted early because
8
+ # there was nothing to commit. Per CLAUDE.md's "Skill Implementation
9
+ # Principle — Scripts Over Inline Logic", that halt-vs-proceed check is a
10
+ # deterministic, repeatable text match — it does not belong as inline
11
+ # conditional logic in `skills/dev-done/SKILL.md`, so it lives here instead,
12
+ # the same way `skills/self-sync/SKILL.md` delegates its own filesystem work
13
+ # to `skills/self-sync/scripts/run.js` rather than inlining it.
14
+ #
15
+ # `/commit` and `/self-sync` are themselves agent-executed skills, not plain
16
+ # executables — this script never shells out to invoke either one. It only
17
+ # classifies text that the calling agent already captured from `/commit`'s
18
+ # output. Invoking `/commit` and `/self-sync` remains `SKILL.md`'s job.
19
+ #
20
+ # ---------------------------------------------------------------------------
21
+ # WHAT IT MATCHES
22
+ # ---------------------------------------------------------------------------
23
+ # `skills/commit/SKILL.md`'s Instructions section states, verbatim:
24
+ #
25
+ # "If no epic, task, or story has been implemented, exit with the message:
26
+ # "No implementation to commit.""
27
+ #
28
+ # That exact sentence — "No implementation to commit." — is the ONLY halt
29
+ # signal this script recognizes. If a future edit to `skills/commit/SKILL.md`
30
+ # changes that wording, this script's match must be updated to match, or it
31
+ # will silently stop recognizing the halt case (see Dependencies & Risks in
32
+ # the task's execution plan).
33
+ #
34
+ # ---------------------------------------------------------------------------
35
+ # INVOCATION
36
+ # ---------------------------------------------------------------------------
37
+ # classify-commit-outcome.sh [<captured-commit-output>]
38
+ #
39
+ # The text `/commit` produced is passed either as the single argument, or
40
+ # (when no argument is given) read from stdin in full, e.g.:
41
+ #
42
+ # skills/dev-done/scripts/classify-commit-outcome.sh <<< "$COMMIT_OUTPUT"
43
+ # printf '%s' "$COMMIT_OUTPUT" | skills/dev-done/scripts/classify-commit-outcome.sh
44
+ #
45
+ # ---------------------------------------------------------------------------
46
+ # OUTPUT CONTRACT (stdout, single line, nothing else)
47
+ # ---------------------------------------------------------------------------
48
+ # HALT case stdout is the exact literal message "No implementation to
49
+ # commit." — the calling agent relays this stdout verbatim
50
+ # to the user and does NOT invoke `/self-sync`.
51
+ # PROCEED case stdout is the literal token "PROCEED" — the calling
52
+ # agent invokes `/self-sync` next.
53
+ #
54
+ # ---------------------------------------------------------------------------
55
+ # EXIT CODES
56
+ # ---------------------------------------------------------------------------
57
+ # 0 PROCEED — `/commit` completed normally (regardless of whether it
58
+ # reported drift or doc-sync findings along the way); stdout is
59
+ # "PROCEED".
60
+ # 1 HALT — `/commit` halted early with the exact "No implementation to
61
+ # commit." message; stdout is that exact message.
62
+ # 2 Usage/environment error (e.g. no input at all — neither an argument
63
+ # nor anything on stdin); nothing meaningful on stdout, an error on
64
+ # stderr.
65
+ # ---------------------------------------------------------------------------
66
+
67
+ set -euo pipefail
68
+
69
+ HALT_MESSAGE="No implementation to commit."
70
+
71
+ if [ "$#" -gt 1 ]; then
72
+ echo "Usage: $(basename "$0") [<captured-commit-output>]" >&2
73
+ echo " (or pipe the captured output on stdin with no argument)" >&2
74
+ exit 2
75
+ fi
76
+
77
+ if [ "$#" -eq 1 ]; then
78
+ INPUT="$1"
79
+ else
80
+ # No argument — read the full captured output from stdin.
81
+ if [ -t 0 ]; then
82
+ echo "ERROR: no argument given and stdin is a terminal — nothing to classify" >&2
83
+ exit 2
84
+ fi
85
+ INPUT="$(cat)"
86
+ fi
87
+
88
+ if [ -z "$INPUT" ]; then
89
+ echo "ERROR: no commit output provided to classify" >&2
90
+ exit 2
91
+ fi
92
+
93
+ # Trim leading/trailing whitespace from the whole input for the exact-match case.
94
+ TRIMMED="$(printf '%s' "$INPUT" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
95
+
96
+ if [ "$TRIMMED" = "$HALT_MESSAGE" ]; then
97
+ printf '%s\n' "$HALT_MESSAGE"
98
+ exit 1
99
+ fi
100
+
101
+ # Also match the halt message appearing as its own standalone line anywhere
102
+ # in a longer captured response (e.g. wrapped in surrounding agent prose),
103
+ # since the calling agent's captured "output text" is free-form, not a
104
+ # guaranteed exact-equals string.
105
+ while IFS= read -r line; do
106
+ LINE_TRIMMED="$(printf '%s' "$line" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
107
+ if [ "$LINE_TRIMMED" = "$HALT_MESSAGE" ]; then
108
+ printf '%s\n' "$HALT_MESSAGE"
109
+ exit 1
110
+ fi
111
+ done <<< "$INPUT"
112
+
113
+ printf 'PROCEED\n'
114
+ exit 0
@@ -128,12 +128,13 @@ This script handles all scaffolding in one step:
128
128
  4. Creates `project/PROJECT_SUMMARY.md` with placeholder content
129
129
  5. Creates `project/configs/workflow.json` with shared constants
130
130
  6. Creates `project/configs/test-config.json` stub
131
- 7. Creates `project/data/baselines.json`
132
- 8. Creates `project/logs/events.json`
133
- 9. Creates `docs/STRATEGY.md` — a strategic brief stub intended for investors, partners, and the product team
134
- 10. Creates `CHANGELOG.md` from the shared template — a running log of notable changes, seeded with an `[Unreleased]` section
135
- 11. Applies the chosen visibility mode via `scripts/apply-project-visibility.sh`, which records it as `project_files_visibility` in `jenga.config.json` and performs any `.gitignore` change
136
- 12. Stages and commits all files with the message `init: scaffold project structure and workflow config`
131
+ 7. Creates `project/configs/scope-thresholds.json` with default execution-scope thresholds (consumed by `/jenga` and `/do`, which halt if it's missing)
132
+ 8. Creates `project/data/baselines.json`
133
+ 9. Creates `project/logs/events.json`
134
+ 10. Creates `docs/STRATEGY.md` — a strategic brief stub intended for investors, partners, and the product team
135
+ 11. Creates `CHANGELOG.md` from the shared template — a running log of notable changes, seeded with an `[Unreleased]` section
136
+ 12. Applies the chosen visibility mode via `scripts/apply-project-visibility.sh`, which records it as `project_files_visibility` in `jenga.config.json` and performs any `.gitignore` change
137
+ 13. Stages and commits all files with the message `init: scaffold project structure and workflow config`
137
138
 
138
139
  The visibility mode is validated before any scaffolding happens, so an invalid
139
140
  value fails fast and leaves nothing behind. It is applied before the commit, so
@@ -0,0 +1,7 @@
1
+ {
2
+ "threshold_version": 1,
3
+ "inline_max_files": 1,
4
+ "inline_max_lines": 20,
5
+ "story_max_files": 5,
6
+ "bundle_lock_ttl_minutes": 30
7
+ }
@@ -67,6 +67,12 @@ cp "$ASSETS_DIR/workflow_template.json" project/configs/workflow.json
67
67
  echo "→ Copying test-config.json from template..."
68
68
  cp "$ASSETS_DIR/test-config_template.json" project/configs/test-config.json
69
69
 
70
+ # ─── 6.5. Create project/configs/scope-thresholds.json ───────────────────────
71
+ # Consumed by skills/jenga (Phase 0) and skills/do (Step 0); both halt if it's
72
+ # missing, so it must exist immediately after scaffold.
73
+ echo "→ Copying scope-thresholds.json from template..."
74
+ cp "$ASSETS_DIR/scope-thresholds_template.json" project/configs/scope-thresholds.json
75
+
70
76
  # ─── 7. Create project/data/baselines.json ───────────────────────────────────
71
77
  echo "→ Creating baselines.json..."
72
78
  echo '{}' > project/data/baselines.json
@@ -9,6 +9,8 @@ keywords:
9
9
  - npm
10
10
  - registry
11
11
  - release notes
12
+ - staged publishing
13
+ - stage
12
14
  examples:
13
15
  - "publish setup --target staging-appstore"
14
16
  - "publish setup --type mobile-ios"
@@ -21,6 +23,9 @@ examples:
21
23
  - "publish deploy --target my-droplet --dry-run"
22
24
  - "publish history --limit 5"
23
25
  - "publish release-notes --target staging-appstore"
26
+ - "test a release before publishing"
27
+ - "publish stage --target npm-registry --dry-run"
28
+ - "approve a staged npm release"
24
29
  metadata:
25
30
  scope: multi-target-v2
26
31
  primary_target: multi
@@ -76,6 +81,7 @@ If config or env validation fails, the skill exits with code `4` and does not co
76
81
  |---|---|---|---|
77
82
  | `/publish setup` | Prepare or refresh target configuration | `skills/publish/scripts/setup_wizard.sh` | Supported types: `mobile-ios`, `npm`, `npm-ci`, `droplet` |
78
83
  | `/publish deploy` | Run the full 11-step deploy orchestration | `skills/publish/scripts/publish_deploy.sh` | Dispatches to the adapter for the target's `type` (`mobile-ios`, `npm`, `npm-ci`, or `droplet`); `--dry-run` is honoured end-to-end |
84
+ | `/publish stage` | Stage an npm release into npm's staged-publishing area, smoke-test it in isolation, then approve or reject it | `skills/publish/scripts/npm_stage_pipeline.sh` (the `publish` sub-command) and `skills/publish/scripts/npm_stage_inspect.sh` (`list`, `view`, `download`, `test`, `approve`, `reject`) | Supported for `npm` and `npm-ci` target types only |
79
85
  | `/publish history` | Read the canonical publish ledger | `skills/publish/scripts/show_history.sh` | Target-agnostic; filter by `--target <name>` |
80
86
  | `/publish release-notes` | Merge new release notes into the standing `CHANGELOG.md` (or a standalone draft via `--output`) without publishing | `skills/publish/scripts/generate_release_notes.sh` | Target-agnostic |
81
87
 
@@ -170,6 +176,66 @@ Example invocations:
170
176
 
171
177
  Use `--dry-run` to rehearse a release, validate that gates pass, and confirm the generated release notes without publishing.
172
178
 
179
+ ### `/publish stage`
180
+
181
+ Staged publishing for `npm`/`npm-ci` targets only. Not available for
182
+ `mobile-ios` or `droplet` — those adapters have no staging area to dispatch
183
+ to. The lifecycle is **stage → test → approve**, with `reject` as the
184
+ discard path at any point after staging:
185
+
186
+ ```text
187
+ staged ──▶ stage_tested ──▶ approved (approve requires 2FA; refuses without
188
+ │ │ a passing test unless --force <reason>)
189
+ │ │
190
+ └─────────────┴──▶ rejected (discard path — available from any staged state)
191
+ ```
192
+
193
+ Two registry constraints trip people up and are worth stating plainly:
194
+
195
+ - **The package must already exist on the registry.** npm's staged-publishing
196
+ workflow only applies to a package that has had at least one normal,
197
+ non-staged publish already — `validate_npm_stage_env.sh` checks this
198
+ up front and exits `4` if it doesn't hold.
199
+ - **Approval requires 2FA; staging does not.** `npm stage publish` (the
200
+ `stage` sub-command below) can run non-interactively in CI. `npm stage
201
+ approve` cannot — npmjs.com requires an interactive one-time-password
202
+ step for approval, so it is always a human action even when staging was
203
+ automated (see the npm-ci adapter's OIDC staging section for the CI-stage,
204
+ human-approve split).
205
+
206
+ All seven sub-commands:
207
+
208
+ ```text
209
+ /publish stage publish <target> <path-to-publish.json> [--dry-run] [--non-interactive] [--otp <otp>]
210
+ /publish stage test <stage-id> [--keep] [--config <path>] [--dry-run] [--json]
211
+ /publish stage list [<package-spec>] [--config <path>] [--dry-run] [--json]
212
+ /publish stage view <stage-id> [--config <path>] [--dry-run] [--json]
213
+ /publish stage download <stage-id> [--out <dir>] [--config <path>] [--dry-run] [--json]
214
+ /publish stage approve <stage-id> [--otp <otp>] [--force <reason>] [--config <path>] [--dry-run] [--json]
215
+ /publish stage reject <stage-id> [--config <path>] [--dry-run] [--json]
216
+ ```
217
+
218
+ Implementation:
219
+
220
+ - `publish` → `bash skills/publish/scripts/npm_stage_pipeline.sh <target> <path-to-publish.json> [--dry-run] [--non-interactive] [--otp <otp>]` — six ordered phases (validate, gates, pack, stage, capture, ledger); writes a `staged` ledger entry on success.
221
+ - `test`, `list`, `view`, `download`, `approve`, `reject` → `bash skills/publish/scripts/npm_stage_inspect.sh <sub> [args] [--config <path>] [--dry-run] [--json]`
222
+ - `test` downloads the exact staged tarball into an isolated scratch directory outside the repo, installs it, runs the target's `npm.stage.smoke_cmd` (or the documented default check), and writes a `stage_tested` ledger entry (`pass`/`fail`).
223
+ - `approve` refuses to run unless a passing `test` is on record for that exact stage id, unless `--force <reason>` is given; writes an `approved` ledger entry.
224
+ - `reject` is the discard path omitted from npm's own staged-publishing docs page — documented here so it stays discoverable; writes a `rejected` ledger entry.
225
+
226
+ Example invocations:
227
+
228
+ ```bash
229
+ /publish stage publish npm-registry ./publish.json --dry-run
230
+ /publish stage test <stage-id>
231
+ /publish stage approve <stage-id>
232
+ /publish stage reject <stage-id>
233
+ ```
234
+
235
+ Use `/publish stage` to rehearse and smoke-test a release before it goes
236
+ live — the point of staging is that a bad tarball is caught in isolation
237
+ instead of being caught by users after `npm publish`.
238
+
173
239
  ### `/publish history`
174
240
 
175
241
  ```text
@@ -190,6 +190,40 @@ A successful non-dry-run produces:
190
190
  - A history entry in `project/logs/publish-history.json` written by
191
191
  `publish_deploy.sh` with `platform_state: "triggered"`
192
192
 
193
+ ## Staged Publishing
194
+
195
+ This target type also supports **staged publishing** via `/publish stage` —
196
+ npm's pre-publish staging area — with an OIDC-specific split between the
197
+ automated and human halves of the flow:
198
+
199
+ - **The actual `npm stage publish --provenance` call always runs inside
200
+ GitHub Actions, never on a local machine.** For `npm-ci` targets, phase 4
201
+ of `npm_stage_pipeline.sh` (run locally via `/publish stage publish`)
202
+ dispatches the `stage` job of the target's generated workflow (the same
203
+ `<workflow_path>` / OIDC Trusted Publisher link `npm_ci_pipeline.sh` uses
204
+ for `/publish deploy`, generated with a `mode: publish | stage`
205
+ `workflow_dispatch` input) via
206
+ `gh workflow run <workflow_filename> --repo <github_repo> -f mode=stage`,
207
+ then blocks on `gh run watch` until that run finishes. The `stage` job
208
+ itself runs a plain `npm stage publish --provenance --access <access>
209
+ --tag <dist_tag>` step, authorised through the same Trusted Publisher OIDC
210
+ link described above — no `NPM_TOKEN` is needed to stage, exactly as none
211
+ is needed to publish. Local `--dry-run` only prints the `gh workflow run`
212
+ command that would be dispatched; it never triggers a workflow run.
213
+ - **Approval is always a human, out-of-CI step.** `npm stage approve`
214
+ requires an interactive npm 2FA one-time password — there is no OIDC
215
+ equivalent for approval. A human runs
216
+ `bash skills/publish/scripts/npm_stage_inspect.sh test <stage-id>` (or
217
+ relies on the automated CI-staged test result) and then
218
+ `bash skills/publish/scripts/npm_stage_inspect.sh approve <stage-id> --otp <otp>`
219
+ from their own machine. `reject` (same script) is available to either
220
+ side to discard a staged candidate.
221
+
222
+ Same registry-existence precondition as a normal `npm-ci` deploy: staged
223
+ publishing only applies to a package that has already had at least one
224
+ non-staged publish (`validate_npm_stage_env.sh` checks this up front, exit
225
+ `4` if not).
226
+
193
227
  ## Post-deploy manual steps
194
228
 
195
229
  After a successful deploy trigger, the adapter prints the workflow run URL.
@@ -109,6 +109,24 @@ A successful run produces:
109
109
  - A local (and optionally pushed) git tag `v<version>` matching the published `version`
110
110
  - A history row appended to `project/logs/publish-history.json`
111
111
 
112
+ ## Staged Publishing
113
+
114
+ Before a live `deploy`, this target type also supports **staged publishing**
115
+ via `/publish stage` — npm's own pre-publish staging area, which lets a
116
+ release be smoke-tested from the exact tarball that would ship before it
117
+ becomes visible on the registry. See `skills/publish/SKILL.md`'s
118
+ `### /publish stage` section for the full command reference; summary here:
119
+
120
+ - `bash skills/publish/scripts/npm_stage_pipeline.sh <target> <path-to-publish.json> [--dry-run] [--non-interactive] [--otp <otp>]` runs validate → gates → pack → stage → capture → ledger and writes a `staged` ledger entry.
121
+ - `bash skills/publish/scripts/npm_stage_inspect.sh test <stage-id>` installs the staged tarball into an isolated scratch directory and smoke-tests it, writing a `stage_tested` ledger entry.
122
+ - `bash skills/publish/scripts/npm_stage_inspect.sh approve <stage-id>` requires an npm 2FA one-time password and refuses without a passing `test` on record unless `--force <reason>` is given.
123
+ - `bash skills/publish/scripts/npm_stage_inspect.sh reject <stage-id>` discards the staged version.
124
+
125
+ Same registry-existence precondition as a normal `npm` publish: staged
126
+ publishing only applies to a package that has already had at least one
127
+ non-staged publish (`validate_npm_stage_env.sh` checks this up front, exit
128
+ `4` if not).
129
+
112
130
  ## Post-deploy manual steps
113
131
 
114
132
  After a successful publish, the deploy flow must print:
@@ -71,6 +71,33 @@ These inputs remain optional in non-interactive mode:
71
71
  | `3` | Deploy failure | Used by the iOS adapter pipeline for build/export/upload failures |
72
72
  | `4` | Config or environment invalid | Implemented by validation scripts |
73
73
 
74
+ ## Staged publishing (`/publish stage`) — exit codes and ledger states
75
+
76
+ `/publish stage` (npm/npm-ci only) has its own exit-code contract, separate
77
+ from the deploy contract above:
78
+
79
+ | Code | Meaning |
80
+ |---|---|
81
+ | `0` | Success (staged, or `--dry-run`/`--help` completed cleanly) |
82
+ | `1` | User declined the pack confirmation prompt (or no tty was available) — `npm_stage_pipeline.sh` only |
83
+ | `2` | A mandatory pre-deploy gate failed; nothing was staged — `npm_stage_pipeline.sh` only |
84
+ | `3` | The underlying `npm stage <sub>` command failed, the tarball install failed, or the smoke test failed (`npm_stage_inspect.sh`); or `npm stage publish`/stage-id capture failed (`npm_stage_pipeline.sh`) |
85
+ | `4` | Config/environment validation failure (`validate_npm_stage_env.sh`, or bad args), or (`npm_stage_inspect.sh` only) a usage error, an unknown sub-command, or the `approve` test interlock refusing without a passing test on record and no `--force <reason>` |
86
+
87
+ `test` (via `npm_stage_inspect.sh`) surfaces a smoke-test failure as a
88
+ non-zero exit — code `3` — distinct from a usage error (`4`).
89
+
90
+ The publish ledger (`project/logs/publish-history.json`) gains four new
91
+ `platform_state` values alongside the existing `uploaded`/`partial`/
92
+ `dry-run`/`failed`:
93
+
94
+ | `platform_state` | Written by | Meaning |
95
+ |---|---|---|
96
+ | `staged` | `npm_stage_pipeline.sh` | Version staged to npm's staged-publishing area; not yet visible on the registry |
97
+ | `stage_tested` | `npm_stage_inspect.sh test` | The staged tarball was installed in isolation and smoke-tested; ledger row records `pass`/`fail` |
98
+ | `approved` | `npm_stage_inspect.sh approve` | The staged version was approved (2FA-gated) and is now published |
99
+ | `rejected` | `npm_stage_inspect.sh reject` | The staged version was discarded and will never be published |
100
+
74
101
  ## Precedence rules
75
102
 
76
103
  1. Command-line flags win over environment variables.
@@ -80,6 +80,33 @@
80
80
  "provider_short_name": "exampleco"
81
81
  },
82
82
  "notes": "Production upload example using App Store export mode."
83
+ },
84
+ {
85
+ "name": "npm-ci-example",
86
+ "type": "npm-ci",
87
+ "platform": "npm-ci-oidc",
88
+ "github_repo": "exampleco/example-package",
89
+ "workflow_path": ".github/workflows/npm-publish.yml",
90
+ "checks": {
91
+ "pre": [
92
+ "lint",
93
+ "type-check"
94
+ ],
95
+ "post": [
96
+ "smoke-test"
97
+ ]
98
+ },
99
+ "npm": {
100
+ "package_name": "@exampleco/example-package",
101
+ "access": "public",
102
+ "registry": "https://registry.npmjs.org",
103
+ "dist_tag": "latest",
104
+ "stage": {
105
+ "smoke_cmd": "npm run smoke-test",
106
+ "require_test_before_approve": true
107
+ }
108
+ },
109
+ "notes": "Scaffold example only. Demonstrates the optional npm.stage block used by '/publish stage'. Replace package_name and github_repo with your real values."
83
110
  }
84
111
  ]
85
112
  }
@@ -237,6 +237,26 @@
237
237
  "minLength": 1,
238
238
  "pattern": "^[a-z0-9][a-z0-9._-]*$",
239
239
  "default": "latest"
240
+ },
241
+ "stage": {
242
+ "$ref": "#/definitions/npmStageSettings"
243
+ }
244
+ }
245
+ },
246
+ "npmStageSettings": {
247
+ "description": "Optional settings for `/publish stage` on an npm or npm-ci target. Absent entirely on targets that don't use staged publishing.",
248
+ "type": "object",
249
+ "additionalProperties": false,
250
+ "properties": {
251
+ "smoke_cmd": {
252
+ "description": "Command that `stage test` runs against the installed staged tarball. When absent, `stage test` falls back to a documented default.",
253
+ "type": "string",
254
+ "minLength": 1
255
+ },
256
+ "require_test_before_approve": {
257
+ "description": "When true, `stage approve` refuses without a recorded successful test for that stage id. Defaults to true.",
258
+ "type": "boolean",
259
+ "default": true
240
260
  }
241
261
  }
242
262
  },
@@ -160,6 +160,15 @@ WORKFLOW_YAML="$(cat <<EOF
160
160
  name: "Publish — ${PACKAGE_NAME}"
161
161
  on:
162
162
  workflow_dispatch:
163
+ inputs:
164
+ mode:
165
+ description: "publish (default) or stage — dispatched by npm_stage_pipeline.sh with mode=stage for /publish stage"
166
+ required: false
167
+ default: publish
168
+ type: choice
169
+ options:
170
+ - publish
171
+ - stage
163
172
 
164
173
  permissions:
165
174
  id-token: write
@@ -167,6 +176,7 @@ permissions:
167
176
 
168
177
  jobs:
169
178
  publish:
179
+ if: \${{ github.event.inputs.mode == 'publish' || github.event.inputs.mode == '' }}
170
180
  runs-on: ubuntu-latest
171
181
  steps:
172
182
  - name: Checkout
@@ -183,6 +193,25 @@ jobs:
183
193
 
184
194
  - name: Publish to npm
185
195
  run: npm publish --provenance --access ${NPM_ACCESS} --tag ${DIST_TAG}
196
+
197
+ stage:
198
+ if: \${{ github.event.inputs.mode == 'stage' }}
199
+ runs-on: ubuntu-latest
200
+ steps:
201
+ - name: Checkout
202
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
203
+
204
+ - name: Setup Node.js
205
+ uses: actions/setup-node@v4
206
+ with:
207
+ node-version: lts/*
208
+ registry-url: https://registry.npmjs.org
209
+
210
+ - name: Install dependencies
211
+ run: npm ci
212
+
213
+ - name: Stage to npm
214
+ run: npm stage publish --provenance --access ${NPM_ACCESS} --tag ${DIST_TAG}
186
215
  EOF
187
216
  )"
188
217