Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ src/
parser/index.ts <- TS Compiler API parser (files, functions, imports)
graph/index.ts <- graphology graph builder + circular dep detection
analyzer/index.ts <- Metrics engine (PageRank, betweenness, cohesion, tension, churn, complexity, blast radius, dead exports)
mcp/index.ts <- MCP stdio server (15 tools, 2 prompts, 3 resources)
mcp/index.ts <- MCP stdio server (16 tools, 2 prompts, 3 resources)
mcp/hints.ts <- Next-step hints for MCP tool responses
server/graph-store.ts <- Global graph state (shared by CLI + MCP)
impact/index.ts <- Symbol-level impact analysis + rename planning
Expand All @@ -21,12 +21,14 @@ src/
community/index.ts <- Louvain clustering
persistence/index.ts <- Graph export/import to .code-visualizer/
install/index.ts <- Agent adoption: managed-block engine + per-agent files + skill (init)
config/index.ts <- Config discovery + zod validation (codebase-intelligence.json)
rules/index.ts <- Rules engine + registry (check command + MCP check tool)
cli.ts <- CLI entry point (commander)
docs/
architecture.md <- Pipeline, module map, data flow, design decisions
data-model.md <- All TypeScript interfaces with field descriptions
metrics.md <- Per-file + module metrics, force analysis, complexity scoring
mcp-tools.md <- 15 MCP tools: inputs, outputs, use cases, selection guide
mcp-tools.md <- 16 MCP tools: inputs, outputs, use cases, selection guide
specs/
active/ <- Current spec
```
Expand Down Expand Up @@ -149,7 +151,7 @@ LLM knowledge base for building this tool. Single source of truth per topic:
| `docs/architecture.md` | Pipeline, module map, data flow, design decisions | New module or pipeline change |
| `docs/data-model.md` | All TypeScript interfaces (mirrors `src/types/index.ts`) | Type changes |
| `docs/metrics.md` | Per-file + module metrics, force analysis, complexity scoring | New metric added |
| `docs/mcp-tools.md` | 15 MCP tools with inputs/outputs/use cases | New tool or param change |
| `docs/mcp-tools.md` | 16 MCP tools with inputs/outputs/use cases | New tool or param change |

## Testing (BLOCKING)

Expand Down
2 changes: 1 addition & 1 deletion codebase-intelligence.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"quiet": false
},
"ci": {
"gate": "new-only",
"gate": "all",
"failOn": "error",
"maxWarnings": -1
}
Expand Down
8 changes: 5 additions & 3 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Core (shared computation)
| result builders used by both MCP and CLI
v
MCP (stdio) CLI (terminal/CI)
| 15 tools, 2 prompts, | 5 commands: overview, hotspots,
| 16 tools, 2 prompts, | 5 commands: overview, hotspots,
| 3 resources for LLMs | file, search, changes + --json
```

Expand All @@ -35,7 +35,9 @@ src/
graph/index.ts <- graphology graph + circular dep detection
analyzer/index.ts <- All metric computation
core/index.ts <- Shared result computation (MCP + CLI)
mcp/index.ts <- 15 MCP tools for LLM integration
config/index.ts <- Config discovery + zod validation
rules/index.ts <- Rules engine + registry (check command + MCP check tool)
mcp/index.ts <- 16 MCP tools for LLM integration
mcp/hints.ts <- Next-step hints for MCP tool responses
impact/index.ts <- Symbol-level impact analysis + rename planning
search/index.ts <- BM25 search engine
Expand Down Expand Up @@ -64,7 +66,7 @@ analyzeGraph(builtGraph, parsedFiles)
}

startMcpServer(codebaseGraph)
-> stdio MCP server with 15 tools, 2 prompts, 3 resources
-> stdio MCP server with 16 tools, 2 prompts, 3 resources
```

## Key Design Decisions
Expand Down
15 changes: 14 additions & 1 deletion docs/mcp-tools.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# MCP Tools Reference

15 tools available via MCP stdio.
16 tools available via MCP stdio.

## 1. codebase_overview

Expand Down Expand Up @@ -155,6 +155,18 @@ Community-detected clusters of related files.
**Use when:** "What files are related?" "Find natural groupings." Discovering emergent groupings that differ from directory structure.
**Not for:** Directory-based modules (use get_module_structure).

## 16. check

Run the configurable rules engine and gate on findings.

**Input:** `{}` (uses the loaded graph + discovered config)
**Returns:** `{ verdict: "pass"|"warn"|"fail", summary: { error, warn, rules }, configPath, findings[] }`. Each finding has ruleId, severity, file, line, column, message, fingerprint, and optional advisory `actions[]` (the tool is read-only — actions are hints, never applied).

Rules: `no-comments` (off by default), `no-circular-deps` (error), `no-dead-exports` (warn). Configure severities and options in `codebase-intelligence.json` (validated by `schema.json`).

**Use when:** Linting a codebase or enforcing a CI gate. "What rule violations exist?"
**Not for:** Architecture metrics (use analyze_forces).

## MCP Prompts

| Prompt | Description |
Expand Down Expand Up @@ -190,3 +202,4 @@ Community-detected clusters of related files.
| "How does data flow through the app?" | `get_processes` |
| "What files naturally belong together?" | `get_clusters` |
| "What are the main areas?" | `get_groups` |
| "What rule violations exist? Lint this." | `check` |
2 changes: 1 addition & 1 deletion schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@
},
"allow": {
"type": "array",
"description": "Additional substrings/patterns whose comments are allowed (e.g. 'TODO', 'FIXME', '@public').",
"description": "Allow comments whose body (delimiters stripped) starts with one of these strings, e.g. 'TODO', 'FIXME', '@public'.",
"items": { "type": "string" }
}
}
Expand Down
94 changes: 92 additions & 2 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { parseCodebase } from "./parser/index.js";
import { buildGraph } from "./graph/index.js";
import { analyzeGraph } from "./analyzer/index.js";
import { startMcpServer } from "./mcp/index.js";
import { setIndexedHead } from "./server/graph-store.js";
import { setIndexedHead, setRoot } from "./server/graph-store.js";
import { exportGraph, importGraph } from "./persistence/index.js";
import {
computeOverview,
Expand All @@ -47,7 +47,10 @@ import {
ALL_AGENT_IDS,
} from "./install/index.js";
import { promptSelection } from "./install/prompt.js";
import type { CodebaseGraph } from "./types/index.js";
import { runCheck, exitCodeFor } from "./rules/check.js";
import { formatResult, formatSummaryLine } from "./rules/format.js";
import { ConfigError } from "./config/index.js";
import type { CodebaseGraph, OutputFormat } from "./types/index.js";

const INDEX_DIR_NAME = ".code-visualizer";

Expand All @@ -63,6 +66,7 @@ function getHeadHash(targetPath: string): string {
cwd: path.resolve(targetPath),
encoding: "utf-8",
timeout: 5000,
stdio: ["ignore", "pipe", "ignore"],
}).trim();
} catch {
return "unknown";
Expand All @@ -88,6 +92,7 @@ function loadGraph(targetPath: string, force = false): { graph: CodebaseGraph; h
process.stderr.write(`Error: Path does not exist: ${targetPath}\n`);
process.exit(1);
}
setRoot(resolved);

const indexDir = getIndexDir(targetPath);
const headHash = getHeadHash(targetPath);
Expand Down Expand Up @@ -1011,6 +1016,90 @@ program
output(`Re-run anytime — writes are idempotent (managed blocks only).`);
});

// ── Subcommand: check ──────────────────────────────────────

interface CheckOptions extends CliCommandOptions {
config?: string;
format?: string;
failOn?: string;
gate?: string;
base?: string;
quiet?: boolean;
summary?: boolean;
}

function resolveCheckFormat(options: CheckOptions): OutputFormat | null {
if (options.json) return "json";
if (!options.format) return "text";
if (options.format === "json" || options.format === "sarif" || options.format === "text") {
return options.format;
}
return null;
}

function parseFailOn(value: string | undefined): "error" | "warn" | "never" | undefined | false {
if (value === undefined) return undefined;
if (value === "error" || value === "warn" || value === "never") return value;
return false;
}

function parseGate(value: string | undefined): "all" | "new-only" | undefined {
return value === "all" || value === "new-only" ? value : undefined;
}

program
.command("check")
.description("Run the rules engine and gate on findings (comments, circular deps, dead exports)")
.argument("<path>", "Path to TypeScript codebase")
.option("--config <path>", "Config file path (overrides discovery)")
.option("--format <fmt>", "Output: text, json, or sarif (default: text)")
.option("--fail-on <severity>", "Severity that fails the gate: error, warn, never")
.option("--gate <mode>", "Gate mode: all or new-only")
.option("--base <ref>", "Base git ref for new-only gating")
.option("--quiet", "Suppress output when the result passes")
.option("--summary", "Print summary counts only")
.option("--json", "Shortcut for --format json")
.option("--force", "Re-index even if HEAD unchanged")
.action((targetPath: string, options: CheckOptions) => {
const format = resolveCheckFormat(options);
if (!format) {
process.stderr.write("Error: --format must be one of: text, json, sarif\n");
process.exit(2);
}

const failOn = parseFailOn(options.failOn);
if (failOn === false) {
process.stderr.write("Error: --fail-on must be one of: error, warn, never\n");
process.exit(2);
}

try {
const { graph } = loadGraph(targetPath, options.force);
const result = runCheck(graph, path.resolve(targetPath), {
configPath: options.config,
format,
failOn,
gate: parseGate(options.gate),
base: options.base,
quiet: options.quiet,
summary: options.summary,
});

const silent = options.quiet === true && result.verdict === "pass";
if (!silent) {
output(options.summary ? formatSummaryLine(result) : formatResult(result, format));
}

process.exit(exitCodeFor(result));
} catch (err) {
if (err instanceof ConfigError) {
process.stderr.write(`Config error: ${err.message}\n`);
process.exit(2);
}
throw err;
}
});

// ── MCP fallback (backward compat) ──────────────────────────

program
Expand All @@ -1025,6 +1114,7 @@ program

async function runMcpMode(targetPath: string, options: McpOptions): Promise<void> {
const indexDir = getIndexDir(targetPath);
setRoot(path.resolve(targetPath));

if (options.clean) {
if (fs.existsSync(indexDir)) {
Expand Down
Loading
Loading