add-coder 0.3.19 → 0.3.20-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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "add-coder",
3
- "version": "0.3.19",
3
+ "version": "0.3.20-0",
4
4
  "private": false,
5
5
  "description": "[codein2027](https://github.com/xiaomingming92/codein2027) - A complete scaffolding for building the ADD programming paradigm, the implementation layer for AI code governance. Core principle: Audit as Infrastructure. Breaks the black-box programming process and cross-session amnesia, evolving programming paradigms into an auditable, traceable, and convergent new era. npx-ready.\n\n[codein2027](https://github.com/xiaomingming92/codein2027) 快速构建 ADD 编程范式的完整脚手架——AI 代码治理的落地方案。以「审计即基础设施」为核心,彻底打破编程过程黑盒与跨轮失忆,让编程范式进化为可审计、可追溯、可收敛的新时代。npx 即用,人人可体验。",
6
6
  "repository": {
@@ -48,13 +48,13 @@
48
48
  "zod": "^4.4.3"
49
49
  },
50
50
  "peerDependencies": {
51
+ "@huggingface/transformers": "^3.8.1",
51
52
  "@prisma/adapter-pg": "^7.0.0",
52
53
  "@prisma/client": "^7.0.0",
53
54
  "dotenv": "*",
54
55
  "prisma": "^7.0.0",
55
56
  "tsx": ">=4",
56
- "vector-cosine-similarity": "^1.8.0",
57
- "@huggingface/transformers": "^3.8.1"
57
+ "vector-cosine-similarity": "^1.8.0"
58
58
  },
59
59
  "peerDependenciesMeta": {
60
60
  "@prisma/client": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "_version": "0.3.19",
2
+ "_version": "0.3.20-0",
3
3
  "adapters/claude/hooks/doc-format-guard.sh": "ec59ad3f",
4
4
  "adapters/claude/hooks/lib/common.sh": "4f3e64ad",
5
5
  "adapters/claude/hooks/lib/context-inject.sh": "cfc8c65e",
@@ -74,7 +74,7 @@
74
74
  "adapters/qoder/hooks/subagent-stop.sh": "5a2c78c6",
75
75
  "adapters/qoder/mcp.json": "b2075957",
76
76
  "adapters/qoder/settings.json": "4bae3c9a",
77
- "adapters/qoder/sync-policy.json": "a4bcbbaf",
77
+ "adapters/qoder/sync-policy.json": "9cb6ff90",
78
78
  "adapters/trae/hooks/doc-format-guard.sh": "9b99f253",
79
79
  "adapters/trae/hooks/lib/common.sh": "4f3e64ad",
80
80
  "adapters/trae/hooks/lib/context-inject.sh": "cfc8c65e",
@@ -188,7 +188,7 @@
188
188
  "core/scripts/mcp-server/notifications/hitl.ts": "96eb7c5d",
189
189
  "core/scripts/mcp-server/notifications/hook.ts": "a6cb861f",
190
190
  "core/scripts/mcp-server/notifications/index.ts": "b6615afa",
191
- "core/scripts/mcp-server/resources/add-coder-version.ts": "299fa071",
191
+ "core/scripts/mcp-server/resources/add-coder-version.ts": "2384fa40",
192
192
  "core/scripts/mcp-server/resources/add-state.ts": "1c1cc3a2",
193
193
  "core/scripts/mcp-server/resources/hook-events-report.ts": "1d4b5996",
194
194
  "core/scripts/mcp-server/resources/index.ts": "50e19629",
@@ -197,12 +197,13 @@
197
197
  "core/scripts/mcp-server/sampling/review.ts": "5db7a8df",
198
198
  "core/scripts/mcp-server/shared/dps-scoring.strategy.ts": "f1c901dc",
199
199
  "core/scripts/mcp-server/shared/env.ts": "57f4c2f2",
200
- "core/scripts/mcp-server/shared/fs.ts": "97257db9",
200
+ "core/scripts/mcp-server/shared/fs.ts": "0e66a214",
201
201
  "core/scripts/mcp-server/shared/hitl-interaction.strategy.ts": "7c5c26f5",
202
202
  "core/scripts/mcp-server/shared/package.json": "e3d5a4a2",
203
- "core/scripts/mcp-server/shared/prisma.ts": "e871942b",
203
+ "core/scripts/mcp-server/shared/prisma.ts": "a9618f99",
204
204
  "core/scripts/mcp-server/shared/project-root-strategy.ts": "1a5bdc57",
205
205
  "core/scripts/mcp-server/shared/response.ts": "f7806b67",
206
+ "core/scripts/mcp-server/shared/run-command.ts": "42caf972",
206
207
  "core/scripts/mcp-server/tasks/index.ts": "c040eb75",
207
208
  "core/scripts/mcp-server/tasks/runner.ts": "d84bd35d",
208
209
  "core/scripts/mcp-server/tasks/store.ts": "2fce1a92",
@@ -212,10 +213,10 @@
212
213
  "core/scripts/mcp-server/tools/docs.ts": "034e473c",
213
214
  "core/scripts/mcp-server/tools/gateway/check_add_route_completeness.ts": "b58e76ee",
214
215
  "core/scripts/mcp-server/tools/gateway/check_add_route_status.ts": "23fbbb6b",
215
- "core/scripts/mcp-server/tools/gateway/check_dps.ts": "0f0bedd8",
216
- "core/scripts/mcp-server/tools/gateway/check_rahs.ts": "fe210727",
217
- "core/scripts/mcp-server/tools/gateway/check_spec_sync.ts": "89c2338b",
218
- "core/scripts/mcp-server/tools/gateway/helpers.ts": "690d7891",
216
+ "core/scripts/mcp-server/tools/gateway/check_dps.ts": "4d599b38",
217
+ "core/scripts/mcp-server/tools/gateway/check_rahs.ts": "4078b0f1",
218
+ "core/scripts/mcp-server/tools/gateway/check_spec_sync.ts": "486df441",
219
+ "core/scripts/mcp-server/tools/gateway/helpers.ts": "f8794a92",
219
220
  "core/scripts/mcp-server/tools/gateway/index.ts": "559acb75",
220
221
  "core/scripts/mcp-server/tools/gateway.backup": "65eae393",
221
222
  "core/scripts/mcp-server/tools/hitl.ts": "f447345f",
@@ -232,7 +233,7 @@
232
233
  "core/specs/add-coder-npm-package/checklist.md": "ed92032c",
233
234
  "core/specs/add-coder-npm-package/spec.md": "c7d0f235",
234
235
  "core/specs/add-coder-npm-package/tasks.md": "c34301e5",
235
- "core/templates/01-架构/《ADD开发工作路径与文档协同规范》.md": "c86814d0",
236
+ "core/templates/01-架构/《ADD开发工作路径与文档协同规范》.md": "c7e2ac6a",
236
237
  "core/templates/TERMINOLOGY.md": "e9f4f781",
237
238
  "core/templates/add-route-template-heavyweight.md": "0e107ac0",
238
239
  "core/templates/add-route-template.md": "da7022df",
@@ -274,6 +275,6 @@
274
275
  "core/templates/tasks-template.md": "2b9d0d52",
275
276
  "core/templates/tasks-template.schema.json": "fff6eb47",
276
277
  "core/tools/README.md": "26a63460",
277
- "core/vocabulary/add-governance-vocabulary.md": "62d71481",
278
+ "core/vocabulary/add-governance-vocabulary.md": "ee9e2bd2",
278
279
  "shared/hooks-lib/common.sh": "441eee52"
279
280
  }
@@ -4,7 +4,7 @@
4
4
  "checks": {
5
5
  "add_route_exists": { "enabled": true, "severity": "block" },
6
6
  "add_route_completeness": { "enabled": true, "severity": "block" },
7
- "dps": { "enabled": true, "severity": "block", "threshold": 85 },
7
+ "dps": { "enabled": true, "severity": "block", "threshold": 80 },
8
8
  "rahs": { "enabled": true, "severity": "block", "threshold": 90 },
9
9
  "tasks": { "enabled": true, "severity": "block" },
10
10
  "checklist": { "enabled": true, "severity": "block" },
@@ -1,7 +1,7 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/server"
2
2
  import { join } from "path"
3
3
  import { readFileSafe, PROJECT_ROOT } from "../shared/fs.js"
4
- import { spawnSync } from "child_process"
4
+ import { runCommand } from "../shared/run-command.js"
5
5
 
6
6
  interface PkgJson { version?: string }
7
7
 
@@ -16,8 +16,9 @@ export function registerVersionResource(server: McpServer) {
16
16
  try { current = (JSON.parse(pkg) as PkgJson).version ?? "unknown" } catch { /* intentionally empty */ }
17
17
  }
18
18
  try {
19
- const result = spawnSync("npm", ["view", "add-coder", "version"], { encoding: "utf-8", timeout: 10000 })
20
- latest = (result.stdout || "").trim() || "unknown"
19
+ // win32 npm .cmd runCommand 自动解析(issue #10 跨端修复)
20
+ const result = runCommand("npm", ["view", "add-coder", "version"], { timeout: 10000 })
21
+ latest = result.stdout.trim() || "unknown"
21
22
  outdated = current !== "unknown" && latest !== "unknown" && current !== latest
22
23
  } catch { /* intentionally empty */ }
23
24
  return { contents: [{ text: JSON.stringify({ current, latest, outdated }), uri: "add-coder://version", mimeType: "application/json" }] }
@@ -1,7 +1,7 @@
1
1
  import { readFile, readdir } from "fs/promises"
2
2
  import { join, relative } from "path"
3
3
  import { existsSync } from "fs"
4
- import { spawnSync } from "child_process"
4
+ import { runCommand } from "./run-command.js"
5
5
  import { PROJECT_ROOT, MAGIC_DIR } from "./env.js"
6
6
  import type { GuardResult } from "../types.js"
7
7
 
@@ -15,9 +15,14 @@ export async function validateDocWithGuard(filePath: string): Promise<GuardResul
15
15
  const content = await readFileSafe(filePath)
16
16
  if (!content) return { ok: false, issues: "文件无法读取" }
17
17
  const guardInput = JSON.stringify({ tool_input: { file_path: filePath, file_content: content } })
18
- const result = spawnSync("bash", [guardScript], { input: guardInput, encoding: "utf-8", timeout: 5000 })
19
- if (result.status !== 0) return { ok: false, issues: result.stderr || "guard 执行失败" }
20
- return { ok: true, issues: "" }
18
+ try {
19
+ // bash Windows 不存在 runCommand 抛"命令不可用" 显式返回 guard 失败(不再静默 null)
20
+ const result = runCommand("bash", [guardScript], { input: guardInput, timeout: 5000 })
21
+ if (result.status !== 0) return { ok: false, issues: result.stderr || "guard 执行失败" }
22
+ return { ok: true, issues: "" }
23
+ } catch (e) {
24
+ return { ok: false, issues: `guard 执行失败: ${e instanceof Error ? e.message : String(e)}` }
25
+ }
21
26
  }
22
27
 
23
28
  export async function readdirRecursive(baseDir: string): Promise<string[]> {
@@ -41,6 +41,16 @@ if (url.startsWith("postgresql://") || url.startsWith("postgres://")) {
41
41
  adapter = new Pg({ connectionString: url })
42
42
  } catch { /* optional dep */ }
43
43
  }
44
+ if (url.startsWith("file:")) {
45
+ // SQLite 完整链路(issue #10 P1-5):DATABASE_URL 为 file: → 加载 better-sqlite3 adapter
46
+ try {
47
+ const bsql = require("@prisma/adapter-better-sqlite3") as Record<string, unknown>
48
+ const Bsql = bsql.PrismaBetterSQLite3 as new (opts: Record<string, unknown>) => Record<string, unknown>
49
+ adapter = new Bsql({ url })
50
+ } catch {
51
+ throw new Error("SQLite 模式需要安装 @prisma/adapter-better-sqlite3(npm install @prisma/adapter-better-sqlite3)")
52
+ }
53
+ }
44
54
 
45
55
  export const prisma: Record<string, Record<string, (...a: unknown[]) => unknown>> = new PrismaClient({
46
56
  ...(adapter ? { adapter } : {}),
@@ -0,0 +1,57 @@
1
+ /*
2
+ * Author : xiaomingming wujixmm@gmail.com
3
+ * Date : 2026-08-07
4
+ * Description : 跨平台子进程统一封装(模板版,消费项目 MCP 使用)
5
+ * 与 add-coder 包 src/lib/run-command.ts 语义一致:
6
+ * - win32 下 npm/npx/pnpm/git 为 .cmd 批处理 → 显式追加 .cmd
7
+ * - 命令不存在(ENOENT)→ 抛"命令不可用",不再静默 status=null
8
+ * - 本项目所有子进程调用 MUST 走 runCommand 单入口
9
+ */
10
+ import { spawnSync } from "child_process"
11
+
12
+ export interface RunCommandOptions {
13
+ cwd?: string
14
+ env?: NodeJS.ProcessEnv
15
+ input?: string
16
+ timeout?: number
17
+ shell?: boolean
18
+ platform?: NodeJS.Platform
19
+ /** stdio 透传(如 "inherit" 实时显示;默认无 input 时 ignore/pipe/pipe 捕获) */
20
+ stdio?: "inherit"
21
+ }
22
+
23
+ export interface RunResult {
24
+ status: number | null
25
+ stdout: string
26
+ stderr: string
27
+ }
28
+
29
+ /** Windows 下为 .cmd 批处理的可执行族(CreateProcess 无法直接执行) */
30
+ const CMD_EXTENSIONS = ["npm", "npx", "pnpm", "git"]
31
+
32
+ export function runCommand(cmd: string, args: string[], opts: RunCommandOptions = {}): RunResult {
33
+ const platform = opts.platform ?? process.platform
34
+ const needsCmdExt = platform === "win32" && CMD_EXTENSIONS.includes(cmd) && !opts.shell
35
+ const effectiveCmd = needsCmdExt ? `${cmd}.cmd` : cmd
36
+ const r = spawnSync(effectiveCmd, args, {
37
+ cwd: opts.cwd,
38
+ env: opts.env,
39
+ input: opts.input,
40
+ timeout: opts.timeout,
41
+ encoding: "utf-8",
42
+ shell: opts.shell,
43
+ stdio: opts.stdio ?? (opts.input !== undefined ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"]),
44
+ })
45
+ if (r.error) {
46
+ throw new Error(`命令不可用: ${cmd}(平台: ${platform},${r.error.message})`)
47
+ }
48
+ return { status: r.status, stdout: r.stdout ?? "", stderr: r.stderr ?? "" }
49
+ }
50
+
51
+ /** 跨平台命令存在性探测:win32 用 where / POSIX 用 which */
52
+ export function commandExists(cmd: string, platform?: NodeJS.Platform): boolean {
53
+ const p = platform ?? process.platform
54
+ const probe = p === "win32" ? "where" : "which"
55
+ const r = spawnSync(probe, [cmd], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] })
56
+ return !r.error && r.status === 0
57
+ }
@@ -169,8 +169,11 @@ export function registerCheckDps(server: ToolRegistrar) {
169
169
  continuityCount++;
170
170
  }
171
171
  } catch {
172
- // embedding 加载失败 → 降级纯结构分
172
+ // embedding 加载失败 → 降级纯结构分;提示预下载入口(不阻断、不报错,issue 关切:网络不通用户不卡死)
173
173
  continuityCount = 0;
174
+ parts.push(
175
+ " ⚠️ embedding 不可用(模型未下载或网络不通):语义延续性降级为 0——可运行 `add-coder model:download` 提前预下载",
176
+ );
174
177
  }
175
178
  }
176
179
  const structSem = mappingTotal > 0 ? (mappingMatched / mappingTotal) * 100 : 0;
@@ -18,6 +18,7 @@ import {
18
18
  MAGIC_DIR,
19
19
  } from "../../shared/fs.js";
20
20
  import { prisma } from "../../shared/prisma.js";
21
+ import { runCommand } from "../../shared/run-command.js";
21
22
 
22
23
  export function registerCheckRahs(server: ToolRegistrar) {
23
24
  server.registerTool(
@@ -51,10 +52,9 @@ export function registerCheckRahs(server: ToolRegistrar) {
51
52
  specScore = 80,
52
53
  symScore = 80;
53
54
  try {
54
- const { spawnSync } = await import("child_process");
55
- const tsc = spawnSync("npx", ["tsc", "--noEmit"], {
55
+ // win32 npx .cmd → runCommand 自动解析(issue #10 跨端修复)
56
+ const tsc = runCommand("npx", ["tsc", "--noEmit"], {
56
57
  cwd: PROJECT_ROOT,
57
- encoding: "utf-8",
58
58
  timeout: 30000,
59
59
  });
60
60
  typeScore =
@@ -62,8 +62,7 @@ export function registerCheckRahs(server: ToolRegistrar) {
62
62
  ? 100
63
63
  : Math.max(
64
64
  0,
65
- 100 -
66
- (tsc.stderr || "").split("\n").filter(Boolean).length * 5,
65
+ 100 - tsc.stderr.split("\n").filter(Boolean).length * 5,
67
66
  );
68
67
  } catch {}
69
68
  try {
@@ -18,6 +18,7 @@ import {
18
18
  PROJECT_ROOT,
19
19
  MAGIC_DIR,
20
20
  } from "../../shared/fs.js";
21
+ import { runCommand } from "../../shared/run-command.js";
21
22
 
22
23
  export function registerCheckSpecSync(server: ToolRegistrar) {
23
24
  server.registerTool(
@@ -72,16 +73,14 @@ export function registerCheckSpecSync(server: ToolRegistrar) {
72
73
  .map((f: string) => f.replace(/`/g, ""));
73
74
  lines.push(`附录文件: ${appendixFiles.length} 个`);
74
75
 
75
- // git diff 变更文件
76
+ // git diff 变更文件(win32 下 git 为 .cmd → runCommand 自动解析,issue #10 跨端修复)
76
77
  let diffFiles: string[] = [];
77
78
  try {
78
- const { spawnSync } = await import("child_process");
79
- const diff = spawnSync("git", ["diff", "--name-only"], {
79
+ const diff = runCommand("git", ["diff", "--name-only"], {
80
80
  cwd: PROJECT_ROOT,
81
- encoding: "utf-8",
82
81
  timeout: 5000,
83
82
  });
84
- diffFiles = (diff.stdout || "").trim().split("\n").filter(Boolean);
83
+ diffFiles = diff.stdout.trim().split("\n").filter(Boolean);
85
84
  } catch {
86
85
  lines.push("Git diff: 无法获取");
87
86
  }
@@ -7,6 +7,8 @@
7
7
  * @Description :
8
8
  */
9
9
  import { DPS_SCORING_CONFIG as CFG } from "../../shared/dps-scoring.strategy.js";
10
+ import { homedir } from "os";
11
+ import { join } from "path";
10
12
 
11
13
  // ═══════════════ DPS 算法:共享辅助函数 ═══════════════
12
14
 
@@ -106,6 +108,11 @@ let _embedPipeline: { embed: (texts: string[]) => Promise<number[][]> } | null =
106
108
  export async function getEmbeddings(texts: string[]): Promise<number[][]> {
107
109
  if (!_embedPipeline) {
108
110
  const { pipeline, env } = await import("@huggingface/transformers");
111
+ // 与 add-coder CLI `model:download` 预下载同源:锚定用户级缓存(HF_HUB_CACHE → HF_HOME/hub → ~/.cache/huggingface/hub)
112
+ // ⚠️ transformers v3 默认 cacheDir 指向包内 .cache(重装即丢)——不锚定则 CLI 预下载的模型无法复用
113
+ const hubCache = process.env.HF_HUB_CACHE;
114
+ const home = process.env.HF_HOME || join(homedir(), ".cache", "huggingface");
115
+ env.cacheDir = hubCache || join(home, "hub");
109
116
  env.remoteHost = "https://hf-mirror.com";
110
117
  env.remotePathTemplate = "{model}/resolve/{revision}/";
111
118
  const extractor = await pipeline("feature-extraction", CFG.EMBEDDING_MODEL);
@@ -392,7 +392,7 @@ Step 8 收敛判断
392
392
  |------|---------|-------------------|
393
393
  | `check_add_route_status` | Step 3 前(前置守卫) | `"add_route_exists": { "enabled": true, "severity": "block" }` |
394
394
  | `check_add_route_completeness` | Step 3 代码完成后自检 | `"add_route_completeness": { "enabled": true, "severity": "block" }` |
395
- | `check_dps({ planKeyword })` | Step 0 末尾(进入 Step 1 前) | `"dps": { "enabled": true, "severity": "block", "threshold": 85 }` |
395
+ | `check_dps({ planKeyword })` | Step 0 末尾(进入 Step 1 前) | `"dps": { "enabled": true, "severity": "block", "threshold": 80 }` |
396
396
  | `check_rahs({ planKeyword })` | Step 4 末尾 + Step 8 收敛 | `"rahs": { "enabled": true, "severity": "block", "threshold": 90 }` |
397
397
 
398
398
  策略文件位于 `{{magicDir}}/sync-policy.json`,重型 add-route 模板已内置对应的 §0.8 / §4.6 闸门段落。
@@ -190,7 +190,7 @@ LLM: "Step 3 前置守卫:我先检查 add-route 是否存在"
190
190
 
191
191
  **错误输出**:
192
192
  ```
193
- Guardian 返回: "⛔ BLOCKED: DPS 评分 72/100(< 85)"
193
+ Guardian 返回: "⛔ BLOCKED: DPS 评分 72/100(< 80)"
194
194
  LLM: "DPS 偏低,但不影响代码,继续 Step 1..." [忽略阻断]
195
195
  ```
196
196