add-coder 0.3.4 → 0.3.6-feature-hitl-enhance-v1.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/README.md +3 -1
- package/dist/index.js +358 -42
- package/package.json +2 -1
- package/templates/.add-coder-src-hash.json +24 -18
- package/templates/adapters/claude/hooks/pre-tool-use.sh +33 -1
- package/templates/adapters/codex/hooks/doc-format-guard.sh +6 -2
- package/templates/adapters/codex/hooks/lib/state-detect.sh +111 -0
- package/templates/adapters/codex/hooks/pre-tool-use.sh +33 -2
- package/templates/adapters/qoder/hooks/pre-tool-use.sh +33 -1
- package/templates/adapters/trae/hooks/doc-format-guard.sh +6 -2
- package/templates/adapters/trae/hooks/lib/state-detect.sh +111 -0
- package/templates/adapters/trae/hooks/pre-tool-use.sh +33 -2
- package/templates/adapters/vscode/hooks/pre-tool-use.sh +33 -1
- package/templates/core/hooks/doc-format-guard.sh +1 -0
- package/templates/core/hooks/pre-tool-use.sh +32 -1
- package/templates/core/prisma/add.prisma +77 -0
- package/templates/core/rules/project_rules.md +51 -0
- package/templates/core/scripts/mcp-server/shared/package.json +1 -0
- package/templates/core/scripts/mcp-server/shared/prisma.ts +6 -2
- package/templates/core/scripts/mcp-server/tools/gateway.ts +249 -39
- package/templates/core/scripts/mcp-server/tools/hitl.ts +435 -0
- package/templates/core/scripts/mcp-server/tools/index.ts +7 -1
- package/templates/core/scripts/mcp-server/tools/plan.ts +191 -0
- package/templates/core/scripts/mcp-server/tools/review.ts +199 -0
- package/templates/core/skills/add-paradigm/SKILL.md +81 -3
- package/templates/core/templates/hitl-template.md +30 -0
- package/templates/core/templates/hitl-template.schema.json +23 -0
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Make 0.75 into one.
|
|
4
4
|
|
|
5
|
+

|
|
6
|
+
|
|
5
7
|
> 🀄中文 | 🔤[English](#-english-readme)
|
|
6
8
|
|
|
7
9
|
**AI 代码治理的落地方案** — [codein2027](https://github.com/xiaomingming92/codein2027) 快速构建 ADD 编程范式的完整脚手架。以「审计即基础设施」为核心,彻底打破编程过程黑盒与跨轮失忆,让编程范式进化为可审计、可追溯、可收敛的新时代。 [NPM](https://www.npmjs.com/package/add-coder) · [GitHub](https://github.com/xiaomingming92/add-coder)
|
|
@@ -9,7 +11,7 @@ Make 0.75 into one.
|
|
|
9
11
|
|
|
10
12
|
> 🧭 **从零上手实操?** 请参见 [GUIDE.md](https://github.com/xiaomingming92/add-coder/blob/main/GUIDE.md) — 包含触发词速查、需求转 Plan、完整链路演练。
|
|
11
13
|
>
|
|
12
|
-
> 📖 **想看 ADD 范式真实落地案例?** → [老设备续命工程](#-案例示范老安卓设备续命工程) — weather_proxy
|
|
14
|
+
> 📖 **想看 ADD 范式真实落地案例?** → [老设备续命工程](#-案例示范老安卓设备续命工程) — weather_proxy 实践。->或者add-coder项目本身就是很好的落地案例(自举成功)。
|
|
13
15
|
>
|
|
14
16
|
> 🔄 **add-coder 升级后担心更新本地Skills,rules,hooks,templates等等会很麻烦?** `npx add-coder sync --adapter=qoder --patch` — 实操看 [GUIDE.md §版本升级](https://github.com/xiaomingming92/add-coder/blob/main/GUIDE.md#%E4%B8%83add-coder-%E5%8D%87%E7%BA%A7%E5%90%8E%E6%80%8E%E4%B9%88%E6%9B%B4%E6%96%B0%E6%9C%AC%E5%9C%B0%E6%96%87%E4%BB%B6) | 原理看 [DEVELOPMENT.md](https://github.com/xiaomingming92/add-coder/blob/main/DEVELOPMENT.md)
|
|
15
17
|
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
2
8
|
|
|
3
9
|
// src/cli/index.ts
|
|
4
|
-
import { readFileSync as
|
|
10
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
5
11
|
import { Command } from "commander";
|
|
6
12
|
|
|
7
13
|
// src/caijuehub/strategies/detect.strategy.ts
|
|
@@ -296,13 +302,97 @@ async function writeFiles(projectRoot, files, options = {}) {
|
|
|
296
302
|
return { created, skipped, overwritten };
|
|
297
303
|
}
|
|
298
304
|
|
|
305
|
+
// src/caijuehub/strategies/prisma-sync.strategy.ts
|
|
306
|
+
var SYNC_PRISMA_CONFIG = {
|
|
307
|
+
BASE_SCHEMA: "node_modules/add-coder/templates/core/prisma/add.prisma",
|
|
308
|
+
TARGET_PATTERN: "prisma/add.prisma",
|
|
309
|
+
SYNC_ITEMS: ["model", "enum"],
|
|
310
|
+
ON_MISSING_MODEL: "interactive",
|
|
311
|
+
ON_FIELD_CONFLICT: "skip",
|
|
312
|
+
ON_MISSING_FIELD: "interactive",
|
|
313
|
+
ON_EXTRA_FIELD: "ignore",
|
|
314
|
+
PROMPT: "add-coder \u6807\u51C6 add.prisma \u4E0E\u6D88\u8D39\u65B9 schema \u5B58\u5728\u5DEE\u5F02\u3002"
|
|
315
|
+
};
|
|
316
|
+
|
|
299
317
|
// src/cli/writer.ts
|
|
318
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
300
319
|
async function writeFiles2(projectRoot, files, options = {}) {
|
|
301
320
|
return writeFiles(projectRoot, files, options);
|
|
302
321
|
}
|
|
322
|
+
function parseSchemaBlocks(content) {
|
|
323
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
324
|
+
const blockRegex = /^(model|enum)\s+(\w+)\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/gm;
|
|
325
|
+
let match;
|
|
326
|
+
while ((match = blockRegex.exec(content)) !== null) {
|
|
327
|
+
const [, type, name, inner] = match;
|
|
328
|
+
let fields = [];
|
|
329
|
+
if (type === "enum") {
|
|
330
|
+
fields = inner.split("\n").map((l) => l.replace(/#.*$/, "").trim()).filter((l) => l.length > 0);
|
|
331
|
+
} else {
|
|
332
|
+
const fieldRegex = /^\s*(\w+)\s+(\w+(?:\([^)]*\))?(?:\[\])?\??)/gm;
|
|
333
|
+
let fm;
|
|
334
|
+
while ((fm = fieldRegex.exec(inner)) !== null) {
|
|
335
|
+
fields.push(`${fm[1]}:${fm[2]}`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
blocks.set(`${type}:${name}`, { type, name, body: match[0], fields });
|
|
339
|
+
}
|
|
340
|
+
return blocks;
|
|
341
|
+
}
|
|
342
|
+
function diffPrisma(basePath, targetPath) {
|
|
343
|
+
const baseContent = existsSync4(basePath) ? readFileSync3(basePath, "utf-8") : "";
|
|
344
|
+
const targetContent = existsSync4(targetPath) ? readFileSync3(targetPath, "utf-8") : "";
|
|
345
|
+
if (!baseContent) return { hasDiff: false, baseSchema: basePath, targetPath, missing: [], fieldDiffs: [] };
|
|
346
|
+
const baseBlocks = parseSchemaBlocks(baseContent);
|
|
347
|
+
const targetBlocks = parseSchemaBlocks(targetContent);
|
|
348
|
+
const missing = [];
|
|
349
|
+
const fieldDiffs = [];
|
|
350
|
+
const types = SYNC_PRISMA_CONFIG.SYNC_ITEMS;
|
|
351
|
+
for (const [key, block] of baseBlocks) {
|
|
352
|
+
if (!types.includes(block.type)) continue;
|
|
353
|
+
if (!targetBlocks.has(key)) {
|
|
354
|
+
missing.push(block);
|
|
355
|
+
} else {
|
|
356
|
+
const target = targetBlocks.get(key);
|
|
357
|
+
const baseByName = /* @__PURE__ */ new Map();
|
|
358
|
+
for (const f of block.fields) {
|
|
359
|
+
const [name] = f.split(":");
|
|
360
|
+
baseByName.set(name, f);
|
|
361
|
+
}
|
|
362
|
+
const targetByName = /* @__PURE__ */ new Map();
|
|
363
|
+
for (const f of target.fields) {
|
|
364
|
+
const [name] = f.split(":");
|
|
365
|
+
targetByName.set(name, f);
|
|
366
|
+
}
|
|
367
|
+
const conflicts = [];
|
|
368
|
+
const missingFields = [];
|
|
369
|
+
const extraFields = [];
|
|
370
|
+
for (const [name, baseDef] of baseByName) {
|
|
371
|
+
if (targetByName.has(name)) {
|
|
372
|
+
const targetDef = targetByName.get(name);
|
|
373
|
+
if (baseDef !== targetDef) {
|
|
374
|
+
conflicts.push({ fieldName: name, baseDef, targetDef });
|
|
375
|
+
}
|
|
376
|
+
} else {
|
|
377
|
+
missingFields.push(baseDef);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
for (const [name, targetDef] of targetByName) {
|
|
381
|
+
if (!baseByName.has(name)) {
|
|
382
|
+
extraFields.push(targetDef);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (conflicts.length > 0 || missingFields.length > 0 || extraFields.length > 0) {
|
|
386
|
+
fieldDiffs.push({ type: block.type, name: block.name, conflicts, missingFields, extraFields });
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
const hasDiff = missing.length > 0 || fieldDiffs.length > 0;
|
|
391
|
+
return { hasDiff, baseSchema: basePath, targetPath, missing, fieldDiffs };
|
|
392
|
+
}
|
|
303
393
|
|
|
304
394
|
// src/core/renderer.ts
|
|
305
|
-
import { readFileSync as
|
|
395
|
+
import { readFileSync as readFileSync4, readdirSync, statSync } from "fs";
|
|
306
396
|
import { join as join4, relative, dirname as dirname2 } from "path";
|
|
307
397
|
import { fileURLToPath } from "url";
|
|
308
398
|
var __filename = fileURLToPath(import.meta.url);
|
|
@@ -339,7 +429,7 @@ function renderCore(config, dryRun) {
|
|
|
339
429
|
if (statSync(full).isDirectory()) {
|
|
340
430
|
if (!SKIP_DIRS.has(name)) walk(full, join4(base, name));
|
|
341
431
|
} else {
|
|
342
|
-
const content =
|
|
432
|
+
const content = readFileSync4(full, "utf-8");
|
|
343
433
|
const rendered = render(content, config);
|
|
344
434
|
const targetRel = join4(CORE_TARGET, relative(CORE_DIR, full));
|
|
345
435
|
files.set(targetRel, rendered);
|
|
@@ -365,7 +455,7 @@ function renderAdapterBase(config, magicPath, githubHooksToRoot, dryRun) {
|
|
|
365
455
|
if (statSync(full).isDirectory()) {
|
|
366
456
|
walk(full, join4(base, name), targetPrefix);
|
|
367
457
|
} else {
|
|
368
|
-
const content =
|
|
458
|
+
const content = readFileSync4(full, "utf-8");
|
|
369
459
|
const rendered = render(content, config);
|
|
370
460
|
let targetRel;
|
|
371
461
|
if (targetPrefix) {
|
|
@@ -414,7 +504,7 @@ function renderAdapter5(config, targetDir, dryRun, magicDir) {
|
|
|
414
504
|
|
|
415
505
|
// src/lib/utils.ts
|
|
416
506
|
import { createInterface as createInterface2 } from "readline";
|
|
417
|
-
import { existsSync as
|
|
507
|
+
import { existsSync as existsSync5 } from "fs";
|
|
418
508
|
import { resolve as resolve2 } from "path";
|
|
419
509
|
function ask(q) {
|
|
420
510
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
@@ -426,7 +516,7 @@ function ask(q) {
|
|
|
426
516
|
});
|
|
427
517
|
}
|
|
428
518
|
function detectPm(projectRoot) {
|
|
429
|
-
return
|
|
519
|
+
return existsSync5(resolve2(projectRoot, "pnpm-lock.yaml")) ? "pnpm" : "npm";
|
|
430
520
|
}
|
|
431
521
|
|
|
432
522
|
// src/cli/prisma-injector.ts
|
|
@@ -435,7 +525,7 @@ import { fileURLToPath as fileURLToPath2 } from "url";
|
|
|
435
525
|
|
|
436
526
|
// src/caijuehub/strategies/prisma.strategy.ts
|
|
437
527
|
import { spawnSync } from "child_process";
|
|
438
|
-
import { copyFileSync, existsSync as
|
|
528
|
+
import { copyFileSync, existsSync as existsSync6, readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
439
529
|
import { resolve as resolve3 } from "path";
|
|
440
530
|
var PRISMA_CONFIG = {
|
|
441
531
|
onMissing: "ask",
|
|
@@ -488,10 +578,10 @@ function runPrismaInit(projectRoot, provider, schemaPath) {
|
|
|
488
578
|
stdio: "inherit",
|
|
489
579
|
shell: false
|
|
490
580
|
});
|
|
491
|
-
if (initResult.status !== 0 || !
|
|
581
|
+
if (initResult.status !== 0 || !existsSync6(schemaPath)) {
|
|
492
582
|
console.log("prisma init \u5931\u8D25\uFF0C\u624B\u52A8\u521B\u5EFA schema.prisma ...");
|
|
493
583
|
const prismaDir = resolve3(projectRoot, "prisma");
|
|
494
|
-
if (!
|
|
584
|
+
if (!existsSync6(prismaDir)) mkdirSync2(prismaDir, { recursive: true });
|
|
495
585
|
const content = `generator client {
|
|
496
586
|
provider = "prisma-client-js"
|
|
497
587
|
}
|
|
@@ -502,7 +592,7 @@ datasource db {
|
|
|
502
592
|
`;
|
|
503
593
|
writeFileSync2(schemaPath, content, "utf-8");
|
|
504
594
|
const devEnvPath = resolve3(projectRoot, ".env.development");
|
|
505
|
-
if (!
|
|
595
|
+
if (!existsSync6(devEnvPath)) {
|
|
506
596
|
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"';
|
|
507
597
|
writeFileSync2(devEnvPath, defaultUrl + "\n", "utf-8");
|
|
508
598
|
console.log("\u5DF2\u521B\u5EFA .env.development");
|
|
@@ -514,16 +604,16 @@ datasource db {
|
|
|
514
604
|
function postInitSetup(projectRoot, schemaPath, addPrismaTemplate, destPath) {
|
|
515
605
|
const envPath = resolve3(projectRoot, ".env");
|
|
516
606
|
const devEnvPath = resolve3(projectRoot, ".env.development");
|
|
517
|
-
if (
|
|
518
|
-
const envContent =
|
|
607
|
+
if (existsSync6(envPath)) {
|
|
608
|
+
const envContent = readFileSync5(envPath, "utf-8");
|
|
519
609
|
const dbUrl = envContent.match(/DATABASE_URL=.*/);
|
|
520
610
|
if (dbUrl) {
|
|
521
|
-
const existing =
|
|
611
|
+
const existing = existsSync6(devEnvPath) ? readFileSync5(devEnvPath, "utf-8") : "";
|
|
522
612
|
if (!existing.includes("DATABASE_URL=")) {
|
|
523
613
|
writeFileSync2(devEnvPath, `${existing}${existing ? "\n" : ""}${dbUrl[0]}
|
|
524
614
|
`, "utf-8");
|
|
525
615
|
}
|
|
526
|
-
if (
|
|
616
|
+
if (existsSync6(envPath)) unlinkSync(envPath);
|
|
527
617
|
console.log("\u5DF2\u5C06 DATABASE_URL \u8FC1\u79FB\u5230 .env.development");
|
|
528
618
|
}
|
|
529
619
|
}
|
|
@@ -536,7 +626,7 @@ async function injectPrisma(projectRoot, addPrismaTemplate, options = {}) {
|
|
|
536
626
|
const schemaPath = resolve3(prismaDir, "schema.prisma");
|
|
537
627
|
const destPath = resolve3(prismaDir, "add.prisma");
|
|
538
628
|
let justInited = false;
|
|
539
|
-
if (!
|
|
629
|
+
if (!existsSync6(prismaDir) || !existsSync6(schemaPath)) {
|
|
540
630
|
if (C.onMissing === "skip") {
|
|
541
631
|
console.log("\u8DF3\u8FC7\uFF1A\u7F3A\u5C11 Prisma");
|
|
542
632
|
return true;
|
|
@@ -555,7 +645,7 @@ async function injectPrisma(projectRoot, addPrismaTemplate, options = {}) {
|
|
|
555
645
|
postInitSetup(projectRoot, schemaPath, addPrismaTemplate, destPath);
|
|
556
646
|
justInited = true;
|
|
557
647
|
}
|
|
558
|
-
if (
|
|
648
|
+
if (existsSync6(destPath) && !justInited) {
|
|
559
649
|
if (options.dryRun) {
|
|
560
650
|
console.log("[dry-run] \u5DF2\u6709 add.prisma");
|
|
561
651
|
return true;
|
|
@@ -572,8 +662,8 @@ async function injectPrisma(projectRoot, addPrismaTemplate, options = {}) {
|
|
|
572
662
|
console.log("\u8986\u76D6");
|
|
573
663
|
} else if (choice === "d") {
|
|
574
664
|
copyFileSync(destPath, destPath + ".bak");
|
|
575
|
-
console.log("=== \u5F53\u524D\uFF08\u5DF2\u5907\u4EFD\uFF09===\n" +
|
|
576
|
-
console.log("=== \u6A21\u677F ===\n" +
|
|
665
|
+
console.log("=== \u5F53\u524D\uFF08\u5DF2\u5907\u4EFD\uFF09===\n" + readFileSync5(destPath, "utf-8"));
|
|
666
|
+
console.log("=== \u6A21\u677F ===\n" + readFileSync5(addPrismaTemplate, "utf-8"));
|
|
577
667
|
if (await ask("\u786E\u8BA4\u8986\u76D6\uFF1F[y/N] ") !== "y") {
|
|
578
668
|
console.log("\u5DF2\u8DF3\u8FC7");
|
|
579
669
|
return true;
|
|
@@ -628,7 +718,7 @@ async function injectPrisma2(projectRoot, options = {}) {
|
|
|
628
718
|
}
|
|
629
719
|
|
|
630
720
|
// src/cli/commands/init.ts
|
|
631
|
-
import { readFileSync as
|
|
721
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3, copyFileSync as copyFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
632
722
|
import { createHash } from "crypto";
|
|
633
723
|
import { resolve as resolve5 } from "path";
|
|
634
724
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
@@ -808,18 +898,18 @@ main().catch((e) => { console.error(e); process.exit(1); });
|
|
|
808
898
|
console.log(`[dry-run] \u5C06\u5199\u5165 ${scriptPath}`);
|
|
809
899
|
return;
|
|
810
900
|
}
|
|
811
|
-
if (!
|
|
901
|
+
if (!existsSync7(scriptsDir)) mkdirSync3(scriptsDir, { recursive: true });
|
|
812
902
|
writeFileSync3(scriptPath, content, "utf-8");
|
|
813
903
|
console.log("\u5DF2\u751F\u6210 scripts/export-db.ts");
|
|
814
904
|
}
|
|
815
905
|
function injectDbExportScript(projectRoot, dryRun) {
|
|
816
906
|
const pkgPath = resolve5(projectRoot, "package.json");
|
|
817
|
-
if (!
|
|
907
|
+
if (!existsSync7(pkgPath)) return;
|
|
818
908
|
if (dryRun) {
|
|
819
909
|
console.log("[dry-run] \u5C06\u6CE8\u5165 db:export");
|
|
820
910
|
return;
|
|
821
911
|
}
|
|
822
|
-
const pkg = JSON.parse(
|
|
912
|
+
const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
823
913
|
if (!pkg.scripts) pkg.scripts = {};
|
|
824
914
|
if (!pkg.scripts["db:export"]) {
|
|
825
915
|
pkg.scripts["db:export"] = "npx tsx scripts/export-db.ts";
|
|
@@ -849,14 +939,14 @@ function writeComposeEnv(ctx) {
|
|
|
849
939
|
if (!db.reuseExisting) {
|
|
850
940
|
const composeName = db.container === "podman" ? "podman-compose.add.yml" : "docker-compose.add.yml";
|
|
851
941
|
const composePath = resolve5(projectRoot, composeName);
|
|
852
|
-
if (!options.dryRun && (!
|
|
942
|
+
if (!options.dryRun && (!existsSync7(composePath) || options.force)) {
|
|
853
943
|
writeFileSync3(composePath, composeContent(config.projectName || "add-project"), "utf-8");
|
|
854
944
|
console.log(`\u5DF2\u521B\u5EFA ${composeName}`);
|
|
855
945
|
}
|
|
856
946
|
}
|
|
857
947
|
const devEnvPath = resolve5(projectRoot, ".env.development");
|
|
858
|
-
if (!options.dryRun &&
|
|
859
|
-
const existing =
|
|
948
|
+
if (!options.dryRun && existsSync7(devEnvPath)) {
|
|
949
|
+
const existing = readFileSync6(devEnvPath, "utf-8");
|
|
860
950
|
if (!/^DATABASE_USER=/m.test(existing)) {
|
|
861
951
|
writeFileSync3(devEnvPath, existing + `
|
|
862
952
|
DATABASE_USER=${db.user || "admin"}
|
|
@@ -896,7 +986,7 @@ async function renderAndWrite(ctx) {
|
|
|
896
986
|
}
|
|
897
987
|
for (const d of [".add", magicDir]) {
|
|
898
988
|
const reviewsDir = resolve5(projectRoot, d, "reviews");
|
|
899
|
-
if (!
|
|
989
|
+
if (!existsSync7(reviewsDir)) {
|
|
900
990
|
if (dry) {
|
|
901
991
|
console.log(`[dry-run] \u5C06\u521B\u5EFA ${reviewsDir}/`);
|
|
902
992
|
} else {
|
|
@@ -908,7 +998,7 @@ async function renderAndWrite(ctx) {
|
|
|
908
998
|
const hashMap = {};
|
|
909
999
|
let npmVer = "";
|
|
910
1000
|
try {
|
|
911
|
-
npmVer = JSON.parse(
|
|
1001
|
+
npmVer = JSON.parse(readFileSync6(resolve5(projectRoot, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json"), "utf-8"))._version ?? "";
|
|
912
1002
|
} catch {
|
|
913
1003
|
}
|
|
914
1004
|
for (const [rp, c] of allFiles) {
|
|
@@ -940,7 +1030,7 @@ async function deployDatabase(ctx) {
|
|
|
940
1030
|
}
|
|
941
1031
|
if (db.engine === "postgresql" && db.container === "manual") {
|
|
942
1032
|
const dbScript = resolve5(projectRoot, magicDir, "scripts", "db-ensure.sh");
|
|
943
|
-
if (
|
|
1033
|
+
if (existsSync7(dbScript)) spawnSync2("bash", [dbScript, "postgresql", "manual"], { cwd: projectRoot, stdio: "inherit" });
|
|
944
1034
|
console.log([
|
|
945
1035
|
"",
|
|
946
1036
|
"\u2501".repeat(30),
|
|
@@ -981,12 +1071,12 @@ function deployDocs(ctx) {
|
|
|
981
1071
|
for (const d of ["00-\u9700\u6C42", "01-\u67B6\u6784", "02-\u89C4\u8303"]) {
|
|
982
1072
|
const srcDir = resolve5(groundingSrc, d);
|
|
983
1073
|
const destDir = resolve5(docsBase, d);
|
|
984
|
-
if (!
|
|
985
|
-
if (!
|
|
1074
|
+
if (!existsSync7(destDir)) mkdirSync3(destDir, { recursive: true });
|
|
1075
|
+
if (!existsSync7(srcDir)) continue;
|
|
986
1076
|
for (const f of readdirSync2(srcDir)) {
|
|
987
1077
|
const src = resolve5(srcDir, f);
|
|
988
1078
|
const dest = resolve5(destDir, f);
|
|
989
|
-
if (
|
|
1079
|
+
if (existsSync7(dest)) continue;
|
|
990
1080
|
try {
|
|
991
1081
|
copyFileSync2(src, dest);
|
|
992
1082
|
} catch {
|
|
@@ -1000,7 +1090,7 @@ function finalize(ctx, result) {
|
|
|
1000
1090
|
\u5B8C\u6210: \u65B0\u5EFA ${result.created}, \u8DF3\u8FC7 ${result.skipped}, \u8986\u76D6 ${result.overwritten}`);
|
|
1001
1091
|
if (options.dryRun) return;
|
|
1002
1092
|
if (db.engine === "sqlite") console.log("\u6570\u636E\u5907\u4EFD: npm run db:export \u2192 data/exports/");
|
|
1003
|
-
const pkg = JSON.parse(
|
|
1093
|
+
const pkg = JSON.parse(readFileSync6(resolve5(import.meta.dirname, "../package.json"), "utf-8"));
|
|
1004
1094
|
const peerNames = Object.keys(pkg.peerDependencies || {});
|
|
1005
1095
|
if (peerNames.length > 0) {
|
|
1006
1096
|
console.log(`
|
|
@@ -1014,7 +1104,7 @@ function finalize(ctx, result) {
|
|
|
1014
1104
|
}
|
|
1015
1105
|
|
|
1016
1106
|
// src/cli/commands/sync.ts
|
|
1017
|
-
import { existsSync as
|
|
1107
|
+
import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
1018
1108
|
import { resolve as resolve6 } from "path";
|
|
1019
1109
|
import { createHash as createHash2 } from "crypto";
|
|
1020
1110
|
|
|
@@ -1068,14 +1158,14 @@ function hash8(c) {
|
|
|
1068
1158
|
}
|
|
1069
1159
|
function loadHashFile(root, magic) {
|
|
1070
1160
|
try {
|
|
1071
|
-
return JSON.parse(
|
|
1161
|
+
return JSON.parse(readFileSync7(resolve6(root, magic, SYNC_CONFIG.HASH_OUTPUT_FILE), "utf-8"));
|
|
1072
1162
|
} catch {
|
|
1073
1163
|
return {};
|
|
1074
1164
|
}
|
|
1075
1165
|
}
|
|
1076
1166
|
function loadVersionFile(root, magic) {
|
|
1077
1167
|
try {
|
|
1078
|
-
return
|
|
1168
|
+
return readFileSync7(resolve6(root, magic, SYNC_CONFIG.VERSION_SENTINEL), "utf-8").trim();
|
|
1079
1169
|
} catch {
|
|
1080
1170
|
return "";
|
|
1081
1171
|
}
|
|
@@ -1132,7 +1222,7 @@ async function syncCommand(options = {}) {
|
|
|
1132
1222
|
const srcHashPath = resolve6(projectRoot, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json");
|
|
1133
1223
|
let npmVersion = "";
|
|
1134
1224
|
try {
|
|
1135
|
-
npmVersion = JSON.parse(
|
|
1225
|
+
npmVersion = JSON.parse(readFileSync7(srcHashPath, "utf-8"))._version ?? "";
|
|
1136
1226
|
} catch {
|
|
1137
1227
|
}
|
|
1138
1228
|
const installedVersion = loadVersionFile(projectRoot, magicDir);
|
|
@@ -1152,12 +1242,12 @@ async function syncCommand(options = {}) {
|
|
|
1152
1242
|
let sameCount = 0;
|
|
1153
1243
|
for (const [relPath, content] of candidates) {
|
|
1154
1244
|
const absPath = resolve6(projectRoot, relPath);
|
|
1155
|
-
if (!
|
|
1245
|
+
if (!existsSync8(absPath)) {
|
|
1156
1246
|
missingFiles.set(relPath, content);
|
|
1157
1247
|
} else if (establishBaseline) {
|
|
1158
1248
|
missingFiles.set(relPath, content);
|
|
1159
1249
|
} else {
|
|
1160
|
-
const curH = hash8(
|
|
1250
|
+
const curH = hash8(readFileSync7(absPath, "utf-8"));
|
|
1161
1251
|
const storedH = outHash[relPath];
|
|
1162
1252
|
if (storedH && curH === storedH) {
|
|
1163
1253
|
sameCount++;
|
|
@@ -1185,11 +1275,12 @@ async function syncCommand(options = {}) {
|
|
|
1185
1275
|
}
|
|
1186
1276
|
saveHashFile(projectRoot, magicDir, new Map([...missingFiles, ...conflictFiles]));
|
|
1187
1277
|
saveVersionFile(projectRoot, magicDir, npmVersion);
|
|
1278
|
+
await checkPrismaDiff(projectRoot, options);
|
|
1188
1279
|
return;
|
|
1189
1280
|
}
|
|
1190
1281
|
const missing = /* @__PURE__ */ new Map();
|
|
1191
1282
|
for (const [relPath, content] of allFiles) {
|
|
1192
|
-
if (!
|
|
1283
|
+
if (!existsSync8(resolve6(projectRoot, relPath))) {
|
|
1193
1284
|
missing.set(relPath, content);
|
|
1194
1285
|
}
|
|
1195
1286
|
}
|
|
@@ -1207,10 +1298,235 @@ async function syncCommand(options = {}) {
|
|
|
1207
1298
|
}
|
|
1208
1299
|
const result = await writeFiles2(projectRoot, filesToWrite, { yes: true });
|
|
1209
1300
|
console.log(`\u540C\u6B65\u5B8C\u6210: \u65B0\u5EFA ${result.created}, \u8DF3\u8FC7 ${result.skipped}`);
|
|
1301
|
+
await checkPrismaDiff(projectRoot, options);
|
|
1302
|
+
}
|
|
1303
|
+
async function checkPrismaDiff(projectRoot, options) {
|
|
1304
|
+
if (!options.patch) return;
|
|
1305
|
+
const basePath = resolve6(projectRoot, SYNC_PRISMA_CONFIG.BASE_SCHEMA);
|
|
1306
|
+
const targetPath = resolve6(projectRoot, SYNC_PRISMA_CONFIG.TARGET_PATTERN);
|
|
1307
|
+
if (!existsSync8(basePath)) {
|
|
1308
|
+
console.log(`
|
|
1309
|
+
\u26A0\uFE0F \u57FA\u51C6 schema \u4E0D\u5B58\u5728: ${basePath}`);
|
|
1310
|
+
console.log(` \u8BF7\u786E\u4FDD add-coder \u5DF2\u6B63\u786E\u5B89\u88C5\u3002`);
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1313
|
+
const result = diffPrisma(basePath, targetPath);
|
|
1314
|
+
if (!result.hasDiff) {
|
|
1315
|
+
console.log(`
|
|
1316
|
+
\u2705 Prisma schema \u4E0E add-coder \u6807\u51C6\u4E00\u81F4\uFF0C\u65E0\u9700\u540C\u6B65\u3002`);
|
|
1317
|
+
return;
|
|
1318
|
+
}
|
|
1319
|
+
const targetExists = existsSync8(targetPath);
|
|
1320
|
+
console.log(`
|
|
1321
|
+
\u26A0\uFE0F Prisma schema \u5DEE\u5F02\u68C0\u6D4B:`);
|
|
1322
|
+
console.log(` \u57FA\u51C6: ${result.baseSchema} (add-coder \u6807\u51C6)`);
|
|
1323
|
+
if (targetExists) console.log(` \u76EE\u6807: ${result.targetPath} (\u6D88\u8D39\u65B9)`);
|
|
1324
|
+
if (result.missing.length > 0 && targetExists) {
|
|
1325
|
+
let selected = [];
|
|
1326
|
+
await handleDiffAction(SYNC_PRISMA_CONFIG.ON_MISSING_MODEL, {
|
|
1327
|
+
label: "\u7F3A\u5931\u6A21\u578B/\u679A\u4E3E",
|
|
1328
|
+
show() {
|
|
1329
|
+
console.log(`
|
|
1330
|
+
\u{1F534} \u7F3A\u5931\u6A21\u578B/\u679A\u4E3E\uFF08${result.missing.length} \u9879\uFF09:`);
|
|
1331
|
+
for (let i = 0; i < result.missing.length; i++) {
|
|
1332
|
+
const m = result.missing[i];
|
|
1333
|
+
console.log(` [${i + 1}] ${m.type} ${m.name} (${m.fields.length} \u5B57\u6BB5)`);
|
|
1334
|
+
}
|
|
1335
|
+
},
|
|
1336
|
+
async confirm() {
|
|
1337
|
+
const ans = await ask(`
|
|
1338
|
+
\u8F93\u5165\u7F16\u53F7\u9009\u62E9\u6027\u6CE8\u5165\uFF08\u5982 1,3\uFF09\uFF0Ca \u5168\u90E8\u6CE8\u5165\uFF0C\u56DE\u8F66\u8DF3\u8FC7: `);
|
|
1339
|
+
if (ans === "a" || ans === "all") {
|
|
1340
|
+
selected = [...result.missing];
|
|
1341
|
+
return true;
|
|
1342
|
+
}
|
|
1343
|
+
if (!ans.trim()) return false;
|
|
1344
|
+
const indices = ans.split(/[,,]+/).map((s) => parseInt(s.trim()) - 1).filter((i) => !isNaN(i) && i >= 0 && i < result.missing.length);
|
|
1345
|
+
if (indices.length === 0) return false;
|
|
1346
|
+
selected = indices.map((i) => result.missing[i]);
|
|
1347
|
+
return true;
|
|
1348
|
+
},
|
|
1349
|
+
async execute() {
|
|
1350
|
+
const n = injectMissingModels(result.targetPath, selected);
|
|
1351
|
+
console.log(` \u2705 \u5DF2\u5C06 ${n} \u4E2A\u6A21\u578B/\u679A\u4E3E\u6CE8\u5165 ${result.targetPath}`);
|
|
1352
|
+
return n;
|
|
1353
|
+
}
|
|
1354
|
+
});
|
|
1355
|
+
}
|
|
1356
|
+
if (result.fieldDiffs.length > 0 && targetExists) {
|
|
1357
|
+
console.log(`
|
|
1358
|
+
\u{1F7E1} \u5B57\u6BB5\u7EA7\u5DEE\u5F02\uFF08${result.fieldDiffs.length} \u4E2A\u6A21\u578B/\u679A\u4E3E\uFF09:`);
|
|
1359
|
+
for (const d of result.fieldDiffs) {
|
|
1360
|
+
console.log(`
|
|
1361
|
+
\u2500\u2500 ${d.type} ${d.name}:`);
|
|
1362
|
+
if (d.conflicts.length > 0) {
|
|
1363
|
+
await handleDiffAction(SYNC_PRISMA_CONFIG.ON_FIELD_CONFLICT, {
|
|
1364
|
+
label: `\u51B2\u7A81\u5B57\u6BB5`,
|
|
1365
|
+
show() {
|
|
1366
|
+
console.log(` \u26A1 \u51B2\u7A81\u5B57\u6BB5\uFF08\u540C\u540D\u5B57\u6BB5\u3001\u5B9A\u4E49\u4E0D\u540C\uFF09:`);
|
|
1367
|
+
for (const c of d.conflicts) {
|
|
1368
|
+
console.log(` \xB7 ${c.fieldName}`);
|
|
1369
|
+
console.log(` \u57FA\u51C6: ${c.baseDef}`);
|
|
1370
|
+
console.log(` \u6D88\u8D39\u65B9: ${c.targetDef}`);
|
|
1371
|
+
}
|
|
1372
|
+
},
|
|
1373
|
+
async confirm() {
|
|
1374
|
+
const ans = await ask(` \u662F\u5426\u7528\u57FA\u51C6\u5B9A\u4E49\u8986\u76D6\u8FD9\u4E9B\u51B2\u7A81\u5B57\u6BB5\uFF1F(y/n): `);
|
|
1375
|
+
return ans === "y" || ans === "yes";
|
|
1376
|
+
},
|
|
1377
|
+
async execute() {
|
|
1378
|
+
const n = overwriteFieldLines(targetPath, basePath, d.name, d.conflicts);
|
|
1379
|
+
console.log(` \u2705 \u5DF2\u8986\u76D6 ${n} \u4E2A\u51B2\u7A81\u5B57\u6BB5`);
|
|
1380
|
+
return n;
|
|
1381
|
+
}
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
if (d.missingFields.length > 0) {
|
|
1385
|
+
await handleDiffAction(SYNC_PRISMA_CONFIG.ON_MISSING_FIELD, {
|
|
1386
|
+
label: `\u7F3A\u5931\u5B57\u6BB5`,
|
|
1387
|
+
show() {
|
|
1388
|
+
console.log(` \u{1F4CB} \u6D88\u8D39\u65B9\u7F3A\u5C11 ${d.missingFields.length} \u4E2A\u5B57\u6BB5:`);
|
|
1389
|
+
for (const f of d.missingFields) {
|
|
1390
|
+
console.log(` + ${f}`);
|
|
1391
|
+
}
|
|
1392
|
+
},
|
|
1393
|
+
async confirm() {
|
|
1394
|
+
const ans = await ask(` \u662F\u5426\u8865\u5145\u8FD9\u4E9B\u7F3A\u5931\u5B57\u6BB5\uFF1F(y/n): `);
|
|
1395
|
+
return ans === "y" || ans === "yes";
|
|
1396
|
+
},
|
|
1397
|
+
async execute() {
|
|
1398
|
+
const n = injectFieldLines(targetPath, basePath, d.name, d.missingFields);
|
|
1399
|
+
console.log(` \u2705 \u5DF2\u8865\u5145 ${n} \u4E2A\u5B57\u6BB5`);
|
|
1400
|
+
return n;
|
|
1401
|
+
}
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
if (d.extraFields.length > 0) {
|
|
1405
|
+
await handleDiffAction(SYNC_PRISMA_CONFIG.ON_EXTRA_FIELD, {
|
|
1406
|
+
label: `\u6D88\u8D39\u65B9\u7279\u6709\u5B57\u6BB5`,
|
|
1407
|
+
show() {
|
|
1408
|
+
console.log(` \u2139\uFE0F \u6D88\u8D39\u65B9\u7279\u6709 ${d.extraFields.length} \u4E2A\u5B57\u6BB5\uFF08\u4E0D\u505A\u64CD\u4F5C\uFF09:`);
|
|
1409
|
+
for (const f of d.extraFields) {
|
|
1410
|
+
console.log(` - ${f}`);
|
|
1411
|
+
}
|
|
1412
|
+
},
|
|
1413
|
+
async confirm() {
|
|
1414
|
+
return false;
|
|
1415
|
+
},
|
|
1416
|
+
async execute() {
|
|
1417
|
+
return 0;
|
|
1418
|
+
}
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
if (targetExists) {
|
|
1424
|
+
console.log(`
|
|
1425
|
+
${SYNC_PRISMA_CONFIG.PROMPT}`);
|
|
1426
|
+
console.log(` \u25B6 \u8BF7\u8FD0\u884C: npx prisma migrate dev && npx prisma generate`);
|
|
1427
|
+
} else {
|
|
1428
|
+
console.log(`
|
|
1429
|
+
\u76EE\u6807 schema \u6587\u4EF6\u4E0D\u5B58\u5728: ${result.targetPath}`);
|
|
1430
|
+
const { writeFileSync: writeFileSync5, mkdirSync: mkdirSync4 } = __require("fs");
|
|
1431
|
+
const { dirname: dirname4 } = __require("path");
|
|
1432
|
+
mkdirSync4(dirname4(targetPath), { recursive: true });
|
|
1433
|
+
writeFileSync5(targetPath, "// add.prisma \u2014 ADD \u6CBB\u7406\u6A21\u578B\n\n", "utf-8");
|
|
1434
|
+
const n = injectMissingModels(targetPath, result.missing);
|
|
1435
|
+
console.log(` \u2705 \u5DF2\u521B\u5EFA\u5E76\u6CE8\u5165 ${n} \u4E2A\u6A21\u578B/\u679A\u4E3E`);
|
|
1436
|
+
console.log(` \u25B6 \u8BF7\u8FD0\u884C: npx prisma migrate dev && npx prisma generate`);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
async function handleDiffAction(action, ctx) {
|
|
1440
|
+
if (action === "skip" || action === "ignore") return;
|
|
1441
|
+
ctx.show();
|
|
1442
|
+
if (action === "auto") {
|
|
1443
|
+
const n = await ctx.execute();
|
|
1444
|
+
console.log(` \u2705 ${ctx.label}: \u5DF2\u5904\u7406 ${n} \u9879`);
|
|
1445
|
+
return;
|
|
1446
|
+
}
|
|
1447
|
+
if (action === "interactive") {
|
|
1448
|
+
const ok = await ctx.confirm();
|
|
1449
|
+
if (!ok) {
|
|
1450
|
+
console.log(` \u5DF2\u8DF3\u8FC7\u3002`);
|
|
1451
|
+
return;
|
|
1452
|
+
}
|
|
1453
|
+
const n = await ctx.execute();
|
|
1454
|
+
console.log(` \u2705 ${ctx.label}: \u5DF2\u5904\u7406 ${n} \u9879`);
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
if (action === "block") {
|
|
1458
|
+
throw new Error(`\u26D4 Prisma \u540C\u6B65\u963B\u65AD: ${ctx.label}\uFF0C\u7B56\u7565\u4E3A block`);
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
function injectMissingModels(targetPath, models) {
|
|
1462
|
+
let content = readFileSync7(targetPath, "utf-8");
|
|
1463
|
+
content = content.replace(/\n*$/, "\n");
|
|
1464
|
+
content += `
|
|
1465
|
+
// ===== \u7531 add-coder sync --patch \u81EA\u52A8\u6CE8\u5165 (${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}) =====
|
|
1466
|
+
|
|
1467
|
+
`;
|
|
1468
|
+
for (const m of models) {
|
|
1469
|
+
content += m.body + "\n\n";
|
|
1470
|
+
}
|
|
1471
|
+
writeFileSync4(targetPath, content, "utf-8");
|
|
1472
|
+
return models.length;
|
|
1473
|
+
}
|
|
1474
|
+
function getBaseFieldLines(basePath, modelName) {
|
|
1475
|
+
const content = readFileSync7(basePath, "utf-8");
|
|
1476
|
+
const blockRegex = /^(model|enum)\s+(\w+)\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/gm;
|
|
1477
|
+
let match;
|
|
1478
|
+
while ((match = blockRegex.exec(content)) !== null) {
|
|
1479
|
+
if (match[2] !== modelName) continue;
|
|
1480
|
+
const fields = {};
|
|
1481
|
+
for (const line of match[3].split("\n")) {
|
|
1482
|
+
const fm = line.match(/^\s*(\w+)\s+/);
|
|
1483
|
+
if (fm) fields[fm[1]] = line.trim();
|
|
1484
|
+
}
|
|
1485
|
+
return fields;
|
|
1486
|
+
}
|
|
1487
|
+
return {};
|
|
1488
|
+
}
|
|
1489
|
+
function injectFieldLines(targetPath, basePath, modelName, fieldKeys) {
|
|
1490
|
+
const baseFields = getBaseFieldLines(basePath, modelName);
|
|
1491
|
+
if (Object.keys(baseFields).length === 0) return 0;
|
|
1492
|
+
let content = readFileSync7(targetPath, "utf-8");
|
|
1493
|
+
let count = 0;
|
|
1494
|
+
for (const key of fieldKeys) {
|
|
1495
|
+
const fieldName = key.split(":")[0];
|
|
1496
|
+
const fieldLine = baseFields[fieldName];
|
|
1497
|
+
if (!fieldLine) continue;
|
|
1498
|
+
if (new RegExp(`^\\s*${fieldName}\\s+`, "m").test(content)) continue;
|
|
1499
|
+
const modelRegex = new RegExp(`(model\\s+${modelName}\\s*\\{[^}]*?)\\n(\\s*\\})`, "m");
|
|
1500
|
+
const m = content.match(modelRegex);
|
|
1501
|
+
if (!m) continue;
|
|
1502
|
+
content = content.replace(modelRegex, `$1
|
|
1503
|
+
${fieldLine}
|
|
1504
|
+
$2`);
|
|
1505
|
+
count++;
|
|
1506
|
+
}
|
|
1507
|
+
if (count > 0) writeFileSync4(targetPath, content, "utf-8");
|
|
1508
|
+
return count;
|
|
1509
|
+
}
|
|
1510
|
+
function overwriteFieldLines(targetPath, basePath, modelName, conflicts) {
|
|
1511
|
+
const baseFields = getBaseFieldLines(basePath, modelName);
|
|
1512
|
+
if (Object.keys(baseFields).length === 0) return 0;
|
|
1513
|
+
let content = readFileSync7(targetPath, "utf-8");
|
|
1514
|
+
let count = 0;
|
|
1515
|
+
for (const { fieldName } of conflicts) {
|
|
1516
|
+
const baseLine = baseFields[fieldName];
|
|
1517
|
+
if (!baseLine) continue;
|
|
1518
|
+
const fieldRegex = new RegExp(`^(\\s*)(${fieldName}\\s+.*)$`, "m");
|
|
1519
|
+
const match = content.match(fieldRegex);
|
|
1520
|
+
if (!match) continue;
|
|
1521
|
+
content = content.replace(fieldRegex, `${match[1]}${baseLine}`);
|
|
1522
|
+
count++;
|
|
1523
|
+
}
|
|
1524
|
+
if (count > 0) writeFileSync4(targetPath, content, "utf-8");
|
|
1525
|
+
return count;
|
|
1210
1526
|
}
|
|
1211
1527
|
|
|
1212
1528
|
// src/cli/commands/status.ts
|
|
1213
|
-
import { existsSync as
|
|
1529
|
+
import { existsSync as existsSync9 } from "fs";
|
|
1214
1530
|
import { resolve as resolve7 } from "path";
|
|
1215
1531
|
async function statusCommand() {
|
|
1216
1532
|
const projectRoot = process.cwd();
|
|
@@ -1220,7 +1536,7 @@ async function statusCommand() {
|
|
|
1220
1536
|
const missing = [];
|
|
1221
1537
|
const present = [];
|
|
1222
1538
|
for (const [relPath] of coreFiles) {
|
|
1223
|
-
if (
|
|
1539
|
+
if (existsSync9(resolve7(projectRoot, relPath))) {
|
|
1224
1540
|
present.push(relPath);
|
|
1225
1541
|
} else {
|
|
1226
1542
|
missing.push(relPath);
|
|
@@ -1238,7 +1554,7 @@ async function statusCommand() {
|
|
|
1238
1554
|
|
|
1239
1555
|
// src/cli/index.ts
|
|
1240
1556
|
var { version } = JSON.parse(
|
|
1241
|
-
|
|
1557
|
+
readFileSync8(new URL("../package.json", import.meta.url), "utf-8")
|
|
1242
1558
|
);
|
|
1243
1559
|
var program = new Command();
|
|
1244
1560
|
program.name("add-coder").description("\u521D\u59CB\u5316 ADD \u8303\u5F0F\u5DE5\u4F5C\u6D41\u6A21\u677F").version(version);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "add-coder",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6-feature-hitl-enhance-v1.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": {
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"@modelcontextprotocol/server": "2.0.0-beta.5",
|
|
44
44
|
"commander": "^13.1.0",
|
|
45
45
|
"smol-toml": "^1.7.0",
|
|
46
|
+
"vector-cosine-similarity": "^1.8.0",
|
|
46
47
|
"zod": "^4.4.3"
|
|
47
48
|
},
|
|
48
49
|
"peerDependencies": {
|