From 025ebc88d6d708edd3732f5cb68516148719a061 Mon Sep 17 00:00:00 2001 From: Colby Mchenry Date: Sun, 24 May 2026 04:41:04 -0500 Subject: [PATCH 1/7] Release 0.9.4: framework-aware routing + dynamic-dispatch coverage + retrieval improvements (#365) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(resolution): close dynamic-dispatch coverage holes (callback synthesis + django ORM) Static tree-sitter extraction misses calls whose target is computed or indirect, so flows through callbacks, observers, and descriptors were absent from the graph. - callback-synthesizer.ts: whole-graph pass after base resolution. Detects registrar/dispatcher channels (field-backed observers + string-keyed EventEmitters), correlates registration sites, and synthesizes dispatcher->callback `calls` edges (provenance:'heuristic'). Records the registration site (registeredAt) in edge metadata. Precision guards: named handlers only, registrar-name match, event fan-out cap. - frameworks/python.ts + resolution/{index,types}.ts: claimsReference hook + django ORM resolver (_iterable_class -> ModelIterable.__iter__). - extraction/tree-sitter.ts: extract named nested functions so inline named handlers become linkable nodes. trace(mutateElement, triggerRender) and trace(_fetch_all, execute_sql) now connect; node count stable (no explosion). Co-Authored-By: Claude Opus 4.7 (1M context) * feat(mcp): self-sufficient flow output + fix explore budget regression - Surface synthesized-edge evidence in trace, the node trail, and context call paths: a dynamic-dispatch hop now shows "callback via onUpdate @App.tsx:3148" with the registration site inline (and trace inlines each hop's call-site source line) -- the exact glue agents previously Read/Grep'd to reconstruct. - Fix non-monotonic explore output budget: the 500-5000 file tier capped maxCharsPerFile at 2500, BELOW the <500 tier's 3800, so on god-file projects (excalidraw's 415 KB App.tsx) one explore returned <1% of the file and forced a Read. Raised to 6500/file, 28000 total. - Stop explore from inviting Read: truncation/trim notes said "use Read for more"; they now steer to another codegraph_explore and treat returned source as already Read. Measured on excalidraw: best-case flow answer went from 5 reads / 131s to 0 reads / 73s with ~3-4 codegraph calls. Co-Authored-By: Claude Opus 4.7 (1M context) * chore(agent-eval): coverage probes, block-read hook, and design docs Dev-only validation harness for the dynamic-dispatch coverage work: - probe-{trace,node,context,explore}.mjs: drive MCP tools against a built index without a full agent run. - block-read-hook.sh + hook-settings.json: PreToolUse experiment that denies source Reads to measure codegraph sufficiency (forced Read-0). - docs/design/: callback-edge-synthesis + dynamic-dispatch-coverage playbook. Co-Authored-By: Claude Opus 4.7 (1M context) * feat(resolution): bridge React boundaries — re-render + JSX child synthesis Closes the two dynamic-dispatch hops that broke "state mutation -> on-screen render" flows in React apps. Both are call-invisible (React-internal) but the code between them is fully call-connected, so one synthesized edge each makes the whole flow trace end-to-end. - reactRenderEdges: setState(...) re-runs the component's render(). For each class with a render method, link sibling methods calling this.setState -> render. The setState gate keeps it to React class components. - reactJsxChildEdges: a component that returns mounts Child. Link parent -> each capitalized JSX child, resolved to a component/function/class node (the resolution gate drops TS generics like Array). File-oriented, capped per parent. - Surface both in synthEdgeNote (trace + node trail) and context call-paths. Validated on excalidraw: trace(mutateElement, renderStaticScene) now connects in 6 hops across callback -> react-render -> jsx-child; 1 + 46 + 280 synthesized edges, node count stable (no explosion). Partial coverage is worse than none: react-render alone raised agent reads (revealed a hop it then drilled); adding the jsx hop closed the flow and dropped reads to 0-1. Co-Authored-By: Claude Opus 4.7 (1M context) * docs(claude): retrieval performance contract + coverage validation methodology Add a "Retrieval performance & dynamic-dispatch coverage" section so future changes/PRs don't silently regress agent retrieval: - the explore call+output budget table by repo size, with the monotonic-per-file invariant (the bug that started this: <5000 tier's 2500 < <500 tier's 3800). - the "partial coverage is worse than none" principle. - the required validation methodology (small/medium/large x >=3 prompts per language x framework; deterministic probes + agent A/B; pass bar). - the Excalidraw worked example (before/after numbers) as the template to replicate for every language/framework. Co-Authored-By: Claude Opus 4.7 (1M context) * docs(claude): use full n=4 measured range in Excalidraw worked example Best run 0 Read/3 cg/76s; typical ~1 Read/~4 cg; occasional over-drill outlier. Report the range, not a single run — run-to-run variance is large. Co-Authored-By: Claude Opus 4.7 (1M context) * feat(mcp): steer flow questions to codegraph_trace first (tightens variance) codegraph_trace was absent from every steering intent map — all three guidance files routed "how does X reach Y" to context+explore, never to the trace tool. So agents used trace only by chance; when one didn't, it floundered reconstructing the path with search+callers (an 18-call run vs ~6 for trace-users). Add codegraph_trace to the intent map + a "flow" common chain (trace from->to FIRST = the whole path in one call, then ONE explore for bodies) across all three synced files (server-instructions, instructions-template, .cursor rule). Validated on excalidraw (hard "to the screen" Q, n=4 before/after): - call count 3-10 -> 3-4 (over-drill outlier gone) - duration 64-112s -> 51-74s - trace adoption 3/4 -> 4/4; search+callers path-reconstruction -> 0 - fully-clean runs (0 Read, 0 Grep) 0/4 -> 2/4; best 3 cg / 0 / 0 / 51s Co-Authored-By: Claude Opus 4.7 (1M context) * feat(resolution): Vue SFC template coverage (events + kebab components) The .vue extractor only parses + + + + diff --git a/site/src/styles/theme.css b/site/src/styles/theme.css new file mode 100644 index 00000000..74a0b0e0 --- /dev/null +++ b/site/src/styles/theme.css @@ -0,0 +1,217 @@ +/* ===================================================================== + codegraph — flat / paper editorial theme + Monochrome ink-on-paper, hairline rules, square corners. Shared by the + custom landing page (src/pages/index.astro) and the Starlight docs. + ===================================================================== */ + +/* ---- Fonts ---- */ +:root { + --sl-font: 'Archivo Variable', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Arial, sans-serif; + --sl-font-mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace; +} + +/* ---- Starlight colour mapping: light / paper (default) ---- */ +:root, +:root[data-theme='light'] { + --sl-color-accent-low: #e2dfd5; + --sl-color-accent: #16150f; + --sl-color-accent-high: #16150f; + --sl-color-white: #16150f; + --sl-color-gray-1: #2a281f; + --sl-color-gray-2: #56544a; + --sl-color-gray-3: #6f6c61; + --sl-color-gray-4: #87847a; + --sl-color-gray-5: #b4b1a5; + --sl-color-gray-6: #d6d3c8; + --sl-color-gray-7: #e8e6dd; + --sl-color-black: #f7f6f2; + + --sl-color-bg: #f7f6f2; + --sl-color-bg-nav: #f7f6f2; + --sl-color-bg-sidebar: #f7f6f2; + --sl-color-bg-inline-code: #e8e6dd; + --sl-color-bg-accent: #16150f; + + --sl-color-text: #16150f; + --sl-color-text-accent: #16150f; + --sl-color-text-invert: #f7f6f2; + + --sl-color-hairline: #16150f; + --sl-color-hairline-light: #d6d3c8; + --sl-color-hairline-shade: #d6d3c8; + + /* shared tokens */ + --cg-paper: #f7f6f2; + --cg-paper-2: #f1efe8; + --cg-paper-press: #e8e6dd; + --cg-ink: #16150f; + --cg-ink-2: #56544a; + --cg-ink-3: #87847a; + --cg-rule: #16150f; + --cg-rule-soft: #d6d3c8; +} + +/* ---- Starlight colour mapping: dark / ink ---- */ +:root[data-theme='dark'] { + --sl-color-accent-low: #34322a; + --sl-color-accent: #f3f1ea; + --sl-color-accent-high: #f3f1ea; + --sl-color-white: #f3f1ea; + --sl-color-gray-1: #e7e5dc; + --sl-color-gray-2: #c9c6ba; + --sl-color-gray-3: #a7a499; + --sl-color-gray-4: #7c7a70; + --sl-color-gray-5: #57554c; + --sl-color-gray-6: #2c2a23; + --sl-color-gray-7: #1e1c16; + --sl-color-black: #16150f; + + --sl-color-bg: #16150f; + --sl-color-bg-nav: #16150f; + --sl-color-bg-sidebar: #16150f; + --sl-color-bg-inline-code: #23211a; + --sl-color-bg-accent: #f3f1ea; + + --sl-color-text: #f3f1ea; + --sl-color-text-accent: #f3f1ea; + --sl-color-text-invert: #16150f; + + --sl-color-hairline: #f3f1ea; + --sl-color-hairline-light: #34322a; + --sl-color-hairline-shade: #34322a; + + --cg-paper: #16150f; + --cg-paper-2: #1e1c16; + --cg-paper-press: #23211a; + --cg-ink: #f3f1ea; + --cg-ink-2: #b8b5a8; + --cg-ink-3: #87847a; + --cg-rule: #f3f1ea; + --cg-rule-soft: #34322a; +} + +/* ---- Global flat resets ---- */ +*, +*::before, +*::after { + border-radius: 0 !important; /* this design has no rounded corners, anywhere */ +} + +:root { + --sl-shadow-sm: none; + --sl-shadow-md: none; + --sl-shadow-lg: none; +} + +body { + background: var(--cg-paper); + color: var(--cg-ink); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +:where(h1, h2, h3, h4, h5) { + letter-spacing: -0.02em; +} + +/* ---- Docs chrome ---- */ + +/* Header: one crisp bottom rule. Starlight nests
inside +
, so a bare `.header { border-bottom }` draws two + lines — put the rule on the outer
only and clear the inner div. */ +.header { + background: var(--cg-paper); + -webkit-backdrop-filter: none; + backdrop-filter: none; +} +header.header { + border-bottom: 1px solid var(--cg-rule); +} +.header .header { + border-bottom: 0; +} + +/* Sidebar: crisp right rule */ +#starlight__sidebar, +.sidebar-pane { + border-inline-end: 1px solid var(--cg-rule); + background: var(--cg-paper); +} + +/* Sidebar group labels — small caps, committed editorial direction */ +.sidebar-content details > summary, +.sidebar-content > ul > li > span, +.sidebar-content .large { + font-weight: 700; + letter-spacing: 0.07em; + text-transform: uppercase; + font-size: 0.72rem; + color: var(--cg-ink-2); +} + +/* Sidebar links */ +.sidebar-content a { + color: var(--cg-ink-2); +} +.sidebar-content a:hover { + background: var(--cg-paper-press); + color: var(--cg-ink); +} +.sidebar-content a[aria-current='page'], +.sidebar-content a[aria-current='page']:hover { + background: transparent; + color: var(--cg-ink); + font-weight: 700; + border-inline-start: 2px solid var(--cg-ink); +} + +/* Right "On this page" rail */ +starlight-toc a { + color: var(--cg-ink-3); +} +starlight-toc a[aria-current='true'] { + color: var(--cg-ink); + font-weight: 600; +} + +/* Prev / next pagination: flat bordered boxes */ +.pagination-links a { + border: 1px solid var(--cg-rule); + box-shadow: none; + background: var(--cg-paper); +} +.pagination-links a:hover { + background: var(--cg-paper-press); +} + +/* Inline code */ +.sl-markdown-content :not(pre) > code { + border: 1px solid var(--cg-rule-soft); + background: var(--cg-paper-2); + font-size: 0.875em; +} + +/* Cards / asides: square, hairline */ +.card, +.starlight-aside { + border: 1px solid var(--cg-rule); + box-shadow: none; +} + +/* Search trigger */ +button[data-open-modal] { + border: 1px solid var(--cg-rule); + background: var(--cg-paper); +} + +/* Content horizontal rules */ +.sl-markdown-content hr { + border: 0; + border-top: 1px solid var(--cg-rule); +} + +/* Links in prose */ +.sl-markdown-content a { + color: var(--cg-ink); + text-underline-offset: 3px; +} diff --git a/site/tsconfig.json b/site/tsconfig.json new file mode 100644 index 00000000..8bf91d3b --- /dev/null +++ b/site/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "astro/tsconfigs/strict", + "include": [".astro/types.d.ts", "**/*"], + "exclude": ["dist"] +} From 7f30c4f5c8705413b4c4641120cb947d97763795 Mon Sep 17 00:00:00 2001 From: Colby Mchenry Date: Sun, 24 May 2026 13:25:24 -0500 Subject: [PATCH 4/7] docs(readme): link to the website & docs site (#376) Add a prominent link to https://colbymchenry.github.io/codegraph/ at the top of the README so visitors landing on the repo can reach the site. Co-authored-by: Claude Opus 4.7 (1M context) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 5d00c671..0b348cb8 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ **~35% cheaper · ~70% fewer tool calls · 100% local** +### [Documentation & Website →](https://colbymchenry.github.io/codegraph/) + [![npm version](https://img.shields.io/npm/v/@colbymchenry/codegraph.svg)](https://www.npmjs.com/package/@colbymchenry/codegraph) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Self-contained](https://img.shields.io/badge/Node.js-bundled%20%C2%B7%20none%20required-brightgreen.svg)](https://nodejs.org/) From 6a2098b19953396521389bc29526592ee87634bb Mon Sep 17 00:00:00 2001 From: Colby Mchenry Date: Sun, 24 May 2026 13:27:45 -0500 Subject: [PATCH 5/7] feat(site): add Docs & Languages links to the docs header (#377) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bring the Starlight docs header to parity with the landing nav — now Docs · Languages · GitHub · star pill, in that order. Added via the SocialIcons slot (no full header rebuild, so search/theme/mobile keep working); the text links and star pill are hidden on mobile, where the sidebar already covers navigation. Co-authored-by: Claude Opus 4.7 (1M context) --- site/src/components/SocialIcons.astro | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/site/src/components/SocialIcons.astro b/site/src/components/SocialIcons.astro index a1b38270..752c241e 100644 --- a/site/src/components/SocialIcons.astro +++ b/site/src/components/SocialIcons.astro @@ -1,13 +1,18 @@ --- -// Keep Starlight's default social icons (the GitHub link) and append a live -// star-count pill, matching the landing page nav. +// Docs-header right cluster: Docs + Languages text links, Starlight's default +// social icons (the GitHub link), and a live star-count pill — same order as +// the landing nav (Docs · Languages · GitHub · Star). Done here, in the slot we +// already own, to avoid a full header rebuild that could break search/mobile. import Default from '@astrojs/starlight/components/SocialIcons.astro'; import { getStarsLabel } from '../lib/github'; const stars = await getStarsLabel(); +const base = import.meta.env.BASE_URL.replace(/\/$/, ''); const repo = 'https://github.com/colbymchenry/codegraph'; --- +Docs +Languages