fix(installer): stop duplicating agent instructions; MCP server is the single source of truth (#529) (#538)

The installer wrote a `## CodeGraph` usage block into each agent's
instructions file (CLAUDE.md / AGENTS.md / GEMINI.md / .cursor/rules /
Kiro steering) that duplicated, almost verbatim, the guidance the MCP
server already emits in its `initialize` response — so agents that
surface MCP instructions (Claude Code) read the same playbook twice
every turn.

All 6 instruction-writing targets (claude, cursor, codex, opencode,
gemini, kiro) now stop writing the block. install self-heals by
stripping a block a previous version wrote (uninstall already did), so
the next `codegraph install`/`uninstall` cleans up existing installs;
upgrading the package alone does not (the leftover block is harmless).
server-instructions.ts is now the single source of truth — the two
steers unique to the old template ("trust codegraph, don't re-verify
with grep" and the not-initialized -> `init -i` hint) are ported there.

Removes the now-dead INSTRUCTIONS_TEMPLATE / CLAUDE_MD_TEMPLATE,
claude-md-template.ts, writeClaudeMd / hasClaudeMdSection, and the
Cursor-only wireProjectSurfaces bootstrap. The install log learned a
"Removed" verb. Tests rewritten to the new contract + self-heal
coverage (140/140 installer tests pass).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-05-28 15:13:23 -05:00
committed by GitHub
co-authored by Claude Opus 4.8
parent cea78ceb1b
commit a9c9e76d8c
18 changed files with 282 additions and 593 deletions
+16 -21
View File
@@ -37,13 +37,11 @@ import {
jsonDeepEqual,
readJsonFile,
removeMarkedSection,
replaceOrAppendMarkedSection,
writeJsonFile,
} from './shared';
import {
CODEGRAPH_SECTION_END,
CODEGRAPH_SECTION_START,
INSTRUCTIONS_TEMPLATE,
} from '../instructions-template';
function configDir(loc: Location): string {
@@ -85,7 +83,13 @@ class GeminiTarget implements AgentTarget {
install(loc: Location, _opts: InstallOptions): WriteResult {
const files: WriteResult['files'] = [];
files.push(writeMcpEntry(loc));
files.push(writeInstructionsEntry(loc));
// GEMINI.md is no longer written — the codegraph usage guidance
// ships in the MCP server's `initialize` response (issue #529).
// Strip a block a previous install left so an upgrade self-heals.
const instrCleanup = removeInstructionsEntry(loc);
if (instrCleanup.action === 'removed') files.push(instrCleanup);
return { files };
}
@@ -108,9 +112,7 @@ class GeminiTarget implements AgentTarget {
files.push({ path: file, action: 'not-found' });
}
const instr = instructionsPath(loc);
const action = removeMarkedSection(instr, CODEGRAPH_SECTION_START, CODEGRAPH_SECTION_END);
files.push({ path: instr, action });
files.push(removeInstructionsEntry(loc));
return { files };
}
@@ -146,22 +148,15 @@ function writeMcpEntry(loc: Location): WriteResult['files'][number] {
return { path: file, action };
}
function writeInstructionsEntry(loc: Location): WriteResult['files'][number] {
/**
* Strip the marker-delimited CodeGraph block from GEMINI.md if a prior
* install wrote one. Used by both install (self-heal on upgrade) and
* uninstall — see issue #529.
*/
function removeInstructionsEntry(loc: Location): WriteResult['files'][number] {
const file = instructionsPath(loc);
const dir = path.dirname(file);
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
const action = replaceOrAppendMarkedSection(
file,
INSTRUCTIONS_TEMPLATE,
CODEGRAPH_SECTION_START,
CODEGRAPH_SECTION_END,
);
const mapped: 'created' | 'updated' | 'unchanged' =
action === 'created' ? 'created'
: action === 'unchanged' ? 'unchanged'
: 'updated';
return { path: file, action: mapped };
const action = removeMarkedSection(file, CODEGRAPH_SECTION_START, CODEGRAPH_SECTION_END);
return { path: file, action };
}
export const geminiTarget: AgentTarget = new GeminiTarget();