gx-code-agent 0.0.0-stage → 0.1.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.
Files changed (45) hide show
  1. package/README.md +276 -2
  2. package/bin/gx-stdio-mcp.js +145 -0
  3. package/bin/gx.js +99 -0
  4. package/contracts/README.md +40 -0
  5. package/contracts/alignment.schema.json +99 -0
  6. package/contracts/code-chunk.schema.json +28 -0
  7. package/contracts/evidence.schema.json +108 -0
  8. package/contracts/examples/alignment.example.json +26 -0
  9. package/contracts/examples/code-chunk.example.json +20 -0
  10. package/contracts/examples/evidence.add-field.json +49 -0
  11. package/contracts/examples/evidence.verify-manifest.json +52 -0
  12. package/contracts/examples/ext-method.example.json +14 -0
  13. package/contracts/examples/manifest.example.json +13 -0
  14. package/contracts/examples/unknown.extension-coverage.json +29 -0
  15. package/contracts/ext-method.schema.json +57 -0
  16. package/contracts/manifest.schema.json +27 -0
  17. package/contracts/unknown.schema.json +119 -0
  18. package/eval/README.md +52 -0
  19. package/eval/code-search-eval.json +216 -0
  20. package/package.json +37 -4
  21. package/src/adapters/cli/args.js +94 -0
  22. package/src/adapters/cli/output.js +66 -0
  23. package/src/adapters/platform/mcpClient.js +202 -0
  24. package/src/application/applyWorkflow.js +138 -0
  25. package/src/application/auditCommand.js +130 -0
  26. package/src/application/auditWorkflow.js +41 -0
  27. package/src/application/chunksCommand.js +87 -0
  28. package/src/application/errors.js +27 -0
  29. package/src/application/evalSearchCommand.js +148 -0
  30. package/src/application/initWorkflow.js +563 -0
  31. package/src/application/pullWorkflow.js +74 -0
  32. package/src/application/scanCommand.js +71 -0
  33. package/src/application/scanWorkflow.js +689 -0
  34. package/src/application/verifyWorkflow.js +142 -0
  35. package/src/domain/canonicalJson.js +72 -0
  36. package/src/domain/chunker.js +297 -0
  37. package/src/domain/digest.js +60 -0
  38. package/src/domain/java/JavaAstChunker.java +139 -0
  39. package/src/domain/java/JavaAstScanner.java +107 -0
  40. package/src/domain/javaAst.js +186 -0
  41. package/src/domain/jdk.js +66 -0
  42. package/src/domain/keepBlocks.js +122 -0
  43. package/src/store/backupStore.js +106 -0
  44. package/src/store/localStore.js +79 -0
  45. package/templates/platform-rules.md +29 -0
package/README.md CHANGED
@@ -1,3 +1,277 @@
1
- # Temporary Holding Version
1
+ # gx-code-agent
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ GX 高代码平台的伴生 CLI。M0 阶段提供 `gx init`:一次性完成 MCP Server 注册与平台规范注入。
4
+
5
+ ## 环境要求
6
+
7
+ - Node.js 18+
8
+
9
+ ## 安装
10
+
11
+ ### 从公共 npm 安装(推荐)
12
+
13
+ ```bash
14
+ # 主 CLI
15
+ npm i -g gx-code-agent@0.1.0
16
+ npx --yes gx-code-agent@0.1.0 --help
17
+
18
+ # 独立本地 MCP 启动器
19
+ npx --yes gx-mcp-local
20
+ ```
21
+
22
+ 包名、版本与发布流程见 `RELEASE.md`。公共 npm 发布若遇到 2FA/E403,使用 `npm run publish:public -- --otp=<验证码>` 或 granular token(需 Bypass 2FA)。
23
+
24
+ ### 本地开发
25
+
26
+ ```bash
27
+ cd gx-code-agent
28
+ npm ci
29
+ npm link
30
+ ```
31
+
32
+ 或直接:
33
+
34
+ ```bash
35
+ node bin/gx.js --help
36
+ ```
37
+
38
+ ## 快速开始
39
+
40
+ ```bash
41
+ export GX_API_TOKEN=gxmcp_xxx
42
+ gx init --client opencode \
43
+ --url http://127.0.0.1:4013/mcp \
44
+ --plan <planId> --template <templateId> \
45
+ --with-local --yes
46
+ ```
47
+
48
+ 初始化会做以下事情:
49
+
50
+ 1. 写入客户端 MCP 配置(OpenCode / Claude / Cline);
51
+ 2. 写入 `timeout`,避免 `generate_code` / `search_code` 过大会话超时;
52
+ 3. 把平台规范 + 项目绑定(planId/templateId/生成层目录)注入 `AGENTS.md` / `CLAUDE.md`,带标记块,重复执行不会重复追加;
53
+ 4. 可选地把 `planId/templateId` 写入 `.gx/config.json`;
54
+ 5. 可选注册本地 stdio MCP `gx-local`(`--with-local`);
55
+ 6. 写入 `.gx/.gitignore`,忽略 `backup/`、`generated/` 等本地文件;
56
+ 7. 写入前把已存在的配置文件备份到 `.gx/backup/<timestamp>/`,并保留最近 5 次;
57
+ 8. 初始化后调用 `ping`、`tools/list`、`get_current_user`,输出连接状态、工具数量和 Token scopes(可用 `--skip-verify` 跳过)。
58
+
59
+ 默认写入的是**环境变量引用**(如 `Bearer {env:GX_API_TOKEN}`),不会把明文 Token 落盘。只有显式加 `--embed-token` 才会写入明文,此时请勿提交对应配置文件。
60
+
61
+ 配置格式:
62
+
63
+ - 默认 `--opencode-format legacy`:写 `mcp.gx-code`,兼容 OpenCode 1.x / 2.x;
64
+ - `--opencode-format v2`:写 `mcp.servers.gx-code`,使用 2.x 原生格式与对象 timeout;
65
+ - `--timeout <ms>`:远端 MCP 超时,默认 `180000`。
66
+
67
+ ## 支持的客户端
68
+
69
+ | 客户端 | 写入文件 | 说明 |
70
+ |---|---|---|
71
+ | `opencode` | `opencode.json` | `mcp.gx-code`,`type: remote`,`oauth: false` |
72
+ | `claude` | `.mcp.json` | `mcpServers.gx-code`,`type: http` |
73
+ | `cline` | `.gx/cline-mcp-settings.json` | 生成片段,需合并到 Cline 的 MCP 设置 |
74
+
75
+ ## 命令
76
+
77
+ ```bash
78
+ gx init [--client opencode|claude|cline] [--url <url>] [--token <token>] \
79
+ [--token-env <name>] [--embed-token] [--plan <planId>] [--template <templateId>] \
80
+ [--generated-dir <dir>] [--dir <path>] [--with-local] \
81
+ [--opencode-format legacy|v2] [--timeout <ms>] \
82
+ [--check] [--skip-verify] [--yes] [--dry-run]
83
+ gx pull --plan <planId> --template <templateId> [--model-ids a,b] [--url <url>] [--token <token>] [--dry-run]
84
+ gx apply [--dir <path>] [--dry-run] [--force]
85
+ gx verify [--dir <path>] [--manifest <path>] [--strict]
86
+ gx scan [--dir <path>] [--compile] [--report|--no-report] [--source-dir a,b]
87
+ gx audit [--dir <path>] [--compile] [--report|--no-report] [--strict]
88
+ gx chunks [--dir <path>] [--source-dir a,b] [--report|--no-report] [--no-content] [--strict]
89
+ gx version
90
+ gx help
91
+ ```
92
+
93
+ - Token 优先取 `--token`,其次取环境变量(默认 `GX_API_TOKEN`,可用 `--token-env` 改名)。
94
+ - 默认写入环境变量引用;`--embed-token` 才会写入明文。
95
+ - `--dry-run` 只打印将写入的内容与 diff,不落盘。
96
+ - `--check` 只检查配置与连接,不写入;配置需要更新时退出码 1。检查时请带上与初始化相同的 `--client/--with-local/--opencode-format` 选项,否则会报告差异。
97
+ - `--with-local` 同时注册 `gx-local` 本地 stdio MCP。
98
+ - `--skip-verify` 跳过初始化后的连接检查(CI / 离线环境使用)。
99
+ - 非交互模式写入必须带 `--yes`,否则退出码 2。
100
+ - 不支持的客户端会直接报错,不会猜测格式。
101
+
102
+ ## M3:拉取 / 应用 / 校验
103
+
104
+ ```bash
105
+ # 1. 从平台拉取生成结果(Token 只放环境变量,不落盘)
106
+ export GX_API_TOKEN=gxmcp_xxx
107
+ gx pull --plan plan-1 --template tpl-1
108
+
109
+ # 2. 预览 apply
110
+ gx apply --dry-run
111
+
112
+ # 3. 应用;保留块内的用户代码不会被覆盖
113
+ gx apply
114
+
115
+ # 4. 已确认生成层被手改,强制覆盖
116
+ gx apply --force
117
+
118
+ # 4. CI 使用严格模式:manifest 缺失或漂移都会失败
119
+ gx verify --strict
120
+ ```
121
+
122
+ `gx pull` 会写入:
123
+
124
+ - `.gx/generated/<path>`:平台返回的生成文件
125
+ - `.gx/manifest.json`:文件 sha256 与 metadataVersion
126
+ - `.gx/pull.json`:本次拉取信息
127
+
128
+ `gx verify` 只校验生成骨架;保留块内部内容归用户所有,不参与漂移判断。
129
+
130
+ `gx apply` 会先做预检:如果目标文件的生成骨架在最近一次 apply 后被修改,默认拒绝覆盖并提示使用 `gx apply --force`。首次 apply 没有历史指纹时会给出警告,但仍允许执行。
131
+
132
+ ## M4:扩展点与对账
133
+
134
+ 扩展层公开方法使用平台注解:
135
+
136
+ ```java
137
+ @ExtMethod(entity = "Order", intent = "大额订单二级审批",
138
+ triggers = {"BEFORE_SAVE"}, fields = {"amount"})
139
+ public void checkApproval(Order order) { ... }
140
+ ```
141
+
142
+ ```bash
143
+ # 解析 @ExtMethod;配置了 planId + Token 时自动上报 record_ext_points
144
+ gx scan
145
+
146
+ # AST 扫描 + 编译扩展层(JDK 9+;推荐 JDK 17,可用 JAVA_HOME/JAVAC 指定)
147
+ gx scan --compile --classpath pig-upms/gx-util-api/target/classes
148
+
149
+ # 只扫描不上报
150
+ gx scan --no-report
151
+
152
+ # 上报扩展点、校验 manifest、调用 audit_alignment,输出待办清单
153
+ gx audit
154
+ gx audit --strict # 有待办时退出码 1
155
+
156
+ # 精简 CI 示例:失败即阻断
157
+ gx audit --no-report --strict
158
+ ```
159
+
160
+ `gx scan` 优先使用 JDK Compiler Tree API(AST)识别公开方法与 `@ExtMethod`;JDK 不可用时自动回退正则,并在 `coverage.facets.ast_scan` 中标记。`--compile` 通过 `javac` 校验类型引用,未解析的类型会写入 `compilation.errors` 并让 `--strict` 失败。
161
+
162
+ `gx audit` 输出三类待办:
163
+
164
+ 1. `MISSING_EXT_METHOD`:扩展层公开方法缺少 `@ExtMethod`;
165
+ 2. `STALE_EXT_REFERENCE`:已上报扩展点引用了已删除/不存在的实体或字段(`@ExtMethod.fields`);
166
+ 3. `FILE_DRIFTED` / `FILE_MISSING` / `MANIFEST_*`:manifest 与工作区不一致。
167
+
168
+ 扫描结果写入 `.gx/ext-points.json`,对账结果写入 `.gx/audit.json`;共享契约见 `contracts/alignment.schema.json`。
169
+
170
+ ## M5:代码切片与检索
171
+
172
+ ```bash
173
+ # 本地生成 Java/Vue 方法级/类级切片,写入 .gx/chunks.json
174
+ gx chunks --no-report
175
+
176
+ # 生成并上报 record_code_chunks(配置 planId + GX_API_TOKEN)
177
+ gx chunks
178
+
179
+ # 数据敏感环境:只上报元数据,不上传切片内容
180
+ gx chunks --no-content
181
+ ```
182
+
183
+ Java 切片优先使用 JDK Compiler Tree API(AST)生成类/方法边界、行号与注解;JDK 不可用时自动回退正则。`coverage.facets.chunking.engine` 会标记 `ast` 或 `regex`。
184
+
185
+ 平台侧使用 `search_code` 做“关键词 + AST 标签 + pgvector 语义”混合检索(`layer`、`granularity`、`annotations`、`frameworkTags`)。CLI 上报时 `embedding` 可为空,平台会按切片内容自动向量化并写入 pgvector;未配置 EmbeddingModel 时自动降级为关键词检索。
186
+
187
+ ## 检索评测
188
+
189
+ ```bash
190
+ gx eval-search --plan <planId> \
191
+ --dataset eval/code-search-eval.json \
192
+ --limit 5 --out .gx/eval-search.json
193
+ ```
194
+
195
+ 输出 HitRate、MRR、平均延迟与每条 query 的命中排名。评测集位于 `eval/code-search-eval.json`,可按项目复制扩展。
196
+
197
+ ## 本地 stdio MCP
198
+
199
+ `gx-mcp-local` 提供不依赖平台的本地 MCP 工具:
200
+
201
+ - `local_verify_manifest`:校验工作区与 `.gx/manifest.json`;
202
+ - `local_scan_ext_points`:扫描 `@ExtMethod`、缺注解方法与编译结果。
203
+
204
+ OpenCode / Claude Code 本地配置示例:
205
+
206
+ ```json
207
+ {
208
+ "mcp": {
209
+ "gx-local": {
210
+ "type": "local",
211
+ "command": ["npx", "gx-mcp-local"],
212
+ "enabled": true
213
+ }
214
+ }
215
+ }
216
+ ```
217
+
218
+ 也可直接用 `node bin/gx-stdio-mcp.js` 启动(JSON-RPC over stdio)。
219
+
220
+ > `npx gx-mcp-local` 使用独立包 `gx-mcp-local`,它会调用已安装的 `gx-code-agent`;
221
+ > 未发布或离线时请使用 `node /absolute/path/gx-code-agent/bin/gx-stdio-mcp.js`。
222
+
223
+ ## CI
224
+
225
+ - GitLab:`ci/gitlab-ci-gx-verify.yml`
226
+ - GitHub Actions:`ci/github-actions-gx-verify.yml`
227
+
228
+ CI 默认执行:
229
+
230
+ 1. `gx verify --strict`:阻断生成层漂移;
231
+ 2. `gx scan --no-report`:输出 `gx-scan.json` artifact;
232
+ 3. 配置 `GX_API_TOKEN`(可选 `GX_PLAN_ID`)后执行 `gx audit --no-report --strict`,阻断缺注解/失效引用/manifest 漂移。
233
+
234
+ CI 变量:
235
+
236
+ | 变量 | 说明 |
237
+ |---|---|
238
+ | `GX_AGENT_VERSION` | 固定 CLI 版本,默认 `0.1.0` |
239
+ | `NPM_REGISTRY` | npm registry;默认 `https://registry.npmjs.org/` |
240
+ | `GX_API_TOKEN` / `GX_PLAN_ID` | MCP 鉴权与方案范围 |
241
+
242
+ 发布流水线:
243
+
244
+ - GitHub Actions:`.github/workflows/publish.yml`
245
+ - GitLab:`ci/gitlab-ci-publish.yml`
246
+ - 发布步骤、公共 npm 登录与双包发布见 `RELEASE.md`。
247
+
248
+ ## 验证
249
+
250
+ ```bash
251
+ node --test
252
+ ```
253
+
254
+ 当前:CLI `39/39`;平台侧 Java MCP `80/80`。
255
+
256
+ ## 当前进度与后续
257
+
258
+ 已完成:
259
+
260
+ - M3:`gx pull` / `gx apply` / `gx verify`
261
+ - M4:`@ExtMethod` / `gx scan` / `gx audit` / `record_ext_points` / `audit_alignment`
262
+ - M5:`gx chunks` / `record_code_chunks` / `search_code`(关键词 + AST 标签 + pgvector 语义)
263
+ - P5:apply 漂移预检、AST 扫描、编译级引用验证
264
+ - P6:真实方案 `wk1` 完成 pull/apply/verify/scan/audit/chunks/search 全链路验收(2146 切片、语义检索通过)
265
+ - P7:审计 compilation facet 合并、向量重建、审计保留、pgVector 健康检查
266
+ - P8:Java AST chunker、混合检索评测集、本地 stdio MCP(`gx-mcp-local`)
267
+ - CLI-02:`gx init` 支持 `--with-local`、timeout、项目绑定、连接自检、`.gx/.gitignore`、`--check`
268
+
269
+ 待办:
270
+
271
+ - `gx context` / `gx push` / `gx adopt`
272
+ - Agent 外壳(本地 stdio MCP 已提供基础版)
273
+ - 公共 npm 发布(发布准备已完成,待 2FA OTP 或 Bypass 2FA granular token)
274
+
275
+ ## 与主平台的关系
276
+
277
+ MCP Server 运行在 `gx-cloud-frp/pig-code-biz`,本仓库只负责开发者侧的注册、校验与代码文件操作。平台保证结构元数据唯一事实源;本 CLI 负责开发者仓库侧的文件级校验。
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import readline from "node:readline";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ import { scanWorkflow } from "../src/application/scanWorkflow.js";
9
+ import { verifyWorkflow } from "../src/application/verifyWorkflow.js";
10
+
11
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
12
+ const PKG = JSON.parse(fs.readFileSync(path.join(ROOT, "package.json"), "utf8"));
13
+
14
+ const TOOLS = [
15
+ {
16
+ name: "local_verify_manifest",
17
+ description: "本地校验仓库文件与 .gx/manifest.json 是否一致;不访问平台。",
18
+ inputSchema: {
19
+ type: "object",
20
+ properties: {
21
+ dir: { type: "string", description: "仓库目录,默认当前工作目录" },
22
+ manifestPath: { type: "string", description: "manifest 路径,默认 .gx/manifest.json" }
23
+ }
24
+ }
25
+ },
26
+ {
27
+ name: "local_scan_ext_points",
28
+ description: "本地扫描 @ExtMethod、缺注解方法与编译结果;不访问平台。",
29
+ inputSchema: {
30
+ type: "object",
31
+ properties: {
32
+ dir: { type: "string", description: "仓库目录,默认当前工作目录" },
33
+ sourceDirs: {
34
+ oneOf: [
35
+ { type: "string", description: "逗号分隔的源码目录" },
36
+ { type: "array", items: { type: "string" } }
37
+ ]
38
+ },
39
+ compile: { type: "boolean", description: "是否执行 javac 编译" },
40
+ classpath: { type: "string", description: "javac classpath" }
41
+ }
42
+ }
43
+ }
44
+ ];
45
+
46
+ function send(message) {
47
+ process.stdout.write(`${JSON.stringify(message)}\n`);
48
+ }
49
+
50
+ function result(id, value) {
51
+ send({ jsonrpc: "2.0", id, result: value });
52
+ }
53
+
54
+ function error(id, code, message) {
55
+ send({ jsonrpc: "2.0", id, error: { code, message } });
56
+ }
57
+
58
+ function toolResult(id, value, isError = false) {
59
+ result(id, {
60
+ content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
61
+ isError
62
+ });
63
+ }
64
+
65
+ function parseSourceDirs(value) {
66
+ if (Array.isArray(value)) {
67
+ return value.filter((item) => typeof item === "string" && item.trim()).map((item) => item.trim());
68
+ }
69
+ if (typeof value === "string") {
70
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
71
+ }
72
+ return undefined;
73
+ }
74
+
75
+ function callTool(name, args = {}) {
76
+ const dir = path.resolve(String(args.dir || process.cwd()));
77
+ if (name === "local_verify_manifest") {
78
+ return verifyWorkflow({
79
+ dir,
80
+ manifestPath: typeof args.manifestPath === "string" ? args.manifestPath : ".gx/manifest.json"
81
+ });
82
+ }
83
+ if (name === "local_scan_ext_points") {
84
+ return scanWorkflow({
85
+ dir,
86
+ sourceDirs: parseSourceDirs(args.sourceDirs),
87
+ compile: Boolean(args.compile),
88
+ classpath: typeof args.classpath === "string" ? args.classpath : undefined
89
+ });
90
+ }
91
+ throw new Error(`未知工具:${name}`);
92
+ }
93
+
94
+ function handle(message) {
95
+ if (!message || typeof message !== "object" || message.jsonrpc !== "2.0") {
96
+ error(null, -32600, "Invalid Request");
97
+ return;
98
+ }
99
+ const { id, method, params } = message;
100
+ if (method === "initialize") {
101
+ result(id, {
102
+ protocolVersion: "2025-06-18",
103
+ capabilities: { tools: { listChanged: false } },
104
+ serverInfo: { name: "gx-code-agent-local", version: PKG.version }
105
+ });
106
+ return;
107
+ }
108
+ if (method === "notifications/initialized" || method === "notifications/cancelled") {
109
+ return;
110
+ }
111
+ if (method === "ping") {
112
+ result(id, {});
113
+ return;
114
+ }
115
+ if (method === "tools/list") {
116
+ result(id, { tools: TOOLS });
117
+ return;
118
+ }
119
+ if (method === "tools/call") {
120
+ const name = params?.name;
121
+ const args = params?.arguments ?? {};
122
+ try {
123
+ toolResult(id, callTool(name, args));
124
+ }
125
+ catch (e) {
126
+ toolResult(id, { error: e instanceof Error ? e.message : String(e) }, true);
127
+ }
128
+ return;
129
+ }
130
+ error(id, -32601, `Method not found: ${method}`);
131
+ }
132
+
133
+ const rl = readline.createInterface({ input: process.stdin, crlfDelay: Infinity });
134
+ rl.on("line", (line) => {
135
+ const trimmed = line.trim();
136
+ if (!trimmed) {
137
+ return;
138
+ }
139
+ try {
140
+ handle(JSON.parse(trimmed));
141
+ }
142
+ catch (e) {
143
+ error(null, -32700, `Parse error: ${e instanceof Error ? e.message : String(e)}`);
144
+ }
145
+ });
package/bin/gx.js ADDED
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ import { parseArgs, usage } from "../src/adapters/cli/args.js";
8
+ import { runApply } from "../src/application/applyWorkflow.js";
9
+ import { runAudit } from "../src/application/auditCommand.js";
10
+ import { runChunks } from "../src/application/chunksCommand.js";
11
+ import { runEvalSearch } from "../src/application/evalSearchCommand.js";
12
+ import { runInit } from "../src/application/initWorkflow.js";
13
+ import { runPull } from "../src/application/pullWorkflow.js";
14
+ import { runScan } from "../src/application/scanCommand.js";
15
+ import { verifyWorkflow } from "../src/application/verifyWorkflow.js";
16
+
17
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
18
+
19
+ function printJson(value) {
20
+ process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
21
+ }
22
+
23
+ async function main() {
24
+ const args = parseArgs(process.argv.slice(2));
25
+ const command = args._[0] || "help";
26
+
27
+ if (command === "help" || args.help) {
28
+ usage();
29
+ return;
30
+ }
31
+ if (command === "version") {
32
+ const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, "package.json"), "utf8"));
33
+ console.log(pkg.version);
34
+ return;
35
+ }
36
+ if (command === "init") {
37
+ await runInit(args);
38
+ return;
39
+ }
40
+ if (command === "pull") {
41
+ await runPull(args);
42
+ return;
43
+ }
44
+ if (command === "apply") {
45
+ await runApply(args);
46
+ return;
47
+ }
48
+ if (command === "verify") {
49
+ const dir = path.resolve(String(args.dir || process.cwd()));
50
+ const result = verifyWorkflow({
51
+ dir,
52
+ manifestPath: typeof args.manifest === "string" ? args.manifest : ".gx/manifest.json"
53
+ });
54
+ printJson(result);
55
+ if (!result.ok || (args.strict && result.coverage.status !== "complete")) {
56
+ process.exitCode = 1;
57
+ }
58
+ return;
59
+ }
60
+ if (command === "scan") {
61
+ const result = await runScan(args);
62
+ if (args.strict && (result.unannotatedMethods?.length > 0
63
+ || result.report?.status === "failed"
64
+ || (result.report?.rejected?.length ?? 0) > 0
65
+ || (result.compilation?.available === true && result.compilation.success === false))) {
66
+ process.exitCode = 1;
67
+ }
68
+ return;
69
+ }
70
+ if (command === "chunks") {
71
+ const result = await runChunks(args);
72
+ if (args.strict && (result.report?.status === "failed" || (result.report?.rejected?.length ?? 0) > 0)) {
73
+ process.exitCode = 1;
74
+ }
75
+ return;
76
+ }
77
+ if (command === "eval-search") {
78
+ await runEvalSearch(args);
79
+ return;
80
+ }
81
+ if (command === "audit") {
82
+ const result = await runAudit(args);
83
+ if (!result.ok || (args.strict && result.coverage?.status === "unknown")) {
84
+ process.exitCode = 1;
85
+ }
86
+ return;
87
+ }
88
+
89
+ usage();
90
+ process.exitCode = 1;
91
+ }
92
+
93
+ try {
94
+ await main();
95
+ } catch (error) {
96
+ const message = error instanceof Error ? error.message : String(error);
97
+ console.error(`[gx] ${message}`);
98
+ process.exitCode = error && typeof error === "object" && "exitCode" in error ? error.exitCode : 1;
99
+ }
@@ -0,0 +1,40 @@
1
+ # GX 共享契约(contractVersion: 1)
2
+
3
+ 本目录是 gx-code-agent(Node/CLI)与 gx-cloud-frp(Java MCP)之间的传输契约源。
4
+
5
+ ## 文件
6
+
7
+ | 文件 | 用途 |
8
+ |---|---|
9
+ | `evidence.schema.json` | Evidence 记录 |
10
+ | `unknown.schema.json` | Residual Unknown(对账待办) |
11
+ | `manifest.schema.json` | 生成清单与 checksum |
12
+ | `ext-method.schema.json` | 扩展方法声明(`fields` 为可选的字段级引用) |
13
+ | `alignment.schema.json` | `audit_alignment` 对账结果 |
14
+ | `code-chunk.schema.json` | M5 代码切片(embedding 可空,平台按需自动向量化) |
15
+ | `examples/` | 双端共用的测试 fixture |
16
+
17
+ ## 通用规则
18
+
19
+ 1. JSON Schema Draft 2020-12。
20
+ 2. 未知字段一律拒绝(`additionalProperties: false`)。
21
+ 3. 字符串不做 Unicode 归一化;问题文本在生成 `unk_` 前做空白归一化。
22
+ 4. 对象 key 按 Unicode code point 排序;`undefined` 字段丢弃。
23
+ 5. 摘要统一使用小写 SHA-256 hex。
24
+
25
+ ## ID 规则
26
+
27
+ - Evidence:`ev_` + `sha256(canonicalJson(evidenceWithoutEvidenceId))`
28
+ - Unknown:`unk_` + `sha256(canonicalJson({domain, question, scope}))`
29
+ - `question` 先执行 `replace(/\s+/gu, " ").trim()`
30
+ - Manifest:`manifestHash = sha256(canonicalJson(manifestWithoutManifestHash))`
31
+
32
+ ## 版本策略
33
+
34
+ - 新增字段必须可选,保持 v1 兼容。
35
+ - 删除字段、修改字段语义、修改 ID 计算方式必须升 major(v2),并在 Node/Java 两端同时更新。
36
+ - 发布前运行 `node --test test/contracts.test.js` 与 Java `ContractSerializationTest`。
37
+
38
+ ## MIT 说明
39
+
40
+ 本目录的字段设计参考了 REA(https://github.com/morluto/rea,MIT)的 Evidence / Unknown 设计思想,但 Schema 与示例为 GX 按自身领域重写,不包含 REA 源代码副本。
@@ -0,0 +1,99 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://gx.local/contracts/alignment.schema.json",
4
+ "title": "GX Audit Alignment Result",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["planId", "ok", "extPointCount", "drift", "missingAnnotations", "staleReferences", "todos", "coverage"],
8
+ "properties": {
9
+ "planId": { "type": "string", "minLength": 1 },
10
+ "ok": { "type": "boolean" },
11
+ "extPointCount": { "type": "integer", "minimum": 0 },
12
+ "drift": {
13
+ "type": "object",
14
+ "additionalProperties": false,
15
+ "required": ["planId", "manifestVersion", "currentVersion", "drifted", "missing", "extra", "reason"],
16
+ "properties": {
17
+ "planId": { "type": "string" },
18
+ "manifestVersion": { "type": ["integer", "null"] },
19
+ "currentVersion": { "type": "integer", "minimum": 1 },
20
+ "drifted": { "type": "array", "items": { "type": "string" } },
21
+ "missing": { "type": "array", "items": { "type": "string" } },
22
+ "extra": { "type": "array", "items": { "type": "string" } },
23
+ "reason": { "type": ["string", "null"] }
24
+ }
25
+ },
26
+ "missingAnnotations": {
27
+ "type": "array",
28
+ "items": {
29
+ "type": "object",
30
+ "additionalProperties": false,
31
+ "required": ["className", "methodName", "file", "line", "reason"],
32
+ "properties": {
33
+ "className": { "type": "string" },
34
+ "methodName": { "type": "string" },
35
+ "file": { "type": ["string", "null"] },
36
+ "line": { "type": ["integer", "null"] },
37
+ "reason": { "type": "string" }
38
+ }
39
+ }
40
+ },
41
+ "staleReferences": {
42
+ "type": "array",
43
+ "items": {
44
+ "type": "object",
45
+ "additionalProperties": false,
46
+ "required": ["className", "methodName", "entity", "field", "reason"],
47
+ "properties": {
48
+ "className": { "type": "string" },
49
+ "methodName": { "type": "string" },
50
+ "entity": { "type": ["string", "null"] },
51
+ "field": { "type": ["string", "null"] },
52
+ "reason": { "type": "string" }
53
+ }
54
+ }
55
+ },
56
+ "todos": {
57
+ "type": "array",
58
+ "items": {
59
+ "type": "object",
60
+ "additionalProperties": false,
61
+ "required": ["todoId", "type", "severity", "summary", "action", "className", "methodName", "entity", "files"],
62
+ "properties": {
63
+ "todoId": { "type": "string", "pattern": "^align_[a-f0-9]{16}$" },
64
+ "type": {
65
+ "type": "string",
66
+ "enum": ["MANIFEST_MISSING", "MANIFEST_PLAN_MISMATCH", "MANIFEST_VERSION_MISSING", "MANIFEST_VERSION_MISMATCH", "MANIFEST_ENTRY_INVALID", "MANIFEST_ENTRY_DUPLICATE", "FILE_DRIFTED", "FILE_MISSING", "EXTRA_FILE", "MISSING_EXT_METHOD", "STALE_EXT_REFERENCE"]
67
+ },
68
+ "severity": { "type": "string", "enum": ["low", "medium", "high", "critical"] },
69
+ "summary": { "type": "string" },
70
+ "action": { "type": "string" },
71
+ "className": { "type": ["string", "null"] },
72
+ "methodName": { "type": ["string", "null"] },
73
+ "entity": { "type": ["string", "null"] },
74
+ "files": { "type": "array", "items": { "type": "string" } }
75
+ }
76
+ }
77
+ },
78
+ "coverage": {
79
+ "type": "object",
80
+ "additionalProperties": false,
81
+ "required": ["status", "facets"],
82
+ "properties": {
83
+ "status": { "type": "string", "enum": ["complete", "partial", "unknown"] },
84
+ "facets": {
85
+ "type": "object",
86
+ "additionalProperties": {
87
+ "type": "object",
88
+ "additionalProperties": false,
89
+ "required": ["available"],
90
+ "properties": {
91
+ "available": { "type": "boolean" },
92
+ "reason": { "type": ["string", "null"] }
93
+ }
94
+ }
95
+ }
96
+ }
97
+ }
98
+ }
99
+ }