add-coder 0.3.12 → 0.3.13

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.
@@ -0,0 +1,66 @@
1
+ # DPS 评分全参数 —— caijuehub 驱动,后续调优只需改此 TOML
2
+
3
+ # ═══════════════ 维度一:语义相关性 ═══════════════
4
+ [semantic]
5
+ # 子权重:Jaccard Plan↔Specs / Cosine Plan↔Specs / Jaccard Plan↔Review / Cosine Plan↔Review
6
+ weights = [0.35, 0.45, 0.08, 0.12]
7
+ # Review 缺失时的惩罚系数(0.0=不惩罚,0.4=打六折,即得分×0.6)
8
+ missing_review_penalty = 0.0
9
+
10
+ # ═══════════════ 维度二:信息熵匹配 ═══════════════
11
+ [entropy]
12
+ # 熵差距乘数(entropyGap × multiplier,越小越不敏感)
13
+ gap_multiplier = 10
14
+ # Deng 不确定性惩罚:每个标记扣分,上限
15
+ deng_per_marker = 0.05
16
+ deng_max_penalty = 0.3
17
+ # Specs 缺失时的熵差距(无 Specs 时 gap 设为此值)
18
+ missing_specs_gap = 10
19
+
20
+ # ═══════════════ 维度三:CPM 关键路径 ═══════════════
21
+ [cpm]
22
+ # 子权重:原子化 / 注意力匹配 / 依赖完整性
23
+ sub_weights = [0.4, 0.35, 0.25]
24
+ # 业务原子化:Jaccard 重叠乘数(overlap × multiplier → penalty)
25
+ overlap_multiplier = 2
26
+ # Task 数超过此阈值时随机采样配对(避免 O(N²) 爆炸,0 = 不限制)
27
+ max_task_pairs = 200
28
+ # 注意力匹配:文件/Task 阈值
29
+ file_per_task_low = 3 # ≤3 → 100
30
+ file_per_task_mid = 5 # 3~5 → 70,>5 → 40
31
+ # 注意力匹配:描述熵阈值(bits)
32
+ entropy_low = 6 # <6 → 100
33
+ entropy_mid = 8 # 6~8 → 75,>8 → 50
34
+ # 注意力匹配:子权重(文件耦合 / 描述熵)
35
+ attention_weights = [0.5, 0.5]
36
+ # 依赖完整性:子权重(覆盖率 / Task Dependencies 章节存在)
37
+ dep_weights = [0.6, 0.4]
38
+
39
+ # ═══════════════ 维度四:结构完整度 ═══════════════
40
+ [structure]
41
+ # 扣分项
42
+ placeholder_penalty = 15
43
+ missing_specs_penalty = 30
44
+ missing_tasks_penalty = 15
45
+ missing_checklist_penalty = 15
46
+ # 子权重:结构分 / 回流分
47
+ sub_weights = [0.6, 0.4]
48
+
49
+ # ═══════════════ FFT 自适应权重 ═══════════════
50
+ [fft]
51
+ cold_start_threshold = 5
52
+ history_limit = 50
53
+ default_weights = [0.25, 0.25, 0.25, 0.25]
54
+
55
+ # ═══════════════ DPS 阈值 ═══════════════
56
+ [thresholds]
57
+ pass = 80
58
+ warn = 65
59
+
60
+ # ═══════════════ Embedding 模型(@xenova/transformers ONNX)═══════════════
61
+ [embedding]
62
+ # 首次 DPS 调用自动下载并缓存到本地 ~/.cache/huggingface/hub/
63
+ # 可选值(注释掉的前两个是备选方案):
64
+ # "Xenova/all-MiniLM-L6-v2" 英文为主,80MB,速度快
65
+ # "Xenova/multilingual-e5-small" 多语言,118MB,平衡选择
66
+ model = "Xenova/bge-small-zh-v1.5"
package/dist/index.js CHANGED
@@ -400,7 +400,7 @@ function diffPrisma(basePath, targetPath) {
400
400
  }
401
401
 
402
402
  // src/core/renderer.ts
403
- import { readFileSync as readFileSync4, readdirSync, statSync } from "fs";
403
+ import { readFileSync as readFileSync4, readdirSync, statSync, existsSync as existsSync5 } from "fs";
404
404
  import { join as join4, relative, dirname as dirname2 } from "path";
405
405
  import { fileURLToPath } from "url";
406
406
  import { parse } from "smol-toml";
@@ -420,10 +420,15 @@ var PLACEHOLDERS = {
420
420
  };
421
421
  function loadDpsThresholds() {
422
422
  try {
423
- const tomlPath = join4(
424
- __dirname,
425
- "../caijuehub/dps-scoring-rules.toml"
426
- );
423
+ const candidates = [
424
+ join4(__dirname, "../caijuehub/dps-scoring-rules.toml"),
425
+ join4(__dirname, "caijuehub/dps-scoring-rules.toml"),
426
+ join4(__dirname, "../../caijuehub/dps-scoring-rules.toml")
427
+ ];
428
+ const tomlPath = candidates.find(existsSync5);
429
+ if (!tomlPath) {
430
+ throw new Error(`\u5019\u9009\u8DEF\u5F84\u5747\u4E0D\u5B58\u5728: ${candidates.join(" | ")}`);
431
+ }
427
432
  const doc = parse(readFileSync4(tomlPath, "utf-8"));
428
433
  if (doc.thresholds && typeof doc.thresholds.pass === "number") {
429
434
  return {
@@ -539,7 +544,7 @@ function renderAdapter5(config, targetDir, dryRun, magicDir) {
539
544
 
540
545
  // src/lib/utils.ts
541
546
  import { createInterface as createInterface2 } from "readline";
542
- import { existsSync as existsSync5 } from "fs";
547
+ import { existsSync as existsSync6 } from "fs";
543
548
  import { resolve as resolve2 } from "path";
544
549
  function ask(q) {
545
550
  const rl = createInterface2({ input: process.stdin, output: process.stdout });
@@ -551,7 +556,7 @@ function ask(q) {
551
556
  });
552
557
  }
553
558
  function detectPm(projectRoot) {
554
- return existsSync5(resolve2(projectRoot, "pnpm-lock.yaml")) ? "pnpm" : "npm";
559
+ return existsSync6(resolve2(projectRoot, "pnpm-lock.yaml")) ? "pnpm" : "npm";
555
560
  }
556
561
 
557
562
  // src/cli/prisma-injector.ts
@@ -560,7 +565,7 @@ import { fileURLToPath as fileURLToPath2 } from "url";
560
565
 
561
566
  // src/caijuehub/strategies/prisma.strategy.ts
562
567
  import { spawnSync } from "child_process";
563
- import { copyFileSync, existsSync as existsSync6, readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
568
+ import { copyFileSync, existsSync as existsSync7, readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
564
569
  import { resolve as resolve3 } from "path";
565
570
  var PRISMA_CONFIG = {
566
571
  onMissing: "ask",
@@ -613,10 +618,10 @@ function runPrismaInit(projectRoot, provider, schemaPath) {
613
618
  stdio: "inherit",
614
619
  shell: false
615
620
  });
616
- if (initResult.status !== 0 || !existsSync6(schemaPath)) {
621
+ if (initResult.status !== 0 || !existsSync7(schemaPath)) {
617
622
  console.log("prisma init \u5931\u8D25\uFF0C\u624B\u52A8\u521B\u5EFA schema.prisma ...");
618
623
  const prismaDir = resolve3(projectRoot, "prisma");
619
- if (!existsSync6(prismaDir)) mkdirSync2(prismaDir, { recursive: true });
624
+ if (!existsSync7(prismaDir)) mkdirSync2(prismaDir, { recursive: true });
620
625
  const content = `generator client {
621
626
  provider = "prisma-client-js"
622
627
  }
@@ -627,7 +632,7 @@ datasource db {
627
632
  `;
628
633
  writeFileSync2(schemaPath, content, "utf-8");
629
634
  const devEnvPath = resolve3(projectRoot, ".env.development");
630
- if (!existsSync6(devEnvPath)) {
635
+ if (!existsSync7(devEnvPath)) {
631
636
  const defaultUrl = provider === "sqlite" ? 'DATABASE_URL="file:./data/dev.db"' : '# \u8BF7\u7F16\u8F91\u4E3A\u4F60\u7684\u6570\u636E\u5E93\u8FDE\u63A5\u4FE1\u606F\nDATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DB?schema=public"';
632
637
  writeFileSync2(devEnvPath, defaultUrl + "\n", "utf-8");
633
638
  console.log("\u5DF2\u521B\u5EFA .env.development");
@@ -639,16 +644,16 @@ datasource db {
639
644
  function postInitSetup(projectRoot, schemaPath, addPrismaTemplate, destPath) {
640
645
  const envPath = resolve3(projectRoot, ".env");
641
646
  const devEnvPath = resolve3(projectRoot, ".env.development");
642
- if (existsSync6(envPath)) {
647
+ if (existsSync7(envPath)) {
643
648
  const envContent = readFileSync5(envPath, "utf-8");
644
649
  const dbUrl = envContent.match(/DATABASE_URL=.*/);
645
650
  if (dbUrl) {
646
- const existing = existsSync6(devEnvPath) ? readFileSync5(devEnvPath, "utf-8") : "";
651
+ const existing = existsSync7(devEnvPath) ? readFileSync5(devEnvPath, "utf-8") : "";
647
652
  if (!existing.includes("DATABASE_URL=")) {
648
653
  writeFileSync2(devEnvPath, `${existing}${existing ? "\n" : ""}${dbUrl[0]}
649
654
  `, "utf-8");
650
655
  }
651
- if (existsSync6(envPath)) unlinkSync(envPath);
656
+ if (existsSync7(envPath)) unlinkSync(envPath);
652
657
  console.log("\u5DF2\u5C06 DATABASE_URL \u8FC1\u79FB\u5230 .env.development");
653
658
  }
654
659
  }
@@ -661,7 +666,7 @@ async function injectPrisma(projectRoot, addPrismaTemplate, options = {}) {
661
666
  const schemaPath = resolve3(prismaDir, "schema.prisma");
662
667
  const destPath = resolve3(prismaDir, "add.prisma");
663
668
  let justInited = false;
664
- if (!existsSync6(prismaDir) || !existsSync6(schemaPath)) {
669
+ if (!existsSync7(prismaDir) || !existsSync7(schemaPath)) {
665
670
  if (C.onMissing === "skip") {
666
671
  console.log("\u8DF3\u8FC7\uFF1A\u7F3A\u5C11 Prisma");
667
672
  return true;
@@ -680,7 +685,7 @@ async function injectPrisma(projectRoot, addPrismaTemplate, options = {}) {
680
685
  postInitSetup(projectRoot, schemaPath, addPrismaTemplate, destPath);
681
686
  justInited = true;
682
687
  }
683
- if (existsSync6(destPath) && !justInited) {
688
+ if (existsSync7(destPath) && !justInited) {
684
689
  if (options.dryRun) {
685
690
  console.log("[dry-run] \u5DF2\u6709 add.prisma");
686
691
  return true;
@@ -753,7 +758,7 @@ async function injectPrisma2(projectRoot, options = {}) {
753
758
  }
754
759
 
755
760
  // src/cli/commands/init.ts
756
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3, copyFileSync as copyFileSync2, readdirSync as readdirSync2 } from "fs";
761
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync8, mkdirSync as mkdirSync3, copyFileSync as copyFileSync2, readdirSync as readdirSync2 } from "fs";
757
762
  import { createHash } from "crypto";
758
763
  import { resolve as resolve5 } from "path";
759
764
  import { spawnSync as spawnSync2 } from "child_process";
@@ -933,13 +938,13 @@ main().catch((e) => { console.error(e); process.exit(1); });
933
938
  console.log(`[dry-run] \u5C06\u5199\u5165 ${scriptPath}`);
934
939
  return;
935
940
  }
936
- if (!existsSync7(scriptsDir)) mkdirSync3(scriptsDir, { recursive: true });
941
+ if (!existsSync8(scriptsDir)) mkdirSync3(scriptsDir, { recursive: true });
937
942
  writeFileSync3(scriptPath, content, "utf-8");
938
943
  console.log("\u5DF2\u751F\u6210 scripts/export-db.ts");
939
944
  }
940
945
  function injectDbExportScript(projectRoot, dryRun) {
941
946
  const pkgPath = resolve5(projectRoot, "package.json");
942
- if (!existsSync7(pkgPath)) return;
947
+ if (!existsSync8(pkgPath)) return;
943
948
  if (dryRun) {
944
949
  console.log("[dry-run] \u5C06\u6CE8\u5165 db:export");
945
950
  return;
@@ -974,13 +979,13 @@ function writeComposeEnv(ctx) {
974
979
  if (!db.reuseExisting) {
975
980
  const composeName = db.container === "podman" ? "podman-compose.add.yml" : "docker-compose.add.yml";
976
981
  const composePath = resolve5(projectRoot, composeName);
977
- if (!options.dryRun && (!existsSync7(composePath) || options.force)) {
982
+ if (!options.dryRun && (!existsSync8(composePath) || options.force)) {
978
983
  writeFileSync3(composePath, composeContent(config.projectName || "add-project"), "utf-8");
979
984
  console.log(`\u5DF2\u521B\u5EFA ${composeName}`);
980
985
  }
981
986
  }
982
987
  const devEnvPath = resolve5(projectRoot, ".env.development");
983
- if (!options.dryRun && existsSync7(devEnvPath)) {
988
+ if (!options.dryRun && existsSync8(devEnvPath)) {
984
989
  const existing = readFileSync6(devEnvPath, "utf-8");
985
990
  if (!/^DATABASE_USER=/m.test(existing)) {
986
991
  writeFileSync3(devEnvPath, existing + `
@@ -1021,7 +1026,7 @@ async function renderAndWrite(ctx) {
1021
1026
  }
1022
1027
  for (const d of [".add", magicDir]) {
1023
1028
  const reviewsDir = resolve5(projectRoot, d, "reviews");
1024
- if (!existsSync7(reviewsDir)) {
1029
+ if (!existsSync8(reviewsDir)) {
1025
1030
  if (dry) {
1026
1031
  console.log(`[dry-run] \u5C06\u521B\u5EFA ${reviewsDir}/`);
1027
1032
  } else {
@@ -1065,7 +1070,7 @@ async function deployDatabase(ctx) {
1065
1070
  }
1066
1071
  if (db.engine === "postgresql" && db.container === "manual") {
1067
1072
  const dbScript = resolve5(projectRoot, magicDir, "scripts", "db-ensure.sh");
1068
- if (existsSync7(dbScript)) spawnSync2("bash", [dbScript, "postgresql", "manual"], { cwd: projectRoot, stdio: "inherit" });
1073
+ if (existsSync8(dbScript)) spawnSync2("bash", [dbScript, "postgresql", "manual"], { cwd: projectRoot, stdio: "inherit" });
1069
1074
  console.log([
1070
1075
  "",
1071
1076
  "\u2501".repeat(30),
@@ -1106,12 +1111,12 @@ function deployDocs(ctx) {
1106
1111
  for (const d of ["00-\u9700\u6C42", "01-\u67B6\u6784", "02-\u89C4\u8303"]) {
1107
1112
  const srcDir = resolve5(groundingSrc, d);
1108
1113
  const destDir = resolve5(docsBase, d);
1109
- if (!existsSync7(destDir)) mkdirSync3(destDir, { recursive: true });
1110
- if (!existsSync7(srcDir)) continue;
1114
+ if (!existsSync8(destDir)) mkdirSync3(destDir, { recursive: true });
1115
+ if (!existsSync8(srcDir)) continue;
1111
1116
  for (const f of readdirSync2(srcDir)) {
1112
1117
  const src = resolve5(srcDir, f);
1113
1118
  const dest = resolve5(destDir, f);
1114
- if (existsSync7(dest)) continue;
1119
+ if (existsSync8(dest)) continue;
1115
1120
  try {
1116
1121
  copyFileSync2(src, dest);
1117
1122
  } catch {
@@ -1139,7 +1144,7 @@ function finalize(ctx, result) {
1139
1144
  }
1140
1145
 
1141
1146
  // src/cli/commands/sync.ts
1142
- import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
1147
+ import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "fs";
1143
1148
  import { resolve as resolve6, dirname as dirname4 } from "path";
1144
1149
  import { createHash as createHash2 } from "crypto";
1145
1150
 
@@ -1277,7 +1282,7 @@ async function syncCommand(options = {}) {
1277
1282
  let sameCount = 0;
1278
1283
  for (const [relPath, content] of candidates) {
1279
1284
  const absPath = resolve6(projectRoot, relPath);
1280
- if (!existsSync8(absPath)) {
1285
+ if (!existsSync9(absPath)) {
1281
1286
  missingFiles.set(relPath, content);
1282
1287
  } else if (establishBaseline) {
1283
1288
  missingFiles.set(relPath, content);
@@ -1315,7 +1320,7 @@ async function syncCommand(options = {}) {
1315
1320
  }
1316
1321
  const missing = /* @__PURE__ */ new Map();
1317
1322
  for (const [relPath, content] of allFiles) {
1318
- if (!existsSync8(resolve6(projectRoot, relPath))) {
1323
+ if (!existsSync9(resolve6(projectRoot, relPath))) {
1319
1324
  missing.set(relPath, content);
1320
1325
  }
1321
1326
  }
@@ -1337,7 +1342,7 @@ async function syncCommand(options = {}) {
1337
1342
  }
1338
1343
  function printMigrateGuidance(targetPath, changed) {
1339
1344
  const g = SYNC_PRISMA_CONFIG.POST_SYNC;
1340
- const isManaged = existsSync8(resolve6(dirname4(targetPath), "migrations"));
1345
+ const isManaged = existsSync9(resolve6(dirname4(targetPath), "migrations"));
1341
1346
  const actions = isManaged ? g.MANAGED_ACTIONS : g.UNMANAGED_ACTIONS;
1342
1347
  console.log(` \u25B6 \u5DF2\u5199\u5165 ${changed} \u5904\u53D8\u66F4\uFF0C${g.HEADER}`);
1343
1348
  for (const a of actions) {
@@ -1361,7 +1366,7 @@ async function checkPrismaDiff(projectRoot, options) {
1361
1366
  if (!options.patch) return;
1362
1367
  const basePath = resolve6(projectRoot, SYNC_PRISMA_CONFIG.BASE_SCHEMA);
1363
1368
  const targetPath = resolve6(projectRoot, SYNC_PRISMA_CONFIG.TARGET_PATTERN);
1364
- if (!existsSync8(basePath)) {
1369
+ if (!existsSync9(basePath)) {
1365
1370
  console.log(`
1366
1371
  \u26A0\uFE0F \u57FA\u51C6 schema \u4E0D\u5B58\u5728: ${basePath}`);
1367
1372
  console.log(` \u8BF7\u786E\u4FDD add-coder \u5DF2\u6B63\u786E\u5B89\u88C5\u3002`);
@@ -1373,7 +1378,7 @@ async function checkPrismaDiff(projectRoot, options) {
1373
1378
  \u2705 Prisma schema \u4E0E add-coder \u6807\u51C6\u4E00\u81F4\uFF0C\u65E0\u9700\u540C\u6B65\u3002`);
1374
1379
  return;
1375
1380
  }
1376
- const targetExists = existsSync8(targetPath);
1381
+ const targetExists = existsSync9(targetPath);
1377
1382
  let modifiedCount = 0;
1378
1383
  console.log(`
1379
1384
  \u26A0\uFE0F Prisma schema \u5DEE\u5F02\u68C0\u6D4B:`);
@@ -1560,13 +1565,12 @@ function injectFieldLines(targetPath, basePath, modelName, fieldKeys) {
1560
1565
  const fieldName = key.split(":")[0];
1561
1566
  const fieldLine = baseFields[fieldName];
1562
1567
  if (!fieldLine) continue;
1563
- if (new RegExp(`^\\s*${fieldName}\\s+`, "m").test(content)) continue;
1564
- const modelRegex = new RegExp(`(model\\s+${modelName}\\s*\\{[^}]*?)\\n(\\s*\\})`, "m");
1568
+ const modelRegex = new RegExp(`(model\\s+${modelName}\\s*\\{)([^}]*?)(\\n\\s*\\})`, "m");
1565
1569
  const m = content.match(modelRegex);
1566
1570
  if (!m) continue;
1567
- content = content.replace(modelRegex, `$1
1568
- ${fieldLine}
1569
- $2`);
1571
+ if (new RegExp(`^\\s*${fieldName}\\s+`, "m").test(m[2])) continue;
1572
+ content = content.replace(modelRegex, `$1$2
1573
+ ${fieldLine}$3`);
1570
1574
  count++;
1571
1575
  }
1572
1576
  if (count > 0) writeFileSync4(targetPath, content, "utf-8");
@@ -1591,7 +1595,7 @@ function overwriteFieldLines(targetPath, basePath, modelName, conflicts) {
1591
1595
  }
1592
1596
 
1593
1597
  // src/cli/commands/status.ts
1594
- import { existsSync as existsSync9 } from "fs";
1598
+ import { existsSync as existsSync10 } from "fs";
1595
1599
  import { resolve as resolve7 } from "path";
1596
1600
  async function statusCommand() {
1597
1601
  const projectRoot = process.cwd();
@@ -1601,7 +1605,7 @@ async function statusCommand() {
1601
1605
  const missing = [];
1602
1606
  const present = [];
1603
1607
  for (const [relPath] of coreFiles) {
1604
- if (existsSync9(resolve7(projectRoot, relPath))) {
1608
+ if (existsSync10(resolve7(projectRoot, relPath))) {
1605
1609
  present.push(relPath);
1606
1610
  } else {
1607
1611
  missing.push(relPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "add-coder",
3
- "version": "0.3.12",
3
+ "version": "0.3.13",
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": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "_version": "0.3.12",
2
+ "_version": "0.3.13",
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",