Skip to content
Draft
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
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,18 @@ This repo keeps test fixtures out of default Codegraph scans with `codegraph.con
{
"discovery": {
"ignoreGlobs": ["tests/samples/**", "tests/languages/samples/**"]
},
"languages": {
"extensions": {
".tpl": "php"
}
}
}
```

Use this pattern in other repos when large fixture, generated, or vendored trees should not participate in search, unresolved-import checks, graphing, indexing, inspect, impact, or review runs. Config globs are project-root-relative. CLI `--include-glob` and `--ignore-glob` stay relative to each active scan root.
Use discovery globs in other repos when large fixture, generated, or vendored trees should not participate in search, unresolved-import checks, graphing, indexing, inspect, impact, or review runs. Config globs are project-root-relative, while CLI `--include-glob` and `--ignore-glob` stay relative to each active scan root.

`languages.extensions` maps additional or built-in literal file suffixes to supported language IDs; longer suffixes win, and built-ins remain active unless explicitly remapped. Suffixes may contain letters, digits, `.`, `_`, `+`, and `-`. The `.vue` and `.svelte` suffixes are always handled as single-file components and cannot be remapped.

## Quick start

Expand All @@ -87,6 +94,9 @@ node ./dist/cli.js review --base HEAD --head WORKTREE --summary
# broader blast-radius map when the review packet needs expansion
node ./dist/cli.js impact --base HEAD --head WORKTREE --pretty

# focused affected-test list for current edits
node ./dist/cli.js affected --base HEAD --head WORKTREE --quiet

# one-call answer for a concrete repo question
node ./dist/cli.js explore "how does auth reach db?" --root . --pretty

Expand Down Expand Up @@ -138,6 +148,7 @@ Use these as starting points, then see [docs/cli.md](./docs/cli.md) for all flag
# fastest code-review handoff for current edits
codegraph review --base HEAD --head WORKTREE --summary
codegraph impact --base HEAD --head WORKTREE --pretty
codegraph affected --base HEAD --head WORKTREE --quiet

# repo question, orientation, and bounded follow-up
codegraph explore "how does auth reach db?" --root . --pretty
Expand Down
7 changes: 5 additions & 2 deletions codegraph-skill/codegraph/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Use Codegraph for structure-aware repo questions:

- repo overview, hotspots, cycles, unresolved imports, and public API surface
- symbol navigation with definitions, references, dependencies, and paths
- PR or worktree impact review with candidate tests and risk signals
- PR or worktree impact review with candidate tests, affected test lists, and risk signals
- duplicate cleanup and refactor-risk triage
- bounded agent context through explore, orientation, search, packets, explain, and MCP

Expand Down Expand Up @@ -40,6 +40,7 @@ Then choose the smallest useful follow-up:
- references: `codegraph refs --file <file> --line <line> --col <column> --pretty`
- duplicates: `codegraph duplicates --root . ./src --profile cleanup`
- impact: `codegraph impact --base HEAD --head WORKTREE --pretty`
- affected tests: `codegraph affected --base HEAD --head WORKTREE --quiet`
- review: `codegraph review --base HEAD --head WORKTREE --summary`
- drift: `codegraph drift ./src --base origin/main --head HEAD --pretty --graph-edges summary --public-api removals`
- installer: `codegraph install --target codex,claude --dry-run`
Expand All @@ -64,6 +65,7 @@ Current high-value surfaces:
- `orient --pretty`: ranked first-turn focus targets with copyable follow-ups
- `impact --pretty`: ranked "what could this break?" map
- `review --summary`: compact reviewer handoff
- `affected --quiet`: deterministic path-only test selection for a changed-file set
- `duplicates --profile cleanup`: refactor ROI ordering
- `duplicates --json`: full grouped duplicate data

Expand All @@ -78,7 +80,8 @@ Fall back to CLI when MCP is unavailable.

## Discovery

Durable repo-local ignores belong in `codegraph.config.json`.
Durable repo-local ignores and custom language suffixes belong in `codegraph.config.json`.
Use `languages.extensions` for literal suffix-to-language mappings such as `.tpl` to `php`; keys start with `.`, may contain letters, digits, `.`, `_`, `+`, and `-`, and values are supported language IDs. The longest suffix wins; `.vue` and `.svelte` are always handled as single-file components and cannot be remapped.
One-off CLI filters use scan-root-relative `--include-glob` and `--ignore-glob`.
Use `--no-gitignore` only when ignored files are intentionally in scope.

Expand Down
26 changes: 25 additions & 1 deletion docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Default workflow:

- code review: `codegraph review --base HEAD --head WORKTREE --summary`
- blast-radius follow-up: `codegraph impact --base HEAD --head WORKTREE --pretty`
- affected tests: `codegraph affected --base HEAD --head WORKTREE --quiet`
- unfamiliar repo: `codegraph explore "how does auth reach db?" --root . --pretty`
- first-turn map: `codegraph orient --root . --budget small --pretty`
- targeted follow-up: `codegraph search "<query>" --json` then `codegraph explain <handle|file|symbol>`
Expand All @@ -35,18 +36,27 @@ Commands that scan a project read `codegraph.config.json` from `--root` when it
"includeGlobs": ["src/**/*.ts"],
"ignoreGlobs": ["tests/samples/**", "tests/languages/samples/**"],
"useGitignore": true
},
"languages": {
"extensions": {
".tpl": "php",
".inc.php": "php",
".build.ts": "ts"
}
}
}
```

- `discovery.includeGlobs` and `discovery.ignoreGlobs` are project-root-relative, even when a command scans child include roots.
- `discovery.ignoreGlobs` is for large fixture, generated, or vendored folders that should not be indexed.
- `languages.extensions` maps additional or built-in literal suffixes to supported language IDs; keys must start with `.` and may contain letters, digits, `.`, `_`, `+`, and `-`, values must name a supported language, and the longest suffix wins.
- Built-in suffixes remain active unless explicitly remapped by `languages.extensions`; `.vue` and `.svelte` are always handled as single-file components and cannot be remapped.
- CLI `--include-glob` and `--ignore-glob` values are one-off additions relative to each scanned root.
- `inspect` follow-up commands preserve the selected `--root` and include roots.
- `--no-gitignore` overrides `useGitignore`.

Config globs and one-off CLI globs apply at different layers. `codegraph.config.json` globs are durable and project-root-relative. CLI scan-root globs are additive for a single command and are evaluated relative to each active scan root. `--no-gitignore` disables `.gitignore` filtering for that command only; it does not change config.
Cache and manifest reuse is rooted at `--root`. Reusing a project root lets commands share compatible index and graph entries when the file signatures, config, graph options, and relevant build options still match. Changing `--root`, changing discovery config, or changing graph options creates a different reuse boundary. Child include-root scans can reuse project-root cache entries, but command summaries and follow-up commands stay scoped to the selected include roots.
Configured language extensions automatically extend discovery for matching files and participate in cache compatibility checks. Reusing a project root lets commands share compatible index and graph entries when the file signatures, config, graph options, and relevant build options still match. Changing `--root`, changing discovery or language-extension config, or changing graph options creates a different reuse boundary. Child include-root scans can reuse project-root cache entries, but command summaries and follow-up commands stay scoped to the selected include roots.

## Core commands

Expand All @@ -56,6 +66,7 @@ Cache and manifest reuse is rooted at `--root`. Reusing a project root lets comm
# Fast code-review handoff for current local edits
codegraph review --base HEAD --head WORKTREE --summary
codegraph impact --base HEAD --head WORKTREE --pretty
codegraph affected --base HEAD --head WORKTREE --quiet

# First-pass repo summary and next-step suggestions
codegraph orient --root . --budget small --pretty
Expand Down Expand Up @@ -125,6 +136,19 @@ Graph, index, and review reports include `backend.native.byLanguage` so native u
- `uninit` removes only recognized lifecycle state by default. It refuses unknown `.codegraph/` entries unless `--force` is passed.
- Lifecycle commands accept either a positional project path or `--root <path>`. They reject using both together because lifecycle manifests always describe one project boundary, not include-root subsets.

### Affected tests

- `affected` maps changed source files to likely test files by traversing reverse dependencies through the project graph. It also includes directly changed test files at depth 0.
- Inputs can be positional files, newline-delimited `--stdin`, or a Git range with `--base <ref> --head <ref>`. Paths are normalized under `--root` and output as project-root-relative paths.
- Use `--depth <n>` to expand transitive reverse dependencies, `--filter <glob>` to restrict returned test paths, `--quiet` for path-only output, or `--json` for `schemaVersion: 1`, `changedFiles`, `affectedTests`, and `omittedCounts`.

```bash
codegraph affected src/auth.ts src/db.ts
codegraph affected --stdin --quiet
codegraph affected --base main --head HEAD --json
codegraph affected --base HEAD --head WORKTREE --filter "tests/**/*.test.ts" --quiet
```

### Symbols, navigation, grep, and chunking

```bash
Expand Down
5 changes: 4 additions & 1 deletion docs/library-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ For repeated calls, prefer one warm session instead of rebuilding indexes ad hoc
- `createCodeReviewSession()` for repeated navigation and impact work in library code
- `createAgentSession()` or MCP for repeated orient/search/explain/packet work in agent hosts

CLI commands and agent sessions read `codegraph.config.json` from the project root when it exists. Core indexing APIs keep discovery explicit, so pass `discovery` options directly when you want the same scan scope in custom code:
CLI commands and agent sessions read `codegraph.config.json` from the project root when it exists. Core indexing APIs keep discovery and language mappings explicit, so pass both options directly when you want the same behavior in custom code:

```ts
import { buildProjectIndex, loadCodegraphConfig } from "@lzehrung/codegraph";
Expand All @@ -37,9 +37,12 @@ const root = process.cwd();
const config = await loadCodegraphConfig(root);
const index = await buildProjectIndex(root, {
...(config.discovery ? { discovery: config.discovery } : {}),
...(config.languages?.extensions ? { languageExtensions: config.languages.extensions } : {}),
});
```

`languageExtensions` uses normalized literal suffixes beginning with `.`, supported language IDs, and longest-suffix matching. Suffixes may contain letters, digits, `.`, `_`, `+`, and `-`; `.vue` and `.svelte` remain single-file components and cannot be remapped.

## Public API Boundary

The npm package exposes these supported entry points:
Expand Down
4 changes: 2 additions & 2 deletions docs/plans/2026-07-03-16-config-extension-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ type CodegraphConfig = {

Rules:

- Extension keys must start with `.`.
- Extension keys must be literal suffixes starting with `.` and containing only letters, digits, `.`, `_`, `+`, or `-`.
- Values must be supported language ids.
- Longer extension keys win first, so `.inc.php` beats `.php`.
- Built-in extensions remain unless explicitly remapped.
- Built-in extensions remain unless explicitly remapped; `.vue` and `.svelte` remain single-file components and cannot be remapped.
- Invalid mappings fail config validation with actionable errors.

## Integration
Expand Down
20 changes: 15 additions & 5 deletions src/agent/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import type { BuildOptions, BuildReport, ProjectIndex } from "../indexer/types.j
import { buildSymbolGraphDetailed } from "../graphs/symbol-graph-detailed.js";
import { type SymbolGraph } from "../graphs/symbol-graph.js";
import type { Graph } from "../types.js";
import { listProjectFiles, type ProjectFileDiscoveryOptions } from "../util/projectFiles.js";
import { DEFAULT_PROJECT_PATTERNS, listProjectFiles, type ProjectFileDiscoveryOptions } from "../util/projectFiles.js";
import { hasDiscoveryOptions, loadCodegraphConfig, mergeDiscoveryOptions } from "../config.js";
import { languageExtensionPatterns, normalizeLanguageExtensions } from "../languages.js";
import { createAgentFileLookup } from "./normalize.js";
import { summarizeAnalysis, type AnalysisSummary } from "../analysisSummary.js";

Expand Down Expand Up @@ -81,6 +82,7 @@ export type AgentFileSignature = {

type AgentDiscoverySettings = {
discoveryOptions?: ProjectFileDiscoveryOptions;
languageExtensions?: BuildOptions["languageExtensions"];
};

type AgentFreshnessDiff = {
Expand All @@ -96,12 +98,19 @@ async function resolveAgentDiscoverySettings(options: AgentSessionOptions): Prom
const discoveryOptions = hasDiscoveryOptions(discovery)
? { ...discovery, globRoot: discovery.globRoot ?? options.root }
: undefined;
return discoveryOptions ? { discoveryOptions } : {};
const languageExtensions =
normalizeLanguageExtensions(options.buildOptions?.languageExtensions) ?? config.languages?.extensions;
return {
...(discoveryOptions ? { discoveryOptions } : {}),
...(languageExtensions ? { languageExtensions } : {}),
};
}

export async function listAgentSessionFiles(options: AgentSessionOptions): Promise<string[]> {
const { discoveryOptions } = await resolveAgentDiscoverySettings(options);
return await listProjectFiles(options.root, undefined, discoveryOptions);
const { discoveryOptions, languageExtensions } = await resolveAgentDiscoverySettings(options);
const customPatterns = languageExtensionPatterns(languageExtensions);
const patterns = customPatterns.length ? [...DEFAULT_PROJECT_PATTERNS, ...customPatterns] : undefined;
return await listProjectFiles(options.root, patterns, discoveryOptions);
}

function isMissingStatRace(error: unknown): boolean {
Expand Down Expand Up @@ -200,7 +209,7 @@ export function createAgentSession(options: AgentSessionOptions): AgentSession {
const loadBase = async (): Promise<AgentProjectBaseSnapshot> => {
if (cachedBase) return cachedBase;
const loadPromise = (async () => {
const { discoveryOptions } = await resolveAgentDiscoverySettings(options);
const { discoveryOptions, languageExtensions } = await resolveAgentDiscoverySettings(options);
const files = await loadFiles();
cachedFileSignatures = await collectAgentFileSignatures(files);
const buildOptions: BuildOptions & { files: string[] } = {
Expand All @@ -209,6 +218,7 @@ export function createAgentSession(options: AgentSessionOptions): AgentSession {
keepParsed: options.buildOptions?.keepParsed ?? true,
files,
...(discoveryOptions ? { discovery: discoveryOptions } : {}),
...(languageExtensions ? { languageExtensions } : {}),
};
if (
options.buildOptions?.useNativeWorkers === undefined &&
Expand Down
Loading