Skip to content

feat: halstead metrics, maintainability index, and docs update#142

Closed
carlos-alm wants to merge 5 commits intomainfrom
feat/halstead-maintainability-index
Closed

feat: halstead metrics, maintainability index, and docs update#142
carlos-alm wants to merge 5 commits intomainfrom
feat/halstead-maintainability-index

Conversation

@carlos-alm
Copy link
Contributor

Summary

  • Halstead metrics & Maintainability Index — extends codegraph complexity with Halstead volume, difficulty, effort, estimated bugs, and MI per function. New --health flag for full Halstead view, --sort mi to rank by worst MI. Stored in function_complexity DB table
  • Pagination & streaming — bounded query results with paginate.js helpers for context-friendly output across CLI and MCP
  • Bug fixes — exclude uncomputed MI=0 rows from threshold filtering; strict type validation for threshold values
  • Docs update — adds complexity, communities (feat: louvain community detection for module boundary analysis #133/feat: louvain community detection + fix complexity build regression #134), and manifesto (feat: manifesto-driven pass/fail rule engine #138) to all 7 doc files: README, CLAUDE.md, BACKLOG, titan-paradigm, recommended-practices, CLI examples, MCP examples. Updates MCP tool count 21→24 (25 multi-repo), marks backlog items 6/11/21/22 as done

Test plan

  • npm test passes
  • codegraph complexity -T shows cognitive, cyclomatic, nesting, MI columns
  • codegraph complexity --health -T shows Halstead columns (volume, diff, effort, bugs, LOC, SLOC)
  • codegraph complexity --sort mi -T sorts ascending by MI
  • codegraph complexity --above-threshold -T filters to flagged functions only
  • Verify no stale "21 tools" references remain in docs (should be 24/25)
  • Verify BACKLOG items 6, 11, 21, 22 are marked DONE

Add formal code health metrics per function: Halstead volume, difficulty,
effort, bugs estimate, LOC/SLOC, and the industry-standard Maintainability
Index (MI) normalized to 0-100 (Microsoft convention). MI below 20 flags
functions needing refactoring attention.

- DB migration v9: 14 new columns on function_complexity table
- HALSTEAD_RULES registry for JS/TS/TSX operator/operand classification
- computeHalsteadMetrics(), computeLOCMetrics(), computeMaintainabilityIndex()
- complexity command: MI column in default view, --health flag for full
  Halstead view, --sort mi|volume|effort|bugs|loc options
- stats/context/explain commands surface MI per function
- MCP tool schema updated with new sort values and health property
- Config: maintainabilityIndex threshold { warn: 20, fail: null }
- 29 new tests (18 unit + 11 integration)

Impact: 16 functions changed, 30 affected

Impact: 16 functions changed, 30 affected
…sults

Add offset/limit pagination to data functions so MCP clients get bounded
results with metadata to request more, and CLI consumers can process
results incrementally via NDJSON.

- New src/paginate.js with paginate(), paginateResult(), MCP_DEFAULTS,
  MCP_MAX_LIMIT utilities
- Pagination support in listFunctionsData, queryNameData, whereData,
  rolesData, listEntryPointsData
- Export limiting for DOT/Mermaid (truncation comments) and JSON
  (edge pagination)
- MCP tool schemas updated with limit/offset props and sensible defaults
  (e.g. list_functions: 100, query_function: 50)
- CLI --limit, --offset, --ndjson flags on query, where, roles, flow
- Programmatic API exports from index.js
- 33 new integration tests covering all pagination scenarios

Impact: 19 functions changed, 18 affected
The --above-threshold filter for maintainabilityIndex now requires
MI > 0, consistent with the exceeds array logic that already guards
against uncomputed rows from pre-migration databases.

Impact: 1 functions changed, 4 affected

Impact: 1 functions changed, 1 affected
Replace loose `!= null` checks with `typeof === 'number' && Number.isFinite()`
to prevent `Number("")`, `Number(null)`, and `Number(true)` from silently
coercing into valid SQL values. Add integration test verifying exceeds
arrays and summary.aboveWarn are correctly computed.

Addresses Greptile review feedback on #136.

Impact: 2 functions changed, 3 affected
Update README, CLAUDE.md, BACKLOG, titan-paradigm, recommended-practices,
and CLI/MCP examples to reflect today's merged PRs: complexity metrics
(#130/#139), Louvain community detection (#133/#134), and manifesto rule
engine (#138). Updates MCP tool count from 21 to 24 (25 in multi-repo),
marks backlog items 6/11/21/22 as done, and adds real CLI output examples.
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 27, 2026

Greptile Summary

This PR adds comprehensive code health metrics and pagination to codegraph. The implementation is solid with thorough test coverage and consistent integration across all components.

Major additions:

  • Halstead metrics & Maintainability Index — computes volume, difficulty, effort, estimated bugs, and MI per function using standard SEI formulas with Microsoft normalization
  • Pagination infrastructure — adds paginate.js utilities with limit, offset, and _pagination metadata; consistently applied to all query functions (queryName, where, listFunctions, listEntryPoints, export)
  • CLI enhancements--health flag for Halstead view, --sort mi|volume|effort|bugs|loc options, --limit/--offset/--ndjson flags across multiple commands
  • MCP integration — pagination support for all 24 tools with sensible defaults (50-100) and hard cap (1000)
  • Bug fixes — excludes MI=0 rows from threshold filtering (line 718 in complexity.js), strict type validation for thresholds

Documentation updates:

  • Tool count updated from 21→24 (22→25 multi-repo) across all 7 doc files
  • New sections for complexity, communities, manifesto commands
  • BACKLOG items 6, 11, 21, 22 marked DONE

Note: src/paginate.js has a logic error on lines 31 and 62 already flagged in previous threads — the && limit !== 0 condition is redundant when checking limit === undefined.

Confidence Score: 4/5

  • Safe to merge after addressing the paginate.js logic error flagged in previous threads
  • Score reflects high-quality implementation with comprehensive tests, proper SQL parameterization, and consistent integration. Deducted 1 point for the logic error in paginate.js that should be fixed before merge.
  • src/paginate.js requires fixing the redundant condition on lines 31 and 62 before merge

Important Files Changed

Filename Overview
src/complexity.js Adds Halstead metrics, MI computation, and LOC analysis for functions — implementations follow standard formulas correctly
src/paginate.js New pagination utilities with logic error on lines 31 and 62 (already flagged in previous threads)
src/queries.js Integrates pagination across query functions, adds MI and Halstead to stats/context/explain outputs
src/db.js Migration 9 adds Halstead and MI columns to function_complexity table with proper indexes
src/cli.js Adds --limit, --offset, --ndjson flags to multiple commands; adds --health flag to complexity command
src/mcp.js Adds pagination support to MCP tools with defaults and max limits; updates complexity tool description and schema
tests/integration/complexity.test.js Comprehensive tests for Halstead metrics, MI, sorting, and threshold filtering
tests/integration/pagination.test.js Thorough pagination test coverage including edge cases, offsets, MCP defaults, and export limiting

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    Start[codegraph build] --> Parse[Parse source files with tree-sitter]
    Parse --> Compute[Compute complexity metrics]
    Compute --> Halstead[Calculate Halstead metrics<br/>n1, n2, N1, N2, volume, difficulty, effort, bugs]
    Compute --> LOC[Calculate LOC metrics<br/>loc, sloc, comment lines]
    Halstead --> MI[Calculate Maintainability Index<br/>MI = f volume, cyclomatic, sloc, comments]
    LOC --> MI
    MI --> Store[Store in function_complexity table<br/>Migration 9: adds Halstead & MI columns]
    
    Store --> Query[Query-time: codegraph complexity]
    Query --> Paginate[Apply pagination<br/>limit, offset, hasMore]
    Paginate --> Filter[Apply filters<br/>--above-threshold, --sort, --file]
    Filter --> Format{Output format?}
    Format -->|--health| Health[Display Halstead view<br/>Volume, Difficulty, Effort, Bugs, MI]
    Format -->|default| Default[Display cognitive, cyclomatic, nesting, MI]
    Format -->|--json| JSON[Return JSON with _pagination metadata]
    
    Store --> MCP[MCP Server: complexity tool]
    MCP --> MCPPaginate[Apply MCP defaults<br/>limit: 20, max: 1000]
    MCPPaginate --> MCPOut[Return paginated results to AI agent]
    
    Store --> OtherQueries[Other query functions<br/>queryName, where, listFunctions, etc.]
    OtherQueries --> PaginateAll[Consistent pagination via paginateResult]
    PaginateAll --> Export[Export: DOT, Mermaid, JSON<br/>Edge limiting with truncation notices]
Loading

Last reviewed commit: d5a322a

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

21 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

* @returns {{ items: any[], pagination?: { total: number, offset: number, limit: number, hasMore: boolean, returned: number } }}
*/
export function paginate(items, { limit, offset } = {}) {
if (limit === undefined && limit !== 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic error: && limit !== 0 should be removed or the operator should be ||

The condition currently checks if limit is undefined AND not zero, but when limit is undefined, the second check is always true and redundant. Based on the docstring ("When limit is undefined the input is returned unchanged"), this should just be:

Suggested change
if (limit === undefined && limit !== 0) {
if (limit === undefined) {

or to handle null as well:

Suggested change
if (limit === undefined && limit !== 0) {
if (limit == null) {

* @returns {object} - Result with paginated field + `_pagination` (if active)
*/
export function paginateResult(result, field, { limit, offset } = {}) {
if (limit === undefined && limit !== 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same logic error as line 31: && limit !== 0 is redundant and confusing

Suggested change
if (limit === undefined && limit !== 0) {
if (limit === undefined) {

@carlos-alm carlos-alm closed this Feb 27, 2026
@carlos-alm carlos-alm deleted the feat/halstead-maintainability-index branch February 27, 2026 02:08
@carlos-alm
Copy link
Contributor Author

Addressed Greptile feedback: removed redundant && limit !== 0 from both guard clauses in src/paginate.js (lines 31 and 62). Fixed in 4edbf07.

@greptileai

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

21 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant