@c4a/extract 0.5.35-beta.1 → 0.5.36

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.
Files changed (2) hide show
  1. package/README.md +147 -103
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,61 +1,162 @@
1
1
  # @c4a/extract
2
2
 
3
- Code extraction framework: plugin protocol, registry, digest generation, and shared parsing infrastructure.
3
+ Code extraction framework for C4A. It owns the language-plugin protocol, repository runner, raw code snapshot contract, digest generation, and shared Tree-sitter parsing utilities.
4
4
 
5
- ## Role in the monorepo
5
+ ## Role in the Monorepo
6
6
 
7
- Defines the standard extraction protocol (`ExtractionPlugin` interface) and provides:
8
- - Plugin registry for language-specific extraction plugins
9
- - ExtractionResult v2 schema (SymbolInfo + RelationInfo)
10
- - Digest generation (exported/internal symbol partitioning) and symbol diff
11
- - Shared Tree-sitter parsing and file scanning utilities
7
+ `@c4a/extract` is the protocol and runner layer under `context capture --code`.
12
8
 
13
- **Depends on:** `core`
14
- **Depended on by:** `extract-ts`, `daemon`, `api`, `e2e`
9
+ - Language plugins implement `ExtractionPlugin` and return `ExtractionResult` v2.
10
+ - The runner loads one or more plugins, scans repository modules, emits progress/module-error/summary events, and can build a raw code snapshot payload.
11
+ - `@c4a/context-cli` writes the snapshot under `.context/raw/aspect/code/<source-slug>/<snapshot-id>/`.
12
+ - `context compile --code <source-slug>` reads that snapshot and materializes package/category/symbol knowledge Nodes.
15
13
 
16
- ## Key exports
14
+ **Depends on:** `@c4a/core`, `web-tree-sitter`, `zod`
17
15
 
18
- ### Plugin protocol
16
+ **Depended on by:** `@c4a/extract-ts`, `@c4a/context-cli`, `@c4a/daemon`, `@c4a/e2e`
19
17
 
20
- - `ExtractionPlugin` — interface that language plugins implement
21
- - `ExtractionPluginRegistry` — register/resolve plugins by source manifest
22
- - `FileSystem` — abstraction for file access (plugins don't touch disk directly)
23
- - `SourceInfo` / `ManifestInfo` — source metadata passed to `canHandle`/`detectEntries`
24
- - `EntryDetectionResult` — package info + entry files (supports monorepo recursion via `subPackages`)
25
- - `PatternDetectionResult` — framework pattern detection (endpoints, implicit deps)
18
+ ## Protocol Layers
26
19
 
27
- ### Types (ExtractionResult v2)
20
+ ### 1. Language Plugin Protocol
28
21
 
29
- - `ExtractionResult` standard output format: `{ version: "2", meta, package, files, symbols, relations, stats }`
30
- - `SymbolInfo` — extracted symbol: name, kind, visibility, file, line, members, params, returnType, etc.
31
- - `RelationInfo` — extracted relation: type, from, to, grounding, confidence, source
22
+ Language packages implement `ExtractionPlugin` from `protocol.ts`.
32
23
 
33
- ### Digest & diff
24
+ ```ts
25
+ interface ExtractionPlugin {
26
+ id: string;
27
+ languages: string[];
28
+ packageManagers: string[];
29
+ canHandle(source: SourceInfo): boolean;
30
+ detectEntries(manifest: ManifestInfo, fs: FileSystem): Promise<EntryDetectionResult>;
31
+ extractSymbols(entries: EntryFile[], fs: FileSystem): Promise<ExtractionResult>;
32
+ detectPatterns?(fs: FileSystem): Promise<PatternDetectionResult>;
33
+ }
34
+ ```
35
+
36
+ Important constraints:
37
+
38
+ - Plugins read through `FileSystem`; they should not access `node:fs` directly.
39
+ - `detectEntries()` must return package identity, package kind, language, optional version, and entry files.
40
+ - `extractSymbols()` must return `ExtractionResult` v2 with stable symbols and relations.
41
+ - `detectEntries()` is called before `extractSymbols()`; plugins may keep per-detection package context between those calls.
42
+
43
+ ### 2. ExtractionResult v2
44
+
45
+ Every plugin returns:
46
+
47
+ ```ts
48
+ {
49
+ version: "2",
50
+ meta: { extractedAt, pluginId, commitHash, language },
51
+ package: { name, kind, language, version? },
52
+ files: [{ path, language, lines }],
53
+ symbols: SymbolInfo[],
54
+ relations: RelationInfo[],
55
+ stats: { files, lines, exportedSymbols, internalSymbols, relations }
56
+ }
57
+ ```
58
+
59
+ `SymbolInfo` supports:
60
+
61
+ - identity: `name`, `kind`, `visibility`, `file`, `line`, `endLine`
62
+ - structure: nested `members`
63
+ - type surfaces: `params`, `returnType`, `typeAnnotation`, `extends`, `implements`, `propsType`, `unionValues`
64
+ - source documentation: `doc`
65
+
66
+ `RelationInfo` supports code edges such as `imports`, `imports_type`, `calls`, `extends`, `implements`, `param_type`, `return_type`, `of_type`, `depends_on`, and `contains`.
67
+
68
+ ### 3. Repository Runner Protocol
69
+
70
+ The package exposes `c4a-extract-code`, a NDJSON runner used by `context capture --code`.
71
+
72
+ Input is JSON on stdin:
73
+
74
+ ```json
75
+ {
76
+ "repoPath": "/path/to/repo",
77
+ "modules": ["packages/example"],
78
+ "commitHash": "abc123",
79
+ "pathFilter": {},
80
+ "plugins": [{ "package": "@c4a/extract-ts", "exportName": "TypeScriptPlugin" }],
81
+ "snapshot": {
82
+ "sourceId": "aspect:code:example",
83
+ "sourceSlug": "example",
84
+ "snapshotId": "code-abc123-deadbeef",
85
+ "codeSnapshotContractVersion": "<contract-version>",
86
+ "scriptHash": "sha256:...",
87
+ "toolchain": {
88
+ "manager_package": "@c4a/context-cli",
89
+ "manager_version": "<manager-version>",
90
+ "runner_package": "@c4a/extract",
91
+ "runner_package_version": "<runner-version>",
92
+ "runner_bin": "c4a-extract-code",
93
+ "plugin_package": "@c4a/extract-ts",
94
+ "plugin_package_version": "<plugin-version>",
95
+ "plugin_export": "TypeScriptPlugin"
96
+ }
97
+ }
98
+ }
99
+ ```
100
+
101
+ Output is one JSON object per line:
102
+
103
+ - `{ "type": "progress", "phase": "scanning|parsing|uploading", ... }`
104
+ - `{ "type": "module_error", "module_name": "...", "module_path": "...", "error": "..." }`
105
+ - `{ "type": "summary", "extraction": ..., "snapshot": ... }`
106
+ - `{ "type": "error", "code": "runner-failed", "message": "..." }`
107
+
108
+ The runner does not write `.context` directly. It returns snapshot files in the summary; `@c4a/context-cli` validates and writes them atomically.
109
+
110
+ ### 4. Raw Code Snapshot Contract
34
111
 
35
- - `generateDigest(result)` ExtractionResult -> DigestData (exported/internal symbol partitioning)
36
- - `generateSymbolDiff(current, previous)` — compare two digests, output `{ added, removed }`
37
- - `DigestData` / `SymbolDiff` types
112
+ When `snapshot` input is provided, the runner builds these files:
38
113
 
39
- ### Shared utilities
114
+ | File | Purpose |
115
+ |---|---|
116
+ | `source.yaml` | Source manifest for the code aspect source |
117
+ | `manifest.json` | Snapshot manifest: contract version, toolchain, counts, hash, dirty state |
118
+ | `_meta.yaml` | Backward-compatible snapshot metadata and input summary |
119
+ | `digests.jsonl` | Per-module digest rows with version, hash, dirty state, and digest payload |
120
+ | `source-files.jsonl` | Source-to-module/digest mapping |
121
+ | `packages.jsonl` | Package rows: name, kind, language, module path, optional version/description |
122
+ | `symbols.jsonl` | Flat symbol rows; nested members are flattened and retain package/module fields |
123
+ | `edges.jsonl` | Code relation rows with package/module/version/hash fields |
40
124
 
41
- - `initParser()` / `parseFile()` Tree-sitter parser initialization and per-file AST parsing
42
- - `scanSourceFiles()` / `detectModules()` / `detectModuleAt()` — file system scanning and module detection
43
- - `getGitCommitHash()` — reads the current git commit hash
44
- - `detectTechStack()` — identifies languages and frameworks in a repository
125
+ `@c4a/context-cli` validates this contract before projection. Required fields include package/module identity, version labels on digest/source-file/edge rows, symbol identity fields, and matching edge/digest versions.
45
126
 
46
- ## Writing a language plugin
127
+ During projection, code-owned Sections receive code `source_ref` values derived from these rows:
47
128
 
48
- To add support for a new language (e.g., Python), create a new package `@c4a/extract-python` that implements `ExtractionPlugin`:
129
+ - package rows: `src-N#package:<package>@<hash>`
130
+ - symbol rows: `src-N#symbol:<locator>:<kind>@<hash>`
49
131
 
50
- ```typescript
132
+ These refs are verified against the raw code snapshot JSONL indexes. They are separate from prose evidence refs, because code snapshots use `evidence.mode: none` and do not create raw block manifests.
133
+
134
+ ## Writing a New Language Plugin
135
+
136
+ Create a package such as `@c4a/extract-python` and export an `ExtractionPlugin`.
137
+
138
+ Minimum requirements:
139
+
140
+ 1. Detect the language manifest in `canHandle()` and `detectEntries()`.
141
+ 2. Return stable package identity: package name, kind, language, and version when available.
142
+ 3. Return `subPackages` when one manifest represents a nested package layout.
143
+ 4. Resolve public entry files so exported symbols can be distinguished from internal symbols.
144
+ 5. Emit `SymbolInfo[]` with stable `name`, `kind`, `visibility`, `file`, `line`, and `endLine`.
145
+ 6. Emit `RelationInfo[]` for imports and important type/inheritance/use edges.
146
+ 7. Keep paths module-relative inside the plugin; the repository runner prefixes them to repo-relative paths.
147
+ 8. Register the plugin in the runner input used by `context capture --code`.
148
+
149
+ Example skeleton:
150
+
151
+ ```ts
51
152
  import type {
153
+ EntryDetectionResult,
154
+ EntryFile,
52
155
  ExtractionPlugin,
53
156
  ExtractionResult,
54
- EntryDetectionResult,
55
157
  FileSystem,
56
158
  ManifestInfo,
57
159
  SourceInfo,
58
- EntryFile,
59
160
  } from "@c4a/extract";
60
161
 
61
162
  export class PythonPlugin implements ExtractionPlugin {
@@ -64,85 +165,28 @@ export class PythonPlugin implements ExtractionPlugin {
64
165
  readonly packageManagers = ["pip"];
65
166
 
66
167
  canHandle(source: SourceInfo): boolean {
67
- // Return true if this source has a pyproject.toml or setup.py
68
- return source.manifests.some(
69
- (m) => m.type === "pyproject.toml",
70
- );
168
+ return source.manifests.some((manifest) => manifest.type === "pyproject.toml");
71
169
  }
72
170
 
73
- async detectEntries(
74
- manifest: ManifestInfo,
75
- fs: FileSystem,
76
- ): Promise<EntryDetectionResult> {
77
- // Parse pyproject.toml to find package name, kind, entry points
78
- // Return: { package: { name, kind, language }, entries: [...] }
171
+ async detectEntries(manifest: ManifestInfo, fs: FileSystem): Promise<EntryDetectionResult> {
172
+ // Parse pyproject.toml/setup metadata and return package + entry files.
79
173
  }
80
174
 
81
- async extractSymbols(
82
- entries: EntryFile[],
83
- fs: FileSystem,
84
- ): Promise<ExtractionResult> {
85
- // Use Tree-sitter with Python grammar to parse AST
86
- // Extract SymbolInfo[] (functions, classes, etc.)
87
- // Extract RelationInfo[] (imports, inheritance, etc.)
88
- // Return ExtractionResult v2
175
+ async extractSymbols(entries: EntryFile[], fs: FileSystem): Promise<ExtractionResult> {
176
+ // Parse entry graph, classify exported/internal symbols, emit relations.
89
177
  }
90
178
  }
91
179
  ```
92
180
 
93
- ### Plugin interface
94
-
95
- | Method | When called | Returns |
96
- |--------|------------|---------|
97
- | `canHandle(source)` | Registry resolves which plugin handles a source | `boolean` |
98
- | `detectEntries(manifest, fs)` | Layer 0: identify package info + entry files | `EntryDetectionResult` |
99
- | `extractSymbols(entries, fs)` | Layer 1: AST parsing + symbol extraction | `ExtractionResult` |
100
- | `detectPatterns?(fs)` | Layer 2 (optional): framework-specific patterns | `PatternDetectionResult` |
101
-
102
- ### Registration
103
-
104
- Plugins are currently registered statically in the daemon:
105
-
106
- ```typescript
107
- import { ExtractionPluginRegistry } from "@c4a/extract";
108
- import { TypeScriptPlugin } from "@c4a/extract-ts";
109
-
110
- const registry = new ExtractionPluginRegistry();
111
- registry.register(new TypeScriptPlugin());
112
-
113
- const plugin = registry.resolve(sourceInfo);
114
- if (plugin) {
115
- const entries = await plugin.detectEntries(manifest, fs);
116
- const result = await plugin.extractSymbols(entries.entries, fs);
117
- }
118
- ```
119
-
120
- ### FileSystem abstraction
121
-
122
- Plugins access files through the `FileSystem` interface, not directly via `node:fs`. This enables future API-based file loading without changing plugin code.
181
+ ## Relationship to Code Compile and Obsidian
123
182
 
124
- ```typescript
125
- interface FileSystem {
126
- readFile(path: string): Promise<string>;
127
- readdir(path: string): Promise<string[]>;
128
- exists(path: string): Promise<boolean>;
129
- readJson<T = unknown>(path: string): Promise<T>;
130
- }
131
- ```
183
+ `@c4a/extract` is upstream of code compile; it does not render knowledge itself.
132
184
 
133
- ### ExtractionResult v2 format
185
+ - `context capture --code` uses the runner to produce raw code snapshots.
186
+ - `context compile --code` consumes `packages.jsonl`, `symbols.jsonl`, `edges.jsonl`, and `digests.jsonl` to build package/category/symbol Nodes such as `pkg`, `pkg/components`, and `pkg/symbol/button`.
187
+ - Obsidian Render reads the compiled Markdown, `_edges.yaml`, and `_external.yaml`. It does not read raw runner snapshots directly.
134
188
 
135
- ```typescript
136
- {
137
- version: "2",
138
- meta: { extractedAt, pluginId, commitHash, language },
139
- package: { name, kind, language },
140
- files: [{ path, language, lines }],
141
- symbols: SymbolInfo[], // exported + internal
142
- relations: RelationInfo[], // imports, calls, type refs, etc.
143
- stats: { files, lines, exportedSymbols, internalSymbols, relations },
144
- }
145
- ```
189
+ That means language plugins affect Obsidian only through the compiled knowledge graph: better symbols/relations produce better symbol Nodes, graph edges, source refs, and source-status chips.
146
190
 
147
191
  ## Development
148
192
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c4a/extract",
3
- "version": "0.5.35-beta.1",
3
+ "version": "0.5.36",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "c4a-extract-code": "./bin/c4a-extract-code.js"