New `codegraph cycles [path] [--json]` prints the groups of files that
all reach each other through imports. It exits 1 when there are any and
0 when there are none, so `codegraph cycles || exit 1` works as a
pre-commit or CI check.
The command stays a thin wrapper over CodeGraph.findCircularDependencies(),
but the method itself had to change. It had no callers, and on real
repos its walk wasn't fit to print:
- It reported one list per back edge, so the number depends on the walk:
1,490 overlapping lists on excalidraw, 87 on gin, 44 on flask.
- It left out files. A walk that meets a visited file stops there, so a
file whose loop closes through one is never listed (now pinned by a
test: a>b, b>a, a>c, c>b).
- It counted every edge kind, and most of the graph's edges aren't
imports. An interface's call fans out to a test's fake, so
src/extraction/parse-pool.ts "depended" on its own test. Two Next.js
pages that link to each other were a cycle. gin's files were a 55-file
knot, though Go forbids import cycles. With exit 1 those are failures
nobody can fix.
The method now takes the strongly connected components of the file
import graph, one group per knot. The Tarjan pass moves from the
viewer's map into src/graph/file-cycles.ts and both surfaces use it, so
the viewer's "Circular imports between files" panel, which was reading
every edge too, now reports the same groups. Dependent counts on the map
keep their own edge list.
Validation: new cli-cycles and file-cycles suites, plus cycles in the
read-commands suite (mid-rebuild, and old schema indexes); 4 of the 10
findCircularDependencies and finder tests fail with main's method and
all 15 pass with this one. Against the TypeScript compiler's own module
resolver over the same files (kit oracle): excalidraw 3 groups, 369
files, all identical; codegraph's src/ 3 of 4 identical, and the fourth
needs src/resolution/name-matcher.ts, which at 1.6 MB is over the
default size limit and so isn't parsed. flask reports 2 groups and gin
none. It runs in under a second on each. Type-only imports count, as
they aren't told apart in the graph.
The MCP tool from PR #889 is left out on purpose: a new tool is rarely
picked up, and this needs no agent steering.
Reimplements PR #1012 by @jiangyq9, with the exit code from the CLI in
PR #889 by @goutham80808.
Fixes#888
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The warning for a git worktree answered from another checkout's index
still said to run "codegraph init -i", a flag that has done nothing
since init started indexing by default. Its one-line form is prepended
to every read-tool result and codegraph_status embeds the long form, so
both told the agent to build the index, which the server instructions
rule out ("Indexing is the user's decision ... don't run it yourself").
The CLI's codegraph status now says: Run "codegraph init" in this
worktree. What an agent reads (the read-tool notice, which also reaches
it through the prompt hook and the explore/node CLI, and
codegraph_status) says the user can run "codegraph init" there.
Docs that no longer matched the code:
- AGENTS.md listed 4 of the 11 installer targets, called a new one "a
5th agent", said the contract suite covers "all 4" targets, and listed
11 of the 24 subcommands. It now lists every target and subcommand,
and names the hidden ones (serve --mcp, prompt-hook, and ui/web unless
CODEGRAPH_UI=1).
- README's Supported Agents section said no instructions file is
written, but Claude Code, Codex CLI, Gemini CLI and opencode get the
short CodeGraph section. Its Codex entry now says the Codex IDE
extension reads the same config: $CODEX_HOME/config.toml (default
~/.codex/config.toml) for a global install, and .codex/config.toml,
used only in a trusted project, for a local one.
- README's WSL2 "Transport closed" entry named only the in-process
fallback. The daemon also moves its socket to the temp directory when
the project's filesystem can't host one.
- init -i and -iv leftovers in internal docs, scripts, the add-lang
skill and the repo's own Cursor rule, which now also leaves indexing
to the user. Two stay on purpose: scripts/agent-eval/audit.sh can
audit a published version older than 0.9.8, where init built the
index only with -i (now commented there), and
docs/benchmarks/codegraph-ab-matrix.md records a 0.9.4 run.
Validation:
- worktree-detection.test.ts: on main 61776927's worktree.ts and
tools.ts, 4 of its 20 tests fail (the CLI wording, the agent wording,
the read-tool notice and codegraph_status). With only the worktree.ts
fix, the codegraph_status test still fails. All 20 pass here, and so
do mcp-nested-repo-projectpath.test.ts's 13.
- Full suite on this patch over 61776927 (CODEGRAPH_KERNEL_EXPECT=1,
4 workers): 815 files (809 passed, 6 skipped), 9608 tests passed,
0 failed, 86 skipped.
- Rebased unchanged onto f67eafcb. Its three new commits share only
CHANGELOG.md with this PR, in other bullets. On the rebased tree these
pass (81 tests): worktree-detection, mcp-nested-repo-projectpath, and
the new commits' default-export-declaration,
rust-path-attribute-modules, rust-path-super-glob,
search-special-characters and strip-comments.
Reimplements #549 by @ashishpatel26, #651 by @GaoSSR, #885 and #886 by @Dashsoap, and #1083 by @zengwenliang416.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AGENTS.md said the installer no longer writes an instructions-file block,
but since PR #704 the Claude, Codex, Gemini and opencode targets write a
short one again, while Cursor and Kiro only strip an old one. The
architecture notes and house rules now describe that block and keep tool
guidance in server-instructions.ts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two surfaces label a synthesized (dynamic-dispatch) `calls` hop:
codegraph_explore's Flow and dynamic-dispatch links plus the
codegraph_node trail (`dynamic: interface → impl @file:line`), and
ContextBuilder's "## Call paths", which `codegraph context` prints
(`→[…]`). Each kept its own list. The call paths' list stopped at
callback, react-render, jsx-render, vue-handler, http-client, queue-job
and socket messages, so every other synthesizer fell through to
`event ${m.event ?? ''}`: an interface-impl hop read
`→[event @src/pdf-exporter.ts:5]`, and so did a C++ override, a redux
thunk, a gin middleware chain or a closure collection.
The wording now lives once, in src/graph/synthesized-hop.ts
(`describeSynthesizedHop`: a summary, the detail only the call paths
print, and the wiring site), as AGENTS.md asks of a derivation two
surfaces render. ToolHandler renders `dynamic: <summary> @site`, the
call paths `<summary><detail> @site`. Explore and the node trail print
exactly what they printed before; the call paths keep their wording for
the kinds they already labelled, including the queue name, a socket's
direction and the HTTP clause, and now name the rest the way explore
does. A bus event reads `bus event` there too, and the section's
footnote no longer says hops are marked `[callback/event …]`.
The helper also holds the wording #2414 gives a Go method a struct gets
by embedding (`metadata.promotedInto`), so that arm moves here too. The
unused long `label` form of ToolHandler.synthEdgeNote (last read by the
removed codegraph_trace) is dropped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On Windows a worker thread that ends - terminate(), its own process.exit(),
or the process exiting - while V8's concurrent marker is marking its heap
crashes the whole process (exit 3221225477 / 0xC0000005, no error, no dump).
Bisected on the Windows 11 ARM64 VM: --no-concurrent-marking alone stops it
(18/480 -> 0/480; no other concurrency flag does), but it costs about a third
of indexing speed, so CodeGraph avoids the dangerous moments instead:
- src/worker-teardown.ts: owners terminate a worker only once it has started
(its first message, posted after its modules load - mid-load is by far the
worst moment), and a worker about to exit runs a full collection first,
which finishes any marking in flight (gc reached at runtime, never global).
- Resolver pool / store writer: the worker collects before exiting on
'close' (the normal path - main 21/480 crashed on the VM, now 2/1200), and
the 5 s close fallback never terminates a worker still starting.
- codegraph_status change counts: the worker posts 'loaded' after its heavy
require and collects before answering; a deadline answers unknown at once
but terminates only once loaded (3/720 -> 0/1440), and MCPEngine.stop()
waits for measurements in flight.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Running the suite on a machine with Claude Code configured could wire the
CodeGraph prompt hook into the developer's own ~/.claude/settings.json:
every upgrade test whose fake upgrade succeeded ran the real post-upgrade
prompt-hook self-heal, which detected the real global Claude profile and
wrote it. Daemon suites likewise left records in the real
~/.codegraph/daemons. On Windows a HOME-only redirect would not have
helped, since os.homedir() reads USERPROFILE there.
- The prompt-hook writer is now injected through UpgradeDeps
(wirePromptHook); the CLI passes the real defaultWirePromptHook and the
upgrade tests a recorder. New tests pin that a successful upgrade calls
the recorder and that the real writer never touches a configured profile.
- A vitest setupFiles entry gives every engine test file a throwaway home:
HOME/USERPROFILE (plus HOMEDRIVE/HOMEPATH/APPDATA/LOCALAPPDATA on
Windows), XDG_CONFIG_HOME, and GIT_CONFIG_GLOBAL (seeded with a dummy
identity) point into it, and CLAUDE_CONFIG_DIR/CODEX_HOME/COPILOT_HOME/
HERMES_HOME are cleared. Spawned CLI/MCP children inherit it. A sandbox
test asserts os.homedir(), the Claude profile, the daemon registry, a
child process and git's global config all resolve inside it.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The 1.6.1 bump changed package.json only, as AGENTS.md said was enough, so
ui-package.test.ts ("is versioned with the engine") has failed on main since.
Bump ui/package.json to match, and say in AGENTS.md that a version bump
touches both files.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(cli): withhold `codegraph ui` until the viewer launches (CODEGRAPH_UI=1 opts in)
The viewer has been exercised on Expo apps only, not on API, Laravel,
Spring, Angular or Swift projects, so it stays out of this release. `ui` and
its alias `web` — also as `help ui` or `ui --help` — are refused before any
startup work, and the command is hidden from `--help`, unless CODEGRAPH_UI=1
is set, which keeps it usable for the people testing it. The viewer assets
still ship, so the opt-in works on a released build.
Reverting this commit is the launch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(site): say the viewer is not in a release yet
The viewer pages have been live on the docs site since they landed, and
1.6.0 answers `codegraph ui` with "unknown command" (#1816, #1900). Until the
viewer launches, the guide, the CLI reference and the next-steps link say so.
Reverting this commit is part of the launch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* docs(changelog): hold the viewer's entries for its launch; new Highlights for the fixes release
53 entries that describe `codegraph ui` and its screens move, verbatim, to
docs/viewer-launch-changelog.md along with the launch Highlights. 12 entries
whose graph and `codegraph_explore` half ships now (the router navigation
edges, the cross-tier hops, explore's `when` conditions, SvelteKit layouts,
wrapped Express handlers, …) are reworded around the graph; their original
wording is kept in the holding file for the launch. A few kept entries lose a
viewer clause. The Highlights are rewritten for this release.
prepare-release.mjs promotes the section and extract-release-notes.mjs reads
it back with no mention of the viewer; the released sections are untouched.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
OpenCode 2 exposes MCP tools through Code Mode by default; `codemode: false`
only survives on `mcp.servers.<name>` with `disabled`. The installer wrote the
v1 `mcp.codegraph` + `enabled` shape, so the opt-out was dropped on normalize.
Write `mcp.servers.codegraph` with `disabled: false` and `codemode: false`,
migrate a leftover v1 entry on re-install, uninstall either shape, and keep
printConfig / README / AGENTS.md in sync.
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>
Codex reads AGENTS.md by default, not CLAUDE.md. Move project guidance to
AGENTS.md (trim/nest long validation notes under docs/AGENTS.md), leave
CLAUDE.md as a thin @AGENTS.md wrapper for Claude Code, and document the
Codex project_doc_max_bytes raise needed for the ~35KiB root file.
Co-authored-by: Colby McHenry <colbymchenry@users.noreply.github.com>