Short-lived PR branches merge into main. There is no develop branch or
long-lived release branch. Ordinary merges never publish to npm.
GitHub Actions runs .github/workflows/ci.yml on every PR, including fork PRs
without secrets. publish.yml calls that same reusable CI on every push to main. It detects affected packages;
shared tooling/workflow/release-plan changes validate all packages. Root
documentation-only changes still run tooling and secret checks. The stable
required status is CI passed.
Each affected package runs:
- Lockfile installation with
npm ci --ignore-scripts. - ESLint and TypeScript checking.
- Separate unit and integration suites.
- A Snyk Open Source scan (
--severity-threshold=high --dev) covering production and development dependencies. - An actual
npm pack, content checks, clean-consumer installation, and Pi registration smoke test against the installed artifact. - Upload of the validated tarball and SHA-512 integrity metadata.
scripts/ci.mjs produces exactly one Package / <name> leg per affected
package, so each package reports a single combined check on current Node and Pi.
There is no separate platform or Pi-version compatibility matrix.
These packages intentionally publish TypeScript source, so building means producing and validating the npm artifact, not introducing a transpilation step. The clean consumer omits host peer packages: Pi supplies those. Runtime dependencies are installed normally. The smoke test disables network calls and uses an isolated home and agent directory.
scripts/packages.mjs is the package allowlist and test partition. Every test
belongs to exactly one of the unit/integration suites; an empty suite or missing
listed test fails. Existing suites contain some mixed-granularity files; the
integration lane includes cross-module contracts, registration, filesystem,
process, and package-load checks. New cross-component tests should be listed in
that lane rather than merely renamed to claim integration coverage.
Live search-provider tests are intentionally not release gates.
Use Node 24 and npm 12.0.0 for the main CI baseline:
npm ci --ignore-scripts
npm ci --prefix pi-ask --ignore-scripts
npm run lint --prefix pi-ask
npm run typecheck --prefix pi-ask
npm run test:unit --prefix pi-ask
npm run test:integration --prefix pi-ask
npm audit --prefix pi-ask --audit-level=high # quick local proxy; CI uses Snyk
npm run build --prefix pi-ask
npm run test:toolingRepeat package checks for the other packages. .artifacts/ is ignored by Git.
Prerequisites: this tooling is merged into main, GitHub CLI is authenticated,
and the one-time account setup below is complete. The prepare command uses the
remote main SHA, not uncommitted local changes or the caller's branch.
Preview without creating a PR or publishing:
npm run release:prepare -- \
--package pi-ask --bump patch \
--notes 'Fix question review navigation.' \
--dry-runRemove --dry-run to dispatch the preparation workflow. Release notes are
required; the script never asks an LLM to infer versions or write notes.
For independently versioned packages in one release:
npm run release:prepare -- \
--package pi-ask:patch \
--package pi-slate:minor \
--notes 'Improve interview navigation and add Slate layout options.'The command checks npm's current versions and dispatches
release-prepare.yml with an exact SHA and explicit inputs. Actions regenerates
the plan on that SHA and creates/updates the short-lived release/next PR.
It updates only:
.release/plan.json- selected package manifests and lockfile root versions
- selected changelogs (preserving released entries and moving Unreleased notes)
The same SHA and inputs produce the same release file contents. Review the versions and notes, let CI pass, then merge the PR.
Main must not advance between preparation and merging. If it does, rerun the prepare command to regenerate the PR. This conservative rule avoids publishing changes that were absent from the reviewed release plan.
Patch/minor/major have their usual numeric meaning, including before 1.0:
0.1.2 --bump major becomes 1.0.0. Choose minor explicitly for a breaking
0.x release if that is the package's policy.
A release includes every change already merged into each selected package. It does not cherry-pick individual feature PRs. Unselected packages remain unpublished. Finish or recover an outstanding release before preparing another.
Publication starts only when main's CI jobs succeed, including the Snyk
scan and secret scanning. It requires both a valid generated plan on the release commit and a
merged release/next PR from this repository. A direct push with version edits
is not publication authorization.
publish.yml checks out the exact successful CI SHA and downloads artifacts
from that same run ID, never a PR run or another commit. Validation and
publication share one push event, keeping npm provenance tied to the actual
validated commit even if main advances during the run. It checks package names,
versions, tarball manifests, registry state, and SHA-512 integrity before
publishing. No package install scripts or prepublishOnly hooks run in the
privileged publishing job: the validation already ran in read-only CI.
npm publication uses OIDC and automatic provenance. Each package gets a
<directory>-v<version> Git tag and GitHub release. Publishing is serialized;
a package already published with identical bytes is skipped on retry. A version
with different bytes, an unexpected current registry version, or a conflicting
tag fails closed.
If publishing fails, rerun the failed Main CI and releases run, not the release preparation command. For example:
gh run rerun <publish-run-id> --failedThis reuses the original CI artifacts (retained for 30 days) and completes remaining publications/tags/releases. Do not delete the plan or manually increment versions to hide a partial failure. If artifacts have expired, restore the exact original artifacts before retrying; any rebuilt bytes must match already-published registry integrity. Published versions cannot be overwritten. Fix a bad release with a new patch release, and deprecate the bad version where appropriate.
If a merged release fails validation and needs source changes rather than a
retry, stop its outstanding Actions run first. If none of its versions
have reached npm, revert the release-only commit through a PR (use
git revert -m 1 <sha> for a merge commit, or git revert <sha> for a squash
commit). CI permits only the exact metadata rollback and verifies that no
target version exists on npm. Cancel before changing those metadata files;
then merge the fix and prepare a fresh release. A partially published release
cannot be cancelled this way: finish its original artifacts before releasing
a follow-up patch.
Repository files cannot configure npm account trust or create your GitHub App. Complete these steps before the first release:
- Release GitHub App: create/install an App on only this repository with
repository Contents: read/write and Pull requests: read/write.
Set repository variable
RELEASE_APP_IDand Actions secretRELEASE_APP_PRIVATE_KEY. The short-lived App token lets the generated PR trigger normal PR CI. Do not use a long-lived npm token or put a private key in a repository file. - Environment: a GitHub environment named
npm, restricted to themainbranch, was created during setup. Preserve that restriction. An extra environment approval is optional; merging the release PR is the normal release decision. - npm trusted publishers: for each of
pi-slate,@gagansd/pi-ask,@gagansd/pi-subagents,@gagansd/pi-web-search, and@gagansd/pi-automode, configure:- GitHub owner:
GaganSD - Repository:
coding-agent-toolkit - Workflow filename:
publish.yml(not the full path) - Environment:
npm - Allow direct
npm publish
- GitHub owner:
- Protect main: after the first CI run, require PRs and the CI passed
check, require the branch to be up to date, block force pushes/deletion, and
avoid bypasses. For a solo-maintainer repository, required checks can apply
without a mandatory second person's approval.
CODEOWNERSroutes changes to release/security tooling to the owner; require code-owner review when your reviewer setup supports it. - Push protection: keep GitHub secret scanning and push protection enabled under repository Settings → Security. These were enabled during setup. They protect supported credential formats before GitHub accepts a push; the Actions scanner is not a replacement.
After a GitHub repository rename, update each npm package's trusted publisher to
GaganSD/coding-agent-toolkit before publishing. GitHub URL redirects do not
update npm's external trust configuration. Also verify the release GitHub App's
repository access and any external Snyk integration after the rename.
Snyk runs inside the Package / <name> matrix job in ci.yml, after the
lockfile install, so it reuses that job's checkout and install instead of
adding another runner. There is no separate Snyk workflow.
For each affected package:
snyk test --severity-threshold=high --devfails the job on high or critical advisories. One scan is enough because--devincludes production and development dependencies; Pi packages publish TypeScript source, so dev dependencies stay on the build path. Advisories (exit code 1) fail immediately. Transient Snyk API errors (exit code 2) are retried five times and then skipped with a warning, so an upstream Snyk outage does not fail the build; the separate Snyk PR check still gates advisories.snyk monitorrefreshes the package's snapshot in the Snyk web UI. It runs only onmainpushes (github.event_name == 'push') and is best-effort, so a Snyk outage cannot freeze a release.
The scanner is pinned as snyk@1.1307.4 in the same npm install --global
line as npm@12.0.0. Pinning the npm package is stronger than
snyk/actions/*@master, whose Docker image tag is mutable, and it avoids
pulling a Docker image for every matrix leg. Bump that version deliberately in
ci.yml.
Snyk requires the repository secret SNYK_TOKEN. Generate one at
https://app.snyk.io/account → Auth Token and set it under
Settings → Secrets and variables → Actions. Fork pull requests cannot read
repository secrets, so the Snyk steps are skipped there; the tooling and secret
lanes still run. publish.yml passes secrets: inherit to ci.yml so the
scan also gates main. Add --org=<id> to the two commands only when the
token must report under a specific organization.
Snyk's free tier covers public repositories without a monthly test limit.
@earendil-works/pi-coding-agent@1.3.0 depends on undici@8.10.2, which has high-severity advisories, so every package resolves it transitively. Every package manifest therefore pins overrides.undici to the first Snyk-clean line, 8.12.2, and every lockfile records that exact version. Remove the override only after the Pi SDK updates its dependency and the Snyk job passes without it.
quickstart.yml validates the README's live quickstart after the main CI/release workflow completes successfully, on a daily schedule, and on manual dispatch. It installs the pinned Pi CLI into an isolated temporary agent directory, runs pi install . from a fresh checkout without node_modules, verifies Pi starts far enough to print help, waits through npm's post-publish processing window, and downloads every publishable package from npm at the exact version in its manifest. The post-release trigger plus registry polling avoids checking a new version before the publish job has made it downloadable.
Dependabot proposes weekly Actions and npm updates. Actions use commit SHA
pins. The Gitleaks binary version and SHA-256 checksum in
scripts/install-gitleaks.sh must be updated together. Dependabot does not
track the pinned Snyk CLI version in ci.yml; bump it manually.
digital-twins uses TruffleHog with verified-only findings, which calls
credential providers and misses credentials without verifiers. This repository
uses Gitleaks 8.30.1 with its default rules: an offline, lightweight scan
without a service token, license, or live credential-verification requests.
CI scans fetched Git history on every PR/main run with redacted output. It also tests that the scanner rejects a synthetic credential. There is no blanket test-directory allowlist or existing-secret baseline.
Install the optional local staged-file guard in each clone:
brew install gitleaks
npm run hooks:install
npm run secrets:scanThe installer refuses to replace an existing hook configuration. The hook blocks a commit if Gitleaks is missing or finds a secret. It is installed in the implementation clone, but Git cannot install it automatically in other clones. Common local credential files are also ignored by Git.
No scanner guarantees detection of every key. CI runs after a push, local hooks can be bypassed, and GitHub push protection supports specific patterns. Never commit real credentials, even temporarily. If a key is exposed, revoke or rotate it immediately; deleting it in a later commit does not remove it from history. Never add an allowlist entry to silence a real key.
Add its publishable metadata and lockfile, validation scripts, and explicit
test partition to scripts/packages.mjs; add its npm directory to Dependabot.
The release allowlist intentionally excludes private/root packages and unknown
paths. Establish its initial npm publication and trusted publisher before
using this existing-package release flow.