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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

### Fixes

- **Explore's Relationships list marks a call matched by name alone.** `codegraph_node`'s trail and explore's Flow already label a hop the resolver could bind only by the callee's name as `[name match, unverified]`; the flat Relationships list (shown for repositories of 500 files or more) now does too, so every place explore draws a call edge shows the same warning.
- **A TypeScript property written as a getter now types the calls made through it.** `c.req.header()` reached nothing when `req` was `get req(): HonoRequest`, because only `req: T` fields were read. A plain constructor parameter named like the field (`constructor(req: Request)`) was also taken for the field's declaration and hid the real one; it now counts only with a modifier (`private req: T`), or in a class that extends another and may pass it to `super`. On hono this adds 74 call edges and removes none, and no added edge contradicts the compiler where it reached the caller; on zod the edges are unchanged.
- **Indexing no longer depends on the runtime's directory order.** Node lists a directory's files alphabetically while Bun lists them in the filesystem's own order, so under Bun files were indexed in a different order and equal-score search and explore results came out differently from Node's. Directories are now read in name order on both.
- The first session search over a large Claude Code history is faster: transcripts of 256 KB and up are scanned for the entries that can hold prose instead of every line being decoded and parsed, since about 95% of a transcript is tool traffic. The indexed passages and titles are identical (checked on 536 real transcripts); spaced JSON or a large file with no match falls back to the whole-file parse. In-process cold build of those 536 transcripts: about 1.0 s before, 0.7 s after.
Expand Down
34 changes: 34 additions & 0 deletions __tests__/edge-trust.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,37 @@ describe('codegraph_node trail labels name-only hops', () => {
expect(calls).toMatch(/proven \([^)]*\)(?! \[name match)/);
});
});

describe('explore\'s Relationships list labels name-only hops', () => {
let dir: string;
let cg: CodeGraph;

beforeEach(async () => {
dir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-edge-trust-rel-'));
const write = (rel: string, body: string) => {
fs.mkdirSync(path.dirname(path.join(dir, rel)), { recursive: true });
fs.writeFileSync(path.join(dir, rel), body);
};
write('pkg_a/tools.py', 'def helper():\n return 1\n');
write('pkg_b/other.py', 'def helper():\n return 2\n');
write('pkg_c/util.py', 'def proven():\n return 3\n');
write('app/main.py', 'from pkg_c.util import proven\n\n\ndef run():\n helper()\n proven()\n');
// The Relationships section is only rendered for repositories of 500 files or more.
for (let i = 0; i < 520; i++) write(`pad/f${i}.py`, `def pad_${i}():\n return ${i}\n`);
cg = CodeGraph.initSync(dir, { config: { include: ['**/*.py'], exclude: [] } });
await cg.indexAll();
});

afterEach(() => {
try { cg.close(); } catch { /* ignore */ }
fs.rmSync(dir, { recursive: true, force: true });
});

it('marks the name-matched hop and leaves the imported one plain', async () => {
const result = await new ToolHandler(cg).execute('codegraph_explore', { query: 'run helper proven' });
const text = result.content?.[0]?.type === 'text' ? result.content[0].text : '';
const rels = text.split('\n').filter((l) => l.startsWith('- run → '));
expect(rels).toContain('- run → helper [name match, unverified]');
expect(rels).toContain('- run → proven');
});
});
6 changes: 3 additions & 3 deletions src/mcp/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6117,14 +6117,14 @@ export class ToolHandler {
lines.push('');

// Group edges by kind for readability
const byKind = new Map<string, Array<{ source: string; target: string }>>();
const byKind = new Map<string, Array<{ source: string; target: string; guess: boolean }>>();
for (const edge of significantEdges) {
const sourceNode = subgraph.nodes.get(edge.source);
const targetNode = subgraph.nodes.get(edge.target);
if (!sourceNode || !targetNode) continue;

const group = byKind.get(edge.kind) || [];
group.push({ source: sourceNode.name, target: targetNode.name });
group.push({ source: sourceNode.name, target: targetNode.name, guess: isNameGuess(edge) });
byKind.set(edge.kind, group);
}

Expand All @@ -6133,7 +6133,7 @@ export class ToolHandler {
const shown = edges.slice(0, cap);
lines.push(`**${kind}:**`);
for (const e of shown) {
lines.push(`- ${e.source} → ${e.target}`);
lines.push(`- ${e.source} → ${e.target}${e.guess ? ' [name match, unverified]' : ''}`);
}
if (edges.length > cap) {
lines.push(`- ... and ${edges.length - cap} more`);
Expand Down
Loading