From bfaf47cea2ec98707542d7aa65ecebba2b41cd0d Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 28 Oct 2026 22:49:19 -0500 Subject: [PATCH 1/4] fix: retain existing npm package identity --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9a9c6b2..460d526 100755 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "@gitdamnit/stranger-danger", + "name": "@gitdamnit/opencode-stranger-danger", "version": "0.2.0", "description": "Secret and PII redaction guard for AI coding agents — scans files, context windows, and outputs before secrets reach LLMs", "keywords": [ From d46bd591650d1cf6cce5ad104ccea15f5bd2a8a4 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 28 Oct 2026 22:49:27 -0500 Subject: [PATCH 2/4] fix: align npm lockfile with existing package --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index e2872d9..bc5af13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "@gitdamnit/stranger-danger", + "name": "@gitdamnit/opencode-stranger-danger", "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@gitdamnit/stranger-danger", + "name": "@gitdamnit/opencode-stranger-danger", "version": "0.2.0", "license": "MIT", "bin": { From d3b084871f35143c2cd9e228a9d054fcaada66e7 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 28 Oct 2026 22:49:30 -0500 Subject: [PATCH 3/4] ci: require manual approval for npm publication --- .github/workflows/publish.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 6038c21..e1a7f64 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,9 +1,7 @@ name: Publish to npm on: - push: - tags: - - "v*.*.*" + workflow_dispatch: permissions: contents: read @@ -54,5 +52,3 @@ jobs: - name: Publish to npm run: npm publish --provenance --access public - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} From 63ff88461faea2cac7c22b935362c4d6b551d28e Mon Sep 17 00:00:00 2001 From: Evan Zellner Date: Thu, 29 Oct 2026 19:04:41 -0500 Subject: [PATCH 4/4] fix: restore npm package identity and publish via OIDC The 0.2.0 release commit renamed the package to @gitdamnit/stranger-danger, a name that has never existed on npm, so no 0.2.x release was installable from the registry. `npm run verify:package` also hardcoded that same name and therefore failed its packed-artifact check. - restore @gitdamnit/opencode-stranger-danger as the package name and release the fixes as 0.2.1 (v0.2.0 already points at the misnamed commit) - derive the package name from package.json in scripts/verify-package.mjs - switch publish.yml to manual workflow_dispatch + npm Trusted Publishing - correct every documented package reference and record D-0007 --- .github/workflows/publish.yml | 83 +++++++++++++++++++++-------------- CHANGELOG.md | 24 +++++++++- DECISION_LOG.md | 26 +++++++++++ PRODUCT_REQUIREMENTS.md | 2 +- PROJECT_STATUS.md | 4 +- README.md | 16 +++---- package-lock.json | 4 +- package.json | 2 +- scripts/verify-package.mjs | 18 +++++--- 9 files changed, 124 insertions(+), 55 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e1a7f64..b1cfdf2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,7 +1,14 @@ +# Manual release publishing using npm Trusted Publishing (OIDC). +# Never triggered by a GitHub release or tag push. name: Publish to npm on: workflow_dispatch: + inputs: + tag: + description: "Existing release tag (for example v0.3.0)" + required: true + type: string permissions: contents: read @@ -9,46 +16,56 @@ permissions: jobs: publish: - name: Publish package runs-on: ubuntu-latest - steps: - - name: Checkout repo + - name: Validate input and checkout tag + env: + RELEASE_TAG: ${{ inputs.tag }} + run: | + set -eu + if ! printf '%s' "$RELEASE_TAG" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "Expected a stable release tag such as v1.2.3" >&2 + exit 1 + fi + - name: Checkout release tag uses: actions/checkout@v4 + with: + ref: ${{ inputs.tag }} + persist-credentials: false - - name: Setup Node + - name: Set up Node for npm Trusted Publishing uses: actions/setup-node@v4 with: - node-version: 22 - cache: npm - registry-url: https://registry.npmjs.org/ - - - name: Install dependencies - run: npm ci - - - name: Compile detection rules from TOML - run: npm run build:rules + node-version: '24' + registry-url: https://registry.npmjs.org + package-manager-cache: false - - name: Typecheck - run: npm run typecheck + - name: Require current npm OIDC client + run: npm install --global 'npm@^11.5.1' - - name: Lint - run: npm run lint + - name: Verify version and tag + env: + RELEASE_TAG: ${{ inputs.tag }} + run: | + set -eu + actual="$(node -p "require('./package.json').version")" + test "$RELEASE_TAG" = "v$actual" || { + echo "Version mismatch: $RELEASE_TAG vs $actual" >&2 + exit 1 + } + echo "Verified release $RELEASE_TAG" - - name: Unit tests - run: npm run test:unit - - - name: Integration tests - run: npm run test:integration - - - name: Security regression tests - run: npm run test:security - - - name: Build - run: npm run build - - - name: Package validation - run: npm run verify:package + - name: Install dependencies + run: npm ci + - name: Build and validate + run: | + npm run build + npm run typecheck + npm run lint + npm run test:unit + npm run test:integration + npm run test:security + npm run verify:package - - name: Publish to npm - run: npm publish --provenance --access public + - name: Publish using OIDC + run: npm publish --access public diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ba7c97..b793c15 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,29 @@ # Changelog +## 0.2.1 (2026-10-29) + +Packaging and npm-identity fix. The 0.2.0 source was distributed only as a +GitHub release; it was never installable from npm. + +### Fixed +- **Restored the published npm package identity.** `package.json` named the + package `@gitdamnit/stranger-danger`, which has never existed on npm, so + `npm install` could not resolve it. The package is again + `@gitdamnit/opencode-stranger-danger`, the name published since 0.1.0. +- **`npm run verify:package` no longer hardcodes the package name.** It reads + the name from `package.json`, so packaging validation cannot drift from what + is actually published. The previous hardcoding is what made the packed-artifact + check fail as soon as the name was corrected. + +### Changed +- **npm publication is manual and OIDC-based.** `.github/workflows/publish.yml` + now runs only on `workflow_dispatch`, checks out and verifies the release tag, + and publishes through npm Trusted Publishing instead of an `NPM_TOKEN` secret. + ## 0.2.0 (2026-10-16) Security hardening and OpenCode V2 migration. Published as a GitHub release; -npm publication of `@gitdamnit/stranger-danger` is pending registry setup +npm publication of `@gitdamnit/opencode-stranger-danger` is pending registry setup (install from the repository in the meantime). ### Security fixes @@ -73,7 +93,7 @@ npm publication of `@gitdamnit/stranger-danger` is pending registry setup broken companion or bad event cannot cascade. - Documented the ordering-independent guarantee (the `session.context` hook is the backstop) and the single optional, read-only, semver-versioned interop - surface (`@gitdamnit/stranger-danger/engine`). + surface (`@gitdamnit/opencode-stranger-danger/engine`). - Removed a stale reference to the retired CheapCode project from `scripts/write_fixture.ts`; it now writes only inside this repository. - Added an independence test suite (isolated instances, session isolation, diff --git a/DECISION_LOG.md b/DECISION_LOG.md index 0f31b39..c9dd0a7 100644 --- a/DECISION_LOG.md +++ b/DECISION_LOG.md @@ -142,3 +142,29 @@ Allowed statuses: `Proposed`, `Accepted`, `Superseded`, `Deprecated`, `Rejected` the npm step because `NPM_TOKEN` is unset. This is expected and does not affect the GitHub release. npm distribution must not be reported as complete. - **Supersedes:** None + +### D-0007 — Publish under the existing `@gitdamnit/opencode-stranger-danger` identity as 0.2.1 + +- **Date:** 2026-10-29 +- **Status:** Accepted +- **Context:** `@gitdamnit/opencode-stranger-danger@0.1.0` is the package that has + been public on npm since 2026-05-14. The 0.2.0 release commit renamed the + package to `@gitdamnit/stranger-danger`, a name that has never existed on npm, + so the 0.2.0 GitHub release could not be installed from the registry. The + `v0.2.0` tag already points at that commit and its GitHub release is public. +- **Alternatives Considered:** publish `@gitdamnit/stranger-danger` as a second + package (rejected: splits the install base, silently abandons the existing + package, and contradicts the documented install command); move or delete the + `v0.2.0` tag so it can be reused (rejected: rewrites published history); + publish 0.2.0 from an untagged commit (rejected: breaks tag-to-artifact + provenance, which npm trusted publishing attests). +- **Decision:** Keep `@gitdamnit/opencode-stranger-danger` as the package + identity, release the identity and packaging fixes as `0.2.1`, tag it, and + publish through npm Trusted Publishing (GitHub Actions OIDC). +- **Rationale:** Restores installability under the name users already have, and + a patch bump keeps 0.2.0's unreachable release from being silently + overwritten or misrepresented. +- **Consequences:** npm's first 0.2.x artifact is 0.2.1, not 0.2.0. `D-0006`'s + "npm publication is a separate, manual step" still holds; the manual step is + now the OIDC `publish.yml` workflow rather than an `NPM_TOKEN` tag push. +- **Supersedes:** None diff --git a/PRODUCT_REQUIREMENTS.md b/PRODUCT_REQUIREMENTS.md index 1f1143b..bd0e750 100644 --- a/PRODUCT_REQUIREMENTS.md +++ b/PRODUCT_REQUIREMENTS.md @@ -2,7 +2,7 @@ ## Project Identity -**Name:** strangerDanger (`@gitdamnit/stranger-danger`) +**Name:** strangerDanger (`@gitdamnit/opencode-stranger-danger`) **Repository:** https://github.com/gitdamnit/opencode-stranger-danger **Licence:** MIT **Maintainer:** gitdamnit (Evan Zellner) diff --git a/PROJECT_STATUS.md b/PROJECT_STATUS.md index e8b9d88..b59fc4a 100644 --- a/PROJECT_STATUS.md +++ b/PROJECT_STATUS.md @@ -37,7 +37,7 @@ Released - [Implemented] CI and npm publish workflows — `.github/workflows/ci.yml`, `.github/workflows/publish.yml` - [Implemented] MIT `LICENSE` and vulnerability-disclosure `SECURITY.md` — `LICENSE`, `SECURITY.md` - [Published] GitHub release `v0.2.0` at merge commit `b634ce5cb84c533c1fd1f6802d922b6e812e5afb` — https://github.com/gitdamnit/opencode-stranger-danger/releases/tag/v0.2.0 -- [Blocked] npm publication of `@gitdamnit/stranger-danger@0.2.0` — registry credentials absent (`ENEEDAUTH`, no `NPM_TOKEN`) +- [Blocked] npm publication of `@gitdamnit/opencode-stranger-danger@0.2.0` — registry credentials absent (`ENEEDAUTH`, no `NPM_TOKEN`) ## Tests @@ -67,7 +67,7 @@ Released ## Next Actions 1. To publish to npm: create/claim the `@gitdamnit` scope, authenticate (`npm login`, or set the `NPM_TOKEN` repository secret), then re-run the `publish.yml` workflow for tag `v0.2.0` (or run `npm publish --access public` from a clean checkout of the tag). `publishConfig.access` is already `public` and `prepublishOnly` gates on typecheck, lint, tests, and build. -2. After npm publication, install with the documented `plugins: ["@gitdamnit/stranger-danger"]` form and re-verify. +2. After npm publication, install with the documented `plugins: ["@gitdamnit/opencode-stranger-danger"]` form and re-verify. ## Release Status diff --git a/README.md b/README.md index e8929e2..12fee58 100755 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ ![strangerDanger logo](./strangerdanger2.jpeg) -# @gitdamnit/stranger-danger +# @gitdamnit/opencode-stranger-danger Secret and PII redaction guard for AI coding agents. -[![npm version](https://img.shields.io/npm/v/@gitdamnit/stranger-danger.svg)](https://www.npmjs.com/package/@gitdamnit/stranger-danger) -[![license](https://img.shields.io/npm/l/@gitdamnit/stranger-danger.svg)](LICENSE) -[![node](https://img.shields.io/node/v/@gitdamnit/stranger-danger.svg)](https://nodejs.org) +[![npm version](https://img.shields.io/npm/v/@gitdamnit/opencode-stranger-danger.svg)](https://www.npmjs.com/package/@gitdamnit/opencode-stranger-danger) +[![license](https://img.shields.io/npm/l/@gitdamnit/opencode-stranger-danger.svg)](LICENSE) +[![node](https://img.shields.io/node/v/@gitdamnit/opencode-stranger-danger.svg)](https://nodejs.org) ## The Problem @@ -45,11 +45,11 @@ Add to your `opencode.jsonc` (`plugins`, not the V1 `plugin` key): ```jsonc { "$schema": "https://opencode.ai/config.json", - "plugins": ["@gitdamnit/stranger-danger"] + "plugins": ["@gitdamnit/opencode-stranger-danger"] } ``` -> **0.2.0 is not yet on npm.** Until it is published, install it from the +> **0.2.1 is not yet on npm.** Until it is published, install it from the > repository or a local checkout — see > [Loading from a directory or a checkout](#loading-from-a-directory-or-a-checkout) > below. The `plugins` entry above resolves once the package is published. @@ -79,7 +79,7 @@ is unavailable, the plugin did not load. ### Standalone CLI ```bash -npm install -g @gitdamnit/stranger-danger # once published to npm +npm install -g @gitdamnit/opencode-stranger-danger # once published to npm ``` ### Use with envsitter-guard @@ -288,7 +288,7 @@ The only interop surface is the semver-versioned, read-only engine export: ```js import { scan, redact, scanAndRedact, scanAndRedactBounded, toSafeFinding } - from "@gitdamnit/stranger-danger/engine"; + from "@gitdamnit/opencode-stranger-danger/engine"; ``` A companion plugin or the Docker Plugin Test Lab may call these to sanitize or diff --git a/package-lock.json b/package-lock.json index bc5af13..c61ddec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@gitdamnit/opencode-stranger-danger", - "version": "0.2.0", + "version": "0.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@gitdamnit/opencode-stranger-danger", - "version": "0.2.0", + "version": "0.2.1", "license": "MIT", "bin": { "sd": "dist/cli/index.js" diff --git a/package.json b/package.json index 460d526..2e0b5ae 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@gitdamnit/opencode-stranger-danger", - "version": "0.2.0", + "version": "0.2.1", "description": "Secret and PII redaction guard for AI coding agents — scans files, context windows, and outputs before secrets reach LLMs", "keywords": [ "opencode", diff --git a/scripts/verify-package.mjs b/scripts/verify-package.mjs index 5475eb2..26e3f24 100644 --- a/scripts/verify-package.mjs +++ b/scripts/verify-package.mjs @@ -14,6 +14,13 @@ const root = resolve(new URL("..", import.meta.url).pathname); const run = (cmd, args, opts = {}) => execFileSync(cmd, args, { stdio: "pipe", encoding: "utf8", ...opts }); +// Derive the identity from package.json so the validation can never drift from +// the name that is actually published. +const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8")); +const pkgName = pkg.name; +const pkgPath = pkgName.split("/"); +const tarballPrefix = pkgName.replace(/^@/, "").replace(/\//g, "-") + "-"; + function step(label, fn) { process.stdout.write(`- ${label} ... `); const result = fn(); @@ -61,22 +68,21 @@ try { }); step("standalone engine export resolves", () => { - const r = run(process.execPath, ["--input-type=module", "-e", - "import('@gitdamnit/stranger-danger/engine').then(m=>{if(typeof m.scan!=='function')process.exit(1)})", + run(process.execPath, ["--input-type=module", "-e", + `import(${JSON.stringify(`${pkgName}/engine`)}).then(m=>{if(typeof m.scan!=='function')process.exit(1)})`, ], { cwd: workdir }); - void r; }); step("plugin default export has the V2 shape", () => { run(process.execPath, ["--input-type=module", "-e", - "import('@gitdamnit/stranger-danger').then(m=>{const p=m.default;if(!p||p.id!=='gitdamnit.stranger-danger'||typeof p.setup!=='function')process.exit(1)})", + `import(${JSON.stringify(pkgName)}).then(m=>{const p=m.default;if(!p||p.id!=='gitdamnit.stranger-danger'||typeof p.setup!=='function')process.exit(1)})`, ], { cwd: workdir }); }); // OpenCode resolves a plugin *directory* to /index.{ts,js} and ignores // main/exports, so the shipped package must include a root entrypoint. step("published package includes the root directory entrypoint", () => { - const entry = join(workdir, "node_modules", "@gitdamnit", "stranger-danger", "index.js"); + const entry = join(workdir, "node_modules", ...pkgPath, "index.js"); if (!existsSync(entry)) throw new Error("package is missing root index.js (directory installs would not load)"); const src = readFileSync(entry, "utf8"); if (!/export\s*\{\s*default\s*\}/.test(src)) throw new Error("root index.js does not export a default plugin"); @@ -96,7 +102,7 @@ try { // Remove any stray tarballs in the repo root. try { for (const f of readdirSync(root)) { - if (f.startsWith("gitdamnit-stranger-danger-") && f.endsWith(".tgz")) { + if (f.startsWith(tarballPrefix) && f.endsWith(".tgz")) { rmSync(join(root, f), { force: true }); } }