josippapez/ai-setup/tree/main/claude/plugins/dev-core
이 mod 소개
dev-core
Claude Code의 일상 기본값입니다. 저장소 근거, 항상 켜진 엔지니어링 규칙, 핵심 스킬과 전문 agent를 담은 OpenCode `interactive-mcp⟧의 Claude 호환 미러이며 이전 이름을 유지합니다.
저장소 근거는 별도의 repo-docs⟧ 플러그인이 제공합니다. find_docs⟧, list_docs⟧, read_doc⟧, find_libs⟧, get_blast_radius⟧, get_file_dependents⟧는 claude/plugins/repo-docs⟧에 있고 orchestrate⟧와 MCP server 및 mcp__plugin_repo-docs_repo-docs__*⟧를 공유합니다. claude/install.sh⟧가 자동 설치하며, 호출할 수 없으면 claude plugin install repo-docs@ai-setup⟧를 실행합니다. .mcp.json⟧은 interactive⟧(@rawwee/interactive-mcp⟧, --disable-tools message_complete_notification,intensive_chat⟧, request_user_input⟧), context7⟧, chrome-devtools⟧, computer-use⟧를 등록합니다. 프로젝트 전용 server는 `~/.claude.json⟧에 둡니다.
WCAG 조회는 MCP가 아니라 @rawwee/wcag-cli⟧ CLI입니다. Bash와 accessibility⟧ skill로 사용하며, 상태 없는 조회는 필요할 때만 실행합니다. 항상 등록된 MCP는 매 세션 subprocess를 만들기 때문에 실제 상태를 가진 repo-docs⟧와 chrome-devtools⟧ 같은 경우에만 유지합니다. `skills/⟧는 OpenCode skills의 미러입니다.
규칙은 3개 층입니다. 항상 켜진 커널은 rules/⟧의 evidence-first⟧, external-facts⟧, proactive-execution⟧이며, hooks/link-rules.cjs⟧가 SessionStart⟧에서 rules/*.md⟧를 갱신하고 ~/.claude/rules/dev-core⟧가 ${CLAUDE_PLUGIN_DATA}/rules/⟧를 가리킵니다. main agent와 모든 subagent는 /context⟧의 Memory files에서 읽고 압축 후에도 다시 로드합니다.
링크를 게시한 세션과 그 뒤 생성된 subagent에는 네이티브 복사본이 없으므로 SessionStart⟧와 SubagentStart⟧에서 규칙을 분할 주입합니다. 10,000자 제한을 지키며 first-session⟧과 session_id⟧를 키로 씁니다. claude plugin disable⟧ 후에는 ~/.claude/rules/⟧에서 ~/.claude/plugins/data/*/rules⟧를 가리키는 링크도 검사하고, claude plugin list --json⟧가 0.3 s 만에 비활성화 또는 목록 제외를 보고하면 삭제합니다. rules-index⟧는 같은 바이트의 복사본을 보존하고 사용자의 실제 ~/.claude/rules/dev-core⟧ 디렉터리는 건드리지 않습니다. hooks.json⟧은 이벤트마다 3개 슬롯(2개 사용)을 등록하여 hooks/inject-rules.cjs⟧를 대체합니다.
두 번째 층은 rules-digest.md⟧입니다. hooks/inject-rules-digest.cjs⟧가 UserPromptSubmit⟧마다 약 250-token으로 커널을 요약합니다. 커널은 약 2.5k token이므로 반복 전송을 피하고, concise-output⟧도 같은 스크립트를 사용해 `[rules-reminder]⟧ 블록이 2개 보입니다.
세 번째 층은 rule-cards/⟧입니다. hooks/rule-cards.ts⟧가 도구 결과에 카드를 붙이고 rule-cards/triggers.json⟧ 및 rule-cards/triggers.test.cjs⟧가 트리거를 검사합니다. PreToolUse⟧ 명령 훅을 대체해 카드마다 Node를 실행하는 비용(0.05~0.06 s, git mv⟧ 가드가 있는 Bash 호출은 5장)을 없앴습니다. 커널은 turn 0에, 카드는 동작 옆에 도착합니다.
| 카드 | 발동 | 건너뛰는 경우 |
| --- | --- | --- |
| writing-code⟧ | Edit⟧/Write⟧/NotebookEdit⟧, Bash의 sed -i⟧, tee⟧, 리디렉션 | — |
| reading-libraries⟧ | node_modules/⟧를 건드리는 도구 또는 Bash | -not -path⟧, --exclude-dir⟧, -g '!node_modules'⟧, -prune⟧ 제외에만 나타날 때 |
| git-commit⟧ | Bash의 git commit⟧ | — |
경로를 이름 붙였다고 작업한 것은 아닙니다. find . -type f -not -path "./node_modules/*"⟧가 감사에서 opensrc 카드를 발동했고 skip⟧ 정규식이 억제합니다. fixture repo에서 프롬프트 5개를 측정한 결과 39번 호출 동안 Edit⟧, Write⟧, Read⟧, Grep⟧, Glob⟧은 호출되지 않았습니다. Bash 패턴을 추가하면 4장이 발동합니다. 카드는 agent별 2분 디바운스이고, 8번 주입은 4개의 서로 다른 본문과 14,240자를 만들었습니다. requires: <path>⟧와 `/reload-plugins⟧도 지원합니다. 번들은 30.5 KB(7.6k token)에서 9.9 KB(2.5k token)로 줄었고 카드는 6.9 KB입니다.
git mv⟧는 강제입니다. hooks/mv-guard.ts⟧가 Bash의 tool.call⟧ hook으로 실행되어 git 추적 source의 일반 mv⟧를 거부하고 git mv⟧를 요구합니다. &&⟧, ||⟧, ;⟧, |⟧ 및 줄바꿈으로 명령을 나눠 각 구간을 검사하며 glob, 여러 source, 추적되지 않거나 무시된 source, temp 경로, 비-git 디렉터리는 통과시킵니다. 첫 시도는 mv src/utils.js src/helpers.js && sed -i '' ... && echo ...⟧였고 수정 후 git mv⟧로 R100 src/utils.js -> src/helpers.js⟧가 되었습니다. `git add -A⟧의 이름 변경 감지에 의존하지 않아 결과가 결정적입니다.
테스트
node --test claude/plugins/dev-core/hooks/*.test.cjs
Mod
hooks/screenshots.tsx⟧는 chrome-devtools의 take_screenshot⟧ 결과를 터미널 트랜스크립트의 이미지로 그립니다(PNG만, 인라인 또는 파일 저장). JPEG/WebP와 다른 표면은 기본 행을 유지합니다.
설치
먼저 작성자의 README에서 marketplace와 플러그인 이름을 확인하세요. 저장소 구조에 따라 명령어가 달라질 수 있습니다.
claude plugin marketplace add josippapez/ai-setup claude plugin install dev-core
원문 / README
dev-core
Everyday defaults for Claude Code: repo grounding, the always-on engineering rules, core skills, and the specialist agents. Claude-compatible mirror of the OpenCode interactive-mcp custom plugin, which keeps the older name.
-
Repo grounding comes from the separate
repo-docsplugin, not from this one.find_docs/list_docs/read_doc/find_libs/get_blast_radius/get_file_dependentslive inclaude/plugins/repo-docs, shared withorchestrateso both use one MCP server and one tool namespace (mcp__plugin_repo-docs_repo-docs__*) instead of each bundling an identical copy.claude/install.shinstallsrepo-docsautomatically alongside this plugin; if its tools are not callable, tell the user to runclaude plugin install repo-docs@ai-setup. Seeclaude/plugins/repo-docs/README.md. -
.mcp.jsonregistersinteractive(@rawwee/interactive-mcp, a question tool subagents can reach since Claude Code withholdsAskUserQuestionfrom them;--disable-tools message_complete_notification,intensive_chatleaves onlyrequest_user_input, since the main agent is told to useAskUserQuestionand nothing here uses intensive chat, so the other tools were roster weight nobody could call), plus bundled third-party MCP servers that should be available by default in every project:context7(@upstash/context7-mcp),chrome-devtools(chrome-devtools-mcp) andcomputer-use(computer-use-mcp, mouse/keyboard/screenshot control of the real desktop for anything with no API and no DOM behind it: native app UI, OS dialogs, browser chrome outside the page, and seeing what actually rendered; it also rescues achrome-devtoolscall stuck on the native remote-debugging dialog, which no timeout cuts short). Documented by thecomputer-useskill. These ship enabled with the plugin so they need no per-machine MCP config. Project- or secret-specific servers (e.g. Figma, a local Storybook endpoint) are intentionally kept out of the plugin and live at user scope in~/.claude.json. -
WCAG lookups are a CLI, not an MCP. Accessibility criteria/techniques/failures come from
@rawwee/wcag-cli, invoked over Bash and documented by the bundledaccessibilityskill. A skill + on-demand CLI costs nothing until used, whereas an always-registered MCP spawns a subprocess every session — the same pattern asopensrcandrtk. Prefer this shape for any stateless reference lookup; keep an MCP only where the server holds real state (a warm index, a browser session), asrepo-docsandchrome-devtoolsdo. -
skills/mirrors the OpenCode skills. -
Rules come in three layers: an always-on kernel, a per-prompt digest, and tool-triggered cards. Claude Code has no native plugin "rules" loader and nothing a plugin ships reaches the system prompt except an output style, so a hook publishes the kernel as user-scope rules and the other two layers arrive as hook-injected
additionalContext.rules/is the kernel:evidence-first,external-facts,proactive-execution. Only what applies to every turn regardless of which tool runs.hooks/link-rules.cjsruns onSessionStart, refreshes everyrules/*.mdin the plugin's data dir file by file through a rename (emptying the directory first made a concurrently starting session report every rule missing) (${CLAUDE_PLUGIN_DATA}/rules/) and points~/.claude/rules/dev-coreat the copy. From the next launch Claude Code loads them natively: into the main agent and every subagent (measured with a no-tools probe subagent), listed under Memory files in/context, reloaded after compaction. The data dir is the one path Claude Code deletes on uninstall, so the rules leave with the plugin; the dangling link is skipped silently (measured).Two gaps, both measured, both closed by the same script. Rules load before
SessionStarthooks run, so the session that publishes the link has no native copy and neither do its subagents (measured: a subagent spawned after the link appeared still reported nothing): in that one session the script emits the rules asadditionalContexton bothSessionStartandSubagentStart, sharded under the 10,000-character cap, keyed on afirst-sessionmarker in the data dir that holds the publishingsession_id. Andclaude plugin disableruns nothing of the disabled plugin, so the script also sweeps~/.claude/rules/for links into~/.claude/plugins/data/*/ruleswhose pluginclaude plugin list --json(0.3 s) reports disabled or no longer lists, and removes them;rules-indexcarries a byte-identical copy so the sweep still runs when dev-core itself is the one disabled. If the user has their own real directory at~/.claude/rules/dev-core, it is left alone and the rules keep arriving as context every session.hooks.jsonregisters 3 shard slots per event (2 in use). This replacedhooks/inject-rules.cjs, which emitted the kernel as sharded context on every session and every subagent spawn.rules-digest.mdis the second layer: a ~250-token restatement of the kernel, emitted on everyUserPromptSubmitbyhooks/inject-rules-digest.cjs. The kernel is ~2.5k tokens and sits in the launch context, so repeating it every prompt is unaffordable and leaving it alone lets its pull fade. The digest carries no rule text of its own; it points at the copy already in context and says the full rules govern where the two differ.concise-outputships the identical script against its own digest, which is why a session shows two[rules-reminder]blocks.rule-cards/is the third layer: guidance that only matters next to a specific tool, delivered by the mod inhooks/rule-cards.ts, which attaches the card to the tool call's result. Its triggers (tools, afireregex, askipregex) live inrule-cards/triggers.json, checked byrule-cards/triggers.test.cjs. It replaced aPreToolUsecommand hook that started a Node process per card on every matching call (0.05 to 0.06 s each, five per Bash call with thegit mvguard). The kernel lands at turn 0 and is far from the work by turn 40; a card arrives adjacent to the action instead.| Card | Fires on | Skips when | | --- | --- | --- | |
writing-code— simplicity, surgical scope, root cause, what a reviewer checks |Edit/Write/NotebookEdit, or a Bashsed -i,tee, or redirect into a file | — | |reading-libraries—opensrcover assumption | any tool call or Bash command touching anode_modules/path | the path appears only in an exclusion (-not -path,--exclude-dir,-g '!node_modules',-prune) | |git-commit— what has to be true before it lands | a Bashgit commit| — |Naming a path is not working on it. The skip column came from this plugin's own audit, where
find . -type f -not -path "./node_modules/*"fired the opensrc card. The trigger'sskipregex suppresses it; a false skip costs nothing, a false fire costs context every two minutes, so the skip pattern wins over the fire pattern.Every card has a Bash trigger as well as a tool-name one, and that is not optional. Benchmarked 2026-09-14 over five prompts in a fixture repo: across 39 tool calls the model made zero
Edit,Write,Read,Grep, andGlobcalls. Every file read, write, search, and move went through Bash (cat -n,cat >> f <<EOF,sed -i,grep -rn,find,mv). With tool-name matchers alone exactly one card fired; with the Bash patterns added, all four fired in the same run.Each card is debounced: it reappears at most once every two minutes, per card, per agent. Injected context does not leave the conversation, it only moves further back, so re-injecting on every user prompt just piles up duplicate copies. Measured over the five-prompt bench run under the original per-prompt keying: 8 injections carrying only 4 distinct bodies, 14,240 characters, half of it a repeat of text already in context. A card may declare
requires: <path>to skip itself unless that path exists under the session cwd. The debounce is kept in the mod's memory, so/reload-pluginsre-arms every card.Cost: the always-on bundle went from 30.5 KB (~7.6k tokens) to 9.9 KB (~2.5k tokens), loaded natively at launch for the main agent and each subagent. The cards total 6.9 KB, and only the one matching the tool is ever sent.
-
git mvis enforced, not suggested.hooks/mv-guard.tsis a modtool.callhook on Bash that denies a plainmvwhose source is tracked by git, and names thegit mvto run instead. Guidance alone did not hold: the rule is read at session start and forgotten at the moment it matters, and a plainmvplusgit addrecords the move as a delete and an add. It splits the command on unquoted&&,||,;,|, and newlines and checks every segment, because the whole-command version never fired once: asked to rename a tracked file, the model wrotemv src/utils.js src/helpers.js && sed -i '' ... && echo ...on the first try. Within a segment it denies only a two-argumentmv, optional short flags, source tracked in the git work tree at the session cwd. Globs, multi-source moves, untracked or ignored sources, temp paths, and non-git directories all run untouched, because a false deny costs the user a blocked command.Verified live: the model hit the deny, read the reason, and re-ran with
git mv, landing the change asR100 src/utils.js -> src/helpers.js. Worth knowing thatgit add -Aplus git's own rename detection often recovers a plainmvanyway — in the control arm it did. The guard makes the rename deterministic rather than dependent on similarity detection.
Tests
node --test claude/plugins/dev-core/hooks/*.test.cjs
Mod
hooks/screenshots.tsx draws a chrome-devtools take_screenshot result as the picture itself in the terminal transcript (PNG only, inline or saved to a file). JPEG/WebP results and other surfaces keep the default row.
