Releases: conreo/paperclip-codegraph
Release list
v0.12.7 — the Exceptions panel now reports decisions, and finds the ungoverned path
paperclip-codegraph v0.12.7
Install
npm install -g @colbymchenry/codegraph@1.6.0 # the CodeGraph CLI, if you do not have it
paperclipai plugin install paperclip-codegraph@0.12.7Pin the version: Paperclip stores a caret range against what was installed, and for a
0.x version a caret does not cross a minor release, so a bare plugin install is a
no-op once the next minor is out.
The plugin installs disabled. Nothing changes for any agent until an organization
turns it on, in Settings → Plugins → CodeGraph.
The Exceptions panel told operators the wrong thing about their own decisions — and
missed the one state that matters.
What it said
On an organization where 20 agents had deliberately been left without an MCP client:
20 of 34 agents cannot receive these tools yet.
… Fix it per agent in its adapter settings, then restart that agent.
Every number was correct. The conclusion was not. Those 20 agents were a decision,
not a backlog — so the panel sent an operator looking for a fix nobody wanted, and it had
no way to say "this is already right".
The state it never looked for
Denying an agent here removes this plugin's tools. It does not remove the agent's
ability to start the organization's CodeGraph server itself, and that is a second,
separate path:
| Path | What it is | Governed by | Audited by |
|---|---|---|---|
| Plugin tools | paperclip-codegraph:codegraph_search, via Paperclip's tool gateway |
the profile, per agent | yes, every call |
| MCP server | codegraph serve --mcp, spawned by the agent's own MCP client |
nothing | no |
A pi agent with --mcp-config therefore reads the codebase through a path this plugin
never sees — and if it is also switched off here, the switch appears to deny something
it does not deny. The panel never asked that question, so on this instance the state was
invisible: an agent could be revoked and fully reachable at the same time.
What it says now
- The delivery warning is about MCP only, and says so: "N of M agents do not see
CodeGraph under MCP." It then states what that agent can still do — "a missing
convenience, not a missing capability" — because the plugin's own tools work with no
MCP client at all. That was already true and the old copy implied the opposite by
calling the state a failure. - A switched-off agent is no longer counted as a gap. Switching one off is the
resolution, not the problem. - New: a switched-off agent that is still wired. Its own banner names the hole —
those calls "never reach this plugin, so nothing here governs or records them" — and
says what to do: remove the MCP client from the adapter as well. A client with no
config is not flagged, because it names no server and every ambient discovery path is
absent on a real host; that assumption is written down where it can be re-checked. - Per-row state is honest. A revoked agent on the MCP path reads "Revoked — but its
adapter still reaches CodeGraph directly" rather than just "Revoked".
The counts moved into src/ui/exceptions.ts and are covered by tests/exceptions.spec.ts
(9 tests), because a rule that only exists as an inline filter can be changed without its
tests — which is how the false alarm survived this long.
How the claim was checked
The old copy claimed these agents "can still reach CodeGraph through the plugin API —
governed and audited". Rather than rewrite it on intuition, it was checked against the
running instance:
- Paperclip documents
POST /api/plugins/tools/executeas "the primary endpoint used by
the agent service to invoke plugin tools during an agent run", and it accepts agent
identity. - The activity log holds 27
codegraph_tool_callrows attributed to an agent, plus
tool_gateway.call_allowed/call_completed/call_deniedrecords for
paperclip-codegraph:codegraph_status|explore|node|search|files|callees|callers|impact.
So the sentence was true, the plugin path is real and does carry denials, and it is now
the part of the panel that stays.
Also in this release
The README gains "The third path, which nothing governs" — the first place this
plugin's documentation admits that a --mcp-config written into an agent's adapter
bypasses both Paperclip's gateway and this plugin. Anyone wiring agents up should know
that before they rely on a denial.
v0.12.6 — an existing index wins over descending into the checkout
paperclip-codegraph v0.12.6
Install
npm install -g @colbymchenry/codegraph@1.6.0 # the CodeGraph CLI, if you do not have it
paperclipai plugin install paperclip-codegraph@0.12.6Pin the version: Paperclip stores a caret range against what was installed, and for a
0.x version a caret does not cross a minor release, so a bare plugin install is a
no-op once the next minor is out.
The plugin installs disabled. Nothing changes for any agent until an organization
turns it on, in Settings → Plugins → CodeGraph.
v0.12.5 made the agent path descend into a project's checkout. It descended too
eagerly, and this corrects that.
What was wrong
The rule v0.12.5 added was "the index lives at the checkout, so a folder that holds one
is the wrong path". Checking it against the host that reported the original problem
showed the premise is only half true. CodeGraph searches upward for .codegraph:
$ cd …/0925bda0-…/_default/dealthai && codegraph status --json
{ "projectPath": "…/_default", ← resolved upward
"indexPath": "…/_default/.codegraph", ← the container's index
"fileCount": 129, … }So dealthai — the shape v0.12.5 was written for — was already answered for. Its
index is at the container folder, and the old behaviour of handing CodeGraph
_default worked. Applying the descent unconditionally:
- hid a working index. A call would be resolved to
_default/dealthai, where
there is no.codegraphof its own. - and broke it further.
ensureIndexchecks the path it is handed, would not find
one, and withautoIndexon would runcodegraph init …/_default/dealthai— a
second index of the same code, 5 MB of it, and a different index answering
afterwards. WithautoIndexoff — the default — the agent simply got "Project
…/dealthai has no .codegraph index" on a repository that is indexed. - and misreported it. The settings page already showed the row as
Not indexed,
because the check was made at the checkout. Pressing Index now would have built
that second index by hand.
(The multi-repository half of v0.12.5 was right and is unchanged: nothing indexes the
container of a project holding six checkouts, so descending is the only way to reach
one.)
What it does now
An index that already exists wins. resolveCheckout takes an injected hasIndex: when
the configured folder already answers, the path is kept exactly as it was, and the
descent only applies to a folder that does not answer. The rule is now:
Read and build where the index already is. Otherwise, find the checkout.
The same rule governs all three places the question comes up:
| Surface | Before | Now |
|---|---|---|
| an agent's tool call | descends, misses the container index, may build a second | keeps the folder when it answers |
| a row's Indexed state | checked at the checkout only | checkout or the folder holding it |
| Index now / Rebuild | targeted the checkout, building a second index | targets wherever the index already is |
readiness and index-status are deliberately left alone: they probe a path an
operator explicitly bound, and "is there an index at the path you bound" is a
defensible meaning for a diagnostic. Changing it there would make the surface used to
debug this harder to reason about, not easier.
Verified against every project on the reporting instance
The decision code was run against all six companies and every project folder, with the
real filesystem and the real indexes:
DEL 8f393fa5 _default: wsIndexed=true effective=_default rows=[(root):shown=true]
DEA 0925bda0 _default: wsIndexed=true effective=_default rows=[dealthai:own=false,shown=true]
VRO e77f5825 _default: wsIndexed=false effective=REFUSED [vroomy-backend, …, vroomy-superproject]
REG 2ccfb99b _default: wsIndexed=false effective=REFUSED [regency-app, regency-os]
SAK 5ebdaf48 pos: wsIndexed=true effective=pos rows=[(root):shown=true]
SAK 3b2a1745 pos: wsIndexed=false effective=pos rows=[(root):shown=false]
GLU b1616c4b _default: wsIndexed=false effective=_default rows=[(root):shown=false]
DEL 1fa33d8c _default: wsIndexed=false effective=_default rows=[] (no code)
Every project that was working keeps the exact path it had. dealthai shows its row as
indexed and resolves to the index that exists. The two multi-repository projects are
refused by name, which is the intended behaviour for a project nobody has bound a
repository for.
Verification
resolveCheckout covers the pair directly: the same directory with hasIndex true
keeps the folder, with hasIndex false descends — the two tests differ in nothing else.
tests/project-discovery.spec.ts asserts the row for a checkout whose container holds
the index reads indexed: true and that a row with no index anywhere still reads
false. tests/repository-selection.spec.ts asserts a container that answers is
not refused even while holding several checkouts.
npm run verify — typecheck, build, 527 tests, all passing.
v0.12.5 — an ambiguous project is refused, not guessed
paperclip-codegraph v0.12.5
Install
npm install -g @colbymchenry/codegraph@1.6.0 # the CodeGraph CLI, if you do not have it
paperclipai plugin install paperclip-codegraph@0.12.5Pin the version: Paperclip stores a caret range against what was installed, and for a
0.x version a caret does not cross a minor release, so a bare plugin install is a
no-op once the next minor is out.
The plugin installs disabled. Nothing changes for any agent until an organization
turns it on, in Settings → Plugins → CodeGraph.
Corrected in v0.12.6. This release says CodeGraph's
index "lives at the checkout, so handing it the folder one level above answers nothing
at all". That is wrong for the case that mattered: CodeGraph searches upward, so a
project indexed at its container folder is answered for from its checkout, and the
agent path was not broken fordealthaiat all. The descent introduced here did fix
multi-repository projects — nothing indexes the container of one — but applied
unconditionally it hid the container index and would have built a second one inside
the checkout. v0.12.6 makes the descent conditional on the folder not already
answering. The rest of this note stands.
The agent side of the same defect v0.12.4 fixed on the settings page.
The gap
v0.12.4 made the settings page find the checkouts inside a project's container folder.
It left the agent path alone, and that path had the identical bug:
projectPath = resolveProjectPath(bindingPathFor(binding.path, root), { … });
// → the workspace: <project>/_defaultA binding — or the workspace Paperclip hands a run — names the project folder. For a
multi-repository project that folder holds every checkout and is not a checkout itself,
so CodeGraph was handed a path with no index in it. The repository is indexed on the
host; the agent gets not indexed; and there is nothing in the answer to say why.
So a plugin that lists six repositories and cannot read any of them is half a fix. This
is the other half.
What a call does now
One function, resolveCheckout, applied to the path a call resolves to:
| The project folder holds | What the call reads |
|---|---|
| the checkout itself | it, unchanged — the ordinary case, byte-identical to before |
| exactly one checkout | that checkout — unambiguous, and plainly what was meant |
| several checkouts | nothing. Refused, with the repository names returned |
| no checkout | the folder, unchanged — it may be a package inside a monorepo whose index sits at an ancestor |
The last row matters as much as the others: a git rev-parse that finds an ancestor is
CodeGraph's own business to resolve, and rewriting the path there would move a working
monorepo deployment somewhere nobody bound.
Why several is refused rather than guessed
The alternative was to read the first checkout, deterministically. It is deterministic
and it is wrong: nothing in a multi-repository project says which repository the
question was about, so the agent would answer out of, say, acme-api when the question
was about acme-web — a confident, well-sourced answer about the wrong codebase, which
is the worst failure mode code intelligence has. Quietly indexing one of six is worse
than refusing all six, because the refusal is visible.
The refusal had to be actionable, so it names what it found and what to do:
CodeGraph project "vroomy" holds 6 repositories (vroomy-backend, vroomy-docs,
vroomy-frontend, vroomy-infra, vroomy-proto, vroomy-superproject), so no repository
is selected. Ask a Paperclip admin to bind one of them in the CodeGraph governance
profile for your company.
The names are relativePaths — directory names relative to the workspace, the same
labels the settings page shows — so nothing about host layout is disclosed. The call is
recorded as ambiguous_repository with the candidate count, so "the agent said
CodeGraph refused" is answerable after the fact rather than a support round-trip.
Also in this release
verify-scopeperforms the same resolution, so it reports what a call would actually
do instead of a path the call would have replaced.- The Repositories section says
0 of 6 repositories indexedfor a multi-repository
project rather thanNot indexed, which hid how many there were to do. - Two stale comments:
graph-projectswas described as what "the graph view can draw"
(there has been no graph view since 0.11.0 — it is what the settings page and the nav
column read), andrepositoriesexplained indexing under a note aboutalias.
Verification
tests/repository-selection.spec.ts runs the real registered tool handler against a
real container folder: it asserts the refusal names all three repositories in order and
says what to do, that the audit row carries the candidate count, that a single nested
checkout is not refused, and that a binding outside allowedProjectRoots is still
refused by containment before the repository check runs at all.
tests/git-identity.spec.ts covers resolveCheckout directly, including the two cases
that must not change: a path that is already a checkout is returned unchanged, and a
path with no checkout is left alone.
npm run verify — typecheck, build, 521 tests, all passing.
v0.12.4 — a project's repositories are found wherever they are
paperclip-codegraph v0.12.4
Install
npm install -g @colbymchenry/codegraph@1.6.0 # the CodeGraph CLI, if you do not have it
paperclipai plugin install paperclip-codegraph@0.12.4Pin the version: Paperclip stores a caret range against what was installed, and for a
0.x version a caret does not cross a minor release, so a bare plugin install is a
no-op once the next minor is out.
The plugin installs disabled. Nothing changes for any agent until an organization
turns it on, in Settings → Plugins → CodeGraph.
A project's repository is now found wherever it actually is, not only at the folder
root.
The report
On the VRO organization the dashboard said no repository, on a host that held six
of them. The question was the right one — why is my repo not detected? — and the
answer was that the plugin was asking the wrong question about the workspace.
What was wrong
graph-projects and repositories resolved a project's workspace and then required
the workspace itself to be a checkout:
// before
if (!workspace?.repoUrl && !(await isGitRepository(indexAt))) {
skipped += 1;
continue;
}That is true of the simplest deployment — one project, one clone, .git at the top —
and false of the shape Paperclip actually creates for a multi-repository project. A
managed folder is a container:
<company>/<project>/_default/ ← the workspace, not a repository
├── vroomy-backend/ .git
├── vroomy-frontend/ .git
├── vroomy-docs/ .git
├── vroomy-infra/ .git
├── vroomy-proto/ .git
└── vroomy-superproject/ .git
VRO's project has no repo_url and no .git at _default, so both halves of the
guard were true and the project was silently skipped. Not "not indexed" —
invisible. dealthai was broken the same way one level down (a single checkout in
_default/dealthai), which is why that organization's plugin showed nothing to
configure while its code was indexed on the host.
What it does now
findRepositories walks the workspace and its immediate children — two depths,
deliberately, because a deep search eventually finds a vendored checkout inside
node_modules and indexing a repository nobody meant to index is worse than not
finding one. Every checkout it finds becomes its own row:
- its own identity. A
repositoryKey— the path relative to the workspace,""
for the workspace itself. A directory name, never an absolute path, so it is safe to
render, safe to log, and safe to send back.index-nowtakes it, so Index now on
the third of six checkouts indexes that one. - its own name. A project with one checkout keeps the project's name, because that
is the label the operator gave it. A project with six cannot — six rows all called
"Vroomy" is not a list — so each names itself fromoriginand the project becomes
the aside. - its own index state, and its own Index now / Rebuild.
The Repositories section now groups by project, because the on/off switch is a
per-project control: six identical switches that all move together is not a decision,
it is a puzzle. The switch belongs to the project, and its checkouts are listed under
it. The Index section stays one row per checkout, because indexing is per repository.
Two cases that keep working
The guard existed for a reason, and both reasons are preserved rather than dropped:
- A project pointing into a larger checkout — a package inside a monorepo. The
workspace holds no.gitand no child repository, so discovery alone would answer
"no repository" for a project that is plainly in one. Paperclip's ownrepo_url
keeps that project listed as a single candidate, andgit rev-parsestill resolves
the ancestor where the index lives. - A checkout that is not on disk yet. Same fallback, and a row saying "not indexed"
is more useful than the project vanishing.
A folder with no code at all — a backlog idea, a cancelled onboarding project — is
still not a repository and is still counted rather than listed, so "nothing here"
stays distinguishable from "three projects, none with code".
Also in this release
- The README's troubleshooting section described a data key 0.11.0 deleted.
It documentedgraph-diagnoseand a JSON payload it can no longer return — no
handler is registered for that key, so an operator following the documentedcurl
gotNo data handler registered for key "graph-diagnose". The section now reads
graph-projects, which is what the page itself reads, and shows how to tell the
three causes apart from its output. repositoryKeyis recorded in the index audit row, so an index built for the wrong
checkout of a multi-repository project is visible after the fact.
Verification
tests/project-discovery.spec.ts runs the registered graph-projects handler over
real temporary workspaces in both shapes, built with real git init and a real
origin. It asserts the six-repository project yields six rows, the nested
single-checkout project yields one, the ordinary project is unchanged, a project with
no code still yields none, and no absolute path appears anywhere in the response.
Each of those tests also asserts the old predicate would have skipped the project,
so the regression is stated rather than described.
tests/repository-rows.spec.ts covers the naming rule (including an empty
repositoryKey being a key and not a missing value, which is what would silently
index the wrong checkout), and tests/git-identity.spec.ts covers discovery itself,
including the repo_url fallback and the fact that it does not fire when a checkout
was already found.
npm run verify — typecheck, build, 511 tests, all passing.
v0.12.3 — the warning names the right cause
paperclip-codegraph v0.12.3
Install
npm install -g @colbymchenry/codegraph@1.6.0 # the CodeGraph CLI, if you do not have it
paperclipai plugin install paperclip-codegraph@0.12.3Pin the version: Paperclip stores a caret range against what was installed, and for a
0.x version a caret does not cross a minor release, so a bare plugin install is a
no-op once the next minor is out.
The plugin installs disabled. Nothing changes for any agent until an organization
turns it on, in Settings → Plugins → CodeGraph.
The "cannot receive these tools" warning was naming the wrong cause.
What it said, and why it was misleading
On an organization where the plugin had never been configured or activated at all,
the Exceptions section said:
5 of 11 agents cannot receive these tools yet. An active profile only makes the tools
allowed. An agent also needs an MCP client pointed at this organization's MCP config…
Fix it per agent in its adapter settings, then restart that agent.
Every sentence is true of a different situation. When the plugin is off, no agent
receives anything whatever its adapter says — so the adapter is not the problem, and
that paragraph sends an operator to the wrong settings page to fix it.
Off is exactly the state a fresh organization is in: GlucoChef, dealthai and
Regency have no config row and no profile at all, so the plugin is off by default
there. The warning was their first impression of the feature, and it was wrong.
Two states, named separately
- Plugin off — "CodeGraph is off for this organization, so no agent receives these
tools yet whatever its adapter is set to. Switch it on at the top of this page, and
this section will say whether anything else is still missing." No mention of adapters. - Plugin on, wiring missing — the wiring warning, now opening with the fact that
earns it: "CodeGraph is on, and an active profile makes these tools allowed — but an
agent also needs an MCP client…"
The off-state note comes first in the conditional chain, so it wins when both are true.
Testing
486 tests, 481 passing and 5 skipped. Two new ones: that the off-state explanation
precedes the wiring warning in the chain, and that revoked agents stay excluded from
the count — an agent somebody switched off is a choice, not a gap.
v0.12.2 — switch matches the host exactly
paperclip-codegraph v0.12.2
Install
npm install -g @colbymchenry/codegraph@1.6.0 # the CodeGraph CLI, if you do not have it
paperclipai plugin install paperclip-codegraph@0.12.2Pin the version: Paperclip stores a caret range against what was installed, and for a
0.x version a caret does not cross a minor release, so a bare plugin install is a
no-op once the next minor is out.
The plugin installs disabled. Nothing changes for any agent until an organization
turns it on, in Settings → Plugins → CodeGraph.
The switch now matches Paperclip's own, to the pixel.
What was wrong
Two details, both invisible in a diff and obvious on screen beside the host's rows.
The thumb was a square — 16×16. The host's is h-4 w-6: 24 wide by 16 tall,
an oval. A square thumb in a capsule track reads as slightly off without it being
clear why.
The track's 2px border was doing double duty. The host's track is h-5 (20px)
including a border-2, so its inner box is 16px — exactly the thumb's height, which
is what makes the thumb sit flush. Mine got there by accident rather than by the same
arithmetic.
The off state also ran bg-input at full strength where the host uses bg-input/90 —
90% opacity, a slightly softer grey.
Matched to the markup
Transcribed from a real settings page rather than from the component source alone:
track: relative inline-flex shrink-0 items-center rounded-full border-2
transition-all h-5 w-11 border-transparent bg-input/90
thumb: pointer-events-none inline-block rounded-full bg-background shadow-sm
transition-transform h-4 w-6 translate-x-0
data-slot="toggle", so anything the host targets by that attribute finds this one;role="switch"witharia-checked, unchanged — it was already a switch, not a
checkbox.
The travel is derived, not written down
The thumb's travel is 44 − 2×2 − 24 = 16. That arithmetic is now in the code as
arithmetic:
const THUMB_TRAVEL = TRACK_WIDTH - TRACK_BORDER * 2 - THUMB_WIDTH;A literal 16 would silently desync the moment any of those three changed — which is
exactly the class of mistake this release is fixing.
Testing
484 tests, 479 passing and 5 skipped. Four new ones pin the geometry as numbers, since
that is what it is: the track and thumb sizes, the 2px border, that the travel is
derived rather than hardcoded, and that the host's arithmetic still comes out at 16.
v0.12.1 — credential masking and delthai wiring
paperclip-codegraph v0.12.1
Install
npm install -g @colbymchenry/codegraph@1.6.0 # the CodeGraph CLI, if you do not have it
paperclipai plugin install paperclip-codegraph@0.12.1Pin the version: Paperclip stores a caret range against what was installed, and for a
0.x version a caret does not cross a minor release, so a bare plugin install is a
no-op once the next minor is out.
The plugin installs disabled. Nothing changes for any agent until an organization
turns it on, in Settings → Plugins → CodeGraph.
Two things: a credential leak this plugin was capable of, and the MCP wiring extended
to a second organization.
A clone URL can carry a token, and this plugin was reading it
git remote get-url origin returns whatever is configured, and a CI-provisioned
checkout often carries a token in it:
https://oauth2:glpat-…@git.example.com/group/repo.git
This plugin reads that URL to label a repository — the identity work in 0.9.2 — and a
label gets rendered, logged and written into audit rows. The URL itself was returned
to callers unmasked. Found while wiring the second organization, not by a report.
redactRemoteUrl now strips a user:secret@ authority at the boundary, before
the name is derived from it, so neither the URL nor the label can carry a token.
It redacts only when a secret is present: ssh://git@github.com/… has a user but no
password, and masking git there would destroy information rather than protect any.
The first version of the fix over-redacted exactly that case, and the test caught it.
Six tests, including the real GitLab form and the assertion that the token is absent
from the result for three URL shapes.
If your instance uses a tokenised clone URL, rotate the token. It has been sitting
in a config file and in the output of every git remote -v an agent runs, which is a
larger exposure than this plugin.
MCP wiring extended to delthai
The wiring added for sake — a --mcp-config path in each agent's adapter arguments,
and a CodeGraph stdio server in the company's own MCP file — now covers the second
organization that can actually use it.
| Org | pi_local agents | wired | why |
|---|---|---|---|
| DEL | 33 | 33 | Has an indexed repository (640 files, 12,085 nodes, 32,373 edges) and the plugin enabled |
| SAK | 17 | 6 | As before — the five engineers and the CEO |
| DEA | 10 | 0 | Its workspace is not a git repository and has no index |
| GLU | 11 | 0 | Two git workspaces, neither indexed |
| REG | 6 | 0 | No repository workspace at all |
An MCP server pointing at a repository with no index answers nothing, so wiring those
would have been a server that fails on first use. Extending to them is a two-step
job — build the index, then wire — and the settings page has the button.
opencode_local agents are excluded on purpose: the wiring passes pi-mcp-adapter,
which that adapter does not use. sake and delthai each have one such agent.
Testing
480 tests, 475 passing and 5 skipped.
v0.12.0 — settings rebuilt as a Paperclip settings page
paperclip-codegraph v0.12.0
Install
npm install -g @colbymchenry/codegraph@1.6.0 # the CodeGraph CLI, if you do not have it
paperclipai plugin install paperclip-codegraph@0.12.0Pin the version: Paperclip stores a caret range against what was installed, and for a
0.x version a caret does not cross a minor release, so a bare plugin install is a
no-op once the next minor is out.
The plugin installs disabled. Nothing changes for any agent until an organization
turns it on, in Settings → Plugins → CodeGraph.
The settings page is rebuilt as a Paperclip settings page, not a plugin form.
What changed, and why it is a rework rather than a restyle
It was five bordered cards — Configuration, Activate, Repositories, Indexing,
Exceptions — with checkboxes, a Save button for everything, and explanatory prose
written for a reader who already knew the answers. Next to Paperclip's own General
settings it read as a form bolted on.
The reference was read, not guessed: ui/src/pages/InstanceGeneralSettings.tsx and
ui/src/components/ui/toggle-switch.tsx. What those files do, and what this now
does:
| Page | A max-w-4xl column with sections separated by space, not a card around each. |
| Section | One idea: a text-sm font-semibold heading, a short muted sentence, then its controls. |
| Setting | Heading left, control right — flex items-start justify-between gap-4. |
| Switch | A capsule that writes immediately. No Save button, because General settings has none, and a form that needs saving is one that can be abandoned half-changed. |
| Typed values | The one place a Save button appears, and it only appears once the text actually changed. |
| On state | The host's status green, not primary — which its ToggleSwitch records as a deliberate ruling. |
| Failures | One destructive-tinted banner, rather than a message beside each control. |
The copy was rewritten to say what happens
The old text was accurate and unreadable — "Availability follows the Paperclip
project an agent is working in, so this is set per repository rather than per
agent". Now:
- Repositories — "An agent reaches the one its Paperclip project uses, so this
is set here rather than per agent — and switching one off can only narrow." - Index — "a repository with no index answers nothing. Building one reads the
whole repository, which is why it happens when you ask rather than by itself." - Directories CodeGraph may read — "A limit, not a list." The field nobody
could read is now answered in its first four words. - Exceptions — "Every agent reaches the repositories its projects use, so there
is nothing to grant here. This is only for taking that away from one agent."
Two things stay because they were asked for and are genuinely useful: the org name
in the page title, and the MCP delivery gap callout — which now names the fix
("Fix it per agent in its adapter settings, then restart that agent") as well as
the problem.
Checkboxes became switches, and rows became rows
Repositories and agents were checkbox lists; they are now rows with the item on the
left — name, then a muted status line — and a switch on the right. A repository row
reads POS · pos / Indexed · 587 files, 5250 symbols and a switch, rather than a
tick box and a sentence about what ticking means.
Testing
475 tests, 470 passing and 5 skipped. New: 12 that pin the structural rules as
source-level assertions, because the things that matter here are layout decisions
types cannot see — the reading width, the split row, the switch writing immediately
rather than behind a Save button, and the status green over primary.
They also pin the copy: that the switch is never described as a grant, that an
unindexed repository is explained rather than only reported, and that the directory
field says "a limit, not a list".
v0.11.0 — reader removed
paperclip-codegraph v0.11.0
Install
npm install -g @colbymchenry/codegraph@1.6.0 # the CodeGraph CLI, if you do not have it
paperclipai plugin install paperclip-codegraph@0.11.0Pin the version: Paperclip stores a caret range against what was installed, and for a
0.x version a caret does not cross a minor release, so a bare plugin install is a
no-op once the next minor is out.
The plugin installs disabled. Nothing changes for any agent until an organization
turns it on, in Settings → Plugins → CodeGraph.
The hand-built graph page is removed. The plugin now does the part it is
actually for, and nothing else.
Why it came out
For several releases this plugin shipped its own reader: a three-pane Symbol view,
an architecture Map, entry points, and a dead-code list. Every one of those already
exists in CodeGraph's own UI, and is better. That UI is under active
development — its own unreleased notes add a Screens tab, rework the Steps layout
several times over, and change the very map and screen layouts this plugin had
reimplemented.
So the reader could only ever fall behind, and the hours spent on it were hours not
spent on the part that is genuinely pluggable. It is gone.
What was investigated first, and why reuse was not adopted
The adapter seam in CodeGraph's UI is real, and it was tested rather than assumed:
ui/src/lib/api.tsis explicit — "a host that already holds the index — the Pro
app — installs its own" adapter.GraphAdapteris 14 methods, withsteps
optional andtrailsexplicitly specified to answer{trails: [], readOnly: true}
so a host shows the section as empty rather than a dead Save button.- Their UI builds standalone from
ui/srcwith Vite and svelte — 1.40s. - A proof of concept ran their real Map view against this plugin's data: 40
module boxes, their legend, their "foundations — depend on nothing below"
caption, no page errors.
It was still not adopted, for a reason that only showed up when checked:
- their
uipackage isprivate: trueand not published to npm; - the built viewer ships inside a 123 MB vendored Node runtime, not as assets
that can be served; - installing from git gives only
files: ['dist','scripts','README.md']— no
ui/source.
So consuming it means a build step that fetches and builds their source on every
update, plus implementing 16 API endpoints against wire.ts (1,033 lines, 78 types).
That is a real maintenance commitment, and the proof of concept found a bug of
exactly the kind it would keep producing: the module dependence counts came out
398 where theirs said 36, because counting all cross-module edges is not the
same as counting files that reference in. The picture looked right; every number
was wrong. A screenshot would not have caught it.
What the plugin is now
| Surface | Where |
|---|---|
| CodeGraph | the nav column — index state at a glance |
| CodeGraph | Settings → Plugins — all configuration |
And behind them, what Paperclip actually needs from a plugin:
- the eight CodeGraph tools, governed and audited through Paperclip's tool
gateway, with per-project and per-agent narrowing; - the MCP wiring that makes those tools reachable by an agent at all;
- repository resolution — a repository is a Paperclip project's workspace,
never a path an agent types.
To read the graph, run codegraph ui on the host. It is a local, read-only viewer
for the project you already indexed and needs no Paperclip wiring.
Removed
src/ui/page.tsx,map-view.tsx,views-panels.tsx,views.ts,
route-sidebar.tsx,reader-layout.ts,search-intent.ts, and their tests.- The
pageandrouteSidebarmanifest slots, and theui.page.register
capability — which also means the host's Back button no longer applies, because
there is no page to go back from. - Eight data handlers that only the reader used:
graph-reader,graph-map,
graph-entry-points,graph-dead-code,graph-search,graph-neighbourhood,
graph-source,graph-diagnose. - The nav entry is a status row rather than a link, since there is no longer a page
to link to.
Kept: the settings page in full (configuration, Activate, repositories, indexing,
exceptions), the nav entry's index state, and every tool and governance path.
Bundles: UI 130 KB → 51 KB, worker 737 KB → 707 KB.
Testing
463 tests, 458 passing and 5 skipped. Removing views removed their tests too —
which is the honest accounting: this is a smaller plugin, not a better-tested one.
v0.10.1 — overlays, toolbar, honest questions
paperclip-codegraph v0.10.1
Install
npm install -g @colbymchenry/codegraph@1.6.0 # the CodeGraph CLI, if you do not have it
paperclipai plugin install paperclip-codegraph@0.10.1Pin the version: Paperclip stores a caret range against what was installed, and for a
0.x version a caret does not cross a minor release, so a bare plugin install is a
no-op once the next minor is out.
The plugin installs disabled. Nothing changes for any agent until an organization
turns it on, in Settings → Plugins → CodeGraph.
Chrome placement, a search bar that matches the viewer's, and one promise the
placeholder was making that this plugin could not keep.
The Key is an overlay, bottom-left
It was a strip above the canvas, taking vertical space from the picture. It is now
an absolutely-positioned panel in the bottom-left of the graph, collapsible with
its Key ▾ toggle, exactly as CodeGraph's own viewer has it — that is what the
overlay is in the original, verified by reading its geometry rather than guessing.
Zoom and reset are an overlay, bottom-right
Same story: the controls moved from a toolbar to a compact vertical stack in the
bottom-right of the graph, in their own bordered card. Both overlays use
pointerEvents: none on the wrapper and auto on the contents, so drag-to-pan still
works through the gaps between them.
The toolbar reads like the viewer's
- Search is first and widest (
flex: 1 1 520px, capped at 720px — the viewer's
own input measures 708px). It was previously squeezed behind a "CodeGraph" label
and a repository name, which is why it looked cramped. - The "CodeGraph" label is gone. The page it sits on is already called
CodeGraph; the rail says so too. It was spending the width the search needed. - Repository and index state moved right, where they change once a session
rather than constantly. - The placeholder is now the viewer's own: Search a symbol or file, or ask "how
does execute reach getFile" — press / to focus.
The placeholder was promising something this plugin does not do
That sentence offers a natural-language question, and treating it as a symbol name
returned "No symbol matches" — the search box failing at something it advertised.
A question is now recognised (src/ui/search-intent.ts, 8 tests, pure) and
answered with what is true: that answering it needs the path search behind
CodeGraph's own Flow view, which this plugin does not implement because it keeps no
call-path history — and then four things that do work, including opening either
symbol in the reader so the path can be followed by hand from either end.
Recognising the pattern also means the common case is protected: createOrder,
orderEngine.ts, GET /api/health and src/api are all still plain searches. The
parser only fires on a phrase with two distinct symbols and a path verb, and falls
back to a name search on anything malformed — a misread question would replace a
working symbol list with an explanation.
Testing
511 tests, 494 passing and 17 skipped without an index. New: 8 for the question
parser, including the placeholder's own example and the cases that must not be
read as questions.