From d7bd29a232eee5d219d3832750f710298fe9971f Mon Sep 17 00:00:00 2001 From: heqikai Date: Thu, 28 May 2026 00:09:12 +0800 Subject: [PATCH 01/22] feat(codedelta): add Phase 1 monorepo, timeline API, and web UI shell Introduce CodeDelta as a local-first commit-aware layer on CodeGraph: repo import, commit timeline REST API, React navigation shell, and Phase 2/3 package stubs with roadmap documentation. Co-authored-by: Cursor --- .gitignore | 3 + README.md | 632 +- apps/web/index.html | 12 + apps/web/package.json | 24 + apps/web/src/App.tsx | 79 + apps/web/src/api/client.ts | 59 + apps/web/src/main.tsx | 13 + apps/web/src/pages/DeltaViewPage.tsx | 51 + apps/web/src/pages/ImportPage.tsx | 79 + apps/web/src/pages/ProviderSettingsPage.tsx | 101 + apps/web/src/pages/TimelinePage.tsx | 185 + apps/web/src/pages/TraceViewPage.tsx | 44 + apps/web/src/routes.tsx | 22 + apps/web/src/styles.css | 336 ++ apps/web/src/types.ts | 54 + apps/web/tsconfig.json | 21 + apps/web/vite.config.ts | 15 + docs/codedelta/ROADMAP.md | 76 + package-lock.json | 5203 ++++++++++++++--- package.json | 14 +- packages/codedelta-graph-diff/package.json | 15 + packages/codedelta-graph-diff/src/index.ts | 14 + packages/codedelta-graph-diff/tsconfig.json | 14 + packages/codedelta-impact-score/package.json | 16 + packages/codedelta-impact-score/src/index.ts | 20 + packages/codedelta-impact-score/tsconfig.json | 14 + .../codedelta-provider-runtime/package.json | 15 + .../codedelta-provider-runtime/src/index.ts | 100 + .../codedelta-provider-runtime/tsconfig.json | 14 + .../__tests__/repo-manager.test.ts | 86 + packages/codedelta-repo-manager/package.json | 16 + .../src/cache-layout.ts | 45 + .../codedelta-repo-manager/src/commits.ts | 204 + .../codedelta-repo-manager/src/git-runner.ts | 85 + .../src/github-clone.ts | 121 + packages/codedelta-repo-manager/src/index.ts | 49 + .../codedelta-repo-manager/src/local-open.ts | 48 + .../codedelta-repo-manager/src/worktree.ts | 47 + packages/codedelta-repo-manager/tsconfig.json | 22 + .../codedelta-server/__tests__/server.test.ts | 67 + packages/codedelta-server/package.json | 28 + packages/codedelta-server/src/index.ts | 39 + .../codedelta-server/src/routes/params.ts | 5 + packages/codedelta-server/src/routes/repos.ts | 131 + .../codedelta-server/src/routes/settings.ts | 27 + .../src/store/repo-registry.ts | 93 + packages/codedelta-server/tsconfig.json | 22 + .../codedelta-snapshot-manager/package.json | 15 + .../codedelta-snapshot-manager/src/index.ts | 35 + .../codedelta-snapshot-manager/tsconfig.json | 14 + packages/codedelta-trace-engine/package.json | 16 + packages/codedelta-trace-engine/src/index.ts | 24 + packages/codedelta-trace-engine/tsconfig.json | 14 + packages/codedelta-types/package.json | 12 + packages/codedelta-types/src/index.ts | 145 + packages/codedelta-types/tsconfig.json | 17 + vitest.config.ts | 2 +- 57 files changed, 7273 insertions(+), 1401 deletions(-) create mode 100644 apps/web/index.html create mode 100644 apps/web/package.json create mode 100644 apps/web/src/App.tsx create mode 100644 apps/web/src/api/client.ts create mode 100644 apps/web/src/main.tsx create mode 100644 apps/web/src/pages/DeltaViewPage.tsx create mode 100644 apps/web/src/pages/ImportPage.tsx create mode 100644 apps/web/src/pages/ProviderSettingsPage.tsx create mode 100644 apps/web/src/pages/TimelinePage.tsx create mode 100644 apps/web/src/pages/TraceViewPage.tsx create mode 100644 apps/web/src/routes.tsx create mode 100644 apps/web/src/styles.css create mode 100644 apps/web/src/types.ts create mode 100644 apps/web/tsconfig.json create mode 100644 apps/web/vite.config.ts create mode 100644 docs/codedelta/ROADMAP.md create mode 100644 packages/codedelta-graph-diff/package.json create mode 100644 packages/codedelta-graph-diff/src/index.ts create mode 100644 packages/codedelta-graph-diff/tsconfig.json create mode 100644 packages/codedelta-impact-score/package.json create mode 100644 packages/codedelta-impact-score/src/index.ts create mode 100644 packages/codedelta-impact-score/tsconfig.json create mode 100644 packages/codedelta-provider-runtime/package.json create mode 100644 packages/codedelta-provider-runtime/src/index.ts create mode 100644 packages/codedelta-provider-runtime/tsconfig.json create mode 100644 packages/codedelta-repo-manager/__tests__/repo-manager.test.ts create mode 100644 packages/codedelta-repo-manager/package.json create mode 100644 packages/codedelta-repo-manager/src/cache-layout.ts create mode 100644 packages/codedelta-repo-manager/src/commits.ts create mode 100644 packages/codedelta-repo-manager/src/git-runner.ts create mode 100644 packages/codedelta-repo-manager/src/github-clone.ts create mode 100644 packages/codedelta-repo-manager/src/index.ts create mode 100644 packages/codedelta-repo-manager/src/local-open.ts create mode 100644 packages/codedelta-repo-manager/src/worktree.ts create mode 100644 packages/codedelta-repo-manager/tsconfig.json create mode 100644 packages/codedelta-server/__tests__/server.test.ts create mode 100644 packages/codedelta-server/package.json create mode 100644 packages/codedelta-server/src/index.ts create mode 100644 packages/codedelta-server/src/routes/params.ts create mode 100644 packages/codedelta-server/src/routes/repos.ts create mode 100644 packages/codedelta-server/src/routes/settings.ts create mode 100644 packages/codedelta-server/src/store/repo-registry.ts create mode 100644 packages/codedelta-server/tsconfig.json create mode 100644 packages/codedelta-snapshot-manager/package.json create mode 100644 packages/codedelta-snapshot-manager/src/index.ts create mode 100644 packages/codedelta-snapshot-manager/tsconfig.json create mode 100644 packages/codedelta-trace-engine/package.json create mode 100644 packages/codedelta-trace-engine/src/index.ts create mode 100644 packages/codedelta-trace-engine/tsconfig.json create mode 100644 packages/codedelta-types/package.json create mode 100644 packages/codedelta-types/src/index.ts create mode 100644 packages/codedelta-types/tsconfig.json diff --git a/.gitignore b/.gitignore index 7c949e1c2..959b3e236 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,9 @@ npm-debug.log* # CodeGraph data directories (in test projects) .codegraph/ +# CodeDelta local cache (repos, snapshots, settings) +.codedelta/ + test_frameworks # Test language repos for manual testing diff --git a/README.md b/README.md index 1db026097..0c424831b 100644 --- a/README.md +++ b/README.md @@ -1,595 +1,133 @@ -
+# CodeDelta -# CodeGraph +**Local-first commit-aware structural code intelligence.** -### Supercharge Claude Code, Cursor, Codex, OpenCode, Hermes Agent, Gemini, Antigravity, and Kiro with Semantic Code Intelligence +CodeDelta explains how code structure **changes across commits** and helps trace when bugs or behavior changes may have been introduced. It is built on top of [CodeGraph](#built-on-codegraph), which provides the structural analysis core. -**~35% cheaper · ~70% fewer tool calls · 100% local** +## What CodeDelta does -### [Documentation & Website →](https://colbymchenry.github.io/codegraph/) +CodeDelta focuses on **structural evolution**, not generic git browsing or line-level diffs. -[![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/) +### Delta View -[![Windows](https://img.shields.io/badge/Windows-supported-blue.svg)](#supported-platforms) -[![macOS](https://img.shields.io/badge/macOS-supported-blue.svg)](#supported-platforms) -[![Linux](https://img.shields.io/badge/Linux-supported-blue.svg)](#supported-platforms) +Compare two commits and visualize structural impact: -[![Claude Code](https://img.shields.io/badge/Claude_Code-supported-blueviolet.svg)](#supported-agents) -[![Cursor](https://img.shields.io/badge/Cursor-supported-blueviolet.svg)](#supported-agents) -[![Codex](https://img.shields.io/badge/Codex-supported-blueviolet.svg)](#supported-agents) -[![opencode](https://img.shields.io/badge/opencode-supported-blueviolet.svg)](#supported-agents) -[![Hermes Agent](https://img.shields.io/badge/Hermes_Agent-supported-blueviolet.svg)](#supported-agents) -[![Gemini](https://img.shields.io/badge/Gemini-supported-blueviolet.svg)](#supported-agents) -[![Antigravity](https://img.shields.io/badge/Antigravity-supported-blueviolet.svg)](#supported-agents) -[![Kiro](https://img.shields.io/badge/Kiro-supported-blueviolet.svg)](#supported-agents) +- Changed symbols (functions, classes, components) +- Changed dependency edges (calls, imports) +- Affected modules and entry points +- Review suggestions ordered by risk -
+### Trace View -## Get Started +Describe a bug, behavior change, or architecture question. CodeDelta: -**No Node.js required** — one command grabs the right build for your OS: +- Retrieves candidate commits from messages, files, and structural diffs +- Assembles evidence chains grounded in the graph +- Returns confidence, uncertainty, and what could not be confirmed -```bash -# macOS / Linux -curl -fsSL https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh | sh - -# Windows (PowerShell) -irm https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1 | iex -``` - -Already have Node? Use npm instead (works on any version): - -```bash -npx @colbymchenry/codegraph # zero-install, or: -npm i -g @colbymchenry/codegraph -``` - -CodeGraph bundles its own runtime — nothing to compile, no native build, works the same everywhere. The interactive installer auto-configures your agent(s) — Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro. - -### Initialize Projects - -```bash -cd your-project -codegraph init -i -``` - -
- -![1_C_VYnhpys0UHrOuOgpgoyw](https://github.com/user-attachments/assets/f168182f-4d9a-44e0-94d7-08d018cc8a3a) - -
- -### Uninstall - -Changed your mind? One command removes CodeGraph from every agent it configured: - -```bash -codegraph uninstall -``` - -Reverses the installer — strips CodeGraph's MCP server config, instructions, and permissions from each configured agent. Your project indexes (`.codegraph/`) are left untouched; remove those per-project with `codegraph uninit`. Use `--target` to remove from specific agents, or `--yes` to run non-interactively. - ---- - -## Why CodeGraph? - -When Claude Code explores a codebase, it spawns **Explore agents** that scan files with grep, glob, and Read — consuming tokens on every tool call. - -**CodeGraph gives those agents a pre-indexed knowledge graph** — symbol relationships, call graphs, and code structure. Agents query the graph instantly instead of scanning files. - -### Benchmark Results - -Tested across **7 real-world open-source codebases** spanning 7 languages, comparing an agent (Claude Code, headless) answering one architecture question **with** and **without** CodeGraph. Each cell is the savings at the **median of 4 runs per arm**. _Re-validated on **v0.9.4** (2026-05-24)._ - -> **Average: 35% cheaper · 57% fewer tokens · 46% faster · 71% fewer tool calls** - -| Codebase | Language | Cost | Tokens | Time | Tool calls | -|----------|----------|------|--------|------|------------| -| **VS Code** | TypeScript · ~10k files | 26% cheaper | 78% fewer | 52% faster | 85% fewer | -| **Excalidraw** | TypeScript · ~640 | 52% cheaper | 90% fewer | 73% faster | 96% fewer | -| **Django** | Python · ~3k | 12% cheaper | 36% fewer | 19% faster | 53% fewer | -| **Tokio** | Rust · ~790 | 82% cheaper | 86% fewer | 71% faster | 92% fewer | -| **OkHttp** | Java · ~645 | 2% cheaper | 13% fewer | 31% faster | 45% fewer | -| **Gin** | Go · ~110 | 21% cheaper | 34% fewer | 27% faster | 40% fewer | -| **Alamofire** | Swift · ~110 | 47% cheaper | 64% fewer | 48% faster | 83% fewer | - -The gains scale with codebase size: on large repos the agent answers from the index in a handful of calls with **zero file reads**, while the no-CodeGraph agent fans out across grep/find/Read (and the sub-agents it spawns). On a small repo like Gin (~150 files) native search is already cheap, so the margin narrows. - -
-Full benchmark details - -**Methodology.** Each arm is `claude -p` (Claude Opus 4.7) run headlessly against the repo with `--strict-mcp-config`: **WITH** = CodeGraph's MCP server enabled, **WITHOUT** = an empty MCP config. Built-in Read/Grep/Bash stay available to both. Same question per repo, **4 runs per arm, median reported**. Cost = the run's `total_cost_usd`; Tokens = total tokens processed (input incl. cached + output); Time = wall-clock; Tool calls = every tool invocation, including those inside any sub-agents the model spawns. Repos cloned at `--depth 1` and indexed by the same CodeGraph build that served them. Re-validated on codegraph **v0.9.4** (2026-05-24); per-repo numbers move run-to-run with how hard the without-arm thrashes (the median-of-4 smooths it, but tails remain — e.g. Tokio's without-arm hit $2.41/3m one batch). - -**Queries:** -| Codebase | Query | -|----------|-------| -| VS Code | "How does the extension host communicate with the main process?" | -| Excalidraw | "How does Excalidraw render and update canvas elements?" | -| Django | "How does Django's ORM build and execute a query from a QuerySet?" | -| Tokio | "How does tokio schedule and run async tasks on its runtime?" | -| OkHttp | "How does OkHttp process a request through its interceptor chain?" | -| Gin | "How does gin route requests through its middleware chain?" | -| Alamofire | "How does Alamofire build, send, and validate a request?" | - -**Raw medians — WITH → WITHOUT:** -| Codebase | Cost | Tokens | Time | Tool calls | -|----------|------|--------|------|------------| -| VS Code | $0.60 → $0.80 | 601k → 2.8M | 1m 10s → 2m 26s | 8 → 55 | -| Excalidraw | $0.43 → $0.90 | 344k → 3.5M | 48s → 2m 58s | 3 → 79 | -| Django | $0.59 → $0.67 | 739k → 1.2M | 1m 19s → 1m 38s | 9 → 19 | -| Tokio | $0.42 → $2.41 | 379k → 2.6M | 53s → 3m 2s | 4 → 53 | -| OkHttp | $0.47 → $0.47 | 636k → 730k | 42s → 1m 1s | 6 → 11 | -| Gin | $0.37 → $0.47 | 444k → 675k | 44s → 1m 0s | 6 → 10 | -| Alamofire | $0.61 → $1.14 | 1.0M → 2.8M | 1m 17s → 2m 27s | 12 → 69 | - -**Why CodeGraph wins:** with the index available, the agent answers directly — `codegraph_context` to map the area, then one `codegraph_explore` for the relevant source — and stops, usually with zero file reads. Without it, the agent (and the Explore sub-agents it spawns) spends most of its budget on discovery (find/ls/grep) before reading the right code. CodeGraph only helps when queried *directly*, so its instructions steer agents to answer directly rather than delegate exploration to file-reading sub-agents — otherwise a sub-agent reads files regardless and CodeGraph becomes overhead. - -
- ---- - -## Key Features - -| | | -|---|---| -| **Smart Context Building** | One tool call returns entry points, related symbols, and code snippets — no expensive exploration agents | -| **Full-Text Search** | Find code by name instantly across your entire codebase, powered by FTS5 | -| **Impact Analysis** | Trace callers, callees, and the full impact radius of any symbol before making changes | -| **Always Fresh** | File watcher uses native OS events (FSEvents/inotify/ReadDirectoryChangesW) with debounced auto-sync — the graph stays current as you code, zero config | -| **20+ Languages** | TypeScript, JavaScript, Python, Go, Rust, Java, C#, PHP, Ruby, C, C++, Objective-C, Swift, Kotlin, Dart, Lua, Luau, Svelte, Liquid, Pascal/Delphi | -| **Framework-aware Routes** | Recognizes web-framework routing files and links URL patterns to their handlers across 14 frameworks | -| **Mixed iOS / React Native / Expo** | Closes cross-language flows that static parsing misses: Swift ↔ ObjC bridging, React Native legacy bridge + TurboModules + Fabric view components, native → JS event emitters, Expo Modules | -| **100% Local** | No data leaves your machine. No API keys. No external services. SQLite database only | - -
-How auto-syncing works — and why you don't need to run codegraph sync manually - -When your agent (Claude Code, Cursor, Codex, opencode) launches `codegraph serve --mcp`, three layers keep the index in step with your code — and make sure the agent never gets a silent wrong answer in the brief window between an edit and the next sync: - -1. **File watcher with debounced auto-sync.** A native FSEvents / inotify / ReadDirectoryChangesW watcher captures every source-file create / modify / delete and triggers a re-index after a debounce window (default `2000ms`, tunable via `CODEGRAPH_WATCH_DEBOUNCE_MS`, clamped to `[100ms, 60s]`). Bursts of edits collapse into a single sync. - -2. **Per-file staleness banner.** During the brief debounce window, MCP tool responses that would reference a still-pending file prepend a `⚠️` banner naming it and telling the agent to `Read` it directly. Pending files NOT referenced by the response surface as a small footer instead. Either way, the agent gets an explicit signal — validated with Claude Code, where the agent literally says "Reading the file directly for the live content" before opening it. - -3. **Connect-time catch-up.** When the MCP server (re)connects, codegraph runs a fast `(size, mtime)` + content-hash reconciliation against the working tree before answering the first query — so edits made while no MCP server was running (a `git pull` from the terminal, edits from another editor, a previous agent session that exited) get absorbed on the next session's first tool call. - -``` -agent writes src/Widget.ts - → watcher fires (<100ms) - → debounce (default 2s) - → sync; Widget.ts is in the index - → next agent query sees it -``` - -**Verify any time** with `codegraph_status` (via MCP) or `codegraph status` (CLI). If anything is pending, you'll see a `### Pending sync:` section naming the files and their edit age. - -The handful of cases where manual `codegraph sync` makes sense: the watcher is disabled (sandboxed environments, or `CODEGRAPH_NO_DAEMON=1`), or you're scripting against the index outside an agent session and want a pre-flight sync at the start of your script. - -→ Full deep-dive in [Guides → Indexing a Project](https://colbymchenry.github.io/codegraph/guides/indexing/#stay-fresh-automatically). - -
- ---- - -## Framework-aware Routes - -CodeGraph detects web-framework routing files and emits `route` nodes linked by `references` edges to their handler classes or functions. Querying callers of a view/controller now surfaces the URL pattern that binds it. - -| Framework | Shapes recognized | -|---|---| -| **Django** | `path()`, `re_path()`, `url()`, `include()` in `urls.py` (CBV `.as_view()`, dotted paths) | -| **Flask** | `@app.route('/path', methods=[...])`, blueprint routes | -| **FastAPI** | `@app.get(...)`, `@router.post(...)`, all standard methods | -| **Express** | `app.get(...)`, `router.post(...)` with middleware chains | -| **NestJS** | `@Controller` + `@Get/@Post/...`, GraphQL `@Resolver` + `@Query/@Mutation`, `@MessagePattern`/`@EventPattern`, `@SubscribeMessage` | -| **Laravel** | `Route::get()`, `Route::resource()`, `Controller@action`, tuple syntax | -| **Drupal** | `*.routing.yml` routes (`_controller`, `_form`, entity handlers); `hook_*` implementations in `.module`/`.theme`/`.install`/`.inc` | -| **Rails** | `get '/x', to: 'users#index'`, hash-rocket `=>` syntax | -| **Spring** | `@GetMapping`, `@PostMapping`, `@RequestMapping` on methods | -| **Gin / chi / gorilla / mux** | `r.GET(...)`, `router.HandleFunc(...)` | -| **Axum / actix / Rocket** | `.route("/x", get(handler))` | -| **ASP.NET** | `[HttpGet("/x")]` attributes on action methods | -| **Vapor** | `app.get("x", use: handler)` | -| **React Router** / **SvelteKit** | Route component nodes | +## What CodeDelta is not ---- +- **Not a generic Git GUI** — no branch management, merge UI, or full history browser +- **Not a CodeWiki clone** — no auto-generated prose documentation site +- **Not just a text diff viewer** — the product is commit-aware **structural** intelligence -## Mixed iOS / React Native / Expo bridging +## Quick start -Real iOS and React Native codebases live across multiple languages — a Swift caller invokes an Objective-C selector that's been auto-bridged, a JS file calls into a native module via the React Native bridge, a JSX component delegates to a native view manager. Static tree-sitter extraction stops at each language boundary. CodeGraph bridges them so `trace`, `callers`, `callees`, and `impact` connect end-to-end across the gap. +Requires Node.js 20–24 and git. -| Boundary | JS / Swift side | Native side | How | -|---|---|---|---| -| **Swift → ObjC** | Swift `obj.foo(bar:)` | ObjC selector `-fooWithBar:` | `@objc` auto-bridging rules (including init/property/protocol forms) + Cocoa preposition prefixes (`With`/`For`/`By`/`In`/`On`/`At`/…) | -| **ObjC → Swift** | ObjC `[obj fooWithBar:]` | Swift `@objc func foo(bar:)` | Reverse-bridge name candidates; verifies `@objc` exposure from source | -| **React Native legacy bridge** | JS `NativeModules.X.fn(...)` | ObjC `RCT_EXPORT_METHOD` / `RCT_REMAP_METHOD` · Java/Kotlin `@ReactMethod` | Parses macro/annotation declarations to build a JS-name → native-method map | -| **React Native TurboModules** | JS `import M from './NativeM'; M.fn(...)` | Native impl matching the Codegen spec | Treats the `Native.ts` spec interface as ground truth | -| **RN native → JS events** | JS `new NativeEventEmitter(...).addListener('e', cb)` | ObjC `[self sendEventWithName:@"e" body:...]` · Swift `sendEvent(withName: "e", ...)` · Java/Kotlin `.emit("e", ...)` | Synthesized cross-language event channel keyed by literal event name | -| **Expo Modules** | JS `requireNativeModule('X').fn(...)` | Swift / Kotlin `Module { Name("X"); AsyncFunction("fn") { ... } }` | Parses the Expo DSL literals; synthetic method nodes resolve via existing name-match | -| **Fabric view components** | JSX `` | TS Codegen spec + native impl class | Spec → `component` node; convention-based name+suffix lookup (`View`/`ComponentView`/`Manager`/`ViewManager`) bridges to native | -| **Legacy Paper view managers** | JSX `` | ObjC `RCT_EXPORT_VIEW_PROPERTY` · Java/Kotlin `@ReactProp` | Same as Fabric — Paper-era declarations also produce `component` + `property` nodes | - -**Validated on real codebases** (small + medium + large for each bridge): - -| Bridge | Small | Medium | Large | -|---|---|---|---| -| Swift ↔ ObjC | [Charts](https://github.com/danielgindi/Charts) | [realm-swift](https://github.com/realm/realm-swift) | [Wikipedia-iOS](https://github.com/wikimedia/wikipedia-ios) | -| RN legacy bridge | [AsyncStorage](https://github.com/react-native-async-storage/async-storage) | [react-native-svg](https://github.com/software-mansion/react-native-svg) | [react-native-firebase](https://github.com/invertase/react-native-firebase) | -| RN native → JS events | [RNGeolocation](https://github.com/Agontuk/react-native-geolocation-service) | — | react-native-firebase | -| Expo Modules | expo-haptics | expo-camera | expo SDK sweep (7 packages) | -| Fabric / Paper views | [react-native-segmented-control](https://github.com/react-native-segmented-control/segmented-control) | [react-native-screens](https://github.com/software-mansion/react-native-screens) | [react-native-skia](https://github.com/Shopify/react-native-skia) | - -Each bridge emits edges tagged `provenance:'heuristic'` with `metadata.synthesizedBy:` set to a stable channel name (e.g. `swift-objc-bridge`, `rn-event-channel`, `fabric-native-impl`, `expo-module-extract`), so the agent can tell at a glance how a hop got into the graph. - ---- - -## Quick Start - -### 1. Run the Installer - -```bash -npx @colbymchenry/codegraph -``` - -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** -- 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 + an instructions file (e.g. `CLAUDE.md`, `.cursor/rules/codegraph.mdc`, `~/.codex/AGENTS.md`, `~/.gemini/GEMINI.md`) -- Set up auto-allow permissions when Claude Code is one of the targets -- Initialize your current project (local installs only) - -**Non-interactive (scripting / CI):** - -```bash -codegraph install --yes # auto-detect agents, install global -codegraph install --target=cursor,claude --yes # explicit target list -codegraph install --target=auto --location=local # detected agents, project-local -codegraph install --print-config codex # print snippet, no file writes -``` - -| Flag | Values | Default | -|---|---|---| -| `--target` | `auto`, `all`, `none`, or csv (`claude,cursor,...`) | prompt | -| `--location` | `global`, `local` | prompt | -| `--yes` | (boolean) | prompt every step | -| `--no-permissions` | (boolean) skip Claude auto-allow list | permissions on | -| `--print-config ` | dump snippet for one agent and exit | — | - -### 2. Restart Your Agent - -Restart your agent (Claude Code / Cursor / Codex CLI / opencode / Hermes Agent / Gemini CLI / Antigravity IDE / Kiro) for the MCP server to load. - -### 3. Initialize Projects - -```bash -cd your-project -codegraph init -i -``` - -Builds the per-project knowledge graph index. Also wires up any project-local agent surfaces (e.g. Cursor's `.cursor/rules/codegraph.mdc`) so a single global `codegraph install` works in every project you open — no need to re-run the installer per project. - -That's it — your agent will use CodeGraph tools automatically when a `.codegraph/` directory exists. - -
-Manual Setup (Alternative) - -**Install globally:** ```bash -npm install -g @colbymchenry/codegraph +git clone +cd CodeDelta +npm install +npm run build:codedelta +npm run dev:codedelta ``` -**Add to `~/.claude.json`:** -```json -{ - "mcpServers": { - "codegraph": { - "type": "stdio", - "command": "codegraph", - "args": ["serve", "--mcp"] - } - } -} -``` - -**Add to `~/.claude/settings.json` (optional, for auto-allow):** -```json -{ - "permissions": { - "allow": [ - "mcp__codegraph__codegraph_search", - "mcp__codegraph__codegraph_context", - "mcp__codegraph__codegraph_callers", - "mcp__codegraph__codegraph_callees", - "mcp__codegraph__codegraph_impact", - "mcp__codegraph__codegraph_node", - "mcp__codegraph__codegraph_status", - "mcp__codegraph__codegraph_files" - ] - } -} -``` +Open [http://localhost:5173](http://localhost:5173). -
+1. **Import** a public GitHub URL (`owner/repo` or full URL) or a **local git path** +2. Open **Commit Timeline** — browse commits on a branch +3. Select a commit — view changed files and quick actions +4. **Delta View** — compare commits (Phase 2: full structural diff) +5. **Trace View** — ask about an issue (Phase 3: evidence-grounded tracing) -
-Global Instructions Reference +API server runs at [http://localhost:3847](http://localhost:3847) (`GET /api/health`). -The installer automatically adds these instructions to `~/.claude/CLAUDE.md`: +## Local-first and cache behavior -```markdown -## CodeGraph +CodeDelta stores analysis locally under **`.codedelta/`** (gitignored): -CodeGraph builds a semantic knowledge graph of codebases for faster, smarter code exploration. - -### If `.codegraph/` exists in the project - -**Answer directly with CodeGraph — don't delegate exploration to a file-reading sub-agent or a grep/read loop.** CodeGraph *is* the pre-built search index; re-deriving its answers with grep + Read repeats work it already did and costs more for the same result. For "how does X work?", architecture, trace, or where-is-X questions, answer in a handful of CodeGraph calls and stop — typically with **zero file reads**. The returned source is complete and authoritative: treat it as already read and do not re-open those files. Reach for raw Read/Grep only to confirm a specific detail CodeGraph didn't cover. - -**Tool selection by intent:** - -| Tool | Use For | +| Path | Purpose | |------|---------| -| `codegraph_context` | Map a task / feature / area first — composes search + node + callers + callees in one call | -| `codegraph_trace` | "How does X reach Y" — the call path, each hop's body inline (follows dynamic-dispatch hops grep can't) | -| `codegraph_explore` | Survey several related symbols' source in ONE budget-capped call | -| `codegraph_search` | Find a symbol by name | -| `codegraph_callers` / `codegraph_callees` | Walk call flow one hop at a time | -| `codegraph_impact` | Check what's affected before editing | -| `codegraph_node` | Get a single symbol's source / signature | - -A direct CodeGraph answer is a handful of calls; a grep/read exploration is dozens. - -### If `.codegraph/` does NOT exist - -At the start of a session, ask the user if they'd like to initialize CodeGraph: +| `.codedelta/repos//` | Cloned or referenced git repositories | +| `.codedelta/registry.json` | Imported repository metadata | +| `.codedelta/snapshots/` | Cached CodeGraph snapshots per commit (Phase 2) | +| `.codedelta/settings.json` | Provider configuration | -"I notice this project doesn't have CodeGraph initialized. Would you like me to run `codegraph init -i` to build a code knowledge graph?" -``` - -
+**Lazy indexing:** commit lists load immediately. CodeGraph snapshots are built only when you open Delta View or Trace View for selected commits — not for full history by default. ---- +## Provider options -## How It Works +Trace View uses a pluggable provider (Phase 3). Supported kinds: -``` -┌───────────────────────────────────────────────────────────────────┐ -│ Claude Code │ -│ │ -│ "How does a request reach the database?" │ -│ calls CodeGraph tools directly — no Explore sub-agent │ -│ │ │ -└─────────────────────────────────┬─────────────────────────────────┘ - │ - ▼ -┌───────────────────────────────────────────────────────────────────┐ -│ CodeGraph MCP Server │ -│ │ -│ context · trace · explore · callers · callees · impact │ -│ │ │ -│ ▼ │ -│ SQLite knowledge graph │ -│ symbols · edges · files · FTS5 full-text search │ -└───────────────────────────────────────────────────────────────────┘ -``` +| Provider | Description | +|----------|-------------| +| **No AI** | Timeline + Delta View with deterministic analysis only (default) | +| **Codex OAuth** | ChatGPT-style login when available | +| **OpenAI API key** | Direct OpenAI API | +| **OpenAI-compatible** | Custom endpoint (LocalAI, vLLM, etc.) | +| **Anthropic** | Claude API | +| **Ollama** | Local models | -1. **Extraction** — [tree-sitter](https://tree-sitter.github.io/) parses source code into ASTs. Language-specific queries extract nodes (functions, classes, methods) and edges (calls, imports, extends, implements). +Configure in **Provider Settings** in the web UI. -2. **Storage** — Everything goes into a local SQLite database (`.codegraph/codegraph.db`) with FTS5 full-text search. +## Built on CodeGraph -3. **Resolution** — After extraction, references are resolved: function calls → definitions, imports → source files, class inheritance, and framework-specific patterns. +This fork retains the CodeGraph core in [`src/`](src/): -4. **Auto-Sync** — The MCP server watches your project using native OS file events. Changes are debounced (2-second quiet window), filtered to source files only, and incrementally synced. The graph stays fresh as you code — no configuration needed. +- Tree-sitter extraction into a local SQLite knowledge graph +- Call graphs, impact radius, MCP tools for AI agents +- CLI: `codegraph init`, `codegraph sync`, `codegraph serve --mcp` ---- +**CodeGraph** answers: *what is the code structure?* +**CodeDelta** answers: *how did structure change across commits, and where might an issue have started?* -## CLI Reference +## Project structure -```bash -codegraph # Run interactive installer -codegraph install # Run installer (explicit) -codegraph uninstall # Remove CodeGraph from your agents (inverse of install) -codegraph init [path] # Initialize in a project (--index to also index) -codegraph uninit [path] # Remove CodeGraph from a project (--force to skip prompt) -codegraph index [path] # Full index (--force to re-index, --quiet for less output) -codegraph sync [path] # Incremental update -codegraph status [path] # Show statistics -codegraph query # Search symbols (--kind, --limit, --json) -codegraph files [path] # Show file structure (--format, --filter, --max-depth, --json) -codegraph context # Build context for AI (--format, --max-nodes) -codegraph callers # Find what calls a function/method (--limit, --json) -codegraph callees # Find what a function/method calls (--limit, --json) -codegraph impact # Analyze what code is affected by changing a symbol (--depth, --json) -codegraph affected [files...] # Find test files affected by changes (see below) -codegraph serve --mcp # Start MCP server ``` - -### `codegraph affected` - -Traces import dependencies transitively to find which test files are affected by changed source files. - -```bash -codegraph affected src/utils.ts src/api.ts # Pass files as arguments -git diff --name-only | codegraph affected --stdin # Pipe from git diff -codegraph affected src/auth.ts --filter "e2e/*" # Custom test file pattern +src/ # CodeGraph core (unchanged) +packages/ + codedelta-types/ # Shared TypeScript models + codedelta-repo-manager/ # Git import, commits, changed files + codedelta-server/ # REST API + codedelta-snapshot-manager/ # Phase 2 + codedelta-graph-diff/ # Phase 2 + codedelta-impact-score/ # Phase 2 + codedelta-trace-engine/ # Phase 3 + codedelta-provider-runtime/ # Phase 3 +apps/ + web/ # React + Vite UI ``` -| Option | Description | Default | -|--------|-------------|---------| -| `--stdin` | Read file list from stdin | `false` | -| `-d, --depth ` | Max dependency traversal depth | `5` | -| `-f, --filter ` | Custom glob to identify test files | auto-detect | -| `-j, --json` | Output as JSON | `false` | -| `-q, --quiet` | Output file paths only | `false` | +See [docs/codedelta/ROADMAP.md](docs/codedelta/ROADMAP.md) for the implementation roadmap. -**CI/hook example:** +## Development ```bash -#!/usr/bin/env bash -AFFECTED=$(git diff --name-only HEAD | codegraph affected --stdin --quiet) -if [ -n "$AFFECTED" ]; then - npx vitest run $AFFECTED -fi -``` - ---- +# CodeGraph core +npm run build +npm test -## MCP Tools - -When running as an MCP server, CodeGraph exposes these tools to Claude Code: - -| Tool | Purpose | -|------|---------| -| `codegraph_search` | Find symbols by name across the codebase | -| `codegraph_context` | Build relevant code context for a task | -| `codegraph_trace` | Trace the call path between two symbols ("how does X reach Y") in one call — each hop with its body inline, following dynamic-dispatch hops (callbacks, React re-render, interface→impl) that grep can't | -| `codegraph_callers` | Find what calls a function | -| `codegraph_callees` | Find what a function calls | -| `codegraph_impact` | Analyze what code is affected by changing a symbol | -| `codegraph_node` | Get details about a specific symbol (optionally with source code) | -| `codegraph_explore` | Return source for several related symbols grouped by file, plus a relationship map, in one call | -| `codegraph_files` | Get indexed file structure (faster than filesystem scanning) | -| `codegraph_status` | Check index health and statistics | - ---- - -## Library Usage - -```typescript -import CodeGraph from '@colbymchenry/codegraph'; - -const cg = await CodeGraph.init('/path/to/project'); -// Or: const cg = await CodeGraph.open('/path/to/project'); - -await cg.indexAll({ - onProgress: (p) => console.log(`${p.phase}: ${p.current}/${p.total}`) -}); - -const results = cg.searchNodes('UserService'); -const callers = cg.getCallers(results[0].node.id); -const context = await cg.buildContext('fix login bug', { maxNodes: 20, includeCode: true, format: 'markdown' }); -const impact = cg.getImpactRadius(results[0].node.id, 2); - -cg.watch(); // auto-sync on file changes -cg.unwatch(); // stop watching -cg.close(); +# CodeDelta packages + web +npm run build:codedelta +npm run dev:codedelta ``` ---- - -## Configuration - -There isn't any — CodeGraph is zero-config, with **no config file** to write or -keep in sync. Language support is automatic from the file extension; there's -nothing to wire up per language. - -What it skips out of the box: - -- **Dependency, build, and cache directories** — `node_modules`, `vendor`, - `dist`, `build`, `target`, `.venv`, `Pods`, `.next`, and the like across every - [supported stack](#supported-languages) — so the graph is your code, not - third-party noise. This holds even with no `.gitignore`. -- **Anything in your `.gitignore`** — honored in git repos via git, and in - non-git projects by reading `.gitignore` directly (root and nested). -- **Files larger than 1 MB** — generated bundles, minified JS, vendored blobs. - -To keep something else out, add it to `.gitignore`. To pull a default-excluded -directory back **in** (say you really do want a vendored dependency indexed), -add a negation — `!vendor/`. The defaults apply uniformly, so committing a -dependency or build directory doesn't force it into the graph; the `.gitignore` -negation is the explicit opt-in. - -## Supported Platforms - -Every release ships a self-contained build (bundled Node runtime — nothing to -compile) for all three desktop OSes, on both Intel/AMD (x64) and ARM (arm64): - -| Platform | Architectures | Install | -|----------|---------------|---------| -| Windows | x64, arm64 | PowerShell installer or npm | -| macOS | x64, arm64 | shell installer or npm | -| Linux | x64, arm64 | shell installer or npm | - -See [Get Started](#get-started) for the one-line install commands. - -## Supported Agents - -The interactive installer auto-detects and configures each of these — wiring up -the MCP server and writing its instructions file: - -- **Claude Code** -- **Cursor** -- **Codex CLI** -- **opencode** -- **Hermes Agent** -- **Gemini CLI** -- **Antigravity IDE** -- **Kiro** - -## Supported Languages - -| Language | Extension | Status | -|----------|-----------|--------| -| TypeScript | `.ts`, `.tsx` | Full support | -| JavaScript | `.js`, `.jsx`, `.mjs` | Full support | -| Python | `.py` | Full support | -| Go | `.go` | Full support | -| Rust | `.rs` | Full support | -| Java | `.java` | Full support | -| C# | `.cs` | Full support | -| PHP | `.php` | Full support | -| Ruby | `.rb` | Full support | -| C | `.c`, `.h` | Full support | -| C++ | `.cpp`, `.hpp`, `.cc` | Full support | -| Objective-C | `.m`, `.mm`, `.h` | Partial support (classes, protocols, methods, `@property`, `#import`, message sends; `.mm` ObjC++ may parse incompletely) | -| Swift | `.swift` | Full support | -| Kotlin | `.kt`, `.kts` | Full support | -| Scala | `.scala`, `.sc` | Full support (classes, traits, methods, type aliases, Scala 3 enums) | -| Dart | `.dart` | Full support | -| Svelte | `.svelte` | Full support (script extraction, Svelte 5 runes, SvelteKit routes) | -| Vue | `.vue` | Full support (script + script-setup extraction, Nuxt page/API/middleware routes) | -| Liquid | `.liquid` | Full support | -| Pascal / Delphi | `.pas`, `.dpr`, `.dpk`, `.lpr` | Full support (classes, records, interfaces, enums, DFM/FMX form files) | -| Lua | `.lua` | Full support (functions, methods with receivers, local variables, `require` imports, call edges) | -| Luau | `.luau` | Full support (everything in Lua, plus `type`/`export type` aliases, typed signatures, and Roblox instance-path `require`) | - -## Troubleshooting - -**"CodeGraph not initialized"** — Run `codegraph init` in your project directory first. - -**Indexing is slow** — Check that `node_modules` and other large directories are excluded. Use `--quiet` to reduce output overhead. - -**MCP hits `database is locked`** — current builds shouldn't: CodeGraph bundles its own Node runtime and uses Node's built-in `node:sqlite` in WAL mode, where concurrent reads never block on a writer. If you still see it: - -- **You're on an old (pre-0.9) install.** Reinstall to get the bundled runtime — `curl -fsSL https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh | sh` (macOS/Linux), `irm https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1 | iex` (Windows), or `npm i -g @colbymchenry/codegraph@latest`. -- **`codegraph status` shows `Journal:` other than `wal`** — WAL couldn't be enabled on this filesystem (common on network shares and WSL2 `/mnt`), so reads can block on writes. Move the project (with its `.codegraph/` folder) onto a local disk. - -**MCP server not connecting** — Ensure the project is initialized/indexed, verify the path in your MCP config, and check that `codegraph serve --mcp` works from the command line. - -**Missing symbols** — The MCP server auto-syncs on save (wait a couple seconds). Run `codegraph sync` manually if needed. Check that the file's language is supported and isn't inside a `.gitignore`d or default-excluded directory (e.g. `node_modules`, `dist`). - -## Star History - - - - - - Star History Chart - - +Environment variables: + +- `CODEDELTA_CACHE_DIR` — override cache location (default: `.codedelta/` in cwd) +- `CODEDELTA_PORT` — API port (default: `3847`) ## License MIT - ---- - -
- -**Made for AI coding agents — Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, and Kiro** - -[Report Bug](https://github.com/colbymchenry/codegraph/issues) · [Request Feature](https://github.com/colbymchenry/codegraph/issues) - -
diff --git a/apps/web/index.html b/apps/web/index.html new file mode 100644 index 000000000..9b5c0712c --- /dev/null +++ b/apps/web/index.html @@ -0,0 +1,12 @@ + + + + + + CodeDelta + + +
+ + + diff --git a/apps/web/package.json b/apps/web/package.json new file mode 100644 index 000000000..e9011ac71 --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,24 @@ +{ + "name": "@codedelta/web", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-router-dom": "^7.1.1" + }, + "devDependencies": { + "@types/react": "^19.0.2", + "@types/react-dom": "^19.0.2", + "@vitejs/plugin-react": "^4.3.4", + "typescript": "^5.0.0", + "vite": "^6.0.6" + }, + "license": "MIT" +} diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx new file mode 100644 index 000000000..edda5fbb2 --- /dev/null +++ b/apps/web/src/App.tsx @@ -0,0 +1,79 @@ +import { NavLink, Outlet, useMatch } from 'react-router-dom'; + +function repoNavPath(repoId: string | undefined, page: string): string { + if (!repoId) return '/import'; + return `/repos/${repoId}/${page}`; +} + +export default function App() { + const match = useMatch('/repos/:repoId/*'); + const repoId = match?.params.repoId; + + return ( +
+
+
+ + CodeDelta + + commit-aware structural code intelligence +
+
+ +
+ + +
+ +
+
+
+ ); +} diff --git a/apps/web/src/api/client.ts b/apps/web/src/api/client.ts new file mode 100644 index 000000000..2e38c5994 --- /dev/null +++ b/apps/web/src/api/client.ts @@ -0,0 +1,59 @@ +import type { + ChangedFile, + CommitDetail, + CommitInfo, + ImportRepoRequest, + ModelProviderConfig, + ProviderKind, + RepoRef, +} from '../types'; + +async function request(path: string, init?: RequestInit): Promise { + const res = await fetch(path, { + headers: { 'Content-Type': 'application/json', ...init?.headers }, + ...init, + }); + if (!res.ok) { + const body = await res.json().catch(() => ({})); + throw new Error((body as { error?: string }).error ?? `Request failed: ${res.status}`); + } + return res.json() as Promise; +} + +export const api = { + health: () => request<{ status: string; product: string }>('/api/health'), + + listRepos: () => request('/api/repos'), + + importRepo: (body: ImportRepoRequest) => + request('/api/repos/import', { method: 'POST', body: JSON.stringify(body) }), + + getRepo: (id: string) => request(`/api/repos/${id}`), + + listBranches: (id: string) => request(`/api/repos/${id}/branches`), + + listCommits: (id: string, branch: string, limit = 50, skip = 0) => + request( + `/api/repos/${id}/commits?branch=${encodeURIComponent(branch)}&limit=${limit}&skip=${skip}`, + ), + + getCommit: (id: string, hash: string) => + request(`/api/repos/${id}/commits/${hash}`), + + getProvider: () => request('/api/settings/provider'), + + setProvider: (config: ModelProviderConfig) => + request('/api/settings/provider', { + method: 'PUT', + body: JSON.stringify(config), + }), +}; + +export type { + RepoRef, + CommitInfo, + CommitDetail, + ChangedFile, + ModelProviderConfig, + ProviderKind, +}; diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx new file mode 100644 index 000000000..4eaf29a3d --- /dev/null +++ b/apps/web/src/main.tsx @@ -0,0 +1,13 @@ +import { StrictMode } from 'react'; +import { createRoot } from 'react-dom/client'; +import { BrowserRouter } from 'react-router-dom'; +import { AppRoutes } from './routes'; +import './styles.css'; + +createRoot(document.getElementById('root')!).render( + + + + + , +); diff --git a/apps/web/src/pages/DeltaViewPage.tsx b/apps/web/src/pages/DeltaViewPage.tsx new file mode 100644 index 000000000..32c33dfff --- /dev/null +++ b/apps/web/src/pages/DeltaViewPage.tsx @@ -0,0 +1,51 @@ +import { useEffect, useState } from 'react'; +import { Link, useParams, useSearchParams } from 'react-router-dom'; +import { api, type RepoRef } from '../api/client'; + +export default function DeltaViewPage() { + const { repoId } = useParams<{ repoId: string }>(); + const [searchParams] = useSearchParams(); + const base = searchParams.get('base') ?? ''; + const head = searchParams.get('head') ?? ''; + + const [repo, setRepo] = useState(null); + + useEffect(() => { + if (!repoId) return; + api.getRepo(repoId).then(setRepo).catch(() => setRepo(null)); + }, [repoId]); + + return ( +
+

Delta View

+

+ Compare commits and visualize structural code changes — symbols, dependency edges, and + impact radius. Not a line-level text diff. +

+ +
+

Coming in Phase 2

+

+ Graph snapshot diff is not built yet. Select base and head commits from the{' '} + Commit Timeline to prepare your comparison. +

+ + {repo && ( +
+
Repository
+
{repo.input}
+
Base commit
+
{base ? {base.slice(0, 12)} : not selected}
+
Head commit
+
{head ? {head.slice(0, 12)} : not selected}
+
+ )} + +

+ Delta View will show: changed symbols, changed dependency edges, affected modules, + impacted entry points, and review suggestions. +

+
+
+ ); +} diff --git a/apps/web/src/pages/ImportPage.tsx b/apps/web/src/pages/ImportPage.tsx new file mode 100644 index 000000000..57b25a42a --- /dev/null +++ b/apps/web/src/pages/ImportPage.tsx @@ -0,0 +1,79 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { api } from '../api/client'; + +export default function ImportPage() { + const navigate = useNavigate(); + const [githubUrl, setGithubUrl] = useState(''); + const [localPath, setLocalPath] = useState(''); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + async function handleImport(source: 'github' | 'local', input: string) { + if (!input.trim()) return; + setLoading(true); + setError(null); + try { + const ref = await api.importRepo({ source, input: input.trim() }); + navigate(`/repos/${ref.id}/timeline`); + } catch (err) { + setError(err instanceof Error ? err.message : 'Import failed'); + } finally { + setLoading(false); + } + } + + return ( +
+

Import Repository

+

+ Import a public GitHub repository or open a local git path. CodeDelta lists commits first + and builds structural graph snapshots only when you analyze them. +

+ + {error &&
{error}
} + +
+

GitHub URL

+

Public repositories only in this version.

+
+ setGithubUrl(e.target.value)} + disabled={loading} + /> + +
+
+ +
+

Local Path

+

Absolute path to a git repository on this machine.

+
+ setLocalPath(e.target.value)} + disabled={loading} + /> + +
+
+
+ ); +} diff --git a/apps/web/src/pages/ProviderSettingsPage.tsx b/apps/web/src/pages/ProviderSettingsPage.tsx new file mode 100644 index 000000000..3ac00a840 --- /dev/null +++ b/apps/web/src/pages/ProviderSettingsPage.tsx @@ -0,0 +1,101 @@ +import { useEffect, useState } from 'react'; +import { api, type ModelProviderConfig, type ProviderKind } from '../api/client'; + +const PROVIDERS: { kind: ProviderKind; label: string; description: string }[] = [ + { kind: 'none', label: 'No AI', description: 'Timeline and Delta View without LLM calls.' }, + { kind: 'codex-oauth', label: 'Codex OAuth', description: 'ChatGPT-style login when available (Phase 3).' }, + { kind: 'openai', label: 'OpenAI API key', description: 'Direct OpenAI API access (Phase 3).' }, + { kind: 'openai-compatible', label: 'OpenAI-compatible', description: 'Custom endpoint (Phase 3).' }, + { kind: 'anthropic', label: 'Anthropic', description: 'Claude API (Phase 3).' }, + { kind: 'ollama', label: 'Ollama', description: 'Local models via Ollama (Phase 3).' }, +]; + +export default function ProviderSettingsPage() { + const [config, setConfig] = useState({ kind: 'none' }); + const [saved, setSaved] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + api.getProvider().then(setConfig).catch(() => setError('Failed to load settings')); + }, []); + + async function save() { + setError(null); + setSaved(false); + try { + const updated = await api.setProvider(config); + setConfig(updated); + setSaved(true); + } catch (err) { + setError(err instanceof Error ? err.message : 'Save failed'); + } + } + + return ( +
+

Provider Settings

+

+ Choose how CodeDelta calls language models for Trace View. No-AI mode keeps all + deterministic structural analysis available. +

+ + {error &&
{error}
} + {saved &&
Settings saved.
} + +
+ {PROVIDERS.map((p) => ( + + ))} +
+ + {config.kind !== 'none' && ( +
+

+ Provider credentials will be configurable in Phase 3. Selection is saved for when + Trace View is enabled. +

+ + + +
+ )} + + +
+ ); +} diff --git a/apps/web/src/pages/TimelinePage.tsx b/apps/web/src/pages/TimelinePage.tsx new file mode 100644 index 000000000..b5f2d137c --- /dev/null +++ b/apps/web/src/pages/TimelinePage.tsx @@ -0,0 +1,185 @@ +import { useCallback, useEffect, useState } from 'react'; +import { Link, useNavigate, useParams, useSearchParams } from 'react-router-dom'; +import { api, type ChangedFile, type CommitDetail, type CommitInfo, type RepoRef } from '../api/client'; + +export default function TimelinePage() { + const { repoId } = useParams<{ repoId: string }>(); + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + + const [repo, setRepo] = useState(null); + const [branches, setBranches] = useState([]); + const [branch, setBranch] = useState(''); + const [commits, setCommits] = useState([]); + const [selected, setSelected] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const loadCommits = useCallback( + async (id: string, b: string) => { + const list = await api.listCommits(id, b); + setCommits(list); + }, + [], + ); + + useEffect(() => { + if (!repoId) return; + let cancelled = false; + + async function load() { + setLoading(true); + setError(null); + try { + const ref = await api.getRepo(repoId!); + if (cancelled) return; + setRepo(ref); + + const branchList = await api.listBranches(repoId!); + if (cancelled) return; + setBranches(branchList); + + const initialBranch = searchParams.get('branch') ?? ref.defaultBranch; + setBranch(branchList.includes(initialBranch) ? initialBranch : (branchList[0] ?? ref.defaultBranch)); + + await loadCommits(repoId!, initialBranch); + } catch (err) { + if (!cancelled) { + setError(err instanceof Error ? err.message : 'Failed to load repository'); + } + } finally { + if (!cancelled) setLoading(false); + } + } + + load(); + return () => { + cancelled = true; + }; + }, [repoId, searchParams, loadCommits]); + + useEffect(() => { + if (!repoId || !branch || loading) return; + loadCommits(repoId, branch).catch((err) => { + setError(err instanceof Error ? err.message : 'Failed to load commits'); + }); + }, [repoId, branch, loading, loadCommits]); + + async function selectCommit(hash: string) { + if (!repoId) return; + try { + const detail = await api.getCommit(repoId, hash); + setSelected(detail); + } catch (err) { + setError(err instanceof Error ? err.message : 'Failed to load commit'); + } + } + + function openDelta(base: string, head: string) { + navigate(`/repos/${repoId}/delta?base=${base}&head=${head}`); + } + + function openTrace(candidate: string) { + navigate(`/repos/${repoId}/trace?candidate=${candidate}`); + } + + if (loading) return

Loading…

; + if (error) return
{error}
; + if (!repo || !repoId) return null; + + return ( +
+

Commit Timeline

+

+ {repo.input} + · {repo.source} +

+ +
+ +
+ +
+ + + + + + + + + + + + {commits.map((c) => ( + selectCommit(c.hash)} + > + + + + + + + ))} + +
HashMessageAuthorDateFiles
{c.shortHash}{c.message}{c.author}{new Date(c.date).toLocaleString()}{c.changedFilesCount}
+ + +
+ +

+ Need another repo? Import again +

+
+ ); +} diff --git a/apps/web/src/pages/TraceViewPage.tsx b/apps/web/src/pages/TraceViewPage.tsx new file mode 100644 index 000000000..202539ecf --- /dev/null +++ b/apps/web/src/pages/TraceViewPage.tsx @@ -0,0 +1,44 @@ +import { useParams, useSearchParams } from 'react-router-dom'; + +export default function TraceViewPage() { + const { repoId } = useParams<{ repoId: string }>(); + const [searchParams] = useSearchParams(); + const candidate = searchParams.get('candidate') ?? ''; + + return ( +
+

Trace View

+

+ Describe a bug, behavior change, or architecture question. CodeDelta traces candidate + commits with evidence — without inventing facts. +

+ +
+ +