@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.
- package/README.md +4 -1
- package/agents/developer.md +18 -0
- package/agents/scrum-master.md +1 -0
- package/agents/tester.md +18 -0
- package/hooks/on_session_end.sh +27 -0
- package/lib/commands/init.js +23 -68
- package/lib/generate-copilot-instructions.js +142 -0
- package/package.json +18 -17
- package/scripts/postinstall.js +21 -0
- package/skills/commit/SKILL.md +11 -1
- package/skills/dev-done/SKILL.md +46 -0
- package/skills/dev-done/scripts/classify-commit-outcome.sh +114 -0
- package/skills/init/SKILL.md +7 -6
- package/skills/init/assets/scope-thresholds_template.json +7 -0
- package/skills/init/scripts/init.sh +6 -0
- package/skills/publish/SKILL.md +66 -0
- package/skills/publish/adapters/npm-ci.md +34 -0
- package/skills/publish/adapters/npm.md +18 -0
- package/skills/publish/assets/ci-contract.md +27 -0
- package/skills/publish/assets/publish.example.json +27 -0
- package/skills/publish/schemas/publish.schema.json +20 -0
- package/skills/publish/scripts/npm_ci_pipeline.sh +29 -0
- package/skills/publish/scripts/npm_stage_inspect.sh +829 -0
- package/skills/publish/scripts/npm_stage_pipeline.sh +427 -0
- package/skills/publish/scripts/publish_common.sh +16 -0
- package/skills/publish/scripts/show_history.sh +12 -5
- package/skills/publish/scripts/validate_npm_stage_env.sh +184 -0
- package/skills/publish/scripts/write_ledger_entry.sh +92 -2
- package/skills/reconcile/SKILL.md +121 -11
- package/skills/reconcile/assets/report_format.md +17 -0
- package/skills/reconcile/scripts/resolve-reconcile-scope.sh +489 -0
- package/skills/uncharted/SKILL.md +200 -21
- package/skills/uncharted/scripts/directory-triage.sh +342 -0
- package/skills/uncharted/scripts/elicitation-state.sh +457 -0
- package/templates/SCRUM_BOARD_SCHEMA.md +57 -0
- package/templates/agent-context.md.tpl +23 -11
- package/templates/copilot-instructions.md.tpl +21 -11
- package/mcp/router/README.md +0 -19
- package/mcp/router/embedder.js +0 -23
- package/mcp/router/index.js +0 -204
- package/mcp/router/matcher.js +0 -87
- package/mcp/router/package-lock.json +0 -1048
- package/mcp/router/package.json +0 -11
- package/mcp/router/skill-index.js +0 -104
- 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
|
package/skills/init/SKILL.md
CHANGED
|
@@ -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/
|
|
132
|
-
8. Creates `project/
|
|
133
|
-
9. Creates `
|
|
134
|
-
10. Creates `
|
|
135
|
-
11.
|
|
136
|
-
12.
|
|
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
|
|
@@ -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
|
package/skills/publish/SKILL.md
CHANGED
|
@@ -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
|
|