Skip to content

Repository files navigation

codebase-ctx

Generate AI-optimized codebase summaries via static analysis.

npm versionnpm downloadslicensenodeTypeScript

codebase-ctx is a zero-dependency static analysis tool that reads a project directory and produces structured, token-efficient context about its setup, dependencies, scripts, language, runtime, and architecture. The output is designed to be injected into AI instruction files (CLAUDE.md, .cursorrules), passed as prompt context, or consumed programmatically by other tools.

Unlike source code dumpers that concatenate entire repositories (producing hundreds of thousands of tokens), codebase-ctx runs a pipeline of modular analyzers that each extract a specific dimension of project context and compress the results into a structured summary. A typical output is 300--800 tokens: enough to convey what a developer learns in their first hour with a codebase, compact enough to leave the vast majority of the context window for the actual task.

All analysis is deterministic, offline, and fast (under 500ms for most projects). No LLM calls, no network requests, no API keys required.


Installation

npm install codebase-ctx

Or install globally for CLI usage:

npm install -g codebase-ctx

Requires Node.js >= 18.


Quick Start

import{analyzeProject,analyzeDependencies,analyzeScripts}from'codebase-ctx';// Analyze project metadataconstproject=analyzeProject('/path/to/project');console.log(project);// {// name: 'my-app',// version: '1.0.0',// description: 'My application',// license: 'MIT',// language: 'TypeScript',// runtime: 'Node.js',// repository: 'https://github.com/user/my-app',// nodeVersion: '>=18',// }// Analyze dependencies with automatic categorizationconstdeps=analyzeDependencies('/path/to/project');console.log(deps.summary);// {// totalProduction: 5,// totalDev: 3,// frameworks: ['react', 'next'],// databases: ['@prisma/client'],// testingTools: ['vitest'],// }// Analyze npm scriptsconstscripts=analyzeScripts('/path/to/project');console.log(scripts.hasBuild,scripts.hasTest);// true true

Features

  • Zero dependencies -- all analysis uses Node.js built-ins (node:fs, node:path). No AST parsers, no tree-sitter, no external tools.
  • Modular analyzers -- each analyzer extracts one dimension of context (project metadata, dependencies, scripts) and runs independently. Analyzers fail gracefully when their input files are missing.
  • Dependency categorization -- a built-in registry of 190+ common npm packages automatically classifies dependencies into categories: framework, database, testing, build, lint, ui, auth, api, observability, and type-definitions.
  • Language detection -- infers TypeScript or JavaScript from tsconfig.json, file extensions in src/, or the presence of typescript in dependencies.
  • Runtime detection -- infers Node.js, Bun, Deno, or Browser from engines fields and framework dependencies.
  • Script categorization -- classifies npm scripts into build, test, lint, start, deploy, and other categories by name pattern matching.
  • Token estimation -- estimates LLM token counts for any text using ceil(chars / 4).
  • Deterministic output -- same codebase always produces the same analysis result.
  • TypeScript-first -- full type definitions shipped with the package.

API Reference

Analyzers

analyzeProject(projectPath: string): ProjectInfo

Reads package.json and inspects the project directory to produce a ProjectInfo summary.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:ProjectInfo

FieldTypeDescription
namestring | nullPackage name from package.json, or directory name as fallback
versionstring | nullPackage version
descriptionstring | nullPackage description
licensestring | nullLicense identifier (e.g. "MIT")
languagestringDetected language: "TypeScript", "JavaScript", or "unknown"
runtimestringDetected runtime: "Node.js", "Bun", "Deno", "Browser", or "unknown"
repositorystring | nullRepository URL (cleaned of git+ prefix and .git suffix)
nodeVersionstring | nullNode.js version constraint from engines.node

Language detection checks in order: tsconfig.json existence, .ts/.tsx files in src/, typescript in dependencies. Falls back to "JavaScript".

Runtime detection checks in order: engines.bun, engines.deno, browser-only framework dependencies (React/Vue/Angular/Svelte without an SSR framework like Next/Nuxt), then defaults to "Node.js".

Fallback behavior: When no package.json exists, returns the directory name as name, "unknown" as language and runtime, and null for all other fields.

Example:

import{analyzeProject}from'codebase-ctx';constinfo=analyzeProject('/home/user/my-express-app');// info.language === 'TypeScript'// info.runtime === 'Node.js'// info.repository === 'https://github.com/user/my-express-app'

analyzeDependencies(projectPath: string): DependencyInfo

Reads package.json and extracts all dependency sections, categorizing each dependency by its purpose using a built-in registry.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:DependencyInfo

FieldTypeDescription
productionDependencyEntry[]Dependencies from dependencies
devDependencyEntry[]Dependencies from devDependencies
peerDependencyEntry[]Dependencies from peerDependencies
optionalDependencyEntry[]Dependencies from optionalDependencies
summaryobjectAggregated summary (see below)

summary fields:

FieldTypeDescription
totalProductionnumberCount of production dependencies
totalDevnumberCount of dev dependencies
frameworksstring[]Names of all framework dependencies across all sections
databasesstring[]Names of all database dependencies across all sections
testingToolsstring[]Names of all testing dependencies across all sections

Each DependencyEntry has the shape:

{
name: string;// Package name (e.g. "react")
version: string;// Version range (e.g. "^18.2.0")
category: DependencyCategory;}

Category inference: Known packages are mapped via a built-in registry of 190+ entries. Unrecognized packages in dependencies default to "utility"; unrecognized packages in devDependencies default to "build". Packages matching @types/* are always categorized as "type-definitions".

Fallback behavior: When package.json does not exist, returns empty arrays and zero counts for all fields.

Example:

import{analyzeDependencies}from'codebase-ctx';constdeps=analyzeDependencies('/home/user/my-project');// Inspect categorized production depsfor(constdepofdeps.production){console.log(`${dep.name} (${dep.category}): ${dep.version}`);}// react (framework): ^18.2.0// @prisma/client (database): ^5.0.0// winston (observability): ^3.0.0// Use the summary for quick contextconsole.log(deps.summary.frameworks);// ['react']console.log(deps.summary.databases);// ['@prisma/client']

analyzeScripts(projectPath: string): ScriptInfo

Reads the scripts field from package.json and categorizes each script by name.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:ScriptInfo

FieldTypeDescription
scriptsScriptEntry[]All scripts with their names, commands, and categories
hasBuildbooleantrue if any script is categorized as "build"
hasTestbooleantrue if any script is categorized as "test"
hasLintbooleantrue if any script is categorized as "lint"
hasStartbooleantrue if any script is categorized as "start"

Each ScriptEntry has the shape:

{
name: string;// Script name (e.g. "build")
command: string;// Script command (e.g. "tsc")
category: 'build'|'test'|'lint'|'start'|'deploy'|'other';}

Category mapping:

CategoryMatched script names
buildbuild, compile, bundle, tsc, build:*
testtest, spec, e2e, coverage, test:*
lintlint, check, format, prettier, lint:*
startstart, dev, serve, develop
deploydeploy, release, publish
otherEverything else

Scripts prefixed with pre or post (e.g. pretest, postbuild, prepublishOnly) inherit the category of their base script.

Fallback behavior: When package.json has no scripts field or does not exist, returns an empty array and all boolean flags as false.

Example:

import{analyzeScripts}from'codebase-ctx';constscripts=analyzeScripts('/home/user/my-project');if(scripts.hasTest){consttestScripts=scripts.scripts.filter(s=>s.category==='test');for(constsoftestScripts){console.log(`${s.name}: ${s.command}`);}}// test: vitest run// test:unit: vitest run src/

Dependency Registry

DEPENDENCY_REGISTRY

A Record<string, DependencyCategory> mapping 190+ common npm package names to their categories. This is the lookup table used by analyzeDependencies.

import{DEPENDENCY_REGISTRY}from'codebase-ctx';console.log(DEPENDENCY_REGISTRY['react']);// 'framework'console.log(DEPENDENCY_REGISTRY['prisma']);// 'database'console.log(DEPENDENCY_REGISTRY['vitest']);// 'testing'console.log(DEPENDENCY_REGISTRY['typescript']);// 'build'console.log(DEPENDENCY_REGISTRY['eslint']);// 'lint'console.log(DEPENDENCY_REGISTRY['tailwindcss']);// 'ui'console.log(DEPENDENCY_REGISTRY['passport']);// 'auth'console.log(DEPENDENCY_REGISTRY['axios']);// 'api'console.log(DEPENDENCY_REGISTRY['winston']);// 'observability'

Categories covered:framework, database, testing, build, lint, ui, auth, api, observability.


categorize(name: string, isDevDep: boolean): DependencyCategory

Categorizes a single package name. Checks the built-in registry first, then @types/* prefix, then falls back to "utility" for production dependencies or "build" for dev dependencies.

Parameters:

ParameterTypeDescription
namestringThe npm package name
isDevDepbooleanWhether the package is a devDependency

Returns:DependencyCategory -- one of "framework", "database", "testing", "build", "lint", "utility", "type-definitions", "ui", "auth", "api", "observability".

Example:

import{categorize}from'codebase-ctx';categorize('react',false);// 'framework'categorize('@types/node',true);// 'type-definitions'categorize('some-unknown-pkg',false);// 'utility'categorize('some-unknown-pkg',true);// 'build'

File Utilities

fileExists(filePath: string): boolean

Synchronously checks whether a file exists at the given path.

import{fileExists}from'codebase-ctx';if(fileExists('/path/to/tsconfig.json')){// TypeScript project}

readFileContent(filePath: string): string

Reads a file synchronously and returns its contents as a UTF-8 string. Throws if the file does not exist.

import{readFileContent}from'codebase-ctx';constcontent=readFileContent('/path/to/package.json');

readLines(filePath: string): string[]

Reads a file and splits it into an array of lines. Throws if the file does not exist.

import{readLines}from'codebase-ctx';constlines=readLines('/path/to/src/index.ts');console.log(`${lines.length} lines`);

readJsonFile<T>(filePath: string): T | null

Reads and parses a JSON file. Returns null if the file does not exist or contains invalid JSON. Never throws.

Type parameter:T -- the expected shape of the parsed JSON object.

import{readJsonFile}from'codebase-ctx';interfacePkgJson{name: string;version: string;}constpkg=readJsonFile<PkgJson>('/path/to/package.json');if(pkg){console.log(pkg.name,pkg.version);}

estimateTokens(text: string): number

Estimates the LLM token count for a string using the approximation Math.ceil(text.length / 4).

Returns 0 for empty strings.

import{estimateTokens}from'codebase-ctx';consttokens=estimateTokens('Hello, world!');// 4 (ceil(13 / 4))

Configuration

AnalyzeOptions

Options accepted by the analysis pipeline:

interfaceAnalyzeOptions{analyzers?: AnalyzerName[];// Which analyzers to run (default: all)exclude?: string[];// Directory/file patterns to excludedetailLevel?: DetailLevel;// 'minimal' | 'standard' | 'detailed'maxSampleFiles?: number;// Max files for pattern samplingmaxDepth?: number;// Max directory traversal depth}

FormatOptions

Options for formatting output:

interfaceFormatOptions{detailLevel?: DetailLevel;// 'minimal' | 'standard' | 'detailed'formatter?: (context: CodebaseContext)=>string;// Custom formatter functionincludeTokenCount?: boolean;// Append token count to output}

AnalyzerName

Valid analyzer names:

"project" | "dependencies" | "structure" | "typescript" | "api" | "scripts" | "config" | "git" | "stats" | "patterns"

DependencyCategory

Valid dependency categories:

"framework" | "database" | "testing" | "build" | "lint" | "utility" | "type-definitions" | "ui" | "auth" | "api" | "observability"

DetailLevel

Controls information density in formatted output:

LevelTarget tokensDescription
minimal150--300Language, framework, architecture, entry point, build/test commands only
standard400--800All major context dimensions with summaries
detailed800--2,000Full dependency lists, complete API surface, all patterns with evidence

OutputFormat

Supported output formats:

FormatDescription
markdownStructured markdown for CLAUDE.md / .cursorrules injection
jsonMachine-readable JSON for programmatic consumption
compactMinimal token count, maximum information density
customUser-provided formatter function via FormatOptions.formatter

Error Handling

All analyzers follow a graceful degradation pattern:

  • Missing package.json: Analyzers that depend on package.json return sensible defaults -- empty arrays, zero counts, null fields, or the directory name as a fallback project name.
  • Invalid JSON: readJsonFile returns null when a file contains malformed JSON. Analyzers that use it handle the null case explicitly.
  • Missing files: fileExists returns false; analyzers skip analysis for files that do not exist rather than throwing.
  • No matching data: Analyzers return empty result objects (empty arrays, false flags) rather than throwing when the data they look for is absent.

The general contract: individual analyzers never throw. If an analyzer cannot extract data, it returns a typed fallback value. Only infrastructure-level errors (directory does not exist, permission denied) produce exceptions.


Advanced Usage

Combining Analyzers

Run multiple analyzers against the same project and assemble the results:

import{analyzeProject,analyzeDependencies,analyzeScripts}from'codebase-ctx';constprojectPath='/home/user/my-app';constproject=analyzeProject(projectPath);constdeps=analyzeDependencies(projectPath);constscripts=analyzeScripts(projectPath);// Build a context summary for prompt injectionconstsummary=[`Project: ${project.name} v${project.version}`,`Language: ${project.language}, Runtime: ${project.runtime}`,`Frameworks: ${deps.summary.frameworks.join(', ')||'none'}`,`Databases: ${deps.summary.databases.join(', ')||'none'}`,`Testing: ${deps.summary.testingTools.join(', ')||'none'}`,`Build: ${scripts.hasBuild ? 'yes' : 'no'}, Test: ${scripts.hasTest ? 'yes' : 'no'}`,].join('\n');console.log(summary);

Extending the Dependency Registry

You can use categorize alongside your own logic to handle packages not in the built-in registry:

import{categorize,DEPENDENCY_REGISTRY}from'codebase-ctx';importtype{DependencyCategory}from'codebase-ctx';constCUSTOM_REGISTRY: Record<string,DependencyCategory>={'my-internal-framework': 'framework','@company/auth-sdk': 'auth',};functioncustomCategorize(name: string,isDev: boolean): DependencyCategory{if(CUSTOM_REGISTRY[name])returnCUSTOM_REGISTRY[name];returncategorize(name,isDev);}

Estimating Prompt Budget

Use estimateTokens to verify that your assembled context fits within a model's context window:

import{estimateTokens}from'codebase-ctx';constcontext='... assembled context string ...';consttokens=estimateTokens(context);constMODEL_LIMIT=128_000;constTASK_BUDGET=MODEL_LIMIT-tokens;console.log(`Context: ${tokens} tokens, leaving ${TASK_BUDGET} for the task`);

Implementing the Analyzer Interface

The Analyzer interface provides a contract for custom analyzer implementations:

importtype{Analyzer,CodebaseContext,OutputFormat,FormatOptions}from'codebase-ctx';constmyAnalyzer: Analyzer={asyncanalyze(projectPath?: string): Promise<CodebaseContext>{// Run analysis and return a CodebaseContext},asyncanalyzeAndFormat(projectPath?: string,outputFormat?: OutputFormat,formatOptions?: FormatOptions,): Promise<string>{// Analyze and return formatted string},};

TypeScript

This package is written in TypeScript and ships type declarations (dist/index.d.ts). All public interfaces and type aliases are exported from the package root:

importtype{// Core result typesCodebaseContext,ProjectInfo,DependencyInfo,DependencyEntry,ScriptInfo,ScriptEntry,// Additional analyzer result typesStructureInfo,DirectoryEntry,TypeScriptInfo,APISurface,APIEntry,ConfigInfo,ConfigEntry,GitInfo,StatsInfo,FileStat,LanguageStat,PatternInfo,DetectedPattern,AnalysisMeta,// Option and configuration typesAnalyzeOptions,FormatOptions,AnalyzerConfig,Analyzer,// String union typesDetailLevel,OutputFormat,AnalyzerName,DependencyCategory,}from'codebase-ctx';

Compiled with target: ES2022, module: commonjs, strict: true.


License

MIT

About

Generate AI-optimized codebase summaries via static analysis

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - SiluPanda/codebase-ctx: Generate AI-optimized codebase summaries via static analysis · GitHub
Skip to content

Repository files navigation

codebase-ctx

Generate AI-optimized codebase summaries via static analysis.

npm versionnpm downloadslicensenodeTypeScript

codebase-ctx is a zero-dependency static analysis tool that reads a project directory and produces structured, token-efficient context about its setup, dependencies, scripts, language, runtime, and architecture. The output is designed to be injected into AI instruction files (CLAUDE.md, .cursorrules), passed as prompt context, or consumed programmatically by other tools.

Unlike source code dumpers that concatenate entire repositories (producing hundreds of thousands of tokens), codebase-ctx runs a pipeline of modular analyzers that each extract a specific dimension of project context and compress the results into a structured summary. A typical output is 300--800 tokens: enough to convey what a developer learns in their first hour with a codebase, compact enough to leave the vast majority of the context window for the actual task.

All analysis is deterministic, offline, and fast (under 500ms for most projects). No LLM calls, no network requests, no API keys required.


Installation

npm install codebase-ctx

Or install globally for CLI usage:

npm install -g codebase-ctx

Requires Node.js >= 18.


Quick Start

import{analyzeProject,analyzeDependencies,analyzeScripts}from'codebase-ctx';// Analyze project metadataconstproject=analyzeProject('/path/to/project');console.log(project);// {// name: 'my-app',// version: '1.0.0',// description: 'My application',// license: 'MIT',// language: 'TypeScript',// runtime: 'Node.js',// repository: 'https://github.com/user/my-app',// nodeVersion: '>=18',// }// Analyze dependencies with automatic categorizationconstdeps=analyzeDependencies('/path/to/project');console.log(deps.summary);// {// totalProduction: 5,// totalDev: 3,// frameworks: ['react', 'next'],// databases: ['@prisma/client'],// testingTools: ['vitest'],// }// Analyze npm scriptsconstscripts=analyzeScripts('/path/to/project');console.log(scripts.hasBuild,scripts.hasTest);// true true

Features

  • Zero dependencies -- all analysis uses Node.js built-ins (node:fs, node:path). No AST parsers, no tree-sitter, no external tools.
  • Modular analyzers -- each analyzer extracts one dimension of context (project metadata, dependencies, scripts) and runs independently. Analyzers fail gracefully when their input files are missing.
  • Dependency categorization -- a built-in registry of 190+ common npm packages automatically classifies dependencies into categories: framework, database, testing, build, lint, ui, auth, api, observability, and type-definitions.
  • Language detection -- infers TypeScript or JavaScript from tsconfig.json, file extensions in src/, or the presence of typescript in dependencies.
  • Runtime detection -- infers Node.js, Bun, Deno, or Browser from engines fields and framework dependencies.
  • Script categorization -- classifies npm scripts into build, test, lint, start, deploy, and other categories by name pattern matching.
  • Token estimation -- estimates LLM token counts for any text using ceil(chars / 4).
  • Deterministic output -- same codebase always produces the same analysis result.
  • TypeScript-first -- full type definitions shipped with the package.

API Reference

Analyzers

analyzeProject(projectPath: string): ProjectInfo

Reads package.json and inspects the project directory to produce a ProjectInfo summary.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:ProjectInfo

FieldTypeDescription
namestring | nullPackage name from package.json, or directory name as fallback
versionstring | nullPackage version
descriptionstring | nullPackage description
licensestring | nullLicense identifier (e.g. "MIT")
languagestringDetected language: "TypeScript", "JavaScript", or "unknown"
runtimestringDetected runtime: "Node.js", "Bun", "Deno", "Browser", or "unknown"
repositorystring | nullRepository URL (cleaned of git+ prefix and .git suffix)
nodeVersionstring | nullNode.js version constraint from engines.node

Language detection checks in order: tsconfig.json existence, .ts/.tsx files in src/, typescript in dependencies. Falls back to "JavaScript".

Runtime detection checks in order: engines.bun, engines.deno, browser-only framework dependencies (React/Vue/Angular/Svelte without an SSR framework like Next/Nuxt), then defaults to "Node.js".

Fallback behavior: When no package.json exists, returns the directory name as name, "unknown" as language and runtime, and null for all other fields.

Example:

import{analyzeProject}from'codebase-ctx';constinfo=analyzeProject('/home/user/my-express-app');// info.language === 'TypeScript'// info.runtime === 'Node.js'// info.repository === 'https://github.com/user/my-express-app'

analyzeDependencies(projectPath: string): DependencyInfo

Reads package.json and extracts all dependency sections, categorizing each dependency by its purpose using a built-in registry.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:DependencyInfo

FieldTypeDescription
productionDependencyEntry[]Dependencies from dependencies
devDependencyEntry[]Dependencies from devDependencies
peerDependencyEntry[]Dependencies from peerDependencies
optionalDependencyEntry[]Dependencies from optionalDependencies
summaryobjectAggregated summary (see below)

summary fields:

FieldTypeDescription
totalProductionnumberCount of production dependencies
totalDevnumberCount of dev dependencies
frameworksstring[]Names of all framework dependencies across all sections
databasesstring[]Names of all database dependencies across all sections
testingToolsstring[]Names of all testing dependencies across all sections

Each DependencyEntry has the shape:

{
name: string;// Package name (e.g. "react")
version: string;// Version range (e.g. "^18.2.0")
category: DependencyCategory;}

Category inference: Known packages are mapped via a built-in registry of 190+ entries. Unrecognized packages in dependencies default to "utility"; unrecognized packages in devDependencies default to "build". Packages matching @types/* are always categorized as "type-definitions".

Fallback behavior: When package.json does not exist, returns empty arrays and zero counts for all fields.

Example:

import{analyzeDependencies}from'codebase-ctx';constdeps=analyzeDependencies('/home/user/my-project');// Inspect categorized production depsfor(constdepofdeps.production){console.log(`${dep.name} (${dep.category}): ${dep.version}`);}// react (framework): ^18.2.0// @prisma/client (database): ^5.0.0// winston (observability): ^3.0.0// Use the summary for quick contextconsole.log(deps.summary.frameworks);// ['react']console.log(deps.summary.databases);// ['@prisma/client']

analyzeScripts(projectPath: string): ScriptInfo

Reads the scripts field from package.json and categorizes each script by name.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:ScriptInfo

FieldTypeDescription
scriptsScriptEntry[]All scripts with their names, commands, and categories
hasBuildbooleantrue if any script is categorized as "build"
hasTestbooleantrue if any script is categorized as "test"
hasLintbooleantrue if any script is categorized as "lint"
hasStartbooleantrue if any script is categorized as "start"

Each ScriptEntry has the shape:

{
name: string;// Script name (e.g. "build")
command: string;// Script command (e.g. "tsc")
category: 'build'|'test'|'lint'|'start'|'deploy'|'other';}

Category mapping:

CategoryMatched script names
buildbuild, compile, bundle, tsc, build:*
testtest, spec, e2e, coverage, test:*
lintlint, check, format, prettier, lint:*
startstart, dev, serve, develop
deploydeploy, release, publish
otherEverything else

Scripts prefixed with pre or post (e.g. pretest, postbuild, prepublishOnly) inherit the category of their base script.

Fallback behavior: When package.json has no scripts field or does not exist, returns an empty array and all boolean flags as false.

Example:

import{analyzeScripts}from'codebase-ctx';constscripts=analyzeScripts('/home/user/my-project');if(scripts.hasTest){consttestScripts=scripts.scripts.filter(s=>s.category==='test');for(constsoftestScripts){console.log(`${s.name}: ${s.command}`);}}// test: vitest run// test:unit: vitest run src/

Dependency Registry

DEPENDENCY_REGISTRY

A Record<string, DependencyCategory> mapping 190+ common npm package names to their categories. This is the lookup table used by analyzeDependencies.

import{DEPENDENCY_REGISTRY}from'codebase-ctx';console.log(DEPENDENCY_REGISTRY['react']);// 'framework'console.log(DEPENDENCY_REGISTRY['prisma']);// 'database'console.log(DEPENDENCY_REGISTRY['vitest']);// 'testing'console.log(DEPENDENCY_REGISTRY['typescript']);// 'build'console.log(DEPENDENCY_REGISTRY['eslint']);// 'lint'console.log(DEPENDENCY_REGISTRY['tailwindcss']);// 'ui'console.log(DEPENDENCY_REGISTRY['passport']);// 'auth'console.log(DEPENDENCY_REGISTRY['axios']);// 'api'console.log(DEPENDENCY_REGISTRY['winston']);// 'observability'

Categories covered:framework, database, testing, build, lint, ui, auth, api, observability.


categorize(name: string, isDevDep: boolean): DependencyCategory

Categorizes a single package name. Checks the built-in registry first, then @types/* prefix, then falls back to "utility" for production dependencies or "build" for dev dependencies.

Parameters:

ParameterTypeDescription
namestringThe npm package name
isDevDepbooleanWhether the package is a devDependency

Returns:DependencyCategory -- one of "framework", "database", "testing", "build", "lint", "utility", "type-definitions", "ui", "auth", "api", "observability".

Example:

import{categorize}from'codebase-ctx';categorize('react',false);// 'framework'categorize('@types/node',true);// 'type-definitions'categorize('some-unknown-pkg',false);// 'utility'categorize('some-unknown-pkg',true);// 'build'

File Utilities

fileExists(filePath: string): boolean

Synchronously checks whether a file exists at the given path.

import{fileExists}from'codebase-ctx';if(fileExists('/path/to/tsconfig.json')){// TypeScript project}

readFileContent(filePath: string): string

Reads a file synchronously and returns its contents as a UTF-8 string. Throws if the file does not exist.

import{readFileContent}from'codebase-ctx';constcontent=readFileContent('/path/to/package.json');

readLines(filePath: string): string[]

Reads a file and splits it into an array of lines. Throws if the file does not exist.

import{readLines}from'codebase-ctx';constlines=readLines('/path/to/src/index.ts');console.log(`${lines.length} lines`);

readJsonFile<T>(filePath: string): T | null

Reads and parses a JSON file. Returns null if the file does not exist or contains invalid JSON. Never throws.

Type parameter:T -- the expected shape of the parsed JSON object.

import{readJsonFile}from'codebase-ctx';interfacePkgJson{name: string;version: string;}constpkg=readJsonFile<PkgJson>('/path/to/package.json');if(pkg){console.log(pkg.name,pkg.version);}

estimateTokens(text: string): number

Estimates the LLM token count for a string using the approximation Math.ceil(text.length / 4).

Returns 0 for empty strings.

import{estimateTokens}from'codebase-ctx';consttokens=estimateTokens('Hello, world!');// 4 (ceil(13 / 4))

Configuration

AnalyzeOptions

Options accepted by the analysis pipeline:

interfaceAnalyzeOptions{analyzers?: AnalyzerName[];// Which analyzers to run (default: all)exclude?: string[];// Directory/file patterns to excludedetailLevel?: DetailLevel;// 'minimal' | 'standard' | 'detailed'maxSampleFiles?: number;// Max files for pattern samplingmaxDepth?: number;// Max directory traversal depth}

FormatOptions

Options for formatting output:

interfaceFormatOptions{detailLevel?: DetailLevel;// 'minimal' | 'standard' | 'detailed'formatter?: (context: CodebaseContext)=>string;// Custom formatter functionincludeTokenCount?: boolean;// Append token count to output}

AnalyzerName

Valid analyzer names:

"project" | "dependencies" | "structure" | "typescript" | "api" | "scripts" | "config" | "git" | "stats" | "patterns"

DependencyCategory

Valid dependency categories:

"framework" | "database" | "testing" | "build" | "lint" | "utility" | "type-definitions" | "ui" | "auth" | "api" | "observability"

DetailLevel

Controls information density in formatted output:

LevelTarget tokensDescription
minimal150--300Language, framework, architecture, entry point, build/test commands only
standard400--800All major context dimensions with summaries
detailed800--2,000Full dependency lists, complete API surface, all patterns with evidence

OutputFormat

Supported output formats:

FormatDescription
markdownStructured markdown for CLAUDE.md / .cursorrules injection
jsonMachine-readable JSON for programmatic consumption
compactMinimal token count, maximum information density
customUser-provided formatter function via FormatOptions.formatter

Error Handling

All analyzers follow a graceful degradation pattern:

  • Missing package.json: Analyzers that depend on package.json return sensible defaults -- empty arrays, zero counts, null fields, or the directory name as a fallback project name.
  • Invalid JSON: readJsonFile returns null when a file contains malformed JSON. Analyzers that use it handle the null case explicitly.
  • Missing files: fileExists returns false; analyzers skip analysis for files that do not exist rather than throwing.
  • No matching data: Analyzers return empty result objects (empty arrays, false flags) rather than throwing when the data they look for is absent.

The general contract: individual analyzers never throw. If an analyzer cannot extract data, it returns a typed fallback value. Only infrastructure-level errors (directory does not exist, permission denied) produce exceptions.


Advanced Usage

Combining Analyzers

Run multiple analyzers against the same project and assemble the results:

import{analyzeProject,analyzeDependencies,analyzeScripts}from'codebase-ctx';constprojectPath='/home/user/my-app';constproject=analyzeProject(projectPath);constdeps=analyzeDependencies(projectPath);constscripts=analyzeScripts(projectPath);// Build a context summary for prompt injectionconstsummary=[`Project: ${project.name} v${project.version}`,`Language: ${project.language}, Runtime: ${project.runtime}`,`Frameworks: ${deps.summary.frameworks.join(', ')||'none'}`,`Databases: ${deps.summary.databases.join(', ')||'none'}`,`Testing: ${deps.summary.testingTools.join(', ')||'none'}`,`Build: ${scripts.hasBuild ? 'yes' : 'no'}, Test: ${scripts.hasTest ? 'yes' : 'no'}`,].join('\n');console.log(summary);

Extending the Dependency Registry

You can use categorize alongside your own logic to handle packages not in the built-in registry:

import{categorize,DEPENDENCY_REGISTRY}from'codebase-ctx';importtype{DependencyCategory}from'codebase-ctx';constCUSTOM_REGISTRY: Record<string,DependencyCategory>={'my-internal-framework': 'framework','@company/auth-sdk': 'auth',};functioncustomCategorize(name: string,isDev: boolean): DependencyCategory{if(CUSTOM_REGISTRY[name])returnCUSTOM_REGISTRY[name];returncategorize(name,isDev);}

Estimating Prompt Budget

Use estimateTokens to verify that your assembled context fits within a model's context window:

import{estimateTokens}from'codebase-ctx';constcontext='... assembled context string ...';consttokens=estimateTokens(context);constMODEL_LIMIT=128_000;constTASK_BUDGET=MODEL_LIMIT-tokens;console.log(`Context: ${tokens} tokens, leaving ${TASK_BUDGET} for the task`);

Implementing the Analyzer Interface

The Analyzer interface provides a contract for custom analyzer implementations:

importtype{Analyzer,CodebaseContext,OutputFormat,FormatOptions}from'codebase-ctx';constmyAnalyzer: Analyzer={asyncanalyze(projectPath?: string): Promise<CodebaseContext>{// Run analysis and return a CodebaseContext},asyncanalyzeAndFormat(projectPath?: string,outputFormat?: OutputFormat,formatOptions?: FormatOptions,): Promise<string>{// Analyze and return formatted string},};

TypeScript

This package is written in TypeScript and ships type declarations (dist/index.d.ts). All public interfaces and type aliases are exported from the package root:

importtype{// Core result typesCodebaseContext,ProjectInfo,DependencyInfo,DependencyEntry,ScriptInfo,ScriptEntry,// Additional analyzer result typesStructureInfo,DirectoryEntry,TypeScriptInfo,APISurface,APIEntry,ConfigInfo,ConfigEntry,GitInfo,StatsInfo,FileStat,LanguageStat,PatternInfo,DetectedPattern,AnalysisMeta,// Option and configuration typesAnalyzeOptions,FormatOptions,AnalyzerConfig,Analyzer,// String union typesDetailLevel,OutputFormat,AnalyzerName,DependencyCategory,}from'codebase-ctx';

Compiled with target: ES2022, module: commonjs, strict: true.


License

MIT

About

Generate AI-optimized codebase summaries via static analysis

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - SiluPanda/codebase-ctx: Generate AI-optimized codebase summaries via static analysis · GitHub
Skip to content

Repository files navigation

codebase-ctx

Generate AI-optimized codebase summaries via static analysis.

npm versionnpm downloadslicensenodeTypeScript

codebase-ctx is a zero-dependency static analysis tool that reads a project directory and produces structured, token-efficient context about its setup, dependencies, scripts, language, runtime, and architecture. The output is designed to be injected into AI instruction files (CLAUDE.md, .cursorrules), passed as prompt context, or consumed programmatically by other tools.

Unlike source code dumpers that concatenate entire repositories (producing hundreds of thousands of tokens), codebase-ctx runs a pipeline of modular analyzers that each extract a specific dimension of project context and compress the results into a structured summary. A typical output is 300--800 tokens: enough to convey what a developer learns in their first hour with a codebase, compact enough to leave the vast majority of the context window for the actual task.

All analysis is deterministic, offline, and fast (under 500ms for most projects). No LLM calls, no network requests, no API keys required.


Installation

npm install codebase-ctx

Or install globally for CLI usage:

npm install -g codebase-ctx

Requires Node.js >= 18.


Quick Start

import{analyzeProject,analyzeDependencies,analyzeScripts}from'codebase-ctx';// Analyze project metadataconstproject=analyzeProject('/path/to/project');console.log(project);// {// name: 'my-app',// version: '1.0.0',// description: 'My application',// license: 'MIT',// language: 'TypeScript',// runtime: 'Node.js',// repository: 'https://github.com/user/my-app',// nodeVersion: '>=18',// }// Analyze dependencies with automatic categorizationconstdeps=analyzeDependencies('/path/to/project');console.log(deps.summary);// {// totalProduction: 5,// totalDev: 3,// frameworks: ['react', 'next'],// databases: ['@prisma/client'],// testingTools: ['vitest'],// }// Analyze npm scriptsconstscripts=analyzeScripts('/path/to/project');console.log(scripts.hasBuild,scripts.hasTest);// true true

Features

  • Zero dependencies -- all analysis uses Node.js built-ins (node:fs, node:path). No AST parsers, no tree-sitter, no external tools.
  • Modular analyzers -- each analyzer extracts one dimension of context (project metadata, dependencies, scripts) and runs independently. Analyzers fail gracefully when their input files are missing.
  • Dependency categorization -- a built-in registry of 190+ common npm packages automatically classifies dependencies into categories: framework, database, testing, build, lint, ui, auth, api, observability, and type-definitions.
  • Language detection -- infers TypeScript or JavaScript from tsconfig.json, file extensions in src/, or the presence of typescript in dependencies.
  • Runtime detection -- infers Node.js, Bun, Deno, or Browser from engines fields and framework dependencies.
  • Script categorization -- classifies npm scripts into build, test, lint, start, deploy, and other categories by name pattern matching.
  • Token estimation -- estimates LLM token counts for any text using ceil(chars / 4).
  • Deterministic output -- same codebase always produces the same analysis result.
  • TypeScript-first -- full type definitions shipped with the package.

API Reference

Analyzers

analyzeProject(projectPath: string): ProjectInfo

Reads package.json and inspects the project directory to produce a ProjectInfo summary.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:ProjectInfo

FieldTypeDescription
namestring | nullPackage name from package.json, or directory name as fallback
versionstring | nullPackage version
descriptionstring | nullPackage description
licensestring | nullLicense identifier (e.g. "MIT")
languagestringDetected language: "TypeScript", "JavaScript", or "unknown"
runtimestringDetected runtime: "Node.js", "Bun", "Deno", "Browser", or "unknown"
repositorystring | nullRepository URL (cleaned of git+ prefix and .git suffix)
nodeVersionstring | nullNode.js version constraint from engines.node

Language detection checks in order: tsconfig.json existence, .ts/.tsx files in src/, typescript in dependencies. Falls back to "JavaScript".

Runtime detection checks in order: engines.bun, engines.deno, browser-only framework dependencies (React/Vue/Angular/Svelte without an SSR framework like Next/Nuxt), then defaults to "Node.js".

Fallback behavior: When no package.json exists, returns the directory name as name, "unknown" as language and runtime, and null for all other fields.

Example:

import{analyzeProject}from'codebase-ctx';constinfo=analyzeProject('/home/user/my-express-app');// info.language === 'TypeScript'// info.runtime === 'Node.js'// info.repository === 'https://github.com/user/my-express-app'

analyzeDependencies(projectPath: string): DependencyInfo

Reads package.json and extracts all dependency sections, categorizing each dependency by its purpose using a built-in registry.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:DependencyInfo

FieldTypeDescription
productionDependencyEntry[]Dependencies from dependencies
devDependencyEntry[]Dependencies from devDependencies
peerDependencyEntry[]Dependencies from peerDependencies
optionalDependencyEntry[]Dependencies from optionalDependencies
summaryobjectAggregated summary (see below)

summary fields:

FieldTypeDescription
totalProductionnumberCount of production dependencies
totalDevnumberCount of dev dependencies
frameworksstring[]Names of all framework dependencies across all sections
databasesstring[]Names of all database dependencies across all sections
testingToolsstring[]Names of all testing dependencies across all sections

Each DependencyEntry has the shape:

{
name: string;// Package name (e.g. "react")
version: string;// Version range (e.g. "^18.2.0")
category: DependencyCategory;}

Category inference: Known packages are mapped via a built-in registry of 190+ entries. Unrecognized packages in dependencies default to "utility"; unrecognized packages in devDependencies default to "build". Packages matching @types/* are always categorized as "type-definitions".

Fallback behavior: When package.json does not exist, returns empty arrays and zero counts for all fields.

Example:

import{analyzeDependencies}from'codebase-ctx';constdeps=analyzeDependencies('/home/user/my-project');// Inspect categorized production depsfor(constdepofdeps.production){console.log(`${dep.name} (${dep.category}): ${dep.version}`);}// react (framework): ^18.2.0// @prisma/client (database): ^5.0.0// winston (observability): ^3.0.0// Use the summary for quick contextconsole.log(deps.summary.frameworks);// ['react']console.log(deps.summary.databases);// ['@prisma/client']

analyzeScripts(projectPath: string): ScriptInfo

Reads the scripts field from package.json and categorizes each script by name.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:ScriptInfo

FieldTypeDescription
scriptsScriptEntry[]All scripts with their names, commands, and categories
hasBuildbooleantrue if any script is categorized as "build"
hasTestbooleantrue if any script is categorized as "test"
hasLintbooleantrue if any script is categorized as "lint"
hasStartbooleantrue if any script is categorized as "start"

Each ScriptEntry has the shape:

{
name: string;// Script name (e.g. "build")
command: string;// Script command (e.g. "tsc")
category: 'build'|'test'|'lint'|'start'|'deploy'|'other';}

Category mapping:

CategoryMatched script names
buildbuild, compile, bundle, tsc, build:*
testtest, spec, e2e, coverage, test:*
lintlint, check, format, prettier, lint:*
startstart, dev, serve, develop
deploydeploy, release, publish
otherEverything else

Scripts prefixed with pre or post (e.g. pretest, postbuild, prepublishOnly) inherit the category of their base script.

Fallback behavior: When package.json has no scripts field or does not exist, returns an empty array and all boolean flags as false.

Example:

import{analyzeScripts}from'codebase-ctx';constscripts=analyzeScripts('/home/user/my-project');if(scripts.hasTest){consttestScripts=scripts.scripts.filter(s=>s.category==='test');for(constsoftestScripts){console.log(`${s.name}: ${s.command}`);}}// test: vitest run// test:unit: vitest run src/

Dependency Registry

DEPENDENCY_REGISTRY

A Record<string, DependencyCategory> mapping 190+ common npm package names to their categories. This is the lookup table used by analyzeDependencies.

import{DEPENDENCY_REGISTRY}from'codebase-ctx';console.log(DEPENDENCY_REGISTRY['react']);// 'framework'console.log(DEPENDENCY_REGISTRY['prisma']);// 'database'console.log(DEPENDENCY_REGISTRY['vitest']);// 'testing'console.log(DEPENDENCY_REGISTRY['typescript']);// 'build'console.log(DEPENDENCY_REGISTRY['eslint']);// 'lint'console.log(DEPENDENCY_REGISTRY['tailwindcss']);// 'ui'console.log(DEPENDENCY_REGISTRY['passport']);// 'auth'console.log(DEPENDENCY_REGISTRY['axios']);// 'api'console.log(DEPENDENCY_REGISTRY['winston']);// 'observability'

Categories covered:framework, database, testing, build, lint, ui, auth, api, observability.


categorize(name: string, isDevDep: boolean): DependencyCategory

Categorizes a single package name. Checks the built-in registry first, then @types/* prefix, then falls back to "utility" for production dependencies or "build" for dev dependencies.

Parameters:

ParameterTypeDescription
namestringThe npm package name
isDevDepbooleanWhether the package is a devDependency

Returns:DependencyCategory -- one of "framework", "database", "testing", "build", "lint", "utility", "type-definitions", "ui", "auth", "api", "observability".

Example:

import{categorize}from'codebase-ctx';categorize('react',false);// 'framework'categorize('@types/node',true);// 'type-definitions'categorize('some-unknown-pkg',false);// 'utility'categorize('some-unknown-pkg',true);// 'build'

File Utilities

fileExists(filePath: string): boolean

Synchronously checks whether a file exists at the given path.

import{fileExists}from'codebase-ctx';if(fileExists('/path/to/tsconfig.json')){// TypeScript project}

readFileContent(filePath: string): string

Reads a file synchronously and returns its contents as a UTF-8 string. Throws if the file does not exist.

import{readFileContent}from'codebase-ctx';constcontent=readFileContent('/path/to/package.json');

readLines(filePath: string): string[]

Reads a file and splits it into an array of lines. Throws if the file does not exist.

import{readLines}from'codebase-ctx';constlines=readLines('/path/to/src/index.ts');console.log(`${lines.length} lines`);

readJsonFile<T>(filePath: string): T | null

Reads and parses a JSON file. Returns null if the file does not exist or contains invalid JSON. Never throws.

Type parameter:T -- the expected shape of the parsed JSON object.

import{readJsonFile}from'codebase-ctx';interfacePkgJson{name: string;version: string;}constpkg=readJsonFile<PkgJson>('/path/to/package.json');if(pkg){console.log(pkg.name,pkg.version);}

estimateTokens(text: string): number

Estimates the LLM token count for a string using the approximation Math.ceil(text.length / 4).

Returns 0 for empty strings.

import{estimateTokens}from'codebase-ctx';consttokens=estimateTokens('Hello, world!');// 4 (ceil(13 / 4))

Configuration

AnalyzeOptions

Options accepted by the analysis pipeline:

interfaceAnalyzeOptions{analyzers?: AnalyzerName[];// Which analyzers to run (default: all)exclude?: string[];// Directory/file patterns to excludedetailLevel?: DetailLevel;// 'minimal' | 'standard' | 'detailed'maxSampleFiles?: number;// Max files for pattern samplingmaxDepth?: number;// Max directory traversal depth}

FormatOptions

Options for formatting output:

interfaceFormatOptions{detailLevel?: DetailLevel;// 'minimal' | 'standard' | 'detailed'formatter?: (context: CodebaseContext)=>string;// Custom formatter functionincludeTokenCount?: boolean;// Append token count to output}

AnalyzerName

Valid analyzer names:

"project" | "dependencies" | "structure" | "typescript" | "api" | "scripts" | "config" | "git" | "stats" | "patterns"

DependencyCategory

Valid dependency categories:

"framework" | "database" | "testing" | "build" | "lint" | "utility" | "type-definitions" | "ui" | "auth" | "api" | "observability"

DetailLevel

Controls information density in formatted output:

LevelTarget tokensDescription
minimal150--300Language, framework, architecture, entry point, build/test commands only
standard400--800All major context dimensions with summaries
detailed800--2,000Full dependency lists, complete API surface, all patterns with evidence

OutputFormat

Supported output formats:

FormatDescription
markdownStructured markdown for CLAUDE.md / .cursorrules injection
jsonMachine-readable JSON for programmatic consumption
compactMinimal token count, maximum information density
customUser-provided formatter function via FormatOptions.formatter

Error Handling

All analyzers follow a graceful degradation pattern:

  • Missing package.json: Analyzers that depend on package.json return sensible defaults -- empty arrays, zero counts, null fields, or the directory name as a fallback project name.
  • Invalid JSON: readJsonFile returns null when a file contains malformed JSON. Analyzers that use it handle the null case explicitly.
  • Missing files: fileExists returns false; analyzers skip analysis for files that do not exist rather than throwing.
  • No matching data: Analyzers return empty result objects (empty arrays, false flags) rather than throwing when the data they look for is absent.

The general contract: individual analyzers never throw. If an analyzer cannot extract data, it returns a typed fallback value. Only infrastructure-level errors (directory does not exist, permission denied) produce exceptions.


Advanced Usage

Combining Analyzers

Run multiple analyzers against the same project and assemble the results:

import{analyzeProject,analyzeDependencies,analyzeScripts}from'codebase-ctx';constprojectPath='/home/user/my-app';constproject=analyzeProject(projectPath);constdeps=analyzeDependencies(projectPath);constscripts=analyzeScripts(projectPath);// Build a context summary for prompt injectionconstsummary=[`Project: ${project.name} v${project.version}`,`Language: ${project.language}, Runtime: ${project.runtime}`,`Frameworks: ${deps.summary.frameworks.join(', ')||'none'}`,`Databases: ${deps.summary.databases.join(', ')||'none'}`,`Testing: ${deps.summary.testingTools.join(', ')||'none'}`,`Build: ${scripts.hasBuild ? 'yes' : 'no'}, Test: ${scripts.hasTest ? 'yes' : 'no'}`,].join('\n');console.log(summary);

Extending the Dependency Registry

You can use categorize alongside your own logic to handle packages not in the built-in registry:

import{categorize,DEPENDENCY_REGISTRY}from'codebase-ctx';importtype{DependencyCategory}from'codebase-ctx';constCUSTOM_REGISTRY: Record<string,DependencyCategory>={'my-internal-framework': 'framework','@company/auth-sdk': 'auth',};functioncustomCategorize(name: string,isDev: boolean): DependencyCategory{if(CUSTOM_REGISTRY[name])returnCUSTOM_REGISTRY[name];returncategorize(name,isDev);}

Estimating Prompt Budget

Use estimateTokens to verify that your assembled context fits within a model's context window:

import{estimateTokens}from'codebase-ctx';constcontext='... assembled context string ...';consttokens=estimateTokens(context);constMODEL_LIMIT=128_000;constTASK_BUDGET=MODEL_LIMIT-tokens;console.log(`Context: ${tokens} tokens, leaving ${TASK_BUDGET} for the task`);

Implementing the Analyzer Interface

The Analyzer interface provides a contract for custom analyzer implementations:

importtype{Analyzer,CodebaseContext,OutputFormat,FormatOptions}from'codebase-ctx';constmyAnalyzer: Analyzer={asyncanalyze(projectPath?: string): Promise<CodebaseContext>{// Run analysis and return a CodebaseContext},asyncanalyzeAndFormat(projectPath?: string,outputFormat?: OutputFormat,formatOptions?: FormatOptions,): Promise<string>{// Analyze and return formatted string},};

TypeScript

This package is written in TypeScript and ships type declarations (dist/index.d.ts). All public interfaces and type aliases are exported from the package root:

importtype{// Core result typesCodebaseContext,ProjectInfo,DependencyInfo,DependencyEntry,ScriptInfo,ScriptEntry,// Additional analyzer result typesStructureInfo,DirectoryEntry,TypeScriptInfo,APISurface,APIEntry,ConfigInfo,ConfigEntry,GitInfo,StatsInfo,FileStat,LanguageStat,PatternInfo,DetectedPattern,AnalysisMeta,// Option and configuration typesAnalyzeOptions,FormatOptions,AnalyzerConfig,Analyzer,// String union typesDetailLevel,OutputFormat,AnalyzerName,DependencyCategory,}from'codebase-ctx';

Compiled with target: ES2022, module: commonjs, strict: true.


License

MIT

About

Generate AI-optimized codebase summaries via static analysis

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - SiluPanda/codebase-ctx: Generate AI-optimized codebase summaries via static analysis · GitHub
Skip to content

Repository files navigation

codebase-ctx

Generate AI-optimized codebase summaries via static analysis.

npm versionnpm downloadslicensenodeTypeScript

codebase-ctx is a zero-dependency static analysis tool that reads a project directory and produces structured, token-efficient context about its setup, dependencies, scripts, language, runtime, and architecture. The output is designed to be injected into AI instruction files (CLAUDE.md, .cursorrules), passed as prompt context, or consumed programmatically by other tools.

Unlike source code dumpers that concatenate entire repositories (producing hundreds of thousands of tokens), codebase-ctx runs a pipeline of modular analyzers that each extract a specific dimension of project context and compress the results into a structured summary. A typical output is 300--800 tokens: enough to convey what a developer learns in their first hour with a codebase, compact enough to leave the vast majority of the context window for the actual task.

All analysis is deterministic, offline, and fast (under 500ms for most projects). No LLM calls, no network requests, no API keys required.


Installation

npm install codebase-ctx

Or install globally for CLI usage:

npm install -g codebase-ctx

Requires Node.js >= 18.


Quick Start

import{analyzeProject,analyzeDependencies,analyzeScripts}from'codebase-ctx';// Analyze project metadataconstproject=analyzeProject('/path/to/project');console.log(project);// {// name: 'my-app',// version: '1.0.0',// description: 'My application',// license: 'MIT',// language: 'TypeScript',// runtime: 'Node.js',// repository: 'https://github.com/user/my-app',// nodeVersion: '>=18',// }// Analyze dependencies with automatic categorizationconstdeps=analyzeDependencies('/path/to/project');console.log(deps.summary);// {// totalProduction: 5,// totalDev: 3,// frameworks: ['react', 'next'],// databases: ['@prisma/client'],// testingTools: ['vitest'],// }// Analyze npm scriptsconstscripts=analyzeScripts('/path/to/project');console.log(scripts.hasBuild,scripts.hasTest);// true true

Features

  • Zero dependencies -- all analysis uses Node.js built-ins (node:fs, node:path). No AST parsers, no tree-sitter, no external tools.
  • Modular analyzers -- each analyzer extracts one dimension of context (project metadata, dependencies, scripts) and runs independently. Analyzers fail gracefully when their input files are missing.
  • Dependency categorization -- a built-in registry of 190+ common npm packages automatically classifies dependencies into categories: framework, database, testing, build, lint, ui, auth, api, observability, and type-definitions.
  • Language detection -- infers TypeScript or JavaScript from tsconfig.json, file extensions in src/, or the presence of typescript in dependencies.
  • Runtime detection -- infers Node.js, Bun, Deno, or Browser from engines fields and framework dependencies.
  • Script categorization -- classifies npm scripts into build, test, lint, start, deploy, and other categories by name pattern matching.
  • Token estimation -- estimates LLM token counts for any text using ceil(chars / 4).
  • Deterministic output -- same codebase always produces the same analysis result.
  • TypeScript-first -- full type definitions shipped with the package.

API Reference

Analyzers

analyzeProject(projectPath: string): ProjectInfo

Reads package.json and inspects the project directory to produce a ProjectInfo summary.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:ProjectInfo

FieldTypeDescription
namestring | nullPackage name from package.json, or directory name as fallback
versionstring | nullPackage version
descriptionstring | nullPackage description
licensestring | nullLicense identifier (e.g. "MIT")
languagestringDetected language: "TypeScript", "JavaScript", or "unknown"
runtimestringDetected runtime: "Node.js", "Bun", "Deno", "Browser", or "unknown"
repositorystring | nullRepository URL (cleaned of git+ prefix and .git suffix)
nodeVersionstring | nullNode.js version constraint from engines.node

Language detection checks in order: tsconfig.json existence, .ts/.tsx files in src/, typescript in dependencies. Falls back to "JavaScript".

Runtime detection checks in order: engines.bun, engines.deno, browser-only framework dependencies (React/Vue/Angular/Svelte without an SSR framework like Next/Nuxt), then defaults to "Node.js".

Fallback behavior: When no package.json exists, returns the directory name as name, "unknown" as language and runtime, and null for all other fields.

Example:

import{analyzeProject}from'codebase-ctx';constinfo=analyzeProject('/home/user/my-express-app');// info.language === 'TypeScript'// info.runtime === 'Node.js'// info.repository === 'https://github.com/user/my-express-app'

analyzeDependencies(projectPath: string): DependencyInfo

Reads package.json and extracts all dependency sections, categorizing each dependency by its purpose using a built-in registry.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:DependencyInfo

FieldTypeDescription
productionDependencyEntry[]Dependencies from dependencies
devDependencyEntry[]Dependencies from devDependencies
peerDependencyEntry[]Dependencies from peerDependencies
optionalDependencyEntry[]Dependencies from optionalDependencies
summaryobjectAggregated summary (see below)

summary fields:

FieldTypeDescription
totalProductionnumberCount of production dependencies
totalDevnumberCount of dev dependencies
frameworksstring[]Names of all framework dependencies across all sections
databasesstring[]Names of all database dependencies across all sections
testingToolsstring[]Names of all testing dependencies across all sections

Each DependencyEntry has the shape:

{
name: string;// Package name (e.g. "react")
version: string;// Version range (e.g. "^18.2.0")
category: DependencyCategory;}

Category inference: Known packages are mapped via a built-in registry of 190+ entries. Unrecognized packages in dependencies default to "utility"; unrecognized packages in devDependencies default to "build". Packages matching @types/* are always categorized as "type-definitions".

Fallback behavior: When package.json does not exist, returns empty arrays and zero counts for all fields.

Example:

import{analyzeDependencies}from'codebase-ctx';constdeps=analyzeDependencies('/home/user/my-project');// Inspect categorized production depsfor(constdepofdeps.production){console.log(`${dep.name} (${dep.category}): ${dep.version}`);}// react (framework): ^18.2.0// @prisma/client (database): ^5.0.0// winston (observability): ^3.0.0// Use the summary for quick contextconsole.log(deps.summary.frameworks);// ['react']console.log(deps.summary.databases);// ['@prisma/client']

analyzeScripts(projectPath: string): ScriptInfo

Reads the scripts field from package.json and categorizes each script by name.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:ScriptInfo

FieldTypeDescription
scriptsScriptEntry[]All scripts with their names, commands, and categories
hasBuildbooleantrue if any script is categorized as "build"
hasTestbooleantrue if any script is categorized as "test"
hasLintbooleantrue if any script is categorized as "lint"
hasStartbooleantrue if any script is categorized as "start"

Each ScriptEntry has the shape:

{
name: string;// Script name (e.g. "build")
command: string;// Script command (e.g. "tsc")
category: 'build'|'test'|'lint'|'start'|'deploy'|'other';}

Category mapping:

CategoryMatched script names
buildbuild, compile, bundle, tsc, build:*
testtest, spec, e2e, coverage, test:*
lintlint, check, format, prettier, lint:*
startstart, dev, serve, develop
deploydeploy, release, publish
otherEverything else

Scripts prefixed with pre or post (e.g. pretest, postbuild, prepublishOnly) inherit the category of their base script.

Fallback behavior: When package.json has no scripts field or does not exist, returns an empty array and all boolean flags as false.

Example:

import{analyzeScripts}from'codebase-ctx';constscripts=analyzeScripts('/home/user/my-project');if(scripts.hasTest){consttestScripts=scripts.scripts.filter(s=>s.category==='test');for(constsoftestScripts){console.log(`${s.name}: ${s.command}`);}}// test: vitest run// test:unit: vitest run src/

Dependency Registry

DEPENDENCY_REGISTRY

A Record<string, DependencyCategory> mapping 190+ common npm package names to their categories. This is the lookup table used by analyzeDependencies.

import{DEPENDENCY_REGISTRY}from'codebase-ctx';console.log(DEPENDENCY_REGISTRY['react']);// 'framework'console.log(DEPENDENCY_REGISTRY['prisma']);// 'database'console.log(DEPENDENCY_REGISTRY['vitest']);// 'testing'console.log(DEPENDENCY_REGISTRY['typescript']);// 'build'console.log(DEPENDENCY_REGISTRY['eslint']);// 'lint'console.log(DEPENDENCY_REGISTRY['tailwindcss']);// 'ui'console.log(DEPENDENCY_REGISTRY['passport']);// 'auth'console.log(DEPENDENCY_REGISTRY['axios']);// 'api'console.log(DEPENDENCY_REGISTRY['winston']);// 'observability'

Categories covered:framework, database, testing, build, lint, ui, auth, api, observability.


categorize(name: string, isDevDep: boolean): DependencyCategory

Categorizes a single package name. Checks the built-in registry first, then @types/* prefix, then falls back to "utility" for production dependencies or "build" for dev dependencies.

Parameters:

ParameterTypeDescription
namestringThe npm package name
isDevDepbooleanWhether the package is a devDependency

Returns:DependencyCategory -- one of "framework", "database", "testing", "build", "lint", "utility", "type-definitions", "ui", "auth", "api", "observability".

Example:

import{categorize}from'codebase-ctx';categorize('react',false);// 'framework'categorize('@types/node',true);// 'type-definitions'categorize('some-unknown-pkg',false);// 'utility'categorize('some-unknown-pkg',true);// 'build'

File Utilities

fileExists(filePath: string): boolean

Synchronously checks whether a file exists at the given path.

import{fileExists}from'codebase-ctx';if(fileExists('/path/to/tsconfig.json')){// TypeScript project}

readFileContent(filePath: string): string

Reads a file synchronously and returns its contents as a UTF-8 string. Throws if the file does not exist.

import{readFileContent}from'codebase-ctx';constcontent=readFileContent('/path/to/package.json');

readLines(filePath: string): string[]

Reads a file and splits it into an array of lines. Throws if the file does not exist.

import{readLines}from'codebase-ctx';constlines=readLines('/path/to/src/index.ts');console.log(`${lines.length} lines`);

readJsonFile<T>(filePath: string): T | null

Reads and parses a JSON file. Returns null if the file does not exist or contains invalid JSON. Never throws.

Type parameter:T -- the expected shape of the parsed JSON object.

import{readJsonFile}from'codebase-ctx';interfacePkgJson{name: string;version: string;}constpkg=readJsonFile<PkgJson>('/path/to/package.json');if(pkg){console.log(pkg.name,pkg.version);}

estimateTokens(text: string): number

Estimates the LLM token count for a string using the approximation Math.ceil(text.length / 4).

Returns 0 for empty strings.

import{estimateTokens}from'codebase-ctx';consttokens=estimateTokens('Hello, world!');// 4 (ceil(13 / 4))

Configuration

AnalyzeOptions

Options accepted by the analysis pipeline:

interfaceAnalyzeOptions{analyzers?: AnalyzerName[];// Which analyzers to run (default: all)exclude?: string[];// Directory/file patterns to excludedetailLevel?: DetailLevel;// 'minimal' | 'standard' | 'detailed'maxSampleFiles?: number;// Max files for pattern samplingmaxDepth?: number;// Max directory traversal depth}

FormatOptions

Options for formatting output:

interfaceFormatOptions{detailLevel?: DetailLevel;// 'minimal' | 'standard' | 'detailed'formatter?: (context: CodebaseContext)=>string;// Custom formatter functionincludeTokenCount?: boolean;// Append token count to output}

AnalyzerName

Valid analyzer names:

"project" | "dependencies" | "structure" | "typescript" | "api" | "scripts" | "config" | "git" | "stats" | "patterns"

DependencyCategory

Valid dependency categories:

"framework" | "database" | "testing" | "build" | "lint" | "utility" | "type-definitions" | "ui" | "auth" | "api" | "observability"

DetailLevel

Controls information density in formatted output:

LevelTarget tokensDescription
minimal150--300Language, framework, architecture, entry point, build/test commands only
standard400--800All major context dimensions with summaries
detailed800--2,000Full dependency lists, complete API surface, all patterns with evidence

OutputFormat

Supported output formats:

FormatDescription
markdownStructured markdown for CLAUDE.md / .cursorrules injection
jsonMachine-readable JSON for programmatic consumption
compactMinimal token count, maximum information density
customUser-provided formatter function via FormatOptions.formatter

Error Handling

All analyzers follow a graceful degradation pattern:

  • Missing package.json: Analyzers that depend on package.json return sensible defaults -- empty arrays, zero counts, null fields, or the directory name as a fallback project name.
  • Invalid JSON: readJsonFile returns null when a file contains malformed JSON. Analyzers that use it handle the null case explicitly.
  • Missing files: fileExists returns false; analyzers skip analysis for files that do not exist rather than throwing.
  • No matching data: Analyzers return empty result objects (empty arrays, false flags) rather than throwing when the data they look for is absent.

The general contract: individual analyzers never throw. If an analyzer cannot extract data, it returns a typed fallback value. Only infrastructure-level errors (directory does not exist, permission denied) produce exceptions.


Advanced Usage

Combining Analyzers

Run multiple analyzers against the same project and assemble the results:

import{analyzeProject,analyzeDependencies,analyzeScripts}from'codebase-ctx';constprojectPath='/home/user/my-app';constproject=analyzeProject(projectPath);constdeps=analyzeDependencies(projectPath);constscripts=analyzeScripts(projectPath);// Build a context summary for prompt injectionconstsummary=[`Project: ${project.name} v${project.version}`,`Language: ${project.language}, Runtime: ${project.runtime}`,`Frameworks: ${deps.summary.frameworks.join(', ')||'none'}`,`Databases: ${deps.summary.databases.join(', ')||'none'}`,`Testing: ${deps.summary.testingTools.join(', ')||'none'}`,`Build: ${scripts.hasBuild ? 'yes' : 'no'}, Test: ${scripts.hasTest ? 'yes' : 'no'}`,].join('\n');console.log(summary);

Extending the Dependency Registry

You can use categorize alongside your own logic to handle packages not in the built-in registry:

import{categorize,DEPENDENCY_REGISTRY}from'codebase-ctx';importtype{DependencyCategory}from'codebase-ctx';constCUSTOM_REGISTRY: Record<string,DependencyCategory>={'my-internal-framework': 'framework','@company/auth-sdk': 'auth',};functioncustomCategorize(name: string,isDev: boolean): DependencyCategory{if(CUSTOM_REGISTRY[name])returnCUSTOM_REGISTRY[name];returncategorize(name,isDev);}

Estimating Prompt Budget

Use estimateTokens to verify that your assembled context fits within a model's context window:

import{estimateTokens}from'codebase-ctx';constcontext='... assembled context string ...';consttokens=estimateTokens(context);constMODEL_LIMIT=128_000;constTASK_BUDGET=MODEL_LIMIT-tokens;console.log(`Context: ${tokens} tokens, leaving ${TASK_BUDGET} for the task`);

Implementing the Analyzer Interface

The Analyzer interface provides a contract for custom analyzer implementations:

importtype{Analyzer,CodebaseContext,OutputFormat,FormatOptions}from'codebase-ctx';constmyAnalyzer: Analyzer={asyncanalyze(projectPath?: string): Promise<CodebaseContext>{// Run analysis and return a CodebaseContext},asyncanalyzeAndFormat(projectPath?: string,outputFormat?: OutputFormat,formatOptions?: FormatOptions,): Promise<string>{// Analyze and return formatted string},};

TypeScript

This package is written in TypeScript and ships type declarations (dist/index.d.ts). All public interfaces and type aliases are exported from the package root:

importtype{// Core result typesCodebaseContext,ProjectInfo,DependencyInfo,DependencyEntry,ScriptInfo,ScriptEntry,// Additional analyzer result typesStructureInfo,DirectoryEntry,TypeScriptInfo,APISurface,APIEntry,ConfigInfo,ConfigEntry,GitInfo,StatsInfo,FileStat,LanguageStat,PatternInfo,DetectedPattern,AnalysisMeta,// Option and configuration typesAnalyzeOptions,FormatOptions,AnalyzerConfig,Analyzer,// String union typesDetailLevel,OutputFormat,AnalyzerName,DependencyCategory,}from'codebase-ctx';

Compiled with target: ES2022, module: commonjs, strict: true.


License

MIT

About

Generate AI-optimized codebase summaries via static analysis

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - SiluPanda/codebase-ctx: Generate AI-optimized codebase summaries via static analysis · GitHub
Skip to content

Repository files navigation

codebase-ctx

Generate AI-optimized codebase summaries via static analysis.

npm versionnpm downloadslicensenodeTypeScript

codebase-ctx is a zero-dependency static analysis tool that reads a project directory and produces structured, token-efficient context about its setup, dependencies, scripts, language, runtime, and architecture. The output is designed to be injected into AI instruction files (CLAUDE.md, .cursorrules), passed as prompt context, or consumed programmatically by other tools.

Unlike source code dumpers that concatenate entire repositories (producing hundreds of thousands of tokens), codebase-ctx runs a pipeline of modular analyzers that each extract a specific dimension of project context and compress the results into a structured summary. A typical output is 300--800 tokens: enough to convey what a developer learns in their first hour with a codebase, compact enough to leave the vast majority of the context window for the actual task.

All analysis is deterministic, offline, and fast (under 500ms for most projects). No LLM calls, no network requests, no API keys required.


Installation

npm install codebase-ctx

Or install globally for CLI usage:

npm install -g codebase-ctx

Requires Node.js >= 18.


Quick Start

import{analyzeProject,analyzeDependencies,analyzeScripts}from'codebase-ctx';// Analyze project metadataconstproject=analyzeProject('/path/to/project');console.log(project);// {// name: 'my-app',// version: '1.0.0',// description: 'My application',// license: 'MIT',// language: 'TypeScript',// runtime: 'Node.js',// repository: 'https://github.com/user/my-app',// nodeVersion: '>=18',// }// Analyze dependencies with automatic categorizationconstdeps=analyzeDependencies('/path/to/project');console.log(deps.summary);// {// totalProduction: 5,// totalDev: 3,// frameworks: ['react', 'next'],// databases: ['@prisma/client'],// testingTools: ['vitest'],// }// Analyze npm scriptsconstscripts=analyzeScripts('/path/to/project');console.log(scripts.hasBuild,scripts.hasTest);// true true

Features

  • Zero dependencies -- all analysis uses Node.js built-ins (node:fs, node:path). No AST parsers, no tree-sitter, no external tools.
  • Modular analyzers -- each analyzer extracts one dimension of context (project metadata, dependencies, scripts) and runs independently. Analyzers fail gracefully when their input files are missing.
  • Dependency categorization -- a built-in registry of 190+ common npm packages automatically classifies dependencies into categories: framework, database, testing, build, lint, ui, auth, api, observability, and type-definitions.
  • Language detection -- infers TypeScript or JavaScript from tsconfig.json, file extensions in src/, or the presence of typescript in dependencies.
  • Runtime detection -- infers Node.js, Bun, Deno, or Browser from engines fields and framework dependencies.
  • Script categorization -- classifies npm scripts into build, test, lint, start, deploy, and other categories by name pattern matching.
  • Token estimation -- estimates LLM token counts for any text using ceil(chars / 4).
  • Deterministic output -- same codebase always produces the same analysis result.
  • TypeScript-first -- full type definitions shipped with the package.

API Reference

Analyzers

analyzeProject(projectPath: string): ProjectInfo

Reads package.json and inspects the project directory to produce a ProjectInfo summary.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:ProjectInfo

FieldTypeDescription
namestring | nullPackage name from package.json, or directory name as fallback
versionstring | nullPackage version
descriptionstring | nullPackage description
licensestring | nullLicense identifier (e.g. "MIT")
languagestringDetected language: "TypeScript", "JavaScript", or "unknown"
runtimestringDetected runtime: "Node.js", "Bun", "Deno", "Browser", or "unknown"
repositorystring | nullRepository URL (cleaned of git+ prefix and .git suffix)
nodeVersionstring | nullNode.js version constraint from engines.node

Language detection checks in order: tsconfig.json existence, .ts/.tsx files in src/, typescript in dependencies. Falls back to "JavaScript".

Runtime detection checks in order: engines.bun, engines.deno, browser-only framework dependencies (React/Vue/Angular/Svelte without an SSR framework like Next/Nuxt), then defaults to "Node.js".

Fallback behavior: When no package.json exists, returns the directory name as name, "unknown" as language and runtime, and null for all other fields.

Example:

import{analyzeProject}from'codebase-ctx';constinfo=analyzeProject('/home/user/my-express-app');// info.language === 'TypeScript'// info.runtime === 'Node.js'// info.repository === 'https://github.com/user/my-express-app'

analyzeDependencies(projectPath: string): DependencyInfo

Reads package.json and extracts all dependency sections, categorizing each dependency by its purpose using a built-in registry.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:DependencyInfo

FieldTypeDescription
productionDependencyEntry[]Dependencies from dependencies
devDependencyEntry[]Dependencies from devDependencies
peerDependencyEntry[]Dependencies from peerDependencies
optionalDependencyEntry[]Dependencies from optionalDependencies
summaryobjectAggregated summary (see below)

summary fields:

FieldTypeDescription
totalProductionnumberCount of production dependencies
totalDevnumberCount of dev dependencies
frameworksstring[]Names of all framework dependencies across all sections
databasesstring[]Names of all database dependencies across all sections
testingToolsstring[]Names of all testing dependencies across all sections

Each DependencyEntry has the shape:

{
name: string;// Package name (e.g. "react")
version: string;// Version range (e.g. "^18.2.0")
category: DependencyCategory;}

Category inference: Known packages are mapped via a built-in registry of 190+ entries. Unrecognized packages in dependencies default to "utility"; unrecognized packages in devDependencies default to "build". Packages matching @types/* are always categorized as "type-definitions".

Fallback behavior: When package.json does not exist, returns empty arrays and zero counts for all fields.

Example:

import{analyzeDependencies}from'codebase-ctx';constdeps=analyzeDependencies('/home/user/my-project');// Inspect categorized production depsfor(constdepofdeps.production){console.log(`${dep.name} (${dep.category}): ${dep.version}`);}// react (framework): ^18.2.0// @prisma/client (database): ^5.0.0// winston (observability): ^3.0.0// Use the summary for quick contextconsole.log(deps.summary.frameworks);// ['react']console.log(deps.summary.databases);// ['@prisma/client']

analyzeScripts(projectPath: string): ScriptInfo

Reads the scripts field from package.json and categorizes each script by name.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:ScriptInfo

FieldTypeDescription
scriptsScriptEntry[]All scripts with their names, commands, and categories
hasBuildbooleantrue if any script is categorized as "build"
hasTestbooleantrue if any script is categorized as "test"
hasLintbooleantrue if any script is categorized as "lint"
hasStartbooleantrue if any script is categorized as "start"

Each ScriptEntry has the shape:

{
name: string;// Script name (e.g. "build")
command: string;// Script command (e.g. "tsc")
category: 'build'|'test'|'lint'|'start'|'deploy'|'other';}

Category mapping:

CategoryMatched script names
buildbuild, compile, bundle, tsc, build:*
testtest, spec, e2e, coverage, test:*
lintlint, check, format, prettier, lint:*
startstart, dev, serve, develop
deploydeploy, release, publish
otherEverything else

Scripts prefixed with pre or post (e.g. pretest, postbuild, prepublishOnly) inherit the category of their base script.

Fallback behavior: When package.json has no scripts field or does not exist, returns an empty array and all boolean flags as false.

Example:

import{analyzeScripts}from'codebase-ctx';constscripts=analyzeScripts('/home/user/my-project');if(scripts.hasTest){consttestScripts=scripts.scripts.filter(s=>s.category==='test');for(constsoftestScripts){console.log(`${s.name}: ${s.command}`);}}// test: vitest run// test:unit: vitest run src/

Dependency Registry

DEPENDENCY_REGISTRY

A Record<string, DependencyCategory> mapping 190+ common npm package names to their categories. This is the lookup table used by analyzeDependencies.

import{DEPENDENCY_REGISTRY}from'codebase-ctx';console.log(DEPENDENCY_REGISTRY['react']);// 'framework'console.log(DEPENDENCY_REGISTRY['prisma']);// 'database'console.log(DEPENDENCY_REGISTRY['vitest']);// 'testing'console.log(DEPENDENCY_REGISTRY['typescript']);// 'build'console.log(DEPENDENCY_REGISTRY['eslint']);// 'lint'console.log(DEPENDENCY_REGISTRY['tailwindcss']);// 'ui'console.log(DEPENDENCY_REGISTRY['passport']);// 'auth'console.log(DEPENDENCY_REGISTRY['axios']);// 'api'console.log(DEPENDENCY_REGISTRY['winston']);// 'observability'

Categories covered:framework, database, testing, build, lint, ui, auth, api, observability.


categorize(name: string, isDevDep: boolean): DependencyCategory

Categorizes a single package name. Checks the built-in registry first, then @types/* prefix, then falls back to "utility" for production dependencies or "build" for dev dependencies.

Parameters:

ParameterTypeDescription
namestringThe npm package name
isDevDepbooleanWhether the package is a devDependency

Returns:DependencyCategory -- one of "framework", "database", "testing", "build", "lint", "utility", "type-definitions", "ui", "auth", "api", "observability".

Example:

import{categorize}from'codebase-ctx';categorize('react',false);// 'framework'categorize('@types/node',true);// 'type-definitions'categorize('some-unknown-pkg',false);// 'utility'categorize('some-unknown-pkg',true);// 'build'

File Utilities

fileExists(filePath: string): boolean

Synchronously checks whether a file exists at the given path.

import{fileExists}from'codebase-ctx';if(fileExists('/path/to/tsconfig.json')){// TypeScript project}

readFileContent(filePath: string): string

Reads a file synchronously and returns its contents as a UTF-8 string. Throws if the file does not exist.

import{readFileContent}from'codebase-ctx';constcontent=readFileContent('/path/to/package.json');

readLines(filePath: string): string[]

Reads a file and splits it into an array of lines. Throws if the file does not exist.

import{readLines}from'codebase-ctx';constlines=readLines('/path/to/src/index.ts');console.log(`${lines.length} lines`);

readJsonFile<T>(filePath: string): T | null

Reads and parses a JSON file. Returns null if the file does not exist or contains invalid JSON. Never throws.

Type parameter:T -- the expected shape of the parsed JSON object.

import{readJsonFile}from'codebase-ctx';interfacePkgJson{name: string;version: string;}constpkg=readJsonFile<PkgJson>('/path/to/package.json');if(pkg){console.log(pkg.name,pkg.version);}

estimateTokens(text: string): number

Estimates the LLM token count for a string using the approximation Math.ceil(text.length / 4).

Returns 0 for empty strings.

import{estimateTokens}from'codebase-ctx';consttokens=estimateTokens('Hello, world!');// 4 (ceil(13 / 4))

Configuration

AnalyzeOptions

Options accepted by the analysis pipeline:

interfaceAnalyzeOptions{analyzers?: AnalyzerName[];// Which analyzers to run (default: all)exclude?: string[];// Directory/file patterns to excludedetailLevel?: DetailLevel;// 'minimal' | 'standard' | 'detailed'maxSampleFiles?: number;// Max files for pattern samplingmaxDepth?: number;// Max directory traversal depth}

FormatOptions

Options for formatting output:

interfaceFormatOptions{detailLevel?: DetailLevel;// 'minimal' | 'standard' | 'detailed'formatter?: (context: CodebaseContext)=>string;// Custom formatter functionincludeTokenCount?: boolean;// Append token count to output}

AnalyzerName

Valid analyzer names:

"project" | "dependencies" | "structure" | "typescript" | "api" | "scripts" | "config" | "git" | "stats" | "patterns"

DependencyCategory

Valid dependency categories:

"framework" | "database" | "testing" | "build" | "lint" | "utility" | "type-definitions" | "ui" | "auth" | "api" | "observability"

DetailLevel

Controls information density in formatted output:

LevelTarget tokensDescription
minimal150--300Language, framework, architecture, entry point, build/test commands only
standard400--800All major context dimensions with summaries
detailed800--2,000Full dependency lists, complete API surface, all patterns with evidence

OutputFormat

Supported output formats:

FormatDescription
markdownStructured markdown for CLAUDE.md / .cursorrules injection
jsonMachine-readable JSON for programmatic consumption
compactMinimal token count, maximum information density
customUser-provided formatter function via FormatOptions.formatter

Error Handling

All analyzers follow a graceful degradation pattern:

  • Missing package.json: Analyzers that depend on package.json return sensible defaults -- empty arrays, zero counts, null fields, or the directory name as a fallback project name.
  • Invalid JSON: readJsonFile returns null when a file contains malformed JSON. Analyzers that use it handle the null case explicitly.
  • Missing files: fileExists returns false; analyzers skip analysis for files that do not exist rather than throwing.
  • No matching data: Analyzers return empty result objects (empty arrays, false flags) rather than throwing when the data they look for is absent.

The general contract: individual analyzers never throw. If an analyzer cannot extract data, it returns a typed fallback value. Only infrastructure-level errors (directory does not exist, permission denied) produce exceptions.


Advanced Usage

Combining Analyzers

Run multiple analyzers against the same project and assemble the results:

import{analyzeProject,analyzeDependencies,analyzeScripts}from'codebase-ctx';constprojectPath='/home/user/my-app';constproject=analyzeProject(projectPath);constdeps=analyzeDependencies(projectPath);constscripts=analyzeScripts(projectPath);// Build a context summary for prompt injectionconstsummary=[`Project: ${project.name} v${project.version}`,`Language: ${project.language}, Runtime: ${project.runtime}`,`Frameworks: ${deps.summary.frameworks.join(', ')||'none'}`,`Databases: ${deps.summary.databases.join(', ')||'none'}`,`Testing: ${deps.summary.testingTools.join(', ')||'none'}`,`Build: ${scripts.hasBuild ? 'yes' : 'no'}, Test: ${scripts.hasTest ? 'yes' : 'no'}`,].join('\n');console.log(summary);

Extending the Dependency Registry

You can use categorize alongside your own logic to handle packages not in the built-in registry:

import{categorize,DEPENDENCY_REGISTRY}from'codebase-ctx';importtype{DependencyCategory}from'codebase-ctx';constCUSTOM_REGISTRY: Record<string,DependencyCategory>={'my-internal-framework': 'framework','@company/auth-sdk': 'auth',};functioncustomCategorize(name: string,isDev: boolean): DependencyCategory{if(CUSTOM_REGISTRY[name])returnCUSTOM_REGISTRY[name];returncategorize(name,isDev);}

Estimating Prompt Budget

Use estimateTokens to verify that your assembled context fits within a model's context window:

import{estimateTokens}from'codebase-ctx';constcontext='... assembled context string ...';consttokens=estimateTokens(context);constMODEL_LIMIT=128_000;constTASK_BUDGET=MODEL_LIMIT-tokens;console.log(`Context: ${tokens} tokens, leaving ${TASK_BUDGET} for the task`);

Implementing the Analyzer Interface

The Analyzer interface provides a contract for custom analyzer implementations:

importtype{Analyzer,CodebaseContext,OutputFormat,FormatOptions}from'codebase-ctx';constmyAnalyzer: Analyzer={asyncanalyze(projectPath?: string): Promise<CodebaseContext>{// Run analysis and return a CodebaseContext},asyncanalyzeAndFormat(projectPath?: string,outputFormat?: OutputFormat,formatOptions?: FormatOptions,): Promise<string>{// Analyze and return formatted string},};

TypeScript

This package is written in TypeScript and ships type declarations (dist/index.d.ts). All public interfaces and type aliases are exported from the package root:

importtype{// Core result typesCodebaseContext,ProjectInfo,DependencyInfo,DependencyEntry,ScriptInfo,ScriptEntry,// Additional analyzer result typesStructureInfo,DirectoryEntry,TypeScriptInfo,APISurface,APIEntry,ConfigInfo,ConfigEntry,GitInfo,StatsInfo,FileStat,LanguageStat,PatternInfo,DetectedPattern,AnalysisMeta,// Option and configuration typesAnalyzeOptions,FormatOptions,AnalyzerConfig,Analyzer,// String union typesDetailLevel,OutputFormat,AnalyzerName,DependencyCategory,}from'codebase-ctx';

Compiled with target: ES2022, module: commonjs, strict: true.


License

MIT

About

Generate AI-optimized codebase summaries via static analysis

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - SiluPanda/codebase-ctx: Generate AI-optimized codebase summaries via static analysis · GitHub
Skip to content

Repository files navigation

codebase-ctx

Generate AI-optimized codebase summaries via static analysis.

npm versionnpm downloadslicensenodeTypeScript

codebase-ctx is a zero-dependency static analysis tool that reads a project directory and produces structured, token-efficient context about its setup, dependencies, scripts, language, runtime, and architecture. The output is designed to be injected into AI instruction files (CLAUDE.md, .cursorrules), passed as prompt context, or consumed programmatically by other tools.

Unlike source code dumpers that concatenate entire repositories (producing hundreds of thousands of tokens), codebase-ctx runs a pipeline of modular analyzers that each extract a specific dimension of project context and compress the results into a structured summary. A typical output is 300--800 tokens: enough to convey what a developer learns in their first hour with a codebase, compact enough to leave the vast majority of the context window for the actual task.

All analysis is deterministic, offline, and fast (under 500ms for most projects). No LLM calls, no network requests, no API keys required.


Installation

npm install codebase-ctx

Or install globally for CLI usage:

npm install -g codebase-ctx

Requires Node.js >= 18.


Quick Start

import{analyzeProject,analyzeDependencies,analyzeScripts}from'codebase-ctx';// Analyze project metadataconstproject=analyzeProject('/path/to/project');console.log(project);// {// name: 'my-app',// version: '1.0.0',// description: 'My application',// license: 'MIT',// language: 'TypeScript',// runtime: 'Node.js',// repository: 'https://github.com/user/my-app',// nodeVersion: '>=18',// }// Analyze dependencies with automatic categorizationconstdeps=analyzeDependencies('/path/to/project');console.log(deps.summary);// {// totalProduction: 5,// totalDev: 3,// frameworks: ['react', 'next'],// databases: ['@prisma/client'],// testingTools: ['vitest'],// }// Analyze npm scriptsconstscripts=analyzeScripts('/path/to/project');console.log(scripts.hasBuild,scripts.hasTest);// true true

Features

  • Zero dependencies -- all analysis uses Node.js built-ins (node:fs, node:path). No AST parsers, no tree-sitter, no external tools.
  • Modular analyzers -- each analyzer extracts one dimension of context (project metadata, dependencies, scripts) and runs independently. Analyzers fail gracefully when their input files are missing.
  • Dependency categorization -- a built-in registry of 190+ common npm packages automatically classifies dependencies into categories: framework, database, testing, build, lint, ui, auth, api, observability, and type-definitions.
  • Language detection -- infers TypeScript or JavaScript from tsconfig.json, file extensions in src/, or the presence of typescript in dependencies.
  • Runtime detection -- infers Node.js, Bun, Deno, or Browser from engines fields and framework dependencies.
  • Script categorization -- classifies npm scripts into build, test, lint, start, deploy, and other categories by name pattern matching.
  • Token estimation -- estimates LLM token counts for any text using ceil(chars / 4).
  • Deterministic output -- same codebase always produces the same analysis result.
  • TypeScript-first -- full type definitions shipped with the package.

API Reference

Analyzers

analyzeProject(projectPath: string): ProjectInfo

Reads package.json and inspects the project directory to produce a ProjectInfo summary.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:ProjectInfo

FieldTypeDescription
namestring | nullPackage name from package.json, or directory name as fallback
versionstring | nullPackage version
descriptionstring | nullPackage description
licensestring | nullLicense identifier (e.g. "MIT")
languagestringDetected language: "TypeScript", "JavaScript", or "unknown"
runtimestringDetected runtime: "Node.js", "Bun", "Deno", "Browser", or "unknown"
repositorystring | nullRepository URL (cleaned of git+ prefix and .git suffix)
nodeVersionstring | nullNode.js version constraint from engines.node

Language detection checks in order: tsconfig.json existence, .ts/.tsx files in src/, typescript in dependencies. Falls back to "JavaScript".

Runtime detection checks in order: engines.bun, engines.deno, browser-only framework dependencies (React/Vue/Angular/Svelte without an SSR framework like Next/Nuxt), then defaults to "Node.js".

Fallback behavior: When no package.json exists, returns the directory name as name, "unknown" as language and runtime, and null for all other fields.

Example:

import{analyzeProject}from'codebase-ctx';constinfo=analyzeProject('/home/user/my-express-app');// info.language === 'TypeScript'// info.runtime === 'Node.js'// info.repository === 'https://github.com/user/my-express-app'

analyzeDependencies(projectPath: string): DependencyInfo

Reads package.json and extracts all dependency sections, categorizing each dependency by its purpose using a built-in registry.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:DependencyInfo

FieldTypeDescription
productionDependencyEntry[]Dependencies from dependencies
devDependencyEntry[]Dependencies from devDependencies
peerDependencyEntry[]Dependencies from peerDependencies
optionalDependencyEntry[]Dependencies from optionalDependencies
summaryobjectAggregated summary (see below)

summary fields:

FieldTypeDescription
totalProductionnumberCount of production dependencies
totalDevnumberCount of dev dependencies
frameworksstring[]Names of all framework dependencies across all sections
databasesstring[]Names of all database dependencies across all sections
testingToolsstring[]Names of all testing dependencies across all sections

Each DependencyEntry has the shape:

{
name: string;// Package name (e.g. "react")
version: string;// Version range (e.g. "^18.2.0")
category: DependencyCategory;}

Category inference: Known packages are mapped via a built-in registry of 190+ entries. Unrecognized packages in dependencies default to "utility"; unrecognized packages in devDependencies default to "build". Packages matching @types/* are always categorized as "type-definitions".

Fallback behavior: When package.json does not exist, returns empty arrays and zero counts for all fields.

Example:

import{analyzeDependencies}from'codebase-ctx';constdeps=analyzeDependencies('/home/user/my-project');// Inspect categorized production depsfor(constdepofdeps.production){console.log(`${dep.name} (${dep.category}): ${dep.version}`);}// react (framework): ^18.2.0// @prisma/client (database): ^5.0.0// winston (observability): ^3.0.0// Use the summary for quick contextconsole.log(deps.summary.frameworks);// ['react']console.log(deps.summary.databases);// ['@prisma/client']

analyzeScripts(projectPath: string): ScriptInfo

Reads the scripts field from package.json and categorizes each script by name.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:ScriptInfo

FieldTypeDescription
scriptsScriptEntry[]All scripts with their names, commands, and categories
hasBuildbooleantrue if any script is categorized as "build"
hasTestbooleantrue if any script is categorized as "test"
hasLintbooleantrue if any script is categorized as "lint"
hasStartbooleantrue if any script is categorized as "start"

Each ScriptEntry has the shape:

{
name: string;// Script name (e.g. "build")
command: string;// Script command (e.g. "tsc")
category: 'build'|'test'|'lint'|'start'|'deploy'|'other';}

Category mapping:

CategoryMatched script names
buildbuild, compile, bundle, tsc, build:*
testtest, spec, e2e, coverage, test:*
lintlint, check, format, prettier, lint:*
startstart, dev, serve, develop
deploydeploy, release, publish
otherEverything else

Scripts prefixed with pre or post (e.g. pretest, postbuild, prepublishOnly) inherit the category of their base script.

Fallback behavior: When package.json has no scripts field or does not exist, returns an empty array and all boolean flags as false.

Example:

import{analyzeScripts}from'codebase-ctx';constscripts=analyzeScripts('/home/user/my-project');if(scripts.hasTest){consttestScripts=scripts.scripts.filter(s=>s.category==='test');for(constsoftestScripts){console.log(`${s.name}: ${s.command}`);}}// test: vitest run// test:unit: vitest run src/

Dependency Registry

DEPENDENCY_REGISTRY

A Record<string, DependencyCategory> mapping 190+ common npm package names to their categories. This is the lookup table used by analyzeDependencies.

import{DEPENDENCY_REGISTRY}from'codebase-ctx';console.log(DEPENDENCY_REGISTRY['react']);// 'framework'console.log(DEPENDENCY_REGISTRY['prisma']);// 'database'console.log(DEPENDENCY_REGISTRY['vitest']);// 'testing'console.log(DEPENDENCY_REGISTRY['typescript']);// 'build'console.log(DEPENDENCY_REGISTRY['eslint']);// 'lint'console.log(DEPENDENCY_REGISTRY['tailwindcss']);// 'ui'console.log(DEPENDENCY_REGISTRY['passport']);// 'auth'console.log(DEPENDENCY_REGISTRY['axios']);// 'api'console.log(DEPENDENCY_REGISTRY['winston']);// 'observability'

Categories covered:framework, database, testing, build, lint, ui, auth, api, observability.


categorize(name: string, isDevDep: boolean): DependencyCategory

Categorizes a single package name. Checks the built-in registry first, then @types/* prefix, then falls back to "utility" for production dependencies or "build" for dev dependencies.

Parameters:

ParameterTypeDescription
namestringThe npm package name
isDevDepbooleanWhether the package is a devDependency

Returns:DependencyCategory -- one of "framework", "database", "testing", "build", "lint", "utility", "type-definitions", "ui", "auth", "api", "observability".

Example:

import{categorize}from'codebase-ctx';categorize('react',false);// 'framework'categorize('@types/node',true);// 'type-definitions'categorize('some-unknown-pkg',false);// 'utility'categorize('some-unknown-pkg',true);// 'build'

File Utilities

fileExists(filePath: string): boolean

Synchronously checks whether a file exists at the given path.

import{fileExists}from'codebase-ctx';if(fileExists('/path/to/tsconfig.json')){// TypeScript project}

readFileContent(filePath: string): string

Reads a file synchronously and returns its contents as a UTF-8 string. Throws if the file does not exist.

import{readFileContent}from'codebase-ctx';constcontent=readFileContent('/path/to/package.json');

readLines(filePath: string): string[]

Reads a file and splits it into an array of lines. Throws if the file does not exist.

import{readLines}from'codebase-ctx';constlines=readLines('/path/to/src/index.ts');console.log(`${lines.length} lines`);

readJsonFile<T>(filePath: string): T | null

Reads and parses a JSON file. Returns null if the file does not exist or contains invalid JSON. Never throws.

Type parameter:T -- the expected shape of the parsed JSON object.

import{readJsonFile}from'codebase-ctx';interfacePkgJson{name: string;version: string;}constpkg=readJsonFile<PkgJson>('/path/to/package.json');if(pkg){console.log(pkg.name,pkg.version);}

estimateTokens(text: string): number

Estimates the LLM token count for a string using the approximation Math.ceil(text.length / 4).

Returns 0 for empty strings.

import{estimateTokens}from'codebase-ctx';consttokens=estimateTokens('Hello, world!');// 4 (ceil(13 / 4))

Configuration

AnalyzeOptions

Options accepted by the analysis pipeline:

interfaceAnalyzeOptions{analyzers?: AnalyzerName[];// Which analyzers to run (default: all)exclude?: string[];// Directory/file patterns to excludedetailLevel?: DetailLevel;// 'minimal' | 'standard' | 'detailed'maxSampleFiles?: number;// Max files for pattern samplingmaxDepth?: number;// Max directory traversal depth}

FormatOptions

Options for formatting output:

interfaceFormatOptions{detailLevel?: DetailLevel;// 'minimal' | 'standard' | 'detailed'formatter?: (context: CodebaseContext)=>string;// Custom formatter functionincludeTokenCount?: boolean;// Append token count to output}

AnalyzerName

Valid analyzer names:

"project" | "dependencies" | "structure" | "typescript" | "api" | "scripts" | "config" | "git" | "stats" | "patterns"

DependencyCategory

Valid dependency categories:

"framework" | "database" | "testing" | "build" | "lint" | "utility" | "type-definitions" | "ui" | "auth" | "api" | "observability"

DetailLevel

Controls information density in formatted output:

LevelTarget tokensDescription
minimal150--300Language, framework, architecture, entry point, build/test commands only
standard400--800All major context dimensions with summaries
detailed800--2,000Full dependency lists, complete API surface, all patterns with evidence

OutputFormat

Supported output formats:

FormatDescription
markdownStructured markdown for CLAUDE.md / .cursorrules injection
jsonMachine-readable JSON for programmatic consumption
compactMinimal token count, maximum information density
customUser-provided formatter function via FormatOptions.formatter

Error Handling

All analyzers follow a graceful degradation pattern:

  • Missing package.json: Analyzers that depend on package.json return sensible defaults -- empty arrays, zero counts, null fields, or the directory name as a fallback project name.
  • Invalid JSON: readJsonFile returns null when a file contains malformed JSON. Analyzers that use it handle the null case explicitly.
  • Missing files: fileExists returns false; analyzers skip analysis for files that do not exist rather than throwing.
  • No matching data: Analyzers return empty result objects (empty arrays, false flags) rather than throwing when the data they look for is absent.

The general contract: individual analyzers never throw. If an analyzer cannot extract data, it returns a typed fallback value. Only infrastructure-level errors (directory does not exist, permission denied) produce exceptions.


Advanced Usage

Combining Analyzers

Run multiple analyzers against the same project and assemble the results:

import{analyzeProject,analyzeDependencies,analyzeScripts}from'codebase-ctx';constprojectPath='/home/user/my-app';constproject=analyzeProject(projectPath);constdeps=analyzeDependencies(projectPath);constscripts=analyzeScripts(projectPath);// Build a context summary for prompt injectionconstsummary=[`Project: ${project.name} v${project.version}`,`Language: ${project.language}, Runtime: ${project.runtime}`,`Frameworks: ${deps.summary.frameworks.join(', ')||'none'}`,`Databases: ${deps.summary.databases.join(', ')||'none'}`,`Testing: ${deps.summary.testingTools.join(', ')||'none'}`,`Build: ${scripts.hasBuild ? 'yes' : 'no'}, Test: ${scripts.hasTest ? 'yes' : 'no'}`,].join('\n');console.log(summary);

Extending the Dependency Registry

You can use categorize alongside your own logic to handle packages not in the built-in registry:

import{categorize,DEPENDENCY_REGISTRY}from'codebase-ctx';importtype{DependencyCategory}from'codebase-ctx';constCUSTOM_REGISTRY: Record<string,DependencyCategory>={'my-internal-framework': 'framework','@company/auth-sdk': 'auth',};functioncustomCategorize(name: string,isDev: boolean): DependencyCategory{if(CUSTOM_REGISTRY[name])returnCUSTOM_REGISTRY[name];returncategorize(name,isDev);}

Estimating Prompt Budget

Use estimateTokens to verify that your assembled context fits within a model's context window:

import{estimateTokens}from'codebase-ctx';constcontext='... assembled context string ...';consttokens=estimateTokens(context);constMODEL_LIMIT=128_000;constTASK_BUDGET=MODEL_LIMIT-tokens;console.log(`Context: ${tokens} tokens, leaving ${TASK_BUDGET} for the task`);

Implementing the Analyzer Interface

The Analyzer interface provides a contract for custom analyzer implementations:

importtype{Analyzer,CodebaseContext,OutputFormat,FormatOptions}from'codebase-ctx';constmyAnalyzer: Analyzer={asyncanalyze(projectPath?: string): Promise<CodebaseContext>{// Run analysis and return a CodebaseContext},asyncanalyzeAndFormat(projectPath?: string,outputFormat?: OutputFormat,formatOptions?: FormatOptions,): Promise<string>{// Analyze and return formatted string},};

TypeScript

This package is written in TypeScript and ships type declarations (dist/index.d.ts). All public interfaces and type aliases are exported from the package root:

importtype{// Core result typesCodebaseContext,ProjectInfo,DependencyInfo,DependencyEntry,ScriptInfo,ScriptEntry,// Additional analyzer result typesStructureInfo,DirectoryEntry,TypeScriptInfo,APISurface,APIEntry,ConfigInfo,ConfigEntry,GitInfo,StatsInfo,FileStat,LanguageStat,PatternInfo,DetectedPattern,AnalysisMeta,// Option and configuration typesAnalyzeOptions,FormatOptions,AnalyzerConfig,Analyzer,// String union typesDetailLevel,OutputFormat,AnalyzerName,DependencyCategory,}from'codebase-ctx';

Compiled with target: ES2022, module: commonjs, strict: true.


License

MIT

About

Generate AI-optimized codebase summaries via static analysis

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - SiluPanda/codebase-ctx: Generate AI-optimized codebase summaries via static analysis · GitHub
Skip to content

Repository files navigation

codebase-ctx

Generate AI-optimized codebase summaries via static analysis.

npm versionnpm downloadslicensenodeTypeScript

codebase-ctx is a zero-dependency static analysis tool that reads a project directory and produces structured, token-efficient context about its setup, dependencies, scripts, language, runtime, and architecture. The output is designed to be injected into AI instruction files (CLAUDE.md, .cursorrules), passed as prompt context, or consumed programmatically by other tools.

Unlike source code dumpers that concatenate entire repositories (producing hundreds of thousands of tokens), codebase-ctx runs a pipeline of modular analyzers that each extract a specific dimension of project context and compress the results into a structured summary. A typical output is 300--800 tokens: enough to convey what a developer learns in their first hour with a codebase, compact enough to leave the vast majority of the context window for the actual task.

All analysis is deterministic, offline, and fast (under 500ms for most projects). No LLM calls, no network requests, no API keys required.


Installation

npm install codebase-ctx

Or install globally for CLI usage:

npm install -g codebase-ctx

Requires Node.js >= 18.


Quick Start

import{analyzeProject,analyzeDependencies,analyzeScripts}from'codebase-ctx';// Analyze project metadataconstproject=analyzeProject('/path/to/project');console.log(project);// {// name: 'my-app',// version: '1.0.0',// description: 'My application',// license: 'MIT',// language: 'TypeScript',// runtime: 'Node.js',// repository: 'https://github.com/user/my-app',// nodeVersion: '>=18',// }// Analyze dependencies with automatic categorizationconstdeps=analyzeDependencies('/path/to/project');console.log(deps.summary);// {// totalProduction: 5,// totalDev: 3,// frameworks: ['react', 'next'],// databases: ['@prisma/client'],// testingTools: ['vitest'],// }// Analyze npm scriptsconstscripts=analyzeScripts('/path/to/project');console.log(scripts.hasBuild,scripts.hasTest);// true true

Features

  • Zero dependencies -- all analysis uses Node.js built-ins (node:fs, node:path). No AST parsers, no tree-sitter, no external tools.
  • Modular analyzers -- each analyzer extracts one dimension of context (project metadata, dependencies, scripts) and runs independently. Analyzers fail gracefully when their input files are missing.
  • Dependency categorization -- a built-in registry of 190+ common npm packages automatically classifies dependencies into categories: framework, database, testing, build, lint, ui, auth, api, observability, and type-definitions.
  • Language detection -- infers TypeScript or JavaScript from tsconfig.json, file extensions in src/, or the presence of typescript in dependencies.
  • Runtime detection -- infers Node.js, Bun, Deno, or Browser from engines fields and framework dependencies.
  • Script categorization -- classifies npm scripts into build, test, lint, start, deploy, and other categories by name pattern matching.
  • Token estimation -- estimates LLM token counts for any text using ceil(chars / 4).
  • Deterministic output -- same codebase always produces the same analysis result.
  • TypeScript-first -- full type definitions shipped with the package.

API Reference

Analyzers

analyzeProject(projectPath: string): ProjectInfo

Reads package.json and inspects the project directory to produce a ProjectInfo summary.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:ProjectInfo

FieldTypeDescription
namestring | nullPackage name from package.json, or directory name as fallback
versionstring | nullPackage version
descriptionstring | nullPackage description
licensestring | nullLicense identifier (e.g. "MIT")
languagestringDetected language: "TypeScript", "JavaScript", or "unknown"
runtimestringDetected runtime: "Node.js", "Bun", "Deno", "Browser", or "unknown"
repositorystring | nullRepository URL (cleaned of git+ prefix and .git suffix)
nodeVersionstring | nullNode.js version constraint from engines.node

Language detection checks in order: tsconfig.json existence, .ts/.tsx files in src/, typescript in dependencies. Falls back to "JavaScript".

Runtime detection checks in order: engines.bun, engines.deno, browser-only framework dependencies (React/Vue/Angular/Svelte without an SSR framework like Next/Nuxt), then defaults to "Node.js".

Fallback behavior: When no package.json exists, returns the directory name as name, "unknown" as language and runtime, and null for all other fields.

Example:

import{analyzeProject}from'codebase-ctx';constinfo=analyzeProject('/home/user/my-express-app');// info.language === 'TypeScript'// info.runtime === 'Node.js'// info.repository === 'https://github.com/user/my-express-app'

analyzeDependencies(projectPath: string): DependencyInfo

Reads package.json and extracts all dependency sections, categorizing each dependency by its purpose using a built-in registry.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:DependencyInfo

FieldTypeDescription
productionDependencyEntry[]Dependencies from dependencies
devDependencyEntry[]Dependencies from devDependencies
peerDependencyEntry[]Dependencies from peerDependencies
optionalDependencyEntry[]Dependencies from optionalDependencies
summaryobjectAggregated summary (see below)

summary fields:

FieldTypeDescription
totalProductionnumberCount of production dependencies
totalDevnumberCount of dev dependencies
frameworksstring[]Names of all framework dependencies across all sections
databasesstring[]Names of all database dependencies across all sections
testingToolsstring[]Names of all testing dependencies across all sections

Each DependencyEntry has the shape:

{
name: string;// Package name (e.g. "react")
version: string;// Version range (e.g. "^18.2.0")
category: DependencyCategory;}

Category inference: Known packages are mapped via a built-in registry of 190+ entries. Unrecognized packages in dependencies default to "utility"; unrecognized packages in devDependencies default to "build". Packages matching @types/* are always categorized as "type-definitions".

Fallback behavior: When package.json does not exist, returns empty arrays and zero counts for all fields.

Example:

import{analyzeDependencies}from'codebase-ctx';constdeps=analyzeDependencies('/home/user/my-project');// Inspect categorized production depsfor(constdepofdeps.production){console.log(`${dep.name} (${dep.category}): ${dep.version}`);}// react (framework): ^18.2.0// @prisma/client (database): ^5.0.0// winston (observability): ^3.0.0// Use the summary for quick contextconsole.log(deps.summary.frameworks);// ['react']console.log(deps.summary.databases);// ['@prisma/client']

analyzeScripts(projectPath: string): ScriptInfo

Reads the scripts field from package.json and categorizes each script by name.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:ScriptInfo

FieldTypeDescription
scriptsScriptEntry[]All scripts with their names, commands, and categories
hasBuildbooleantrue if any script is categorized as "build"
hasTestbooleantrue if any script is categorized as "test"
hasLintbooleantrue if any script is categorized as "lint"
hasStartbooleantrue if any script is categorized as "start"

Each ScriptEntry has the shape:

{
name: string;// Script name (e.g. "build")
command: string;// Script command (e.g. "tsc")
category: 'build'|'test'|'lint'|'start'|'deploy'|'other';}

Category mapping:

CategoryMatched script names
buildbuild, compile, bundle, tsc, build:*
testtest, spec, e2e, coverage, test:*
lintlint, check, format, prettier, lint:*
startstart, dev, serve, develop
deploydeploy, release, publish
otherEverything else

Scripts prefixed with pre or post (e.g. pretest, postbuild, prepublishOnly) inherit the category of their base script.

Fallback behavior: When package.json has no scripts field or does not exist, returns an empty array and all boolean flags as false.

Example:

import{analyzeScripts}from'codebase-ctx';constscripts=analyzeScripts('/home/user/my-project');if(scripts.hasTest){consttestScripts=scripts.scripts.filter(s=>s.category==='test');for(constsoftestScripts){console.log(`${s.name}: ${s.command}`);}}// test: vitest run// test:unit: vitest run src/

Dependency Registry

DEPENDENCY_REGISTRY

A Record<string, DependencyCategory> mapping 190+ common npm package names to their categories. This is the lookup table used by analyzeDependencies.

import{DEPENDENCY_REGISTRY}from'codebase-ctx';console.log(DEPENDENCY_REGISTRY['react']);// 'framework'console.log(DEPENDENCY_REGISTRY['prisma']);// 'database'console.log(DEPENDENCY_REGISTRY['vitest']);// 'testing'console.log(DEPENDENCY_REGISTRY['typescript']);// 'build'console.log(DEPENDENCY_REGISTRY['eslint']);// 'lint'console.log(DEPENDENCY_REGISTRY['tailwindcss']);// 'ui'console.log(DEPENDENCY_REGISTRY['passport']);// 'auth'console.log(DEPENDENCY_REGISTRY['axios']);// 'api'console.log(DEPENDENCY_REGISTRY['winston']);// 'observability'

Categories covered:framework, database, testing, build, lint, ui, auth, api, observability.


categorize(name: string, isDevDep: boolean): DependencyCategory

Categorizes a single package name. Checks the built-in registry first, then @types/* prefix, then falls back to "utility" for production dependencies or "build" for dev dependencies.

Parameters:

ParameterTypeDescription
namestringThe npm package name
isDevDepbooleanWhether the package is a devDependency

Returns:DependencyCategory -- one of "framework", "database", "testing", "build", "lint", "utility", "type-definitions", "ui", "auth", "api", "observability".

Example:

import{categorize}from'codebase-ctx';categorize('react',false);// 'framework'categorize('@types/node',true);// 'type-definitions'categorize('some-unknown-pkg',false);// 'utility'categorize('some-unknown-pkg',true);// 'build'

File Utilities

fileExists(filePath: string): boolean

Synchronously checks whether a file exists at the given path.

import{fileExists}from'codebase-ctx';if(fileExists('/path/to/tsconfig.json')){// TypeScript project}

readFileContent(filePath: string): string

Reads a file synchronously and returns its contents as a UTF-8 string. Throws if the file does not exist.

import{readFileContent}from'codebase-ctx';constcontent=readFileContent('/path/to/package.json');

readLines(filePath: string): string[]

Reads a file and splits it into an array of lines. Throws if the file does not exist.

import{readLines}from'codebase-ctx';constlines=readLines('/path/to/src/index.ts');console.log(`${lines.length} lines`);

readJsonFile<T>(filePath: string): T | null

Reads and parses a JSON file. Returns null if the file does not exist or contains invalid JSON. Never throws.

Type parameter:T -- the expected shape of the parsed JSON object.

import{readJsonFile}from'codebase-ctx';interfacePkgJson{name: string;version: string;}constpkg=readJsonFile<PkgJson>('/path/to/package.json');if(pkg){console.log(pkg.name,pkg.version);}

estimateTokens(text: string): number

Estimates the LLM token count for a string using the approximation Math.ceil(text.length / 4).

Returns 0 for empty strings.

import{estimateTokens}from'codebase-ctx';consttokens=estimateTokens('Hello, world!');// 4 (ceil(13 / 4))

Configuration

AnalyzeOptions

Options accepted by the analysis pipeline:

interfaceAnalyzeOptions{analyzers?: AnalyzerName[];// Which analyzers to run (default: all)exclude?: string[];// Directory/file patterns to excludedetailLevel?: DetailLevel;// 'minimal' | 'standard' | 'detailed'maxSampleFiles?: number;// Max files for pattern samplingmaxDepth?: number;// Max directory traversal depth}

FormatOptions

Options for formatting output:

interfaceFormatOptions{detailLevel?: DetailLevel;// 'minimal' | 'standard' | 'detailed'formatter?: (context: CodebaseContext)=>string;// Custom formatter functionincludeTokenCount?: boolean;// Append token count to output}

AnalyzerName

Valid analyzer names:

"project" | "dependencies" | "structure" | "typescript" | "api" | "scripts" | "config" | "git" | "stats" | "patterns"

DependencyCategory

Valid dependency categories:

"framework" | "database" | "testing" | "build" | "lint" | "utility" | "type-definitions" | "ui" | "auth" | "api" | "observability"

DetailLevel

Controls information density in formatted output:

LevelTarget tokensDescription
minimal150--300Language, framework, architecture, entry point, build/test commands only
standard400--800All major context dimensions with summaries
detailed800--2,000Full dependency lists, complete API surface, all patterns with evidence

OutputFormat

Supported output formats:

FormatDescription
markdownStructured markdown for CLAUDE.md / .cursorrules injection
jsonMachine-readable JSON for programmatic consumption
compactMinimal token count, maximum information density
customUser-provided formatter function via FormatOptions.formatter

Error Handling

All analyzers follow a graceful degradation pattern:

  • Missing package.json: Analyzers that depend on package.json return sensible defaults -- empty arrays, zero counts, null fields, or the directory name as a fallback project name.
  • Invalid JSON: readJsonFile returns null when a file contains malformed JSON. Analyzers that use it handle the null case explicitly.
  • Missing files: fileExists returns false; analyzers skip analysis for files that do not exist rather than throwing.
  • No matching data: Analyzers return empty result objects (empty arrays, false flags) rather than throwing when the data they look for is absent.

The general contract: individual analyzers never throw. If an analyzer cannot extract data, it returns a typed fallback value. Only infrastructure-level errors (directory does not exist, permission denied) produce exceptions.


Advanced Usage

Combining Analyzers

Run multiple analyzers against the same project and assemble the results:

import{analyzeProject,analyzeDependencies,analyzeScripts}from'codebase-ctx';constprojectPath='/home/user/my-app';constproject=analyzeProject(projectPath);constdeps=analyzeDependencies(projectPath);constscripts=analyzeScripts(projectPath);// Build a context summary for prompt injectionconstsummary=[`Project: ${project.name} v${project.version}`,`Language: ${project.language}, Runtime: ${project.runtime}`,`Frameworks: ${deps.summary.frameworks.join(', ')||'none'}`,`Databases: ${deps.summary.databases.join(', ')||'none'}`,`Testing: ${deps.summary.testingTools.join(', ')||'none'}`,`Build: ${scripts.hasBuild ? 'yes' : 'no'}, Test: ${scripts.hasTest ? 'yes' : 'no'}`,].join('\n');console.log(summary);

Extending the Dependency Registry

You can use categorize alongside your own logic to handle packages not in the built-in registry:

import{categorize,DEPENDENCY_REGISTRY}from'codebase-ctx';importtype{DependencyCategory}from'codebase-ctx';constCUSTOM_REGISTRY: Record<string,DependencyCategory>={'my-internal-framework': 'framework','@company/auth-sdk': 'auth',};functioncustomCategorize(name: string,isDev: boolean): DependencyCategory{if(CUSTOM_REGISTRY[name])returnCUSTOM_REGISTRY[name];returncategorize(name,isDev);}

Estimating Prompt Budget

Use estimateTokens to verify that your assembled context fits within a model's context window:

import{estimateTokens}from'codebase-ctx';constcontext='... assembled context string ...';consttokens=estimateTokens(context);constMODEL_LIMIT=128_000;constTASK_BUDGET=MODEL_LIMIT-tokens;console.log(`Context: ${tokens} tokens, leaving ${TASK_BUDGET} for the task`);

Implementing the Analyzer Interface

The Analyzer interface provides a contract for custom analyzer implementations:

importtype{Analyzer,CodebaseContext,OutputFormat,FormatOptions}from'codebase-ctx';constmyAnalyzer: Analyzer={asyncanalyze(projectPath?: string): Promise<CodebaseContext>{// Run analysis and return a CodebaseContext},asyncanalyzeAndFormat(projectPath?: string,outputFormat?: OutputFormat,formatOptions?: FormatOptions,): Promise<string>{// Analyze and return formatted string},};

TypeScript

This package is written in TypeScript and ships type declarations (dist/index.d.ts). All public interfaces and type aliases are exported from the package root:

importtype{// Core result typesCodebaseContext,ProjectInfo,DependencyInfo,DependencyEntry,ScriptInfo,ScriptEntry,// Additional analyzer result typesStructureInfo,DirectoryEntry,TypeScriptInfo,APISurface,APIEntry,ConfigInfo,ConfigEntry,GitInfo,StatsInfo,FileStat,LanguageStat,PatternInfo,DetectedPattern,AnalysisMeta,// Option and configuration typesAnalyzeOptions,FormatOptions,AnalyzerConfig,Analyzer,// String union typesDetailLevel,OutputFormat,AnalyzerName,DependencyCategory,}from'codebase-ctx';

Compiled with target: ES2022, module: commonjs, strict: true.


License

MIT

About

Generate AI-optimized codebase summaries via static analysis

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - SiluPanda/codebase-ctx: Generate AI-optimized codebase summaries via static analysis · GitHub
Skip to content

Repository files navigation

codebase-ctx

Generate AI-optimized codebase summaries via static analysis.

npm versionnpm downloadslicensenodeTypeScript

codebase-ctx is a zero-dependency static analysis tool that reads a project directory and produces structured, token-efficient context about its setup, dependencies, scripts, language, runtime, and architecture. The output is designed to be injected into AI instruction files (CLAUDE.md, .cursorrules), passed as prompt context, or consumed programmatically by other tools.

Unlike source code dumpers that concatenate entire repositories (producing hundreds of thousands of tokens), codebase-ctx runs a pipeline of modular analyzers that each extract a specific dimension of project context and compress the results into a structured summary. A typical output is 300--800 tokens: enough to convey what a developer learns in their first hour with a codebase, compact enough to leave the vast majority of the context window for the actual task.

All analysis is deterministic, offline, and fast (under 500ms for most projects). No LLM calls, no network requests, no API keys required.


Installation

npm install codebase-ctx

Or install globally for CLI usage:

npm install -g codebase-ctx

Requires Node.js >= 18.


Quick Start

import{analyzeProject,analyzeDependencies,analyzeScripts}from'codebase-ctx';// Analyze project metadataconstproject=analyzeProject('/path/to/project');console.log(project);// {// name: 'my-app',// version: '1.0.0',// description: 'My application',// license: 'MIT',// language: 'TypeScript',// runtime: 'Node.js',// repository: 'https://github.com/user/my-app',// nodeVersion: '>=18',// }// Analyze dependencies with automatic categorizationconstdeps=analyzeDependencies('/path/to/project');console.log(deps.summary);// {// totalProduction: 5,// totalDev: 3,// frameworks: ['react', 'next'],// databases: ['@prisma/client'],// testingTools: ['vitest'],// }// Analyze npm scriptsconstscripts=analyzeScripts('/path/to/project');console.log(scripts.hasBuild,scripts.hasTest);// true true

Features

  • Zero dependencies -- all analysis uses Node.js built-ins (node:fs, node:path). No AST parsers, no tree-sitter, no external tools.
  • Modular analyzers -- each analyzer extracts one dimension of context (project metadata, dependencies, scripts) and runs independently. Analyzers fail gracefully when their input files are missing.
  • Dependency categorization -- a built-in registry of 190+ common npm packages automatically classifies dependencies into categories: framework, database, testing, build, lint, ui, auth, api, observability, and type-definitions.
  • Language detection -- infers TypeScript or JavaScript from tsconfig.json, file extensions in src/, or the presence of typescript in dependencies.
  • Runtime detection -- infers Node.js, Bun, Deno, or Browser from engines fields and framework dependencies.
  • Script categorization -- classifies npm scripts into build, test, lint, start, deploy, and other categories by name pattern matching.
  • Token estimation -- estimates LLM token counts for any text using ceil(chars / 4).
  • Deterministic output -- same codebase always produces the same analysis result.
  • TypeScript-first -- full type definitions shipped with the package.

API Reference

Analyzers

analyzeProject(projectPath: string): ProjectInfo

Reads package.json and inspects the project directory to produce a ProjectInfo summary.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:ProjectInfo

FieldTypeDescription
namestring | nullPackage name from package.json, or directory name as fallback
versionstring | nullPackage version
descriptionstring | nullPackage description
licensestring | nullLicense identifier (e.g. "MIT")
languagestringDetected language: "TypeScript", "JavaScript", or "unknown"
runtimestringDetected runtime: "Node.js", "Bun", "Deno", "Browser", or "unknown"
repositorystring | nullRepository URL (cleaned of git+ prefix and .git suffix)
nodeVersionstring | nullNode.js version constraint from engines.node

Language detection checks in order: tsconfig.json existence, .ts/.tsx files in src/, typescript in dependencies. Falls back to "JavaScript".

Runtime detection checks in order: engines.bun, engines.deno, browser-only framework dependencies (React/Vue/Angular/Svelte without an SSR framework like Next/Nuxt), then defaults to "Node.js".

Fallback behavior: When no package.json exists, returns the directory name as name, "unknown" as language and runtime, and null for all other fields.

Example:

import{analyzeProject}from'codebase-ctx';constinfo=analyzeProject('/home/user/my-express-app');// info.language === 'TypeScript'// info.runtime === 'Node.js'// info.repository === 'https://github.com/user/my-express-app'

analyzeDependencies(projectPath: string): DependencyInfo

Reads package.json and extracts all dependency sections, categorizing each dependency by its purpose using a built-in registry.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:DependencyInfo

FieldTypeDescription
productionDependencyEntry[]Dependencies from dependencies
devDependencyEntry[]Dependencies from devDependencies
peerDependencyEntry[]Dependencies from peerDependencies
optionalDependencyEntry[]Dependencies from optionalDependencies
summaryobjectAggregated summary (see below)

summary fields:

FieldTypeDescription
totalProductionnumberCount of production dependencies
totalDevnumberCount of dev dependencies
frameworksstring[]Names of all framework dependencies across all sections
databasesstring[]Names of all database dependencies across all sections
testingToolsstring[]Names of all testing dependencies across all sections

Each DependencyEntry has the shape:

{
name: string;// Package name (e.g. "react")
version: string;// Version range (e.g. "^18.2.0")
category: DependencyCategory;}

Category inference: Known packages are mapped via a built-in registry of 190+ entries. Unrecognized packages in dependencies default to "utility"; unrecognized packages in devDependencies default to "build". Packages matching @types/* are always categorized as "type-definitions".

Fallback behavior: When package.json does not exist, returns empty arrays and zero counts for all fields.

Example:

import{analyzeDependencies}from'codebase-ctx';constdeps=analyzeDependencies('/home/user/my-project');// Inspect categorized production depsfor(constdepofdeps.production){console.log(`${dep.name} (${dep.category}): ${dep.version}`);}// react (framework): ^18.2.0// @prisma/client (database): ^5.0.0// winston (observability): ^3.0.0// Use the summary for quick contextconsole.log(deps.summary.frameworks);// ['react']console.log(deps.summary.databases);// ['@prisma/client']

analyzeScripts(projectPath: string): ScriptInfo

Reads the scripts field from package.json and categorizes each script by name.

Parameters:

ParameterTypeDescription
projectPathstringAbsolute path to the project directory

Returns:ScriptInfo

FieldTypeDescription
scriptsScriptEntry[]All scripts with their names, commands, and categories
hasBuildbooleantrue if any script is categorized as "build"
hasTestbooleantrue if any script is categorized as "test"
hasLintbooleantrue if any script is categorized as "lint"
hasStartbooleantrue if any script is categorized as "start"

Each ScriptEntry has the shape:

{
name: string;// Script name (e.g. "build")
command: string;// Script command (e.g. "tsc")
category: 'build'|'test'|'lint'|'start'|'deploy'|'other';}

Category mapping:

CategoryMatched script names
buildbuild, compile, bundle, tsc, build:*
testtest, spec, e2e, coverage, test:*
lintlint, check, format, prettier, lint:*
startstart, dev, serve, develop
deploydeploy, release, publish
otherEverything else

Scripts prefixed with pre or post (e.g. pretest, postbuild, prepublishOnly) inherit the category of their base script.

Fallback behavior: When package.json has no scripts field or does not exist, returns an empty array and all boolean flags as false.

Example:

import{analyzeScripts}from'codebase-ctx';constscripts=analyzeScripts('/home/user/my-project');if(scripts.hasTest){consttestScripts=scripts.scripts.filter(s=>s.category==='test');for(constsoftestScripts){console.log(`${s.name}: ${s.command}`);}}// test: vitest run// test:unit: vitest run src/

Dependency Registry

DEPENDENCY_REGISTRY

A Record<string, DependencyCategory> mapping 190+ common npm package names to their categories. This is the lookup table used by analyzeDependencies.

import{DEPENDENCY_REGISTRY}from'codebase-ctx';console.log(DEPENDENCY_REGISTRY['react']);// 'framework'console.log(DEPENDENCY_REGISTRY['prisma']);// 'database'console.log(DEPENDENCY_REGISTRY['vitest']);// 'testing'console.log(DEPENDENCY_REGISTRY['typescript']);// 'build'console.log(DEPENDENCY_REGISTRY['eslint']);// 'lint'console.log(DEPENDENCY_REGISTRY['tailwindcss']);// 'ui'console.log(DEPENDENCY_REGISTRY['passport']);// 'auth'console.log(DEPENDENCY_REGISTRY['axios']);// 'api'console.log(DEPENDENCY_REGISTRY['winston']);// 'observability'

Categories covered:framework, database, testing, build, lint, ui, auth, api, observability.


categorize(name: string, isDevDep: boolean): DependencyCategory

Categorizes a single package name. Checks the built-in registry first, then @types/* prefix, then falls back to "utility" for production dependencies or "build" for dev dependencies.

Parameters:

ParameterTypeDescription
namestringThe npm package name
isDevDepbooleanWhether the package is a devDependency

Returns:DependencyCategory -- one of "framework", "database", "testing", "build", "lint", "utility", "type-definitions", "ui", "auth", "api", "observability".

Example:

import{categorize}from'codebase-ctx';categorize('react',false);// 'framework'categorize('@types/node',true);// 'type-definitions'categorize('some-unknown-pkg',false);// 'utility'categorize('some-unknown-pkg',true);// 'build'

File Utilities

fileExists(filePath: string): boolean

Synchronously checks whether a file exists at the given path.

import{fileExists}from'codebase-ctx';if(fileExists('/path/to/tsconfig.json')){// TypeScript project}

readFileContent(filePath: string): string

Reads a file synchronously and returns its contents as a UTF-8 string. Throws if the file does not exist.

import{readFileContent}from'codebase-ctx';constcontent=readFileContent('/path/to/package.json');

readLines(filePath: string): string[]

Reads a file and splits it into an array of lines. Throws if the file does not exist.

import{readLines}from'codebase-ctx';constlines=readLines('/path/to/src/index.ts');console.log(`${lines.length} lines`);

readJsonFile<T>(filePath: string): T | null

Reads and parses a JSON file. Returns null if the file does not exist or contains invalid JSON. Never throws.

Type parameter:T -- the expected shape of the parsed JSON object.

import{readJsonFile}from'codebase-ctx';interfacePkgJson{name: string;version: string;}constpkg=readJsonFile<PkgJson>('/path/to/package.json');if(pkg){console.log(pkg.name,pkg.version);}

estimateTokens(text: string): number

Estimates the LLM token count for a string using the approximation Math.ceil(text.length / 4).

Returns 0 for empty strings.

import{estimateTokens}from'codebase-ctx';consttokens=estimateTokens('Hello, world!');// 4 (ceil(13 / 4))

Configuration

AnalyzeOptions

Options accepted by the analysis pipeline:

interfaceAnalyzeOptions{analyzers?: AnalyzerName[];// Which analyzers to run (default: all)exclude?: string[];// Directory/file patterns to excludedetailLevel?: DetailLevel;// 'minimal' | 'standard' | 'detailed'maxSampleFiles?: number;// Max files for pattern samplingmaxDepth?: number;// Max directory traversal depth}

FormatOptions

Options for formatting output:

interfaceFormatOptions{detailLevel?: DetailLevel;// 'minimal' | 'standard' | 'detailed'formatter?: (context: CodebaseContext)=>string;// Custom formatter functionincludeTokenCount?: boolean;// Append token count to output}

AnalyzerName

Valid analyzer names:

"project" | "dependencies" | "structure" | "typescript" | "api" | "scripts" | "config" | "git" | "stats" | "patterns"

DependencyCategory

Valid dependency categories:

"framework" | "database" | "testing" | "build" | "lint" | "utility" | "type-definitions" | "ui" | "auth" | "api" | "observability"

DetailLevel

Controls information density in formatted output:

LevelTarget tokensDescription
minimal150--300Language, framework, architecture, entry point, build/test commands only
standard400--800All major context dimensions with summaries
detailed800--2,000Full dependency lists, complete API surface, all patterns with evidence

OutputFormat

Supported output formats:

FormatDescription
markdownStructured markdown for CLAUDE.md / .cursorrules injection
jsonMachine-readable JSON for programmatic consumption
compactMinimal token count, maximum information density
customUser-provided formatter function via FormatOptions.formatter

Error Handling

All analyzers follow a graceful degradation pattern:

  • Missing package.json: Analyzers that depend on package.json return sensible defaults -- empty arrays, zero counts, null fields, or the directory name as a fallback project name.
  • Invalid JSON: readJsonFile returns null when a file contains malformed JSON. Analyzers that use it handle the null case explicitly.
  • Missing files: fileExists returns false; analyzers skip analysis for files that do not exist rather than throwing.
  • No matching data: Analyzers return empty result objects (empty arrays, false flags) rather than throwing when the data they look for is absent.

The general contract: individual analyzers never throw. If an analyzer cannot extract data, it returns a typed fallback value. Only infrastructure-level errors (directory does not exist, permission denied) produce exceptions.


Advanced Usage

Combining Analyzers

Run multiple analyzers against the same project and assemble the results:

import{analyzeProject,analyzeDependencies,analyzeScripts}from'codebase-ctx';constprojectPath='/home/user/my-app';constproject=analyzeProject(projectPath);constdeps=analyzeDependencies(projectPath);constscripts=analyzeScripts(projectPath);// Build a context summary for prompt injectionconstsummary=[`Project: ${project.name} v${project.version}`,`Language: ${project.language}, Runtime: ${project.runtime}`,`Frameworks: ${deps.summary.frameworks.join(', ')||'none'}`,`Databases: ${deps.summary.databases.join(', ')||'none'}`,`Testing: ${deps.summary.testingTools.join(', ')||'none'}`,`Build: ${scripts.hasBuild ? 'yes' : 'no'}, Test: ${scripts.hasTest ? 'yes' : 'no'}`,].join('\n');console.log(summary);

Extending the Dependency Registry

You can use categorize alongside your own logic to handle packages not in the built-in registry:

import{categorize,DEPENDENCY_REGISTRY}from'codebase-ctx';importtype{DependencyCategory}from'codebase-ctx';constCUSTOM_REGISTRY: Record<string,DependencyCategory>={'my-internal-framework': 'framework','@company/auth-sdk': 'auth',};functioncustomCategorize(name: string,isDev: boolean): DependencyCategory{if(CUSTOM_REGISTRY[name])returnCUSTOM_REGISTRY[name];returncategorize(name,isDev);}

Estimating Prompt Budget

Use estimateTokens to verify that your assembled context fits within a model's context window:

import{estimateTokens}from'codebase-ctx';constcontext='... assembled context string ...';consttokens=estimateTokens(context);constMODEL_LIMIT=128_000;constTASK_BUDGET=MODEL_LIMIT-tokens;console.log(`Context: ${tokens} tokens, leaving ${TASK_BUDGET} for the task`);

Implementing the Analyzer Interface

The Analyzer interface provides a contract for custom analyzer implementations:

importtype{Analyzer,CodebaseContext,OutputFormat,FormatOptions}from'codebase-ctx';constmyAnalyzer: Analyzer={asyncanalyze(projectPath?: string): Promise<CodebaseContext>{// Run analysis and return a CodebaseContext},asyncanalyzeAndFormat(projectPath?: string,outputFormat?: OutputFormat,formatOptions?: FormatOptions,): Promise<string>{// Analyze and return formatted string},};

TypeScript

This package is written in TypeScript and ships type declarations (dist/index.d.ts). All public interfaces and type aliases are exported from the package root:

importtype{// Core result typesCodebaseContext,ProjectInfo,DependencyInfo,DependencyEntry,ScriptInfo,ScriptEntry,// Additional analyzer result typesStructureInfo,DirectoryEntry,TypeScriptInfo,APISurface,APIEntry,ConfigInfo,ConfigEntry,GitInfo,StatsInfo,FileStat,LanguageStat,PatternInfo,DetectedPattern,AnalysisMeta,// Option and configuration typesAnalyzeOptions,FormatOptions,AnalyzerConfig,Analyzer,// String union typesDetailLevel,OutputFormat,AnalyzerName,DependencyCategory,}from'codebase-ctx';

Compiled with target: ES2022, module: commonjs, strict: true.


License

MIT

About

Generate AI-optimized codebase summaries via static analysis

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages