@titan-design/code-read 0.1.3 → 0.1.4

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/dist/index.js CHANGED
@@ -124,6 +124,8 @@ function describeRule(rule) {
124
124
  return `${rule.metric} must be at least ${rule.min}${scopeSuffix(rule.kind, rule.excludeRoles)}.`;
125
125
  case "metric-product-max":
126
126
  return `The product ${rule.metrics.join(" * ")} must be at most ${rule.max}${scopeSuffix(rule.kind, rule.excludeRoles)}.`;
127
+ case "metric-outlier":
128
+ return `${rule.metric} must not exceed its ${rule.percentile}th percentile over every ${rule.kind} in the snapshot.`;
127
129
  case "forbid-import":
128
130
  return `Files matching ${rule.from} must not import ${rule.to}.`;
129
131
  case "layered-deps":
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/lru.ts","../src/source-reader.ts","../src/rule-text.ts","../src/live-source.ts","../src/findings-live.ts","../src/register.ts"],"sourcesContent":["/** A least-recently-used cache; Map iteration order is insertion order, so the first key is the eldest. */\nexport class LruCache<K, V> {\n private readonly entries = new Map<K, V>();\n\n constructor(readonly capacity: number) {\n if (!Number.isInteger(capacity) || capacity < 1) throw new RangeError(`LRU capacity must be a positive integer, got ${capacity}`);\n }\n\n get size(): number {\n return this.entries.size;\n }\n\n has(key: K): boolean {\n return this.entries.has(key);\n }\n\n /** The cached value, refreshed to most recent; otherwise `load`'s value, evicting the eldest when full. */\n getOrLoad(key: K, load: () => V): V {\n if (this.entries.has(key)) {\n const value = this.entries.get(key)!;\n this.entries.delete(key);\n this.entries.set(key, value);\n return value;\n }\n const value = load();\n this.entries.set(key, value);\n if (this.entries.size > this.capacity) this.entries.delete(this.entries.keys().next().value!);\n return value;\n }\n\n clear(): void {\n this.entries.clear();\n }\n\n keys(): K[] {\n return [...this.entries.keys()];\n }\n}\n","import { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { hashContent, type CodeGraphStore } from \"@titan-design/code-graph\";\nimport { LruCache } from \"./lru.js\";\nimport type { SourceRead } from \"./query/source.js\";\n\nexport type FingerprintStore = Pick<CodeGraphStore, \"listFingerprints\">;\nexport type SourceReader = (snapshotId: number, fileId: string) => SourceRead;\n\n/** Split into lines without inventing an empty last line for a trailing newline. */\nexport function splitLines(text: string): string[] {\n if (text === \"\") return [];\n const lines = text.split(/\\r?\\n/);\n if (lines.at(-1) === \"\") lines.pop();\n return lines;\n}\n\nfunction readText(file: string): string | null {\n try {\n return readFileSync(file, \"utf8\");\n } catch {\n return null;\n }\n}\n\n/**\n * Reads files from the working tree and serves them only when their hash matches the snapshot's fingerprint,\n * so line numbers never disagree with the snapshot the finding came from.\n */\nexport function createWorktreeReader(store: FingerprintStore, repoRoot: string, cacheSize = 3): SourceReader {\n const hashes = new LruCache<number, Map<string, string>>(cacheSize);\n const expected = (snapshotId: number): Map<string, string> =>\n hashes.getOrLoad(snapshotId, () => new Map(store.listFingerprints(snapshotId).map((f) => [f.fileId, f.contentHash])));\n return (snapshotId, fileId) => {\n const want = expected(snapshotId).get(fileId);\n if (want === undefined) return { unavailable: \"not-a-source-file\" };\n const text = readText(path.join(repoRoot, fileId));\n if (text === null) return { unavailable: \"unreadable\" };\n const contentHash = hashContent(text);\n if (contentHash !== want) return { unavailable: \"changed-since-snapshot\" };\n const lines = splitLines(text);\n return { path: fileId, contentHash, origin: \"worktree\", startLine: 1, lines, lineCount: lines.length };\n };\n}\n","import type { CheckRule } from \"@titan-design/code-graph\";\n\nfunction scopeSuffix(kind: string | undefined, excludeRoles: readonly string[] | undefined): string {\n const on = kind ? ` on each ${kind}` : \"\";\n const except = excludeRoles && excludeRoles.length > 0 ? `, except ${excludeRoles.join(\" and \")} files` : \"\";\n return `${on}${except}`;\n}\n\n/** A plain-language statement of what a check rule demands, for a finding's `why`. */\nexport function describeRule(rule: CheckRule): string {\n switch (rule.type) {\n case \"metric-max\":\n return `${rule.metric} must be at most ${rule.max}${scopeSuffix(rule.kind, rule.excludeRoles)}.`;\n case \"metric-min\":\n return `${rule.metric} must be at least ${rule.min}${scopeSuffix(rule.kind, rule.excludeRoles)}.`;\n case \"metric-product-max\":\n return `The product ${rule.metrics.join(\" * \")} must be at most ${rule.max}${scopeSuffix(rule.kind, rule.excludeRoles)}.`;\n case \"forbid-import\":\n return `Files matching ${rule.from} must not import ${rule.to}.`;\n case \"layered-deps\":\n return `An import may point only to its own layer or a lower one, across ${rule.layers.length} layers.`;\n case \"no-internal-only-barrels\":\n return \"A barrel file must be imported from outside its own package, or it should not exist.\";\n }\n}\n","import { describeMetric, type CheckRule, type CodeGraphStore, type SnapshotRow } from \"@titan-design/code-graph\";\nimport { deriveFindings, toModelRules } from \"./findings-live.js\";\nimport { LruCache } from \"./lru.js\";\nimport { COMMAND_NAMES } from \"./query/contract.js\";\nimport { buildReadModel, type ModelNode, type ReadModel } from \"./query/model.js\";\nimport type { Capabilities, SnapshotInfo } from \"./query/schemas.js\";\nimport { snapshotNotFound, type ReadSource, type SourceFacts } from \"./query/source.js\";\nimport { createWorktreeReader, type SourceReader } from \"./source-reader.js\";\n\n/** The store reads the live source needs; structural, so any compatible code-graph release fits. */\nexport type SnapshotStore = Pick<\n CodeGraphStore,\n \"getSnapshot\" | \"listSnapshots\" | \"listNodes\" | \"listEdges\" | \"listMetrics\" | \"listAliases\" | \"listFingerprints\"\n>;\n\nexport interface CodeReadDeps {\n /** Called once, on first use; the source holds the store for its lifetime. */\n openStore: () => SnapshotStore;\n /**\n * The product's check rules: reported by `api.describe` and run on each snapshot to derive findings. Policy, so the\n * package has no default set. Return the same array while the rules are unchanged; a new array drops every cached model.\n */\n rules?: () => readonly CheckRule[];\n /** The git toplevel the snapshots were indexed from; excerpts are served only where the file still matches its snapshot. */\n repoRoot?: string | null;\n /** Snapshot models kept in memory; each holds a whole snapshot. Defaults to 3. */\n cacheSize?: number;\n}\n\nexport const DEFAULT_MODEL_CACHE_SIZE = 3;\n\n// Findings are derived from check rules; the stored findings, verdicts, themes, and feedback arrive with G11.\nfunction liveCapabilities(hasSource: boolean): Capabilities {\n return {\n findings: \"check-rules\",\n verdicts: false,\n themes: false,\n feedback: false,\n embeddings: null,\n cochange: false,\n sourceAtCommit: false,\n excerpts: hasSource ? \"any\" : \"none\",\n };\n}\n\n// Far above any real snapshot count; the store's own default of 50 would hide older snapshots.\nconst ALL_SNAPSHOTS = 1_000_000;\n\nexport function toSnapshotInfo(row: SnapshotRow): SnapshotInfo {\n return { id: row.id, ref: row.ref, commit: row.commitHash, takenAt: row.takenAt, indexVersion: row.indexVersion };\n}\n\nfunction toModelNode(node: ReturnType<SnapshotStore[\"listNodes\"]>[number]): ModelNode {\n const out: ModelNode = { id: node.id, kind: node.kind, name: node.name, parentId: node.parentId ?? null, attrs: node.attrs ?? {} };\n if (node.language !== undefined) out.language = node.language;\n if (node.role !== undefined) out.role = node.role;\n return out;\n}\n\n/** How a model derives its findings: the rules to run and, optionally, a reader that locates flagged lines. */\nexport interface FindingOptions {\n rules: readonly CheckRule[];\n read?: SourceReader;\n}\n\n/** Read one whole snapshot, symbols and reference edges included, into a `ReadModel`, with findings when rules are given. */\nexport function loadReadModel(store: SnapshotStore, snapshotId: number, findings?: FindingOptions): ReadModel {\n const row = store.getSnapshot(snapshotId);\n if (!row) throw snapshotNotFound(snapshotId);\n const edges = store.listEdges(snapshotId, { includeReferences: true }).map((e) => ({ ...e, attrs: e.attrs ?? {} }));\n const rules = findings?.rules ?? [];\n return buildReadModel({\n snapshot: toSnapshotInfo(row),\n nodes: store.listNodes(snapshotId, { includeSymbols: true }).map(toModelNode),\n edges,\n aliases: store.listAliases(snapshotId),\n metrics: store.listMetrics(snapshotId),\n describe: describeMetric,\n findings: deriveFindings(store, rules, { snapshotId, edges, read: findings?.read }),\n rules: toModelRules(rules),\n });\n}\n\nexport interface LiveSource extends ReadSource {\n /** Snapshot ids currently cached, eldest first. */\n cachedSnapshots(): number[];\n}\n\n/** A `ReadSource` over a code-graph store; snapshots are immutable, so models are cached by id alone. */\nconst NO_RULES: readonly CheckRule[] = [];\n\ninterface RuleTrackedModels {\n models: LruCache<number, ReadModel>;\n /** The product's current rules; a different array than last time empties the model cache. */\n rules(): readonly CheckRule[];\n}\n\nfunction ruleTrackedModels(deps: CodeReadDeps): RuleTrackedModels {\n const models = new LruCache<number, ReadModel>(deps.cacheSize ?? DEFAULT_MODEL_CACHE_SIZE);\n let seen = NO_RULES;\n const rules = (): readonly CheckRule[] => {\n const current = deps.rules?.() ?? NO_RULES;\n if (current !== seen) {\n models.clear();\n seen = current;\n }\n return current;\n };\n return { models, rules };\n}\n\n/** A `ReadSource` over a code-graph store; snapshots are immutable, so models are cached by id until the rules change. */\nexport function createLiveSource(deps: CodeReadDeps): LiveSource {\n let store: SnapshotStore | undefined;\n let reader: SourceReader | undefined;\n const openOnce = (): SnapshotStore => (store ??= deps.openStore());\n const root = deps.repoRoot ?? null;\n const read: SourceReader | undefined =\n root === null ? undefined : (id, file) => (reader ??= createWorktreeReader(openOnce(), root))(id, file);\n const { models, rules } = ruleTrackedModels(deps);\n const facts = (): SourceFacts => ({\n dataset: \"live\",\n commands: COMMAND_NAMES,\n capabilities: liveCapabilities(read !== undefined),\n rules: rules().map((r) => ({ id: r.id, type: r.type, severity: r.severity ?? \"error\" })),\n });\n return {\n facts,\n snapshots: () => openOnce().listSnapshots({ limit: ALL_SNAPSHOTS }).map(toSnapshotInfo),\n model: (snapshotId) => {\n const current = rules();\n return models.getOrLoad(snapshotId, () => loadReadModel(openOnce(), snapshotId, { rules: current, read }));\n },\n ...(read ? { readSource: read } : {}),\n cachedSnapshots: () => models.keys(),\n };\n}\n","import { snapshotViolations, violationKey, type CheckRule, type CheckViolation, type RuleStore } from \"@titan-design/code-graph\";\nimport type { ModelEdge, ModelFinding, ModelRule } from \"./query/model.js\";\nimport type { Span } from \"./query/schemas.js\";\nimport { describeRule } from \"./rule-text.js\";\nimport type { SourceReader } from \"./source-reader.js\";\n\nconst IMPORT_KINDS = new Set([\"imports\", \"re-exports\"]);\nconst QUOTES = ['\"', \"'\", \"`\"];\n\nexport function toModelRules(rules: readonly CheckRule[]): ModelRule[] {\n return rules.map((r) => ({ id: r.id, type: r.type, severity: r.severity ?? \"error\", text: describeRule(r) }));\n}\n\nconst pairKey = (srcId: string, dstId: string): string => `${srcId}\\n${dstId}`;\n\nfunction specifiersOf(edges: readonly ModelEdge[]): Map<string, string> {\n const out = new Map<string, string>();\n for (const e of edges) {\n if (IMPORT_KINDS.has(e.kind) && typeof e.attrs.specifier === \"string\") out.set(pairKey(e.srcId, e.dstId), e.attrs.specifier);\n }\n return out;\n}\n\n/** Lines naming the specifier in quotes: the import statement's last line, where `from \"x\"` sits. */\nexport function specifierLines(lines: readonly string[], specifier: string): Span[] {\n const quoted = QUOTES.map((q) => `${q}${specifier}${q}`);\n const out: Span[] = [];\n lines.forEach((line, i) => {\n if (quoted.some((q) => line.includes(q))) out.push({ startLine: i + 1, endLine: i + 1 });\n });\n return out;\n}\n\nexport interface FindingInputs {\n snapshotId: number;\n edges: readonly ModelEdge[];\n read?: SourceReader;\n}\n\ninterface Located extends FindingInputs {\n specifiers: ReadonlyMap<string, string>;\n}\n\n// Edges carry no line numbers, so an import finding's range comes from the source text, when it matches the snapshot.\nfunction importRanges(v: CheckViolation, inputs: Located): Span[] | undefined {\n if (!v.destinationId || !inputs.read) return undefined;\n const specifier = inputs.specifiers.get(pairKey(v.nodeId, v.destinationId));\n if (specifier === undefined) return undefined;\n const source = inputs.read(inputs.snapshotId, v.nodeId);\n if (\"unavailable\" in source) return undefined;\n const ranges = specifierLines(source.lines, specifier);\n return ranges.length > 0 ? ranges : undefined;\n}\n\nfunction toModelFinding(v: CheckViolation, inputs: Located): ModelFinding {\n const f: ModelFinding = { id: violationKey(v), rule: v.ruleId, severity: v.severity, nodeId: v.nodeId, message: v.message };\n if (v.destinationId !== undefined) f.destinationId = v.destinationId;\n if (v.metric !== undefined) f.metric = v.metric;\n if (v.value !== undefined) f.value = v.value;\n if (v.threshold !== undefined) f.threshold = v.threshold;\n const ranges = importRanges(v, inputs);\n if (ranges) f.ranges = ranges;\n return f;\n}\n\n/** The snapshot's check-rule violations as findings, computed by the same engine and keyed as the ratchet keys them. */\nexport function deriveFindings(store: RuleStore, rules: readonly CheckRule[], inputs: FindingInputs): ModelFinding[] {\n if (rules.length === 0) return [];\n const violations = snapshotViolations(store, inputs.snapshotId, rules);\n const located: Located = { ...inputs, specifiers: specifiersOf(inputs.edges) };\n return violations.map((v) => toModelFinding(v, located));\n}\n","import { defineCommand, type AnyCommand, type BaseContext, type CliMeta, type CommandRegistry } from \"@titan-design/registry\";\nimport { createLiveSource, type CodeReadDeps, type LiveSource } from \"./live-source.js\";\nimport { QUERIES, type QueryFn } from \"./query/commands.js\";\nimport { COMMAND_NAMES, CONTRACT, type CommandName } from \"./query/contract.js\";\nimport type { ReadSource } from \"./query/source.js\";\n\nconst CLI: Partial<Record<CommandName, CliMeta>> = {\n \"snapshot.list\": {\n options: {\n ref: { long: \"--ref\", description: \"Only snapshots of this ref\" },\n limit: { long: \"--limit\", description: \"Most snapshots to return (default 50, max 500)\" },\n },\n },\n \"hierarchy.get\": {\n options: {\n snapshot: { long: \"--snapshot\", description: \"Snapshot id or ref name (default: newest)\" },\n root: { long: \"--root\", description: \"Node id to start from; a directory id ends in / (default: the repo)\" },\n depth: { long: \"--depth\", description: \"Levels below the root (default 2, max 8)\" },\n metrics: { long: \"--metric\", description: \"Metric to report; repeatable (default loc)\" },\n baseline: { long: \"--baseline\", description: \"Snapshot id or ref to compute deltas against\" },\n include_symbols: { long: \"--include-symbols\", description: \"Descend into symbols below files\" },\n exclude_roles: { long: \"--exclude-role\", description: \"Leave out files with this role; repeatable\" },\n },\n },\n \"node.get\": {\n positional: [\"id\"],\n options: {\n snapshot: { long: \"--snapshot\", description: \"Snapshot id or ref name (default: newest)\" },\n baseline: { long: \"--baseline\", description: \"Snapshot id or ref to compute deltas against\" },\n metrics: { long: \"--metric\", description: \"Metric to report; repeatable (default: every one that applies)\" },\n },\n },\n \"node.resolve\": {\n positional: [\"query\"],\n options: {\n snapshot: { long: \"--snapshot\", description: \"Snapshot id or ref name (default: newest)\" },\n path: { long: \"--path\", description: \"A file path instead of a query\" },\n line: { long: \"--line\", description: \"With --path: resolve to the innermost symbol containing this line\" },\n limit: { long: \"--limit\", description: \"Most candidates (default 10, max 50)\" },\n },\n },\n \"findings.list\": {\n options: {\n snapshot: { long: \"--snapshot\", description: \"Snapshot id or ref name (default: newest)\" },\n baseline: { long: \"--baseline\", description: \"Snapshot id or ref to give each finding a status against\" },\n scope: { long: \"--scope\", description: \"Node id; only findings on it or under it\" },\n rule: { long: \"--rule\", description: \"Only this rule; repeatable\" },\n severity: { long: \"--severity\", description: \"Only this severity; repeatable\" },\n tool: { long: \"--tool\", description: \"Only this tool; repeatable\" },\n provenance: { long: \"--provenance\", description: \"Only measured, derived, or model findings; repeatable\" },\n kind: { long: \"--kind\", description: \"Only findings on nodes of this kind; repeatable\" },\n status: { long: \"--status\", description: \"With --baseline: new, carryover, resolved, worsened, or improved; repeatable\" },\n sort: { long: \"--sort\", description: \"severity (default), excess, value, path, or rule\" },\n order: { long: \"--order\", description: \"desc (default) or asc; flips the sort key only\" },\n offset: { long: \"--offset\", description: \"Rows to skip (default 0)\" },\n limit: { long: \"--limit\", description: \"Rows to return (default 20, max 500; 0 for counts only)\" },\n facets: { long: \"--facets\", description: \"Also count rows per rule, severity, tool, provenance, kind, and child\" },\n },\n },\n \"finding.get\": {\n positional: [\"id\"],\n options: {\n snapshot: { long: \"--snapshot\", description: \"Snapshot id or ref name (default: newest)\" },\n baseline: { long: \"--baseline\", description: \"Snapshot id or ref for status and the baseline value\" },\n context_lines: { long: \"--context-lines\", description: \"Lines of context either side of the flagged range (default 5, max 20)\" },\n },\n },\n \"node.neighbors\": {\n positional: [\"id\"],\n options: {\n snapshot: { long: \"--snapshot\", description: \"Snapshot id or ref name (default: newest)\" },\n direction: { long: \"--direction\", description: \"in, out, or both (default)\" },\n edge_kinds: { long: \"--edge-kind\", description: \"Only this edge kind; repeatable (default: all but references, except for a symbol)\" },\n metrics: { long: \"--metric\", description: \"Neighbour metric to report; repeatable (default loc and utilization)\" },\n offset: { long: \"--offset\", description: \"Edges to skip on each side (default 0)\" },\n limit: { long: \"--limit\", description: \"Edges per side (default 20, max 100)\" },\n },\n },\n};\n\nfunction defineOne<Ctx extends BaseContext>(source: ReadSource, name: CommandName): AnyCommand<Ctx> {\n const { description, args, result } = CONTRACT[name];\n const query = QUERIES[name] as QueryFn<CommandName>;\n return defineCommand<unknown, unknown, Ctx>({\n name,\n description,\n args,\n result,\n cli: CLI[name],\n run: (parsed) => Promise.resolve(query(source, parsed as never)),\n });\n}\n\n/**\n * One registry command per contract entry, answering from `source`. Exposed so a product can\n * register a subset on a second registry, for example an MCP-only one, until the registry filters surfaces.\n */\nexport function defineCodeReadCommands<Ctx extends BaseContext = BaseContext>(source: ReadSource): AnyCommand<Ctx>[] {\n return COMMAND_NAMES.map((name) => defineOne<Ctx>(source, name));\n}\n\n/** Register every read command on a product's registry over a live store; returns the source for inspection. */\nexport function registerCodeReadCommands<Ctx extends BaseContext>(\n registry: CommandRegistry<Ctx>,\n deps: CodeReadDeps,\n): LiveSource {\n const source = createLiveSource(deps);\n for (const cmd of defineCodeReadCommands<Ctx>(source)) registry.register(cmd);\n return source;\n}\n"],"mappings":"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","names":["value"]}
1
+ {"version":3,"sources":["../src/lru.ts","../src/source-reader.ts","../src/rule-text.ts","../src/live-source.ts","../src/findings-live.ts","../src/register.ts"],"sourcesContent":["/** A least-recently-used cache; Map iteration order is insertion order, so the first key is the eldest. */\nexport class LruCache<K, V> {\n private readonly entries = new Map<K, V>();\n\n constructor(readonly capacity: number) {\n if (!Number.isInteger(capacity) || capacity < 1) throw new RangeError(`LRU capacity must be a positive integer, got ${capacity}`);\n }\n\n get size(): number {\n return this.entries.size;\n }\n\n has(key: K): boolean {\n return this.entries.has(key);\n }\n\n /** The cached value, refreshed to most recent; otherwise `load`'s value, evicting the eldest when full. */\n getOrLoad(key: K, load: () => V): V {\n if (this.entries.has(key)) {\n const value = this.entries.get(key)!;\n this.entries.delete(key);\n this.entries.set(key, value);\n return value;\n }\n const value = load();\n this.entries.set(key, value);\n if (this.entries.size > this.capacity) this.entries.delete(this.entries.keys().next().value!);\n return value;\n }\n\n clear(): void {\n this.entries.clear();\n }\n\n keys(): K[] {\n return [...this.entries.keys()];\n }\n}\n","import { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { hashContent, type CodeGraphStore } from \"@titan-design/code-graph\";\nimport { LruCache } from \"./lru.js\";\nimport type { SourceRead } from \"./query/source.js\";\n\nexport type FingerprintStore = Pick<CodeGraphStore, \"listFingerprints\">;\nexport type SourceReader = (snapshotId: number, fileId: string) => SourceRead;\n\n/** Split into lines without inventing an empty last line for a trailing newline. */\nexport function splitLines(text: string): string[] {\n if (text === \"\") return [];\n const lines = text.split(/\\r?\\n/);\n if (lines.at(-1) === \"\") lines.pop();\n return lines;\n}\n\nfunction readText(file: string): string | null {\n try {\n return readFileSync(file, \"utf8\");\n } catch {\n return null;\n }\n}\n\n/**\n * Reads files from the working tree and serves them only when their hash matches the snapshot's fingerprint,\n * so line numbers never disagree with the snapshot the finding came from.\n */\nexport function createWorktreeReader(store: FingerprintStore, repoRoot: string, cacheSize = 3): SourceReader {\n const hashes = new LruCache<number, Map<string, string>>(cacheSize);\n const expected = (snapshotId: number): Map<string, string> =>\n hashes.getOrLoad(snapshotId, () => new Map(store.listFingerprints(snapshotId).map((f) => [f.fileId, f.contentHash])));\n return (snapshotId, fileId) => {\n const want = expected(snapshotId).get(fileId);\n if (want === undefined) return { unavailable: \"not-a-source-file\" };\n const text = readText(path.join(repoRoot, fileId));\n if (text === null) return { unavailable: \"unreadable\" };\n const contentHash = hashContent(text);\n if (contentHash !== want) return { unavailable: \"changed-since-snapshot\" };\n const lines = splitLines(text);\n return { path: fileId, contentHash, origin: \"worktree\", startLine: 1, lines, lineCount: lines.length };\n };\n}\n","import type { CheckRule } from \"@titan-design/code-graph\";\n\nfunction scopeSuffix(kind: string | undefined, excludeRoles: readonly string[] | undefined): string {\n const on = kind ? ` on each ${kind}` : \"\";\n const except = excludeRoles && excludeRoles.length > 0 ? `, except ${excludeRoles.join(\" and \")} files` : \"\";\n return `${on}${except}`;\n}\n\n/** A plain-language statement of what a check rule demands, for a finding's `why`. */\nexport function describeRule(rule: CheckRule): string {\n switch (rule.type) {\n case \"metric-max\":\n return `${rule.metric} must be at most ${rule.max}${scopeSuffix(rule.kind, rule.excludeRoles)}.`;\n case \"metric-min\":\n return `${rule.metric} must be at least ${rule.min}${scopeSuffix(rule.kind, rule.excludeRoles)}.`;\n case \"metric-product-max\":\n return `The product ${rule.metrics.join(\" * \")} must be at most ${rule.max}${scopeSuffix(rule.kind, rule.excludeRoles)}.`;\n case \"metric-outlier\":\n return `${rule.metric} must not exceed its ${rule.percentile}th percentile over every ${rule.kind} in the snapshot.`;\n case \"forbid-import\":\n return `Files matching ${rule.from} must not import ${rule.to}.`;\n case \"layered-deps\":\n return `An import may point only to its own layer or a lower one, across ${rule.layers.length} layers.`;\n case \"no-internal-only-barrels\":\n return \"A barrel file must be imported from outside its own package, or it should not exist.\";\n }\n}\n","import { describeMetric, type CheckRule, type CodeGraphStore, type SnapshotRow } from \"@titan-design/code-graph\";\nimport { deriveFindings, toModelRules } from \"./findings-live.js\";\nimport { LruCache } from \"./lru.js\";\nimport { COMMAND_NAMES } from \"./query/contract.js\";\nimport { buildReadModel, type ModelNode, type ReadModel } from \"./query/model.js\";\nimport type { Capabilities, SnapshotInfo } from \"./query/schemas.js\";\nimport { snapshotNotFound, type ReadSource, type SourceFacts } from \"./query/source.js\";\nimport { createWorktreeReader, type SourceReader } from \"./source-reader.js\";\n\n/** The store reads the live source needs; structural, so any compatible code-graph release fits. */\nexport type SnapshotStore = Pick<\n CodeGraphStore,\n \"getSnapshot\" | \"listSnapshots\" | \"listNodes\" | \"listEdges\" | \"listMetrics\" | \"listAliases\" | \"listFingerprints\"\n>;\n\nexport interface CodeReadDeps {\n /** Called once, on first use; the source holds the store for its lifetime. */\n openStore: () => SnapshotStore;\n /**\n * The product's check rules: reported by `api.describe` and run on each snapshot to derive findings. Policy, so the\n * package has no default set. Return the same array while the rules are unchanged; a new array drops every cached model.\n */\n rules?: () => readonly CheckRule[];\n /** The git toplevel the snapshots were indexed from; excerpts are served only where the file still matches its snapshot. */\n repoRoot?: string | null;\n /** Snapshot models kept in memory; each holds a whole snapshot. Defaults to 3. */\n cacheSize?: number;\n}\n\nexport const DEFAULT_MODEL_CACHE_SIZE = 3;\n\n// Findings are derived from check rules; the stored findings, verdicts, themes, and feedback arrive with G11.\nfunction liveCapabilities(hasSource: boolean): Capabilities {\n return {\n findings: \"check-rules\",\n verdicts: false,\n themes: false,\n feedback: false,\n embeddings: null,\n cochange: false,\n sourceAtCommit: false,\n excerpts: hasSource ? \"any\" : \"none\",\n };\n}\n\n// Far above any real snapshot count; the store's own default of 50 would hide older snapshots.\nconst ALL_SNAPSHOTS = 1_000_000;\n\nexport function toSnapshotInfo(row: SnapshotRow): SnapshotInfo {\n return { id: row.id, ref: row.ref, commit: row.commitHash, takenAt: row.takenAt, indexVersion: row.indexVersion };\n}\n\nfunction toModelNode(node: ReturnType<SnapshotStore[\"listNodes\"]>[number]): ModelNode {\n const out: ModelNode = { id: node.id, kind: node.kind, name: node.name, parentId: node.parentId ?? null, attrs: node.attrs ?? {} };\n if (node.language !== undefined) out.language = node.language;\n if (node.role !== undefined) out.role = node.role;\n return out;\n}\n\n/** How a model derives its findings: the rules to run and, optionally, a reader that locates flagged lines. */\nexport interface FindingOptions {\n rules: readonly CheckRule[];\n read?: SourceReader;\n}\n\n/** Read one whole snapshot, symbols and reference edges included, into a `ReadModel`, with findings when rules are given. */\nexport function loadReadModel(store: SnapshotStore, snapshotId: number, findings?: FindingOptions): ReadModel {\n const row = store.getSnapshot(snapshotId);\n if (!row) throw snapshotNotFound(snapshotId);\n const edges = store.listEdges(snapshotId, { includeReferences: true }).map((e) => ({ ...e, attrs: e.attrs ?? {} }));\n const rules = findings?.rules ?? [];\n return buildReadModel({\n snapshot: toSnapshotInfo(row),\n nodes: store.listNodes(snapshotId, { includeSymbols: true }).map(toModelNode),\n edges,\n aliases: store.listAliases(snapshotId),\n metrics: store.listMetrics(snapshotId),\n describe: describeMetric,\n findings: deriveFindings(store, rules, { snapshotId, edges, read: findings?.read }),\n rules: toModelRules(rules),\n });\n}\n\nexport interface LiveSource extends ReadSource {\n /** Snapshot ids currently cached, eldest first. */\n cachedSnapshots(): number[];\n}\n\n/** A `ReadSource` over a code-graph store; snapshots are immutable, so models are cached by id alone. */\nconst NO_RULES: readonly CheckRule[] = [];\n\ninterface RuleTrackedModels {\n models: LruCache<number, ReadModel>;\n /** The product's current rules; a different array than last time empties the model cache. */\n rules(): readonly CheckRule[];\n}\n\nfunction ruleTrackedModels(deps: CodeReadDeps): RuleTrackedModels {\n const models = new LruCache<number, ReadModel>(deps.cacheSize ?? DEFAULT_MODEL_CACHE_SIZE);\n let seen = NO_RULES;\n const rules = (): readonly CheckRule[] => {\n const current = deps.rules?.() ?? NO_RULES;\n if (current !== seen) {\n models.clear();\n seen = current;\n }\n return current;\n };\n return { models, rules };\n}\n\n/** A `ReadSource` over a code-graph store; snapshots are immutable, so models are cached by id until the rules change. */\nexport function createLiveSource(deps: CodeReadDeps): LiveSource {\n let store: SnapshotStore | undefined;\n let reader: SourceReader | undefined;\n const openOnce = (): SnapshotStore => (store ??= deps.openStore());\n const root = deps.repoRoot ?? null;\n const read: SourceReader | undefined =\n root === null ? undefined : (id, file) => (reader ??= createWorktreeReader(openOnce(), root))(id, file);\n const { models, rules } = ruleTrackedModels(deps);\n const facts = (): SourceFacts => ({\n dataset: \"live\",\n commands: COMMAND_NAMES,\n capabilities: liveCapabilities(read !== undefined),\n rules: rules().map((r) => ({ id: r.id, type: r.type, severity: r.severity ?? \"error\" })),\n });\n return {\n facts,\n snapshots: () => openOnce().listSnapshots({ limit: ALL_SNAPSHOTS }).map(toSnapshotInfo),\n model: (snapshotId) => {\n const current = rules();\n return models.getOrLoad(snapshotId, () => loadReadModel(openOnce(), snapshotId, { rules: current, read }));\n },\n ...(read ? { readSource: read } : {}),\n cachedSnapshots: () => models.keys(),\n };\n}\n","import { snapshotViolations, violationKey, type CheckRule, type CheckViolation, type RuleStore } from \"@titan-design/code-graph\";\nimport type { ModelEdge, ModelFinding, ModelRule } from \"./query/model.js\";\nimport type { Span } from \"./query/schemas.js\";\nimport { describeRule } from \"./rule-text.js\";\nimport type { SourceReader } from \"./source-reader.js\";\n\nconst IMPORT_KINDS = new Set([\"imports\", \"re-exports\"]);\nconst QUOTES = ['\"', \"'\", \"`\"];\n\nexport function toModelRules(rules: readonly CheckRule[]): ModelRule[] {\n return rules.map((r) => ({ id: r.id, type: r.type, severity: r.severity ?? \"error\", text: describeRule(r) }));\n}\n\nconst pairKey = (srcId: string, dstId: string): string => `${srcId}\\n${dstId}`;\n\nfunction specifiersOf(edges: readonly ModelEdge[]): Map<string, string> {\n const out = new Map<string, string>();\n for (const e of edges) {\n if (IMPORT_KINDS.has(e.kind) && typeof e.attrs.specifier === \"string\") out.set(pairKey(e.srcId, e.dstId), e.attrs.specifier);\n }\n return out;\n}\n\n/** Lines naming the specifier in quotes: the import statement's last line, where `from \"x\"` sits. */\nexport function specifierLines(lines: readonly string[], specifier: string): Span[] {\n const quoted = QUOTES.map((q) => `${q}${specifier}${q}`);\n const out: Span[] = [];\n lines.forEach((line, i) => {\n if (quoted.some((q) => line.includes(q))) out.push({ startLine: i + 1, endLine: i + 1 });\n });\n return out;\n}\n\nexport interface FindingInputs {\n snapshotId: number;\n edges: readonly ModelEdge[];\n read?: SourceReader;\n}\n\ninterface Located extends FindingInputs {\n specifiers: ReadonlyMap<string, string>;\n}\n\n// Edges carry no line numbers, so an import finding's range comes from the source text, when it matches the snapshot.\nfunction importRanges(v: CheckViolation, inputs: Located): Span[] | undefined {\n if (!v.destinationId || !inputs.read) return undefined;\n const specifier = inputs.specifiers.get(pairKey(v.nodeId, v.destinationId));\n if (specifier === undefined) return undefined;\n const source = inputs.read(inputs.snapshotId, v.nodeId);\n if (\"unavailable\" in source) return undefined;\n const ranges = specifierLines(source.lines, specifier);\n return ranges.length > 0 ? ranges : undefined;\n}\n\nfunction toModelFinding(v: CheckViolation, inputs: Located): ModelFinding {\n const f: ModelFinding = { id: violationKey(v), rule: v.ruleId, severity: v.severity, nodeId: v.nodeId, message: v.message };\n if (v.destinationId !== undefined) f.destinationId = v.destinationId;\n if (v.metric !== undefined) f.metric = v.metric;\n if (v.value !== undefined) f.value = v.value;\n if (v.threshold !== undefined) f.threshold = v.threshold;\n const ranges = importRanges(v, inputs);\n if (ranges) f.ranges = ranges;\n return f;\n}\n\n/** The snapshot's check-rule violations as findings, computed by the same engine and keyed as the ratchet keys them. */\nexport function deriveFindings(store: RuleStore, rules: readonly CheckRule[], inputs: FindingInputs): ModelFinding[] {\n if (rules.length === 0) return [];\n const violations = snapshotViolations(store, inputs.snapshotId, rules);\n const located: Located = { ...inputs, specifiers: specifiersOf(inputs.edges) };\n return violations.map((v) => toModelFinding(v, located));\n}\n","import { defineCommand, type AnyCommand, type BaseContext, type CliMeta, type CommandRegistry } from \"@titan-design/registry\";\nimport { createLiveSource, type CodeReadDeps, type LiveSource } from \"./live-source.js\";\nimport { QUERIES, type QueryFn } from \"./query/commands.js\";\nimport { COMMAND_NAMES, CONTRACT, type CommandName } from \"./query/contract.js\";\nimport type { ReadSource } from \"./query/source.js\";\n\nconst CLI: Partial<Record<CommandName, CliMeta>> = {\n \"snapshot.list\": {\n options: {\n ref: { long: \"--ref\", description: \"Only snapshots of this ref\" },\n limit: { long: \"--limit\", description: \"Most snapshots to return (default 50, max 500)\" },\n },\n },\n \"hierarchy.get\": {\n options: {\n snapshot: { long: \"--snapshot\", description: \"Snapshot id or ref name (default: newest)\" },\n root: { long: \"--root\", description: \"Node id to start from; a directory id ends in / (default: the repo)\" },\n depth: { long: \"--depth\", description: \"Levels below the root (default 2, max 8)\" },\n metrics: { long: \"--metric\", description: \"Metric to report; repeatable (default loc)\" },\n baseline: { long: \"--baseline\", description: \"Snapshot id or ref to compute deltas against\" },\n include_symbols: { long: \"--include-symbols\", description: \"Descend into symbols below files\" },\n exclude_roles: { long: \"--exclude-role\", description: \"Leave out files with this role; repeatable\" },\n },\n },\n \"node.get\": {\n positional: [\"id\"],\n options: {\n snapshot: { long: \"--snapshot\", description: \"Snapshot id or ref name (default: newest)\" },\n baseline: { long: \"--baseline\", description: \"Snapshot id or ref to compute deltas against\" },\n metrics: { long: \"--metric\", description: \"Metric to report; repeatable (default: every one that applies)\" },\n },\n },\n \"node.resolve\": {\n positional: [\"query\"],\n options: {\n snapshot: { long: \"--snapshot\", description: \"Snapshot id or ref name (default: newest)\" },\n path: { long: \"--path\", description: \"A file path instead of a query\" },\n line: { long: \"--line\", description: \"With --path: resolve to the innermost symbol containing this line\" },\n limit: { long: \"--limit\", description: \"Most candidates (default 10, max 50)\" },\n },\n },\n \"findings.list\": {\n options: {\n snapshot: { long: \"--snapshot\", description: \"Snapshot id or ref name (default: newest)\" },\n baseline: { long: \"--baseline\", description: \"Snapshot id or ref to give each finding a status against\" },\n scope: { long: \"--scope\", description: \"Node id; only findings on it or under it\" },\n rule: { long: \"--rule\", description: \"Only this rule; repeatable\" },\n severity: { long: \"--severity\", description: \"Only this severity; repeatable\" },\n tool: { long: \"--tool\", description: \"Only this tool; repeatable\" },\n provenance: { long: \"--provenance\", description: \"Only measured, derived, or model findings; repeatable\" },\n kind: { long: \"--kind\", description: \"Only findings on nodes of this kind; repeatable\" },\n status: { long: \"--status\", description: \"With --baseline: new, carryover, resolved, worsened, or improved; repeatable\" },\n sort: { long: \"--sort\", description: \"severity (default), excess, value, path, or rule\" },\n order: { long: \"--order\", description: \"desc (default) or asc; flips the sort key only\" },\n offset: { long: \"--offset\", description: \"Rows to skip (default 0)\" },\n limit: { long: \"--limit\", description: \"Rows to return (default 20, max 500; 0 for counts only)\" },\n facets: { long: \"--facets\", description: \"Also count rows per rule, severity, tool, provenance, kind, and child\" },\n },\n },\n \"finding.get\": {\n positional: [\"id\"],\n options: {\n snapshot: { long: \"--snapshot\", description: \"Snapshot id or ref name (default: newest)\" },\n baseline: { long: \"--baseline\", description: \"Snapshot id or ref for status and the baseline value\" },\n context_lines: { long: \"--context-lines\", description: \"Lines of context either side of the flagged range (default 5, max 20)\" },\n },\n },\n \"node.neighbors\": {\n positional: [\"id\"],\n options: {\n snapshot: { long: \"--snapshot\", description: \"Snapshot id or ref name (default: newest)\" },\n direction: { long: \"--direction\", description: \"in, out, or both (default)\" },\n edge_kinds: { long: \"--edge-kind\", description: \"Only this edge kind; repeatable (default: all but references, except for a symbol)\" },\n metrics: { long: \"--metric\", description: \"Neighbour metric to report; repeatable (default loc and utilization)\" },\n offset: { long: \"--offset\", description: \"Edges to skip on each side (default 0)\" },\n limit: { long: \"--limit\", description: \"Edges per side (default 20, max 100)\" },\n },\n },\n};\n\nfunction defineOne<Ctx extends BaseContext>(source: ReadSource, name: CommandName): AnyCommand<Ctx> {\n const { description, args, result } = CONTRACT[name];\n const query = QUERIES[name] as QueryFn<CommandName>;\n return defineCommand<unknown, unknown, Ctx>({\n name,\n description,\n args,\n result,\n cli: CLI[name],\n run: (parsed) => Promise.resolve(query(source, parsed as never)),\n });\n}\n\n/**\n * One registry command per contract entry, answering from `source`. Exposed so a product can\n * register a subset on a second registry, for example an MCP-only one, until the registry filters surfaces.\n */\nexport function defineCodeReadCommands<Ctx extends BaseContext = BaseContext>(source: ReadSource): AnyCommand<Ctx>[] {\n return COMMAND_NAMES.map((name) => defineOne<Ctx>(source, name));\n}\n\n/** Register every read command on a product's registry over a live store; returns the source for inspection. */\nexport function registerCodeReadCommands<Ctx extends BaseContext>(\n registry: CommandRegistry<Ctx>,\n deps: CodeReadDeps,\n): LiveSource {\n const source = createLiveSource(deps);\n for (const cmd of defineCodeReadCommands<Ctx>(source)) registry.register(cmd);\n return source;\n}\n"],"mappings":"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","names":["value"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@titan-design/code-read",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Versioned read API over code-graph snapshots: a contract, a per-snapshot ReadModel, browser-safe query functions, and registry commands",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -35,9 +35,9 @@
35
35
  "access": "public"
36
36
  },
37
37
  "dependencies": {
38
- "@titan-design/code-graph": "^0.6.0",
38
+ "@titan-design/registry": "^0.3.0",
39
39
  "@titan-design/rpc-protocol": "^0.1.0",
40
- "@titan-design/registry": "^0.3.0"
40
+ "@titan-design/code-graph": "^0.7.0"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "zod": "^4.0.0"