Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/skills/codegraph-installer/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <n>` (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 <path...>`), 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.
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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).

</details>

Expand Down
68 changes: 68 additions & 0 deletions __tests__/installer-targets.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}-`));
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion site/src/content/docs/reference/mcp-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
17 changes: 16 additions & 1 deletion src/installer/instructions-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '<!-- CODEGRAPH_START -->';
export const CODEGRAPH_SECTION_END = '<!-- CODEGRAPH_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.
*
Expand All @@ -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 "<symbol names or question>"\` 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}`;
Loading