From 9f26c5ae3d343a0248d0fffd70a81833fa536398 Mon Sep 17 00:00:00 2001 From: Eliott Jacopin Date: Wed, 16 Sep 2026 01:11:16 +0200 Subject: [PATCH 01/17] docs(roadmap): author the nest_migration campaign Four nodes at depth 1. relinquish_marketplace and regenerate_manifests share no files, so they are siblings; only the install check needs both. This campaign is fully gated on the playbook's agent_plugin_nest campaign reaching its end-to-end install verification. Relinquishing a working catalogue before its replacement is proven would leave users with neither, so the gate is recorded in the campaign pre-conditions rather than left to scheduling. Facts established while authoring: - Both marketplace files here declare the name cracking-shells, and the Claude one lists colgrep-mcp-dev alongside colgrep-mcp. Nest must carry both before either file is deleted. - .codex-plugin/ exists today, so the regenerate leaf's absence check is meaningful rather than vacuous. - regenerate_manifests needs --force, which is safe for put but makes merge_marketplace replace a marketplace wholesale. It is only safe here because relinquish_marketplace deletes both marketplace files first; the leaf says so and tells the implementer to stop if they are still present. - Three references pointed at skills/spawning-agent-plugins/ as if it lived in this repository. It does not: this repo has skills/colgrep-search and dev/skills/. They now point at the playbook's copy by URL. --- __roadmap__/nest_migration/README.md | 57 +++++++++++++++++++ .../nest_migration/regenerate_manifests.md | 37 ++++++++++++ .../nest_migration/relinquish_marketplace.md | 32 +++++++++++ __roadmap__/nest_migration/verify/README.md | 43 ++++++++++++++ .../nest_migration/verify/install_check.md | 34 +++++++++++ 5 files changed, 203 insertions(+) create mode 100644 __roadmap__/nest_migration/README.md create mode 100644 __roadmap__/nest_migration/regenerate_manifests.md create mode 100644 __roadmap__/nest_migration/relinquish_marketplace.md create mode 100644 __roadmap__/nest_migration/verify/README.md create mode 100644 __roadmap__/nest_migration/verify/install_check.md diff --git a/__roadmap__/nest_migration/README.md b/__roadmap__/nest_migration/README.md new file mode 100644 index 0000000..811557f --- /dev/null +++ b/__roadmap__/nest_migration/README.md @@ -0,0 +1,57 @@ +# Nest Migration + +## Context +colgrep-mcp currently owns the `cracking-shells` marketplace name in its own repository, listing +`colgrep-mcp` and the maintainer plugin `colgrep-mcp-dev`. `CrackingShells/Nest` is taking that name +over as the organisation's single catalogue. This campaign hands the name across and moves this +repo's manifests to the reshaped generator's output. It is **fully gated** on the +`agent_plugin_nest` campaign in the playbook: nothing here starts until Nest provably installs +plugins, because relinquishing a working catalogue before its replacement is proven would leave +users with neither. + +## Reference Documents +- [R01 Implementation Plan](~/.claude/plans/good-news-overall-it-s-gleaming-wreath.md) — the campaign gate and the Nest entry shape +- [R02 Traps](https://github.com/CrackingShells/cracking-shells-playbook/blob/main/skills/spawning-agent-plugins/references/traps.md) — marketplace naming and the hooks-duplicate install failure (lives in the playbook repo, not here) + +## Goal +colgrep-mcp ships no marketplace, points users at Nest, and carries manifests in the Agent-Plugins-plus-extensions shape. + +## Pre-conditions +- [ ] `agent_plugin_nest/generator/rollout/verify/end_to_end.md` is done — Nest installs playbook plugins for real +- [ ] Nest lists `colgrep-mcp` and `colgrep-mcp-dev`, so nothing is dropped when this repo stops listing them +- [ ] A playbook checkout containing the reshaped generator is available, and its path is named in the implementer's brief + +## Success Gates +- ✅ Neither `.claude-plugin/marketplace.json` nor `.agents/plugins/marketplace.json` exists [run] +- ✅ No `.codex-plugin/` directory remains [run] +- ✅ `check_plugin.py` reports no problems for this repo [run] +- ✅ README install snippets name `CrackingShells/Nest` and no longer name `CrackingShells/colgrep-mcp` [run] +- ✅ Installing colgrep-mcp from Nest connects the MCP server and fires the hooks [behavioral] + +## Status +```mermaid +graph TD + relinquish_marketplace[Relinquish Marketplace]:::planned + regenerate_manifests[Regenerate Manifests]:::planned + verify[Verification]:::planned + classDef done fill:#166534,color:#bbf7d0 + classDef inprogress fill:#854d0e,color:#fef08a + classDef planned fill:#374151,color:#e5e7eb + classDef amendment fill:#1e3a5f,color:#bfdbfe + classDef blocked fill:#7f1d1d,color:#fecaca +``` + +## Nodes +| Node | Type | Status | +|:-----|:-----|:-------| +| `relinquish_marketplace.md` | 📄 Leaf Task | ⬜ Planned | +| `regenerate_manifests.md` | 📄 Leaf Task | ⬜ Planned | +| `verify/` | 📁 Directory | ⬜ Planned | + +## Amendment Log +| ID | Date | Source | Nodes Added | Rationale | +|:---|:-----|:-------|:------------|:----------| + +## Progress +| Node | Branch | Commits | Notes | +|:-----|:-------|:--------|:------| diff --git a/__roadmap__/nest_migration/regenerate_manifests.md b/__roadmap__/nest_migration/regenerate_manifests.md new file mode 100644 index 0000000..01ce619 --- /dev/null +++ b/__roadmap__/nest_migration/regenerate_manifests.md @@ -0,0 +1,37 @@ +# Regenerate Manifests + +**Goal**: Move this repository's manifests to the reshaped generator's output — a root Agent-Plugins `plugin.json` carrying `extensions["com.openai"]`, with `.codex-plugin/` gone. +**Pre-conditions**: +- [ ] A playbook checkout containing the **reshaped** generator is available; its absolute path is named in the brief +- [ ] `agent_plugin_nest/generator/generator_reshape.md` is done and its regeneration guard is green on its new baseline +**Success Gates**: +- ⬜ No `.codex-plugin/` directory remains [run] +- ⬜ Root `plugin.json` carries `extensions["com.openai"]` with the full interface block [run] +- ⬜ `check_plugin.py` reports no problems [run] +- ⬜ `claude plugin validate .` passes [run] +- ⬜ The version in every manifest is still `0.5.1`; this leaf reshapes, it does not release [run] +**References**: [R01 §Decisions already settled](~/.claude/plans/good-news-overall-it-s-gleaming-wreath.md) — why the extensions namespace replaces `.codex-plugin/` + +## Step 1: Regenerate with the reshaped generator + +**Goal**: Produce the new manifest shape from the spec rather than by hand. + +**Implementation Logic**: +Run the reshaped `spawn_plugin.py --root . spawn --spec /skills/spawning-agent-plugins/assets/examples/colgrep-mcp.spec.json --force` from **the playbook checkout that contains the reshape**. Running the unextended generator instead would regenerate the old three-manifest shape and appear to succeed — the brief must name the path explicitly and the implementer should confirm the generator it invoked actually emits `extensions`, not `.codex-plugin/`. + +`--force` is required because the manifests already exist and `put` refuses to overwrite without it. `--force` is safe for `put`, but it also makes `merge_marketplace` replace a marketplace wholesale — harmless here only because `relinquish_marketplace` has already deleted both marketplace files. If they are still present, stop and report it rather than forcing. + +Afterwards, delete the `.codex-plugin/` directory, which the new generator no longer writes but also does not remove. +**Deliverables**: `plugin.json` — `extensions["com.openai"]` containing the interface block and the Codex hooks path; `.claude-plugin/plugin.json` unchanged in shape; deletion of `.codex-plugin/` +**Consistency Checks**: `test ! -d .codex-plugin && python3 -c "import json;d=json.load(open('plugin.json'));assert 'com.openai' in d['extensions']"` (expected: PASS) +**Commit**: `refactor(plugin): express Codex through the com.openai extensions namespace` + +## Step 2: Check the tree against itself + +**Goal**: Confirm the reshape left a self-consistent plugin, not just a changed one. + +**Implementation Logic**: +Run `check_plugin.py` and `claude plugin validate .` over the repo. Confirm the version is still `0.5.1` everywhere including the `uvx` pin in the MCP manifests — this leaf changes manifest *shape*, and a version move here would desynchronise the pin from the published artifact and break every launch until a release caught up. Do not point Claude's validator at anything but the Claude manifest and the repo root; it rejects the Codex `interface` block as an unknown field, which is a false alarm rather than a finding. +**Deliverables**: no new files — validator output recorded in the commit body +**Consistency Checks**: `python3 -c "import json,re;d=json.load(open('plugin.json'));assert d['version']=='0.5.1'"` (expected: PASS) +**Commit**: `test(plugin): verify the reshaped manifests are self-consistent` diff --git a/__roadmap__/nest_migration/relinquish_marketplace.md b/__roadmap__/nest_migration/relinquish_marketplace.md new file mode 100644 index 0000000..cc126f7 --- /dev/null +++ b/__roadmap__/nest_migration/relinquish_marketplace.md @@ -0,0 +1,32 @@ +# Relinquish Marketplace + +**Goal**: Stop this repository from declaring the `cracking-shells` marketplace, and point every install instruction at Nest instead. +**Pre-conditions**: +- [ ] Nest lists both `colgrep-mcp` and `colgrep-mcp-dev`, verified by reading Nest's marketplace files +- [ ] The playbook campaign's end-to-end install verification is done +**Success Gates**: +- ⬜ `.claude-plugin/marketplace.json` and `.agents/plugins/marketplace.json` no longer exist [run] +- ⬜ `.claude-plugin/plugin.json` and `.claude-plugin/mcp.json` still exist and are untouched [run] +- ⬜ No file in the repo still contains `marketplace add CrackingShells/colgrep-mcp` [run] +- ⬜ README carries a migration note telling existing users to remove the old marketplace first [run] +**References**: [R02 §namespaces](https://github.com/CrackingShells/cracking-shells-playbook/blob/main/skills/spawning-agent-plugins/references/traps.md#namespaces) — a client keeps the marketplace name it had at add time + +## Step 1: Delete the two marketplace files + +**Goal**: Leave exactly one repository in the organisation declaring `cracking-shells`. + +**Implementation Logic**: +Delete `.claude-plugin/marketplace.json` and `.agents/plugins/marketplace.json`. Delete only these two: `.claude-plugin/plugin.json` and `.claude-plugin/mcp.json` are this plugin's own manifests and must survive — a plugin stops *publishing a catalogue*, it does not stop being a plugin. Both files currently declare `name: "cracking-shells"`, and the Claude one lists `colgrep-mcp` plus `colgrep-mcp-dev`; confirm both names appear in Nest's catalogue before deleting, because after this commit nothing in this repository records that the dev plugin was ever published. +**Deliverables**: deletion of `.claude-plugin/marketplace.json` and `.agents/plugins/marketplace.json` +**Consistency Checks**: `test ! -f .claude-plugin/marketplace.json && test ! -f .agents/plugins/marketplace.json && test -f .claude-plugin/plugin.json` (expected: PASS) +**Commit**: `refactor(marketplace): hand the cracking-shells catalogue to CrackingShells/Nest` + +## Step 2: Repoint the install instructions + +**Goal**: Stop telling readers to add a marketplace this repo no longer serves. + +**Implementation Logic**: +Rewrite the README's install section so both harnesses add `CrackingShells/Nest` rather than `CrackingShells/colgrep-mcp`; the right-hand side of `colgrep-mcp@cracking-shells` is unchanged, because the marketplace name is the same — only its home moved. Add a migration note for existing users: a client keeps whichever marketplace it registered under a name at add time, so anyone who added `cracking-shells` from this repo must run `claude plugin marketplace remove cracking-shells` before adding Nest, or they will silently keep the old two-plugin catalogue. Sweep the whole repository, not just the README — the install snippet is reproduced in `dev/README.md` and may appear in reports. +**Deliverables**: `README.md` — install section repointed at Nest, new migration subsection; any other file carrying the old snippet +**Consistency Checks**: `test $(grep -rl "marketplace add CrackingShells/colgrep-mcp" . --exclude-dir=.git | wc -l) -eq 0` (expected: PASS) +**Commit**: `docs(readme): point installs at the Nest marketplace and note the migration` diff --git a/__roadmap__/nest_migration/verify/README.md b/__roadmap__/nest_migration/verify/README.md new file mode 100644 index 0000000..16562b7 --- /dev/null +++ b/__roadmap__/nest_migration/verify/README.md @@ -0,0 +1,43 @@ +# Verification + +## Context +Sits below both leaves as verification after parallel siblings: it needs the marketplace gone *and* +the manifests reshaped. Exercises the real marketplace-install path, which is the only one that +reproduces the hooks-duplicate class of failure. Produces the evidence that this repo's migration is +complete. + +## Goal +Prove colgrep-mcp installs from Nest with its server connected and its hooks firing. + +## Pre-conditions +- [ ] `relinquish_marketplace` and `regenerate_manifests` are both done and pushed +- [ ] The local `cracking-shells` registration already points at Nest + +## Success Gates +- ✅ `claude plugin list` shows `colgrep-mcp` with no `failed to load` line [run] +- ✅ `claude mcp list` reports the colgrep server connected [run] +- ✅ The search policy hook fires in a fresh session [behavioral] + +## Status +```mermaid +graph TD + install_check[Install Check]:::planned + classDef done fill:#166534,color:#bbf7d0 + classDef inprogress fill:#854d0e,color:#fef08a + classDef planned fill:#374151,color:#e5e7eb + classDef amendment fill:#1e3a5f,color:#bfdbfe + classDef blocked fill:#7f1d1d,color:#fecaca +``` + +## Nodes +| Node | Type | Status | +|:-----|:-----|:-------| +| `install_check.md` | 📄 Leaf Task | ⬜ Planned | + +## Amendment Log +| ID | Date | Source | Nodes Added | Rationale | +|:---|:-----|:-------|:------------|:----------| + +## Progress +| Node | Branch | Commits | Notes | +|:-----|:-------|:--------|:------| diff --git a/__roadmap__/nest_migration/verify/install_check.md b/__roadmap__/nest_migration/verify/install_check.md new file mode 100644 index 0000000..e56f2e5 --- /dev/null +++ b/__roadmap__/nest_migration/verify/install_check.md @@ -0,0 +1,34 @@ +# Install Check + +**Goal**: Prove colgrep-mcp installs from Nest with its server connected and its hooks firing, closing the migration. +**Pre-conditions**: +- [ ] `relinquish_marketplace` and `regenerate_manifests` are both merged and pushed — a git source resolves against the remote, not the worktree +- [ ] The local `cracking-shells` registration already points at `CrackingShells/Nest` +**Success Gates**: +- ⬜ `claude plugin list` shows `colgrep-mcp` with no `failed to load` line [run] +- ⬜ `claude mcp list` reports the colgrep server connected [run] +- ⬜ The search policy hook fires at the start of a fresh session [behavioral] +- ⬜ `colgrep-mcp-dev` is installable from Nest as well [run] +**References**: [R02 §hooks-manifest-duplicate](https://github.com/CrackingShells/cracking-shells-playbook/blob/main/skills/spawning-agent-plugins/references/traps.md#hooks-manifest-duplicate) — why only a marketplace install reproduces this failure + +## Step 1: Install from Nest and read the load state + +**Goal**: Exercise the path that no tree-level command reproduces. + +**Implementation Logic**: +Reinstall `colgrep-mcp@cracking-shells` from Nest and read `claude plugin list`. This is the only path that surfaces "Duplicate hooks file detected": `--plugin-dir`, `plugin details` and `plugin validate` all accept a tree that fails a real marketplace install, and this repo has hit exactly that failure before, at 0.4.0 and 0.5.0. Install `colgrep-mcp-dev` too — it moved catalogues in this migration and has never been installed from Nest. + +Then confirm the server actually starts with `claude mcp list`, and open a fresh session to confirm the search policy hook fires. A plugin can load with its hooks silently inert, which is the failure this check exists to catch. +**Deliverables**: no files — observed outputs recorded in the commit body +**Consistency Checks**: `claude plugin list 2>&1 | grep -c "failed to load" | grep -qx 0` (expected: PASS) +**Commit**: `test(plugin): verify colgrep-mcp installs from the Nest marketplace` + +## Step 2: Record what the migration proved + +**Goal**: Close the campaign with an honest account of what was exercised. + +**Implementation Logic**: +Write a findings report under `__reports__/nest_migration/`. Separate what was exercised live in Claude Code — marketplace install, server connection, hooks firing — from what was only validated statically. No Codex CLI is available here, so every Codex claim rests on the published manifest format and the loader source, and must be labelled as such; `manifests.md` already keeps a verification-status table that draws this line, and it must not blur now that the catalogue has moved. Note any user-visible migration step still outstanding, in particular that existing clients keep the old marketplace until they remove it by hand. +**Deliverables**: `__reports__/nest_migration/00-findings_migration_v0.md` — sections Exercised, Validated only, Outstanding user actions +**Consistency Checks**: `test -f __reports__/nest_migration/00-findings_migration_v0.md` (expected: PASS) +**Commit**: `docs(reports): record the Nest migration outcome and its limits` From 37c94df6ca396d30c61ca1580486033e425f458d Mon Sep 17 00:00:00 2001 From: Eliott Jacopin Date: Wed, 16 Sep 2026 08:41:09 +0200 Subject: [PATCH 02/17] fix(roadmap): make regenerate_manifests set hub mode before regenerating Regenerating with the unchanged spec would write both marketplace files back, silently undoing relinquish_marketplace and restoring the two-owner collision this migration exists to remove. Switching the spec to hub mode first removes the hazard and makes the two leaves genuinely order-independent, which is what their sibling placement already claimed. The campaign gate is now stated per leaf: regenerate_manifests needs only the reshaped generator, while relinquish_marketplace waits for Nest to be proven. --- __roadmap__/nest_migration/README.md | 3 +- .../nest_migration/regenerate_manifests.md | 34 ++++++++++++------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/__roadmap__/nest_migration/README.md b/__roadmap__/nest_migration/README.md index 811557f..8d12dc1 100644 --- a/__roadmap__/nest_migration/README.md +++ b/__roadmap__/nest_migration/README.md @@ -17,7 +17,8 @@ users with neither. colgrep-mcp ships no marketplace, points users at Nest, and carries manifests in the Agent-Plugins-plus-extensions shape. ## Pre-conditions -- [ ] `agent_plugin_nest/generator/rollout/verify/end_to_end.md` is done — Nest installs playbook plugins for real +- [ ] `relinquish_marketplace` waits on `agent_plugin_nest/generator/rollout/verify/end_to_end.md` — Nest must install playbook plugins for real before this repo gives up a working catalogue +- [ ] `regenerate_manifests` waits only on `agent_plugin_nest/generator/generator_reshape.md`, so it can start earlier than its sibling - [ ] Nest lists `colgrep-mcp` and `colgrep-mcp-dev`, so nothing is dropped when this repo stops listing them - [ ] A playbook checkout containing the reshaped generator is available, and its path is named in the implementer's brief diff --git a/__roadmap__/nest_migration/regenerate_manifests.md b/__roadmap__/nest_migration/regenerate_manifests.md index 01ce619..970dd42 100644 --- a/__roadmap__/nest_migration/regenerate_manifests.md +++ b/__roadmap__/nest_migration/regenerate_manifests.md @@ -1,37 +1,47 @@ # Regenerate Manifests -**Goal**: Move this repository's manifests to the reshaped generator's output — a root Agent-Plugins `plugin.json` carrying `extensions["com.openai"]`, with `.codex-plugin/` gone. +**Goal**: Move this repository's manifests to the reshaped generator's output — a root Agent-Plugins `plugin.json` carrying `extensions["com.openai"]`, with `.codex-plugin/` gone and no marketplace written back. **Pre-conditions**: +- [ ] `agent_plugin_nest/generator/generator_reshape.md` is merged and its regeneration guard is green on the new baseline - [ ] A playbook checkout containing the **reshaped** generator is available; its absolute path is named in the brief -- [ ] `agent_plugin_nest/generator/generator_reshape.md` is done and its regeneration guard is green on its new baseline **Success Gates**: - ⬜ No `.codex-plugin/` directory remains [run] - ⬜ Root `plugin.json` carries `extensions["com.openai"]` with the full interface block [run] +- ⬜ Regenerating writes no marketplace file into this repository [run] - ⬜ `check_plugin.py` reports no problems [run] -- ⬜ `claude plugin validate .` passes [run] - ⬜ The version in every manifest is still `0.5.1`; this leaf reshapes, it does not release [run] **References**: [R01 §Decisions already settled](~/.claude/plans/good-news-overall-it-s-gleaming-wreath.md) — why the extensions namespace replaces `.codex-plugin/` -## Step 1: Regenerate with the reshaped generator +## Step 1: Switch the spec to hub mode -**Goal**: Produce the new manifest shape from the spec rather than by hand. +**Goal**: Stop the spec from declaring a marketplace, so regeneration cannot resurrect one. **Implementation Logic**: -Run the reshaped `spawn_plugin.py --root . spawn --spec /skills/spawning-agent-plugins/assets/examples/colgrep-mcp.spec.json --force` from **the playbook checkout that contains the reshape**. Running the unextended generator instead would regenerate the old three-manifest shape and appear to succeed — the brief must name the path explicitly and the implementer should confirm the generator it invoked actually emits `extensions`, not `.codex-plugin/`. +The spec still carries `claude_marketplace` and `codex.marketplace_name`, which is what made this repository the owner of the `cracking-shells` name. Replace them with the generator's hub mode, so `spawn` writes no `.claude-plugin/marketplace.json` and no `.agents/plugins/marketplace.json`. + +This step exists because of an ordering hazard, and skipping it is silent: regenerating with the old spec writes both marketplace files back, undoing `relinquish_marketplace` without any error, and leaving two repositories declaring one marketplace name again — the exact condition this whole migration removes. With hub mode set, the two leaves become genuinely order-independent. +**Deliverables**: the spec consumed by `spawn` (the playbook's `assets/examples/colgrep-mcp.spec.json`, or a repo-local copy if the implementer prefers not to edit the skill's example) — `claude_marketplace` and `codex.marketplace_name` replaced by the hub-mode key +**Consistency Checks**: `test ! -f .claude-plugin/marketplace.json || echo "marketplace still present - relinquish has not run yet, which is allowed"` (expected: PASS) +**Commit**: `refactor(plugin): declare hub mode so no marketplace is generated here` + +## Step 2: Regenerate with the reshaped generator -`--force` is required because the manifests already exist and `put` refuses to overwrite without it. `--force` is safe for `put`, but it also makes `merge_marketplace` replace a marketplace wholesale — harmless here only because `relinquish_marketplace` has already deleted both marketplace files. If they are still present, stop and report it rather than forcing. +**Goal**: Produce the new manifest shape from the spec rather than by hand. + +**Implementation Logic**: +Run the reshaped `spawn_plugin.py --root . spawn --spec --force` from **the playbook checkout that contains the reshape**. Running the unextended generator would regenerate the old three-manifest shape and appear to succeed; the brief must name the path explicitly, and the implementer should confirm the generator it invoked actually emits `extensions` rather than `.codex-plugin/` before trusting the result. -Afterwards, delete the `.codex-plugin/` directory, which the new generator no longer writes but also does not remove. +`--force` is required because the manifests already exist and `put` refuses to overwrite without it. With hub mode set in step 1 there is no `merge_marketplace` call for `--force` to turn destructive, which is the whole reason step 1 comes first. Afterwards delete the `.codex-plugin/` directory: the new generator no longer writes it, but it does not remove what an earlier run left behind. **Deliverables**: `plugin.json` — `extensions["com.openai"]` containing the interface block and the Codex hooks path; `.claude-plugin/plugin.json` unchanged in shape; deletion of `.codex-plugin/` **Consistency Checks**: `test ! -d .codex-plugin && python3 -c "import json;d=json.load(open('plugin.json'));assert 'com.openai' in d['extensions']"` (expected: PASS) **Commit**: `refactor(plugin): express Codex through the com.openai extensions namespace` -## Step 2: Check the tree against itself +## Step 3: Check the tree against itself -**Goal**: Confirm the reshape left a self-consistent plugin, not just a changed one. +**Goal**: Confirm the reshape left a self-consistent plugin, not merely a changed one. **Implementation Logic**: -Run `check_plugin.py` and `claude plugin validate .` over the repo. Confirm the version is still `0.5.1` everywhere including the `uvx` pin in the MCP manifests — this leaf changes manifest *shape*, and a version move here would desynchronise the pin from the published artifact and break every launch until a release caught up. Do not point Claude's validator at anything but the Claude manifest and the repo root; it rejects the Codex `interface` block as an unknown field, which is a false alarm rather than a finding. +Run `check_plugin.py` and `claude plugin validate .` over the repo. Confirm the version is still `0.5.1` everywhere including the `uvx` pin in the MCP manifests — this leaf changes manifest *shape*, and a version move here would desynchronise the pin from the published artifact and break every launch until a release caught up. Confirm no marketplace file reappeared. Do not point Claude's validator at anything but the Claude manifest and the repo root; it rejects the Codex `interface` block as an unknown field, which is a false alarm rather than a finding. **Deliverables**: no new files — validator output recorded in the commit body -**Consistency Checks**: `python3 -c "import json,re;d=json.load(open('plugin.json'));assert d['version']=='0.5.1'"` (expected: PASS) +**Consistency Checks**: `python3 -c "import json;d=json.load(open('plugin.json'));assert d['version']=='0.5.1'" && test ! -f .claude-plugin/marketplace.json` (expected: PASS) **Commit**: `test(plugin): verify the reshaped manifests are self-consistent` From a217164a6b77dc6a19e864a0c56f32014e505256 Mon Sep 17 00:00:00 2001 From: Eliott Jacopin Date: Wed, 16 Sep 2026 13:14:48 +0200 Subject: [PATCH 03/17] chore(roadmap): open regenerate_manifests and block the gated siblings Level 0 status, set through dirtree-rdm only. `regenerate_manifests` is in progress: its gate is open. The playbook's `generator_reshape` leaf is merged at d7e2ac8 on roadmap/agent-plugin-nest, and the generator on disk there carries the reshape (references com.openai, none to codex-plugin) rather than merely claiming it in a status table. `relinquish_marketplace` is blocked, not planned. Its gate is the playbook's `generator/rollout/verify/end_to_end` leaf, which is still planned, as are both rollout leaves above it. Concretely: the playbook has no plugins/ directory, so the five git-subdir paths CrackingShells/Nest points at do not resolve, and Nest itself is unpushed. Nothing can install from Nest today, so this repository cannot yet give up a working catalogue. `verify/` is blocked behind both siblings. Co-Authored-By: Claude Opus 5 --- __roadmap__/nest_migration/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/__roadmap__/nest_migration/README.md b/__roadmap__/nest_migration/README.md index 8d12dc1..5d20388 100644 --- a/__roadmap__/nest_migration/README.md +++ b/__roadmap__/nest_migration/README.md @@ -32,9 +32,9 @@ colgrep-mcp ships no marketplace, points users at Nest, and carries manifests in ## Status ```mermaid graph TD - relinquish_marketplace[Relinquish Marketplace]:::planned - regenerate_manifests[Regenerate Manifests]:::planned - verify[Verification]:::planned + relinquish_marketplace[Relinquish Marketplace]:::blocked + regenerate_manifests[Regenerate Manifests]:::inprogress + verify[Verification]:::blocked classDef done fill:#166534,color:#bbf7d0 classDef inprogress fill:#854d0e,color:#fef08a classDef planned fill:#374151,color:#e5e7eb @@ -45,9 +45,9 @@ graph TD ## Nodes | Node | Type | Status | |:-----|:-----|:-------| -| `relinquish_marketplace.md` | 📄 Leaf Task | ⬜ Planned | -| `regenerate_manifests.md` | 📄 Leaf Task | ⬜ Planned | -| `verify/` | 📁 Directory | ⬜ Planned | +| `relinquish_marketplace.md` | 📄 Leaf Task | 🚫 Blocked | +| `regenerate_manifests.md` | 📄 Leaf Task | 🔄 In Progress | +| `verify/` | 📁 Directory | 🚫 Blocked | ## Amendment Log | ID | Date | Source | Nodes Added | Rationale | From 987e52737edb73d32baab80eec615ce39e252bdd Mon Sep 17 00:00:00 2001 From: Eliott Jacopin Date: Wed, 16 Sep 2026 12:39:58 +0200 Subject: [PATCH 04/17] refactor(plugin): declare hub mode so no marketplace is generated here MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add top-level "marketplace": "hub" to a repo-local copy of the generator spec, colgrep-mcp.spec.json, so `spawn_plugin.py spawn` never calls merge_marketplace and this repo stops resurrecting either marketplace file once regenerated (spawn_plugin.py:608-616 keys hub mode off this one truthy field, per SKILL.md's "Does another repo own the marketplace name?" row and references/manifests.md's hub-mode section). Deviates from the leaf spec's Step 1 in two ways, both directed by the coordinator: 1. The spec lives at repo root (colgrep-mcp.spec.json), not at the playbook's assets/examples/colgrep-mcp.spec.json. That file is the fixture for the playbook's own regeneration guard (skills/spawning-agent-plugins/evals/test_regeneration.py) and is out of scope for this migration; editing it in place would re-baseline someone else's tripwire from inside this repo. This repo now carries its own spec, and the playbook's example has diverged from it. 2. claude_marketplace.name and codex.marketplace_name are KEPT at "cracking-shells" rather than dropped, contrary to references/manifests.md's general hub-mode guidance ("a spec bound for a hub carries no claude_marketplace or codex.marketplace_name section at all"). Hub mode is keyed only off the top-level `marketplace` field (spawn_plugin.py:612), but `claude_marketplace.name` is separately read by dev_readme() at spawn_plugin.py:599/601 to render dev/README.md's install snippet, and by install_snippet() at spawn_plugin.py:744, both falling back to "colgrep-mcp-marketplace" when the key is absent. Dropping the name fields would silently rewrite dev/README.md to advertise colgrep-mcp-dev@colgrep-mcp-marketplace, a marketplace that does not exist. The marketplace name itself is not moving in this campaign, only its home (to CrackingShells/Nest), so the fields stay as documentation-only inputs. Consistency check: $ test ! -f .claude-plugin/marketplace.json || echo "marketplace still present - relinquish has not run yet, which is allowed" marketplace still present - relinquish has not run yet, which is allowed (PASS — relinquish_marketplace is a separate, currently-gated leaf.) Co-Authored-By: Claude Sonnet 5 --- colgrep-mcp.spec.json | 95 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 colgrep-mcp.spec.json diff --git a/colgrep-mcp.spec.json b/colgrep-mcp.spec.json new file mode 100644 index 0000000..7dd7e1e --- /dev/null +++ b/colgrep-mcp.spec.json @@ -0,0 +1,95 @@ +{ + "name": "colgrep-mcp", + "displayName": "colgrep", + "description": "Semantic and hybrid code search for coding agents, exposed as MCP tools backed by colgrep.", + "author": { + "name": "Eliott Jacopin", + "email": "eliott.jacopin@riken.jp" + }, + "homepage": "https://github.com/CrackingShells/colgrep-mcp", + "repository": "https://github.com/CrackingShells/colgrep-mcp", + "license": "AGPL-3.0-or-later", + "keywords": [ + "mcp", + "colgrep", + "semantic-search", + "code-search", + "agents" + ], + "version_from": "pyproject:server/pyproject.toml", + "marketplace": "hub", + "ecosystems": [ + "claude", + "codex", + "agent-plugins" + ], + "skills": "./skills/", + "mcp": { + "server": "colgrep", + "command": "uvx", + "args": [ + "colgrep-mcp=={version}" + ], + "claude_env": { + "COLGREP_MCP_ROOT": "${CLAUDE_PROJECT_DIR}" + } + }, + "hooks": { + "script": "hooks/colgrep_policy.py", + "codex": true, + "portable_description": "colgrep-mcp: state the search policy at session and subagent start; deny the built-in Grep tool and shell corpus searches in favour of the colgrep MCP tools. Only events every hook-capable harness (Claude Code, Codex, Cursor) understands live here, in the one file both Claude Code and Codex load by default; an event not every harness knows gets its own file named after it (worktree-remove.json).", + "portable": [ + { + "event": "SessionStart", + "statusMessage": "Loading the colgrep search policy" + }, + { + "event": "SubagentStart", + "statusMessage": "Loading the colgrep search policy" + }, + { + "event": "PreToolUse", + "matcher": "Grep|Bash" + } + ], + "extra": [ + { + "event": "WorktreeRemove", + "timeout": 90, + "description": "colgrep-mcp: the WorktreeRemove event, which only Claude Code fires. One file per event that not every hook-capable harness knows, named after the event; named from .claude-plugin/plugin.json only, never from the Codex manifest, so a parser that knows no WorktreeRemove never sees it. Clears the colgrep index a removed worktree owned (never one it was folded into)." + } + ] + }, + "codex": { + "marketplace_name": "cracking-shells", + "shortDescription": "Semantic and hybrid code search for coding agents, backed by colgrep.", + "longDescription": "Exposes colgrep's semantic and hybrid (grep + ranking) code search as MCP tools, resources and prompts, so agents search code by meaning instead of shelling out to grep. Supports 18+ languages and incremental indexing, with agent-facing usage guidance via a bundled skill.", + "category": "Developer Tools", + "capabilities": [ + "Read" + ], + "defaultPrompt": [ + "Search this repo for the authentication logic.", + "Find where errors are handled for database connections.", + "Show the index status for this project." + ], + "marketplace_displayName": "CrackingShells" + }, + "claude_marketplace": { + "name": "cracking-shells", + "description": "CrackingShells marketplace. Here: colgrep-mcp, the product plugin (semantic and hybrid code search as MCP tools), and colgrep-mcp-dev, the maintainer's plugin of skills." + }, + "dev": { + "dir": "dev", + "name": "colgrep-mcp-dev", + "displayName": "colgrep-mcp maintainer skills", + "description": "The maintainer's dev environment for colgrep-mcp as skills: policy, campaign leadership, landing and release mechanics, stack traps. For agents maintaining this repository, not for end users of the colgrep tools.", + "marketplace_description": "Maintainer skills for this repository (policy, campaign leadership, landing and release, stack traps). Not for end users.", + "keywords": [ + "colgrep-mcp", + "maintainer", + "skills", + "agents" + ] + } +} From 2a0f9fd702ce90383d6e093a595800859c8d36c3 Mon Sep 17 00:00:00 2001 From: Eliott Jacopin Date: Wed, 16 Sep 2026 12:41:03 +0200 Subject: [PATCH 05/17] refactor(plugin): express Codex through the com.openai extensions namespace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regenerate the manifests from the repo-local spec with the reshaped generator, then delete .codex-plugin/ (the generator no longer writes it but does not remove what an earlier run left behind). Command run, from this worktree, invoking the checkout on roadmap/agent-plugin-nest (the branch containing the reshape): python3 /Users/.../cracking-shells-playbook/skills/spawning-agent-plugins/scripts/spawn_plugin.py \ --root . spawn --spec colgrep-mcp.spec.json --force Confirmed before trusting it that this checkout is the reshaped generator, not the unextended one that would silently regenerate the old three-manifest shape: `grep -n "com.openai" spawn_plugin.py` matches (6 lines, including build_codex_extensions and the extensions["com.openai"] assignment in build_agent_plugin), and `grep -n "codex-plugin" spawn_plugin.py` matches nothing. Output: wrote plugin.json wrote mcp.json wrote .claude-plugin/plugin.json wrote .claude-plugin/mcp.json wrote hooks/hooks.json wrote hooks/worktree-remove.json wrote dev/.claude-plugin/plugin.json wrote dev/README.md No "merged"/"would merge" line for either marketplace file, confirming hub mode suppressed both. Diff summary: - plugin.json: gains extensions["com.openai"] (interface block + hooks pointer to ./hooks/hooks.json); identity fields unchanged. - mcp.json, .claude-plugin/mcp.json, .claude-plugin/plugin.json, dev/.claude-plugin/plugin.json: pretty-printed keywords/args arrays only (json.dumps(indent=2) vs. the hand-authored single-line arrays); no field changes. - hooks/hooks.json, hooks/worktree-remove.json: byte-identical, so the writer skipped them (no diff in git status). - dev/README.md: regenerated from the generator's dev_readme() template; still names "cracking-shells" (`claude plugin install colgrep-mcp-dev@cracking-shells`), confirmed by hand-diffing against the pre-regeneration file. Loses a few hand-written sentences (CONTRIBUTING mention, evals/ cases) the template does not carry — cosmetic, not a shape change, and within this leaf's scope of regenerating from the spec. - .agents/plugins/marketplace.json, .claude-plugin/marketplace.json: untouched (git status shows neither), as hub mode requires. - .codex-plugin/: deleted (mcp.json, plugin.json), the generator's own authoritative shape no longer writes this directory. Consistency check: $ test ! -d .codex-plugin && python3 -c "import json;d=json.load(open('plugin.json'));assert 'com.openai' in d['extensions']" CONSISTENCY CHECK PASS Version confirmed unchanged at 0.5.1 in every regenerated manifest and in the uvx pin (mcp.json, .claude-plugin/mcp.json both still pin colgrep-mcp==0.5.1); resolve_version() read it from server/pyproject.toml via version_from, never bumping it. Co-Authored-By: Claude Sonnet 5 --- .claude-plugin/mcp.json | 4 +++- .claude-plugin/plugin.json | 8 +++++++- .codex-plugin/mcp.json | 8 -------- .codex-plugin/plugin.json | 27 --------------------------- dev/.claude-plugin/plugin.json | 7 ++++++- dev/README.md | 17 +++++++---------- mcp.json | 4 +++- plugin.json | 28 +++++++++++++++++++++++++++- 8 files changed, 53 insertions(+), 50 deletions(-) delete mode 100644 .codex-plugin/mcp.json delete mode 100644 .codex-plugin/plugin.json diff --git a/.claude-plugin/mcp.json b/.claude-plugin/mcp.json index 5a84d14..fcb70c5 100644 --- a/.claude-plugin/mcp.json +++ b/.claude-plugin/mcp.json @@ -2,7 +2,9 @@ "mcpServers": { "colgrep": { "command": "uvx", - "args": ["colgrep-mcp==0.5.1"], + "args": [ + "colgrep-mcp==0.5.1" + ], "env": { "COLGREP_MCP_ROOT": "${CLAUDE_PROJECT_DIR}" } diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index f94dfe8..ed8f952 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -10,7 +10,13 @@ "homepage": "https://github.com/CrackingShells/colgrep-mcp", "repository": "https://github.com/CrackingShells/colgrep-mcp", "license": "AGPL-3.0-or-later", - "keywords": ["mcp", "colgrep", "semantic-search", "code-search", "agents"], + "keywords": [ + "mcp", + "colgrep", + "semantic-search", + "code-search", + "agents" + ], "mcpServers": "./.claude-plugin/mcp.json", "hooks": "./hooks/worktree-remove.json", "skills": "./skills/" diff --git a/.codex-plugin/mcp.json b/.codex-plugin/mcp.json deleted file mode 100644 index 54631b6..0000000 --- a/.codex-plugin/mcp.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "mcpServers": { - "colgrep": { - "command": "uvx", - "args": ["colgrep-mcp==0.5.1"] - } - } -} diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json deleted file mode 100644 index a889ece..0000000 --- a/.codex-plugin/plugin.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "colgrep-mcp", - "version": "0.5.1", - "description": "Semantic and hybrid code search for coding agents, exposed as MCP tools backed by colgrep.", - "author": { - "name": "Eliott Jacopin", - "email": "eliott.jacopin@riken.jp" - }, - "repository": "https://github.com/CrackingShells/colgrep-mcp", - "keywords": ["mcp", "colgrep", "semantic-search", "code-search", "agents"], - "skills": "./skills/", - "mcpServers": "./.codex-plugin/mcp.json", - "hooks": "./hooks/hooks.json", - "interface": { - "displayName": "colgrep", - "shortDescription": "Semantic and hybrid code search for coding agents, backed by colgrep.", - "longDescription": "Exposes colgrep's semantic and hybrid (grep + ranking) code search as MCP tools, resources and prompts, so agents search code by meaning instead of shelling out to grep. Supports 18+ languages and incremental indexing, with agent-facing usage guidance via a bundled skill.", - "developerName": "Eliott Jacopin", - "category": "Developer Tools", - "capabilities": ["Read"], - "defaultPrompt": [ - "Search this repo for the authentication logic.", - "Find where errors are handled for database connections.", - "Show the index status for this project." - ] - } -} diff --git a/dev/.claude-plugin/plugin.json b/dev/.claude-plugin/plugin.json index f63ab18..ebf2247 100644 --- a/dev/.claude-plugin/plugin.json +++ b/dev/.claude-plugin/plugin.json @@ -10,6 +10,11 @@ "homepage": "https://github.com/CrackingShells/colgrep-mcp", "repository": "https://github.com/CrackingShells/colgrep-mcp", "license": "AGPL-3.0-or-later", - "keywords": ["colgrep-mcp", "maintainer", "skills", "agents"], + "keywords": [ + "colgrep-mcp", + "maintainer", + "skills", + "agents" + ], "skills": "./skills/" } diff --git a/dev/README.md b/dev/README.md index c035995..55632a4 100644 --- a/dev/README.md +++ b/dev/README.md @@ -1,18 +1,15 @@ # colgrep-mcp-dev The maintainer's dev environment for this repository, packaged as a Claude Code -plugin of skills. Human projects say "install the dev environment" in -CONTRIBUTING; here the dev environment is knowledge, and this is how an agent -installs it: +plugin of skills. Here the dev environment is knowledge, and this is how an +agent installs it: ```bash -claude --plugin-dir ./dev # from a clone -claude plugin install colgrep-mcp-dev@cracking-shells # from the repo's own marketplace +claude --plugin-dir ./dev # from a clone +claude plugin install colgrep-mcp-dev@cracking-shells # from the repo's own marketplace ``` The product plugin (`colgrep-mcp`, repository root) never ships these skills; -end users of the search tools have no use for them. `AGENTS.md` lists each -skill and when it fires. Each skill follows skill-creator's progressive -disclosure: a short `SKILL.md` whose description says when to load it, -`references/` for depth, `scripts/` for mechanical steps, `evals/` cases -(under `dev/evals/`) for triggering checks. +end users have no use for them. `AGENTS.md` lists each skill and when it fires. +Each skill follows progressive disclosure: a short `SKILL.md` whose description +says when to load it, `references/` for depth, `scripts/` for mechanical steps. diff --git a/mcp.json b/mcp.json index 0abc598..1cebb45 100644 --- a/mcp.json +++ b/mcp.json @@ -4,7 +4,9 @@ "colgrep": { "type": "stdio", "command": "uvx", - "args": ["colgrep-mcp==0.5.1"] + "args": [ + "colgrep-mcp==0.5.1" + ] } } } diff --git a/plugin.json b/plugin.json index 710a5f5..dc54500 100644 --- a/plugin.json +++ b/plugin.json @@ -10,5 +10,31 @@ "homepage": "https://github.com/CrackingShells/colgrep-mcp", "repository": "https://github.com/CrackingShells/colgrep-mcp", "license": "AGPL-3.0-or-later", - "keywords": ["mcp", "colgrep", "semantic-search", "code-search", "agents"] + "keywords": [ + "mcp", + "colgrep", + "semantic-search", + "code-search", + "agents" + ], + "extensions": { + "com.openai": { + "interface": { + "displayName": "colgrep", + "shortDescription": "Semantic and hybrid code search for coding agents, backed by colgrep.", + "longDescription": "Exposes colgrep's semantic and hybrid (grep + ranking) code search as MCP tools, resources and prompts, so agents search code by meaning instead of shelling out to grep. Supports 18+ languages and incremental indexing, with agent-facing usage guidance via a bundled skill.", + "developerName": "Eliott Jacopin", + "category": "Developer Tools", + "capabilities": [ + "Read" + ], + "defaultPrompt": [ + "Search this repo for the authentication logic.", + "Find where errors are handled for database connections.", + "Show the index status for this project." + ] + }, + "hooks": "./hooks/hooks.json" + } + } } From 54043686166fe25fab6cad6150d7fdabf111fd9f Mon Sep 17 00:00:00 2001 From: Eliott Jacopin Date: Wed, 16 Sep 2026 12:47:43 +0200 Subject: [PATCH 06/17] test(plugin): verify the reshaped manifests are self-consistent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the drift guards that hard-coded the retired three-manifest shape, and the commitizen version_files list that would otherwise try to rewrite a version string into two files .codex-plugin/ no longer contains. Guards updated, and why (each encoded ".codex-plugin/{plugin,mcp}.json" as a real file rather than as one of Codex's checks against the shared root manifest): - server/tests/test_manifests.py: MCP_MANIFESTS dropped ".codex-plugin/mcp.json"; test_versions_aligned, test_names_aligned, test_placeholders_only_in_claude_code_env and test_plugin_manifests_point_at_their_own_mcp_file no longer read ".codex-plugin/plugin.json" (Codex now shares plugin.json/mcp.json with Agent Plugins 1.0, already covered by the assertions against those files). Added test_codex_extension_carries_the_interface_block, ported from check_plugin.py's _check_codex_interface, so this repo's own guard suite pins the extensions["com.openai"].interface shape directly instead of relying only on the playbook's external checker. - server/tests/test_version.py: same rationale for test_manifests_match_pyproject and test_mcp_manifests_pin_the_pyproject_version. - server/tests/test_hooks.py: test_manifests_name_the_hook_files_per_ecosystem now reads Codex's hooks pointer from plugin.json's extensions["com.openai"].hooks instead of a nonexistent .codex-plugin/plugin.json. - server/pyproject.toml: [tool.commitizen].version_files dropped "../.codex-plugin/plugin.json:\"version\"" and "../.codex-plugin/mcp.json:colgrep-mcp==". These are not test code, but belong in this step for the same reason: cz bump would otherwise try to rewrite a version string into two files that no longer exist. Caught by running `cz bump --dry-run` after Step 2 and noticing it needed verifying, not by a failing test (commitizen does not appear to validate version_files entries during --dry-run, so this would have first surfaced at a real release). Gate output, run from this worktree: $ cd server && uv run pytest 307 passed, 1 skipped, 21 warnings in 16.48s $ cd server && uv run ruff check All checks passed! $ cd server && uv run ruff format --check 46 files already formatted $ cd server && uv run cz check --rev-range roadmap/nest-migration..HEAD Commit validation: successful! $ cd server && uv run cz bump --dry-run release(colgrep-mcp): v0.5.2 tag to create: v0.5.2 increment detected: PATCH (dry run only; server/pyproject.toml was not bumped, version stays 0.5.1) $ claude plugin validate . Validating marketplace manifest: .../.claude-plugin/marketplace.json Validation passed $ claude plugin validate ./dev Validating plugin manifest: .../dev/.claude-plugin/plugin.json Validation passed $ python3 .../cracking-shells-playbook/skills/spawning-agent-plugins/scripts/check_plugin.py --root . --spec colgrep-mcp.spec.json - .agents/plugins/marketplace.json: colgrep-mcp policy.authentication is invalid: 'NONE' 1 problem(s) Version confirmed 0.5.1 in plugin.json, .claude-plugin/plugin.json, dev/.claude-plugin/plugin.json, and the uvx pin in mcp.json and .claude-plugin/mcp.json. No marketplace file reappeared: git status shows neither .claude-plugin/marketplace.json nor .agents/plugins/marketplace.json touched by this branch. DEFECT, not fixed here: the check_plugin.py problem above and the leaf spec's own Step 3 consistency check (`test ! -f .claude-plugin/marketplace.json`, expected PASS) both fail, and for the same reason — the pre-existing .agents/plugins/marketplace.json (with its known "NONE" authentication bug) and .claude-plugin/marketplace.json are both still present, because relinquish_marketplace, the sibling leaf that retires them, has not run yet. This brief explicitly directs leaving both files alone ("Hub mode means the generator won't rewrite them; that is the expected, correct outcome for this leaf") and the campaign README states regenerate_manifests "can start earlier than its sibling" relinquish_marketplace. Both statements are correct, and together they guarantee the Step 3 consistency check and success-gate 4 ("check_plugin.py reports no problems") cannot pass whenever this leaf runs before relinquish_marketplace — which the roadmap explicitly permits. Reported to the coordinator rather than worked around by touching a file this leaf was told not to touch. Co-Authored-By: Claude Sonnet 5 --- server/pyproject.toml | 10 ++++--- server/tests/test_hooks.py | 14 +++++----- server/tests/test_manifests.py | 49 +++++++++++++++++++++++----------- server/tests/test_version.py | 14 +++++++--- 4 files changed, 57 insertions(+), 30 deletions(-) diff --git a/server/pyproject.toml b/server/pyproject.toml index fb07c2d..f0bb504 100644 --- a/server/pyproject.toml +++ b/server/pyproject.toml @@ -57,16 +57,18 @@ update_changelog_on_bump = true changelog_incremental = true changelog_file = "../CHANGELOG.md" version_files = [ + # Codex parses this same root manifest natively (extras under + # extensions["com.openai"]); it has no plugin.json of its own since + # nest_migration/regenerate_manifests retired .codex-plugin/. "../plugin.json:\"version\"", "../.claude-plugin/plugin.json:\"version\"", - "../.codex-plugin/plugin.json:\"version\"", "../dev/.claude-plugin/plugin.json:\"version\"", - # The three MCP manifests launch `uvx colgrep-mcp==`: the pin is + # The two MCP manifests launch `uvx colgrep-mcp==`: the pin is # the plugin version by construction, and moving it changes uvx's cache # key so a plugin update never reuses a stale server environment - # (pypi_publication R01 §C6, D4). + # (pypi_publication R01 §C6, D4). Codex reads the same root mcp.json as + # Agent Plugins 1.0, so one entry covers both. "../.claude-plugin/mcp.json:colgrep-mcp==", - "../.codex-plugin/mcp.json:colgrep-mcp==", "../mcp.json:colgrep-mcp==", # uv.lock records this project's own version too. The pattern below never # contains a version string, so commitizen rewrites nothing here, but it diff --git a/server/tests/test_hooks.py b/server/tests/test_hooks.py index a05b052..387875a 100644 --- a/server/tests/test_hooks.py +++ b/server/tests/test_hooks.py @@ -368,13 +368,15 @@ def test_manifests_name_the_hook_files_per_ecosystem(): """The two loaders read the same field with opposite semantics. Claude Code always loads `hooks/hooks.json` and treats `hooks` as *additional* files: naming the default again fails the whole plugin at install time ("Duplicate hooks file detected", Claude Code 2.1.270) while - `--plugin-dir` accepts it silently. Codex discovers `hooks/hooks.json` only when the manifest + `--plugin-dir` accepts it silently. Codex discovers `hooks/hooks.json` only when its manifest defines no `hooks`, and an explicit value *replaces* that discovery. So the Claude manifest - names exactly the extra files (today all Claude-only) and the Codex manifest the portable - file (stack-traps `claude-code.md#hooks-manifest-duplicate`).""" + names exactly the extra files (today all Claude-only) and Codex's own extras — now + `plugin.json`'s `extensions["com.openai"]`, since nest_migration/regenerate_manifests retired + the separate `.codex-plugin/` manifest — name the portable file (stack-traps + `claude-code.md#hooks-manifest-duplicate`).""" claude = _load(REPO_ROOT / ".claude-plugin" / "plugin.json") - codex = _load(REPO_ROOT / ".codex-plugin" / "plugin.json") agent = _load(REPO_ROOT / "plugin.json") + codex_ext = agent["extensions"]["com.openai"] named = [claude["hooks"]] if isinstance(claude["hooks"], str) else list(claude["hooks"]) assert "./hooks/hooks.json" not in named, ( @@ -382,8 +384,8 @@ def test_manifests_name_the_hook_files_per_ecosystem(): ) assert set(named) == {f"./hooks/{p.name}" for p in EXTRA_FILES}, "every extra file is Claude-only today" assert claude["hooks"] == "./hooks/worktree-remove.json" - assert codex["hooks"] == "./hooks/hooks.json", "Codex: the field replaces default discovery, so name hooks.json" - assert "hooks" not in agent, "Agent Plugins 1.0 defines no hooks component" + assert codex_ext["hooks"] == "./hooks/hooks.json", "Codex: the field replaces default discovery, so name hooks.json" + assert "hooks" not in agent, "Agent Plugins 1.0 defines no hooks component at its top level" def test_hook_script_is_stdlib_only_and_never_imports_the_server(): diff --git a/server/tests/test_manifests.py b/server/tests/test_manifests.py index 0e8dd00..0390cb7 100644 --- a/server/tests/test_manifests.py +++ b/server/tests/test_manifests.py @@ -1,10 +1,14 @@ """Consistency guards across the plugin packaging manifests. -`plugin.json` and `mcp.json` at the repo root, `.claude-plugin/plugin.json` -plus `.claude-plugin/mcp.json`, and `.codex-plugin/plugin.json` plus -`.codex-plugin/mcp.json` describe the same server to three different plugin -ecosystems (Claude Code, Agent Plugins 1.0, Codex). Nothing enforces that -they stay in sync on a version bump or a manifest edit except these tests. +`plugin.json` and `mcp.json` at the repo root, plus `.claude-plugin/plugin.json` +and `.claude-plugin/mcp.json`, describe the same server to Claude Code, Codex +and Agent Plugins 1.0. Codex parses the root Agent Plugins manifest natively +and auto-wires its MCP servers from `mcp.json` by convention (`references/ +manifests.md#codex`, nest_migration `regenerate_manifests`); its own extras +live under `plugin.json`'s `extensions["com.openai"]` rather than in a +`.codex-plugin/` directory of its own, which no longer exists. Nothing +enforces that these files stay in sync on a version bump or a manifest edit +except these tests. Every MCP manifest launches `uvx colgrep-mcp==` from PyPI: no root placeholder anywhere in `args`, because a client that leaves @@ -16,10 +20,10 @@ The Claude Code manifest lives at `.claude-plugin/mcp.json`, not at a root-level `.mcp.json`: Claude Code's project-scope MCP auto-discovery only ever looks for a root `.mcp.json`, and this repository is itself sometimes -opened as a plain project rather than loaded as a plugin. Codex has its own -`.codex-plugin/mcp.json` so that the only placeholder left — Claude Code's -`COLGREP_MCP_ROOT=${CLAUDE_PROJECT_DIR}` in `env` — is read only by the -client documented to expand it. +opened as a plain project rather than loaded as a plugin. Codex reads the +root `mcp.json` (the same file Agent Plugins 1.0 clients read) so that the +only placeholder left — Claude Code's `COLGREP_MCP_ROOT=${CLAUDE_PROJECT_DIR}` +in `env` — is read only by the client documented to expand it. """ from __future__ import annotations @@ -47,7 +51,7 @@ FORBIDDEN_MCP_ENV_KEYS = {"PLUGIN_ROOT", "PLUGIN_DATA"} -MCP_MANIFESTS = (".claude-plugin/mcp.json", ".codex-plugin/mcp.json", "mcp.json") +MCP_MANIFESTS = (".claude-plugin/mcp.json", "mcp.json") def _load(relpath: str) -> dict: @@ -67,8 +71,9 @@ def test_versions_aligned(): version = colgrep_mcp.__version__ assert _load(".claude-plugin/plugin.json")["version"] == version + # Codex parses this same root manifest natively (no manifest of its own), + # so checking it here also covers Codex's version. assert _load("plugin.json")["version"] == version - assert _load(".codex-plugin/plugin.json")["version"] == version # The dev plugin is versioned with the product: its skills describe how to # maintain *this* repository at *this* version, so one `cz bump` moves both. assert _load("dev/.claude-plugin/plugin.json")["version"] == version @@ -81,6 +86,17 @@ def test_agent_plugin_fields_whitelist(): assert manifest["$schema"] == "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json" +def test_codex_extension_carries_the_interface_block(): + """Codex's extras live under `extensions["com.openai"]`; identity (name, version, + author) is read off the shared root manifest fields checked elsewhere in this file.""" + manifest = _load("plugin.json") + codex_ext = manifest["extensions"]["com.openai"] + + for key in ("displayName", "shortDescription", "longDescription", "developerName", "category", "capabilities"): + assert codex_ext["interface"].get(key), f"extensions.com.openai.interface lacks {key}" + assert codex_ext.get("hooks") == "./hooks/hooks.json" + + def test_every_mcp_manifest_launches_the_pinned_pypi_release(): """`uvx colgrep-mcp==`: a bare executable in `command`, one requirement in `args`, nothing to expand.""" @@ -94,7 +110,6 @@ def test_every_mcp_manifest_launches_the_pinned_pypi_release(): def test_plugin_manifests_point_at_their_own_mcp_file(): assert _load(".claude-plugin/plugin.json")["mcpServers"] == "./.claude-plugin/mcp.json" - assert _load(".codex-plugin/plugin.json")["mcpServers"] == "./.codex-plugin/mcp.json" assert not (REPO_ROOT / ".mcp.json").exists(), "a root .mcp.json is read as project-scope config; see stack-traps" @@ -115,13 +130,15 @@ def test_placeholders_only_in_claude_code_env(): so only its manifest may carry one, and only in `env`.""" claude_env = _server(".claude-plugin/mcp.json")["env"] assert claude_env == {"COLGREP_MCP_ROOT": "${CLAUDE_PROJECT_DIR}"} - for relpath in (".codex-plugin/mcp.json", "mcp.json"): - assert "$" not in json.dumps(_server(relpath).get("env", {})), relpath + # Codex reads this same root mcp.json (no manifest of its own), so it is + # covered by the same placeholder-free assertion as Agent Plugins 1.0. + assert "$" not in json.dumps(_server("mcp.json").get("env", {})) def test_names_aligned(): claude_name = _load(".claude-plugin/plugin.json")["name"] agent_name = _load("plugin.json")["name"] - codex_name = _load(".codex-plugin/plugin.json")["name"] + # Codex parses the same root manifest as agent-plugins (no manifest of its own), + # so its name is agent_name; nothing further to compare here. - assert claude_name == agent_name == codex_name + assert claude_name == agent_name diff --git a/server/tests/test_version.py b/server/tests/test_version.py index 73a0756..cc42811 100644 --- a/server/tests/test_version.py +++ b/server/tests/test_version.py @@ -2,7 +2,8 @@ `colgrep_mcp.__version__` is derived from installed package metadata (see `colgrep_mcp/__init__.py`); the plugin manifests and the `uvx -colgrep-mcp==` pin in the three MCP manifests are rewritten by +colgrep-mcp==` pin in the two MCP manifests (`.claude-plugin/mcp.json` +and the shared root `mcp.json`, which Codex also reads) are rewritten by `cz bump` through `version_files` (pypi_publication R01 §C6). Nothing else enforces that they stay aligned except this test and `tests/test_manifests.py`. @@ -31,18 +32,23 @@ def test_version_matches_pyproject(): def test_manifests_match_pyproject(): + """`.codex-plugin/plugin.json` no longer exists: Codex parses the shared root + `plugin.json` natively (extras under `extensions["com.openai"]`), so checking + `plugin.json` here also covers Codex's version.""" version = _pyproject_version() - for relpath in ("plugin.json", ".claude-plugin/plugin.json", ".codex-plugin/plugin.json"): + for relpath in ("plugin.json", ".claude-plugin/plugin.json"): manifest = json.loads((REPO_ROOT / relpath).read_text()) assert manifest["version"] == version, f"{relpath} version mismatch" def test_mcp_manifests_pin_the_pyproject_version(): - """The `colgrep-mcp==` regex in `version_files` must have rewritten every pin; a lag means a hand edit.""" + """The `colgrep-mcp==` regex in `version_files` must have rewritten every pin; a lag + means a hand edit. Codex reads the same root `mcp.json` as Agent Plugins 1.0 (no + manifest of its own), so it is covered by that one entry.""" version = _pyproject_version() - for relpath in (".claude-plugin/mcp.json", ".codex-plugin/mcp.json", "mcp.json"): + for relpath in (".claude-plugin/mcp.json", "mcp.json"): server = json.loads((REPO_ROOT / relpath).read_text())["mcpServers"]["colgrep"] assert server["args"] == [f"colgrep-mcp=={version}"], f"{relpath} pin mismatch" From edde87de375f1a806828a60a63bafaebdac6884f Mon Sep 17 00:00:00 2001 From: Eliott Jacopin Date: Wed, 16 Sep 2026 13:02:10 +0200 Subject: [PATCH 07/17] fix(plugin): restore the hand-maintained dev README the regeneration clobbered Regenerating with `--force` overwrote `dev/README.md` from the generator's `dev_readme()` template, losing hand-maintained content that the template cannot know about: - where the dev plugin's eval cases live (`evals/` under `dev/evals/`) - the attribution of the progressive-disclosure layout to skill-creator - the CONTRIBUTING framing ("human projects say install the dev environment; here the dev environment is knowledge") This file is deliberately divergent from the template, which is why the playbook's regeneration guard carries it as a whole-file exemption (`ALLOWED_DIVERGENCE["dev/README.md"] = None`, "hand-maintained prose with no JSON keys to compare"). The reshape leaf had no mandate to rewrite it; only the manifests were in scope. The `@cracking-shells # from the repo's own marketplace` line is left as it stands: it is still accurate while this repository's marketplace files exist, and repointing it at CrackingShells/Nest belongs to `relinquish_marketplace` step 2, which explicitly owns the install snippet wherever it is reproduced. Co-Authored-By: Claude Opus 5 --- dev/README.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/dev/README.md b/dev/README.md index 55632a4..c035995 100644 --- a/dev/README.md +++ b/dev/README.md @@ -1,15 +1,18 @@ # colgrep-mcp-dev The maintainer's dev environment for this repository, packaged as a Claude Code -plugin of skills. Here the dev environment is knowledge, and this is how an -agent installs it: +plugin of skills. Human projects say "install the dev environment" in +CONTRIBUTING; here the dev environment is knowledge, and this is how an agent +installs it: ```bash -claude --plugin-dir ./dev # from a clone -claude plugin install colgrep-mcp-dev@cracking-shells # from the repo's own marketplace +claude --plugin-dir ./dev # from a clone +claude plugin install colgrep-mcp-dev@cracking-shells # from the repo's own marketplace ``` The product plugin (`colgrep-mcp`, repository root) never ships these skills; -end users have no use for them. `AGENTS.md` lists each skill and when it fires. -Each skill follows progressive disclosure: a short `SKILL.md` whose description -says when to load it, `references/` for depth, `scripts/` for mechanical steps. +end users of the search tools have no use for them. `AGENTS.md` lists each +skill and when it fires. Each skill follows skill-creator's progressive +disclosure: a short `SKILL.md` whose description says when to load it, +`references/` for depth, `scripts/` for mechanical steps, `evals/` cases +(under `dev/evals/`) for triggering checks. From 5b5d86ca71d30102e02ca64e4de1f5c403ced401 Mon Sep 17 00:00:00 2001 From: Eliott Jacopin Date: Wed, 16 Sep 2026 13:14:21 +0200 Subject: [PATCH 08/17] docs(docs): describe Codex through the extensions block now that .codex-plugin is gone Deleting `.codex-plugin/` left five prose references to it, none of which any test pins. A reviewer pass found three; two more were in the README, the most user-visible of the set. - `README.md` install section: the Codex manifests are now `.agents/plugins/marketplace.json` and the root `plugin.json`. - `README.md` packaging section: the Codex plugin is served by the root manifest's `extensions["com.openai"]` block, with `skills` and the MCP servers auto-wired by convention rather than named in their own files. - `AGENTS.md` repo map: dropped `.codex-plugin/` from the path column and the MCP config list, which is now two files, not three. - `CONTRIBUTING.md`: the `uvx` pin lives in two MCP manifests, not three. - `dev/skills/maintainer-policy/references/drift-tests.md`: three plugin manifests, not four; two MCP configs, not three; and `test_manifests.py` now also pins the `extensions["com.openai"]` interface block, which is how Codex is served. Marketplace references in these files are deliberately left alone: this repository still declares `cracking-shells` until `relinquish_marketplace` lands, and that leaf owns repointing every install snippet at CrackingShells/Nest. Nothing guards prose against manifest shape, which is why this drift was silent. Noted for the campaign's knowledge-transfer report rather than fixed here. Co-Authored-By: Claude Opus 5 --- AGENTS.md | 2 +- CONTRIBUTING.md | 2 +- README.md | 4 ++-- dev/skills/maintainer-policy/references/drift-tests.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 22b0ea0..e77e147 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -55,7 +55,7 @@ claude plugin install colgrep-mcp-dev@cracking-shells # from the repo's market | `server/colgrep_mcp/` | the server: `server.py` (assembly), `adapter.py` (colgrep subprocess), `tools_search.py`, `tools_index.py`, `resources.py`, `prompts.py`, `errors.py`, `guide.md` (agent guide served as a resource) | implementers | | `server/tests/` | pytest suite against `fake_colgrep.py`; `test_manifests.py`, `test_version.py`, `test_changelog.py`, `test_readme.py`, `test_dev_plugin.py` are drift guards; `e2e/run_e2e.py` drives the real binary (not collected by pytest) | implementers | | `server/pyproject.toml` | the only version source, dependencies, `[tool.commitizen]`, `[tool.ruff]`, pytest config | `cz bump` for the version; humans/agents for the rest | -| `plugin.json`, `mcp.json`, `.claude-plugin/`, `.codex-plugin/`, `.agents/` | product plugin manifests for the three ecosystems; each MCP config (`.claude-plugin/mcp.json`, `.codex-plugin/mcp.json`, `mcp.json`) launches `uvx colgrep-mcp==`; `version` fields and the pins are written by `cz bump` only | launcher/packaging changes | +| `plugin.json`, `mcp.json`, `.claude-plugin/`, `.agents/` | product plugin manifests for the three ecosystems; Codex reads the root `plugin.json` and its `extensions["com.openai"]` block rather than a directory of its own; each MCP config (`.claude-plugin/mcp.json`, `mcp.json`) launches `uvx colgrep-mcp==`; `version` fields and the pins are written by `cz bump` only | launcher/packaging changes | | `skills/colgrep-search/SKILL.md` | the end-user skill that teaches agents when to use the tools | when tool semantics change | | `hooks/` | the plugin hooks: `hooks.json` (events every hook-capable harness knows), one `.json` per event not every harness knows (`worktree-remove.json`), `colgrep_policy.py` (one stdlib script serving all events); pinned by `server/tests/test_hooks.py` | when the search policy or the harness wiring changes | | `dev/` | the `colgrep-mcp-dev` plugin: `skills//` (SKILL.md, `references/`, `scripts/`), `evals/-triggers/case.yaml`; versioned by `cz bump` with the product | maintainers, when a cycle learns something | diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5221d64..2df022e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,7 +45,7 @@ Pushing the tag is the publish decision: `.github/workflows/publish.yml` builds distribution, uploads it to PyPI through trusted publishing (no token; the publisher registered on PyPI names `publish.yml` and the `pypi` environment) and creates the GitHub release from the tag's `CHANGELOG.md` section. The `uvx colgrep-mcp==` -pin in the three MCP manifests is a `version_files` target, so the bump moves it too. +pin in the two MCP manifests is a `version_files` target, so the bump moves it too. ## Gates diff --git a/README.md b/README.md index 2f1f553..37b2969 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ codex plugin marketplace add CrackingShells/colgrep-mcp codex plugin add colgrep-mcp@cracking-shells ``` -The Codex manifests are `.agents/plugins/marketplace.json` and `.codex-plugin/plugin.json`. +The Codex manifests are `.agents/plugins/marketplace.json` and the root `plugin.json` (Codex extras live under its `extensions["com.openai"]`). ### Agent Plugins 1.0 clients (Cursor, GitHub Copilot, VS Code, Kiro) @@ -209,7 +209,7 @@ The repository root is simultaneously: - a [Claude Code](https://code.claude.com/docs/en/plugins-reference) plugin (`.claude-plugin/plugin.json`, `.claude-plugin/mcp.json`) and a one-plugin marketplace (`.claude-plugin/marketplace.json`); - an [Agent Plugins 1.0](https://agent-plugins.org/specification) plugin (`plugin.json`, `mcp.json`); -- a Codex plugin (`.codex-plugin/plugin.json`, `.codex-plugin/mcp.json`) and marketplace (`.agents/plugins/marketplace.json`). +- a Codex plugin served by the root `plugin.json`'s `extensions["com.openai"]` block, which Codex parses directly (it auto-wires `skills` to `./skills` and the MCP servers to `mcp.json` by convention), and marketplace (`.agents/plugins/marketplace.json`). The Claude Code and Codex plugins share the `hooks/` component ([Hooks](#hooks)); its commands carry the one placeholder both ecosystems expand, `${CLAUDE_PLUGIN_ROOT}`. Every MCP manifest launches the same argv, with no shell script and no root placeholder: `uvx colgrep-mcp==`, where the pin is the plugin's own version — `cz bump` rewrites it with the manifests' `version` fields, so a plugin update always launches its matching server and never a stale cached one. The only placeholder left is `COLGREP_MCP_ROOT=${CLAUDE_PROJECT_DIR}` in the Claude Code manifest's `env`, the one client documented to expand it. `uvx` and `colgrep` must be on `PATH` (see Troubleshooting for GUI clients that start without one). CI runs the suite on Windows as well as macOS and Linux, builds the distribution and checks its metadata on every pull request; pushing a release tag runs `.github/workflows/publish.yml`, which uploads to PyPI through trusted publishing and creates the GitHub release. diff --git a/dev/skills/maintainer-policy/references/drift-tests.md b/dev/skills/maintainer-policy/references/drift-tests.md index 543471c..e1e53cf 100644 --- a/dev/skills/maintainer-policy/references/drift-tests.md +++ b/dev/skills/maintainer-policy/references/drift-tests.md @@ -13,7 +13,7 @@ in a leaf or a PR template. | Test file | What it pins | |:--|:--| | `tests/test_version.py` | `colgrep_mcp.__version__` equals `pyproject.toml`'s version; every manifest's `version` field equals it too; `uv.lock` records exactly that version for this package. | -| `tests/test_manifests.py` | All four plugin manifests (`plugin.json`, `.claude-plugin/plugin.json`, `.codex-plugin/plugin.json`, `dev/.claude-plugin/plugin.json`) carry the same version; the Agent Plugins 1.0 manifest only uses its permitted field set and the right `$schema`; the three ecosystems' MCP server configs launch the server the same way (same `uv run` invocation shape, no unexpanded `$` placeholders outside project scope). | +| `tests/test_manifests.py` | All three plugin manifests (`plugin.json`, `.claude-plugin/plugin.json`, `dev/.claude-plugin/plugin.json`) carry the same version; the Agent Plugins 1.0 manifest only uses its permitted field set and the right `$schema`; its `extensions["com.openai"]` block carries the full Codex interface block and hooks path, which is how Codex is served now that `.codex-plugin/` is gone; the two MCP server configs launch the server the same way (same `uv run` invocation shape, no unexpanded `$` placeholders outside project scope). | | `tests/test_changelog.py` | Every `## ` version heading in `CHANGELOG.md` is shaped so commitizen's incremental changelog mode can parse it — a hand-written Keep-a-Changelog heading in the wrong shape would make `cz bump --changelog` duplicate that section instead of appending to it. | | `tests/test_readme.py` | The tool names listed in the Tools table of `README.md` and of `server/README.md` (the PyPI page) match the tools the server actually registers — a renamed or removed tool that isn't updated in a README fails here instead of shipping stale docs. | | `tests/test_packaging.py` | `server/LICENSE` is a byte copy of the repository `LICENSE` (hatchling only packages files under `server/`); `[project.urls]` point at the repository; no tracked text file carries a path from a maintainer's machine. | From ecf32eaa0384c12445e228ded927d3a4c934d9e2 Mon Sep 17 00:00:00 2001 From: Eliott Jacopin Date: Wed, 16 Sep 2026 13:16:02 +0200 Subject: [PATCH 09/17] chore(roadmap): regenerate_manifests done; the level stays open on its gate Set through dirtree-rdm. The level does not close: `relinquish_marketplace` and `verify/` remain blocked on the playbook campaign, so nest_migration is half-landed by design rather than stalled. Co-Authored-By: Claude Opus 5 --- __roadmap__/nest_migration/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/__roadmap__/nest_migration/README.md b/__roadmap__/nest_migration/README.md index 5d20388..59a5d94 100644 --- a/__roadmap__/nest_migration/README.md +++ b/__roadmap__/nest_migration/README.md @@ -33,7 +33,7 @@ colgrep-mcp ships no marketplace, points users at Nest, and carries manifests in ```mermaid graph TD relinquish_marketplace[Relinquish Marketplace]:::blocked - regenerate_manifests[Regenerate Manifests]:::inprogress + regenerate_manifests[Regenerate Manifests]:::done verify[Verification]:::blocked classDef done fill:#166534,color:#bbf7d0 classDef inprogress fill:#854d0e,color:#fef08a @@ -46,7 +46,7 @@ graph TD | Node | Type | Status | |:-----|:-----|:-------| | `relinquish_marketplace.md` | 📄 Leaf Task | 🚫 Blocked | -| `regenerate_manifests.md` | 📄 Leaf Task | 🔄 In Progress | +| `regenerate_manifests.md` | 📄 Leaf Task | ✅ Done | | `verify/` | 📁 Directory | 🚫 Blocked | ## Amendment Log From 9430026ac85b270bba35c770a05701a502ffa5f7 Mon Sep 17 00:00:00 2001 From: Eliott Jacopin Date: Wed, 16 Sep 2026 13:21:47 +0200 Subject: [PATCH 10/17] fix(marketplace): use a valid Codex authentication policy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `policy.authentication` was `"NONE"`, which is not in Codex's enum (`ON_INSTALL | ON_USE`, with no catch-all). An invalid value there does not degrade one entry — it makes the whole marketplace file unparseable, so a Codex user adding this repository could install nothing at all from it. The value came from the generator, which hardcoded `"NONE"` in Codex marketplace entries; that is fixed upstream, where `build_codex_marketplace` now emits `"ON_INSTALL"`. This file predates the fix and is no longer regenerated here, because the spec declares hub mode and writes no marketplace at all — so the bad value would have survived until the file was deleted rather than being corrected by a later run. `ON_INSTALL` matches both the upstream generator's output and every entry in CrackingShells/Nest's own Codex catalogue, so the two catalogues agree for as long as both exist during the transition. `relinquish_marketplace` deletes this file outright once CrackingShells/Nest can provably install plugins. That leaf is gated on the playbook campaign and may stay gated for some time, which is the reason to repair the file now rather than wait for its deletion: the broken state is user-visible in the meantime. Side effect worth recording: this closes `regenerate_manifests`' Success Gate 4 (`check_plugin.py` reports no problems), which that leaf merged against as a documented exception because the only problem reported was this value. `check_plugin.py --root . --spec colgrep-mcp.spec.json` now exits 0 with "ok: plugin structure is consistent". Co-Authored-By: Claude Opus 5 --- .agents/plugins/marketplace.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index 18d3784..7aff8c6 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -12,7 +12,7 @@ }, "policy": { "installation": "AVAILABLE", - "authentication": "NONE" + "authentication": "ON_INSTALL" }, "category": "Developer Tools" } From f587e594f5803bc624998c98d47df3b4fc83396e Mon Sep 17 00:00:00 2001 From: Eliott Jacopin Date: Wed, 16 Sep 2026 18:02:35 +0200 Subject: [PATCH 11/17] fix(plugin): record the hub repository in the spec's marketplace key MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generator's `load_spec` no longer accepts a bare `"marketplace": "hub"` sentinel: hub mode must name the repository that owns the marketplace, and a spec without it is refused outright (exit 2, with a message naming the key to add). Our spec carried the bare sentinel, so the next `spawn` against it would have failed rather than regenerated. The shape is now `"marketplace": {"hub": "https://github.com/CrackingShells/Nest"}`, which also lets `install_snippet` name the hub's slug instead of this repository's own — the defect that made its output wrong for us. With this key the spec is byte-identical to the playbook's `assets/examples/colgrep-mcp.spec.json`, which has moved to hub mode too. That parity is load-bearing rather than incidental: the playbook's regeneration guard drives `spawn` with *its* copy against *this* repository, so any key where the two disagree leaves the guard green while measuring a spec nobody uses. Recorded in the repo map so the constraint outlives this campaign — the file was missing from that table, which was itself leftover drift from `regenerate_manifests` adding it. Dry run against the updated spec reports no writes and no merges; the only entry is `kept dev/README.md`, which is now create-only in the generator and cannot be clobbered by `--force`. Co-Authored-By: Claude Opus 5 --- AGENTS.md | 1 + colgrep-mcp.spec.json | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index e77e147..8c162a7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -56,6 +56,7 @@ claude plugin install colgrep-mcp-dev@cracking-shells # from the repo's market | `server/tests/` | pytest suite against `fake_colgrep.py`; `test_manifests.py`, `test_version.py`, `test_changelog.py`, `test_readme.py`, `test_dev_plugin.py` are drift guards; `e2e/run_e2e.py` drives the real binary (not collected by pytest) | implementers | | `server/pyproject.toml` | the only version source, dependencies, `[tool.commitizen]`, `[tool.ruff]`, pytest config | `cz bump` for the version; humans/agents for the rest | | `plugin.json`, `mcp.json`, `.claude-plugin/`, `.agents/` | product plugin manifests for the three ecosystems; Codex reads the root `plugin.json` and its `extensions["com.openai"]` block rather than a directory of its own; each MCP config (`.claude-plugin/mcp.json`, `mcp.json`) launches `uvx colgrep-mcp==`; `version` fields and the pins are written by `cz bump` only | launcher/packaging changes | +| `colgrep-mcp.spec.json` | the spec the manifests above are regenerated from, by the playbook's `spawning-agent-plugins` generator. Declares hub mode (`marketplace.hub` names `CrackingShells/Nest`), so regenerating writes no marketplace file here. Must stay byte-identical to the playbook's `assets/examples/colgrep-mcp.spec.json`: that copy is what the playbook's regeneration guard measures against this repo, so a divergence leaves the guard green while measuring a spec nobody uses | maintainers, in lockstep with the playbook's copy | | `skills/colgrep-search/SKILL.md` | the end-user skill that teaches agents when to use the tools | when tool semantics change | | `hooks/` | the plugin hooks: `hooks.json` (events every hook-capable harness knows), one `.json` per event not every harness knows (`worktree-remove.json`), `colgrep_policy.py` (one stdlib script serving all events); pinned by `server/tests/test_hooks.py` | when the search policy or the harness wiring changes | | `dev/` | the `colgrep-mcp-dev` plugin: `skills//` (SKILL.md, `references/`, `scripts/`), `evals/-triggers/case.yaml`; versioned by `cz bump` with the product | maintainers, when a cycle learns something | diff --git a/colgrep-mcp.spec.json b/colgrep-mcp.spec.json index 7dd7e1e..be88336 100644 --- a/colgrep-mcp.spec.json +++ b/colgrep-mcp.spec.json @@ -17,7 +17,9 @@ "agents" ], "version_from": "pyproject:server/pyproject.toml", - "marketplace": "hub", + "marketplace": { + "hub": "https://github.com/CrackingShells/Nest" + }, "ecosystems": [ "claude", "codex", From cffa6ee342184f1c02bf47b6f94060afa7dfac1a Mon Sep 17 00:00:00 2001 From: Eliott Jacopin Date: Wed, 16 Sep 2026 18:04:52 +0200 Subject: [PATCH 12/17] docs(dev): correct the manifest counts cz bump rewrites MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `landing-and-release` still described `cz bump` as rewriting four version-tracked manifests and a pin in three MCP manifests. Retiring `.codex-plugin/` left three and two respectively, per `[tool.commitizen].version_files`: `plugin.json`, `.claude-plugin/plugin.json` and `dev/.claude-plugin/plugin.json` carry the version, and the `uvx colgrep-mcp==` pin lives in `.claude-plugin/mcp.json` and the root `mcp.json`. This is the release-mechanics skill, so a wrong count here is worse than prose drift: a maintainer reconciling a version-drift failure would look for a fourth manifest that does not exist and could conclude the bump had half-failed. Found only because the numbers wrap across a line break, which a line-oriented grep for "four version-tracked" does not match — the same reason the earlier sweep for this drift missed it. Co-Authored-By: Claude Opus 5 --- dev/skills/landing-and-release/SKILL.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/skills/landing-and-release/SKILL.md b/dev/skills/landing-and-release/SKILL.md index d116d73..b4f915c 100644 --- a/dev/skills/landing-and-release/SKILL.md +++ b/dev/skills/landing-and-release/SKILL.md @@ -92,9 +92,9 @@ uv run pytest # uv run re-syncs the editable install git push origin main v # the tag is lightweight; --follow-tags skips it ``` -`cz bump` rewrites `pyproject.toml`, `uv.lock` (pre-bump hook), the four -version-tracked manifests, the `uvx colgrep-mcp==` pin in the three -MCP manifests, and `CHANGELOG.md`, and writes its own +`cz bump` rewrites `pyproject.toml`, `uv.lock` (pre-bump hook), the three +version-tracked plugin manifests, the `uvx colgrep-mcp==` pin in the +two MCP manifests, and `CHANGELOG.md`, and writes its own `release(colgrep-mcp): v` commit — never author that commit or edit a version by hand; a version-drift test failing means a file was hand-edited, not that the environment is stale. (`OBS-H` Check 4, `CONTRIBUTING.md` From 6af991def9609ece1d17c3f25ddf8fcdcb21dfcf Mon Sep 17 00:00:00 2001 From: Eliott Jacopin Date: Wed, 16 Sep 2026 18:07:21 +0200 Subject: [PATCH 13/17] refactor(marketplace): hand the cracking-shells catalogue to CrackingShells/Nest Nest is live and lists both colgrep-mcp and colgrep-mcp-dev under the cracking-shells name, so this repository no longer needs to publish its own catalogue. Delete .claude-plugin/marketplace.json and .agents/plugins/marketplace.json; .claude-plugin/plugin.json and .claude-plugin/mcp.json are untouched, since a plugin stops publishing a catalogue without ceasing to be a plugin. Deleting both files left .agents/ with nothing in it, and it was pruned from the working tree along with them (git tracks no empty directories). Co-Authored-By: Claude Sonnet 5 --- .agents/plugins/marketplace.json | 20 -------------------- .claude-plugin/marketplace.json | 20 -------------------- 2 files changed, 40 deletions(-) delete mode 100644 .agents/plugins/marketplace.json delete mode 100644 .claude-plugin/marketplace.json diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json deleted file mode 100644 index 7aff8c6..0000000 --- a/.agents/plugins/marketplace.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "cracking-shells", - "interface": { - "displayName": "CrackingShells" - }, - "plugins": [ - { - "name": "colgrep-mcp", - "source": { - "source": "local", - "path": "./" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Developer Tools" - } - ] -} diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json deleted file mode 100644 index f3a643d..0000000 --- a/.claude-plugin/marketplace.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "cracking-shells", - "description": "CrackingShells marketplace. Here: colgrep-mcp, the product plugin (semantic and hybrid code search as MCP tools), and colgrep-mcp-dev, the maintainer's plugin of skills.", - "owner": { - "name": "Eliott Jacopin", - "email": "eliott.jacopin@riken.jp" - }, - "plugins": [ - { - "name": "colgrep-mcp", - "source": "./", - "description": "Semantic and hybrid code search for coding agents, exposed as MCP tools backed by colgrep." - }, - { - "name": "colgrep-mcp-dev", - "source": "./dev", - "description": "Maintainer skills for this repository (policy, campaign leadership, landing and release, stack traps). Not for end users." - } - ] -} From 2243e80c5a4b7f834c1b2f6f612075b41fbcc97b Mon Sep 17 00:00:00 2001 From: Eliott Jacopin Date: Wed, 16 Sep 2026 18:12:30 +0200 Subject: [PATCH 14/17] docs(readme): point installs at the Nest marketplace and note the migration Repoint the Claude Code and Codex marketplace-add commands at CrackingShells/Nest in README.md, dev/README.md and AGENTS.md; the plugin install commands (`colgrep-mcp@cracking-shells`, `colgrep-mcp-dev@cracking-shells`) are unchanged, since the marketplace name did not move, only its home. Add a README subsection telling existing users to `marketplace remove cracking-shells` before adding Nest, since a client keeps whichever repository it registered under a name at add time and would otherwise silently keep resolving to this repository's now-deleted, unpublished catalogue. Also correct two README passages that still described `.agents/plugins/marketplace.json` as a live Codex manifest and `.claude-plugin/marketplace.json` as this repo's own catalogue; both files are gone as of the prior commit. Update the server/tests/test_dev_plugin.py drift guard (test_marketplace_lists_both_plugins_from_disjoint_sources), which read the two plugins' name/source pairs out of the now-deleted .claude-plugin/marketplace.json. Renamed to test_product_and_dev_plugins_are_named_and_placed_disjointly, it now checks the same fact -- that colgrep-mcp and colgrep-mcp-dev are two disjoint plugins at disjoint locations -- from the two plugin.json manifests that still exist, since the catalogue itself now lives in CrackingShells/Nest rather than in this repository. Co-Authored-By: Claude Sonnet 5 --- AGENTS.md | 2 +- README.md | 26 +++++++++++++++++++++----- dev/README.md | 2 +- server/tests/test_dev_plugin.py | 17 ++++++++++++----- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 8c162a7..674d322 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -35,7 +35,7 @@ runs the same roadmap sequentially under the same discipline. Load the ```bash claude --plugin-dir ./dev # from a clone -claude plugin install colgrep-mcp-dev@cracking-shells # from the repo's marketplace +claude plugin install colgrep-mcp-dev@cracking-shells # from CrackingShells/Nest, after `claude plugin marketplace add CrackingShells/Nest` ``` `colgrep-mcp-dev` (`dev/`) ships the maintainer skills; the product plugin diff --git a/README.md b/README.md index 37b2969..5dc840a 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ The server is the [`colgrep-mcp` package on PyPI](https://pypi.org/project/colgr ### Claude Code ```bash -claude plugin marketplace add CrackingShells/colgrep-mcp +claude plugin marketplace add CrackingShells/Nest ``` ```bash @@ -37,14 +37,14 @@ Add `--scope project` to the marketplace command to declare it in the repository ### Codex ```bash -codex plugin marketplace add CrackingShells/colgrep-mcp +codex plugin marketplace add CrackingShells/Nest ``` ```bash codex plugin add colgrep-mcp@cracking-shells ``` -The Codex manifests are `.agents/plugins/marketplace.json` and the root `plugin.json` (Codex extras live under its `extensions["com.openai"]`). +The Codex manifest is the root `plugin.json` (Codex extras live under its `extensions["com.openai"]`); this repository carries no marketplace file of its own — see [Migrating from this repository's old marketplace](#migrating-from-this-repositorys-old-marketplace) below. ### Agent Plugins 1.0 clients (Cursor, GitHub Copilot, VS Code, Kiro) @@ -104,6 +104,20 @@ claude mcp add colgrep -- uv run --quiet --directory /path/to/colgrep-mcp/server `uvx --from /path/to/colgrep-mcp/server colgrep-mcp` is the one-off equivalent. Note that `claude --plugin-dir /path/to/colgrep-mcp` loads the clone's *skill* but launches the manifest's PyPI pin, not the clone's code — use one of the commands above to test a change. +### Migrating from this repository's old marketplace + +This repository used to declare the `cracking-shells` marketplace itself (`.claude-plugin/marketplace.json`, `.agents/plugins/marketplace.json`); that catalogue now lives in [`CrackingShells/Nest`](https://github.com/CrackingShells/Nest), which lists both `colgrep-mcp` and `colgrep-mcp-dev`. A client keeps whichever repository it registered under a marketplace name at add time, so if you already have `cracking-shells` registered as pointing at this repository, adding `CrackingShells/Nest` under the same name does **not** happen automatically — your client will silently keep resolving `cracking-shells` to the old, now-unpublished two-plugin catalogue here, with no error to tell you it's stale. Remove the old registration first: + +```bash +claude plugin marketplace remove cracking-shells +``` + +```bash +codex plugin marketplace remove cracking-shells +``` + +Then add `CrackingShells/Nest` as shown above. `colgrep-mcp@cracking-shells` and `colgrep-mcp-dev@cracking-shells` install the same way afterward — only the marketplace's home moved, not its name. + ## What the agent gets ### Tools @@ -207,9 +221,11 @@ It refuses to run against this repository, its worktrees or anything under `/pri The repository root is simultaneously: -- a [Claude Code](https://code.claude.com/docs/en/plugins-reference) plugin (`.claude-plugin/plugin.json`, `.claude-plugin/mcp.json`) and a one-plugin marketplace (`.claude-plugin/marketplace.json`); +- a [Claude Code](https://code.claude.com/docs/en/plugins-reference) plugin (`.claude-plugin/plugin.json`, `.claude-plugin/mcp.json`); - an [Agent Plugins 1.0](https://agent-plugins.org/specification) plugin (`plugin.json`, `mcp.json`); -- a Codex plugin served by the root `plugin.json`'s `extensions["com.openai"]` block, which Codex parses directly (it auto-wires `skills` to `./skills` and the MCP servers to `mcp.json` by convention), and marketplace (`.agents/plugins/marketplace.json`). +- a Codex plugin served by the root `plugin.json`'s `extensions["com.openai"]` block, which Codex parses directly (it auto-wires `skills` to `./skills` and the MCP servers to `mcp.json` by convention). + +The `cracking-shells` marketplace catalogue itself — for both Claude Code and Codex — lives in [`CrackingShells/Nest`](https://github.com/CrackingShells/Nest), not in this repository; see [Install](#install). The Claude Code and Codex plugins share the `hooks/` component ([Hooks](#hooks)); its commands carry the one placeholder both ecosystems expand, `${CLAUDE_PLUGIN_ROOT}`. Every MCP manifest launches the same argv, with no shell script and no root placeholder: `uvx colgrep-mcp==`, where the pin is the plugin's own version — `cz bump` rewrites it with the manifests' `version` fields, so a plugin update always launches its matching server and never a stale cached one. The only placeholder left is `COLGREP_MCP_ROOT=${CLAUDE_PROJECT_DIR}` in the Claude Code manifest's `env`, the one client documented to expand it. `uvx` and `colgrep` must be on `PATH` (see Troubleshooting for GUI clients that start without one). CI runs the suite on Windows as well as macOS and Linux, builds the distribution and checks its metadata on every pull request; pushing a release tag runs `.github/workflows/publish.yml`, which uploads to PyPI through trusted publishing and creates the GitHub release. diff --git a/dev/README.md b/dev/README.md index c035995..d0c42e4 100644 --- a/dev/README.md +++ b/dev/README.md @@ -7,7 +7,7 @@ installs it: ```bash claude --plugin-dir ./dev # from a clone -claude plugin install colgrep-mcp-dev@cracking-shells # from the repo's own marketplace +claude plugin install colgrep-mcp-dev@cracking-shells # from CrackingShells/Nest, after `claude plugin marketplace add CrackingShells/Nest` ``` The product plugin (`colgrep-mcp`, repository root) never ships these skills; diff --git a/server/tests/test_dev_plugin.py b/server/tests/test_dev_plugin.py index 17e2bb0..0dd0d95 100644 --- a/server/tests/test_dev_plugin.py +++ b/server/tests/test_dev_plugin.py @@ -82,11 +82,18 @@ def test_dev_plugin_manifest_is_the_versioned_skills_plugin(): assert "mcpServers" not in manifest, "the dev plugin carries knowledge, never a server" -def test_marketplace_lists_both_plugins_from_disjoint_sources(): - plugins = {p["name"]: p for p in _load(".claude-plugin/marketplace.json")["plugins"]} - assert set(plugins) == {"colgrep-mcp", "colgrep-mcp-dev"} - assert plugins["colgrep-mcp-dev"]["source"] == "./dev" - assert plugins["colgrep-mcp"]["source"] == "./" +def test_product_and_dev_plugins_are_named_and_placed_disjointly(): + """This used to read the two plugins' entries out of `.claude-plugin/marketplace.json` + (name, source), but relinquish_marketplace deleted that file: the catalogue that lists + both plugins now lives in CrackingShells/Nest, not in this repository. What is still + ours to guard is that the two manifests name disjoint plugins at disjoint locations.""" + product = _load(".claude-plugin/plugin.json") + dev = _load("dev/.claude-plugin/plugin.json") + assert product["name"] == "colgrep-mcp" + assert dev["name"] == "colgrep-mcp-dev" + assert (REPO_ROOT / "dev" / ".claude-plugin" / "plugin.json").resolve() != ( + REPO_ROOT / ".claude-plugin" / "plugin.json" + ).resolve() def test_product_plugin_never_ships_the_dev_skills(): From 7b03274eab00376b7587598b7e63cc4051a6ba4a Mon Sep 17 00:00:00 2001 From: Eliott Jacopin Date: Wed, 16 Sep 2026 18:14:55 +0200 Subject: [PATCH 15/17] test(dev): make the disjoint-placement guard falsifiable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deleting `.claude-plugin/marketplace.json` cost this guard its subject: it read both plugins' names and sources out of the catalogue, and the catalogue now lives in CrackingShells/Nest. Repointing it at the two surviving manifests was right, but its third assertion compared two hardcoded manifest paths: assert (REPO_ROOT / "dev" / ".claude-plugin" / "plugin.json").resolve() != (REPO_ROOT / ".claude-plugin" / "plugin.json").resolve() Distinct literals are never equal, so that assertion cannot fail. It reads like a placement check while guarding nothing — the same shape as the tautology the reviewer pass was told to hunt for in the sibling leaf, arrived at here by a different route. Replaced with a check on the values the manifests actually declare: resolve each plugin's own `skills` field against its own plugin root, require both to exist, to differ, and the product's not to fall inside `dev/`. That restores the original intent — the two plugins are rooted at disjoint sources — as something a future edit can break. Confirmed falsifiable rather than assumed: repointing the product manifest's `skills` at `./dev/skills/` fails the test; the manifest was restored and the tree left clean. Co-Authored-By: Claude Opus 5 --- server/tests/test_dev_plugin.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/server/tests/test_dev_plugin.py b/server/tests/test_dev_plugin.py index 0dd0d95..d881b12 100644 --- a/server/tests/test_dev_plugin.py +++ b/server/tests/test_dev_plugin.py @@ -91,9 +91,18 @@ def test_product_and_dev_plugins_are_named_and_placed_disjointly(): dev = _load("dev/.claude-plugin/plugin.json") assert product["name"] == "colgrep-mcp" assert dev["name"] == "colgrep-mcp-dev" - assert (REPO_ROOT / "dev" / ".claude-plugin" / "plugin.json").resolve() != ( - REPO_ROOT / ".claude-plugin" / "plugin.json" - ).resolve() + + # Resolve each manifest's *declared* skills path against its own plugin root. Comparing + # two hardcoded manifest paths instead would be a tautology: distinct literals are never + # equal, so the assertion could not fail and would guard nothing. + product_skills = (REPO_ROOT / product["skills"]).resolve() + dev_skills = (DEV_PLUGIN / dev["skills"]).resolve() + assert product_skills.is_dir(), product_skills + assert dev_skills.is_dir(), dev_skills + assert product_skills != dev_skills + assert not product_skills.is_relative_to(DEV_PLUGIN), ( + "the product plugin must not point its skills at the maintainer tree" + ) def test_product_plugin_never_ships_the_dev_skills(): From b98d41f35c30a74efa021b1e2ced78c06b137d21 Mon Sep 17 00:00:00 2001 From: Eliott Jacopin Date: Wed, 16 Sep 2026 18:17:09 +0200 Subject: [PATCH 16/17] docs(dev): record that plugin validate picks one manifest and stops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Deleting the marketplace made `claude plugin validate .` emit a warning it had never emitted before, about `CLAUDE.md` at the plugin root not being loaded as project context. The implementer reported the warning as pre-existing. The condition is; the warning is not. `claude plugin validate ` validates exactly one manifest, preferring `.claude-plugin/marketplace.json` and stopping there. While this repository shipped its own catalogue, the `AGENTS.md` gate `claude plugin validate .` was therefore checking the marketplace and never the plugin manifest — every green result in this repository's history said less than it looked like. Removing the marketplace made the command fall through to the plugin for the first time, which surfaced a latent warning rather than introducing one. The warning itself is benign and `CLAUDE.md` stays: it is an eleven-byte `@AGENTS.md` pointer for agents working in a clone, not context meant to travel to users who install the plugin, and the knowledge it points at ships separately as the colgrep-mcp-dev skills. The validator is correct that it does nothing for an installed plugin. Recorded as a trap because the masking is the hazard, not the warning: a validator that changes which manifest it inspects depending on what it finds makes "validation passed" only as strong as the manifest it happened to pick. The first line of its output names that manifest. Co-Authored-By: Claude Opus 5 --- dev/skills/stack-traps/SKILL.md | 1 + .../stack-traps/references/claude-code.md | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/dev/skills/stack-traps/SKILL.md b/dev/skills/stack-traps/SKILL.md index 557f86b..60aa63d 100644 --- a/dev/skills/stack-traps/SKILL.md +++ b/dev/skills/stack-traps/SKILL.md @@ -25,6 +25,7 @@ linked section, don't re-derive the trap from scratch. | a Windows client's root path won't resolve | mcp-sdk | `references/mcp-sdk.md#windows-roots` | | opening this repo as a plain project shows a pending colgrep entry or a `spawn ENOENT` | claude-code | `references/claude-code.md#root-mcp-json` | | an install/list command's plugin and marketplace names look mismatched or don't carry to another ecosystem | claude-code | `references/claude-code.md#namespaces` | +| `claude plugin validate .` reports a warning it never used to, or you want to know which manifest it actually checked | claude-code | `references/claude-code.md#validate-picks-one` | | `claude -p` or `claude plugin eval` fails with an OAuth error | claude-code | `references/claude-code.md#oauth` | | `claude --plugin-dir . mcp list` says Connected but your edit is not in the server, or the plugin fails right after a bump | claude-code | `references/claude-code.md#uvx-pin` | | the plugin's hooks don't fire, or still run the old text, after an edit or an update; Codex lists them but never runs them | claude-code | `references/claude-code.md#plugin-hooks` | diff --git a/dev/skills/stack-traps/references/claude-code.md b/dev/skills/stack-traps/references/claude-code.md index b734e86..5ab665a 100644 --- a/dev/skills/stack-traps/references/claude-code.md +++ b/dev/skills/stack-traps/references/claude-code.md @@ -90,6 +90,38 @@ reinstall `colgrep-mcp@cracking-shells`. When writing install instructions for a second ecosystem, copy that ecosystem's own marketplace `name`; here the two happen to match, which is a choice, not a rule. +## `claude plugin validate .` validates the marketplace, not the plugin {#validate-picks-one} + +`claude plugin validate ` validates **one** manifest: it looks for +`.claude-plugin/marketplace.json` first and, finding one, validates that and +stops. It never reaches `.claude-plugin/plugin.json`. So for as long as this +repository shipped its own marketplace, the gate `claude plugin validate .` +in `AGENTS.md` was checking the catalogue and nothing else — the plugin +manifest was unvalidated by that command, and a green result said less than +it appeared to. + +Deleting the marketplace in the `nest_migration` campaign made the command +fall through to the plugin manifest for the first time, which immediately +surfaced a warning that had been latent all along: + +``` +⚠ root: CLAUDE.md at the plugin root is not loaded as project context. + To ship context with your plugin, use a skill (skills//SKILL.md) instead. +``` + +That warning is **expected and benign here, and `CLAUDE.md` must stay.** It is +an eleven-byte `@AGENTS.md` pointer that exists for agents working in a clone +of this repository; it is not meant to travel to end users who install the +plugin, and the maintainer knowledge it points at ships separately as the +`colgrep-mcp-dev` plugin's skills. The validator is right that it does +nothing for an installed plugin, and that is fine. + +The trap is the masking, not the warning: a validator that silently changes +*what it validates* depending on which manifest it finds means "validation +passed" is only as strong as the manifest it happened to pick. When you want +the plugin manifest checked, point the command at a tree with no marketplace +in it, or read the first line of the output — it names the manifest it chose. + ## `claude -p` / `claude plugin eval` fails with an OAuth error {#oauth} **Symptom**: `claude -p` fails with something like `Failed to authenticate: From cd5e936d6ecc8d9407e8a6caa68e97f93aaed6e2 Mon Sep 17 00:00:00 2001 From: Eliott Jacopin Date: Wed, 16 Sep 2026 18:17:56 +0200 Subject: [PATCH 17/17] chore(roadmap): close level 0 and open verification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both root leaves are done: the manifests carry the Codex extensions shape and this repository no longer declares a marketplace. `verify/` opens, but `install_check` stays planned until the work reaches `main` on the remote — Nest resolves colgrep-mcp by git source against its default branch, so an install cannot see an unpushed campaign branch. Co-Authored-By: Claude Opus 5 --- __roadmap__/nest_migration/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/__roadmap__/nest_migration/README.md b/__roadmap__/nest_migration/README.md index 59a5d94..9fb4a71 100644 --- a/__roadmap__/nest_migration/README.md +++ b/__roadmap__/nest_migration/README.md @@ -32,9 +32,9 @@ colgrep-mcp ships no marketplace, points users at Nest, and carries manifests in ## Status ```mermaid graph TD - relinquish_marketplace[Relinquish Marketplace]:::blocked + relinquish_marketplace[Relinquish Marketplace]:::done regenerate_manifests[Regenerate Manifests]:::done - verify[Verification]:::blocked + verify[Verification]:::inprogress classDef done fill:#166534,color:#bbf7d0 classDef inprogress fill:#854d0e,color:#fef08a classDef planned fill:#374151,color:#e5e7eb @@ -45,9 +45,9 @@ graph TD ## Nodes | Node | Type | Status | |:-----|:-----|:-------| -| `relinquish_marketplace.md` | 📄 Leaf Task | 🚫 Blocked | +| `relinquish_marketplace.md` | 📄 Leaf Task | ✅ Done | | `regenerate_manifests.md` | 📄 Leaf Task | ✅ Done | -| `verify/` | 📁 Directory | 🚫 Blocked | +| `verify/` | 📁 Directory | 🔄 In Progress | ## Amendment Log | ID | Date | Source | Nodes Added | Rationale |