diff --git a/.claude/skills/codegraph-installer/SKILL.md b/.claude/skills/codegraph-installer/SKILL.md index 4ff80fc9a8..4f64714ac4 100644 --- a/.claude/skills/codegraph-installer/SKILL.md +++ b/.claude/skills/codegraph-installer/SKILL.md @@ -11,7 +11,7 @@ description: Use when editing anything under `src/installer/`, changing `codegra - `targets/types.ts` defines the `AgentTarget` interface. Adding an agent is **one new file in `targets/` + one entry in `registry.ts`**. Each target owns its config-file location, MCP-server JSON/TOML/JSONC writing, and any instructions-file integration described below. - `targets/toml.ts` is a hand-rolled TOML serializer scoped to `[mcp_servers.codegraph]` (used by Codex). Sibling tables and `[[array_of_tables]]` are preserved verbatim. No new dependency. - opencode reads `opencode.jsonc` by default; the installer prefers existing `.jsonc`, falls back to `.json`, and creates `.jsonc` for greenfield installs. Edits are surgical via `jsonc-parser` so user comments and formatting survive install/re-install/uninstall round-trips. The MCP entry is OpenCode 2's native `mcp.servers.codegraph` with `disabled: false` and `codemode: false` (so `codegraph_explore` stays on the native tool list); a pre-#1698 `mcp.codegraph` + `enabled` entry is migrated on re-install and removed by uninstall. -- `instructions-template.ts` holds a deliberately short marker-fenced pointer for subagents and non-MCP harnesses that cannot receive MCP `initialize` instructions. Claude, Codex, opencode, and Gemini upsert it into their instructions files; uninstall removes it. Cursor and Kiro do not write an instructions block and only strip legacy blocks. Keep detailed tool behavior in `server-instructions.ts` so the short pointer does not recreate the pre-#529 duplicated playbook. +- `instructions-template.ts` holds a deliberately short marker-fenced pointer for subagents and non-MCP harnesses that cannot receive MCP `initialize` instructions. The block carries one query-first sentence (`CODEGRAPH_QUERY_FIRST_SENTENCE`), measured as text in the instructions file; keep its wording stable and re-measure before changing it. Claude, Codex, opencode, Gemini and Devin upsert it into their instructions files; uninstall removes it. Cursor and Kiro do not write an instructions block and only strip legacy blocks. Keep detailed tool behavior in `server-instructions.ts` so the short pointer does not recreate the pre-#529 duplicated playbook. - All installer changes need matching coverage in `__tests__/installer-targets.test.ts`, including install idempotency, sibling preservation, uninstall reverses install, byte-equal re-runs returning `unchanged`, and partial-state recovery. ### Cursor MCP working-directory quirk diff --git a/AGENTS.md b/AGENTS.md index e48418f993..5de6f8a797 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -37,7 +37,7 @@ These stay here because breaking them is silent. The full procedures are in `cod An agent falls back to Read/Grep the instant a codegraph answer is insufficient. A flow question should resolve in 1 codegraph call on small repos and 3–5 on large ones, with Read/Grep at 0; judge every change by wall-clock time and tool-call count. -- Don't try to change the agent's tool choice through instructions or new tools. Make `codegraph_explore` answer better from the input it already gets. +- Don't try to change the agent's tool choice through instructions or new tools. Make `codegraph_explore` answer better from the input it already gets. The one exception is the query-first sentence in the installer's instructions block (`CODEGRAPH_QUERY_FIRST_SENTENCE`), kept on measured evidence; see `codegraph-installer`. - Reserve `isError` for security refusals and real malfunctions. Recoverable conditions (not indexed, symbol not found, file not in the index) return success-shaped guidance, and the tools stay exposed at an un-indexed root. - Explore output never tells the agent to "use Read". Keep `getExploreBudget` and `getExploreOutputBudget` monotonic with indexed file count. - Partial coverage is worse than none: bridge a dynamic-dispatch flow end to end and re-measure, or don't ship it. Synthesized edges are `provenance:'heuristic'` with `metadata.synthesizedBy` and `registeredAt`. diff --git a/CHANGELOG.md b/CHANGELOG.md index 0319f5925c..faded19993 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### New Features +- The instructions block `codegraph install` writes now also tells agents to query the CodeGraph tools before any Grep, Glob or Read; re-run `codegraph install` to rewrite an installed block. - `codegraph_sessions` and `codegraph sessions` take `full` / `--full` to return each hit's whole stored passage instead of a 24-token snippet, up to 16,000 bytes in all; hits past the budget keep their snippet and the answer says how many. Snippets stay the default. - `codegraph_sessions` and `codegraph sessions` take `context` / `--context ` (0 to 5, implies `full`) to return the passages either side of each hit from the same transcript. Neighbours spend the same byte budget after every hit's own passage, nearest first, and the answer says how many it left out. Passages are returned as stored, including any credentials a transcript holds. - **Experimental:** `codegraph_sessions` takes `projects` (CLI: `--project `), up to five project paths, to search several projects' session histories in one call. Each project keeps its own session index and answers as it would alone; the hits are interleaved by each project's own ranking (scores are not comparable across indexes), each names its project, and one limit and one 16,000-byte passage budget cover the whole list. A project with no transcripts, with `"sessions": false`, or without an index is listed as skipped and never fails the call. Searching refreshes each listed project's own session index, as a search inside it would. There is no allowlist and no config key, so the experiment is one file (`src/sessions/fanout.ts`) plus its callers. diff --git a/README.md b/README.md index 945f924882..330af921fd 100644 --- a/README.md +++ b/README.md @@ -643,7 +643,7 @@ The installer will: - Ask which agent(s) to configure — auto-detects installed ones from: **Claude Code**, **Cursor**, **Codex CLI**, **opencode**, **Hermes Agent**, **Gemini CLI**, **Antigravity IDE**, **Kiro**, **GitHub Copilot** (VS Code, Copilot CLI, JetBrains IDEs), **Devin** (CLI and Desktop) - Prompt to install `codegraph` on your PATH (so agents can launch the MCP server) - Ask whether configs apply to all your projects or just this one -- Write each chosen agent's MCP server config, plus a small marker-fenced CodeGraph section in the agent's instructions file (`CLAUDE.md` / `AGENTS.md` / `GEMINI.md`) — that's how subagents and non-MCP agents learn the `codegraph explore` command, since the MCP server's own guidance only reaches the main agent. Removed cleanly by `codegraph uninstall`. +- Write each chosen agent's MCP server config, plus a small marker-fenced CodeGraph section in the agent's instructions file (`CLAUDE.md` / `AGENTS.md` / `GEMINI.md`) — that's how subagents and non-MCP agents learn the `codegraph explore` command (and, for Claude, Codex, opencode, Gemini and Devin, to query CodeGraph first), since the MCP server's own guidance only reaches the main agent. Removed cleanly by `codegraph uninstall`. - Set up auto-allow permissions when Claude Code is one of the targets The installer **wires up your agents only — it does not index your code.** After it finishes, build each project's graph yourself with `codegraph init` (step 3). One global `codegraph install` covers every project; you run `codegraph init` once per project. @@ -733,7 +733,7 @@ CodeGraph's MCP server delivers its usage guidance to your agent **automatically - **Trust the results — don't re-verify with grep**, and check the staleness banner after edits. - Works **per project**: query any project that has a `.codegraph/` index by passing `projectPath` — so a monorepo where only some services are indexed, or a second repo, works in one session. A path with no index returns clean guidance to use built-in tools; indexing stays your decision. -The exact text is `src/mcp/server-instructions.ts` — the single source of truth for the main agent. Because subagents and non-MCP harnesses never see the MCP guidance, the installer also writes a short marker-fenced section into the agent's instructions file pointing at the `codegraph explore` CLI equivalent. +The exact text is `src/mcp/server-instructions.ts` — the single source of truth for the main agent. Because subagents and non-MCP harnesses never see the MCP guidance, the installer also writes a short marker-fenced section into the agent's instructions file pointing at the `codegraph explore` CLI equivalent (for Claude, Codex, opencode, Gemini and Devin it also tells the agent to query CodeGraph before Grep, Glob or Read). diff --git a/__tests__/installer-targets.test.ts b/__tests__/installer-targets.test.ts index 4b714f1d5d..cc556b6522 100644 --- a/__tests__/installer-targets.test.ts +++ b/__tests__/installer-targets.test.ts @@ -24,6 +24,12 @@ import { ALL_TARGETS, getTarget, resolveTargetFlag } from '../src/installer/targ import { uninstallTargets, refreshTargets, offerWatchFallback } from '../src/installer'; import { upsertTomlTable, removeTomlTable, buildTomlTable } from '../src/installer/targets/toml'; import { cleanupLegacyHooks, writePromptHookEntry, removePromptHookEntry } from '../src/installer/targets/claude'; +import { + CODEGRAPH_INSTRUCTIONS_BLOCK, + CODEGRAPH_QUERY_FIRST_SENTENCE, + CODEGRAPH_SECTION_END, + CODEGRAPH_SECTION_START, +} from '../src/installer/instructions-template'; function mkTmpDir(label: string): string { return fs.mkdtempSync(path.join(os.tmpdir(), `cg-targets-${label}-`)); @@ -231,6 +237,68 @@ describe('Installer targets — contract', () => { } }); +describe('Installer targets — query-first sentence in the instructions block', () => { + let tmpHome: string; + let tmpCwd: string; + let origCwd: string; + let homeRestore: { restore: () => void }; + + beforeEach(() => { + tmpHome = mkTmpDir('home'); + tmpCwd = mkTmpDir('cwd'); + origCwd = process.cwd(); + process.chdir(tmpCwd); + homeRestore = setHome(tmpHome); + }); + + afterEach(() => { + homeRestore.restore(); + process.chdir(origCwd); + fs.rmSync(tmpHome, { recursive: true, force: true }); + fs.rmSync(tmpCwd, { recursive: true, force: true }); + }); + + it('uses the measured wording', () => { + expect(CODEGRAPH_QUERY_FIRST_SENTENCE).toBe( + 'Query the codegraph MCP tools first for every repo question — symbols, files, call paths, and documentation alike — before any Grep, Glob, or Read. Use Grep or Read only to verify a result or when the index returns nothing relevant.', + ); + }); + + it.each(['claude', 'codex', 'opencode', 'gemini', 'devin'])( + '%s: writes the query-first sentence inside the markers, ahead of the unindexed-repo line', + (id) => { + const target = getTarget(id)!; + const result = target.install('global', { autoAllow: true }); + const instructions = result.files.find((f) => /(CLAUDE|AGENTS|GEMINI)\.md$/.test(f.path))!; + const body = fs.readFileSync(instructions.path, 'utf-8'); + const start = body.indexOf(CODEGRAPH_SECTION_START); + const end = body.indexOf(CODEGRAPH_SECTION_END); + const sentence = body.indexOf(CODEGRAPH_QUERY_FIRST_SENTENCE); + const unindexed = body.indexOf('If there is no `.codegraph/` directory'); + expect(body.split(CODEGRAPH_QUERY_FIRST_SENTENCE)).toHaveLength(2); + expect(start).toBeLessThan(sentence); + expect(sentence).toBeLessThan(unindexed); + expect(unindexed).toBeLessThan(end); + }, + ); + + it('re-install rewrites the previous short block to the current one, keeping user content', () => { + const claude = getTarget('claude')!; + const claudeMd = path.join(tmpHome, '.claude', 'CLAUDE.md'); + fs.mkdirSync(path.dirname(claudeMd), { recursive: true }); + const previousBlock = CODEGRAPH_INSTRUCTIONS_BLOCK.replace(`${CODEGRAPH_QUERY_FIRST_SENTENCE}\n\n`, ''); + expect(previousBlock).not.toContain(CODEGRAPH_QUERY_FIRST_SENTENCE); + fs.writeFileSync(claudeMd, `# Mine\n\nAlways respond in pirate.\n\n${previousBlock}\n`); + + claude.install('global', { autoAllow: true }); + + const body = fs.readFileSync(claudeMd, 'utf-8'); + expect(body).toContain('Always respond in pirate.'); + expect(body.split(CODEGRAPH_QUERY_FIRST_SENTENCE)).toHaveLength(2); + expect(body.split(CODEGRAPH_SECTION_START)).toHaveLength(2); + }); +}); + describe('Installer targets — partial-state idempotency', () => { let tmpHome: string; let tmpCwd: string; diff --git a/site/src/content/docs/reference/mcp-server.md b/site/src/content/docs/reference/mcp-server.md index ddc5be01c3..49ec864ee9 100644 --- a/site/src/content/docs/reference/mcp-server.md +++ b/site/src/content/docs/reference/mcp-server.md @@ -75,4 +75,4 @@ A named COBOL copybook prioritizes its indexed source and lists COPY and EXEC SQ CodeGraph *is* the pre-built search index. For "how does X work?", architecture, a flow ("how does X reach Y"), or where-is-X questions — and while editing code — an agent should answer with `codegraph_explore` and stop, typically with **zero file reads**, rather than re-deriving the answer with `grep` + `Read`. A direct CodeGraph answer is one to a few calls; a grep/read exploration is dozens. -The MCP server delivers this guidance to the main agent automatically, in the MCP `initialize` response. Because subagents and non-MCP harnesses never see that response, the installer also writes a short marker-fenced section into each agent's instructions file pointing at the `codegraph explore` CLI equivalent. +The MCP server delivers this guidance to the main agent automatically, in the MCP `initialize` response. Because subagents and non-MCP harnesses never see that response, the installer also writes a short marker-fenced section into each agent's instructions file pointing at the `codegraph explore` CLI equivalent (for Claude, Codex, opencode, Gemini and Devin it also tells the agent to query CodeGraph before Grep, Glob or Read). diff --git a/src/installer/instructions-template.ts b/src/installer/instructions-template.ts index fd2b4a3290..5cff911933 100644 --- a/src/installer/instructions-template.ts +++ b/src/installer/instructions-template.ts @@ -22,13 +22,26 @@ * * Keep this block SHORT. The main agent reads it every turn on top of the * server instructions — the #529 duplication-cost argument still bounds - * its size. Command names and the two surfaces, nothing more. + * its size. Command names, the two surfaces and the one query-first + * instruction below, nothing more. */ /** Markers used by the marker-based section write/removal. */ export const CODEGRAPH_SECTION_START = ''; export const CODEGRAPH_SECTION_END = ''; +/** + * The query-first instruction inside the block. Without it, agents that + * have the block still reach for Grep and Read first; as text in the + * instructions file it cut tool calls on Opus 5.5, Astra 6 and Sol about as + * much as the same sentence in a system prompt. It sits ahead of the + * "no \`.codegraph/\` directory" line so that line still scopes it to + * indexed repositories. Keep the wording stable: the measured text is this + * sentence. + */ +export const CODEGRAPH_QUERY_FIRST_SENTENCE = + 'Query the codegraph MCP tools first for every repo question — symbols, files, call paths, and documentation alike — before any Grep, Glob, or Read. Use Grep or Read only to verify a result or when the index returns nothing relevant.'; + /** * The full block, markers included, exactly as written to disk. * @@ -47,5 +60,7 @@ In repositories indexed by CodeGraph (a \`.codegraph/\` directory exists at the - **MCP tool** (when available): \`codegraph_explore\` answers most code questions in one call — the relevant symbols' verbatim source plus the call paths between them, including dynamic-dispatch hops grep can't follow. Name a file or symbol in the query to read its current line-numbered source. If it's listed but deferred, load it by name via tool search. - **Shell** (always works): \`codegraph explore ""\` prints the same output. +${CODEGRAPH_QUERY_FIRST_SENTENCE} + If there is no \`.codegraph/\` directory, skip CodeGraph entirely — indexing is the user's decision. ${CODEGRAPH_SECTION_END}`;