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
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@

### Documentation

- simplify the setup story around three cases: default rootless setup, single-project fallback, and explicit `project` retries
- clarify that issue #63 fixed the architecture and workspace-aware workflow, but issue #2 is not fully solved when the client does not provide enough project context
- simplify the setup story around a roots-first contract: roots-capable multi-project sessions, single-project fallback, and explicit `project` retries
- clarify that issue #63 fixed the architecture and workspace-aware workflow, but issue #2 is still only partially solved when the client does not provide roots or active-project context
- remove the repo-local `init` / marker-file story from the public setup guidance

## [1.9.0](https://github.com/PatrickSys/codebase-context/compare/v1.8.2...v1.9.0) (2026-03-19)
Expand Down
8 changes: 4 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ See [README.md](./README.md) for configuration with Claude Desktop, VS Code, Cur
src/
analyzers/
angular/ # Angular-specific analysis
nextjs/ # Next.js routes, metadata, and client/server detection
react/ # React components, hooks, and context patterns
generic/ # Fallback for non-Angular files
core/
indexer.ts # Scans files, creates chunks
Expand All @@ -34,8 +36,6 @@ src/

## What Would Help

**React analyzer** - Biggest gap right now. Look at `src/analyzers/angular/index.ts` for the pattern. Needs to detect components, hooks, context usage, etc.

**Vue analyzer** - Same deal. Detect components, composables, Pinia stores.

**Better search ranking** - The hybrid search in `src/core/search.ts` could use tuning. Currently uses RRF to combine semantic and keyword scores.
Expand All @@ -44,9 +44,9 @@ src/

## Adding a Framework Analyzer

1. Create `src/analyzers/react/index.ts`
1. Create `src/analyzers/<framework>/index.ts`
2. Implement `FrameworkAnalyzer` interface
3. Register in `src/index.ts`
3. Register in `src/index.ts`, `src/cli.ts`, and `src/lib.ts`

The interface is straightforward:

Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ If you get `selection_required`, retry with one of the paths from `availableProj

## Language Support

10 languages with full symbol extraction via Tree-sitter: TypeScript, JavaScript, Python, Java, Kotlin, C, C++, C#, Go, Rust. 30+ languages with indexing and retrieval coverage, including PHP, Ruby, Swift, Scala, Shell, and config formats. Angular has a dedicated analyzer; everything else uses the Generic analyzer with AST-aligned chunking when a grammar is available.
10 languages with full symbol extraction via Tree-sitter: TypeScript, JavaScript, Python, Java, Kotlin, C, C++, C#, Go, Rust. 30+ languages with indexing and retrieval coverage, including PHP, Ruby, Swift, Scala, Shell, and config formats. Angular, React, and Next.js have dedicated analyzers; everything else uses the Generic analyzer with AST-aligned chunking when a grammar is available.

## Configuration

Expand All @@ -156,6 +156,9 @@ If you get `selection_required`, retry with one of the paths from `availableProj
| `CODEBASE_ROOT` | — | Bootstrap root for CLI and single-project MCP clients |
| `CODEBASE_CONTEXT_DEBUG` | — | Set to `1` for verbose logging |
| `EMBEDDING_MODEL` | `Xenova/bge-small-en-v1.5` | Local embedding model override |
| `CODEBASE_CONTEXT_HTTP` | — | Set to `1` to start in HTTP mode (same as `--http` flag) |
| `CODEBASE_CONTEXT_PORT` | `3100` | HTTP server port override (same as `--port`; ignored in stdio mode) |
| `CODEBASE_CONTEXT_CONFIG_PATH` | `~/.codebase-context/config.json` | Override the server config file path |

## Performance

Expand Down
6 changes: 5 additions & 1 deletion docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Per-project config overrides supported today:
- `projects[].analyzerHints.analyzer`: prefers a registered analyzer by name for that project and falls back safely when the name is missing or invalid
- `projects[].analyzerHints.extensions`: adds project-local source extensions for indexing and auto-refresh watching without changing defaults for other projects


Copy-pasteable client config templates are shipped in the package:

- `templates/mcp/stdio/.mcp.json` — stdio setup for `.mcp.json`-style clients
Expand Down Expand Up @@ -104,6 +105,7 @@ Behavior matrix:
Rules:

- If the client provides workspace context, that becomes the trusted workspace boundary for the session. In practice this usually comes from MCP roots.
- Treat seamless multi-project routing as evidence-backed only for roots-capable hosts. Without roots, explicit fallback is still required.
- If the server still cannot tell which project to use, a bootstrap path or explicit absolute `project` path remains the fallback.
- `project` is the canonical explicit selector when routing is ambiguous.
- `project` may point at a project path, file path, `file://` URI, or relative subproject path.
Expand Down Expand Up @@ -267,6 +269,8 @@ Impact is 2-hop transitive: direct importers (hop 1) and their importers (hop 2)
## Analyzers

- **Angular**: signals, standalone components, control flow syntax, lifecycle hooks, DI patterns, component metadata
- **React**: function/class components, custom hooks, context usage, memoization, Suspense, ecosystem signal extraction
- **Next.js**: App Router and Pages Router detection, route/API classification, route paths, `"use client"`, metadata exports
- **Generic**: 30+ have indexing/retrieval coverage including PHP, Ruby, Swift, Scala, Shell, config/markup., 10 languages have full symbol extraction (Tree-sitter: TypeScript, JavaScript, Python, Java, Kotlin, C, C++, C#, Go, Rust).

Notes:
Expand All @@ -289,6 +293,6 @@ Reproducible evaluation is shipped as a CLI entrypoint backed by shared scoring/

- **Symbol refs are not a call-graph.** `get_symbol_references` counts identifier-node occurrences in the AST (comments/strings excluded via Tree-sitter). It does not distinguish call sites from type annotations, variable assignments, or imports. Full call-site-specific analysis (`call_expression` nodes only) is a roadmap item.
- **Impact is 2-hop max.** `computeImpactCandidates` walks direct importers then their importers. Full BFS reachability is on the roadmap.
- **Angular is the only framework with a rich dedicated analyzer.** All other languages go through the Generic analyzer (30+ languages, chunking + import graph, no framework-specific signal extraction).
- **Angular, React, and Next.js have dedicated analyzers.** All other languages go through the Generic analyzer (30+ languages, chunking + import graph, no framework-specific signal extraction).
- **Default embedding model is `bge-small-en-v1.5` (512-token context).** Granite (8192 context) is opt-in via `EMBEDDING_MODEL`. OpenAI is opt-in via `EMBEDDING_PROVIDER=openai` — sends code externally.
- **Patterns are file-level frequency counts.** Not semantic clustering. Rising/Declining trend is derived from git commit recency for files using each pattern, not from usage semantics.
16 changes: 14 additions & 2 deletions docs/client-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@ npx -y codebase-context --http --port 4000

Copy-pasteable templates: [`templates/mcp/stdio/.mcp.json`](../templates/mcp/stdio/.mcp.json) and [`templates/mcp/http/.mcp.json`](../templates/mcp/http/.mcp.json).

## Project routing contract

Automatic multi-project routing is evidence-backed only when the MCP host announces workspace roots. Treat that as the primary path.

If the host does not send roots, or still cannot tell which project is active, use one of the explicit fallbacks instead:

- start the server with a single bootstrap path
- set `CODEBASE_ROOT`
- retry tool calls with `project`

If multiple projects are available and no active project can be inferred safely, the server returns `selection_required` instead of guessing.

## Claude Code

```bash
Expand Down Expand Up @@ -197,9 +209,9 @@ Check these three flows:

1. **Single project** — call `search_codebase` or `metadata`. Routing is automatic.

2. **Multiple projects, one server entry** — open two repos or a monorepo. Call `codebase://context`. Expected: workspace overview, then automatic routing once a project is active.
2. **Multiple projects on a roots-capable host** — open two repos or a monorepo. Call `codebase://context`. Expected: workspace overview, then automatic routing once a project is active.

3. **Ambiguous selection** — start without a bootstrap path, call `search_codebase`. Expected: `selection_required`. Retry with `project` set to `apps/dashboard` or `/repos/customer-portal`.
3. **Ambiguous or no-roots selection** — start without a bootstrap path, call `search_codebase`. Expected: `selection_required`. Retry with `project` set to `apps/dashboard` or `/repos/customer-portal`.

For monorepos, test all three selector forms:

Expand Down
8 changes: 8 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@
"import": "./dist/analyzers/angular/index.js",
"types": "./dist/analyzers/angular/index.d.ts"
},
"./analyzers/react": {
"import": "./dist/analyzers/react/index.js",
"types": "./dist/analyzers/react/index.d.ts"
},
"./analyzers/nextjs": {
"import": "./dist/analyzers/nextjs/index.js",
"types": "./dist/analyzers/nextjs/index.d.ts"
},
"./analyzers/generic": {
"import": "./dist/analyzers/generic/index.js",
"types": "./dist/analyzers/generic/index.d.ts"
Expand Down
2 changes: 2 additions & 0 deletions scripts/run-vitest.mjs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Spawns vitest via process.execPath to avoid bin-resolution failures when
// Node is invoked directly (e.g. `node scripts/run-vitest.mjs`) without pnpm.
import { spawn } from 'node:child_process';
import { fileURLToPath } from 'node:url';

Expand Down
34 changes: 19 additions & 15 deletions src/analyzers/angular/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
KEYWORD_INDEX_FILENAME
} from '../../constants/codebase-context.js';
import { registerComplementaryPatterns } from '../../patterns/semantics.js';
import { isFileNotFoundError } from '../shared/metadata.js';

interface AngularInput {
name: string;
Expand Down Expand Up @@ -911,7 +912,7 @@ export class AngularAnalyzer implements FrameworkAnalyzer {
...packageJson.devDependencies
};

const angularVersion = allDeps['@angular/core']?.replace(/[\^~]/, '') || 'unknown';
const angularCoreVersion = allDeps['@angular/core'];

// Detect state management
const stateManagement: string[] = [];
Expand All @@ -931,15 +932,17 @@ export class AngularAnalyzer implements FrameworkAnalyzer {
if (allDeps['karma']) testingFrameworks.push('Karma');
if (allDeps['jest']) testingFrameworks.push('Jest');

metadata.framework = {
name: 'Angular',
version: angularVersion,
type: 'angular',
variant: 'unknown', // Will be determined during analysis
stateManagement,
uiLibraries,
testingFrameworks
};
if (angularCoreVersion) {
metadata.framework = {
name: 'Angular',
version: angularCoreVersion.replace(/[\^~]/, ''),
type: 'angular',
variant: 'unknown', // Will be determined during analysis
stateManagement,
uiLibraries,
testingFrameworks
};
}

// Convert dependencies
metadata.dependencies = Object.entries(allDeps).map(([name, version]) => ({
Expand All @@ -948,7 +951,9 @@ export class AngularAnalyzer implements FrameworkAnalyzer {
category: this.categorizeDependency(name)
}));
} catch (error) {
console.warn('Failed to read Angular project metadata:', error);
if (!isFileNotFoundError(error)) {
console.warn('Failed to read Angular project metadata:', error);
}
}

// Calculate statistics from existing index if available
Expand All @@ -966,8 +971,6 @@ export class AngularAnalyzer implements FrameworkAnalyzer {
const chunks =
parsedObj && Array.isArray(parsedObj.chunks) ? (parsedObj.chunks as IndexChunk[]) : null;
if (Array.isArray(chunks) && chunks.length > 0) {
console.error(`Loading statistics from ${indexPath}: ${chunks.length} chunks`);

metadata.statistics.totalFiles = new Set(chunks.map((c) => c.filePath)).size;
metadata.statistics.totalLines = chunks.reduce(
(sum, c) => sum + ((c.endLine ?? 0) - (c.startLine ?? 0) + 1),
Expand Down Expand Up @@ -1005,8 +1008,9 @@ export class AngularAnalyzer implements FrameworkAnalyzer {
metadata.architecture.layers = layerCounts;
}
} catch (error) {
// Index doesn't exist yet, keep statistics at 0
console.warn('Failed to calculate statistics from index:', error);
if (!isFileNotFoundError(error)) {
console.warn('Failed to calculate statistics from index:', error);
}
}

return metadata;
Expand Down
Loading
Loading