@ctxo/cli 0.11.5 → 0.13.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.
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ COMMUNITY_HISTORY_DIR,
4
+ INDEX_ARTIFACT_FILES,
5
+ JsonIndexReader
6
+ } from "./chunk-DT3ZH4AU.js";
7
+ import "./chunk-5DSKZJNN.js";
8
+ export {
9
+ COMMUNITY_HISTORY_DIR,
10
+ INDEX_ARTIFACT_FILES,
11
+ JsonIndexReader
12
+ };
13
+ //# sourceMappingURL=json-index-reader-OXTRTGE6.js.map
package/llms-full.txt CHANGED
@@ -1,15 +1,17 @@
1
1
  # Ctxo MCP Server — Full Reference
2
2
 
3
3
  > AI agents don't fail because they can't code. They fail because they code blind. Ctxo gives them the full picture before they write a single line.
4
- > npm: @ctxo/cli (v0.7.0-alpha.0) | GitHub: https://github.com/alperhankendi/Ctxo
4
+ > npm: @ctxo/cli | GitHub: https://github.com/alperhankendi/Ctxo
5
5
 
6
6
  ## Setup
7
7
 
8
8
  ```bash
9
9
  npm install -g @ctxo/cli # One-time global install (gives you the `ctxo` command)
10
- ctxo init # Detect languages + install plugins + wire MCP
10
+ ctxo init # Rules, skills, guard, MCP config, git hooks, plugins
11
11
  ctxo index # Build codebase index (required once)
12
- ctxo index --check # CI gate: fail if index stale
12
+ ctxo index --check # CI gate: fail if index stale (writes nothing, records no snapshot)
13
+ ctxo index --commit HEAD # Re-index the files one commit changed (what the post-commit hook runs)
14
+ ctxo index --files a.ts b.ts # Re-index only these files (alias: --file <path>)
13
15
  ctxo # Start MCP server (stdio transport)
14
16
  ```
15
17
 
@@ -22,21 +24,25 @@ MCP client configuration:
22
24
 
23
25
  ## Monorepo & Plugin Architecture (v0.7+)
24
26
 
25
- Ctxo is a pnpm workspace monorepo with six packages:
27
+ Ctxo is a pnpm workspace monorepo:
26
28
 
27
- - `@ctxo/cli` (packages/cli) — CLI entry, composition root, MCP server, tool handlers
28
- - `@ctxo/plugin-api` (packages/plugin-api) — `CtxoLanguagePlugin` v1 protocol interface
29
- - `@ctxo/lang-typescript` — ts-morph (full tier) plugin for TS/JS
30
- - `@ctxo/lang-go` — tree-sitter / gopls plugin for Go
31
- - `@ctxo/lang-csharp` — Roslyn plugin for C#
29
+ - `@ctxo/cli` (packages/cli) - CLI entry, composition root, MCP server, tool handlers
30
+ - `@ctxo/plugin-api` (packages/plugin-api) - `CtxoLanguagePlugin` protocol interface (v1 and v2)
31
+ - `@ctxo/lang-typescript` - ts-morph (full tier) plugin for TS/JS
32
+ - `@ctxo/lang-go` - tree-sitter / gopls plugin for Go
33
+ - `@ctxo/lang-csharp` - Roslyn plugin for C# (attribute annotations; SQL edges at known call sites when `@ctxo/lang-sql` is installed)
32
34
  - `@ctxo/lang-java` - tree-sitter Java plugin (syntax tier); full tier via `@ctxo/lang-java-analyzer` companion package (JRE 11+ required)
35
+ - `@ctxo/lang-sql` (packages/lang-sql) - T-SQL plugin for `.sql` files, protocol v2 (`ctxo install sql`, alias `tsql`)
36
+ - `@ctxo/sql-extract` (packages/sql-extract) - zero-dependency T-SQL tokenizer and recognizers; a library, not a plugin, never discovered
33
37
 
34
- Plugin protocol v1 (`CtxoLanguagePlugin`):
35
- - `apiVersion: 1`
38
+ Plugin protocol (`CtxoLanguagePlugin`):
39
+ - `apiVersion`: `'1'` or `'2'`, the lowest version the plugin works on. The CLI accepts both (`SUPPORTED_API_VERSIONS = ['1', '2']`) and handles their output identically; `'1'` stays accepted for at least 12 months after `'2'` ships. The official plugins declare `'1'`; `@ctxo/lang-sql` declares `'2'`.
36
40
  - `id`: unique plugin identifier
37
41
  - `extensions`: file extensions handled (e.g. `['.ts', '.tsx']`)
38
42
  - `tier`: `'syntax'` | `'full'`
39
43
  - `createAdapter(ctx)`: factory returning an `ILanguageAdapter` instance
44
+ - v2 additions (all additive): the data kinds, `SymbolNode.annotations`, optional `extractFacts`, and `ctx.capabilities` (`facts`, `sqlCallSites`). See "Plugin Protocol v2, Data Coupling and SQL" below.
45
+ - Dependencies: plugins import only from `@ctxo/plugin-api`; the one exception is `@ctxo/sql-extract` (ADR-017).
40
46
 
41
47
  Discovery: at startup Ctxo scans the project's `package.json` dependencies for names matching `@ctxo/lang-*` or `ctxo-lang-*` and registers them. `ctxo install` adds plugins; `ctxo index --install-missing` auto-installs plugins required by detected languages. New CLI: `ctxo doctor --fix`, `ctxo version`, and `ctxo --version --verbose|--json` for plugin/runtime diagnostics.
42
48
 
@@ -55,6 +61,8 @@ Parameters:
55
61
  - level (number 1-4, default 3): L1=signature, L2=direct deps, L3=full closure, L4=with token budget
56
62
  - intent (string, optional): Filter dependencies by keyword
57
63
 
64
+ Nodes carry their `annotations` (C# attributes, SQL table `columns`) at L4 only; L1-L3 omit them (and nodes from the SQLite fallback have none). A 2-part data ID such as `dbo.usp_closeloan::procedure` walks the body of the winning definition; a copy's full ID walks that copy's own body.
65
+
58
66
  When to use: Understanding what a symbol depends on (downstream view).
59
67
  Instead of: get_blast_radius (upstream/impact), get_context_for_task (task-specific).
60
68
 
@@ -62,14 +70,16 @@ Instead of: get_blast_radius (upstream/impact), get_context_for_task (task-speci
62
70
  Find all symbols that would break if a target changes.
63
71
 
64
72
  Parameters:
65
- - symbolId (string, required): Symbol ID
73
+ - symbolId (string, required): Symbol ID, or the 2-part ID of a table, view or procedure (`dbo.loans::table`)
66
74
  - confidence (enum: confirmed|likely|potential, optional): Filter by tier
67
75
  - intent (string, optional): Filter by keyword
68
76
 
69
- Returns: impactedSymbols array with symbolId, depth, riskScore, confidence, edgeKinds.
70
- 3-tier model: confirmed (calls/extends/implements), likely (uses/co-changed), potential (imports-only).
77
+ Returns: impactedSymbols array with symbolId, depth, riskScore, confidence, edgeKinds, coChangeFrequency (when the dependent's file co-changes with the target's file).
78
+ 3-tier model by the strongest edge that reaches each dependent: confirmed (calls/extends/implements and the data edges reads/writes/executes), likely (uses/co-changed), potential (imports-only). A potential dependent whose file co-changes with the target's file at frequency above 0.5 is promoted to likely. The traversal is transitive across all tiers; riskScore falls with depth.
79
+ Also returns coChangedFiles (when co-change data exists): up to 10 files that co-change with the target's file at frequency >= 0.5 but have no graph path to it, each with file, frequency, sharedCommits. Informational: not counted in tiers, risk scores or the safe-edit gate.
80
+ Every copy of a table, view or procedure that several files define, and its 2-part ID, reports the same dependents (those of the winning definition).
71
81
 
72
- When to use: BEFORE modifying any function or class.
82
+ When to use: BEFORE modifying any symbol, including a SQL table, view or procedure.
73
83
  Instead of: get_logic_slice (forward deps), get_pr_impact (PR-level).
74
84
 
75
85
  ### get_architectural_overlay
@@ -77,10 +87,12 @@ Get project architectural layer map.
77
87
 
78
88
  Parameters:
79
89
  - layer (string, optional): Filter by layer name
90
+ - mode (enum: both|regex|communities|contexts, default both): "regex" layer map, "communities" Louvain clusters, "contexts" candidate bounded contexts (file level), "both" layers plus communities
91
+ - candidate (number, optional): With mode "contexts", the id of one candidate to return in full
80
92
 
81
- Returns: Symbols grouped into Domain, Infrastructure, Adapter, Test, Composition, Configuration layers.
93
+ Returns: Symbols grouped into Domain, Infrastructure, Adapter, Test, Composition, Configuration layers; with communities, data-driven clusters and god nodes. With mode "contexts", a summary: `commitSha`, `coverage`, `candidates` (`id`, `label`, `fingerprint`, `fileCount`, `cohesion`, top 5 `topTerms`), `hubs` (capped at 50, with `hubsTotal`), `collisions` (capped at 50, with `collisionsTotal`), `unclustered` count and a `hint`. Data coupling appears in the summary as counts only: `sharedTableCount` on each candidate that has shared tables, plus `dataResourcesTotal`, `sharedResourcesTotal` (resources of two or more candidates) and `externalResourcesTotal` (resources no `.sql` file defines). With `candidate: <id>`, that candidate in full (files capped at 15 with `fileCount`, cross-candidate edges, co-change pairs, `sharedTables` with `sharedTableCount`), its hubs (50), its collisions (10, each with only that candidate's own occurrences: `symbolId`, first 5 members, `memberCount`) and its own `dataResources` with `dataResourcesTotal`. `sharedTables`: the first 15 by name, each `{ name, kind?, external, accesses: [{ candidate, modes }], owner?, accessCount }` with up to 10 accesses (the asking candidate's own access always kept). `dataResources`: the first 15 by name, each `{ name, kind?, external, modes, owner?, writerCount }`. When the detail is still larger than the response limit, trailing entries are dropped by name order from the longer of the two lists (dataResources on a tie); the counts keep the totals. An unknown id or no contexts view returns `found: false` with a hint (run `ctxo index`). `ctxo contexts --json` prints the full artifact.
82
94
 
83
- When to use: Onboarding to a new codebase, validating layer boundaries.
95
+ When to use: Onboarding to a new codebase, validating layer boundaries, preparing a context map (mode "contexts").
84
96
 
85
97
  ### get_why_context
86
98
  Retrieve git commit history and anti-pattern warnings for a symbol.
@@ -89,7 +101,7 @@ Parameters:
89
101
  - symbolId (string, required): Symbol ID
90
102
  - maxCommits (number, optional): Limit to N most recent commits
91
103
 
92
- Returns: commitHistory array + antiPatternWarnings (reverts, rollbacks, undo patterns).
104
+ Returns: commitHistory array + antiPatternWarnings (reverts, rollbacks, undo patterns), and driftSignals when a community snapshot exists. driftSignals carries confidence (low below 3 distinct commits, medium 3-6, high 7+), distinctCommits (different commits in the snapshot history, other than the current one), snapshotsAvailable (history entries passed in, kept for compatibility) and an optional hint.
93
105
 
94
106
  When to use: Understanding WHY code was written this way, checking for problem history.
95
107
  Pair with: get_change_intelligence (complexity/churn scores).
@@ -111,7 +123,7 @@ Parameters:
111
123
  - includeTests (boolean, default false): Include test files
112
124
  - intent (string, optional): Filter results by keyword
113
125
 
114
- Returns: deadSymbols (with confidence 1.0/0.9/0.7 and reason), deadFiles, unusedExports, scaffolding markers.
126
+ Returns: deadSymbols (with confidence 1.0/0.9/0.7 and reason), deadFiles, unusedExports, scaffolding markers. Symbols in .sql files are never reported (their consumers may live outside the index).
115
127
 
116
128
  When to use: Cleanup, refactoring, confirming code is truly unused.
117
129
  Instead of: find_importers (specific symbol reverse lookup).
@@ -130,6 +142,8 @@ Task types:
130
142
  - refactor: Importers + complexity + churn (restructuring)
131
143
  - understand: Full slice + architecture (learning)
132
144
 
145
+ Context entries that depend on the symbol (its blast radius) carry an optional confidence tier (confirmed|likely|potential); co-change history can raise potential to likely. Entries carry no symbol annotations (use get_logic_slice level 4). A 2-part data ID (`dbo.loans::table`) is accepted.
146
+
133
147
  When to use: BEST starting point when you know the symbol AND your intent.
134
148
 
135
149
  ### get_ranked_context
@@ -139,6 +153,7 @@ Parameters:
139
153
  - query (string, required): Natural language search query
140
154
  - tokenBudget (number, default 4000): Max tokens for results
141
155
  - strategy (enum: combined|dependency|importance, default combined): Ranking strategy
156
+ - searchMode (enum: fts|legacy, default fts): "fts" is BM25 with camelCase splitting, trigram fallback and automatic typo correction (reported in `fuzzyCorrection`); "legacy" is substring matching
142
157
 
143
158
  Returns: Symbols ranked by BM25 relevance + PageRank importance, packed within budget.
144
159
 
@@ -150,9 +165,10 @@ Search symbols by name or regex pattern.
150
165
 
151
166
  Parameters:
152
167
  - pattern (string, required): Substring or regex pattern
153
- - kind (enum: function|class|interface|method|variable|type, optional): Filter by kind
168
+ - kind (enum: function|class|interface|method|variable|type|table|view|procedure, optional): Filter by kind (table, view and procedure come from SQL plugins)
154
169
  - filePattern (string, optional): Filter by file path substring
155
170
  - limit (number 1-100, default 25): Max results
171
+ - mode (enum: regex|fts, default regex): "regex" is a case-insensitive regex or substring match on names; "fts" is BM25 relevance search with typo correction
156
172
 
157
173
  When to use: Know (part of) the symbol name, need its ID for other tools.
158
174
  Instead of: get_ranked_context (semantic/relevance search).
@@ -171,8 +187,8 @@ Instead of: get_pr_impact (full PR risk assessment).
171
187
  Find all symbols that depend on a given symbol (reverse dependency).
172
188
 
173
189
  Parameters:
174
- - symbolId (string, required): Symbol ID
175
- - edgeKinds (string[], optional): Filter by imports|calls|extends|implements|uses
190
+ - symbolId (string, required): Symbol ID, or the 2-part ID of a table, view or procedure (`dbo.loans::table`; every copy gives the same importers)
191
+ - edgeKinds (string[], optional): Filter by imports|calls|extends|implements|uses|reads|writes|executes (the last three are data access)
176
192
  - transitive (boolean, default false): Follow transitive reverse edges
177
193
  - maxDepth (number 1-10, default 5): Max BFS depth
178
194
  - intent (string, optional): Filter by keyword
@@ -194,7 +210,7 @@ Rank symbols by PageRank centrality.
194
210
 
195
211
  Parameters:
196
212
  - limit (number 1-200, default 25): Max results
197
- - kind (enum: function|class|interface|method|variable|type, optional): Filter
213
+ - kind (enum: function|class|interface|method|variable|type|table|view|procedure, optional): Filter (PageRank runs on the whole graph, data edges included)
198
214
  - filePattern (string, optional): Filter by file path
199
215
  - damping (number 0-1, default 0.85): PageRank damping factor
200
216
 
@@ -209,7 +225,7 @@ Parameters:
209
225
  - maxFiles (number, default 50): Max files
210
226
  - confidence (enum: confirmed|likely|potential, optional): Filter tier
211
227
 
212
- Returns: changedFiles, changedSymbols, totalImpact, riskLevel (low|medium|high), per-file blast radius, co-change data.
228
+ Returns: changedFiles, changedSymbols, totalImpact, riskLevel (low|medium|high), per-file blast radius, co-change data (coChangedWith per file), boundaryViolations (confidence from distinctCommits) and clustersAffected.
213
229
 
214
230
  When to use: FIRST tool when reviewing a PR or evaluating recent commits.
215
231
 
@@ -246,6 +262,10 @@ Onboarding to a new codebase?
246
262
  -> get_architectural_overlay (layer map)
247
263
  -> get_symbol_importance (most critical symbols)
248
264
 
265
+ Preparing a context map?
266
+ -> get_architectural_overlay(mode: "contexts") (candidate bounded contexts)
267
+ -> then candidate: <id> for one in full
268
+
249
269
  Cleaning up code?
250
270
  -> find_dead_code (unused symbols)
251
271
  -> get_change_intelligence (complexity hotspots)
@@ -269,6 +289,7 @@ All tool responses include:
269
289
  ```
270
290
  - Default truncation threshold: 8KB
271
291
  - Configurable via CTXO_RESPONSE_LIMIT environment variable
292
+ - In `get_architectural_overlay` mode "contexts", only `candidates` is truncatable (hint: use `candidate: <id>` or `ctxo contexts --json`); `hubs` and `collisions` are capped at the source and never truncated. In the `candidate: <id>` detail, `sharedTables` and `dataResources` are not truncatable either: they are capped at 15 each and shortened from the end until the detail fits the limit, with `sharedTableCount` and `dataResourcesTotal` keeping the totals
272
293
 
273
294
  ### Intent Filtering
274
295
  4 tools accept `intent` parameter: get_blast_radius, get_logic_slice, find_importers, find_dead_code.
@@ -278,15 +299,151 @@ All tool responses include:
278
299
 
279
300
  ### Symbol ID Format
280
301
  All symbols use deterministic IDs: `"<relativePath>::<name>::<kind>"`
281
- - Example: `"src/core/graph/symbol-graph.ts::SymbolGraph::class"`
282
- - Valid kinds: function, class, interface, method, variable, type
283
- - Valid edge kinds: imports, calls, extends, implements, uses
302
+ - Example: `"src/core/graph/symbol-graph.ts::SymbolGraph::class"`, `"db/lending.sql::dbo.loans::table"`
303
+ - Valid kinds: function, class, interface, method, variable, type, table, view, procedure
304
+ - Valid edge kinds: imports, calls, extends, implements, uses, reads, writes, executes
305
+ - Edge targets may also be 2-part name references (`<name>::<kind>`); data edges point at canonical names such as `dbo.loans::table` (lowercase, delimiters stripped, default schema `dbo`), resolved to the defining `table` or `view`. With several definitions, the winner is a copy outside test files (default test globs; `index.tests` is not consulted), then outside `docs`/`doc`/`documentation` folders, then the codepoint-smallest symbol ID
306
+ - Impact tools (get_blast_radius, find_importers, get_pr_impact, get_context_for_task, the gate, `ctxo blast-radius`) accept a 2-part data ID and give every copy of a multi-defined object the winner's dependents; forward edges (get_logic_slice) stay per copy; get_why_context with a 2-part ID reads the winner's file
284
307
 
285
308
  ### Resources
286
309
  - `ctxo://status` — Health check resource (confirms server is running)
287
310
 
288
311
  ---
289
312
 
313
+ ## Co-change, Incremental Indexing and Snapshots (v0.13, #157)
314
+
315
+ Co-change (files that change together in git history) feeds every blast-radius path: get_blast_radius, get_pr_impact, get_context_for_task, `ctxo blast-radius`, the gate hook and `ctxo gate --preview`.
316
+
317
+ ### The co-change window
318
+
319
+ Built by `ctxo index` from one `git log --no-merges -M --name-status` pass over the commits reachable from HEAD. The same pass fills each file's `intent[]`, and both follow renames. The window is independent of `--max-history` (which only limits commits kept per file in `intent[]`, default 20). Config in `.ctxo/config.yaml` (all optional):
320
+
321
+ ```yaml
322
+ cochange:
323
+ maxCommits: 2000 # last N non-merge commits from HEAD, or "all"
324
+ # since: 12m # also limit by age; d, w, m, y; counted back from HEAD's committer date
325
+ maxFilesPerCommit: 30 # commits touching more analysed files are skipped (massSkipped)
326
+ minShared: 2 # shared commits needed for a pair
327
+ include: [] # extra tracked, non-indexed files to analyse (globs)
328
+ exclude: [] # indexed files to leave out (globs); also vetoes include-added files
329
+ ```
330
+
331
+ - CLI overrides: `ctxo index --cochange-commits <n|all>`, `ctxo index --cochange-since <duration>` (30d, 2w, 12m, 1y).
332
+ - A pair becomes an entry at `shared >= minShared` and `frequency = shared / min(countA, countB) >= 0.1`. The applied window is recorded in `.ctxo/index/co-changes.json` under `window`; the file is only rewritten when entries or window change.
333
+ - `commitsScanned` counts every non-merge commit of the repository, so in a monorepo a numeric maxCommits covers fewer relevant commits than its value suggests.
334
+ - Sparse checkout: files outside the cone are absent from the matrix, and their committed index entries are never pruned.
335
+ - `ctxo verify-index` reports files with a rename in their history as STALE until the next `ctxo index` after upgrading, because `intent[]` now follows renames.
336
+
337
+ ### Incremental indexing and the post-commit hook
338
+
339
+ - `ctxo index --files <path...>` (alias `--file <path>`) re-indexes the listed files. `ctxo index --commit <rev>` re-indexes the files one commit changed and removes the ones it deleted or renamed away. Both load the symbol registry from the committed index and compute co-change, communities, the snapshot, the drift and boundary artifacts and the SQLite cache from the merged index. Candidates pass the same filters as a full run.
340
+ - A full `ctxo index` uses only the files it discovers and prunes index entries of deleted or newly ignored sources (never of skip-worktree paths).
341
+ - `ctxo init` installs a versioned post-commit block (`# ctxo-start v2`) that runs `ctxo index --commit HEAD` once per commit, guarded by `[ -d .ctxo ]`. `ctxo init` and `ctxo doctor --fix` upgrade an older block in place, collapse duplicate blocks, and follow `core.hooksPath`. A global or system `core.hooksPath` is shared by every repository, so ctxo writes nothing there and prints the blocks to add by hand (scope detection needs git 2.26+).
342
+ - `ctxo doctor` has a `git_hooks` check: warns on a missing, outdated or duplicated post-commit block.
343
+ - `ctxo <command> --help` (and `-h`) prints the help and runs nothing.
344
+
345
+ ### One snapshot per commit
346
+
347
+ `.ctxo/index/communities.json` is the current snapshot and `communities.history/` keeps at most one earlier snapshot per commit. A second run on the same commit replaces the current snapshot without archiving. Drift compares against the newest history entry from a different commit. Drift and boundary confidence count `distinctCommits` (low below 3, medium 3-6, high 7+; boundary needs at least 2). `ctxo index --check` writes nothing and records no snapshot. The first write after upgrading removes older same-commit duplicates from `communities.history/`.
348
+
349
+ ### Gate consequence
350
+
351
+ The safe-edit gate counts confirmed + likely dependents, so co-change-upgraded dependents count and the gate may fire somewhat more often. `ctxo gate --preview` shows the effect.
352
+
353
+ Design record: docs/architecture/ADR/adr-016-co-change-engine-and-snapshot-policy.md
354
+
355
+ ---
356
+
357
+ ## Contexts View (v0.13, #156)
358
+
359
+ Candidate bounded contexts, computed by `ctxo index` and written to `.ctxo/index/contexts-view.json` (committed). Louvain runs on a file graph whose edges blend dependency weight (distinct symbol edges, log-normalised) and co-change frequency; hub files (fan-in or god nodes) have their edges damped by `1 / log2(2 + dependents)` and are listed separately. Groups smaller than `minCandidateFiles` fold into a neighbour; files with no edge are `unclustered`. Each candidate has a suggested label (longest common directory; PageRank fallback at the repo root), a `fingerprint`, cohesion, top terms, cross-candidate edges and co-change pairs. Collisions are type names that appear in two candidates with different member shapes (Jaccard below 0.5). No domain is named automatically.
360
+
361
+ Refresh: every index run that re-indexes files (full, `--files`, `--commit`, so also every post-commit hook run); skipped by `--skip-community`; not refreshed by `ctxo watch`.
362
+
363
+ Surfaces: `get_architectural_overlay({ mode: "contexts" })` (summary first, `candidate: <id>` for one in full) and `ctxo contexts [--json]` (table to stderr, full artifact to stdout; without a view it exits 0 with `found: false`).
364
+
365
+ Config in `.ctxo/config.yaml` (all optional, defaults shown):
366
+
367
+ ```yaml
368
+ communities: # symbol-level Louvain
369
+ resolution: 1.0
370
+ godNodeMinBridged: 3
371
+ godNodeMaxResults: 25
372
+ contextsView:
373
+ weights: { dependency: 1.0, coChange: 1.0, data: 0 } # data: weight of data edges (0 = shared tables never merge candidates)
374
+ exclude: [] # files left out of the file graph (globs)
375
+ hubs: { auto: true, topPercent: 1, minDependents: 10, paths: [] } # paths lose all their edges
376
+ resolution: 1.0
377
+ minCandidateFiles: 3
378
+ technicalTerms: [] # replaces the default list (Service, Repository, Controller, Handler, Manager, Dto, Impl, Async)
379
+ index:
380
+ tests: [] # extra test-file globs; test files are never graph nodes
381
+ facts: false # true asks plugins for code facts (protocol v2, index JSON only)
382
+ ```
383
+
384
+ Design record and evidence: docs/architecture/ADR/adr-016-co-change-engine-and-snapshot-policy.md (addendum "Contexts view defaults (PR 1A)").
385
+
386
+ ---
387
+
388
+ ## Plugin Protocol v2, Data Coupling and SQL (v0.13, #156)
389
+
390
+ Two services can look independent in the graph while both write the same table. Phase 1 of the domain knowledge layer makes that data coupling visible. Feature doc: docs/features/domain-knowledge.md. Design record: docs/architecture/ADR/adr-017-plugin-protocol-v2-and-data-coupling.md.
391
+
392
+ ### Protocol v2 (`@ctxo/plugin-api`)
393
+
394
+ - Kinds: `SYMBOL_KINDS` adds `table`, `view`, `procedure`; `EDGE_KINDS` adds `reads`, `writes`, `executes`; `DATA_RESOURCE_KINDS = ['table', 'view']` is what core analyses use.
395
+ - Annotations: `SymbolNode.annotations?: { name, args?, named? }[]`. Always on. Host-language attributes are capped at 20 per symbol, 10 positional entries and 10 named entries plus the reserved `type`, 200 characters per value; `named.type` is reserved for the fully qualified type name, and a source argument named `type` is not kept. lang-sql's `columns` annotation lists every column of a table. Core checks shape only; JSON index only.
396
+ - Facts: `FACT_KINDS = ['literal-comparison', 'normalization', 'retry-timeout', 'endpoint']`, `CodeFact { kind, symbolId, line (0-based), text (single line, trimmed, max 200 chars), detail? }`, optional `ILanguageAdapter.extractFacts`. Called only when `index.facts: true`; results go to the file's index JSON under `facts`. No official plugin implements it yet.
397
+ - Capabilities: `PluginContext.capabilities?: { facts?, sqlCallSites? }`, computed before any `createAdapter` call and offered to every plugin. `sqlCallSites` is set only when a plugin for `.sql` is installed.
398
+ - Versioning: `SUPPORTED_API_VERSIONS = ['1', '2']`; plugins declare the lowest version they work on. Older CLIs skip `@ctxo/lang-sql` with a warning and drop the new kinds from a newer index with a warning.
399
+
400
+ ### `@ctxo/lang-sql`
401
+
402
+ - `ctxo install sql` (alias `tsql`). Syntax tier, no database, no .NET. Detection, `ctxo doctor` and `ctxo index --install-missing` suggest it when the repository has at least 5 `.sql` files that are also at least 2% of its files (as `git ls-files` lists them).
403
+ - Definitions: `CREATE [OR ALTER] TABLE | VIEW | PROC[EDURE] | FUNCTION`, `ALTER VIEW | PROC[EDURE] | FUNCTION`. Symbol IDs carry the canonical name (`db/lending.sql::dbo.loans::table`); `name` keeps the declared spelling. Functions map to `function`.
404
+ - Accesses from view, procedure and function bodies: `FROM`, `JOIN`, `APPLY`, `MERGE ... USING` -> reads; `INSERT`, `UPDATE`, `DELETE`, `MERGE`, `TRUNCATE TABLE`, `SELECT ... INTO`, `OUTPUT ... INTO` -> writes; `EXEC` / `EXECUTE` -> executes. Temp tables, table variables, CTE names, aliases, 3- and 4-part names and the `sys` / `information_schema` schemas are skipped. Only ASCII A-Z is case-folded.
405
+ - Limits: T-SQL only with `dbo` as the default schema; top-level DML in scripts is skipped; `ALTER TABLE ... ADD` columns count only in the defining file; a CTE name reused later in a batch without `;` is skipped by sql-extract but read by ScriptDom.
406
+ - Size limit: a `.sql` file larger than 5 MiB is skipped (no symbols, edges or facts) with one warning per file: `skipped <file>: <n> MiB is over the 5 MiB SQL size limit`. Use `index.ignore` for dumps to silence it.
407
+ - Several definitions of one object (migrations, docs or test copies): see "Symbol ID Format" above for the winner; every copy reports the winner's dependents, each copy keeps its own forward edges, and the contexts view gives an executing candidate the bodies of every production copy of a procedure (the winner's alone when no copy is production; docs and test copies add nothing when a production copy exists). The winner among production copies is the codepoint-smallest path, not necessarily the newest migration (`V10__` sorts before `V7__`).
408
+
409
+ ### SQL at C# call sites
410
+
411
+ With lang-sql installed and the Roslyn tier (.NET SDK 8+), the C# helper parses constant SQL with ScriptDom at: `SqlCommand` constructor `cmdText`; `CommandText` assignments on `SqlCommand`, `DbCommand`, `IDbCommand` and subtypes (a procedure name when `CommandType = StoredProcedure` is set on the same local or in the same object initializer); Dapper `SqlMapper` methods starting with `Query` or `Execute` (`sql` argument; `commandType: CommandType.StoredProcedure` means a procedure name); the Dapper `CommandDefinition` constructor (`commandText` argument; a named or positional `commandType` of `StoredProcedure` means a procedure name; the edge belongs to the method that constructs it); `SqlBulkCopy.DestinationTableName` (writes); EF Core `FromSqlRaw`, `ExecuteSqlRaw`, `ExecuteSqlRawAsync`, `SqlQueryRaw` (EF `{0}` placeholders become parameters before parsing). Only compile-time constants count: use `const string`; `static readonly` fields, runtime strings and `CommandText +=` are non-constant. Dapper `IN @ids` is read as `IN (@ids)`. The tree-sitter fallback gives no SQL edges. `DEBUG=ctxo:roslyn` logs the constant, non-constant and unparsable counts.
412
+
413
+ ### C# annotations and test files
414
+
415
+ - The C# plugin reports attributes in both tiers. Roslyn keeps constant arguments (strings and characters as their value, numbers in invariant culture, booleans, enum constants as `Type.Member`, `typeof(T)` as a display string, arrays flattened, nulls skipped); tree-sitter keeps literal arguments only (strings, numbers, booleans). In the Roslyn semantic path a `name:` argument arrives in `args`; tree-sitter puts it in `named`. Roslyn formats real-number exponents its own way. A source argument named `type` is dropped in both tiers (`named.type` is reserved).
416
+ - Test files (contexts view): a test glob match, or any symbol carrying `Fact`, `Theory`, `SkippableFact`, `SkippableTheory`, `Test`, `TestCase`, `TestCaseSource`, `TestFixture`, `TestMethod`, `DataTestMethod` or `TestClass`. Once C# annotations exist in the index, a file matched only by `**/*Test.cs` or `**/*Tests.cs` needs a test annotation. Missed: test classes with only other attributes (FsCheck `[Property]`), custom attributes derived from `Fact` or `Theory` (names match exactly, base types are not resolved) and unannotated `*Tests.cs` in a bare capital `Tests/` or `Test/` folder (not a default test glob); fix with an `index.tests` glob.
417
+ - Run a full `ctxo index` after upgrading `@ctxo/lang-csharp`: an incremental run annotates only the files it re-indexes, while the contexts view already counts the index as annotated, so name-only test matches that were not re-indexed show up as production files until the full run.
418
+
419
+ ### Data coupling in core and the contexts view
420
+
421
+ - `reads`, `writes`, `executes` are confirmed blast-radius dependents, so the safe-edit gate also guards heavily used tables and procedures. Data edges stay out of symbol-level communities, god nodes, cross-cluster counts and boundary violations. `.sql` symbols are never dead code.
422
+ - Contexts view artifact: top-level `dataResources` (`name`, `kind?`, `external`, `accesses` per candidate with `modes`, `writers`, `owner?` when exactly one candidate writes it) and per-candidate `sharedTables` (resources of two or more candidates). An `executes` edge to an indexed procedure adds that procedure's reads and writes, transitively; reading a view does not expand to its tables; unclustered files and test files are ignored and hubs count for their assigned candidate.
423
+ - Procedure attribution: procedure bodies count only for the candidates that execute them, decided by canonical name (no copy of an executed procedure credits its own candidate). With several definitions, an executing candidate takes the union of the bodies of every production copy (outside test files and `docs`, `doc` or `documentation` folders), or the winner's body alone when no copy is production; docs and test copies of a procedure that has a production copy add nothing. A procedure that no candidate executes, directly or through other procedures, credits the candidate whose file holds it (with several copies, the candidate of each production copy, or of the winner when none is production); procedures that only execute each other each credit their own candidate. A procedure that only tests or unclustered files execute therefore still credits the script's candidate.
424
+ - `contextsView.weights.data` (default 0) weights data edges between an accessing file and the defining `.sql` file in the file graph. At 0, a `.sql` file joins a candidate only through co-change, so scripts without a co-change partner stay unclustered and lower `coverage` (0.99 to 0.91 on one fixture, candidates unchanged); `contextsView.exclude` leaves them out. Table and view names and columns join the term harvest; tables take part in collisions with their columns, so a C# class and a same-name table can collide (often an entity mapped to its table: possible noise).
425
+ - `ctxo index` appends `, <S> shared tables` to its contexts line; `ctxo contexts` prints the count and one `shared` line per shared resource (owner, several writers or read only).
426
+ - Size: a table shared by k candidates is listed k times with k accesses each in the artifact (k squared growth); the MCP responses are capped.
427
+
428
+ ### Doctor: `csharp_tier`
429
+
430
+ Five outcomes:
431
+ 1. No C# sources: pass, "no C# sources detected".
432
+ 2. No .NET 8+ SDK: pass, "syntax tier (install the .NET 8+ SDK for the full tier)".
433
+ 3. `@ctxo/lang-csharp` not resolvable from the project: pass, "syntax tier (@ctxo/lang-csharp not installed)".
434
+ 4. `dotnet restore` of the helper succeeds: pass, "full tier (.NET SDK <version>, Roslyn helper restores)".
435
+ 5. The restore fails or times out (120 s): warn, "syntax tier: the Roslyn helper could not restore (<first error line>)". Fix: make `Microsoft.SqlServer.TransactSql.ScriptDom` and the `Microsoft.CodeAnalysis` packages reachable from your NuGet sources (nuget.org or a mirror), then run `ctxo index`.
436
+
437
+ When the helper cannot restore or build during `ctxo index`, the C# plugin falls back to tree-sitter (symbols and attribute annotations with literal arguments kept; `named.type`, non-literal argument values and SQL edges lost) and the summary reads `C#: N files (syntax tier - the Roslyn helper failed, run "ctxo doctor")`. In `ctxo watch`, a keep-alive that shut down for inactivity or crashed is restarted once on the next re-index; if that fails, the file falls back to tree-sitter instead of being written with zero symbols. The helper targets `net8.0` with `RollForward` `Major`, so the .NET host can roll forward to a newer major runtime when no 8.x is installed; the built helper has been run with `dotnet exec --fx-version` on .NET 8, 9 and 10. A lone `.csproj` without a solution now indexes in the Roslyn tier.
438
+
439
+ ### Upgrade notes
440
+
441
+ - First re-index after upgrading rewrites the committed JSON of C# files once (annotations). Make it a full `ctxo index` (see "C# annotations and test files").
442
+ - UTF-16 files with a BOM are decoded; their hash changes once, so `ctxo index --check` reports them stale until the next `ctxo index`.
443
+ - Shared conformance corpus: packages/sql-extract/corpus (`<case>.sql` + `<case>.expected.json`, modes `statement` and `file`), run by sql-extract, lang-sql and (with .NET) ScriptDom.
444
+
445
+ ---
446
+
290
447
  ## Safe-Edit Guard (v0.8+)
291
448
 
292
449
  The safe-edit guard closes the loop between MCP tool availability and agent compliance. An agent with access to get_blast_radius can still skip it. The guard makes skipping impossible for high-impact symbols on Claude Code.
@@ -300,6 +457,10 @@ The safe-edit guard closes the loop between MCP tool availability and agent comp
300
457
 
301
458
  On every Edit call, `ctxo gate-hook` checks whether get_blast_radius has been called for that symbol in the current session. If not, the hook blocks the edit and returns a message: "High-impact symbol - run get_blast_radius first." Once the agent calls the tool, the edit goes through. Fires once per symbol per session. Fail-open on any error.
302
459
 
460
+ - Matcher `Edit` only: `ctxo gate-hook` ignores every other tool, so `Write` (new files, whole-file rewrites) and `NotebookEdit` are not gated.
461
+ - Session state is a file store: the hook runs as a new process per Edit call, records the symbols it already warned about in `.ctxo/.cache/gate/<session_id>.json`, and also accepts a get_blast_radius call found in the session transcript. It survives MCP server restarts.
462
+ - A `.claude/settings.json` that is not valid JSON (JSONC comments included) or not a settings object is never overwritten; `ctxo init` warns and leaves it. The guard is also detected in `.claude/settings.local.json` (read only).
463
+
303
464
  ### Gate decision
304
465
 
305
466
  A symbol is flagged if it exceeds both thresholds:
@@ -319,7 +480,7 @@ Sensitivity levels (configured in `.ctxo/config.yaml`):
319
480
  ### Skills
320
481
 
321
482
  Three model-invoked skill files installed to `.claude/skills/` (Claude Code) and `.cursor/rules/` (Cursor):
322
- - `ctxo-understand`: run get_context_for_task before reading source files
483
+ - `ctxo-understand`: run get_context_for_task at the start of a task, and pick the right tool for a structural question from a question-to-tool table that covers all 14 tools (reading a file to see its contents needs no index call)
323
484
  - `ctxo-safe-edit`: run get_blast_radius and get_why_context before any edit
324
485
  - `ctxo-review-pr`: run get_pr_impact before reviewing a diff
325
486
 
@@ -330,3 +491,34 @@ Three model-invoked skill files installed to `.claude/skills/` (Claude Code) and
330
491
  | Claude Code | Yes (PreToolUse) | Yes | Yes |
331
492
  | Cursor | No (no blocking pre-edit hook in v3.6) | Yes | Yes |
332
493
  | Other platforms | No | Where supported | Yes |
494
+
495
+ ### Tiered rules (v0.13, #159)
496
+
497
+ `ctxo init` writes one ctxo block into each AI tool's rules file, between `<!-- ctxo-rules-start v<version> -->` and `<!-- ctxo-rules-end -->`. The version is the rules template version, the CLI minor in which the rules text last changed.
498
+
499
+ - Compact tier, Claude Code only (`CLAUDE.md`, 4 lines of text; at most 8 non-blank lines including the markers, 6 today): written when the guard hook is registered (`.claude/settings.json` or `.claude/settings.local.json`), all three skills are in `.claude/skills/`, and `gate.enabled` is not false. It says the repo has a ctxo index, to use the ctxo tools for structural questions before grepping, that the guard blocks the first edit to a high-impact symbol until get_blast_radius is called, and names the three skills.
500
+ - Full tier: every other client, Claude Code without one of those three, and a rules file several clients share (an `AGENTS.md` or `GEMINI.md` that is a hard link or a symlink to `CLAUDE.md`). First rule: "For structural questions (dependencies, impact, history), use ctxo tools first; reading a file to see its contents needs no index call." Then the tool guide; its modification steps end with "Only then edit the source".
501
+ - The tier follows the disk after the skills and guard steps; `--no-guard` and `--skip skills` give the full block unless the guard and skills are already installed (they remove nothing).
502
+ - Re-runs replace the first ctxo block in place (versioned, unversioned `<!-- ctxo-rules-start -->`, or the old unmarked body), collapse later blocks into it, and keep every byte outside the blocks, including a UTF-8 BOM and CRLF line endings. Create-mode files (`.cursor/rules/ctxo.mdc`, `AGENTS.md` and the rest) keep user content. A block from a newer ctxo, a start marker without an end marker, or an end marker without a start marker leaves the file unchanged with a warning.
503
+ - Formatter stable: blocks and skills are written in the layout Prettier prints, and blocks, the old unmarked body and skills are compared with formatting ignored (line endings, BOM, trailing whitespace, blank lines, table padding; blocks and skills also ignore line wrapping, so `proseWrap: always` or `never` is no edit). A block a formatter reshaped is current and `unchanged`, and the tier is read from the first non-blank line. The compact tier counts a skill only when it is readable and current. A rules file ctxo cannot read is left unchanged with one warning, and init goes on.
504
+ - Held blocks: init (real run, dry run, `--only rules`, `--rules`) and `ctxo doctor --fix` leave `CLAUDE.md` or `.cursor/rules/ctxo.mdc` unchanged when its block is pre-tier or compact, the user has not declined the guard or skills (`--no-guard`, the guard question answered no, `gate.enabled: false`, `--skip skills`), and after the skills and guard steps they are still not on disk (an unreadable `.claude/settings.json` counts as no guard). Init warns `<file>: left unchanged until the guard and skills are installed; run "ctxo init --only rules,skills,guard" or "ctxo doctor --fix"`; the dry run prints `unchanged (held)`. A file linked to a held one is held too. A file without a ctxo block is never held.
505
+ - Skills carry a version marker, `<!-- ctxo-skill v<version> -->` (SKILLS_TEMPLATE_VERSION). An installed skill with a newer marker is left alone with a warning and is not outdated; no marker or an older one is outdated and replaced.
506
+
507
+ ### Granular init (v0.13, #159)
508
+
509
+ - Parts, run in this order: `config` (`.ctxo/config.yaml`, `.gitignore` entry, `.ctxo/index/`), `skills`, `guard`, `rules`, `mcp`, `git-hooks`.
510
+ - `ctxo init --only <parts>` installs exactly those parts (and skips the plugin step); `ctxo init --skip <parts>` installs the rest (the plugin step still runs); `ctxo init --no-guard` does not install the guard and never asks. `-y` without `--tools` sets up all nine tools, the same set the dry run previews. `--only` cannot be combined with `--skip` or `--rules`; an unknown part exits 1.
511
+ - `ctxo init --rules` is deprecated: the same as `--skip git-hooks,guard`, prints a notice, and still skips the plugin step.
512
+ - The interactive guard question appears only when Claude Code is selected. Cursor gets the skills and the full block, but no guard question and no hook.
513
+ - `ctxo init --dry-run` lists every file init would create or change (`would create`, `would update`, `unchanged`) and writes nothing.
514
+ - Init output: `[ctxo] ✓ <file> (<created|updated|unchanged>)`, warnings as `[ctxo] ⚠ <text>`.
515
+
516
+ ### Agent-setup doctor checks (v0.13, #159)
517
+
518
+ - `ai_rules`: an outdated block (unversioned, older template, or edited), a `CLAUDE.md` block that expects the guard while it is missing, a current block in the wrong tier, several blocks in one file, a start marker without an end marker, an end marker without a start marker. A block from a newer ctxo passes, and so does a file with no ctxo block. Linked files are reported once.
519
+ - `skills`: missing or outdated ctxo skills for Claude Code and Cursor, where expected (one ctxo skill installed, or the rules block is pre-tier or compact). Formatting is ignored, and a skill from a newer ctxo is not reported.
520
+ - `claude_guard_hook`: an unreadable `.claude/settings.json`, or a missing guard while the `CLAUDE.md` block expects it and the gate is enabled.
521
+ - All three warn, never fail. A versioned full block and `gate.enabled: false` are opt-outs and pass.
522
+ - `ctxo doctor --fix` order: Config, Git hooks, Skills, Claude guard hook, AI rules, then plugins, index and cache. AI rules re-render whenever any of the three checks warned, so the tier follows the fixes. A block is held unchanged by the same rule as init while the guard or the skills it expects are not in place (unreadable settings, failed install); doctor keeps reporting them until fixed. The Skills step reports a skill from a newer ctxo as a warning line and fails on a skill path it cannot write. `--fix` never installs what the user declined, never writes an unreadable `.claude/settings.json`, and replaces edited ctxo skills with the bundled copy (copy a customised skill under another name).
523
+
524
+ Design record: docs/architecture/ADR/adr-015-safe-edit-guard.md (amendment: tiered rules)
package/llms.txt CHANGED
@@ -8,27 +8,29 @@ Ctxo is a Model Context Protocol (MCP) server that gives AI agents structured co
8
8
 
9
9
  ```
10
10
  npm install -g @ctxo/cli # One-time global install (gives you the `ctxo` command)
11
- ctxo init # Detect languages, install plugins, wire MCP
11
+ ctxo init # Rules, skills, guard, MCP config, git hooks, plugins
12
12
  ctxo index # Build codebase index
13
13
  ctxo # Start MCP server (stdio)
14
14
  ```
15
15
 
16
- ## Package Layout (v0.7.0-alpha.0, pnpm monorepo)
16
+ ## Package Layout (pnpm monorepo)
17
17
 
18
- - `@ctxo/cli` — CLI + MCP server (formerly `ctxo-mcp`)
19
- - `@ctxo/plugin-api` — `CtxoLanguagePlugin` v1 interface (apiVersion, id, extensions, tier, createAdapter)
20
- - `@ctxo/lang-typescript` — ts-morph full-tier plugin
21
- - `@ctxo/lang-go` — tree-sitter / gopls plugin
22
- - `@ctxo/lang-csharp` — Roslyn plugin
18
+ - `@ctxo/cli` - CLI + MCP server (formerly `ctxo-mcp`)
19
+ - `@ctxo/plugin-api` - `CtxoLanguagePlugin` interface, protocol v1 and v2 (`SUPPORTED_API_VERSIONS = ['1', '2']`; v2 adds data kinds, annotations, facts on request and host capabilities)
20
+ - `@ctxo/lang-typescript` - ts-morph full-tier plugin
21
+ - `@ctxo/lang-go` - tree-sitter / gopls plugin
22
+ - `@ctxo/lang-csharp` - Roslyn plugin (attribute annotations; SQL edges at known call sites when `@ctxo/lang-sql` is installed)
23
23
  - `@ctxo/lang-java` - tree-sitter Java plugin (syntax tier built-in; full tier via `@ctxo/lang-java-analyzer` companion, requires JRE 11+)
24
+ - `@ctxo/lang-sql` - T-SQL plugin for `.sql` files (protocol v2): tables, views, procedures, functions and their reads/writes/executes
25
+ - `@ctxo/sql-extract` - zero-dependency T-SQL tokenizer and recognizers (a library, not a plugin; the one dependency plugins may take besides `@ctxo/plugin-api`)
24
26
 
25
27
  Language adapters ship as separate npm packages and are discovered by scanning the project `package.json` for `@ctxo/lang-*` or `ctxo-lang-*` dependencies.
26
28
 
27
29
  ## 14 Tools
28
30
 
29
31
  - get_logic_slice: Symbol + transitive dependencies (L1-L4 detail levels)
30
- - get_blast_radius: What breaks if a symbol changes (3-tier: confirmed/likely/potential)
31
- - get_architectural_overlay: Project layer map (Domain/Infrastructure/Adapter)
32
+ - get_blast_radius: What breaks if a symbol changes (3-tier: confirmed/likely/potential; data edges are confirmed)
33
+ - get_architectural_overlay: Project layer map (Domain/Infrastructure/Adapter), Louvain communities, and candidate bounded contexts (mode: "contexts")
32
34
  - get_why_context: Git commit intent + anti-pattern warnings (reverts, rollbacks)
33
35
  - get_change_intelligence: Complexity x churn composite score
34
36
  - find_dead_code: Unreachable symbols and files
@@ -51,6 +53,45 @@ Language adapters ship as separate npm packages and are discovered by scanning t
51
53
  - Don't know the symbol name? -> search_symbols or get_ranked_context
52
54
  - Finding unused code? -> find_dead_code
53
55
  - Safe to delete? -> find_importers
56
+ - Preparing a context map? -> get_architectural_overlay(mode: "contexts")
57
+
58
+ ## New in v0.13
59
+
60
+ ### Tiered agent rules, granular init, agent-setup doctor checks (#159)
61
+
62
+ - Two rules tiers inside versioned markers (`<!-- ctxo-rules-start v<version> -->` / `<!-- ctxo-rules-end -->`). Claude Code gets a compact block (4 lines in `CLAUDE.md`) when the safe-edit guard hook, all three skills and an enabled gate (`gate.enabled` not false) are in place; every other client, and Claude Code without them, gets the full block. The full block's first rule is narrowed: "For structural questions (dependencies, impact, history), use ctxo tools first; reading a file to see its contents needs no index call."
63
+ - The rules writer replaces the ctxo block in place in every rules file (versioned, unversioned or the old unmarked body), collapses duplicates, keeps everything outside the block (BOM and line endings included), and leaves a block from a newer ctxo or a damaged block (a start or end marker without its partner) unchanged with a warning. `ctxo-understand` carries a question-to-tool table for all 14 tools.
64
+ - Blocks and skills are written in Prettier's layout and compared with formatting ignored (line endings, BOM, trailing whitespace, blank lines, table padding, line wrapping), so a markdown formatter and ctxo never rewrite each other. The compact block is at most 8 non-blank lines including the markers (6 today).
65
+ - A rules file several clients share (an `AGENTS.md` or `GEMINI.md` hard-linked or symlinked to `CLAUDE.md`) gets the full block; init and doctor treat it as one file.
66
+ - Each bundled skill ends with `<!-- ctxo-skill v<version> -->`; a skill from a newer ctxo is left alone and not reported as outdated.
67
+ - No silent opt-out: init (real run, dry run, `--only rules`, `--rules`) and `doctor --fix` leave a pre-tier or compact block unchanged while the guard or skills it expects are missing and not declined, with the warning `<file>: left unchanged until the guard and skills are installed; run "ctxo init --only rules,skills,guard" or "ctxo doctor --fix"`. A file linked to a held one is held too.
68
+ - `ctxo init` parts: `rules`, `skills`, `guard`, `mcp`, `git-hooks`, `config`. New flags `--only <parts>`, `--skip <parts>`, `--no-guard` (does not install the guard); `--rules` is a deprecated alias for `--skip git-hooks,guard`. `-y` without `--tools` sets up all nine tools. `--dry-run` lists every file init would create or change. A corrupt or oddly shaped `.claude/settings.json` is never overwritten.
69
+ - New doctor checks `ai_rules`, `skills` and `claude_guard_hook` (warn only). `ctxo doctor --fix` installs the skills and the guard, then re-renders the rules blocks; it holds a block by the same rule as init, installs nothing the user declined (a versioned full block or `gate.enabled: false`), and replaces edited ctxo skills with the bundled copy.
70
+
71
+ ### Contexts view, plugin protocol v2 and T-SQL data coupling (#156)
72
+
73
+ - **Contexts view (#156)** - `ctxo index` writes `.ctxo/index/contexts-view.json`: candidate bounded contexts from file-level clustering of dependency and co-change edges, with hub files damped. Each candidate has a suggested label, cohesion, top terms, cross-candidate edges and co-change pairs; type names with different shapes in different candidates are listed as collisions. Recomputed on every index run that re-indexes files (and every post-commit hook run); skipped by `--skip-community`; not refreshed by `ctxo watch`.
74
+ - `get_architectural_overlay` `mode: "contexts"` returns a summary of all candidates (hubs and collisions capped at 50); `candidate: <id>` returns one candidate in full. `mode` (overlay), `searchMode` (get_ranked_context) and `mode` (search_symbols) are now registered, so clients can send them. The unused `fuzzy` parameter is gone (typo correction is automatic in `fts` mode).
75
+ - `ctxo contexts [--json]` - candidates as a table (stderr) or the full artifact (stdout).
76
+ - New config in `.ctxo/config.yaml`: `communities:` (resolution, godNodeMinBridged, godNodeMaxResults), `contextsView:` (weights, exclude, hubs, resolution, minCandidateFiles, technicalTerms) and `index.tests` (extra test globs).
77
+ - **Plugin protocol v2 (#156)** - new symbol kinds `table`, `view`, `procedure` and edge kinds `reads`, `writes`, `executes`; typed symbol `annotations` (`name`, `args`, `named`); code facts on request (`index.facts: true`); host capabilities (`facts`, `sqlCallSites`). The CLI loads plugins declaring `apiVersion` '1' or '2'; a plugin declares the lowest version it works on.
78
+ - **`@ctxo/lang-sql`** - `ctxo install sql` (alias `tsql`). T-SQL definitions become symbols (tables with a `columns` annotation); view, procedure and function bodies give reads/writes/executes edges to canonical `schema.name` targets (default `dbo`). T-SQL only; top-level DML in scripts is skipped. Suggested by detection when the repository has at least 5 `.sql` files that are also at least 2% of its files.
79
+ - **SQL size limit** - a `.sql` file larger than 5 MiB is skipped with one warning (`skipped <file>: <n> MiB is over the 5 MiB SQL size limit`) and gets no symbols or edges, so a data dump cannot crash `ctxo index`.
80
+ - **SQL at C# call sites** - with lang-sql installed and the .NET SDK 8+, constant SQL at ADO.NET, Dapper (including the `CommandDefinition` constructor), `SqlBulkCopy` and EF Core raw SQL call sites becomes data edges (ScriptDom). Use `const string`; `static readonly` SQL is invisible. The C# plugin also reports attribute annotations (10 named entries plus the reserved `type`).
81
+ - **Data coupling** - data edges are `confirmed` blast-radius dependents (the gate guards heavily used tables) and stay out of symbol-level communities; `.sql` symbols are never dead code. The contexts view adds `dataResources` and per-candidate `sharedTables` (ownership, external resources); `contextsView.weights.data` defaults to 0. Overlay summary: `sharedTableCount`, `dataResourcesTotal`, `sharedResourcesTotal`, `externalResourcesTotal`; `candidate: <id>` detail: up to 15 `sharedTables` and 15 `dataResources`, fewer when the response limit requires.
82
+ - **Procedures in the contexts view** - a stored procedure's reads and writes count for the candidates that execute it (by canonical name), not for the candidate holding the script; a procedure nobody executes credits its own candidate. With several copies, the bodies of every production copy count (the winner's alone when no copy is production), and docs and test copies add nothing when a production copy exists. A C# class and a same-name table can show up as a collision (possible noise).
83
+ - **One identity per data resource** - when several files define a table, view or procedure, references resolve to a copy outside test files, then outside docs folders, then the codepoint-smallest path (`index.tests` is not consulted). Every copy, and the 2-part ID (`dbo.loans::table`), gives the same dependents in get_blast_radius, find_importers, get_pr_impact, get_context_for_task, the gate and `ctxo blast-radius`. get_logic_slice walks each copy's own body (the winner's for a 2-part ID); get_why_context with a 2-part ID reads the winner's file history.
84
+ - **Annotations in MCP output** - get_logic_slice includes node `annotations` at L4 only; L1-L3 and get_context_for_task omit them.
85
+ - **Test files by annotation** - `Fact`, `Theory`, `SkippableFact`, `SkippableTheory`, `Test`, `TestCase`, `TestCaseSource`, `TestFixture`, `TestMethod`, `DataTestMethod`, `TestClass` mark test files; name-only `*Test.cs` / `*Tests.cs` matches need one once C# annotations exist. Attributes derived from `Fact` or `Theory` and bare capital `Tests/` folders are not recognised: add `index.tests`. Run a full `ctxo index` after upgrading `@ctxo/lang-csharp`.
86
+ - **`csharp_tier` doctor check** - warns when the Roslyn helper cannot restore its NuGet packages (ScriptDom); the C# plugin then falls back to tree-sitter. The helper declares `RollForward` `Major`, so it can run on a newer .NET runtime when no .NET 8 runtime is installed. `ctxo watch` restarts an idle or crashed Roslyn keep-alive once and falls back to tree-sitter if that fails, instead of writing a C# file with zero symbols.
87
+
88
+ ### Co-change engine, incremental indexing, one snapshot per commit (#157)
89
+
90
+ - Co-change is built from one `git log` pass with its own window, independent of `--max-history` (which controls per-file intent depth only). Config: `cochange:` in `.ctxo/config.yaml` (maxCommits 2000 or "all", since, maxFilesPerCommit 30, minShared 2, include, exclude). Overrides: `ctxo index --cochange-commits <n|all>`, `--cochange-since <duration>`. Merge commits are excluded; commits above `maxFilesPerCommit` are skipped and counted (`massSkipped`); renames are followed.
91
+ - Every blast-radius path uses co-change: get_blast_radius, get_pr_impact, get_context_for_task, `ctxo blast-radius`, the safe-edit gate hook and `ctxo gate --preview`. `likely` includes co-change-upgraded dependents, `coChangeFrequency` is filled, and get_blast_radius adds `coChangedFiles` (up to 10 files, informational). The gate may fire somewhat more often.
92
+ - Incremental indexing: `ctxo index --files <path...>` (alias `--file`) and `ctxo index --commit <rev>` re-index part of the index and recompute everything global from the merged index. The v2 post-commit hook runs `ctxo index --commit HEAD` once per commit, guarded by `[ -d .ctxo ]`. `ctxo init` and `ctxo doctor --fix` upgrade old hook blocks in place and refuse to write into a global or system `core.hooksPath`. New `git_hooks` doctor check.
93
+ - One community snapshot per commit. Drift and boundary confidence count `distinctCommits` (low below 3, medium 3-6, high 7+). `ctxo index --check` records no snapshot.
94
+ - `ctxo <command> --help` prints help and runs nothing.
54
95
 
55
96
  ## New in v0.8
56
97
 
@@ -70,7 +111,9 @@ Language adapters ship as separate npm packages and are discovered by scanning t
70
111
 
71
112
  ## Key Concepts
72
113
 
73
- - Symbol ID format: "file::name::kind" (e.g., "src/foo.ts::myFn::function")
114
+ - Symbol ID format: "file::name::kind" (e.g., "src/foo.ts::myFn::function", "db/lending.sql::dbo.loans::table")
115
+ - Symbol kinds: function, class, interface, method, variable, type, table, view, procedure
116
+ - Edge kinds: imports, calls, extends, implements, uses, reads, writes, executes (data edges point at 2-part names such as "dbo.loans::table")
74
117
  - All responses include _meta: { totalItems, returnedItems, truncated, totalBytes }
75
118
  - 4 tools support intent filtering: get_blast_radius, get_logic_slice, find_importers, find_dead_code
76
119
  - Large responses auto-truncated at 8KB (configurable via CTXO_RESPONSE_LIMIT)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ctxo/cli",
3
- "version": "0.11.5",
3
+ "version": "0.13.0",
4
4
  "description": "MCP server delivering dependency-aware, history-enriched context for codebases",
5
5
  "type": "module",
6
6
  "engines": {
@@ -40,22 +40,24 @@
40
40
  "sql.js": "^1.14.1",
41
41
  "yaml": "^2.6.1",
42
42
  "zod": "^4.6.7",
43
- "@ctxo/plugin-api": "0.7.3"
43
+ "@ctxo/plugin-api": "0.9.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/node": "^26.6.1",
47
47
  "@types/picomatch": "^4.0.3",
48
48
  "@vitest/coverage-v8": "^5.0.1",
49
49
  "eslint": "^10.6.0",
50
+ "prettier": "2.8.8",
50
51
  "tsup": "^8.4.0",
51
52
  "tsx": "^4.19.4",
52
53
  "typescript": "^6.0.3",
53
54
  "typescript-eslint": "^8.71.1",
54
55
  "vitest": "^5.0.1",
55
- "@ctxo/lang-csharp": "0.7.5",
56
- "@ctxo/lang-go": "0.8.2",
57
- "@ctxo/lang-java": "0.9.1",
58
- "@ctxo/lang-typescript": "0.7.2"
56
+ "@ctxo/lang-csharp": "0.9.0",
57
+ "@ctxo/lang-go": "0.9.1",
58
+ "@ctxo/lang-java": "0.10.1",
59
+ "@ctxo/lang-sql": "0.1.0",
60
+ "@ctxo/lang-typescript": "0.8.1"
59
61
  },
60
62
  "repository": {
61
63
  "type": "git",
@@ -288,7 +288,10 @@ var EDGE_COLORS = {
288
288
  calls: { color: '#59A14F', opacity: 0.7 },
289
289
  extends: { color: '#B07AA1', opacity: 0.8 },
290
290
  implements: { color: '#F28E2B', opacity: 0.8 },
291
- uses: { color: '#BAB0AC', opacity: 0.35 }
291
+ uses: { color: '#BAB0AC', opacity: 0.35 },
292
+ reads: { color: '#76B7B2', opacity: 0.6 },
293
+ writes: { color: '#9C755F', opacity: 0.7 },
294
+ executes: { color: '#EDC948', opacity: 0.7 }
292
295
  };
293
296
 
294
297
  // Boundary violations override edge styling. Red for "this edge crosses
@@ -953,7 +956,10 @@ function buildEdgeLegend() {
953
956
  { name: 'calls', dashed: false },
954
957
  { name: 'extends', dashed: false },
955
958
  { name: 'implements', dashed: false },
956
- { name: 'uses', dashed: true }
959
+ { name: 'uses', dashed: true },
960
+ { name: 'reads', dashed: false },
961
+ { name: 'writes', dashed: false },
962
+ { name: 'executes', dashed: false }
957
963
  ];
958
964
  edgeKinds.forEach(function(ek) {
959
965
  var ec = EDGE_COLORS[ek.name];
@@ -8,3 +8,5 @@ description: Use when reviewing a PR, a diff, or recent changes, to get a full r
8
8
  1. `get_pr_impact` - single call: changed symbols + blast radius + co-change risk.
9
9
  2. For any high-risk symbol it surfaces, follow up with `get_why_context`.
10
10
  3. Summarize risk for the human: what breaks, what needs extra review, what has a bad history.
11
+
12
+ <!-- ctxo-skill v0.13 -->
@@ -13,3 +13,5 @@ Before changing a symbol, in order - do NOT skip:
13
13
  4. After editing, re-check the dependents from step 1.
14
14
 
15
15
  A deterministic guard may block the first edit of a high-impact symbol until you have run get_blast_radius. That is expected - run it, then re-issue the edit.
16
+
17
+ <!-- ctxo-skill v0.13 -->