Adds a cross-channel uninstall that removes CodeGraph from every agent it's configured on (Claude Code, Cursor, Codex CLI, opencode, Hermes). Prompts global-vs-local up front (no flags required) and reports which providers it actually hit; --location / --target / --yes supported for non-interactive use. Removes only what install wrote; leaves the .codegraph/ index to `uninit`. Also fixes Cursor uninstall leaving an orphaned .cursor/rules/codegraph.mdc (its description: CodeGraph frontmatter lingered); the dedicated rules file is now deleted outright while user content outside our markers is preserved. Validated end-to-end on macOS and Docker Linux (global + local sweeps clean). Adds 8 tests; full suite 730 passing. Bumps to 0.9.3 with CHANGELOG entry. Resolves #313. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
77e29c436c
commit
bf73f4d05c
@@ -46,7 +46,6 @@ import {
|
||||
getMcpServerConfig,
|
||||
jsonDeepEqual,
|
||||
readJsonFile,
|
||||
removeMarkedSection,
|
||||
replaceOrAppendMarkedSection,
|
||||
writeJsonFile,
|
||||
} from './shared';
|
||||
@@ -140,9 +139,7 @@ class CursorTarget implements AgentTarget {
|
||||
}
|
||||
|
||||
if (loc === 'local') {
|
||||
const rules = rulesPath();
|
||||
const action = removeMarkedSection(rules, CODEGRAPH_SECTION_START, CODEGRAPH_SECTION_END);
|
||||
files.push({ path: rules, action });
|
||||
files.push(removeRulesEntry());
|
||||
}
|
||||
|
||||
return { files };
|
||||
@@ -237,4 +234,57 @@ function writeRulesEntry(): WriteResult['files'][number] {
|
||||
return { path: file, action: mapped };
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the Cursor rules file on uninstall.
|
||||
*
|
||||
* Unlike the shared CLAUDE.md / AGENTS.md files (where codegraph owns
|
||||
* only a marker-delimited section), `.cursor/rules/codegraph.mdc` is a
|
||||
* file we create OUTRIGHT — the frontmatter is ours too. So a plain
|
||||
* `removeMarkedSection` is wrong here: it would strip our instruction
|
||||
* block but leave the orphaned `description: CodeGraph ...` frontmatter
|
||||
* behind, so the file lingers and still "mentions" codegraph.
|
||||
*
|
||||
* Instead: strip our block, and if nothing but our own frontmatter
|
||||
* remains, delete the whole file. Only when the user has added their
|
||||
* own content outside our markers do we keep the file (minus our block).
|
||||
*/
|
||||
function removeRulesEntry(): WriteResult['files'][number] {
|
||||
const file = rulesPath();
|
||||
if (!fs.existsSync(file)) return { path: file, action: 'not-found' };
|
||||
|
||||
let content: string;
|
||||
try {
|
||||
content = fs.readFileSync(file, 'utf-8');
|
||||
} catch {
|
||||
return { path: file, action: 'not-found' };
|
||||
}
|
||||
|
||||
const ourFrontmatter = MDC_FRONTMATTER.trim();
|
||||
const startIdx = content.indexOf(CODEGRAPH_SECTION_START);
|
||||
const endIdx = content.indexOf(CODEGRAPH_SECTION_END);
|
||||
|
||||
// Our marked block is present — strip it, then decide what's left.
|
||||
if (startIdx !== -1 && endIdx > startIdx) {
|
||||
const before = content.substring(0, startIdx).trimEnd();
|
||||
const after = content.substring(endIdx + CODEGRAPH_SECTION_END.length).trimStart();
|
||||
const remainder = (before + (before && after ? '\n\n' : '') + after).trim();
|
||||
if (remainder === '' || remainder === ourFrontmatter) {
|
||||
try { fs.unlinkSync(file); } catch { /* ignore */ }
|
||||
} else {
|
||||
atomicWriteFileSync(file, remainder + '\n');
|
||||
}
|
||||
return { path: file, action: 'removed' };
|
||||
}
|
||||
|
||||
// No block, but the file is still our pristine frontmatter-only file
|
||||
// — it's ours, so remove it.
|
||||
if (content.trim() === ourFrontmatter) {
|
||||
try { fs.unlinkSync(file); } catch { /* ignore */ }
|
||||
return { path: file, action: 'removed' };
|
||||
}
|
||||
|
||||
// Foreign content we don't recognize — leave it alone.
|
||||
return { path: file, action: 'not-found' };
|
||||
}
|
||||
|
||||
export const cursorTarget: AgentTarget = new CursorTarget();
|
||||
|
||||
Reference in New Issue
Block a user