Skip to content

feat: add native Claude Code Plugin with auto-discovery Skill#19

Merged
costiash merged 7 commits intomainfrom
feat/plugin-modernization
Feb 28, 2026
Merged

feat: add native Claude Code Plugin with auto-discovery Skill#19
costiash merged 7 commits intomainfrom
feat/plugin-modernization

Conversation

@costiash
Copy link
Owner

Purpose

Transform claude-code-docs from a shell-script-only tool into a native Claude Code Plugin. This gives users a zero-dependency installation path, an auto-discovery Skill that proactively searches docs without requiring /docs, and session-start auto-updates.

What Changed

New: Plugin Structure (plugin/)

File Purpose
plugin/.claude-plugin/plugin.json Plugin metadata (v0.6.0)
plugin/commands/docs.md /docs slash command — AI-powered intent classification, scoped search, synthesis
plugin/skills/claude-docs/SKILL.md Auto-discovery Skill — Claude reads docs automatically for Claude-related questions
plugin/skills/claude-docs/manifest-reference.md 11-category reference table for scoped search routing
plugin/hooks/hooks.json + sync-docs.sh SessionStart hook — clones or pulls docs on every session
.claude-plugin/marketplace.json Marketplace registration for costiash/claude-code-docs

Updated: Existing Files

File Change
README.md Rewritten — plugin-first install, value comparison table, auto-discovery highlight
CLAUDE.md Added plugin to repo structure/key files, fixed category labels (6 → 11), fixed broken docs.claude.com URLs in examples
CHANGELOG.md Added v0.6.0 entry
.gitignore Added eval workspace + .env

Key Fixes

  • URL mapping rules: Corrected filename-to-URL conversion in all plugin instruction files
    • claude-code__<page>.mdhttps://code.claude.com/docs/en/<page>
    • docs__en__<section>__<page>.mdhttps://platform.claude.com/en/docs/<section>/<page>
  • Broken domain references: Eliminated all docs.anthropic.com and docs.claude.com citations (these domains are dead/wrong)
  • CLAUDE.md example URLs: Fixed docs.claude.complatform.claude.com throughout

Why This Matters

Eval results (6 evals, with-skill vs without-skill):

Metric With Skill Without Skill
Pass rate 100% (22/22 assertions) 77% (17/22)
URL correctness 100% 40% (cites broken domains)
File discovery 100% 100%
Synthesis quality 100% 100%

The skill's primary value is URL mapping guidance. Without the skill, Claude consistently generates plausible-looking but broken citation URLs (docs.anthropic.com, docs.claude.com). With the skill, every URL is correct.

Plugin vs Script Install

Feature Plugin Install Script Install
Dependencies None git, jq, curl, Python 3.9+ optional
Auto-updates Every session (hook) Manual (/docs -t)
Auto-discovery Skill triggers automatically Only via /docs command
/docs command Yes Yes

Test Plan

  • All 303 existing tests pass (no regressions)
  • 6 skill quality evals pass at 100% with-skill
  • URL mapping produces correct domains for both claude-code__ and docs__en__ files
  • No broken domain references in any plugin instruction file
  • .gitignore excludes eval workspace and .env
  • Plan documents untracked (already gitignored)
  • Plugin installation via /install-plugin costiash/claude-code-docs verified locally
  • SessionStart hook clones docs on first run, pulls on subsequent runs

Create native Claude Code Plugin with /docs command, auto-discoverable
Skill, and SessionStart hook. Plugin uses Claude's native Read/Grep/Glob
tools with zero Python or shell dependencies for end users.

New plugin components:
- Marketplace manifest (.claude-plugin/marketplace.json)
- Plugin manifest with metadata (plugin/.claude-plugin/plugin.json)
- /docs slash command using native tools (plugin/commands/docs.md)
- Documentation search Skill for auto-discovery (plugin/skills/claude-docs/)
- SessionStart hook for docs auto-sync (plugin/hooks/)

Includes design document and implementation plans for the full
plugin modernization roadmap (Phases 1-4).
- Fix filename-to-URL mapping rules in SKILL.md, docs.md, and
  manifest-reference.md (claude-code__ → code.claude.com,
  docs__en__ → platform.claude.com)
- Expand SKILL.md description for better trigger accuracy (574 chars)
- Add scoped search strategy table and no-results fallback to Skill
- Expand manifest categories from 6 to 11 (agent_sdk, agents_and_tools,
  about_claude, get_started, test_and_evaluate)
- Fix broken docs.claude.com URLs in CLAUDE.md examples
- Add plugin directory to CLAUDE.md structure and key files sections
- Update category labels table with all 11 categories
- Rewrite README: plugin-first install, value comparison table,
  auto-discovery Skill highlight, zero-dependency emphasis
- Add v0.6.0 changelog entry
- Gitignore eval workspace and evals directories
Remove docs/plans/ from tracking — these are local development
artifacts already covered by .gitignore.
@github-actions
Copy link

Coverage report

This PR does not seem to contain any modification to coverable code.

@claude
Copy link

claude bot commented Feb 27, 2026

PR Review: feat: add native Claude Code Plugin with auto-discovery Skill

Overall this is a well-structured addition. The plugin architecture is clean, the auto-discovery Skill concept is solid, and the eval-driven rationale (URL correctness jumping from 40% to 100%) makes a compelling case. A few issues worth addressing before merge:


Bugs / Correctness

1. README says "Two commands" but only shows one

The Quick Start header says "Two commands, no dependencies:" but only lists one command. Either update the copy to "One command, no dependencies:" or add the missing second command.


2. CLAUDE.md repo structure still says "6 categories" after this PR adds 11

The comment paths_manifest.json # Active paths manifest (6 categories) was not updated. The PR description and CHANGELOG both note the expansion from 6 to 11 categories.


3. manifest-reference.md is missing the resources user-facing label

The "User-Friendly Labels" table in manifest-reference.md lists 10 mappings but omits resources -> "Resources", which is present in CLAUDE.md's equivalent table.


4. CHANGELOG date is tomorrow

The entry is dated 2026-02-28 but today is 2026-02-27. Trivial fix worth correcting before merge.


Potential Issues

5. sync-docs.sh: output_context() constructs JSON via string interpolation

The current callers only interpolate numeric values so the practical risk is low now. However, if a future caller passes a string containing quotes, backslashes, or newlines, this will produce malformed JSON. Consider using jq for serialization if available, or at minimum document that this function only accepts safe/numeric values.


6. sync-docs.sh: timeout is not available on macOS without GNU coreutils

timeout 10 git pull ... relies on GNU coreutils, which is not installed by default on macOS. Since the README mentions macOS support, a portable guard would help:

if command -v timeout >/dev/null 2>&1; then
    timeout 10 git pull --ff-only origin main >/dev/null 2>&1 || true
else
    git pull --ff-only origin main >/dev/null 2>&1 || true
fi

7. hooks.json: nested "hooks" key inside SessionStart array item looks non-standard

The structure is hooks.SessionStart[0].hooks[0].type rather than hooks.SessionStart[0].type. If the Claude Code Plugin spec expects the array to contain hook objects directly (not wrapped in a {"hooks":[...]} envelope), this could silently fail to register the hook. Worth verifying the hook fires correctly after install.


8. sync-docs.sh: shallow clone + --ff-only can silently fail after upstream history rewrite

--ff-only on a shallow clone will silently fail (suppressed by || true) if the remote history was rewritten. The risk is low for a documentation repo, but consider git fetch --depth 1 && git reset --hard origin/main as a more resilient alternative, or at minimum output a warning context message when the pull fails so the user knows to re-clone.


Minor / Style

  • sync-docs.sh: The if [ $? -eq 0 ]; then after the git clone call can be simplified to check the return value directly: if git clone --depth 1 ... ; then
  • SKILL.md URL mapping rule "replace docs__ prefix and remaining __ with /" is slightly ambiguous for single-segment files. A concrete before/after transform example would eliminate ambiguity for edge cases.

What Is Working Well

  • The filename-based category inference in SKILL.md is a clean, zero-dependency approach -- no Python needed for basic operation.
  • The fallback chain (Glob -> Grep -> synonym suggestions -> /docs -t) is well thought out.
  • The 15s hook timeout in hooks.json vs. 10s in the script provides a reasonable outer safety margin.
  • URL corrections throughout (eliminating docs.anthropic.com and docs.claude.com) address exactly the issue the evals expose.
  • .gitignore additions for evals and .env are appropriate.

Summary: Issues 1-4 are straightforward fixes. Issues 5-8 are lower priority but worth addressing for robustness, especially the macOS timeout compatibility for users on that platform.

@claude
Copy link

claude bot commented Feb 27, 2026

PR Review: feat: add native Claude Code Plugin with auto-discovery Skill

Overall this is a solid, well-structured addition. The plugin architecture is clean, the documentation is comprehensive, and the value proposition (URL correctness improvement from 40% to 100%) is compelling. Here are my findings:


Bug: URL construction rule in SKILL.md may produce incorrect URLs

In plugin/skills/claude-docs/SKILL.md (line 627):

Files starting with docs__en__https://platform.claude.com/<path> (replace docs__ prefix and remaining __ with /)

Following this rule literally:

  • File: docs__en__agent-sdk__python.md
  • Remove docs__ prefix → en__agent-sdk__python.md
  • Replace __ with /en/agent-sdk/python
  • Result: https://platform.claude.com/en/agent-sdk/python

The correct URL is https://platform.claude.com/en/docs/agent-sdk/python (missing /docs/ in the path).

The rule in docs.md (line 452) and the CHANGELOG both correctly describe the transformation as yielding /en/docs/<section>/<page>, but SKILL.md's wording doesn't match. A clearer rule would be:

Replace the docs__en__ prefix with en/docs/ and replace remaining __ with /

This is particularly important since URL correctness is the headline value of this PR.


Warning: First-time clone has no timeout (may exceed the 15s hook limit)

In plugin/hooks/sync-docs.sh (line 513):

git clone --depth 1 "$REPO_URL" "$DOCS_DIR" >/dev/null 2>&1

The hook timeout in hooks.json is 15 seconds, and the git pull has timeout 10, but the initial clone has no timeout guard. On slow networks, cloning 574+ files could exceed the 15s limit and be killed mid-clone, leaving a partial/corrupt ~/.claude-code-docs/ directory.

Suggested fix:

timeout 30 git clone --depth 1 "$REPO_URL" "$DOCS_DIR" >/dev/null 2>&1

And bump the hook timeout to 35s for the first-run case. Alternatively, add a partial-clone guard ([ ! -f "$DOCS_DIR/.git/HEAD" ]) before the pull path.


Warning: JSON output in sync-docs.sh doesn't escape the message

The output_context() function (lines 499–509) directly embeds $msg into a JSON string without escaping. Currently all messages are hardcoded strings or contain only numeric variables ($DOC_COUNT, $NEW_COMMITS) so this is safe in practice — but it's fragile. If the error path ever includes a dynamic value containing " or \, the JSON will be malformed silently.


Minor: uninstall handler in docs.md shows script-install path only

In plugin/commands/docs.md (line 459), the uninstall hint shows:

rm -rf ~/.claude-code-docs && rm ~/.claude/commands/docs.md

For plugin-installed instances, ~/.claude/commands/docs.md won't exist. This should show /plugin uninstall claude-docs as the primary method, with the manual path as a fallback.


Minor: CHANGELOG date is one day in the future

CHANGELOG.md line 52: ## [0.6.0] - 2026-02-28 — today is 2026-02-27.


Minor: Bash style — checking $? separately is an anti-pattern

In sync-docs.sh (lines 513–514):

git clone --depth 1 "$REPO_URL" "$DOCS_DIR" >/dev/null 2>&1
if [ $? -eq 0 ]; then

Prefer the idiomatic form:

if git clone --depth 1 "$REPO_URL" "$DOCS_DIR" >/dev/null 2>&1; then

What's well done

  • Graceful degradation preserved — the plugin is purely additive; shell/script install still works
  • git pull --ff-only prevents unexpected merge commits in the local mirror
  • 10s timeout on git pull keeps sessions responsive
  • --depth 1 clone minimizes initial download size
  • $BEFORE/$AFTER diff detection gives informative session-start context only when something actually changed
  • Category taxonomy expansion (6 → 11) and filename-to-URL corrections are solid improvements
  • Eval-driven motivation — the 40% → 100% URL correctness improvement is well-documented and convincing
  • All 303 existing tests pass — no regressions

Test coverage gap

No new tests were added for sync-docs.sh. Given this script runs on every session start, basic tests covering: (1) fresh clone path, (2) up-to-date pull path, (3) failed clone error path, and (4) valid JSON output format would strengthen confidence. Even adding shellcheck to CI for this script would catch common issues.


Summary: The URL construction ambiguity in SKILL.md (the core fix this PR aims to deliver) is the most important item to address before merging. The other items are lower priority but worth addressing for robustness.

The auto-update workflow adds paths from sitemaps that may be
unfetchable (HTML-only, redirects, SDK variants). The manifest
grew from 573 to 773 paths while only ~573 docs exist on disk,
causing the 20% threshold to fail at 25.9%.
@claude
Copy link

claude bot commented Feb 27, 2026

PR Review: feat: add native Claude Code Plugin with auto-discovery Skill

Overall this is a solid addition. The plugin structure is clean, the auto-discovery Skill is well-designed, and the README rewrite significantly improves the value proposition. The eval-driven development approach (100% pass rate with skill vs 77% without) is a great way to validate the change. A few issues worth addressing before merging:


Bug: URL mapping inconsistency in SKILL.md (High)

There is a discrepancy between SKILL.md and commands/docs.md for the docs__en__* URL transformation.

plugin/skills/claude-docs/SKILL.md (line 627):

Files starting with docs__en__ -> https://platform.claude.com/
(replace docs__ prefix and remaining __ with /)

Following this rule on docs__en__agent-sdk__python.md:

  1. Strip docs__ prefix -> en__agent-sdk__python
  2. Replace __ with / -> en/agent-sdk/python
  3. URL: https://platform.claude.com/en/agent-sdk/python -- missing /docs/

plugin/commands/docs.md (line 74):

docs__en__*.md -> https://platform.claude.com/en/docs/

This produces https://platform.claude.com/en/docs/agent-sdk/python which matches CLAUDE.md and the actual site structure.

The SKILL.md instruction strips only docs__ instead of docs__en__, losing the /docs/ segment. The correct rule: strip docs__en__ prefix, replace remaining __ with /, prepend https://platform.claude.com/en/docs/.

This is the primary value of the PR (URL correctness), so worth fixing before merge.


Bug: JSON injection risk in sync-docs.sh (Medium)

The output_context() function embeds dollar-sign-msg directly into a JSON heredoc without escaping. If dollar-sign-msg contains a double-quote (e.g., from a git error message containing a URL), the output will be malformed JSON and the hook context may be silently dropped.

Minimal fix -- escape before embedding using sed to escape backslashes and double-quotes, then use the escaped variable in the heredoc instead of dollar-sign-msg.


Potential issue: hooks.json double-nesting (Medium)

The hook structure has an extra layer of nesting -- the SessionStart array contains an object with another hooks key, which in turn contains the actual hook definition. If the plugin spec expects SessionStart entries to be hook objects directly (without the intermediate wrapper), this nesting would cause the hook not to fire at all. Worth verifying against the plugin spec or confirming with a live install test.


Test threshold loosening (Low)

The orphan path threshold was raised from 20% to 30% in tests/unit/test_manifest_validation.py. The added comment explains the reasoning well. Suggestion: surface it in the assert message too, so future pytest failures include the context inline rather than requiring a code read.


Minor: CHANGELOG date is tomorrow (Low)

v0.6.0 is dated 2026-02-28 but today is 2026-02-27. Fine if pre-dated intentionally, otherwise a one-character fix.


Positive highlights

  • The SKILL.md frontmatter description field is thorough -- gives Claude strong, specific activation signals covering all the right topics.
  • --depth 1 and timeout 10 in sync-docs.sh is exactly right for a non-blocking session hook.
  • Progressive search strategy (Glob -> Grep -> fallback -> synonym suggestions) is the correct UX pattern.
  • manifest-reference.md as a separate Skill reference file is a clean separation of concerns.
  • Removing the Python badge from README accurately reflects the zero-dependency plugin path.
  • Fixing all docs.claude.com and docs.anthropic.com references directly addresses the 40% URL accuracy problem shown in the eval results.

- Fix SKILL.md URL mapping rule: docs__en__ prefix now correctly maps
  to /en/docs/ path segment instead of losing it
- Fix hooks.json: flatten SessionStart array to match plugin spec
  (remove extra {hooks:[]} wrapper for matcher-less events)
- Fix README: "Two commands" → "One command" (only one shown)
- Fix manifest-reference.md: add missing resources → "Resources" label
- Fix sync-docs.sh: escape $msg for valid JSON in output_context()
- Fix sync-docs.sh: add portable run_with_timeout() wrapper for macOS
- Fix sync-docs.sh: add 30s timeout to first-time git clone
- Fix sync-docs.sh: replace $? anti-pattern with direct if-command
- Fix docs.md: uninstall hint now shows /uninstall-plugin as primary
@costiash
Copy link
Owner Author

Review Response

Pushed fixes in e3136e9. All 303 tests pass, bash syntax validated, JSON validated.


Fixed

1. SKILL.md URL mapping rule (flagged by all 3 reviews)
Fixed in plugin/skills/claude-docs/SKILL.md:83. The rule now reads:

Files starting with docs__en__https://platform.claude.com/en/docs/<path> (replace docs__en__ prefix with en/docs/ and remaining __ with /)

Previously it said "replace docs__ prefix", which would produce /en/agent-sdk/python instead of /en/docs/agent-sdk/python — losing the /docs/ segment.

2. hooks.json double-nesting (flagged by reviews 1 & 3)
Fixed in plugin/hooks/hooks.json. Flattened from SessionStart[].hooks[].type to SessionStart[].type. Verified against the Claude Code plugin spec: SessionStart is a matcher-less event, so the array should contain hook objects directly without an intermediate { "hooks": [...] } wrapper.

3. README "Two commands" copy (flagged by review 1)
Fixed in README.md:25. Changed to "One command, no dependencies:" — only one command is shown.

5. manifest-reference.md missing resources label (flagged by review 1)
Fixed in plugin/skills/claude-docs/manifest-reference.md:41. Added resources → "Resources" to the User-Friendly Labels section, matching CLAUDE.md and the Categories table.

6. sync-docs.sh JSON escaping (flagged by all 3 reviews)
Fixed in plugin/hooks/sync-docs.sh:22-23. output_context() now escapes backslashes and double-quotes in $msg before JSON interpolation.

7. macOS timeout compatibility (flagged by reviews 1 & 2)
Fixed in plugin/hooks/sync-docs.sh:8-16. Added run_with_timeout() wrapper that checks for GNU timeout availability and falls back to running without it. The hook's outer 15s timeout in hooks.json still provides a safety net on macOS.

8. First-time clone missing timeout (flagged by review 2)
Fixed in plugin/hooks/sync-docs.sh:36. git clone now uses run_with_timeout 30 (30s for initial clone vs 10s for pulls).

9. docs.md uninstall hint (flagged by review 2)
Fixed in plugin/commands/docs.md:81. Primary uninstall method is now /uninstall-plugin claude-docs, with manual rm -rf ~/.claude-code-docs as fallback.

10. $? anti-pattern (flagged by reviews 1 & 2)
Fixed in plugin/hooks/sync-docs.sh:36. Replaced git clone ...; if [ $? -eq 0 ] with if run_with_timeout 30 git clone ...; then.


Not a bug — pushing back

4. CLAUDE.md "6 categories" (flagged by review 1)
Verified: paths_manifest.json actually contains 6 categories (checked with json.load). The 11-category taxonomy exists only in the plugin's manifest-reference.md as a finer-grained classification for Skill routing. These are two separate categorization systems. The CLAUDE.md comment paths_manifest.json # Active paths manifest (6 categories) is accurate.

CHANGELOG date (flagged by all 3 reviews)
The entry is dated 2026-02-28 which is today's date. No change needed.


Not addressing (low priority)

Tests for sync-docs.sh (flagged by review 2)
This is a 60-line hook script with simple git operations and || true safety nets. A shell test harness mocking git clone/pull adds disproportionate complexity. The hook timeout in hooks.json provides an outer safety margin.

Test threshold assert message (flagged by review 3)
The existing inline comment explains the 30% threshold reasoning. Duplicating it in the assert message is marginal value.

SKILL.md single-segment ambiguity (flagged by review 1)
Resolved by fixing the URL rule itself (#1). The corrected rule applies unambiguously to all file patterns.

@claude
Copy link

claude bot commented Feb 27, 2026

PR Review: feat: add native Claude Code Plugin with auto-discovery Skill

Overall this is a well-structured PR with a clear value proposition — the eval results (100% vs 77% pass rate, and especially the URL correctness jump from 40% to 100%) make a compelling case for the auto-discovery Skill. The plugin architecture is clean and the docs are thorough. A few issues worth addressing before merge:


Bug: .md extension not stripped in URL generation

plugin/skills/claude-docs/SKILL.md, line 634:

The instruction says to replace claude-code__ and __ with / but never says to strip the .md extension. Following this literally, claude-code__hooks.mdhttps://code.claude.com/docs/en/hooks.md — a broken URL. The docs.md command uses <page> as a placeholder which leaves this implicit, but SKILL.md's phrasing is more algorithmic and the omission is easy to miss.

Suggested fix — add explicit .md stripping to line 634 of SKILL.md:

Files starting with `claude-code__` → `https://code.claude.com/docs/en/<page>` (strip `claude-code__` prefix, `.md` extension, and replace remaining `__` with `/`)

The same omission applies to the docs__en__ mapping in the same line.


macOS timeout gap in sync-docs.sh

The portable timeout wrapper falls back to running without any timeout on macOS (where GNU timeout isn't available by default). On macOS, a hung git operation could block session start indefinitely. The hook's 15-second timeout field in hooks.json may catch this at the Claude Code level, but relying on the host enforcer is fragile. A portable perl fallback works on macOS without extra deps:

elif command -v perl >/dev/null 2>&1; then
    perl -e "alarm $secs; exec @ARGV" -- "$@"

Silent failure on cd in sync-docs.sh

cd "$DOCS_DIR" || exit 0

If this fails (e.g. the directory was deleted between the existence check and the cd), the hook exits 0 with no output — the user gets no context. Consider:

cd "$DOCS_DIR" || { output_context "Failed to enter docs directory. Re-run /docs -t to reinstall."; exit 0; }

Test threshold loosened without a soft-warning

tests/unit/test_manifest_validation.py: The orphaned-path threshold was bumped 20% → 30% with a reasonable explanation. But without a lower-warning band, drift could silently climb to 29% before anything fails. Consider adding a warnings.warn() at 20% while keeping the hard assert at 30%.


CLAUDE.md structure diagram still says "6 categories"

The category table and README were correctly updated (6 → 11), but the repo structure diagram in CLAUDE.md was missed:

├── paths_manifest.json     # Active paths manifest (6 categories)

Should read (11 categories).


Minor: CHANGELOG date is one day ahead

The entry is dated 2026-02-28 but today is 2026-02-27. Fine if you plan to merge tomorrow.


What works well

  • --depth 1 clone keeps the initial install fast for a docs mirror.
  • --ff-only pull is the safe choice — won't corrupt any local edits.
  • wc -l | tr -d ' ' correctly handles macOS's space-padded wc output.
  • JSON escaping in output_context() (backslashes then double-quotes) is correct for the static strings being passed.
  • URL fixes in CLAUDE.md (docs.claude.complatform.claude.com) are a solid cleanup that prevents Claude from citing dead domains.
  • The SKILL.md YAML frontmatter description is comprehensive — covers the right trigger topics without being so broad it fires on everything.
  • The eval results (22/22 vs 17/22, URL correctness 100% vs 40%) make a convincing case for the skill's value.

The .md extension omission in the URL mapping is the only functional bug; the rest are defensive improvements. Good work overall.

…ling

- SKILL.md: URL mapping rules now explicitly mention stripping .md extension
- sync-docs.sh: cd failure now outputs context message instead of silent exit
@costiash
Copy link
Owner Author

Review Response (follow-up review)

Pushed fixes in c784104. 303 tests pass.


Fixed

.md extension not stripped in URL rules — Fixed in plugin/skills/claude-docs/SKILL.md:82-83. Both rules now explicitly state "strip .md extension" before the prefix/separator replacements. Note: both code.claude.com and platform.claude.com accept URLs with or without .md (both return 200), so this was not a broken-URL bug — but making it explicit improves clarity.

Silent cd failure — Fixed in plugin/hooks/sync-docs.sh:45. The cd "$DOCS_DIR" || exit 0 now outputs a context message via output_context() before exiting, so the user gets feedback if the directory disappeared.


Pushing back

macOS timeout perl fallback — The run_with_timeout() wrapper already falls back to running without timeout. The hook's 15s timeout field in hooks.json is the designed outer enforcer at the Claude Code level — it's not a fragile fallback, it's the primary safety mechanism. Adding a perl-based alarm is over-engineering for a hook that already has two layers of protection (wrapper + host timeout).

Test threshold soft-warning at 20% — YAGNI. The threshold exists to prevent CI flakiness from upstream sitemap changes (HTML-only pages, URL restructuring). Adding a warnings.warn() band introduces complexity for a test that's only meaningful in CI. If the orphan rate climbs, the hard assert catches it.

CLAUDE.md "6 categories" — Addressed in previous response: paths_manifest.json actually contains 6 categories (verified via json.load()). The 11-category taxonomy exists only in the plugin's manifest-reference.md. The comment refers to the manifest file, not the plugin taxonomy.

CHANGELOG date — Today is 2026-02-28. The date is correct.

@claude
Copy link

claude bot commented Feb 27, 2026

PR Review: feat: add native Claude Code Plugin with auto-discovery Skill

Good overall addition — the plugin architecture is clean and the eval data makes a compelling case. Here are my findings:


Security / Correctness

1. Incomplete JSON escaping in sync-docs.sh output_context()

msg="${msg//\\/\\\\}"
msg="${msg//\"/\\\"}"

Only backslashes and double-quotes are escaped, but JSON strings must also escape newlines (\n), carriage returns (\r), and tabs (\t). If git log output or an error message contains a newline, the emitted JSON will be malformed and the hook context silently lost. Consider using jq -Rs . or printf '%s' "$msg" | python3 -c "import json,sys; print(json.dumps(sys.stdin.read()))" for robust escaping, or restrict msg to a single sanitized line.

2. run_with_timeout has no fallback on macOS

else
    "$@"   # no timeout at all
fi

When timeout is unavailable (macOS without coreutils), git operations run unbounded. A 30-second clone or a 10-second pull could silently hang a Claude Code session startup. A common portable approach:

( "$@" ) & PID=$!; sleep "$secs" && kill "$PID" 2>/dev/null & wait "$PID" 2>/dev/null

Or document the brew install coreutils prerequisite.


Bugs

3. CLAUDE.md still says "6 categories" in the repo structure comment

├── paths_manifest.json     # Active paths manifest (6 categories)

The PR expands to 11 categories in manifest-reference.md and the CHANGELOG, but this line was not updated. Minor but misleading for future contributors.

4. CHANGELOG date is in the future

## [0.6.0] - 2026-02-28

Today is 2026-02-27. Setting it to tomorrow will create confusion when reading git log history. Use today's date or a CI-populated date.

5. manifest-reference.mdget_started pattern looks wrong

| `get_started` | Quickstart guides | `docs__en__get-started.md` |

Every other category uses a wildcard glob (*.md), but get_started matches only a single literal filename. If there are multiple quickstart files, they will silently fall outside the category. Should this be docs__en__get-started__*.md?


Code Quality

6. Orphan threshold relaxed without context

-        assert orphan_pct < 20, (
+        assert orphan_pct < 30, (

The comment explains why the threshold is being raised, which is helpful. However, changing from 20% → 30% is a significant weakening (50% relative increase in allowed drift). Consider adding a comment with the current measured orphan percentage so reviewers can verify the threshold is tight rather than arbitrarily generous. If the current rate is e.g. 23%, setting the cap to 25% would be safer.

7. docs.mdgit pull --dry-run in freshness check contacts the network

cd ~/.claude-code-docs && git log -1 --format="%ci" && git pull --dry-run 2>&1

git pull --dry-run still contacts the remote. If the user is offline this will error out. Consider using git fetch --dry-run with a timeout, or just checking git log for the last-update timestamp without contacting the remote and making the actual pull a separate step.

8. Hardcoded repo URL in sync-docs.sh

REPO_URL="https://github.com/costiash/claude-code-docs.git"

If someone forks this repo and installs their fork as a plugin, the hook will still clone from costiash/claude-code-docs. This is likely intended, but a comment like # Always clone from canonical upstream source would prevent future confusion.


Minor Observations

9. Python badge removed from README

The PR removes the [![Python](https://img.shields.io/badge/python-3.9+-blue)] badge. Script-install users still benefit from Python for content search and validation. Worth keeping or adding a note in the "Alternative: Script Install" section so those users know to check Python availability.

10. .gitignore blank line

The change folds .env into the venv block without a blank line. Functionally fine, but previous style used blank lines between logical groups.


What's Working Well

  • The hookSpecificOutput JSON structure for SessionStart context is correctly structured.
  • The Skill description (SKILL.md) is thorough — the filename-to-URL mapping table directly explains the eval improvement from 40% → 100% URL correctness.
  • Non-blocking pull (|| true) means a network failure at session start won't break the session.
  • The comparison table in README is a strong UX improvement for new users.
  • Fixing the docs.claude.com / docs.anthropic.com references is a real, measurable improvement validated by evals.

Summary

Area Status
Security (JSON escaping) ⚠️ Fix recommended
macOS timeout fallback ⚠️ Fix recommended
CLAUDE.md category count Minor — easy fix
CHANGELOG date Minor — easy fix
get_started pattern Minor — worth verifying
Orphan threshold Low risk — add comment
git pull --dry-run offline behavior Low risk
Hardcoded REPO_URL Intentional but undocumented

The JSON escaping issue (#1) is the highest-priority fix since malformed hook output could silently suppress the session-start context message. The macOS timeout issue (#2) is the second priority since it affects a large portion of Claude Code's user base.

@costiash costiash merged commit 9507f58 into main Feb 28, 2026
7 checks passed
costiash added a commit that referenced this pull request Feb 28, 2026
- Fix SKILL.md URL mapping rule: docs__en__ prefix now correctly maps
  to /en/docs/ path segment instead of losing it
- Fix hooks.json: flatten SessionStart array to match plugin spec
  (remove extra {hooks:[]} wrapper for matcher-less events)
- Fix README: "Two commands" → "One command" (only one shown)
- Fix manifest-reference.md: add missing resources → "Resources" label
- Fix sync-docs.sh: escape $msg for valid JSON in output_context()
- Fix sync-docs.sh: add portable run_with_timeout() wrapper for macOS
- Fix sync-docs.sh: add 30s timeout to first-time git clone
- Fix sync-docs.sh: replace $? anti-pattern with direct if-command
- Fix docs.md: uninstall hint now shows /uninstall-plugin as primary
@costiash costiash deleted the feat/plugin-modernization branch February 28, 2026 00:03
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.

1 participant