Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

emergent-tools

A Claude Code plugin that notices what keeps repeating in a project's sessions and offers to turn it into tooling.

At session start it prints the top repeated tool calls and repeated errors for the current project, gathered from Claude Code's own transcripts. You say "build 2" and Claude proposes the smallest thing that removes the repetition: a CLAUDE.md line, a permission entry, a shell script, a skill or a hook. Nothing is built until you approve the proposal.

[emergent-tools] 3 candidate(s) for this project (last 30 days, 16 sessions):
  1. exact Read: C:/Users/me/proj/scripts/deploy-manual.sh  —  6 calls / 5 sessions
  2. error mcp__projektor__list_issues error: Error: result (<n> characters) exceeds maximum  —  6 calls / 3 sessions
  3. shape Bash: gh pr view <n> --json <arg> --jq <arg>  —  5 calls / 3 sessions
Run /emergent-tools for detail, or say "build N" / "ignore N". Nothing is built without approval.

Install

/plugin marketplace add TAJD/emergent-tools
/plugin install emergent-tools@emergent-tools

Or from a local checkout: claude plugin add <path-to-repo>.

Zero dependencies. Node only, which Claude Code already ships.

How it works

Two entry points share one data directory, ~/.claude/emergent-tools/<project-key>/.

hooks/session-start.mjs runs on SessionStart. It reads digest.json for the current project, prints the briefing if there is at least one candidate, then spawns the analyser detached and exits. Startup cost is one small file read. On a project with no history it prints nothing.

src/analyse.mjs scans ~/.claude/projects/<project-key>/*.jsonl. It is incremental: state.json records how many bytes of each transcript have been processed, so a refresh only parses lines added since last time. Counts are stored per session so the 30-day window is a filter, not a recount. The digest is rebuilt from state on every run.

Because the current session's transcript is still being written, its calls show up at the next session start.

What counts as a candidate

Every tool call gets two fingerprints.

  • exact: the tool and its full input. For Read/Edit/Write that is the file path; for Bash the command.
  • shape: the same after normalisation. Bash commands have numbers, paths, quoted strings, $(…) and positional arguments replaced (gh pr view <n> --json <arg>), redirect targets kept as > <arg>, 2>&1 dropped, a leading cd X && dropped, and the rtk prefix stripped. File tools reduce to directory plus extension. WebFetch reduces to the host. Grep, Glob and WebSearch have no shape.

Only Bash, PowerShell, Read, Edit, Write, MultiEdit, NotebookEdit, Grep, Glob, WebFetch and WebSearch are counted as calls. Harness tools (Agent, Skill, ToolSearch, StructuredOutput, …) and MCP tools are not: they are already tooling. They still contribute errors. Reader-only shell pipelines and inline scripting (node -e, python -c, pwsh -Command, …) are excluded too — the interpreter is the tool, not the one-off script.

An error is a tool result flagged is_error, or whose first line matches a marker such as Error:, ENOENT, is not recognized, No such file or directory, Exit code N. Shell errors are keyed by the command shape that failed (Bash:failed: git pull), other tools by the normalised first line. If one of the next three calls in the session has the same shape as the failed one, it is counted as a retry.

A message is a notable line anywhere in a tool result, failed or not: timeouts, Error/Exception, FAIL/, flaky, ECONNRESET/EBUSY/EPERM, Access is denied, Elixir ** (, Rust panicked at. Lines that look like source code, diffs or numbered listings are skipped, as are zero-count summaries like FAIL (0) and banner lines that just repeat a count already in the summary (vitest's ⎯ Failed Tests N ⎯). Only output from non-reading commands and non-file tools is scanned, so a test log counts but cating that log does not. Each result contributes a pattern once. This is how a recurring flaky test or a Bash tool timeout shows up.

A candidate needs at least 5 occurrences across at least 2 sessions in the window (10 for a hot-file exact/shape candidate, see Configuration). Ranking is by sessions × log2(occurrences + 1), so something hit twenty times in three sessions outranks something read once in each of six. A shape is not reported for occurrences already covered by a qualifying exact candidate.

Across projects. After each refresh the analyser also merges every project's state into ~/.claude/emergent-tools/_global/digest.json and the briefing appends up to two candidates seen in at least two projects and five sessions. This is where harness-wide habits show up, such as reading paths relative to the wrong directory.

Worktrees. A worktree gets its own project key (<repo>-wt-<name>, <repo>--claude-worktrees-<name>), so its sessions would otherwise undercount the parent repo. A project's digest folds in any sibling data directory whose key reduces to it once that suffix is stripped — only when the parent key also exists as a sibling, so a repo whose own name happens to contain -wt- is never misfolded. The fold only affects the digest that's built; each key's own state.json stays independent.

Configuration

~/.claude/emergent-tools/config.json (all optional):

{ "windowDays": 30, "minOccurrences": 5, "minSessions": 2, "maxCandidates": 5, "globalMinSessions": 5, "globalMaxCandidates": 2, "hotFileMinOccurrences": 10 }

hotFileMinOccurrences raises the bar for exact/shape candidates on Read/Edit/Write/MultiEdit/NotebookEdit — a hot file needs more evidence than an error or message to compete for a briefing slot.

~/.claude/emergent-tools/<project-key>/ignore.json: array of candidate keys to suppress. The /emergent-tools skill appends to it when you say "ignore N".

Environment: EMERGENT_TOOLS_DATA_DIR overrides the data directory; EMERGENT_TOOLS_NO_REFRESH=1 stops the hook spawning the analyser.

Privacy

Only the current project's transcript directory is read. Stored per call: tool name, fingerprint, up to five example inputs truncated to 300 characters, session id. From tool output, only the normalised first line of an error and individual notable lines (see above) are stored, never whole results. Bearer tokens, common API-key prefixes (sk-, ghp_, AKIA, …), password=/token= values and credentials in URLs are redacted before anything is written. Nothing leaves the machine. State older than twice the window is pruned on each refresh.

Manual run

node src/analyse.mjs --project "C:\Users\me\proj" --print

Development

npm test

Tests are node --test, no dependencies. Bugs are tracked on projektor (DEV project).

About

Claude Code plugin: surfaces repeated tool calls and errors across a project's sessions as tooling candidates

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages