From 49a46008dc8a388c60bf9a407d67342f64768085 Mon Sep 17 00:00:00 2001 From: Zack Whitson Date: Tue, 14 Jul 2026 17:57:43 -0500 Subject: [PATCH] Prepare plugin packages for reviewed directories --- .agents/plugins/marketplace.json | 3 +- .claude-plugin/marketplace.json | 12 +-- .github/workflows/release.yml | 38 ++++++++ .github/workflows/validate.yml | 2 + .gitignore | 1 + README.md | 23 +++-- SECURITY.md | 18 ++++ package.json | 7 +- .../.claude-plugin/plugin.json | 6 +- plugins/codetruss-claude/LICENSE | 21 +++++ plugins/codetruss-claude/README.md | 32 +++++++ .../skills/codetruss/SKILL.md | 2 +- .../skills/codetruss/agents/openai.yaml | 2 +- plugins/codetruss/.codex-plugin/plugin.json | 15 ++-- plugins/codetruss/LICENSE | 21 +++++ plugins/codetruss/README.md | 32 +++++++ .../codetruss/assets/codetruss-mark-512.png | Bin 0 -> 21938 bytes plugins/codetruss/skills/codetruss/SKILL.md | 2 +- .../skills/codetruss/agents/openai.yaml | 2 +- scripts/build-release.mjs | 68 ++++++++++++++ scripts/validate.mjs | 43 +++++++++ scripts/verify-release.mjs | 32 +++++++ skills/codetruss/SKILL.md | 2 +- skills/codetruss/agents/openai.yaml | 2 +- submission/PUBLISHER_CHECKLIST.md | 47 ++++++++++ submission/claude.md | 34 +++++-- submission/fixtures/README.md | 40 +++++++++ submission/fixtures/setup.mjs | 85 ++++++++++++++++++ submission/openai.md | 61 +++++++++---- 29 files changed, 601 insertions(+), 52 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 SECURITY.md create mode 100644 plugins/codetruss-claude/LICENSE create mode 100644 plugins/codetruss-claude/README.md create mode 100644 plugins/codetruss/LICENSE create mode 100644 plugins/codetruss/README.md create mode 100644 plugins/codetruss/assets/codetruss-mark-512.png create mode 100644 scripts/build-release.mjs create mode 100644 scripts/verify-release.mjs create mode 100644 submission/PUBLISHER_CHECKLIST.md create mode 100644 submission/fixtures/README.md create mode 100644 submission/fixtures/setup.mjs diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index da332f3..47589d6 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -12,7 +12,8 @@ }, "policy": { "installation": "AVAILABLE", - "authentication": "ON_INSTALL" + "authentication": "ON_INSTALL", + "products": ["CODEX"] }, "category": "Developer Tools" } diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 585d207..7fefaa2 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1,11 +1,8 @@ { "$schema": "https://json.schemastore.org/claude-code-marketplace.json", "name": "codetruss", - "description": "Catch scope drift and AI slop before commit with local acceptance receipts", - "metadata": { - "description": "Catch scope drift and AI slop before commit with local acceptance receipts", - "version": "0.1.1" - }, + "description": "Verify the acceptance contract for coding-agent changes locally", + "version": "0.1.2", "owner": { "name": "CodeTruss", "email": "zack@codetruss.com" @@ -14,10 +11,9 @@ { "name": "codetruss", "source": "./plugins/codetruss-claude", - "description": "Bind agent changes to the task and produce a verifiable local receipt before commit", - "version": "0.1.1", + "description": "Enforce approved scope and checks with the separately installed free CodeTruss CLI", "category": "development", - "tags": ["scope-drift", "verification", "local-first", "coding-agents"] + "tags": ["acceptance-gate", "scope-drift", "verification", "local-first", "coding-agents"] } ] } diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..94d4c6d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,38 @@ +name: Release + +on: + push: + tags: ['v*'] + +permissions: + attestations: write + contents: write + id-token: write + +jobs: + release: + name: Publish deterministic plugin archives + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + fetch-depth: 0 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 24 + - run: npm test + - run: npm run release:verify + - name: Attest plugin archives + uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 + with: + subject-path: dist/*.zip + - name: Publish release + env: + GH_TOKEN: ${{ github.token }} + run: >- + gh release create "$GITHUB_REF_NAME" dist/* + --verify-tag + --generate-notes + --title "CodeTruss agent plugins $GITHUB_REF_NAME" diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index d76c6f0..84827dc 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -25,3 +25,5 @@ jobs: with: node-version: 24 - run: npm test + - run: npm run fixtures:verify + - run: npm run release:verify diff --git a/.gitignore b/.gitignore index 646ac51..bf56190 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .DS_Store node_modules/ +dist/ diff --git a/README.md b/README.md index b36a72d..c21d376 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,10 @@ claude plugin marketplace add DeliriumPulse/codetruss-plugins claude plugin install codetruss@codetruss ``` -The same validated plugin is ready for Anthropic's reviewed marketplace. The -owned marketplace is live now; official listing remains pending review. +The owned marketplace is live now. The same validated plugin is prepared for +Anthropic's reviewed community marketplace but has not been submitted. The +automatically available official marketplace is separately curated at +Anthropic's discretion. ## Codex @@ -36,9 +38,9 @@ codex plugin marketplace add DeliriumPulse/codetruss-plugins codex plugin add codetruss@codetruss ``` -The validated skills-only bundle is ready for OpenAI's universal Plugin -Directory. The owned marketplace is live now; official listing remains pending -verified publisher submission and review. +The validated skills-only bundle is ready for OpenAI's Plugin Directory. The +owned marketplace is live now; public-directory submission has not occurred and +still requires a verified publisher with Apps Management write access. ## What the skill does @@ -60,10 +62,21 @@ self-contained. npm test claude plugin validate ./plugins/codetruss-claude python3 /path/to/plugin-creator/scripts/validate_plugin.py ./plugins/codetruss +npm run release:verify ``` `npm test` uses only Node.js built-ins and verifies manifests, marketplace entries, skill parity, privacy guardrails, and submission-case counts. +`npm run release:verify` builds the OpenAI and Claude submission archives twice +from the committed Git tree and proves their bytes are reproducible. + +## Security and support + +Read [SECURITY.md](SECURITY.md) before reporting a vulnerability. General +wrapper issues belong in this repository; CLI issues belong in the +[CodeTruss CLI tracker](https://github.com/DeliriumPulse/codetruss-cli/issues). +Never attach third-party source, credentials, or unredacted receipts to a public +issue. ## Licensing boundary diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..fbe3bc8 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,18 @@ +# Security policy + +Please report suspected vulnerabilities privately to `zack@codetruss.com` with +the subject `CodeTruss security report`. Include the affected wrapper or CLI +version, reproduction steps, impact, and whether public disclosure is already +planned. Do not include third-party source code, credentials, or unredacted +receipts unless requested through a mutually agreed secure channel. + +We will acknowledge a report within two business days, provide a preliminary +triage within five business days when reproducible, and coordinate disclosure +after a fix or mitigation is available. Availability and timelines may change +for reports that cannot be reproduced or concern unsupported versions. + +The MIT wrapper repository contains skill instructions and manifests only. CLI +implementation reports belong in the public CLI issue tracker unless they are +security-sensitive. Deterministic CLI operation is local; optional provider +review sends the reviewed diff directly to the selected provider, and explicit +sync sends a privacy-minimized receipt to CodeTruss but never the patch. diff --git a/package.json b/package.json index 19ac4f3..bb37406 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,14 @@ { "name": "codetruss-agent-plugins", - "version": "0.1.1", + "version": "0.1.2", "private": true, "description": "Open Claude Code, Codex, and Agent Skills wrappers for the CodeTruss CLI", "license": "MIT", "scripts": { - "test": "node scripts/validate.mjs" + "test": "node scripts/validate.mjs", + "fixtures:verify": "node submission/fixtures/setup.mjs /tmp/codetruss-plugin-fixtures", + "release:build": "node scripts/build-release.mjs", + "release:verify": "node scripts/verify-release.mjs" }, "engines": { "node": ">=20.9" diff --git a/plugins/codetruss-claude/.claude-plugin/plugin.json b/plugins/codetruss-claude/.claude-plugin/plugin.json index 30c0e50..25ef789 100644 --- a/plugins/codetruss-claude/.claude-plugin/plugin.json +++ b/plugins/codetruss-claude/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "codetruss", - "version": "0.1.1", - "description": "Catch scope drift and AI slop before commit with local acceptance receipts.", + "version": "0.1.2", + "description": "Verify coding-agent changes locally with the separately installed free CodeTruss CLI.", "author": { "name": "CodeTruss", "email": "zack@codetruss.com", @@ -10,6 +10,6 @@ "homepage": "https://codetruss.com/cli", "repository": "https://github.com/DeliriumPulse/codetruss-plugins", "license": "MIT", - "keywords": ["coding-agents", "scope-drift", "local-first", "verification", "receipts"], + "keywords": ["acceptance-gate", "coding-agents", "scope-drift", "local-first", "verification", "receipts"], "skills": "./skills/" } diff --git a/plugins/codetruss-claude/LICENSE b/plugins/codetruss-claude/LICENSE new file mode 100644 index 0000000..d462a98 --- /dev/null +++ b/plugins/codetruss-claude/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 CodeTruss + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugins/codetruss-claude/README.md b/plugins/codetruss-claude/README.md new file mode 100644 index 0000000..b8f930e --- /dev/null +++ b/plugins/codetruss-claude/README.md @@ -0,0 +1,32 @@ +# CodeTruss for Claude Code + +This plugin teaches Claude Code to operate the separately installed free +CodeTruss CLI as a local acceptance gate for coding-agent changes. + +Prerequisites: + +- Git +- Node.js 20.9 or newer +- CodeTruss CLI v0.2.14 or newer from + +The plugin contains no analyzer, bundled hook, MCP server, background service, +or upload path. It delegates hook installation to the tested CLI after developer +confirmation. Deterministic checks stay local. Provider-backed review, +authentication, and receipt sync require separate explicit developer actions. + +Troubleshooting: + +- Run `codetruss --version`; install or upgrade from the CLI page when it is + missing or older than v0.2.14. +- Run `codetruss hooks status claude` and `codetruss hooks doctor claude` + before changing or reinstalling Claude settings. +- Exit 1 means `REVIEW_REQUIRED`, exit 2 means `FAILED`, and both still produce + a receipt. Exit 3 is a usage or environment failure. +- Report wrapper problems at + and CLI/security problems at . +- Follow for private vulnerability reporting + and the current product security boundary. + +The wrapper files in this package are MIT licensed. The separately installed +CLI is source-visible and free to use under the CodeTruss CLI Proprietary +License and CodeTruss Terms of Service. diff --git a/plugins/codetruss-claude/skills/codetruss/SKILL.md b/plugins/codetruss-claude/skills/codetruss/SKILL.md index 272c09e..4e7d8ae 100644 --- a/plugins/codetruss-claude/skills/codetruss/SKILL.md +++ b/plugins/codetruss-claude/skills/codetruss/SKILL.md @@ -1,6 +1,6 @@ --- name: codetruss -description: Catch scope drift and AI slop by setting up and operating CodeTruss local acceptance gates for AI-generated code. Use when a developer asks to bind an agent task to allowed or denied files, configure repository verification, install or diagnose Claude Code or Codex hooks, review a working-tree or staged diff before commit, interpret or verify a signed CodeTruss receipt, repair a failed verdict, or explicitly opt into provider-backed review or receipt sync. +description: Operate CodeTruss local acceptance gates for coding-agent changes. Use when a developer asks to bind an agent task to allowed or denied files, configure repository verification, install or diagnose Claude Code or Codex hooks, review a working-tree or staged diff before commit, interpret or verify a signed CodeTruss receipt, repair a failed verdict, or explicitly opt into provider-backed review or receipt sync. --- # CodeTruss diff --git a/plugins/codetruss-claude/skills/codetruss/agents/openai.yaml b/plugins/codetruss-claude/skills/codetruss/agents/openai.yaml index 784a465..288602d 100644 --- a/plugins/codetruss-claude/skills/codetruss/agents/openai.yaml +++ b/plugins/codetruss-claude/skills/codetruss/agents/openai.yaml @@ -1,4 +1,4 @@ interface: display_name: "CodeTruss" - short_description: "Catch scope drift and AI slop before commit" + short_description: "Local acceptance gate for coding-agent changes" default_prompt: "Use $codetruss to review my current agent changes and explain the receipt." diff --git a/plugins/codetruss/.codex-plugin/plugin.json b/plugins/codetruss/.codex-plugin/plugin.json index 4a8e98e..e3391cb 100644 --- a/plugins/codetruss/.codex-plugin/plugin.json +++ b/plugins/codetruss/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "codetruss", - "version": "0.1.1", - "description": "Catch scope drift and AI slop before commit with local acceptance receipts.", + "version": "0.1.2", + "description": "Verify the acceptance contract for coding-agent changes locally.", "author": { "name": "CodeTruss", "email": "zack@codetruss.com", @@ -10,12 +10,12 @@ "homepage": "https://codetruss.com/cli", "repository": "https://github.com/DeliriumPulse/codetruss-plugins", "license": "MIT", - "keywords": ["coding-agents", "scope-drift", "local-first", "verification", "receipts"], + "keywords": ["acceptance-gate", "coding-agents", "scope-drift", "local-first", "verification", "receipts"], "skills": "./skills/", "interface": { "displayName": "CodeTruss", - "shortDescription": "Catch scope drift and AI slop before commit", - "longDescription": "Bind agent changes to the task, run deterministic local checks, and produce a verifiable receipt before commit.", + "shortDescription": "Local acceptance gate for coding-agent changes", + "longDescription": "Declare approved scope and repository checks, evaluate the captured Git state locally, and produce a verifiable receipt before commit. Requires the separately installed free CodeTruss CLI; no CodeTruss account or upload is required.", "developerName": "CodeTruss", "category": "Developer Tools", "capabilities": ["Interactive", "Read", "Write"], @@ -27,6 +27,9 @@ "Review my current changes against the task", "Verify and explain my latest CodeTruss receipt" ], - "brandColor": "#B93A26" + "brandColor": "#B93A26", + "composerIcon": "./assets/codetruss-mark-512.png", + "logo": "./assets/codetruss-mark-512.png", + "logoDark": "./assets/codetruss-mark-512.png" } } diff --git a/plugins/codetruss/LICENSE b/plugins/codetruss/LICENSE new file mode 100644 index 0000000..d462a98 --- /dev/null +++ b/plugins/codetruss/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 CodeTruss + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugins/codetruss/README.md b/plugins/codetruss/README.md new file mode 100644 index 0000000..3fa891f --- /dev/null +++ b/plugins/codetruss/README.md @@ -0,0 +1,32 @@ +# CodeTruss for Codex + +This skills-only plugin teaches Codex to operate the separately installed free +CodeTruss CLI as a local acceptance gate for coding-agent changes. + +Prerequisites: + +- Git +- Node.js 20.9 or newer +- CodeTruss CLI v0.2.14 or newer from + +The plugin contains no analyzer, MCP server, background service, or upload path. +Deterministic checks stay local. Provider-backed review, authentication, and +receipt sync require separate explicit developer actions. + +Troubleshooting: + +- Run `codetruss --version`; install or upgrade from the CLI page when it is + missing or older than v0.2.14. +- Run `codetruss hooks status codex` and `codetruss hooks doctor codex` before + changing hook configuration. Codex may require approval of the exact hook in + `/hooks`. +- Exit 1 means `REVIEW_REQUIRED`, exit 2 means `FAILED`, and both still produce + a receipt. Exit 3 is a usage or environment failure. +- Report wrapper problems at + and CLI/security problems at . +- Follow for private vulnerability reporting + and the current product security boundary. + +The wrapper files in this package are MIT licensed. The separately installed +CLI is source-visible and free to use under the CodeTruss CLI Proprietary +License and CodeTruss Terms of Service. diff --git a/plugins/codetruss/assets/codetruss-mark-512.png b/plugins/codetruss/assets/codetruss-mark-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d10bd0cd53428fc09e8a189417b8457c92df18b8 GIT binary patch literal 21938 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 literal 0 HcmV?d00001 diff --git a/plugins/codetruss/skills/codetruss/SKILL.md b/plugins/codetruss/skills/codetruss/SKILL.md index 272c09e..4e7d8ae 100644 --- a/plugins/codetruss/skills/codetruss/SKILL.md +++ b/plugins/codetruss/skills/codetruss/SKILL.md @@ -1,6 +1,6 @@ --- name: codetruss -description: Catch scope drift and AI slop by setting up and operating CodeTruss local acceptance gates for AI-generated code. Use when a developer asks to bind an agent task to allowed or denied files, configure repository verification, install or diagnose Claude Code or Codex hooks, review a working-tree or staged diff before commit, interpret or verify a signed CodeTruss receipt, repair a failed verdict, or explicitly opt into provider-backed review or receipt sync. +description: Operate CodeTruss local acceptance gates for coding-agent changes. Use when a developer asks to bind an agent task to allowed or denied files, configure repository verification, install or diagnose Claude Code or Codex hooks, review a working-tree or staged diff before commit, interpret or verify a signed CodeTruss receipt, repair a failed verdict, or explicitly opt into provider-backed review or receipt sync. --- # CodeTruss diff --git a/plugins/codetruss/skills/codetruss/agents/openai.yaml b/plugins/codetruss/skills/codetruss/agents/openai.yaml index 784a465..288602d 100644 --- a/plugins/codetruss/skills/codetruss/agents/openai.yaml +++ b/plugins/codetruss/skills/codetruss/agents/openai.yaml @@ -1,4 +1,4 @@ interface: display_name: "CodeTruss" - short_description: "Catch scope drift and AI slop before commit" + short_description: "Local acceptance gate for coding-agent changes" default_prompt: "Use $codetruss to review my current agent changes and explain the receipt." diff --git a/scripts/build-release.mjs b/scripts/build-release.mjs new file mode 100644 index 0000000..cdd21ea --- /dev/null +++ b/scripts/build-release.mjs @@ -0,0 +1,68 @@ +import { createHash } from 'node:crypto' +import { execFileSync } from 'node:child_process' +import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { fileURLToPath } from 'node:url' + +const root = join(dirname(fileURLToPath(import.meta.url)), '..') +const packageManifest = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')) +const version = packageManifest.version +const tag = `v${version}` +const ref = process.env.CODETRUSS_PLUGIN_RELEASE_REF || 'HEAD' +const dist = join(root, 'dist') +const commitDate = execFileSync('git', ['show', '-s', '--format=%cI', ref], { + cwd: root, + encoding: 'utf8', +}).trim() + +function readJsonAtRef(path) { + return JSON.parse(execFileSync('git', ['show', `${ref}:${path}`], { + cwd: root, + encoding: 'utf8', + })) +} + +const committedPackage = readJsonAtRef('package.json') +if (committedPackage.version !== version) { + throw new Error( + `Committed ${ref} is version ${committedPackage.version}, but the working tree is ${version}; commit the release inputs before packaging`, + ) +} + +for (const manifestPath of [ + 'plugins/codetruss/.codex-plugin/plugin.json', + 'plugins/codetruss-claude/.claude-plugin/plugin.json', +]) { + const manifest = readJsonAtRef(manifestPath) + if (manifest.version !== version) { + throw new Error(`${manifestPath} at ${ref} is ${manifest.version}, expected ${version}`) + } +} + +if (process.env.GITHUB_REF_TYPE === 'tag' && process.env.GITHUB_REF_NAME !== tag) { + throw new Error(`Release tag ${process.env.GITHUB_REF_NAME} does not match package ${tag}`) +} + +rmSync(dist, { recursive: true, force: true }) +mkdirSync(dist, { recursive: true }) + +const packages = [ + ['openai', 'plugins/codetruss'], + ['claude', 'plugins/codetruss-claude'], +] + +for (const [target, tree] of packages) { + const filename = `codetruss-${target}-plugin-v${version}.zip` + const output = join(dist, filename) + execFileSync('git', [ + 'archive', + '--format=zip', + `--mtime=${commitDate}`, + `--output=${output}`, + `${ref}:${tree}`, + ], { cwd: root, stdio: 'inherit' }) + const digest = createHash('sha256').update(readFileSync(output)).digest('hex') + writeFileSync(`${output}.sha256`, `${digest} ${filename}\n`) +} + +process.stdout.write(`Built reproducible CodeTruss plugin archives for ${tag} from ${ref}.\n`) diff --git a/scripts/validate.mjs b/scripts/validate.mjs index 33d7c6a..6429847 100644 --- a/scripts/validate.mjs +++ b/scripts/validate.mjs @@ -1,4 +1,5 @@ import assert from 'node:assert/strict' +import { createHash } from 'node:crypto' import { readFile } from 'node:fs/promises' import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' @@ -6,6 +7,7 @@ import { fileURLToPath } from 'node:url' const root = join(dirname(fileURLToPath(import.meta.url)), '..') const read = (path) => readFile(join(root, path), 'utf8') const json = async (path) => JSON.parse(await read(path)) +const packageManifest = await json('package.json') const canonicalSkill = await read('skills/codetruss/SKILL.md') const claudeSkill = await read('plugins/codetruss-claude/skills/codetruss/SKILL.md') @@ -41,6 +43,7 @@ const codexManifest = await json('plugins/codetruss/.codex-plugin/plugin.json') for (const manifest of [claudeManifest, codexManifest]) { assert.equal(manifest.name, 'codetruss') assert.match(manifest.version, /^\d+\.\d+\.\d+$/) + assert.equal(manifest.version, packageManifest.version) assert.equal(manifest.license, 'MIT') assert.equal(manifest.author.name, 'CodeTruss') assert.equal(manifest.author.email, 'zack@codetruss.com') @@ -51,20 +54,60 @@ assert.equal(codexManifest.interface.privacyPolicyURL, 'https://codetruss.com/pr assert.equal(codexManifest.interface.termsOfServiceURL, 'https://codetruss.com/terms') assert.ok(!('mcpServers' in codexManifest), 'local-first wrapper must not add an MCP server') assert.ok(!('hooks' in codexManifest), 'plugin must delegate hooks to the tested CLI installer') +assert.equal( + codexManifest.interface.shortDescription, + 'Local acceptance gate for coding-agent changes', +) +for (const assetField of ['composerIcon', 'logo', 'logoDark']) { + assert.equal(codexManifest.interface[assetField], './assets/codetruss-mark-512.png') +} +const brandMark = await readFile(join(root, 'plugins/codetruss/assets/codetruss-mark-512.png')) +assert.equal( + createHash('sha256').update(brandMark).digest('hex'), + '2c46b3aca474ec80918333b6331326ef6f1df6d4a09d48c4f4f9f128dc3d45b8', +) const claudeMarketplace = await json('.claude-plugin/marketplace.json') assert.equal(claudeMarketplace.name, 'codetruss') assert.equal(claudeMarketplace.plugins[0].source, './plugins/codetruss-claude') +assert.equal(claudeMarketplace.version, packageManifest.version) +assert.ok(!('version' in claudeMarketplace.plugins[0])) const codexMarketplace = await json('.agents/plugins/marketplace.json') assert.equal(codexMarketplace.name, 'codetruss') assert.equal(codexMarketplace.plugins[0].source.path, './plugins/codetruss') assert.deepEqual(codexMarketplace.plugins[0].policy, { installation: 'AVAILABLE', authentication: 'ON_INSTALL', + products: ['CODEX'], }) const submission = await read('submission/openai.md') assert.equal((submission.match(/^### Positive \d+:/gm) ?? []).length, 5) assert.equal((submission.match(/^### Negative \d+:/gm) ?? []).length, 3) +assert.match(submission, /Status: \*\*Prepared and locally validated; not submitted or approved\.\*\*/) +assert.match(submission, /Short description: Local acceptance gate for coding-agent changes\./) +assert.match(submission, /Requires the separately installed free CodeTruss CLI/) +assert.match(submission, /Availability:/) +assert.match(submission, /submission\/fixtures\/setup\.mjs/) + +const claudeSubmission = await read('submission/claude.md') +assert.match(claudeSubmission, /reviewed community marketplace/) +assert.match(claudeSubmission, /not submitted or approved/) +assert.match(claudeSubmission, /Submitter email: `zack@codetruss\.com`/) +assert.match(claudeSubmission, /Supported platform: `Claude Code`/) +assert.match(claudeSubmission, /Wrapper license: `MIT`/) +assert.match(claudeSubmission, /## Example use cases/) +assert.doesNotMatch(claudeSubmission, /It never uploads code/) +assert.match(claudeSubmission, /Optional\n> `--llm` sends the reviewed diff directly/) + +const publisherChecklist = await read('submission/PUBLISHER_CHECKLIST.md') +assert.match(publisherChecklist, /Apps Management: Write/) +assert.match(publisherChecklist, /gate the listing to Codex/) +assert.match(publisherChecklist, /Select `Claude Code` only/) +assert.match(publisherChecklist, /does not satisfy identity verification/) + +const readme = await read('README.md') +assert.doesNotMatch(readme, /official listing remains pending review/) +assert.match(readme, /official marketplace is separately curated/) process.stdout.write('CodeTruss Claude, Codex, and Agent Skills packages validated.\n') diff --git a/scripts/verify-release.mjs b/scripts/verify-release.mjs new file mode 100644 index 0000000..4b84a91 --- /dev/null +++ b/scripts/verify-release.mjs @@ -0,0 +1,32 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import { createHash } from 'node:crypto' +import { readdirSync, readFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import { fileURLToPath } from 'node:url' +import { setTimeout } from 'node:timers/promises' + +const root = join(dirname(fileURLToPath(import.meta.url)), '..') +const dist = join(root, 'dist') + +function buildAndHash() { + execFileSync(process.execPath, [join(root, 'scripts/build-release.mjs')], { + cwd: root, + stdio: 'inherit', + }) + return Object.fromEntries( + readdirSync(dist).sort().map((name) => [ + name, + createHash('sha256').update(readFileSync(join(dist, name))).digest('hex'), + ]), + ) +} + +const first = buildAndHash() +await setTimeout(1_100) +const second = buildAndHash() +assert.deepEqual(second, first, 'release archives changed across identical builds') +assert.equal(Object.keys(first).filter((name) => name.endsWith('.zip')).length, 2) +assert.equal(Object.keys(first).filter((name) => name.endsWith('.sha256')).length, 2) + +process.stdout.write('CodeTruss plugin release archives are byte-for-byte reproducible.\n') diff --git a/skills/codetruss/SKILL.md b/skills/codetruss/SKILL.md index 272c09e..4e7d8ae 100644 --- a/skills/codetruss/SKILL.md +++ b/skills/codetruss/SKILL.md @@ -1,6 +1,6 @@ --- name: codetruss -description: Catch scope drift and AI slop by setting up and operating CodeTruss local acceptance gates for AI-generated code. Use when a developer asks to bind an agent task to allowed or denied files, configure repository verification, install or diagnose Claude Code or Codex hooks, review a working-tree or staged diff before commit, interpret or verify a signed CodeTruss receipt, repair a failed verdict, or explicitly opt into provider-backed review or receipt sync. +description: Operate CodeTruss local acceptance gates for coding-agent changes. Use when a developer asks to bind an agent task to allowed or denied files, configure repository verification, install or diagnose Claude Code or Codex hooks, review a working-tree or staged diff before commit, interpret or verify a signed CodeTruss receipt, repair a failed verdict, or explicitly opt into provider-backed review or receipt sync. --- # CodeTruss diff --git a/skills/codetruss/agents/openai.yaml b/skills/codetruss/agents/openai.yaml index 784a465..288602d 100644 --- a/skills/codetruss/agents/openai.yaml +++ b/skills/codetruss/agents/openai.yaml @@ -1,4 +1,4 @@ interface: display_name: "CodeTruss" - short_description: "Catch scope drift and AI slop before commit" + short_description: "Local acceptance gate for coding-agent changes" default_prompt: "Use $codetruss to review my current agent changes and explain the receipt." diff --git a/submission/PUBLISHER_CHECKLIST.md b/submission/PUBLISHER_CHECKLIST.md new file mode 100644 index 0000000..1466764 --- /dev/null +++ b/submission/PUBLISHER_CHECKLIST.md @@ -0,0 +1,47 @@ +# Publisher submission checklist + +The packages are technically ready, but these account-level gates must be +completed by the verified publisher. Do not describe either listing as +submitted or approved until the vendor portal confirms that state. + +## OpenAI Plugin Directory + +1. In the publishing OpenAI Platform organization, complete business identity + verification under the exact public operator name `CodeTruss`. If only + individual verification is available, first align the plugin publisher, + website operator, support identity, privacy policy, and terms to that + verified identity. +2. Confirm the submitter role has `Apps Management: Write`. +3. Choose `Skills only` and gate the listing to Codex. The plugin requires a + local Git checkout and executable CLI; it is not a ChatGPT Work web workflow. +4. Upload `codetruss-openai-plugin-v0.1.2.zip` from the matching GitHub release, + the checksum-pinned 512×512 logo, the three starter prompts, exactly five + positive tests, and exactly three negative tests from `submission/openai.md`. +5. Select all portal-supported regions intended for launch, review the public + prerequisite/licensing text, and submit. Approval still requires an explicit + Publish action in the portal. + +An OpenAI API key or project membership does not satisfy identity verification +or Apps Management permission. + +## Anthropic community marketplace + +1. Use the Anthropic Console submission form with repository + `https://github.com/DeliriumPulse/codetruss-plugins` and plugin path + `plugins/codetruss-claude`. +2. Select `Claude Code` only. Cowork has not been tested and must not be claimed. +3. Copy the description and three examples from `submission/claude.md`, use + `zack@codetruss.com`, identify the wrapper license as MIT, review the terms, + and submit. +4. Record the reviewed commit SHA. Approved third-party submissions enter the + `claude-community` marketplace. The automatically available official + marketplace is separately curated at Anthropic's discretion. + +## Evidence to retain + +- vendor submission ID and timestamp; +- exact Git commit, release URL, archive SHA-256, and logo SHA-256; +- successful `gh attestation verify --repo DeliriumPulse/codetruss-plugins` output; +- screenshots of the final submitted fields; +- review status and every requested remediation; +- approval and publication timestamps as separate events. diff --git a/submission/claude.md b/submission/claude.md index 9b378c6..29fbfeb 100644 --- a/submission/claude.md +++ b/submission/claude.md @@ -1,8 +1,17 @@ # Claude community submission draft +Status: **Prepared and locally validated; not submitted or approved.** This is +for the reviewed community marketplace. Official-marketplace inclusion is a +separate Anthropic-curated decision. + - Plugin: `codetruss` - Repository: - Package path: `plugins/codetruss-claude` +- Submitter email: `zack@codetruss.com` +- Supported platform: `Claude Code` +- Wrapper license: `MIT` +- Terms acknowledgement: confirm in the Console form after reviewing the final + submission. - Homepage: - Logo: (512 × 512; SHA-256 `2c46b3aca474ec80918333b6331326ef6f1df6d4a09d48c4f4f9f128dc3d45b8`) @@ -12,12 +21,27 @@ Description: -> Catch scope drift and AI slop before commit. Keep Claude Code changes inside -> the approved task, run deterministic local analyzers and repository -> verification, and produce a verifiable receipt. The +> Verify the acceptance contract for Claude Code changes before commit. Keep +> changes inside the approved task, run deterministic local analyzers and +> repository verification, and produce a verifiable receipt. The > skill installs CodeTruss's existing project hook only after the developer -> confirms repository scope. It never uploads code; provider review and receipt -> sync remain explicit opt-ins. +> confirms repository scope. Deterministic review uploads nothing. Optional +> `--llm` sends the reviewed diff directly to the developer's chosen provider; +> explicit sync uploads only a redacted receipt and never the patch. Requires +> the separately installed free CodeTruss CLI; no CodeTruss account or upload +> is required for local use. The wrapper is MIT licensed; the CLI is +> source-visible under the CodeTruss CLI Proprietary License. + +## Example use cases + +1. Configure a narrow acceptance boundary for a repository by inspecting its + existing scripts, proposing allow/deny globs, and installing the Claude Code + hook only after developer confirmation. +2. Review the current working-tree or staged changes against the stated task, + then explain every scope, sensitive-surface, analyzer, and verification + reason in the resulting verdict. +3. Verify the latest receipt and explain the difference between post-generation + integrity evidence, trusted execution, authorship, and compliance. Pre-submission evidence: diff --git a/submission/fixtures/README.md b/submission/fixtures/README.md new file mode 100644 index 0000000..04bad01 --- /dev/null +++ b/submission/fixtures/README.md @@ -0,0 +1,40 @@ +# Reviewer fixtures + +These fixtures make the five positive OpenAI skill tests reproducible without +placing source code, credentials, or a CodeTruss account in the submission. + +Prerequisites: Git, Node.js 20.9 or newer, CodeTruss CLI v0.2.14 or newer, and +the CodeTruss plugin under review. + +Generate all repositories: + +```bash +node submission/fixtures/setup.mjs /tmp/codetruss-plugin-fixtures +``` + +The script recreates the destination and makes deterministic initial commits. + +## Positive-test mapping + +1. `configure-boundary`: start here for repository setup. It intentionally has + no `.codetruss.yml`. Ask the plugin to allow `src/**` and `tests/**`, deny + `infra/production/**` and `.env*`, use `npm run lint` plus `npm test`, and + install the Codex hook. +2. `review-working-tree`: contains an uncommitted `src/rate-limit.ts` change and + a valid policy. Ask the plugin to review it for the password-reset + rate-limiting task. +3. `review-staged`: contains staged `src/staged.ts` and unstaged + `src/unstaged.ts`. Ask for a staged-only pre-commit review and confirm the + receipt excludes the unstaged path. +4. `diagnose-claude-hook`: contains a stale Claude Stop-hook command. Ask the + plugin to diagnose it without deleting or reinstalling settings first. +5. `verify-receipt`: contains an uncommitted `src/receipt.ts` change. Create the + signed local receipt, then ask the plugin to verify and explain it: + + ```bash + cd /tmp/codetruss-plugin-fixtures/verify-receipt + codetruss review --task "Add the receipt fixture" --no-verify + ``` + +Exit 1 or 2 is a product verdict and still creates the receipt required by the +test. No fixture invokes `--llm`, authentication, or sync. diff --git a/submission/fixtures/setup.mjs b/submission/fixtures/setup.mjs new file mode 100644 index 0000000..a85a996 --- /dev/null +++ b/submission/fixtures/setup.mjs @@ -0,0 +1,85 @@ +import { execFileSync } from 'node:child_process' +import { mkdirSync, rmSync, writeFileSync } from 'node:fs' +import { dirname, join, resolve } from 'node:path' + +const destination = resolve(process.argv[2] || '/tmp/codetruss-plugin-fixtures') +const gitEnv = { + ...process.env, + GIT_AUTHOR_DATE: '2026-01-01T00:00:00Z', + GIT_COMMITTER_DATE: '2026-01-01T00:00:00Z', +} + +function run(cwd, command, args) { + execFileSync(command, args, { cwd, env: gitEnv, stdio: 'ignore' }) +} + +function write(root, path, contents) { + const target = join(root, path) + mkdirSync(dirname(target), { recursive: true }) + writeFileSync(target, contents) +} + +const policy = `version: 1 +allow: + - src/** + - tests/** +deny: + - infra/production/** + - .env* +verify: + - npm run lint + - npm test +receipts: + dir: .codetruss/receipts +` + +function createRepository(name, { withPolicy = true } = {}) { + const root = join(destination, name) + mkdirSync(root, { recursive: true }) + write(root, 'package.json', `${JSON.stringify({ + name: `codetruss-${name}-fixture`, + private: true, + scripts: { + lint: 'node -e "process.exit(0)"', + test: 'node -e "process.exit(0)"', + }, + }, null, 2)}\n`) + write(root, 'src/index.ts', 'export const fixture = true\n') + write(root, 'tests/index.test.ts', 'export const fixtureTest = true\n') + write(root, 'infra/production/main.tf', '# deliberately sensitive fixture surface\n') + write(root, '.env.example', 'EXAMPLE_ONLY=true\n') + if (withPolicy) write(root, '.codetruss.yml', policy) + run(root, 'git', ['init', '-q']) + run(root, 'git', ['config', 'user.name', 'CodeTruss Fixture']) + run(root, 'git', ['config', 'user.email', 'reviewer@example.invalid']) + run(root, 'git', ['add', '.']) + run(root, 'git', ['commit', '-q', '-m', 'Initial reviewer fixture']) + return root +} + +rmSync(destination, { recursive: true, force: true }) +mkdirSync(destination, { recursive: true }) + +createRepository('configure-boundary', { withPolicy: false }) + +const workingTree = createRepository('review-working-tree') +write(workingTree, 'src/rate-limit.ts', 'export const passwordResetLimit = 5\n') + +const staged = createRepository('review-staged') +write(staged, 'src/staged.ts', 'export const stagedChange = true\n') +run(staged, 'git', ['add', 'src/staged.ts']) +write(staged, 'src/unstaged.ts', 'export const unstagedChange = true\n') + +const hook = createRepository('diagnose-claude-hook') +write(hook, '.claude/settings.json', `${JSON.stringify({ + hooks: { + Stop: [{ + hooks: [{ type: 'command', command: 'codetruss hooks run claude --legacy' }], + }], + }, +}, null, 2)}\n`) + +const receipt = createRepository('verify-receipt') +write(receipt, 'src/receipt.ts', 'export const receiptFixture = true\n') + +process.stdout.write(`Created CodeTruss reviewer fixtures at ${destination}\n`) diff --git a/submission/openai.md b/submission/openai.md index b02977f..20cf4e2 100644 --- a/submission/openai.md +++ b/submission/openai.md @@ -2,26 +2,52 @@ Submission type: **Skills only** +Status: **Prepared and locally validated; not submitted or approved.** +Product scope: **Codex only.** The workflow requires a local Git checkout and +the separately installed CLI, so it must not be represented as a ChatGPT Work +web workflow. + ## Listing - Name: CodeTruss - Category: Developer Tools -- Short description: Catch scope drift and AI slop before commit. +- Short description: Local acceptance gate for coding-agent changes. - Logo: (512 × 512; SHA-256 `2c46b3aca474ec80918333b6331326ef6f1df6d4a09d48c4f4f9f128dc3d45b8`) - Website: - Support: - Privacy: - Terms: +- Availability: all regions offered by the submission portal, subject to + publisher eligibility. +- Prerequisite: Requires the separately installed free CodeTruss CLI. The + wrapper is MIT licensed; the CLI is source-visible under the CodeTruss CLI + Proprietary License. +- Publisher access: business/developer identity verification and Apps + Management write access must be completed in the publishing organization + before submission. +- Publisher alignment gate: metadata currently names `CodeTruss`. Submit only + from a verified business identity with that exact public operator name, or + first align the publisher metadata, website, support identity, privacy policy, + and terms to the verified individual identity. Long description: -> Bind agent changes to the task. Catch scope drift and AI slop with -> deterministic local checks, run your repository's own verification commands, -> and produce a verifiable receipt -> for AI-generated changes. CodeTruss works across coding agents. Deterministic -> analysis stays on your machine; provider review and receipt sync are explicit -> opt-ins. +> Declare the files a coding agent may change and the checks its work must pass. +> CodeTruss evaluates the captured Git state locally and returns a verifiable +> PASS, REVIEW_REQUIRED, or FAILED receipt before commit. It works across coding +> agents. Deterministic analysis stays on your machine; provider review and +> receipt sync are explicit opt-ins. Requires the separately installed free +> CodeTruss CLI; no CodeTruss account or upload is required for local use. + +Reviewer fixtures are generated with: + +```bash +node submission/fixtures/setup.mjs /tmp/codetruss-plugin-fixtures +``` + +The exact repository state and setup command for each positive test are in +`submission/fixtures/README.md`. ## Starter prompts @@ -41,7 +67,8 @@ Long description: verification config, install the Codex hook, and run the doctor. - Expected result: A configured repository and a reminder to approve the exact hook in `/hooks`; no LLM call, auth, or sync. -- Fixture: A clean Git repository with package scripts and CodeTruss installed. +- Fixture: `/tmp/codetruss-plugin-fixtures/configure-boundary`, generated by the + reviewer fixture command. CodeTruss CLI v0.2.14 or newer must be installed. ### Positive 2: review an existing working-tree change @@ -52,7 +79,7 @@ Long description: verify its integrity. - Expected result: Verdict first, explicit scope/sensitive/analyzer/verification reasons, receipt ID/path, and integrity status. -- Fixture: A Git repository with a changed file and valid `.codetruss.yml`. +- Fixture: `/tmp/codetruss-plugin-fixtures/review-working-tree`. ### Positive 3: review only staged changes @@ -60,7 +87,7 @@ Long description: - Expected behavior: Run `codetruss review --staged --task "Pre-commit review"`, preserve configured verification, inspect the receipt, and verify it. - Expected result: A staged-only verdict with reasons and receipt details. -- Fixture: A Git repository with different staged and unstaged changes. +- Fixture: `/tmp/codetruss-plugin-fixtures/review-staged`. ### Positive 4: diagnose an installed Claude hook @@ -71,7 +98,8 @@ Long description: a repair. - Expected result: Evidence-backed diagnosis that preserves existing settings and asks before any material rewrite. -- Fixture: A repository with a deliberately stale or malformed CodeTruss hook. +- Fixture: `/tmp/codetruss-plugin-fixtures/diagnose-claude-hook`, which contains + a deliberately stale Claude Stop-hook command. ### Positive 5: interpret and verify a receipt @@ -81,7 +109,8 @@ Long description: distinguish integrity evidence from trusted execution or authorship. - Expected result: Integrity status, verdict/reasons, limitations, and no compliance overclaim. -- Fixture: A repository containing a valid local receipt. +- Fixture: `/tmp/codetruss-plugin-fixtures/verify-receipt`; first run the exact + receipt-creation command in `submission/fixtures/README.md`. ## Negative tests @@ -116,7 +145,7 @@ Long description: ## Initial release notes -Initial skills-only CodeTruss integration for Codex and ChatGPT. It configures -and invokes the separately installed local CLI, preserves explicit network -consent, understands verdict exit codes, installs the existing Codex hook with -developer approval, and explains verifiable receipt limitations. +Initial skills-only CodeTruss integration for Codex. It configures and invokes +the separately installed local CLI, preserves explicit network consent, +understands verdict exit codes, installs the existing Codex hook with developer +approval, and explains verifiable receipt limitations.