Skip to content

feat(scripts): ship the repository as the cli-tools Claude Code plugin - #158

Merged
CybotTM merged 1 commit into
mainfrom
feat/claude-plugin
Sep 23, 2026
Merged

CybotTM merged 1 commit into
mainfrom
feat/claude-plugin

Conversation

@CybotTM

@CybotTM CybotTM commented Sep 23, 2026

Copy link
Copy Markdown
Member

Merging this makes the repository installable as the cli-tools Claude Code plugin: its skill resolves command not found, installs and updates catalog tools and audits a project's environment by calling this repository's own scripts. It replaces the separate netresearch/cli-tools-skill, which carried a drifting copy of these scripts; that fork is archived and the marketplace entry repointed afterwards.

What is added.

  • .claude-plugin/plugin.json plus the portable Agent Plugins plugin.json at the root, kept in sync (sync-plugin-manifest.sh --check passes). No version: Claude Code then versions the plugin by commit SHA, so every merge to main reaches users. A pinned version would hold them back until someone bumps it, and this repository has no release flow that would (docs).
  • skills/cli-tools/SKILL.md and five references, ported from the fork and adapted. Scripts are called as ${CLAUDE_SKILL_DIR}/../../scripts/…, so no copy exists. The binary map is rebuilt from the 107-entry catalog: the fork mapped docker to compose, but docker has its own entry here, and difft, awf, gws, wslview and ble.sh were missing. Tools that are not cataloged, such as phpstan and mypy, are now listed as project dependencies rather than machine requirements. The fork's allowed-tools grant for apt, brew, npm and pip is dropped, so installs go through the normal permission prompt.
  • scripts/check_environment.sh and scripts/detect_project_type.sh, ported with fixes that came out of running them here:
    • detect … json printed "project_types": [""] for an empty directory. The JSON is now built by jq.
    • k8s was tested with -f, so a k8s/ directory never matched.
    • Required tools were checked by catalog name, so rust (rustc), compose (docker) and ansible-core (ansible) read as missing. The binary now comes from binary_name.
    • Duplicate detection counted one file reached through two PATH entries, /bin/usr/bin, and virtualenvs as several installations. It now uses detect_all_installations.
    • Version probes ran without </dev/null through | head -1, and go --version does not exist.
    • The ansible heuristic matched any *.yml containing hosts:; it is gone.
  • Licensing follows the fork: LICENSE becomes LICENSE-MIT, and LICENSE-CC-BY-SA-4.0 covers the skill content (about 430 of the added lines).
  • evals/evals.json: the fork's 25 evals, unchanged.

Not included: the fork's hook. Measured against the hooks reference, it could never have fired. It read output/stdout at the top level, where the input has tool_response, and it printed to stdout, which Claude Code does not pass to the model; that takes additionalContext. Most of all, PostToolUse does not fire for a failed command, which is exactly the command not found case; PostToolUseFailure does. A working version follows in its own PR.

Tests. tests/test_claude_plugin.py ties the prose to the files: both manifests, every script and reference that SKILL.md names, the binary table against the catalog in both directions, and the project-type table against detect_project_type.sh. With the fork's two scripts swapped in and the tests kept, 5 of the 6 script tests fail. Full suite 1014 passed / 1 skipped; both shell test scripts and test_smoke.sh pass. validate-skill.sh . reports no errors for the skill itself. The remaining repo-level errors are a pre-existing smart_column.py mode, which is ruff-specific and not applicable to this flake8 repo, and composer.json. Whether a composer or npm channel should continue is settled together with the fork's archival, against its actual consumers.

Assisted by claude-code:claude-opus-5-5 — Session

https://claude.ai/code/session_01NxSeVq1hDnGBCqKLGcjQ6m

The cli-tools skill lived in netresearch/cli-tools-skill, a fork carrying
its own copy of these scripts and catalog, and the two drifted: the last
three PRs ported fixes the fork had and this repository lacked. The skill
now ships from here and calls these scripts through
${CLAUDE_SKILL_DIR}/../../scripts/, so there is one copy.

- .claude-plugin/plugin.json and the portable Agent Plugins plugin.json.
  No version: Claude Code then versions the plugin by commit, so every
  merge reaches users; a pinned version would freeze them until someone
  bumps it, and this repository has no release flow that would.
- skills/cli-tools/SKILL.md and references/, ported from the fork and
  adapted. The binary map is rebuilt from the catalog (the fork mapped
  `docker` to `compose`; here docker has its own entry, and difft, awf,
  gws, wslview and ble.sh were missing). Project tools that are not
  cataloged (phpstan, mypy, ...) are now listed as project dependencies
  instead of machine requirements.
- scripts/check_environment.sh and scripts/detect_project_type.sh, ported
  with fixes found by running them:
  * detect json printed `"project_types": [""]` for a directory with no
    type -- the JSON is now built by jq;
  * `k8s` was tested with -f, so a k8s/ directory never matched;
  * required tools were checked by catalog name, so rust (rustc),
    compose (docker) and ansible-core (ansible) read as missing -- the
    binary now comes from the catalog's binary_name;
  * duplicate detection counted one file reached through two PATH
    entries, or /bin -> /usr/bin, and virtualenvs, as several
    installations -- it now uses detect_all_installations;
  * version probes ran without </dev/null through `| head -1`, and
    `go --version` does not exist.
  The ansible heuristic that matched any *.yml containing "hosts:" is gone.
- LICENSE becomes LICENSE-MIT, plus LICENSE-CC-BY-SA-4.0 for the skill
  content: the fork's split licensing, kept.
- evals/evals.json, the fork's 25 evals, unchanged.

The PostToolUse hook from the fork is not included: it read keys the hook
input does not have and printed to stdout, which Claude Code does not pass
to the model, and PostToolUse does not fire for a failed command at all.
A working version follows separately.

tests/test_claude_plugin.py ties the prose to the files: manifests, every
script and reference SKILL.md names, the binary table against the catalog,
and the project-type table against detect_project_type.sh. With the fork's
two scripts swapped in, 5 of its 6 script tests fail.

Assisted-by: claude-code:claude-opus-5-5
Agent-Session: https://claude.ai/code/session_01NxSeVq1hDnGBCqKLGcjQ6m
Agent-Host: 32116e
Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
Copilot AI lite review requested due to automatic review settings September 23, 2026 08:48
@coderabbitai

coderabbitai Bot commented Sep 23, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 51 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 1bb38093-a03a-4d0f-87c4-e934c6d73460

📥 Commits

Reviewing files that changed from the base of the PR and between cf67ad0 and 4224413.

📒 Files selected for processing (17)
  • .claude-plugin/plugin.json
  • LICENSE-CC-BY-SA-4.0
  • LICENSE-MIT
  • README.md
  • evals/evals.json
  • plugin.json
  • scripts/AGENTS.md
  • scripts/README.md
  • scripts/check_environment.sh
  • scripts/detect_project_type.sh
  • skills/cli-tools/SKILL.md
  • skills/cli-tools/references/binary_to_tool_map.md
  • skills/cli-tools/references/preferred-tools.md
  • skills/cli-tools/references/project_type_requirements.md
  • skills/cli-tools/references/resolution-workflow.md
  • skills/cli-tools/references/troubleshooting.md
  • tests/test_claude_plugin.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

Copy link
Copy Markdown
Contributor

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

@CybotTM

CybotTM commented Sep 23, 2026

Copy link
Copy Markdown
Member Author

Review before merge

CodeRabbit refused this head as rate limited and Copilot is out of quota, so the review of 4224413 is mine.

Checked against the tools, not the description.

  • claude plugin validate . passes; its one warning is the missing version, which is deliberate. The marketplace entry sets no version either, so Claude Code versions the plugin by commit. sync-plugin-manifest.sh --check confirms the two manifests agree.
  • Every script SKILL.md names exists and is executable, and every reference it names exists; tests/test_claude_plugin.py holds both, and the binary table against the catalog in both directions.
  • The cold-start claim was measured: install_tool.sh jq status with a python3 that always fails exits 0 and prints only # Warning: Failed to refresh snapshot. uv run --project <root> python <root>/audit.py --help works from an unrelated directory.
  • check_environment.sh audit . was run on this machine and read line by line. That run is where the duplicate and go --version defects showed up; both are fixed and the duplicate case is under test.
  • The README install command names netresearch-claude-code-marketplace, which is the marketplace's actual name.

Limits, stated. validate-skill.sh . reports no error for the skill. Its repo-level errors are a pre-existing smart_column.py mode (a ruff rule; this repository lints with flake8) and a missing composer.json, which is settled when the fork is archived, against that package's consumers. The evals are ported unchanged and nothing runs them yet, as in the fork.

21 checks pass, 0 fail; 0 unresolved threads; full suite 1014 passed / 1 skipped locally.

Assisted by claude-code:claude-opus-5-5 — Session

@CybotTM

CybotTM commented Sep 23, 2026

Copy link
Copy Markdown
Member Author

Self-review: 4224413

The bot review this pull request demands is unsatisfiable (Copilot quota wall or repeated bot failures on this head). The diff on this head was reviewed by the PR author; this comment is the on-the-record attestation the merge gate reads back. It stops matching on the next push.

@CybotTM
CybotTM merged commit a6560a0 into main Sep 23, 2026
25 of 26 checks passed
@CybotTM
CybotTM deleted the feat/claude-plugin branch September 23, 2026 09:01
CybotTM added a commit that referenced this pull request Sep 23, 2026
Merging this adds the `cli-tools` plugin's hook. When a Bash call fails
with `command not found`, Claude gets the catalog entry that provides
the binary and the exact install command in its context (`rg` → catalog
entry `ripgrep` → `…/scripts/install_tool.sh ripgrep install`).

**Why a new hook rather than the fork's.** `netresearch/cli-tools-skill`
shipped one, and against the [hooks
reference](https://code.claude.com/docs/en/hooks) it could never have
fired:

- It was registered for `PostToolUse` only, which fires after a
*successful* call. A missing command exits 127 and fires
`PostToolUseFailure`.
- It read `output`/`stdout`/`stderr` at the top level of the input. The
failure text actually arrives in `error` (`PostToolUseFailure`) or in
`tool_response.stderr` (`PostToolUse`).
- It printed to stdout, which these events do not pass to the model.
Text reaches the model only through
`hookSpecificOutput.additionalContext`.

Given either documented payload, the fork's script prints nothing; that
was measured, not inferred.

**How this one works.** `hooks/hooks.json` registers
`scripts/detect_missing_tool.py` for both events on `Bash`:

- `PostToolUseFailure` reads `error`.
- `PostToolUse` reads `tool_response.stderr`. This covers a missing
command inside a pipeline or list whose exit status is still 0.

The script matches only the lines shells print: bash, zsh (including
`(eval):1:`) and dash forms, never the phrase anywhere in the output. It
looks the binary up by file name and by `binary_name` in
`catalog/*.json`. It uses only the standard library and fails open on
any error, so it cannot break the call it observes.

**Tests.** `tests/test_detect_missing_tool.py` covers:

- each message form, plus the real stderr of `bash`, `sh` and (where
installed) `zsh`;
- both payload shapes, including that `PostToolUse` reads stderr only;
- the JSON output and the `hooks.json` registration.

With the fork's script in place, the output test fails: it emits
nothing. Full suite 1019 passed / 2 skipped.

Independent of #158, which adds the plugin manifest and skill; the hook
is loaded once both are on `main` and the plugin is installed from this
repository.

_Assisted by claude-code:claude-opus-5-5 —
[Session](https://claude.ai/code/session_01NxSeVq1hDnGBCqKLGcjQ6m)_

https://claude.ai/code/session_01NxSeVq1hDnGBCqKLGcjQ6m
CybotTM added a commit to netresearch/claude-code-marketplace that referenced this pull request Sep 23, 2026
Merging this makes `/plugin install
cli-tools@netresearch-claude-code-marketplace` install from
[`netresearch/coding_agent_cli_toolset`](https://github.com/netresearch/coding_agent_cli_toolset)
instead of `netresearch/cli-tools-skill`. The plugin keeps its name.

**Why.** The `cli-tools` skill always wrapped the toolset's scripts and
catalog; `cli-tools-skill` carried a copy of them that drifted. The
toolset now ships the plugin itself
([#158](netresearch/coding_agent_cli_toolset#158)),
after the fixes only the fork had were ported
([#154](netresearch/coding_agent_cli_toolset#154),
[#155](netresearch/coding_agent_cli_toolset#155),
[#156](netresearch/coding_agent_cli_toolset#156)).
The fork is archived once this is merged.

**Changes.** `.claude-plugin/marketplace.json` source repo, the README
catalog row, and the German description; the catalog count reads 100+
(the toolset has 107 entries) instead of 74+. No `version` is set here
or in the plugin's manifest, so Claude Code versions the plugin by
commit.

`./scripts/validate.sh` passes (40 plugins, no warning for this entry).

_Assisted by claude-code:claude-opus-5-5 —
[Session](https://claude.ai/code/session_01NxSeVq1hDnGBCqKLGcjQ6m)_

https://claude.ai/code/session_01NxSeVq1hDnGBCqKLGcjQ6m
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants