@c4a/extract 0.7.1 → 0.7.5
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 +22 -26
- package/README.zh-CN.md +17 -17
- package/bin/c4a-extract-code.js +7 -5
- package/index.js +994 -55
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -14,13 +14,12 @@ knowledge through the normal lifecycle.
|
|
|
14
14
|
|
|
15
15
|
## Package Role
|
|
16
16
|
|
|
17
|
-
`@c4a/extract` is the protocol and runner layer used by
|
|
18
|
-
project extraction phases such as `extractTs({ source, collection: "codegraph" })`.
|
|
17
|
+
`@c4a/extract` is the protocol and runner layer used by Code Indexer Providers.
|
|
19
18
|
|
|
20
19
|
- Language plugins implement `ExtractionPlugin` and return `ExtractionResult` v2.
|
|
21
20
|
- The runner loads one or more plugins, scans repository modules, emits progress/module-error/summary events, and can build an optional low-level code snapshot payload.
|
|
22
|
-
-
|
|
23
|
-
-
|
|
21
|
+
- The selected Provider invokes the runner through the controlled Indexer workset and returns typed facts and artifacts.
|
|
22
|
+
- Context stages Candidate state under `.tmp/context-runtime/`; approved Markdown is written only by Review apply and close.
|
|
24
23
|
|
|
25
24
|
**Depends on:** `@c4a/core`, `web-tree-sitter`, `zod`
|
|
26
25
|
|
|
@@ -31,12 +30,12 @@ language plugins + repository runner
|
|
|
31
30
|
↓
|
|
32
31
|
versioned raw code snapshot
|
|
33
32
|
↓
|
|
34
|
-
|
|
33
|
+
Indexer facts → reader-oriented Candidates → approved knowledge
|
|
35
34
|
```
|
|
36
35
|
|
|
37
36
|
Knowledge-workspace users normally reach this package through the installed
|
|
38
|
-
Agent entry and
|
|
39
|
-
|
|
37
|
+
Agent entry and selected Code Indexer Provider. The runner protocol below is
|
|
38
|
+
for parser authors, Provider authors, and Context maintainers.
|
|
40
39
|
|
|
41
40
|
## Protocol Layers
|
|
42
41
|
|
|
@@ -108,10 +107,9 @@ Every plugin returns:
|
|
|
108
107
|
|
|
109
108
|
### 3. Repository Runner Protocol
|
|
110
109
|
|
|
111
|
-
The package exposes `c4a-extract-code`, a low-level NDJSON runner.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
input for normal workspace operation.
|
|
110
|
+
The package exposes `c4a-extract-code`, a low-level NDJSON runner. Code Indexer
|
|
111
|
+
Providers may invoke it as an implementation detail; agents should not
|
|
112
|
+
hand-build runner input for normal workspace operation.
|
|
115
113
|
|
|
116
114
|
Input is JSON on stdin:
|
|
117
115
|
|
|
@@ -192,9 +190,8 @@ Minimum requirements:
|
|
|
192
190
|
5. Emit `SymbolInfo[]` with stable `name`, `kind`, `visibility`, `file`, `line`, and `endLine`.
|
|
193
191
|
6. Emit `RelationInfo[]` for imports and important type/inheritance/use edges.
|
|
194
192
|
7. Keep paths module-relative inside the plugin; the repository runner prefixes them to repo-relative paths.
|
|
195
|
-
8. Register the plugin in the
|
|
196
|
-
|
|
197
|
-
`src/index.ts` and execute it through `context run <phase-id>`.
|
|
193
|
+
8. Register the plugin in the Code Indexer Provider's runner configuration.
|
|
194
|
+
User-facing workspaces select the Provider through `src/indexers.yaml`.
|
|
198
195
|
|
|
199
196
|
Example skeleton:
|
|
200
197
|
|
|
@@ -229,31 +226,30 @@ export class PythonPlugin implements ExtractionPlugin {
|
|
|
229
226
|
}
|
|
230
227
|
```
|
|
231
228
|
|
|
232
|
-
## Relationship to
|
|
229
|
+
## Relationship to the Code Indexer
|
|
233
230
|
|
|
234
|
-
`@c4a/extract` is upstream of the
|
|
235
|
-
|
|
231
|
+
`@c4a/extract` is upstream of the Code Indexer Provider; it does not render
|
|
232
|
+
approved knowledge itself.
|
|
236
233
|
|
|
237
234
|
- `context source add repo <name> --local <repo-or-subdir>` registers the code
|
|
238
235
|
source boundary.
|
|
239
|
-
- `src/
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
state.
|
|
236
|
+
- `src/indexers.yaml` binds the source scope and reader requirements to a Code
|
|
237
|
+
Indexer Provider.
|
|
238
|
+
- The current Route prepares a controlled workset and the Provider returns the
|
|
239
|
+
current Indexer result.
|
|
240
|
+
- Context writes draft Candidates and recoverable runtime artifacts under
|
|
241
|
+
`.tmp/context-runtime/`; that directory is not committed state.
|
|
246
242
|
- `context review html` and `context review apply` own human approval and
|
|
247
243
|
approved Markdown materialization.
|
|
248
244
|
- `context close`, `context verify`, and `context build` own final package
|
|
249
245
|
readiness.
|
|
250
246
|
|
|
251
247
|
That means language plugins affect published knowledge only through the current
|
|
252
|
-
|
|
248
|
+
Indexer flow: better symbols/relations produce better draft Candidates, stable
|
|
253
249
|
`repo:<source>#symbol:...` source refs, review evidence, approved Markdown, and
|
|
254
250
|
package output.
|
|
255
251
|
|
|
256
|
-
Approved
|
|
252
|
+
Approved code knowledge may localize canonical refs as
|
|
257
253
|
`src-N#symbol:<file>:<symbol>:<kind>@<digest>`. The file segment is part of the
|
|
258
254
|
deterministic evidence identity used by verification; agents copy the complete
|
|
259
255
|
ref as an opaque token.
|
package/README.zh-CN.md
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
Tree-sitter 解析工具。
|
|
8
8
|
|
|
9
9
|
它不判断代码对产品或读者意味着什么,也不写入正式知识。语言插件只输出结构事实;
|
|
10
|
-
Context
|
|
11
|
-
|
|
10
|
+
Code Indexer Provider 将这些事实组织成当前 Indexer Result;Context 负责来源身份、
|
|
11
|
+
Candidate 状态和审核通过的知识。
|
|
12
12
|
|
|
13
13
|
## 在知识生产链中的职责
|
|
14
14
|
|
|
@@ -19,19 +19,19 @@ Context 运行时将这些事实绑定到来源身份、暂存审核候选,并
|
|
|
19
19
|
↓
|
|
20
20
|
版本化原始代码快照
|
|
21
21
|
↓
|
|
22
|
-
|
|
22
|
+
Indexer 事实 → 面向读者的 Candidate → 正式知识
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
- 语言 package 实现 `ExtractionPlugin` 并返回 `ExtractionResult` v2。
|
|
26
26
|
- Runner 加载一个或多个插件,扫描仓库模块,输出 progress、module-error 和
|
|
27
27
|
summary 事件,并可生成底层代码快照。
|
|
28
|
-
-
|
|
29
|
-
-
|
|
28
|
+
- 选中的 Code Indexer Provider 通过受控 workset 调用 Runner。
|
|
29
|
+
- Candidate 和运行快照留在 `.tmp/context-runtime/`;只有 Review apply/close 会写入正式
|
|
30
30
|
Markdown。
|
|
31
31
|
|
|
32
|
-
知识工作区用户通常通过已安装的 Agent
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
知识工作区用户通常通过已安装的 Agent 入口和选中的 Code Indexer Provider 使用
|
|
33
|
+
本包,不需要手工构造 Runner 输入。下面的协议主要面向解析器作者、Provider 作者
|
|
34
|
+
和 Context 维护者。
|
|
35
35
|
|
|
36
36
|
**依赖:** `@c4a/core`、`web-tree-sitter`、`zod`
|
|
37
37
|
|
|
@@ -99,8 +99,8 @@ denominator。
|
|
|
99
99
|
|
|
100
100
|
### 3. Repository Runner
|
|
101
101
|
|
|
102
|
-
包暴露底层 NDJSON Runner `c4a-extract-code
|
|
103
|
-
|
|
102
|
+
包暴露底层 NDJSON Runner `c4a-extract-code`。Code Indexer Provider 可以把它作为
|
|
103
|
+
实现细节调用;Agent 不应手工生成 stdin Payload。
|
|
104
104
|
|
|
105
105
|
Runner 输出一行一个 JSON 对象:
|
|
106
106
|
|
|
@@ -148,20 +148,20 @@ Agent 将完整 `source_ref` 当作不透明 token 复制,不自行解析或
|
|
|
148
148
|
5. 输出稳定的符号名、kind、visibility、文件和行范围;
|
|
149
149
|
6. 输出导入、调用、类型、继承或使用关系;
|
|
150
150
|
7. 插件内部保持 module-relative 路径,由 Runner 加上 repo-relative 前缀;
|
|
151
|
-
8.
|
|
151
|
+
8. 在 Code Indexer Provider 的 Runner 配置中注册插件。
|
|
152
152
|
|
|
153
|
-
## 与
|
|
153
|
+
## 与 Code Indexer 的关系
|
|
154
154
|
|
|
155
|
-
`@c4a/extract`
|
|
155
|
+
`@c4a/extract` 位于 Code Indexer Provider 上游,不渲染正式知识。典型流程是:
|
|
156
156
|
|
|
157
157
|
1. 用户确认代码来源边界;
|
|
158
|
-
2. `src/
|
|
159
|
-
3. 当前 Route
|
|
160
|
-
4.
|
|
158
|
+
2. `src/indexers.yaml` 将来源范围和读者需求绑定到 Code Indexer Provider;
|
|
159
|
+
3. 当前 Route 准备受控 workset,Provider 返回当前 Indexer Result;
|
|
160
|
+
4. Context 生成待审 Candidate 和可恢复运行态;
|
|
161
161
|
5. review apply 将批准内容写入正式知识;
|
|
162
162
|
6. close、verify 和 build 完成本轮并生成知识包。
|
|
163
163
|
|
|
164
|
-
|
|
164
|
+
因此,更好的符号和关系会产生更可靠的 Candidate、来源引用和知识包,但业务分类继续由
|
|
165
165
|
Agent 与用户依据证据决定。
|
|
166
166
|
|
|
167
167
|
## 开发
|
package/bin/c4a-extract-code.js
CHANGED
|
@@ -13163,6 +13163,8 @@ function keyTokens(key) {
|
|
|
13163
13163
|
return key.replace(/([a-z0-9])([A-Z])/gu, "$1-$2").replace(/[^A-Za-z0-9]+/gu, "-").toLowerCase().split("-").filter(Boolean);
|
|
13164
13164
|
}
|
|
13165
13165
|
function sensitiveKey(key, value) {
|
|
13166
|
+
if (value === INDEXER_OUTPUT_REDACTION_MARKER)
|
|
13167
|
+
return false;
|
|
13166
13168
|
const tokens = keyTokens(key);
|
|
13167
13169
|
if (tokens.length === 0)
|
|
13168
13170
|
return false;
|
|
@@ -13204,12 +13206,12 @@ function replaceWithCount(value, pattern, replacement, count) {
|
|
|
13204
13206
|
function redactKnownText(value, count) {
|
|
13205
13207
|
let output = value;
|
|
13206
13208
|
output = replaceWithCount(output, /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/gu, INDEXER_OUTPUT_REDACTION_MARKER, count);
|
|
13207
|
-
output = replaceWithCount(output,
|
|
13209
|
+
output = replaceWithCount(output, new RegExp(`(\\bauthorization\\s*:\\s*(?:bearer|basic)\\s+)` + `(?!${escapeRegExp(INDEXER_OUTPUT_REDACTION_MARKER)})[^\\s,;]+`, "giu"), (_match, prefix) => `${prefix}${INDEXER_OUTPUT_REDACTION_MARKER}`, count);
|
|
13208
13210
|
output = replaceWithCount(output, /([a-z][a-z0-9+.-]*:\/\/)[^\s/@:]+:[^\s/@]+@/giu, (_match, prefix) => `${prefix}${INDEXER_OUTPUT_REDACTION_MARKER}@`, count);
|
|
13209
|
-
output = replaceWithCount(output,
|
|
13211
|
+
output = replaceWithCount(output, new RegExp(`([?&](?:access_token|refresh_token|api_key|password|secret)=)` + `(?!${escapeRegExp(INDEXER_OUTPUT_REDACTION_MARKER)})[^&#\\s]+`, "giu"), (_match, prefix) => `${prefix}${INDEXER_OUTPUT_REDACTION_MARKER}`, count);
|
|
13210
13212
|
const key = "(?:[A-Za-z0-9_.-]*(?:password|passwd|pwd|secret|token|credential|cookie)[A-Za-z0-9_.-]*|api[-_]?key|access[-_]?(?:key|token)|private[-_]?key|client[-_]?secret|authorization)";
|
|
13211
13213
|
const assignment = `(?:=\\s*|:\\s+(?=\\S)|:\\s*(?=["']))`;
|
|
13212
|
-
output = replaceWithCount(output, new RegExp(`((?:["']?${key}["']?)\\s*${assignment})(?:"(?:\\\\.|[^"])*"|'(?:\\\\.|[^'])*'|[^\\s,;}\\]]+)`, "giu"), (_match, prefix) => `${prefix}"${INDEXER_OUTPUT_REDACTION_MARKER}"`, count);
|
|
13214
|
+
output = replaceWithCount(output, new RegExp(`((?:["']?${key}["']?)\\s*${assignment})` + `(?!["']?${escapeRegExp(INDEXER_OUTPUT_REDACTION_MARKER)})` + `(?:"(?:\\\\.|[^"])*"|'(?:\\\\.|[^'])*'|[^\\s,;}\\]]+)`, "giu"), (_match, prefix) => `${prefix}"${INDEXER_OUTPUT_REDACTION_MARKER}"`, count);
|
|
13213
13215
|
return output;
|
|
13214
13216
|
}
|
|
13215
13217
|
function redactBlockedText(value, blocked, count) {
|
|
@@ -14926,7 +14928,7 @@ function selectedEntryFiles(input) {
|
|
|
14926
14928
|
for (const entry of input.detected.entries) {
|
|
14927
14929
|
const sourcePath = input.module.path === "." ? safeSourceRelativePath(entry.path) : safeSourceRelativePath(`${input.module.path}/${entry.path}`);
|
|
14928
14930
|
if (!includedSourceFiles.has(sourcePath)) {
|
|
14929
|
-
throw new Error(`Auto-detected entry is outside
|
|
14931
|
+
throw new Error(`Auto-detected entry is outside the parser include: ${sourcePath}. Update the Provider include or configured entries.`);
|
|
14930
14932
|
}
|
|
14931
14933
|
}
|
|
14932
14934
|
return input.detected.entries;
|
|
@@ -14937,7 +14939,7 @@ function selectedEntryFiles(input) {
|
|
|
14937
14939
|
}
|
|
14938
14940
|
return sourceEntries.map((sourcePath, index) => {
|
|
14939
14941
|
if (!includedSourceFiles.has(sourcePath)) {
|
|
14940
|
-
throw new Error(`Configured extraction entry is missing or outside
|
|
14942
|
+
throw new Error(`Configured extraction entry is missing or outside the parser include: ${sourcePath}. Update the Provider entries or include.`);
|
|
14941
14943
|
}
|
|
14942
14944
|
const modulePath = moduleRelativeEntryPath(input.module.path, sourcePath);
|
|
14943
14945
|
return {
|