@titan-design/code-graph 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -44,10 +44,13 @@ Ported with TP-250: package partition quality (`src/analysis/partition-quality.t
44
44
  snapshot pruning (`src/prune.ts`). See [Partition quality](#partition-quality) and
45
45
  [Pruning snapshots](#pruning-snapshots).
46
46
 
47
- Deferred, all of it still in codewatch, all of it a follow-up on this package rather than a
48
- change to it:
47
+ Ported with TP-130: community detection and the convention layer (`src/conventions/`). See
48
+ [Conventions](#conventions). codewatch keeps the CLI command, the `claude -p` summarizer, and
49
+ the MCP and read-API wiring.
49
50
 
50
- - Graph analyses over a finished snapshot: communities, conventions, reuse-delta reporting.
51
+ Deferred, still in codewatch, a follow-up on this package rather than a change to it:
52
+
53
+ - Reuse-delta reporting over a finished snapshot.
51
54
 
52
55
  Python support is new here rather than ported. codewatch walked TypeScript only; the parser
53
56
  already had the grammar. The Python extractor is deliberately narrower than the ts-morph one:
@@ -364,6 +367,43 @@ The domain tables declare no foreign key, so `CodeGraphStore.deleteSnapshots` cl
364
367
  `SNAPSHOT_SCOPED_TABLES` itself rather than relying on a cascade. `blob_cache` is
365
368
  content-addressed and not snapshot-scoped, so a prune never drops a cached embedding.
366
369
 
370
+ ## Conventions
371
+
372
+ "How does this repo do X, and where does code like this belong?" Ported from codewatch's
373
+ unmerged C-88 branch (TP-130). The layer cuts the barrel-resolved file graph into a few coarse
374
+ areas, has an injected summarizer describe each one, and ranks areas against a question by
375
+ embedding similarity. Verified against this release on this repo's `packages/`, with a fake
376
+ summarizer:
377
+
378
+ ```ts
379
+ import { findConventions, getConventionMap, summarizeConventions } from "@titan-design/code-graph";
380
+
381
+ const summarizer = { model: "claude:sonnet", summarize: (prompt: string) => callYourLlm(prompt) };
382
+ await summarizeConventions(store, snapshotId, summarizer);
383
+ // { coverage: { files: 713, grouped: 513, areas: 24, summarized: 24 }, newlySummarized: 24, reused: 0, … }
384
+ // a second run: { newlySummarized: 0, reused: 24 }
385
+
386
+ getConventionMap(store, snapshotId, "claude:sonnet"); // the same areas, stored summaries only
387
+ await findConventions(store, snapshotId, "how are CLI commands registered?", embedder, "claude:sonnet");
388
+ // { matches: [ { label, summary, files: [ …up to 5 ], size, score }, … up to 3 ] }
389
+ ```
390
+
391
+ - **The cut.** `detectCommunities` is greedy modularity (Clauset-Newman-Moore), not Leiden. It
392
+ is deterministic without a seed: ties resolve by sorted id. `targetCount` keeps merging past
393
+ the natural modularity stop until that many communities remain, and a size cap of twice the
394
+ ideal share keeps a dense repo from collapsing into one area. The default target is one area
395
+ per 25 files, clamped to 6..40. Areas under `minSize` (default 3) files are left unsummarized.
396
+ Disconnected components never merge, so the component count is the floor.
397
+ - **Only the coarse level is summarized.** LLM cost is one call per area, and each summary is
398
+ stored in `blob_cache` under `code-graph/community-summary`, keyed by `summarizer.model` and a
399
+ hash of the prompt. The prompt carries the member files and their key exported signatures, so
400
+ an area whose membership and signatures did not change is a cache hit in any snapshot. On
401
+ this repo a finer cut (`targetCount: 60`) still reused 8 of its 35 areas.
402
+ - **The package ships no LLM client.** `Summarizer` is `{ model, summarize(prompt) }`; the
403
+ product supplies it. `getConventionMap` and `findConventions` never call it. `findConventions`
404
+ throws when no summary is stored for the model, and returns candidates with scores, not
405
+ verdicts. Summary vectors go through the same embedding cache as similar symbols.
406
+
367
407
  ## Git history
368
408
 
369
409
  Ported in TP-126, strictly as codewatch had it: churn over rolling windows, first-seen dates,
package/dist/index.d.ts CHANGED
@@ -1206,6 +1206,14 @@ interface CheckViolation {
1206
1206
  threshold?: number;
1207
1207
  destinationId?: string;
1208
1208
  isCarryover?: boolean;
1209
+ /** Repo-relative file the violation sits in; a symbol's parent file. */
1210
+ path?: string;
1211
+ lineStart?: number;
1212
+ lineEnd?: number;
1213
+ symbol?: string;
1214
+ /** One line a reader can check without re-running the rule, such as `loc=412 (max 350)`. */
1215
+ evidence?: string;
1216
+ tool?: string;
1209
1217
  }
1210
1218
  interface CheckResult {
1211
1219
  snapshotId: number;
@@ -1236,6 +1244,34 @@ declare function violationKey(v: CheckViolation): string;
1236
1244
  /** {@link violationKey} with both node ids carried into another snapshot's id space; unmoved ids key as before. */
1237
1245
  declare function rebasedViolationKey(v: CheckViolation, resolve: (id: string) => string): string;
1238
1246
 
1247
+ /** One tool-neutral audit finding; every finding cites a path so a citation checker can verify it. */
1248
+ interface Finding {
1249
+ id: string;
1250
+ path: string;
1251
+ lineStart?: number;
1252
+ lineEnd?: number;
1253
+ symbol?: string;
1254
+ signal: string;
1255
+ value?: number;
1256
+ baseline?: number;
1257
+ threshold?: number;
1258
+ severity: Severity;
1259
+ evidence?: string;
1260
+ tool: string;
1261
+ }
1262
+ /** A diagnostic from an outside linter, shaped structurally so no linter package is imported. */
1263
+ interface ExternalDiagnostic {
1264
+ tool: string;
1265
+ rule: string;
1266
+ file: string;
1267
+ line: number;
1268
+ endLine?: number;
1269
+ message: string;
1270
+ severity: "error" | "warning";
1271
+ }
1272
+ declare function toFindings(result: CheckResult): Finding[];
1273
+ declare function externalToFinding(input: ExternalDiagnostic): Finding;
1274
+
1239
1275
  /** Glob when the pattern has a `*` (`**` crosses directories, `*` stays in a segment); otherwise a case-sensitive substring. */
1240
1276
  declare function patternToRegex(pattern: string): RegExp;
1241
1277
  declare function compilePatterns(patterns: readonly string[] | undefined): RegExp[];
@@ -1432,4 +1468,124 @@ declare function tryEmbedSnapshot(store: CodeGraphStore, snapshotId: number, emb
1432
1468
  */
1433
1469
  declare function findSimilarCapability(store: CodeGraphStore, snapshotId: number, query: string, embedder: Embedder, opts?: FindSimilarOptions): Promise<SimilarResult>;
1434
1470
 
1435
- export { ALIAS_BASE_ATTR, ALL_ROLES, type AliasChain, type AliasChainInput, type AliasChainOptions, type AliasLoader, type AliasResolution, COVERAGE_METRIC_NAME, type CachedEmbedResult, type CheckDiff, type CheckResult, type CheckRule, type CheckRulesFile, type CheckSnapshotOptions, type CheckSnapshotResult, type CheckViolation, CodeGraphStore, DEAD_CODE_METRIC_NAMES, DEFAULT_CHURN_WINDOWS, DOMAIN_DDL, type DeepAst, type DeepAstInput, type DiffCheckResultsOptions, type DiffSnapshotsOptions, type EdgeKind, type EmbedAttempt, type EmbedCoverage, type EmbedSnapshotResult, type EmbeddableSymbol, type FileFingerprint, type FindSimilarOptions, type ForbidImportRule, GROWTH_RISK_METRIC_NAMES, type GraphDiff, type GraphDiffSummary, type GraphEdge, type GraphFragment, type GraphMetric, type GraphNode, type HistoryMetricsOptions, INDEX_VERSION, type IdAlias, type IdAliasReason, type IndexOptions, type IndexResult, type IstanbulCoverage, KIT, LanguageExtractor, type LanguageExtractorOptions, type LayeredDepsRule, type LineSpan, type Lineage, type LineageSnapshot, type LineageStep, type LinkMethod, type LinkTestsOptions, type LoadedHistory, METRIC_CATALOGUE, MIGRATIONS, type MemberInfo, type MetricAbsence, type MetricAggregate, type MetricDelta, type MetricDescriptor, type MetricDirection, type MetricMaxRule, type MetricMinRule, type MetricProductMaxRule, type MetricRollup, type MetricSource, type MetricUnit, type NoInternalOnlyBarrelsRule, type NodeKind, type NodeRename, type NodeRole, type PackageFlag, type PackageLayer, type PackageStats, type PageRankOptions, type PageRankResult, type PageRankRow, type PairCoupling, type PairFlag, type ParamInfo, type PartitionQualityInput, type PartitionQualityResult, type PriorSnapshotOptions, type PruneOptions, type PrunePlan, type PruneResult, PythonGraphExtractor, type ReadFile, type ReferenceEdgeLite, type RelevanceOptions, type ResolveAliasOptions, type ReuseBasis, type RuleStore, type RunChecksOptions, SCHEMA_VERSION, SNAPSHOT_SCOPED_TABLES, SOURCE_METRIC_NAMES, SYMBOL_EMBEDDING_NAMESPACE, SYMBOL_ID_SEP, type Severity, type SimilarCandidate, type SimilarResult, type SnapshotInsert, type SnapshotRow, type SnapshotSpec, type SourceLanguage, type SymbolConsumers, type SymbolCouplingOptions, type SymbolCouplingPair, type SymbolSpan, type TestCoverageOwnershipOptions, type TestSourceLink, type TopMetricRow, TsMorphGraphExtractor, type TsMorphGraphExtractorOptions, type UnchangedViolation, type ValidateRulesOptions, aggregateMetrics, aliasChain, annotateRoles, attributeCoverage, buildAliases, buildEmbedText, buildFileModuleNodes, buildIndexerMetrics, buildLineage, canonicalEdgeKind, canonicalMetricName, canonicalRole, checkSnapshot, classifyRole, collectDeclaredNames, collectDeclaredSpans, compilePatterns, computeDeadCodeMetrics, computeDeepAst, computeGrowthRiskMetrics, computeMetrics, computePageRank, computePartitionQuality, computeRecencyWindows, computeRelevance, computeRoleHints, computeSourceMetrics, computeSymbolConsumers, computeSymbolCoupling, computeTestCoverageOwnership, createAliasChain, describeMetric, describeMetrics, detectGitHead, detectGitToplevel, detectRenames, diffCheckResults, diffSnapshots, edgeWeight, embedSnapshot, embedTextsCached, externalId, fileId, findSimilarCapability, getEdgeWeight, groupTestsBySource, hashContent, hashEmbedText, indexPaths, invertBuckets, isGeneratedByHeuristic, isGeneratedFile, isInsideGitRepo, lineagePath, linkTestsToSources, listEdges, listEdgesTouching, listEmbeddableSymbols, listMetrics, listMetricsForNode, listNodes, loadCheckRules, loadGeneratedPatterns, loadHistoryMetrics, loadLineage, matchesAny, moduleId, openCodeGraph, packageId, parentModuleId, parseSymbolId, patternToRegex, planPrune, priorSnapshotForRef, pruneDanglingReferences, readSourceFiles, rebasedViolationKey, resolveAlias, resolveBarrelEdges, resolveChurnWindows, resolveGitRef, resolveSnapshot, runChecks, runPrune, snapshotPageRank, snapshotReferenceEdges, snapshotRelevance, snapshotSymbolConsumers, snapshotSymbolCoupling, snapshotViolations, structuralSignature, symbolId, testCoverageCountMetrics, tryEmbedSnapshot, validateRules, violationKey, walkSourceFiles, windowSuffix };
1471
+ /** A text-summarization backend the product injects; `model` keys the stored summaries. */
1472
+ interface Summarizer {
1473
+ readonly model: string;
1474
+ summarize(prompt: string): Promise<string>;
1475
+ }
1476
+ interface ConventionSymbol {
1477
+ name: string;
1478
+ signature: string;
1479
+ purpose?: string;
1480
+ }
1481
+ interface ConventionArea {
1482
+ /** Stable over membership: `area-` plus a hash of the member file list. */
1483
+ id: string;
1484
+ /** Dominant directory of the members. */
1485
+ label: string;
1486
+ /** Member files, most-connected first. */
1487
+ files: string[];
1488
+ size: number;
1489
+ topSymbols: ConventionSymbol[];
1490
+ /** Hash of the summarizer prompt, which is the summary-cache key. */
1491
+ contentHash: string;
1492
+ summary?: string;
1493
+ }
1494
+ interface ConventionCoverage {
1495
+ /** Non-generated indexed files in the snapshot. */
1496
+ files: number;
1497
+ /** Files inside a kept area; the rest are too small or isolated to summarize. */
1498
+ grouped: number;
1499
+ areas: number;
1500
+ summarized: number;
1501
+ }
1502
+ interface ConventionMap {
1503
+ model: string;
1504
+ coverage: ConventionCoverage;
1505
+ areas: ConventionArea[];
1506
+ }
1507
+ interface SummarizeConventionsResult extends ConventionMap {
1508
+ /** Cache misses sent to the summarizer on this call. */
1509
+ newlySummarized: number;
1510
+ /** Areas whose summary was already stored. */
1511
+ reused: number;
1512
+ }
1513
+ interface ConventionMatch {
1514
+ id: string;
1515
+ label: string;
1516
+ summary: string;
1517
+ files: string[];
1518
+ size: number;
1519
+ /** Cosine similarity of the query to the area summary, in [-1, 1]. */
1520
+ score: number;
1521
+ }
1522
+ interface ConventionQueryResult {
1523
+ query: string;
1524
+ model: string;
1525
+ embeddingModel: string;
1526
+ coverage: ConventionCoverage;
1527
+ matches: ConventionMatch[];
1528
+ }
1529
+ /** The cut level: how coarse the partition is and which areas are kept. */
1530
+ interface ConventionOptions {
1531
+ /** Coarse community count to merge down to; default scales with repo size. */
1532
+ targetCount?: number;
1533
+ /** Areas smaller than this stay ungrouped (not summarized). Default 3. */
1534
+ minSize?: number;
1535
+ }
1536
+ interface FindConventionsOptions extends ConventionOptions {
1537
+ /** Matches returned. Default 3. */
1538
+ limit?: number;
1539
+ }
1540
+ interface ConventionCorpus {
1541
+ areas: ConventionArea[];
1542
+ /** Summarizer prompt per area, keyed by the area's `contentHash`. */
1543
+ prompts: Map<string, string>;
1544
+ coverage: ConventionCoverage;
1545
+ }
1546
+
1547
+ /**
1548
+ * Greedy-modularity (Clauset-Newman-Moore) community detection over the
1549
+ * undirected file dependency graph. Deterministic: ties and labels resolve
1550
+ * by sorted node id, so the same graph always yields the same partition.
1551
+ *
1552
+ * Two optional controls shape the C-88 capability-altitude cut, in opposite
1553
+ * directions: `maxSize` skips any merge that would grow a community past it
1554
+ * (greedy CNM otherwise snowballs a dense graph into one giant blob — the
1555
+ * cap shapes formation instead of trying to split after the fact), and
1556
+ * `targetCount` keeps merging past the natural modularity stop (taking the
1557
+ * least-bad allowed merge) while more communities than that remain, so a
1558
+ * sparse fragmented graph still coarsens. Disconnected components can never
1559
+ * merge, so the floor is the component count.
1560
+ */
1561
+ declare function detectCommunities(fileIds: readonly string[], edges: readonly GraphEdge[], opts?: {
1562
+ targetCount?: number;
1563
+ maxSize?: number;
1564
+ }): Map<string, string[]>;
1565
+
1566
+ /** Coarse capability-altitude cut: about one area per 25 files, clamped to 6..40. */
1567
+ declare function defaultTargetCount(fileCount: number): number;
1568
+ /**
1569
+ * The deterministic half of the convention layer: partition the barrel-resolved
1570
+ * file graph into coarse areas and build each area's summarizer prompt and cache key.
1571
+ */
1572
+ declare function buildConventionAreas(store: CodeGraphStore, snapshotId: number, opts?: ConventionOptions): ConventionCorpus;
1573
+
1574
+ /** The blob_cache namespace for area summaries; the model column holds `summarizer.model`. */
1575
+ declare const COMMUNITY_SUMMARY_NAMESPACE = "code-graph/community-summary";
1576
+ /**
1577
+ * Generate or reuse the summary of every area. Only cache misses reach the
1578
+ * summarizer, so cost scales with what structurally changed, not with repo size.
1579
+ */
1580
+ declare function summarizeConventions(store: CodeGraphStore, snapshotId: number, summarizer: Summarizer, opts?: ConventionOptions): Promise<SummarizeConventionsResult>;
1581
+ /** Read-only view: the partition plus whatever summaries are already stored. Never calls a summarizer. */
1582
+ declare function getConventionMap(store: CodeGraphStore, snapshotId: number, model: string, opts?: ConventionOptions): ConventionMap;
1583
+
1584
+ /**
1585
+ * "How does this repo do X?": rank summarized areas by similarity of their
1586
+ * summary to the question. Returns candidates, not verdicts. Summary vectors
1587
+ * share the content-addressed embedding cache with the symbol layer.
1588
+ */
1589
+ declare function findConventions(store: CodeGraphStore, snapshotId: number, query: string, embedder: Embedder, model: string, opts?: FindConventionsOptions): Promise<ConventionQueryResult>;
1590
+
1591
+ export { ALIAS_BASE_ATTR, ALL_ROLES, type AliasChain, type AliasChainInput, type AliasChainOptions, type AliasLoader, type AliasResolution, COMMUNITY_SUMMARY_NAMESPACE, COVERAGE_METRIC_NAME, type CachedEmbedResult, type CheckDiff, type CheckResult, type CheckRule, type CheckRulesFile, type CheckSnapshotOptions, type CheckSnapshotResult, type CheckViolation, CodeGraphStore, type ConventionArea, type ConventionCorpus, type ConventionCoverage, type ConventionMap, type ConventionMatch, type ConventionOptions, type ConventionQueryResult, type ConventionSymbol, DEAD_CODE_METRIC_NAMES, DEFAULT_CHURN_WINDOWS, DOMAIN_DDL, type DeepAst, type DeepAstInput, type DiffCheckResultsOptions, type DiffSnapshotsOptions, type EdgeKind, type EmbedAttempt, type EmbedCoverage, type EmbedSnapshotResult, type EmbeddableSymbol, type ExternalDiagnostic, type FileFingerprint, type FindConventionsOptions, type FindSimilarOptions, type Finding, type ForbidImportRule, GROWTH_RISK_METRIC_NAMES, type GraphDiff, type GraphDiffSummary, type GraphEdge, type GraphFragment, type GraphMetric, type GraphNode, type HistoryMetricsOptions, INDEX_VERSION, type IdAlias, type IdAliasReason, type IndexOptions, type IndexResult, type IstanbulCoverage, KIT, LanguageExtractor, type LanguageExtractorOptions, type LayeredDepsRule, type LineSpan, type Lineage, type LineageSnapshot, type LineageStep, type LinkMethod, type LinkTestsOptions, type LoadedHistory, METRIC_CATALOGUE, MIGRATIONS, type MemberInfo, type MetricAbsence, type MetricAggregate, type MetricDelta, type MetricDescriptor, type MetricDirection, type MetricMaxRule, type MetricMinRule, type MetricProductMaxRule, type MetricRollup, type MetricSource, type MetricUnit, type NoInternalOnlyBarrelsRule, type NodeKind, type NodeRename, type NodeRole, type PackageFlag, type PackageLayer, type PackageStats, type PageRankOptions, type PageRankResult, type PageRankRow, type PairCoupling, type PairFlag, type ParamInfo, type PartitionQualityInput, type PartitionQualityResult, type PriorSnapshotOptions, type PruneOptions, type PrunePlan, type PruneResult, PythonGraphExtractor, type ReadFile, type ReferenceEdgeLite, type RelevanceOptions, type ResolveAliasOptions, type ReuseBasis, type RuleStore, type RunChecksOptions, SCHEMA_VERSION, SNAPSHOT_SCOPED_TABLES, SOURCE_METRIC_NAMES, SYMBOL_EMBEDDING_NAMESPACE, SYMBOL_ID_SEP, type Severity, type SimilarCandidate, type SimilarResult, type SnapshotInsert, type SnapshotRow, type SnapshotSpec, type SourceLanguage, type SummarizeConventionsResult, type Summarizer, type SymbolConsumers, type SymbolCouplingOptions, type SymbolCouplingPair, type SymbolSpan, type TestCoverageOwnershipOptions, type TestSourceLink, type TopMetricRow, TsMorphGraphExtractor, type TsMorphGraphExtractorOptions, type UnchangedViolation, type ValidateRulesOptions, aggregateMetrics, aliasChain, annotateRoles, attributeCoverage, buildAliases, buildConventionAreas, buildEmbedText, buildFileModuleNodes, buildIndexerMetrics, buildLineage, canonicalEdgeKind, canonicalMetricName, canonicalRole, checkSnapshot, classifyRole, collectDeclaredNames, collectDeclaredSpans, compilePatterns, computeDeadCodeMetrics, computeDeepAst, computeGrowthRiskMetrics, computeMetrics, computePageRank, computePartitionQuality, computeRecencyWindows, computeRelevance, computeRoleHints, computeSourceMetrics, computeSymbolConsumers, computeSymbolCoupling, computeTestCoverageOwnership, createAliasChain, defaultTargetCount, describeMetric, describeMetrics, detectCommunities, detectGitHead, detectGitToplevel, detectRenames, diffCheckResults, diffSnapshots, edgeWeight, embedSnapshot, embedTextsCached, externalId, externalToFinding, fileId, findConventions, findSimilarCapability, getConventionMap, getEdgeWeight, groupTestsBySource, hashContent, hashEmbedText, indexPaths, invertBuckets, isGeneratedByHeuristic, isGeneratedFile, isInsideGitRepo, lineagePath, linkTestsToSources, listEdges, listEdgesTouching, listEmbeddableSymbols, listMetrics, listMetricsForNode, listNodes, loadCheckRules, loadGeneratedPatterns, loadHistoryMetrics, loadLineage, matchesAny, moduleId, openCodeGraph, packageId, parentModuleId, parseSymbolId, patternToRegex, planPrune, priorSnapshotForRef, pruneDanglingReferences, readSourceFiles, rebasedViolationKey, resolveAlias, resolveBarrelEdges, resolveChurnWindows, resolveGitRef, resolveSnapshot, runChecks, runPrune, snapshotPageRank, snapshotReferenceEdges, snapshotRelevance, snapshotSymbolConsumers, snapshotSymbolCoupling, snapshotViolations, structuralSignature, summarizeConventions, symbolId, testCoverageCountMetrics, toFindings, tryEmbedSnapshot, validateRules, violationKey, walkSourceFiles, windowSuffix };