diff --git a/__tests__/server-instructions.test.ts b/__tests__/server-instructions.test.ts new file mode 100644 index 000000000..c60b11f96 --- /dev/null +++ b/__tests__/server-instructions.test.ts @@ -0,0 +1,43 @@ +/** + * Server Instructions Tests + * + * The MCP `initialize` instructions land in the agent's system prompt on + * EVERY session, for every client — so they carry a size budget (the header + * of server-instructions.ts already demands "keep it tight"; this pins it), + * and the literal marker strings that agents pattern-match on (staleness + * banners, the dedup pointer line) must survive any rewording. + */ + +import { describe, it, expect } from 'vitest'; +import { + SERVER_INSTRUCTIONS, + SERVER_INSTRUCTIONS_NO_ROOT_INDEX, +} from '../src/mcp/server-instructions'; + +describe('server instructions', () => { + it('keeps the literal marker strings agents pattern-match on', () => { + expect(SERVER_INSTRUCTIONS).toContain( + '⚠️ Some files referenced below were edited since the last index sync', + ); + expect(SERVER_INSTRUCTIONS).toContain('⚠️ CodeGraph auto-sync is DISABLED'); + expect(SERVER_INSTRUCTIONS).toContain('changed on disk after the last index sync'); + expect(SERVER_INSTRUCTIONS).toContain('Already sent earlier in this conversation'); + }); + + it('keeps the core operative guidance', () => { + expect(SERVER_INSTRUCTIONS).toContain('codegraph_explore'); + expect(SERVER_INSTRUCTIONS).toContain('codegraph init'); + // The default MCP surface is codegraph_explore alone — no other tool + // may be named (they are hidden unless re-enabled via CODEGRAPH_MCP_TOOLS). + expect(SERVER_INSTRUCTIONS).not.toMatch(/codegraph_(?!explore)\w+/); + expect(SERVER_INSTRUCTIONS_NO_ROOT_INDEX).toContain('projectPath'); + expect(SERVER_INSTRUCTIONS_NO_ROOT_INDEX).toContain('codegraph init'); + }); + + it('stays inside the per-session size budget (chars ≈ tokens×4)', () => { + // ~1k tokens. Injected into every session of every MCP client; growth + // here is paid by every agent conversation before any work happens. + expect(SERVER_INSTRUCTIONS.length).toBeLessThan(4000); + expect(SERVER_INSTRUCTIONS_NO_ROOT_INDEX.length).toBeLessThan(1500); + }); +}); diff --git a/src/mcp/server-instructions.ts b/src/mcp/server-instructions.ts index c5a1b688b..6a3226b31 100644 --- a/src/mcp/server-instructions.ts +++ b/src/mcp/server-instructions.ts @@ -19,59 +19,57 @@ */ export const SERVER_INSTRUCTIONS = `# Codegraph — code intelligence over an indexed knowledge graph -Codegraph is a SQLite knowledge graph of every symbol, edge, and file in -the workspace — pre-computed structure you would otherwise re-derive by -reading files (cached intelligence: thousands of parse/trace decisions you -don't pay to re-reason each run). It indexes 30+ languages -(TypeScript/JavaScript, Python, Go, Rust, Java, C#, C/C++, PHP, Ruby, Swift, -Kotlin, and more) — don't assume a language here isn't covered. Reads are -sub-millisecond; the index lags writes by ~1s through the file watcher. Reach for it BEFORE *and* while -writing or editing code — not just for questions: one call returns the -verbatim source PLUS who calls it and what it affects, so you edit with the -blast radius in view. More accurate context, in far fewer tokens and -round-trips than reading files yourself. +Codegraph is a SQLite knowledge graph of every symbol, edge, and file in the +workspace, covering 30+ languages (TypeScript/JavaScript, Python, Go, Rust, +Java, C#, C/C++, PHP, Ruby, Swift, Kotlin, …) — don't assume a language isn't +covered. Reads are sub-millisecond; the index lags writes by ~1s through the +file watcher. ## One tool: codegraph_explore — use it instead of reading files -There is a single tool, \`codegraph_explore\`, and it is Read-equivalent. It -takes either a natural-language question or a bag of symbol/file names and -returns the **verbatim, line-numbered source** of the relevant symbols -grouped by file — the same \`\\t\` shape \`Read\` gives you, safe to -\`Edit\` from — PLUS the call path among them (including dynamic-dispatch hops -like callbacks, React re-render, and JSX children that grep can't follow) and -a blast-radius summary of what depends on them. +\`codegraph_explore\` is Read-equivalent. Give it a natural-language question +("how does X work", a bug, "what/where is X", an area survey) or a bag of +symbol/file names; one call returns the **verbatim, line-numbered source** of +the relevant symbols grouped by file — the same \`\\t\` shape \`Read\` +gives you, safe to \`Edit\` from — PLUS the call path among them (including +dynamic-dispatch hops like callbacks, React re-render, and JSX children that +grep can't follow) and a blast-radius summary of what depends on them. For an +overloaded name it returns every matching definition's body in one call. -Whether you're answering "how does X work" or implementing a change (fixing a -bug, adding a feature), call \`codegraph_explore\` before you Read. ONE call -usually answers the whole question. Codegraph IS the pre-built search index — -so running your own grep + read loop, or delegating the lookup to a separate -file-reading sub-task/agent, repeats work codegraph already did and costs more -for the same answer. A direct codegraph answer is typically one to a few -calls; a grep/read exploration is dozens. - -## How to query - -- **Almost any question — "how does X work", architecture, a bug, "what/where is X", or surveying an area** → \`codegraph_explore\` with a natural-language question or the relevant names. ONE capped call returns the verbatim source grouped by file; most often the ONLY call you need. -- **"How does X reach/become Y? / the flow / the path from X to Y"** → \`codegraph_explore\`, naming the symbols that span the flow (e.g. \`mutateElement renderScene\`) — it surfaces the call path among them, riding dynamic-dispatch hops, and returns their source. -- **Reading or editing a file/symbol you can name** → put its name or file path in the \`codegraph_explore\` query — it returns that current line-numbered source (safe to \`Edit\` from) with the call path and blast radius attached, so you don't Read it separately. For an overloaded name it returns every matching definition's body in one call. -- **Need more?** Call \`codegraph_explore\` again with more specific names — treat the source it returns as already Read. +Use it BEFORE and while writing or editing indexed code, not just for +questions: one to a few calls replace the dozens of round-trips of a +grep + Read loop (or a delegated file-reading sub-task/agent — both repeat +work the index already did). For a flow ("how does X reach/become Y?"), name +the symbols spanning the flow in one query (e.g. \`mutateElement renderScene\`) +— it surfaces the call path between them. Need more? Query again with more +specific names, and treat any source it returned as already Read. ## Anti-patterns -- **Trust codegraph's results — don't re-verify them with grep.** They come from a full AST parse; re-checking with grep is slower, less accurate, and wastes context. -- **Don't grep or Read first** to find or understand indexed code — ONE \`codegraph_explore\` returns the relevant symbols' source together in a single round-trip. Reach for raw \`Read\`/\`Grep\` only to confirm a specific detail codegraph didn't cover, or for what codegraph doesn't index (configs, docs). -- **Don't reconstruct a flow by hand** — name the endpoints in one \`codegraph_explore\` and it surfaces the path between them, dynamic-dispatch hops included. -- **After editing, check the staleness banner.** When a tool response starts with "⚠️ Some files referenced below were edited since the last index sync…", the listed files are pending re-index — Read those specific files for accurate content. Every file NOT in that banner is fresh, so still trust codegraph. A different, rarer banner — "⚠️ CodeGraph auto-sync is DISABLED…" — means live watching stopped entirely (the whole index is frozen, not just a few files); until it's resolved, Read files directly to confirm anything that may have changed. -- **A file flagged "⚠ changed on disk after the last index sync" drifted from its index** (most common on projects queried via \`projectPath\`, which have no live watcher). Codegraph never serves a possibly-mis-sliced body from such a file — it either shows the file's full CURRENT source (trust it as a Read) or omits the source with this flag. When the source was omitted, Read that specific file; line numbers referencing it elsewhere in the response may be shifted until that project's next sync. All unflagged files remain trustworthy. +- **Don't re-verify codegraph's results with grep.** They come from a full + AST parse; grep re-checking is slower, less accurate, and wastes context. +- **Don't grep or Read first** to find or understand indexed code. Raw + \`Read\`/\`Grep\` is for confirming a specific detail codegraph didn't cover, + or for what it doesn't index (configs, docs). +- **Don't reconstruct a flow by hand** — name the endpoints in one call. + +## Staleness signals -- **"Already sent earlier in this conversation" is a pointer, not a gap.** When a file's section carries that line instead of (or above) its source, an earlier \`codegraph_explore\` in THIS conversation already returned those exact lines and the file has not changed since — so the copy already in your context is current and exact. Scroll back to it; don't re-fetch it and don't Read the file. The bytes it freed went into source you have not seen yet, elsewhere in the same response. +- A response starting "⚠️ Some files referenced below were edited since the last index sync…": the LISTED files are pending re-index — Read those specific files; every file not listed is fresh, so still trust codegraph. +- The rarer "⚠️ CodeGraph auto-sync is DISABLED…" banner: live watching stopped and the whole index is frozen — until resolved, Read files directly to confirm anything that may have changed. +- A file flagged "⚠ changed on disk after the last index sync" drifted from its index (most common via \`projectPath\`, which has no live watcher). Codegraph never serves a possibly-mis-sliced body from such a file: it either shows the file's full CURRENT source (trust it as a Read) or omits the source with this flag — then Read that file, and expect line numbers referencing it elsewhere in the response to be shifted until that project's next sync. Unflagged files remain trustworthy. +- **"Already sent earlier in this conversation" is a pointer, not a gap**: an earlier \`codegraph_explore\` in THIS conversation already returned those exact lines and the file hasn't changed since — scroll back to that copy; don't re-fetch and don't Read. The freed bytes went into source elsewhere in the response you haven't seen yet. ## Limitations -- If a tool reports a project isn't indexed (no \`.codegraph/\`), stop calling codegraph tools for that project for the rest of the session and use your built-in tools there instead. Indexing is the user's decision — mention they can run \`codegraph init\` if it comes up, but don't run it yourself. -- Index lags file writes by ~1 second. -- Cross-file resolution is best-effort name matching; ambiguous calls may return multiple candidates. -- No live correctness validation — that's still the TypeScript compiler / test suite / linter's job. Codegraph supplements those with structural context they don't have. +- If a tool reports a project isn't indexed (no \`.codegraph/\`), stop calling + codegraph tools for that project this session and use built-in tools there. + Indexing is the user's decision — mention \`codegraph init\` if it comes up, + but don't run it yourself. +- Cross-file resolution is best-effort name matching; ambiguous calls may + return multiple candidates. +- No live correctness validation — that's still the compiler / test suite / + linter's job; codegraph supplements them with structural context. `; /**