@ctxo/lang-go 0.7.0 → 0.8.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # @ctxo/lang-go
2
+
3
+ Go language plugin for [Ctxo](https://github.com/alperhankendi/Ctxo). Ships
4
+ two analysis tiers selected at runtime:
5
+
6
+ | Tier | Engine | Activated when |
7
+ |---|---|---|
8
+ | **Full** | `ctxo-go-analyzer` binary (Go stdlib + `x/tools` SSA + CHA) | Go ≥ 1.22 on PATH **and** the bundled analyzer source builds successfully |
9
+ | **Syntax** | `tree-sitter-go` | Go toolchain absent; binary build fails; or analyzer cannot locate `go.mod` / `go.work` |
10
+
11
+ The composite picks at `initialize()` and forwards every `extractSymbols` /
12
+ `extractEdges` call to the active layer. `extractComplexity` is always served
13
+ by tree-sitter — the analyzer intentionally emits empty complexity arrays so
14
+ the two layers compose cleanly.
15
+
16
+ ## What full-tier adds over tree-sitter
17
+
18
+ - **Cross-package symbol-id resolution.** `pkg/a` calling `pkg/b.Do()` produces
19
+ an edge whose target is `pkg/b/file.go::Do::function`, not a synthetic
20
+ placeholder. Every `get_blast_radius`, `find_importers`, and `get_logic_slice`
21
+ query that crosses a package boundary now returns real results.
22
+ - **`implements` edges.** Go's structural typing means interface satisfaction
23
+ is invisible to a syntactic parser; the analyzer pairs every concrete type
24
+ against every interface and emits an edge when `types.Implements(T, I)` or
25
+ `types.Implements(*T, I)` is true.
26
+ - **`extends` edges for embedding.** Anonymous fields (struct embedding) and
27
+ embedded interface members produce explicit hierarchy edges so
28
+ `get_class_hierarchy` works on Go.
29
+ - **Generics with `typeArgs` metadata.** `List[int]` and `List[string]` both
30
+ produce a `uses` edge to the unconstructed `List` symbol; the type
31
+ arguments are preserved on edge metadata for future query precision.
32
+ See [ADR-013 §4 Q4](../../docs/architecture/ADR/adr-013-go-full-tier-via-ctxo-go-analyzer-binary.md#open-question-decisions-resolved-2026-04-13).
33
+ - **Dead-code detection via CHA reachability.** Unreachable functions and
34
+ methods are emitted in a single `dead` record. A reflect-safe pass marks
35
+ methods of any type touched by `reflect.TypeOf` / `reflect.ValueOf` /
36
+ `reflect.New` or `json.Marshal` / `Unmarshal` / `NewDecoder` / `NewEncoder`
37
+ as live to prevent false positives.
38
+
39
+ ## How the binary is built
40
+
41
+ On first use, `GoAnalyzerAdapter.initialize()` hashes the analyzer source +
42
+ `go version` and runs `go build -trimpath -o
43
+ ~/.cache/ctxo/lang-go-analyzer/<hash-goVersion>/ctxo-go-analyzer[.exe]`.
44
+ Subsequent runs reuse the cached binary. No build happens during `npm install`
45
+ to keep package installs sandbox-safe (ADR-013 §4 Q2).
46
+
47
+ If `go` is not on PATH, build fails, or no `go.mod`/`go.work` is found, the
48
+ plugin transparently falls back to tree-sitter and `ctxo doctor` surfaces the
49
+ missing toolchain.
50
+
51
+ ## Limitations (v0.8.0-alpha.0)
52
+
53
+ - **Library mode is approximate.** Without a `main.main` to anchor RTA-style
54
+ reachability, every exported function/method becomes a root. This
55
+ over-approximates liveness — fewer false dead-code reports, but symbols
56
+ that nothing actually consumes still look live.
57
+ - **Generic types skipped from `implements` / `extends` enumeration.** Pairing
58
+ generic named types is unstable in current `go/types`; we revisit when the
59
+ upstream API stabilizes.
60
+ - **Closures and reflective construction.** Functions passed to stdlib
61
+ callbacks (`sort.Slice(..., func(i,j) bool {...})`) follow a CHA edge so
62
+ they ARE reachable; constructed-via-reflection types are kept alive only
63
+ for the explicit reflect/json call sites listed above.
64
+
65
+ ## Architecture and rationale
66
+
67
+ See [ADR-013](../../docs/architecture/ADR/adr-013-go-full-tier-via-ctxo-go-analyzer-binary.md)
68
+ for the full decision record (alternative options considered, library
69
+ choices, open question resolutions). Mirrors the C# pattern from
70
+ [ADR-007](../../docs/architecture/ADR/adr-007-csharp-roslyn-lsp.md).
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { ILanguageAdapter, SymbolKind, SymbolNode, GraphEdge, ComplexityMetrics, CtxoLanguagePlugin } from '@ctxo/plugin-api';
2
2
  import Parser, { Tree, SyntaxNode } from 'tree-sitter';
3
3
 
4
- type Language = Parameters<InstanceType<typeof Parser>['setLanguage']>[0];
4
+ type Language = Parameters<InstanceType<typeof Parser>['setLanguage']>[0] | object;
5
5
 
6
6
  declare abstract class TreeSitterAdapter implements ILanguageAdapter {
7
7
  abstract readonly extensions: readonly string[];
@@ -38,6 +38,73 @@ declare class GoAdapter extends TreeSitterAdapter {
38
38
  private findFirstExportedSymbolId;
39
39
  }
40
40
 
41
+ /**
42
+ * Full-tier Go adapter. Owns the analyzer binary lifecycle and an
43
+ * in-memory cache of batch results keyed by project-relative file path.
44
+ *
45
+ * Caching strategy: the first extractSymbols/extractEdges call after
46
+ * initialize() triggers a single batch analysis run. Subsequent calls
47
+ * read from cache. This keeps the ILanguageAdapter contract intact
48
+ * (per-file methods) while the binary runs once per index.
49
+ */
50
+ declare class GoAnalyzerAdapter implements ILanguageAdapter {
51
+ readonly extensions: readonly [".go"];
52
+ readonly tier: "full";
53
+ private moduleRoot;
54
+ private binaryPath;
55
+ private modulePathPrefix;
56
+ private cache;
57
+ private deadSymbolIds;
58
+ private hasMain;
59
+ private timedOut;
60
+ private batchPromise;
61
+ private initialized;
62
+ isSupported(filePath: string): boolean;
63
+ isReady(): boolean;
64
+ initialize(rootDir: string): Promise<void>;
65
+ /**
66
+ * Returns the dead-symbol set emitted by the last batch run. Empty until
67
+ * extractSymbols/Edges has been called at least once. Consumed by cli's
68
+ * find_dead_code MCP tool via the composite delegate.
69
+ */
70
+ getDeadSymbolIds(): Set<string>;
71
+ /** True when the analyzer ran in binary mode (precise dead-code). */
72
+ reachabilityHasMain(): boolean;
73
+ /** True when reach analysis exceeded the deadline (degraded precision). */
74
+ reachabilityTimedOut(): boolean;
75
+ extractSymbols(filePath: string, _source: string): Promise<SymbolNode[]>;
76
+ extractEdges(filePath: string, _source: string): Promise<GraphEdge[]>;
77
+ extractComplexity(_filePath: string, _source: string): Promise<ComplexityMetrics[]>;
78
+ dispose(): Promise<void>;
79
+ private ensureBatch;
80
+ private runBatch;
81
+ private absorbBatch;
82
+ private normalizePath;
83
+ private buildPathRewriter;
84
+ }
85
+
86
+ /**
87
+ * Picks between the full-tier ctxo-go-analyzer and the syntax-tier
88
+ * tree-sitter adapter at initialize() time. Symbols and edges come from
89
+ * whichever delegate is active; complexity is ALWAYS sourced from
90
+ * tree-sitter because the binary intentionally emits empty complexity.
91
+ */
92
+ declare class GoCompositeAdapter implements ILanguageAdapter {
93
+ private analyzer;
94
+ private treeSitter;
95
+ constructor();
96
+ initialize(rootDir: string): Promise<void>;
97
+ dispose(): Promise<void>;
98
+ extractSymbols(filePath: string, source: string): Promise<SymbolNode[]>;
99
+ extractEdges(filePath: string, source: string): Promise<GraphEdge[]>;
100
+ extractComplexity(filePath: string, source: string): Promise<ComplexityMetrics[]>;
101
+ isSupported(filePath: string): boolean;
102
+ setSymbolRegistry(registry: Map<string, SymbolKind>): void;
103
+ /** Exposed for cli optimizations. Null when running in syntax tier. */
104
+ getAnalyzerDelegate(): GoAnalyzerAdapter | null;
105
+ getTier(): 'full' | 'syntax' | 'unavailable';
106
+ }
107
+
41
108
  declare const plugin: CtxoLanguagePlugin;
42
109
 
43
- export { GoAdapter, TreeSitterAdapter, plugin as default, plugin };
110
+ export { GoAdapter, GoAnalyzerAdapter, GoCompositeAdapter, TreeSitterAdapter, plugin as default, plugin };