add-coder 0.3.18 → 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/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli/index.ts
4
- import { readFileSync as readFileSync9 } from "fs";
4
+ import { readFileSync as readFileSync10 } from "fs";
5
5
  import { Command } from "commander";
6
6
 
7
7
  // src/caijuehub/strategies/detect.strategy.ts
@@ -184,7 +184,7 @@ async function selectFiles(projectRoot, files) {
184
184
  process.stdout.write(renderList(items, selected, scrollIdx, VISIBLE));
185
185
  }
186
186
  draw();
187
- return new Promise((resolve9) => {
187
+ return new Promise((resolve10) => {
188
188
  input.on("keypress", (_char, key) => {
189
189
  if (!key) return;
190
190
  if (key.name === "up") {
@@ -225,7 +225,7 @@ async function selectFiles(projectRoot, files) {
225
225
  if (selected.has(idx)) result.set(relPath, content);
226
226
  idx++;
227
227
  }
228
- resolve9(result);
228
+ resolve10(result);
229
229
  });
230
230
  });
231
231
  }
@@ -331,13 +331,34 @@ async function writeFiles2(projectRoot, files, options = {}) {
331
331
  }
332
332
  function parseSchemaBlocks(content) {
333
333
  const blocks = /* @__PURE__ */ new Map();
334
- const blockRegex = /^(model|enum)\s+(\w+)\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/gm;
335
- let match;
336
- while ((match = blockRegex.exec(content)) !== null) {
337
- const [, type, name, inner] = match;
334
+ const lines = content.split("\n");
335
+ let i = 0;
336
+ while (i < lines.length) {
337
+ const head = lines[i].match(/^(model|enum)\s+(\w+)\s*\{/);
338
+ if (!head) {
339
+ i++;
340
+ continue;
341
+ }
342
+ const type = head[1];
343
+ const name = head[2];
344
+ const bodyLines = [lines[i]];
345
+ let depth = 1;
346
+ let j = i + 1;
347
+ while (j < lines.length && depth > 0) {
348
+ const line = lines[j];
349
+ bodyLines.push(line);
350
+ const commentless = line.split("//")[0];
351
+ for (const ch of commentless) {
352
+ if (ch === "{") depth++;
353
+ else if (ch === "}") depth--;
354
+ }
355
+ j++;
356
+ }
357
+ const body = bodyLines.join("\n");
358
+ const inner = bodyLines.slice(1, -1).join("\n");
338
359
  let fields = [];
339
360
  if (type === "enum") {
340
- fields = inner.split("\n").map((l) => l.replace(/#.*$/, "").trim()).filter((l) => l.length > 0);
361
+ fields = inner.split("\n").map((l) => l.replace(/\/\/.*$/, "").replace(/#.*$/, "").trim()).filter((l) => l.length > 0);
341
362
  } else {
342
363
  const fieldRegex = /^\s*(\w+)\s+(\w+(?:\([^)]*\))?(?:\[\])?\??)/gm;
343
364
  let fm;
@@ -345,7 +366,8 @@ function parseSchemaBlocks(content) {
345
366
  fields.push(`${fm[1]}:${fm[2]}`);
346
367
  }
347
368
  }
348
- blocks.set(`${type}:${name}`, { type, name, body: match[0], fields });
369
+ blocks.set(`${type}:${name}`, { type, name, body, fields });
370
+ i = j;
349
371
  }
350
372
  return blocks;
351
373
  }
@@ -627,9 +649,38 @@ import { resolve as resolve4, dirname as dirname3 } from "path";
627
649
  import { fileURLToPath as fileURLToPath2 } from "url";
628
650
 
629
651
  // src/caijuehub/strategies/prisma.strategy.ts
630
- import { spawnSync } from "child_process";
631
652
  import { copyFileSync, existsSync as existsSync7, readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
632
653
  import { resolve as resolve3 } from "path";
654
+
655
+ // src/lib/run-command.ts
656
+ import { spawnSync } from "child_process";
657
+ var CMD_EXTENSIONS = ["npm", "npx", "pnpm", "git"];
658
+ function runCommand(cmd, args, opts = {}) {
659
+ const platform = opts.platform ?? process.platform;
660
+ const needsCmdExt = platform === "win32" && CMD_EXTENSIONS.includes(cmd) && !opts.shell;
661
+ const effectiveCmd = needsCmdExt ? `${cmd}.cmd` : cmd;
662
+ const r = spawnSync(effectiveCmd, args, {
663
+ cwd: opts.cwd,
664
+ env: opts.env,
665
+ input: opts.input,
666
+ timeout: opts.timeout,
667
+ encoding: "utf-8",
668
+ shell: opts.shell,
669
+ stdio: opts.stdio ?? (opts.input !== void 0 ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"])
670
+ });
671
+ if (r.error) {
672
+ throw new Error(`\u547D\u4EE4\u4E0D\u53EF\u7528: ${cmd}\uFF08\u5E73\u53F0: ${platform}\uFF0C${r.error.message}\uFF09`);
673
+ }
674
+ return { status: r.status, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
675
+ }
676
+ function commandExists(cmd, platform) {
677
+ const p = platform ?? process.platform;
678
+ const probe = p === "win32" ? "where" : "which";
679
+ const r = spawnSync(probe, [cmd], { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
680
+ return !r.error && r.status === 0;
681
+ }
682
+
683
+ // src/caijuehub/strategies/prisma.strategy.ts
633
684
  var PRISMA_CONFIG = {
634
685
  onMissing: "ask",
635
686
  onExistingAddPrisma: "ask",
@@ -657,12 +708,10 @@ function ensurePrismaConfig(projectRoot) {
657
708
  ].join("\n") + "\n", "utf-8");
658
709
  }
659
710
  function backupAddTables(projectRoot) {
660
- const pgDump = spawnSync("which", ["pg_dump"], { timeout: 2e3 });
661
- if (pgDump.status !== 0) return null;
711
+ if (!commandExists("pg_dump")) return null;
662
712
  const bak = resolve3(projectRoot, `add-backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19)}.sql`);
663
- const r = spawnSync("pg_dump", ["--table=AddUser", "--table=DevOperation", "--table=AuditLog", "--if-exists"], {
713
+ const r = runCommand("pg_dump", ["--table=AddUser", "--table=DevOperation", "--table=AuditLog", "--if-exists"], {
664
714
  cwd: projectRoot,
665
- stdio: ["ignore", "pipe", "pipe"],
666
715
  timeout: 3e4
667
716
  });
668
717
  if (r.stdout.length > 0) {
@@ -673,16 +722,18 @@ function backupAddTables(projectRoot) {
673
722
  return null;
674
723
  }
675
724
  function runPrismaInit(projectRoot, provider, schemaPath) {
676
- console.log("\u6267\u884C npx prisma init ...");
725
+ console.log("\u6267\u884C prisma init ...");
677
726
  const pm = detectPm(projectRoot);
678
- const initArgs = pm === "pnpm" ? ["dlx", "prisma", "init", "--datasource-provider", provider] : ["prisma", "init", "--datasource-provider", provider];
679
- const initResult = spawnSync(pm, initArgs, {
680
- cwd: projectRoot,
681
- stdio: "inherit",
682
- shell: false
683
- });
727
+ const initArgs = pm === "pnpm" ? ["dlx", "prisma", "init", "--datasource-provider", provider] : ["exec", "prisma", "--", "init", "--datasource-provider", provider];
728
+ let initResult;
729
+ try {
730
+ initResult = runCommand(pm, initArgs, { cwd: projectRoot });
731
+ } catch (e) {
732
+ console.error(`\u2717 prisma init \u65E0\u6CD5\u6267\u884C: ${e instanceof Error ? e.message : String(e)}`);
733
+ initResult = { status: null, stdout: "", stderr: "" };
734
+ }
684
735
  if (initResult.status !== 0 || !existsSync7(schemaPath)) {
685
- console.log("prisma init \u5931\u8D25\uFF0C\u624B\u52A8\u521B\u5EFA schema.prisma ...");
736
+ console.error(`\u26A0\uFE0F prisma init \u672A\u5B8C\u6210\uFF08\u9000\u51FA\u7801: ${initResult.status}\uFF09\uFF0C\u56DE\u9000\u624B\u52A8\u521B\u5EFA schema.prisma\u2014\u2014db push \u5C06\u9A8C\u8BC1\u5176\u53EF\u7528\u6027`);
686
737
  const prismaDir = resolve3(projectRoot, "prisma");
687
738
  if (!existsSync7(prismaDir)) mkdirSync3(prismaDir, { recursive: true });
688
739
  const content = `generator client {
@@ -722,6 +773,28 @@ function postInitSetup(projectRoot, schemaPath, addPrismaTemplate, destPath) {
722
773
  }
723
774
  copyFileSync(addPrismaTemplate, destPath);
724
775
  console.log("\u5DF2\u590D\u5236 add.prisma");
776
+ patchGeneratorOutput(schemaPath);
777
+ }
778
+ function patchGeneratorOutput(schemaPath) {
779
+ if (!existsSync7(schemaPath)) return;
780
+ let content = readFileSync5(schemaPath, "utf-8");
781
+ const genBlock = content.match(/generator\s+\w+\s*\{[\s\S]*?\}/);
782
+ if (!genBlock) {
783
+ content += `
784
+ generator client {
785
+ provider = "prisma-client-js"
786
+ output = "../src/generated/prisma"
787
+ }
788
+ `;
789
+ writeFileSync3(schemaPath, content, "utf-8");
790
+ console.log("\u5DF2\u8FFD\u52A0 generator client\uFF08\u542B output \u2192 src/generated/prisma\uFF09");
791
+ return;
792
+ }
793
+ if (genBlock[0].includes("output")) return;
794
+ const patched = genBlock[0].replace(/\}\s*$/, ` output = "../src/generated/prisma"
795
+ }`);
796
+ writeFileSync3(schemaPath, content.replace(genBlock[0], patched), "utf-8");
797
+ console.log("\u5DF2\u6CE8\u5165 generator output \u2192 src/generated/prisma");
725
798
  }
726
799
  async function injectPrisma(projectRoot, addPrismaTemplate, options = {}) {
727
800
  const C = PRISMA_CONFIG;
@@ -786,10 +859,10 @@ async function injectPrisma(projectRoot, addPrismaTemplate, options = {}) {
786
859
  ensurePrismaConfig(projectRoot);
787
860
  backupAddTables(projectRoot);
788
861
  const pm = detectPm(projectRoot);
789
- const args = pm === "pnpm" ? ["dlx", "prisma", "db", "push"] : ["prisma", "db", "push"];
862
+ const args = pm === "pnpm" ? ["dlx", "prisma", "db", "push"] : ["exec", "prisma", "--", "db", "push"];
790
863
  if (C.schemaArg) args.push(C.schemaArg);
791
864
  console.log(`\u6267\u884C ${pm} ${args.join(" ")} ...`);
792
- const r = spawnSync(pm, args, { cwd: projectRoot, stdio: "inherit", shell: false });
865
+ const r = runCommand(pm, args, { cwd: projectRoot });
793
866
  if (r.status !== 0) throw new Error(`prisma db push \u9000\u51FA\u7801: ${r.status}`);
794
867
  } catch (err) {
795
868
  if (C.onMigrateFail === "keep") {
@@ -805,8 +878,14 @@ async function injectPrisma(projectRoot, addPrismaTemplate, options = {}) {
805
878
  }
806
879
  if (C.autoGenerate) {
807
880
  const pm = detectPm(projectRoot);
881
+ const genArgs = pm === "pnpm" ? ["dlx", "prisma", "generate"] : ["exec", "prisma", "--", "generate"];
808
882
  console.log("\u6267\u884C prisma generate ...");
809
- spawnSync(pm, pm === "pnpm" ? ["dlx", "prisma", "generate"] : ["prisma", "generate"], { cwd: projectRoot, stdio: "inherit", shell: false });
883
+ const g = runCommand(pm, genArgs, { cwd: projectRoot });
884
+ if (g.status !== 0) {
885
+ const detail = g.stderr.trim().split("\n").slice(0, 5).join("\n");
886
+ throw new Error(`prisma generate \u9000\u51FA\u7801: ${g.status}${detail ? `
887
+ ${detail}` : ""}`);
888
+ }
810
889
  }
811
890
  console.log("ADD \u6CBB\u7406\u6A21\u578B\u5DF2\u5C31\u7EEA");
812
891
  return true;
@@ -821,11 +900,88 @@ async function injectPrisma2(projectRoot, options = {}) {
821
900
  }
822
901
 
823
902
  // src/cli/commands/init.ts
824
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync4, copyFileSync as copyFileSync2, readdirSync as readdirSync2 } from "fs";
903
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync9, mkdirSync as mkdirSync4, copyFileSync as copyFileSync2, readdirSync as readdirSync2 } from "fs";
825
904
  import { createHash } from "crypto";
826
- import { resolve as resolve5 } from "path";
827
- import { spawnSync as spawnSync2 } from "child_process";
905
+ import { resolve as resolve6 } from "path";
828
906
  import { createConnection } from "net";
907
+
908
+ // src/lib/model-predownload.ts
909
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
910
+ import { join as join5, resolve as resolve5 } from "path";
911
+ import { homedir } from "os";
912
+ import { parse as parse2 } from "smol-toml";
913
+ var DEFAULT_DOWNLOAD_TIMEOUT_MS = 5 * 60 * 1e3;
914
+ var TOML_CANDIDATES = [
915
+ resolve5(import.meta.dirname, "caijuehub/dps-scoring-rules.toml"),
916
+ resolve5(import.meta.dirname, "../caijuehub/dps-scoring-rules.toml")
917
+ ];
918
+ function resolveEmbeddingModel() {
919
+ const tomlPath = TOML_CANDIDATES.find((p) => existsSync8(p));
920
+ if (!tomlPath) {
921
+ throw new Error(
922
+ `dps-scoring-rules.toml \u672A\u627E\u5230\uFF08\u671F\u671B\u8DEF\u5F84: ${TOML_CANDIDATES.join(" \u6216 ")}\uFF09`
923
+ );
924
+ }
925
+ const cfg = parse2(readFileSync6(tomlPath, "utf-8"));
926
+ const model = cfg.embedding?.model;
927
+ if (typeof model !== "string" || model.length === 0) {
928
+ throw new Error(`dps-scoring-rules.toml [embedding] model \u672A\u914D\u7F6E\uFF08${tomlPath}\uFF09`);
929
+ }
930
+ return model;
931
+ }
932
+ function resolveCacheDir() {
933
+ const hubCache = process.env.HF_HUB_CACHE;
934
+ if (hubCache) return hubCache;
935
+ const home = process.env.HF_HOME || join5(homedir(), ".cache", "huggingface");
936
+ return join5(home, "hub");
937
+ }
938
+ function modelCacheName(model) {
939
+ const parts = model.split("/");
940
+ const org = parts.length > 1 ? parts[0] : "models";
941
+ const name = parts[parts.length - 1];
942
+ return `models--${org}--${name}`;
943
+ }
944
+ function isModelCached(model) {
945
+ const cacheDir = resolveCacheDir();
946
+ return existsSync8(join5(cacheDir, modelCacheName(model), "snapshots"));
947
+ }
948
+ async function ensureEmbeddingModel(options) {
949
+ const force = options?.force ?? false;
950
+ const skip = options?.skip ?? false;
951
+ const timeoutMs = options?.timeoutMs ?? DEFAULT_DOWNLOAD_TIMEOUT_MS;
952
+ if (skip) {
953
+ return { status: "skipped", model: "", cacheDir: "" };
954
+ }
955
+ const model = resolveEmbeddingModel();
956
+ const { pipeline, env } = await import("@huggingface/transformers");
957
+ env.cacheDir = resolveCacheDir();
958
+ const cacheDir = env.cacheDir;
959
+ const snapshotsDir = join5(cacheDir, modelCacheName(model), "snapshots");
960
+ if (!force && existsSync8(snapshotsDir)) {
961
+ return { status: "already-cached", model, cacheDir };
962
+ }
963
+ env.remoteHost = "https://hf-mirror.com";
964
+ env.remotePathTemplate = "{model}/resolve/{revision}/";
965
+ const run = (async () => {
966
+ const extractor = await pipeline("feature-extraction", model);
967
+ await extractor(["\u6D4B\u8BD5"], { pooling: "mean", normalize: true });
968
+ })();
969
+ let timer;
970
+ const timeout = new Promise((_, reject) => {
971
+ timer = setTimeout(
972
+ () => reject(new Error(`\u6A21\u578B\u4E0B\u8F7D\u8D85\u65F6\uFF08${timeoutMs}ms\uFF09`)),
973
+ timeoutMs
974
+ );
975
+ });
976
+ try {
977
+ await Promise.race([run, timeout]);
978
+ } finally {
979
+ if (timer) clearTimeout(timer);
980
+ }
981
+ return { status: "downloaded", model, cacheDir };
982
+ }
983
+
984
+ // src/cli/commands/init.ts
829
985
  var ADAPTER_RENDERERS = {
830
986
  claude: renderAdapter,
831
987
  qoder: renderAdapter2,
@@ -844,9 +1000,17 @@ async function initCommand(options) {
844
1000
  console.log(`[dry-run] \u5C06\u5199\u5165 ${ctx.magicDir}/stack.json \u2192 ${ctx.stack}`);
845
1001
  }
846
1002
  const result = await renderAndWrite(ctx);
847
- await deployDatabase(ctx);
1003
+ const dbFail = await deployDatabase(ctx);
848
1004
  deployDocs(ctx);
849
- finalize(ctx, result);
1005
+ finalize(ctx, result, dbFail);
1006
+ if (!options.dryRun) {
1007
+ try {
1008
+ const r = await ensureEmbeddingModel({ skip: options.skipModel });
1009
+ console.log(`\u6A21\u578B\u9884\u4E0B\u8F7D: ${r.status}${r.model ? ` (${r.model})` : ""}`);
1010
+ } catch (e) {
1011
+ console.warn(`\u26A0\uFE0F \u6A21\u578B\u9884\u4E0B\u8F7D\u5931\u8D25\uFF08\u4E0D\u5F71\u54CD\u4E3B\u6D41\u7A0B\uFF0C\u9996\u6B21 DPS \u8C03\u7528\u4F1A\u81EA\u52A8\u8865\u4E0B\u8F7D\uFF09: ${e instanceof Error ? e.message : String(e)}`);
1012
+ }
1013
+ }
850
1014
  }
851
1015
  async function resolveAdapter(projectRoot, specified) {
852
1016
  if (specified) {
@@ -901,27 +1065,31 @@ function portInUse(port) {
901
1065
  }
902
1066
  function hasPgIsready() {
903
1067
  try {
904
- const containers = spawnSync2("podman", ["ps", "--filter", "publish=5433", "--format", "{{.Names}}"], { timeout: 3e3 });
1068
+ const containers = runCommand("podman", ["ps", "--filter", "publish=5433", "--format", "{{.Names}}"], { timeout: 3e3 });
905
1069
  const name = containers.stdout.toString().trim().split("\n")[0];
906
- if (name && spawnSync2("podman", ["exec", name, "pg_isready", "--version"], { timeout: 2e3 }).status === 0) return true;
1070
+ if (name && runCommand("podman", ["exec", name, "pg_isready", "--version"], { timeout: 2e3 }).status === 0) return true;
907
1071
  } catch {
908
1072
  }
909
- return spawnSync2("which", ["pg_isready"], { timeout: 2e3 }).status === 0;
1073
+ return commandExists("pg_isready");
910
1074
  }
911
1075
  function testPostgresConnection(port, user, password, dbName) {
912
1076
  if (!hasPgIsready()) {
913
1077
  console.log(" \u26A0\uFE0F \u65E0\u6CD5\u9A8C\u8BC1\u51ED\u636E\uFF08\u5BB9\u5668\u672A\u8FD0\u884C\u4E14 pg_isready \u672A\u5B89\u88C5\uFF09\uFF0C\u4FE1\u4EFB\u8F93\u5165");
914
1078
  return true;
915
1079
  }
916
- const containers = spawnSync2("podman", ["ps", "--filter", "publish=5433", "--format", "{{.Names}}"], { timeout: 3e3 });
917
- const containerName = containers.stdout.toString().trim().split("\n")[0];
918
- const args = containerName ? ["exec", containerName, "pg_isready", "-U", user, "-d", dbName] : ["-h", "localhost", "-p", port, "-U", user, "-d", dbName];
919
- const cmd = containerName ? "podman" : "pg_isready";
920
- const r = spawnSync2(cmd, args, {
921
- timeout: 5e3,
922
- env: containerName ? process.env : { ...process.env, PGPASSWORD: password }
923
- });
924
- return r.status === 0;
1080
+ try {
1081
+ const containers = runCommand("podman", ["ps", "--filter", "publish=5433", "--format", "{{.Names}}"], { timeout: 3e3 });
1082
+ const containerName = containers.stdout.toString().trim().split("\n")[0];
1083
+ const args = containerName ? ["exec", containerName, "pg_isready", "-U", user, "-d", dbName] : ["-h", "localhost", "-p", port, "-U", user, "-d", dbName];
1084
+ const cmd = containerName ? "podman" : "pg_isready";
1085
+ const r = runCommand(cmd, args, {
1086
+ timeout: 5e3,
1087
+ env: containerName ? void 0 : { ...process.env, PGPASSWORD: password }
1088
+ });
1089
+ return r.status === 0;
1090
+ } catch {
1091
+ return false;
1092
+ }
925
1093
  }
926
1094
  async function resolveDbCredentials(force) {
927
1095
  const d = { user: "admin", password: "change-me-in-production", port: "5433" };
@@ -984,8 +1152,8 @@ networks:
984
1152
  `;
985
1153
  }
986
1154
  function writeSqliteExportScript(projectRoot, dryRun) {
987
- const scriptsDir = resolve5(projectRoot, "scripts");
988
- const scriptPath = resolve5(scriptsDir, "export-db.ts");
1155
+ const scriptsDir = resolve6(projectRoot, "scripts");
1156
+ const scriptPath = resolve6(scriptsDir, "export-db.ts");
989
1157
  const content = `import { PrismaClient } from "@prisma/client";
990
1158
  import { writeFileSync, mkdirSync, existsSync } from "fs";
991
1159
  import { resolve } from "path";
@@ -1009,18 +1177,18 @@ main().catch((e) => { console.error(e); process.exit(1); });
1009
1177
  console.log(`[dry-run] \u5C06\u5199\u5165 ${scriptPath}`);
1010
1178
  return;
1011
1179
  }
1012
- if (!existsSync8(scriptsDir)) mkdirSync4(scriptsDir, { recursive: true });
1180
+ if (!existsSync9(scriptsDir)) mkdirSync4(scriptsDir, { recursive: true });
1013
1181
  writeFileSync4(scriptPath, content, "utf-8");
1014
1182
  console.log("\u5DF2\u751F\u6210 scripts/export-db.ts");
1015
1183
  }
1016
1184
  function injectDbExportScript(projectRoot, dryRun) {
1017
- const pkgPath = resolve5(projectRoot, "package.json");
1018
- if (!existsSync8(pkgPath)) return;
1185
+ const pkgPath = resolve6(projectRoot, "package.json");
1186
+ if (!existsSync9(pkgPath)) return;
1019
1187
  if (dryRun) {
1020
1188
  console.log("[dry-run] \u5C06\u6CE8\u5165 db:export");
1021
1189
  return;
1022
1190
  }
1023
- const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
1191
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
1024
1192
  if (!pkg.scripts) pkg.scripts = {};
1025
1193
  if (!pkg.scripts["db:export"]) {
1026
1194
  pkg.scripts["db:export"] = "npx tsx scripts/export-db.ts";
@@ -1067,15 +1235,15 @@ function writeComposeEnv(ctx) {
1067
1235
  if (db.engine !== "postgresql" || !db.container || db.container === "manual") return;
1068
1236
  if (!db.reuseExisting) {
1069
1237
  const composeName = db.container === "podman" ? "podman-compose.add.yml" : "docker-compose.add.yml";
1070
- const composePath = resolve5(projectRoot, composeName);
1071
- if (!options.dryRun && (!existsSync8(composePath) || options.force)) {
1238
+ const composePath = resolve6(projectRoot, composeName);
1239
+ if (!options.dryRun && (!existsSync9(composePath) || options.force)) {
1072
1240
  writeFileSync4(composePath, composeContent(config.projectName || "add-project"), "utf-8");
1073
1241
  console.log(`\u5DF2\u521B\u5EFA ${composeName}`);
1074
1242
  }
1075
1243
  }
1076
- const devEnvPath = resolve5(projectRoot, ".env.development");
1077
- if (!options.dryRun && existsSync8(devEnvPath)) {
1078
- const existing = readFileSync6(devEnvPath, "utf-8");
1244
+ const devEnvPath = resolve6(projectRoot, ".env.development");
1245
+ if (!options.dryRun && existsSync9(devEnvPath)) {
1246
+ const existing = readFileSync7(devEnvPath, "utf-8");
1079
1247
  if (!/^DATABASE_USER=/m.test(existing)) {
1080
1248
  writeFileSync4(devEnvPath, existing + `
1081
1249
  DATABASE_USER=${db.user || "admin"}
@@ -1114,8 +1282,8 @@ async function renderAndWrite(ctx) {
1114
1282
  console.log(`claude adapter (via Agent Host): ${claudeFiles.size} \u6587\u4EF6`);
1115
1283
  }
1116
1284
  for (const d of [".add", magicDir]) {
1117
- const reviewsDir = resolve5(projectRoot, d, "reviews");
1118
- if (!existsSync8(reviewsDir)) {
1285
+ const reviewsDir = resolve6(projectRoot, d, "reviews");
1286
+ if (!existsSync9(reviewsDir)) {
1119
1287
  if (dry) {
1120
1288
  console.log(`[dry-run] \u5C06\u521B\u5EFA ${reviewsDir}/`);
1121
1289
  } else {
@@ -1127,16 +1295,16 @@ async function renderAndWrite(ctx) {
1127
1295
  const hashMap = {};
1128
1296
  let npmVer = "";
1129
1297
  try {
1130
- npmVer = JSON.parse(readFileSync6(resolve5(projectRoot, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json"), "utf-8"))._version ?? "";
1298
+ npmVer = JSON.parse(readFileSync7(resolve6(projectRoot, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json"), "utf-8"))._version ?? "";
1131
1299
  } catch {
1132
1300
  }
1133
1301
  for (const [rp, c] of allFiles) {
1134
1302
  hashMap[rp] = createHash("sha256").update(c).digest("hex").slice(0, 8);
1135
1303
  }
1136
- const hashOut = resolve5(projectRoot, magicDir, ".add-coder-hash.json");
1304
+ const hashOut = resolve6(projectRoot, magicDir, ".add-coder-hash.json");
1137
1305
  writeFileSync4(hashOut, JSON.stringify(hashMap, null, 2) + "\n", "utf-8");
1138
1306
  if (npmVer) {
1139
- writeFileSync4(resolve5(projectRoot, magicDir, ".add-coder-version"), npmVer + "\n", "utf-8");
1307
+ writeFileSync4(resolve6(projectRoot, magicDir, ".add-coder-version"), npmVer + "\n", "utf-8");
1140
1308
  }
1141
1309
  console.log(`hash: ${Object.keys(hashMap).length} entries \u2192 ${magicDir}/.add-coder-hash.json`);
1142
1310
  }
@@ -1144,22 +1312,37 @@ async function renderAndWrite(ctx) {
1144
1312
  }
1145
1313
  async function deployDatabase(ctx) {
1146
1314
  const { projectRoot, options, magicDir, config, db } = ctx;
1147
- if (options.dryRun) return;
1315
+ if (options.dryRun) return null;
1316
+ let fail = null;
1148
1317
  if (db.engine === "postgresql" && db.container && db.container !== "manual") {
1149
- const dbScript = resolve5(projectRoot, magicDir, "scripts", "db-ensure.sh");
1318
+ const dbScript = resolve6(projectRoot, magicDir, "scripts", "db-ensure.sh");
1150
1319
  const dbEnv = { ...process.env, DATABASE_USER: db.user, DATABASE_PASSWORD: db.password, DATABASE_PORT: db.port, PROJECT_NAME: config.projectName };
1151
1320
  const mode = db.reuseExisting ? "manual" : db.container;
1152
1321
  console.log(db.reuseExisting ? "\u590D\u7528\u5DF2\u6709 PostgreSQL ..." : `\u90E8\u7F72\u6570\u636E\u5E93 (${db.container}) ...`);
1153
- spawnSync2("bash", [dbScript, "postgresql", mode, "--migrate"], { cwd: projectRoot, stdio: "inherit", env: dbEnv });
1322
+ try {
1323
+ const bashRun = runCommand("bash", [dbScript, "postgresql", mode, "--migrate"], { cwd: projectRoot, env: dbEnv, stdio: "inherit" });
1324
+ if (bashRun.status !== 0) fail = `db-ensure.sh \u9000\u51FA\u7801: ${bashRun.status}${bashRun.stderr ? `
1325
+ ${bashRun.stderr.trim().split("\n").slice(0, 5).join("\n")}` : ""}`;
1326
+ } catch (e) {
1327
+ fail = e instanceof Error ? e.message : String(e);
1328
+ }
1154
1329
  try {
1155
1330
  await injectPrisma2(projectRoot, { force: !!options.force });
1156
1331
  } catch (e) {
1157
- console.log(`Prisma \u540C\u6B65\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
1332
+ fail = `Prisma \u540C\u6B65\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`;
1158
1333
  }
1159
1334
  }
1160
1335
  if (db.engine === "postgresql" && db.container === "manual") {
1161
- const dbScript = resolve5(projectRoot, magicDir, "scripts", "db-ensure.sh");
1162
- if (existsSync8(dbScript)) spawnSync2("bash", [dbScript, "postgresql", "manual"], { cwd: projectRoot, stdio: "inherit" });
1336
+ const dbScript = resolve6(projectRoot, magicDir, "scripts", "db-ensure.sh");
1337
+ if (existsSync9(dbScript)) {
1338
+ try {
1339
+ const bashRun = runCommand("bash", [dbScript, "postgresql", "manual"], { cwd: projectRoot, stdio: "inherit" });
1340
+ if (bashRun.status !== 0) fail = `db-ensure.sh \u9000\u51FA\u7801: ${bashRun.status}${bashRun.stderr ? `
1341
+ ${bashRun.stderr.trim().split("\n").slice(0, 5).join("\n")}` : ""}`;
1342
+ } catch (e) {
1343
+ fail = e instanceof Error ? e.message : String(e);
1344
+ }
1345
+ }
1163
1346
  console.log([
1164
1347
  "",
1165
1348
  "\u2501".repeat(30),
@@ -1187,25 +1370,26 @@ async function deployDatabase(ctx) {
1187
1370
  try {
1188
1371
  await injectPrisma2(projectRoot, { force: !!options.force, datasource: "sqlite" });
1189
1372
  } catch (e) {
1190
- console.log(`SQLite \u540C\u6B65\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
1373
+ fail = `SQLite \u540C\u6B65\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`;
1191
1374
  }
1192
1375
  }
1376
+ return fail;
1193
1377
  }
1194
1378
  function deployDocs(ctx) {
1195
1379
  const { projectRoot, options, config } = ctx;
1196
1380
  if (options.dryRun) return;
1197
1381
  const pn = config.projectName || "add-project";
1198
- const docsBase = resolve5(projectRoot, "docs", pn, "knowledge");
1199
- const groundingSrc = resolve5(import.meta.dirname, "../templates/core/templates");
1382
+ const docsBase = resolve6(projectRoot, "docs", pn, "knowledge");
1383
+ const groundingSrc = resolve6(import.meta.dirname, "../templates/core/templates");
1200
1384
  for (const d of ["00-\u9700\u6C42", "01-\u67B6\u6784", "02-\u89C4\u8303"]) {
1201
- const srcDir = resolve5(groundingSrc, d);
1202
- const destDir = resolve5(docsBase, d);
1203
- if (!existsSync8(destDir)) mkdirSync4(destDir, { recursive: true });
1204
- if (!existsSync8(srcDir)) continue;
1385
+ const srcDir = resolve6(groundingSrc, d);
1386
+ const destDir = resolve6(docsBase, d);
1387
+ if (!existsSync9(destDir)) mkdirSync4(destDir, { recursive: true });
1388
+ if (!existsSync9(srcDir)) continue;
1205
1389
  for (const f of readdirSync2(srcDir)) {
1206
- const src = resolve5(srcDir, f);
1207
- const dest = resolve5(destDir, f);
1208
- if (existsSync8(dest)) continue;
1390
+ const src = resolve6(srcDir, f);
1391
+ const dest = resolve6(destDir, f);
1392
+ if (existsSync9(dest)) continue;
1209
1393
  try {
1210
1394
  copyFileSync2(src, dest);
1211
1395
  } catch {
@@ -1213,30 +1397,51 @@ function deployDocs(ctx) {
1213
1397
  }
1214
1398
  }
1215
1399
  }
1216
- function finalize(ctx, result) {
1400
+ function finalize(ctx, result, dbFail) {
1217
1401
  const { projectRoot, options, db } = ctx;
1218
- console.log(`
1402
+ if (options.dryRun) {
1403
+ console.log(`
1219
1404
  \u5B8C\u6210: \u65B0\u5EFA ${result.created}, \u8DF3\u8FC7 ${result.skipped}, \u8986\u76D6 ${result.overwritten}`);
1220
- if (options.dryRun) return;
1405
+ return;
1406
+ }
1221
1407
  if (db.engine === "sqlite") console.log("\u6570\u636E\u5907\u4EFD: npm run db:export \u2192 data/exports/");
1222
- const pkg = JSON.parse(readFileSync6(resolve5(import.meta.dirname, "../package.json"), "utf-8"));
1408
+ const pkg = JSON.parse(readFileSync7(resolve6(import.meta.dirname, "../package.json"), "utf-8"));
1223
1409
  const peerNames = Object.keys(pkg.peerDependencies || {});
1224
1410
  if (peerNames.length > 0) {
1225
1411
  console.log(`
1226
1412
  \u5B89\u88C5 peer \u4F9D\u8D56 (${peerNames.join(" ")}) ...`);
1227
1413
  const pm = detectPm(projectRoot);
1228
- spawnSync2(pm, pm === "pnpm" ? ["add", ...peerNames] : ["install", ...peerNames], { cwd: projectRoot, stdio: "inherit" });
1414
+ const installArgs = pm === "pnpm" ? ["add", ...peerNames] : ["install", ...peerNames];
1415
+ try {
1416
+ const ir = runCommand(pm, installArgs, { cwd: projectRoot });
1417
+ if (ir.status !== 0) console.warn(`\u26A0\uFE0F peer \u4F9D\u8D56\u5B89\u88C5\u5931\u8D25\uFF08\u9000\u51FA\u7801: ${ir.status}\uFF09\uFF0C\u540E\u7EED MCP \u542F\u52A8\u53EF\u80FD\u62A5\u9519`);
1418
+ } catch (e) {
1419
+ console.warn(`\u26A0\uFE0F peer \u4F9D\u8D56\u5B89\u88C5\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
1420
+ }
1229
1421
  }
1230
1422
  if (db.engine !== "manual" && (db.engine !== "postgresql" || db.container !== "manual")) {
1231
1423
  console.log("\u63D0\u793A: \u91CD\u542F IDE \u4EE5\u52A0\u8F7D hook \u914D\u7F6E");
1232
1424
  }
1425
+ if (dbFail) {
1426
+ console.error(`
1427
+ \u2717 \u6CBB\u7406\u6A21\u578B\u672A\u5C31\u7EEA: ${dbFail}`);
1428
+ console.error(" \u8BF7\u6309\u9519\u8BEF\u63D0\u793A\u4FEE\u590D\u540E\u91CD\u65B0\u8FD0\u884C add-coder init\u3002");
1429
+ process.exit(1);
1430
+ }
1431
+ console.log(`
1432
+ \u5B8C\u6210: \u65B0\u5EFA ${result.created}, \u8DF3\u8FC7 ${result.skipped}, \u8986\u76D6 ${result.overwritten}`);
1233
1433
  }
1234
1434
 
1235
1435
  // src/cli/commands/sync.ts
1236
- import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5 } from "fs";
1237
- import { resolve as resolve6, dirname as dirname4 } from "path";
1436
+ import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5 } from "fs";
1437
+ import { resolve as resolve7, dirname as dirname4 } from "path";
1238
1438
  import { createHash as createHash2 } from "crypto";
1239
1439
 
1440
+ // src/lib/path-normalize.ts
1441
+ function normalizeRelPath(p) {
1442
+ return p.replaceAll("\\", "/");
1443
+ }
1444
+
1240
1445
  // src/caijuehub/strategies/sync.strategy.ts
1241
1446
  var SYNC_CONFIG = {
1242
1447
  PATCH_GUARD: [/[/]plans[/]/, /[/]specs[/]/, /[/]reviews[/]/, /[/]rules[/]profiles[/]/],
@@ -1280,32 +1485,65 @@ function resolveAdapter2(projectRoot, specified) {
1280
1485
  return "qoder";
1281
1486
  }
1282
1487
  function isUserData(p) {
1283
- return SYNC_CONFIG.PATCH_GUARD.some((r) => r.test(p));
1488
+ return SYNC_CONFIG.PATCH_GUARD.some((r) => r.test(normalizeRelPath(p)));
1284
1489
  }
1285
1490
  function hash8(c) {
1286
1491
  return createHash2("sha256").update(c).digest("hex").slice(0, SYNC_CONFIG.HASH_HEX_LENGTH);
1287
1492
  }
1288
1493
  function loadHashFile(root, magic) {
1289
1494
  try {
1290
- return JSON.parse(readFileSync7(resolve6(root, magic, SYNC_CONFIG.HASH_OUTPUT_FILE), "utf-8"));
1495
+ const raw = JSON.parse(readFileSync8(resolve7(root, magic, SYNC_CONFIG.HASH_OUTPUT_FILE), "utf-8"));
1496
+ const normalized = {};
1497
+ for (const [k, v] of Object.entries(raw)) normalized[normalizeRelPath(k)] = v;
1498
+ return normalized;
1291
1499
  } catch {
1292
1500
  return {};
1293
1501
  }
1294
1502
  }
1295
1503
  function loadVersionFile(root, magic) {
1296
1504
  try {
1297
- return readFileSync7(resolve6(root, magic, SYNC_CONFIG.VERSION_SENTINEL), "utf-8").trim();
1505
+ return readFileSync8(resolve7(root, magic, SYNC_CONFIG.VERSION_SENTINEL), "utf-8").trim();
1298
1506
  } catch {
1299
1507
  return "";
1300
1508
  }
1301
1509
  }
1302
1510
  function saveVersionFile(root, magic, version2) {
1303
- writeFileSync5(resolve6(root, magic, SYNC_CONFIG.VERSION_SENTINEL), version2 + "\n", "utf-8");
1511
+ writeFileSync5(resolve7(root, magic, SYNC_CONFIG.VERSION_SENTINEL), version2 + "\n", "utf-8");
1304
1512
  }
1305
1513
  function saveHashFile(root, magic, files) {
1306
1514
  const m = {};
1307
- for (const [p, c] of files) m[p] = hash8(c);
1308
- writeFileSync5(resolve6(root, magic, SYNC_CONFIG.HASH_OUTPUT_FILE), JSON.stringify(m, null, 2) + "\n", "utf-8");
1515
+ for (const [p, c] of files) m[p] = c;
1516
+ writeFileSync5(resolve7(root, magic, SYNC_CONFIG.HASH_OUTPUT_FILE), JSON.stringify(m, null, 2) + "\n", "utf-8");
1517
+ }
1518
+ function mergeFullHash(outHash, candidates, readDiskHash) {
1519
+ const finalHash = /* @__PURE__ */ new Map();
1520
+ for (const [k, v] of Object.entries(outHash)) finalHash.set(k, v);
1521
+ for (const { relPath, absPath } of candidates) {
1522
+ const key = normalizeRelPath(relPath);
1523
+ const h = readDiskHash(absPath);
1524
+ if (h !== null) finalHash.set(key, h);
1525
+ }
1526
+ return finalHash;
1527
+ }
1528
+ async function maybeModelDownload(options) {
1529
+ let model;
1530
+ try {
1531
+ model = resolveEmbeddingModel();
1532
+ } catch (e) {
1533
+ console.warn(`\u26A0\uFE0F \u6A21\u578B\u914D\u7F6E\u7F3A\u5931\uFF08\u8DF3\u8FC7\u68C0\u6D4B\uFF09: ${e instanceof Error ? e.message : String(e)}`);
1534
+ return;
1535
+ }
1536
+ if (isModelCached(model)) return;
1537
+ if (options.model) {
1538
+ try {
1539
+ const r = await ensureEmbeddingModel();
1540
+ console.log(`\u6A21\u578B\u9884\u4E0B\u8F7D: ${r.status} (${r.model})`);
1541
+ } catch (e) {
1542
+ console.warn(`\u26A0\uFE0F \u6A21\u578B\u9884\u4E0B\u8F7D\u5931\u8D25\uFF08\u9996\u6B21 DPS \u8C03\u7528\u4F1A\u81EA\u52A8\u8865\u4E0B\u8F7D\uFF09: ${e instanceof Error ? e.message : String(e)}`);
1543
+ }
1544
+ } else {
1545
+ console.log(`\u6A21\u578B\u672A\u9884\u4E0B\u8F7D: \u8FD0\u884C \`add-coder model:download\` \u63D0\u524D\u4E0B\u8F7D\uFF08\u9996\u6B21 DPS \u8C03\u7528\u4E5F\u4F1A\u81EA\u52A8\u4E0B\u8F7D\uFF09`);
1546
+ }
1309
1547
  }
1310
1548
  async function syncCommand(options = {}) {
1311
1549
  const projectRoot = process.cwd();
@@ -1348,10 +1586,10 @@ async function syncCommand(options = {}) {
1348
1586
  candidates.set(p, c);
1349
1587
  }
1350
1588
  const outHash = loadHashFile(projectRoot, magicDir);
1351
- const srcHashPath = resolve6(projectRoot, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json");
1589
+ const srcHashPath = resolve7(projectRoot, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json");
1352
1590
  let npmVersion = "";
1353
1591
  try {
1354
- npmVersion = JSON.parse(readFileSync7(srcHashPath, "utf-8"))._version ?? "";
1592
+ npmVersion = JSON.parse(readFileSync8(srcHashPath, "utf-8"))._version ?? "";
1355
1593
  } catch {
1356
1594
  }
1357
1595
  const installedVersion = loadVersionFile(projectRoot, magicDir);
@@ -1370,18 +1608,19 @@ async function syncCommand(options = {}) {
1370
1608
  const conflictFiles = /* @__PURE__ */ new Map();
1371
1609
  let sameCount = 0;
1372
1610
  for (const [relPath, content] of candidates) {
1373
- const absPath = resolve6(projectRoot, relPath);
1374
- if (!existsSync9(absPath)) {
1375
- missingFiles.set(relPath, content);
1611
+ const key = normalizeRelPath(relPath);
1612
+ const absPath = resolve7(projectRoot, relPath);
1613
+ if (!existsSync10(absPath)) {
1614
+ missingFiles.set(key, content);
1376
1615
  } else if (establishBaseline) {
1377
- missingFiles.set(relPath, content);
1616
+ missingFiles.set(key, content);
1378
1617
  } else {
1379
- const curH = hash8(readFileSync7(absPath, "utf-8"));
1380
- const storedH = outHash[relPath];
1618
+ const curH = hash8(readFileSync8(absPath, "utf-8"));
1619
+ const storedH = outHash[key];
1381
1620
  if (storedH && curH === storedH) {
1382
1621
  sameCount++;
1383
1622
  } else {
1384
- conflictFiles.set(relPath, content);
1623
+ conflictFiles.set(key, content);
1385
1624
  }
1386
1625
  }
1387
1626
  }
@@ -1402,14 +1641,20 @@ async function syncCommand(options = {}) {
1402
1641
  if (missingFiles.size === 0 && conflictFiles.size === 0) {
1403
1642
  console.log(SYNC_CONFIG.PROMPT_PATCH_DONE);
1404
1643
  }
1405
- saveHashFile(projectRoot, magicDir, new Map([...missingFiles, ...conflictFiles]));
1644
+ const finalHash = mergeFullHash(
1645
+ outHash,
1646
+ [...candidates].map(([relPath]) => ({ relPath, absPath: resolve7(projectRoot, relPath) })),
1647
+ (absPath) => existsSync10(absPath) ? hash8(readFileSync8(absPath, "utf-8")) : null
1648
+ );
1649
+ saveHashFile(projectRoot, magicDir, finalHash);
1406
1650
  saveVersionFile(projectRoot, magicDir, npmVersion);
1407
1651
  await checkPrismaDiff(projectRoot, options);
1652
+ await maybeModelDownload(options);
1408
1653
  return;
1409
1654
  }
1410
1655
  const missing = /* @__PURE__ */ new Map();
1411
1656
  for (const [relPath, content] of allFiles) {
1412
- if (!existsSync9(resolve6(projectRoot, relPath))) {
1657
+ if (!existsSync10(resolve7(projectRoot, relPath))) {
1413
1658
  missing.set(relPath, content);
1414
1659
  }
1415
1660
  }
@@ -1428,10 +1673,11 @@ async function syncCommand(options = {}) {
1428
1673
  const result = await writeFiles2(projectRoot, filesToWrite, { yes: true });
1429
1674
  console.log(`\u540C\u6B65\u5B8C\u6210: \u65B0\u5EFA ${result.created}, \u8DF3\u8FC7 ${result.skipped}`);
1430
1675
  await checkPrismaDiff(projectRoot, options);
1676
+ await maybeModelDownload(options);
1431
1677
  }
1432
1678
  function printMigrateGuidance(targetPath, changed) {
1433
1679
  const g = SYNC_PRISMA_CONFIG.POST_SYNC;
1434
- const isManaged = existsSync9(resolve6(dirname4(targetPath), "migrations"));
1680
+ const isManaged = existsSync10(resolve7(dirname4(targetPath), "migrations"));
1435
1681
  const actions = isManaged ? g.MANAGED_ACTIONS : g.UNMANAGED_ACTIONS;
1436
1682
  console.log(` \u25B6 \u5DF2\u5199\u5165 ${changed} \u5904\u53D8\u66F4\uFF0C${g.HEADER}`);
1437
1683
  for (const a of actions) {
@@ -1453,9 +1699,9 @@ function printMigrateGuidance(targetPath, changed) {
1453
1699
  }
1454
1700
  async function checkPrismaDiff(projectRoot, options) {
1455
1701
  if (!options.patch) return;
1456
- const basePath = resolve6(projectRoot, SYNC_PRISMA_CONFIG.BASE_SCHEMA);
1457
- const targetPath = resolve6(projectRoot, SYNC_PRISMA_CONFIG.TARGET_PATTERN);
1458
- if (!existsSync9(basePath)) {
1702
+ const basePath = resolve7(projectRoot, SYNC_PRISMA_CONFIG.BASE_SCHEMA);
1703
+ const targetPath = resolve7(projectRoot, SYNC_PRISMA_CONFIG.TARGET_PATTERN);
1704
+ if (!existsSync10(basePath)) {
1459
1705
  console.log(`
1460
1706
  \u26A0\uFE0F \u57FA\u51C6 schema \u4E0D\u5B58\u5728: ${basePath}`);
1461
1707
  console.log(` \u8BF7\u786E\u4FDD add-coder \u5DF2\u6B63\u786E\u5B89\u88C5\u3002`);
@@ -1467,7 +1713,7 @@ async function checkPrismaDiff(projectRoot, options) {
1467
1713
  \u2705 Prisma schema \u4E0E add-coder \u6807\u51C6\u4E00\u81F4\uFF0C\u65E0\u9700\u540C\u6B65\u3002`);
1468
1714
  return;
1469
1715
  }
1470
- const targetExists = existsSync9(targetPath);
1716
+ const targetExists = existsSync10(targetPath);
1471
1717
  let modifiedCount = 0;
1472
1718
  console.log(`
1473
1719
  \u26A0\uFE0F Prisma schema \u5DEE\u5F02\u68C0\u6D4B:`);
@@ -1618,7 +1864,7 @@ async function handleDiffAction(action, ctx) {
1618
1864
  }
1619
1865
  }
1620
1866
  function injectMissingModels(targetPath, models) {
1621
- let content = readFileSync7(targetPath, "utf-8");
1867
+ let content = readFileSync8(targetPath, "utf-8");
1622
1868
  content = content.replace(/\n*$/, "\n");
1623
1869
  content += `
1624
1870
  // ===== \u7531 add-coder sync --patch \u81EA\u52A8\u6CE8\u5165 (${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}) =====
@@ -1631,44 +1877,77 @@ function injectMissingModels(targetPath, models) {
1631
1877
  return models.length;
1632
1878
  }
1633
1879
  function getBaseFieldLines(basePath, modelName) {
1634
- const content = readFileSync7(basePath, "utf-8");
1635
- const blockRegex = /^(model|enum)\s+(\w+)\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/gm;
1636
- let match;
1637
- while ((match = blockRegex.exec(content)) !== null) {
1638
- if (match[2] !== modelName) continue;
1639
- const fields = {};
1640
- for (const line of match[3].split("\n")) {
1880
+ const content = readFileSync8(basePath, "utf-8");
1881
+ const blocks = parseSchemaBlocks(content);
1882
+ const block = blocks.get(`model:${modelName}`) ?? blocks.get(`enum:${modelName}`);
1883
+ if (!block) return {};
1884
+ const fields = {};
1885
+ for (const line of block.body.split("\n")) {
1886
+ const clean = line.replace(/\/\/.*$/, "").trim();
1887
+ if (!clean) continue;
1888
+ if (block.type === "enum") {
1889
+ if (/^\w+$/.test(clean)) fields[clean] = line.trim();
1890
+ } else {
1641
1891
  const fm = line.match(/^\s*(\w+)\s+/);
1642
1892
  if (fm) fields[fm[1]] = line.trim();
1643
1893
  }
1644
- return fields;
1645
1894
  }
1646
- return {};
1895
+ return fields;
1647
1896
  }
1648
1897
  function injectFieldLines(targetPath, basePath, modelName, fieldKeys) {
1649
1898
  const baseFields = getBaseFieldLines(basePath, modelName);
1650
- if (Object.keys(baseFields).length === 0) return 0;
1651
- let content = readFileSync7(targetPath, "utf-8");
1652
- let count = 0;
1899
+ if (Object.keys(baseFields).length === 0) {
1900
+ console.warn(`\u26A0\uFE0F \u6CE8\u5165\u5931\u8D25\uFF1A${modelName} \u5728\u57FA\u51C6\u4E2D\u672A\u627E\u5230\u5B57\u6BB5\u5B9A\u4E49\uFF08${fieldKeys.length} \u4E2A\u5B57\u6BB5\u672A\u5199\u5165\uFF09`);
1901
+ return 0;
1902
+ }
1903
+ const content = readFileSync8(targetPath, "utf-8");
1904
+ const lines = content.split("\n");
1905
+ const blocks = parseSchemaBlocks(content);
1906
+ const target = blocks.get(`model:${modelName}`) ?? blocks.get(`enum:${modelName}`);
1907
+ if (!target) {
1908
+ console.warn(`\u26A0\uFE0F \u6CE8\u5165\u5931\u8D25\uFF1A\u76EE\u6807\u4E2D\u4E0D\u5B58\u5728 ${modelName} \u5757\uFF08${fieldKeys.length} \u4E2A\u5B57\u6BB5\u672A\u5199\u5165\uFF09`);
1909
+ return 0;
1910
+ }
1911
+ const bodyLines = target.body.split("\n");
1912
+ const startIdx = lines.findIndex((l) => l === bodyLines[0]);
1913
+ if (startIdx < 0) {
1914
+ console.warn(`\u26A0\uFE0F \u6CE8\u5165\u5931\u8D25\uFF1A\u65E0\u6CD5\u5B9A\u4F4D ${modelName} \u5757\u4F4D\u7F6E\uFF08${fieldKeys.length} \u4E2A\u5B57\u6BB5\u672A\u5199\u5165\uFF09`);
1915
+ return 0;
1916
+ }
1917
+ const endIdx = startIdx + bodyLines.length - 1;
1918
+ const existingNames = new Set(
1919
+ target.fields.map((f) => f.split(":")[0])
1920
+ );
1921
+ const newFieldLines = [];
1653
1922
  for (const key of fieldKeys) {
1654
1923
  const fieldName = key.split(":")[0];
1655
1924
  const fieldLine = baseFields[fieldName];
1656
1925
  if (!fieldLine) continue;
1657
- const modelRegex = new RegExp(`(model\\s+${modelName}\\s*\\{)([^}]*?)(\\n\\s*\\})`, "m");
1658
- const m = content.match(modelRegex);
1659
- if (!m) continue;
1660
- if (new RegExp(`^\\s*${fieldName}\\s+`, "m").test(m[2])) continue;
1661
- content = content.replace(modelRegex, `$1$2
1662
- ${fieldLine}$3`);
1663
- count++;
1926
+ if (existingNames.has(fieldName)) continue;
1927
+ newFieldLines.push(` ${fieldLine}`);
1664
1928
  }
1665
- if (count > 0) writeFileSync5(targetPath, content, "utf-8");
1666
- return count;
1929
+ if (newFieldLines.length === 0) {
1930
+ if (fieldKeys.length > 0) {
1931
+ console.warn(`\u26A0\uFE0F \u6CE8\u5165\u5931\u8D25\uFF1A${modelName} \u7684 ${fieldKeys.length} \u4E2A\u5B57\u6BB5\u672A\u5199\u5165\uFF08\u53EF\u80FD\u5DF2\u5B58\u5728\u6216\u5B9A\u4E49\u4E0D\u5339\u914D\uFF09`);
1932
+ }
1933
+ return 0;
1934
+ }
1935
+ let insertIdx = endIdx;
1936
+ for (let k = startIdx + 1; k < endIdx; k++) {
1937
+ if (/^\s*@@/.test(lines[k])) insertIdx = k;
1938
+ }
1939
+ const merged = [
1940
+ ...lines.slice(0, insertIdx),
1941
+ ...newFieldLines,
1942
+ ...lines.slice(insertIdx)
1943
+ ];
1944
+ writeFileSync5(targetPath, merged.join("\n"), "utf-8");
1945
+ return newFieldLines.length;
1667
1946
  }
1668
1947
  function overwriteFieldLines(targetPath, basePath, modelName, conflicts) {
1669
1948
  const baseFields = getBaseFieldLines(basePath, modelName);
1670
1949
  if (Object.keys(baseFields).length === 0) return 0;
1671
- let content = readFileSync7(targetPath, "utf-8");
1950
+ let content = readFileSync8(targetPath, "utf-8");
1672
1951
  let count = 0;
1673
1952
  for (const { fieldName } of conflicts) {
1674
1953
  const baseLine = baseFields[fieldName];
@@ -1684,8 +1963,8 @@ function overwriteFieldLines(targetPath, basePath, modelName, conflicts) {
1684
1963
  }
1685
1964
 
1686
1965
  // src/cli/commands/status.ts
1687
- import { existsSync as existsSync10 } from "fs";
1688
- import { resolve as resolve7 } from "path";
1966
+ import { existsSync as existsSync11 } from "fs";
1967
+ import { resolve as resolve8 } from "path";
1689
1968
  async function statusCommand() {
1690
1969
  const projectRoot = process.cwd();
1691
1970
  const config = await loadConfig(projectRoot);
@@ -1694,7 +1973,7 @@ async function statusCommand() {
1694
1973
  const missing = [];
1695
1974
  const present = [];
1696
1975
  for (const [relPath] of coreFiles) {
1697
- if (existsSync10(resolve7(projectRoot, relPath))) {
1976
+ if (existsSync11(resolve8(projectRoot, relPath))) {
1698
1977
  present.push(relPath);
1699
1978
  } else {
1700
1979
  missing.push(relPath);
@@ -1705,14 +1984,15 @@ async function statusCommand() {
1705
1984
  if (missing.length > 0) {
1706
1985
  console.log(` \u7F3A\u5931: ${missing.length} \u6587\u4EF6`);
1707
1986
  missing.forEach((f) => console.log(` - ${f}`));
1987
+ process.exit(1);
1708
1988
  } else {
1709
1989
  console.log(" \u6240\u6709\u6587\u4EF6\u5B8C\u6574\u3002");
1710
1990
  }
1711
1991
  }
1712
1992
 
1713
1993
  // src/cli/commands/stack.ts
1714
- import { readdirSync as readdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync6 } from "fs";
1715
- import { resolve as resolve8, join as join5 } from "path";
1994
+ import { readdirSync as readdirSync3, readFileSync as readFileSync9, writeFileSync as writeFileSync6, existsSync as existsSync12, mkdirSync as mkdirSync6 } from "fs";
1995
+ import { resolve as resolve9, join as join6 } from "path";
1716
1996
  import { createHash as createHash3 } from "crypto";
1717
1997
  var MAGIC_DIR_MAP3 = { claude: ".claude", qoder: ".qoder", vscode: ".vscode", trae: ".trae", codex: ".codex" };
1718
1998
  var HASH_OUTPUT_FILE = ".add-coder-hash.json";
@@ -1729,14 +2009,14 @@ function resolveMagicDir(projectRoot, specified) {
1729
2009
  return MAGIC_DIR_MAP3[adapter];
1730
2010
  }
1731
2011
  function listCustomProfiles(projectRoot, magicDir, registryNames) {
1732
- const dir = resolve8(projectRoot, magicDir, "rules", "profiles");
1733
- if (!existsSync11(dir)) return [];
2012
+ const dir = resolve9(projectRoot, magicDir, "rules", "profiles");
2013
+ if (!existsSync12(dir)) return [];
1734
2014
  return readdirSync3(dir).filter((f) => f.endsWith(".md") && !registryNames.has(f.replace(/-profile\.md$/, ""))).sort();
1735
2015
  }
1736
2016
  function profileExists(projectRoot, magicDir, name) {
1737
2017
  const registry = loadProfileRegistry();
1738
2018
  if (registry.some((p) => p.name === name)) return true;
1739
- return existsSync11(resolve8(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`));
2019
+ return existsSync12(resolve9(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`));
1740
2020
  }
1741
2021
  function stackCommand(sub, name, options = {}) {
1742
2022
  const projectRoot = process.cwd();
@@ -1773,12 +2053,12 @@ function stackCommand(sub, name, options = {}) {
1773
2053
  console.log("\u6280\u672F\u6808: \u672A\u8BBE\u7F6E\uFF08\u4E2D\u6027\uFF0C\u65E0\u6280\u672F\u6808\u5047\u8BBE\uFF09");
1774
2054
  return;
1775
2055
  }
1776
- const profilePath = resolve8(projectRoot, magicDir, "rules", "profiles", `${current}-profile.md`);
1777
- const stat = existsSync11(profilePath) ? readFileSync8(profilePath, "utf-8").length : 0;
2056
+ const profilePath = resolve9(projectRoot, magicDir, "rules", "profiles", `${current}-profile.md`);
2057
+ const stat = existsSync12(profilePath) ? readFileSync9(profilePath, "utf-8").length : 0;
1778
2058
  console.log(`\u6280\u672F\u6808: ${current}`);
1779
- console.log(`profile \u6587\u4EF6: ${profilePath}${existsSync11(profilePath) ? ` (${stat} \u5B57\u7B26)` : "\uFF08\u7F3A\u5931\uFF09"}`);
2059
+ console.log(`profile \u6587\u4EF6: ${profilePath}${existsSync12(profilePath) ? ` (${stat} \u5B57\u7B26)` : "\uFF08\u7F3A\u5931\uFF09"}`);
1780
2060
  try {
1781
- const raw = JSON.parse(readFileSync8(resolve8(projectRoot, magicDir, "stack.json"), "utf-8"));
2061
+ const raw = JSON.parse(readFileSync9(resolve9(projectRoot, magicDir, "stack.json"), "utf-8"));
1782
2062
  if (raw.updatedAt) console.log(`\u66F4\u65B0\u65F6\u95F4: ${raw.updatedAt}`);
1783
2063
  } catch {
1784
2064
  }
@@ -1803,9 +2083,9 @@ function buildConfig(projectRoot, magicDir, stack) {
1803
2083
  stack
1804
2084
  };
1805
2085
  try {
1806
- const pkgPath = resolve8(projectRoot, "package.json");
1807
- if (existsSync11(pkgPath)) {
1808
- const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
2086
+ const pkgPath = resolve9(projectRoot, "package.json");
2087
+ if (existsSync12(pkgPath)) {
2088
+ const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
1809
2089
  if (pkg.name) config.projectName = pkg.name;
1810
2090
  }
1811
2091
  } catch {
@@ -1814,9 +2094,9 @@ function buildConfig(projectRoot, magicDir, stack) {
1814
2094
  }
1815
2095
  function applyStack(projectRoot, magicDir, name) {
1816
2096
  if (!profileExists(projectRoot, magicDir, name)) {
1817
- const registry = loadProfileRegistry();
2097
+ const registry2 = loadProfileRegistry();
1818
2098
  console.error(`\u2717 profile \u4E0D\u5B58\u5728: ${name}`);
1819
- console.error(` \u5185\u7F6E: ${registry.map((p) => p.name).join(" | ")}\uFF08\u6216\u81EA\u5B9A\u4E49: ${magicDir}/rules/profiles/{name}-profile.md\uFF09`);
2099
+ console.error(` \u5185\u7F6E: ${registry2.map((p) => p.name).join(" | ")}\uFF08\u6216\u81EA\u5B9A\u4E49: ${magicDir}/rules/profiles/{name}-profile.md\uFF09`);
1820
2100
  process.exit(1);
1821
2101
  }
1822
2102
  const config = buildConfig(projectRoot, magicDir, name);
@@ -1824,42 +2104,69 @@ function applyStack(projectRoot, magicDir, name) {
1824
2104
  const coreFiles = renderCore(config, false);
1825
2105
  const stackRelated = /* @__PURE__ */ new Map();
1826
2106
  for (const [relPath, content] of coreFiles) {
1827
- if (relPath.includes("/rules/profiles/") || relPath.endsWith("/rules/project_rules.md")) {
1828
- stackRelated.set(relPath, content);
2107
+ const rp = normalizeRelPath(relPath);
2108
+ if (rp.includes("/rules/profiles/") || rp.endsWith("/rules/project_rules.md")) {
2109
+ stackRelated.set(rp, content);
1829
2110
  }
1830
2111
  }
1831
2112
  const hashMap = {};
1832
2113
  try {
1833
- Object.assign(hashMap, JSON.parse(readFileSync8(resolve8(projectRoot, magicDir, HASH_OUTPUT_FILE), "utf-8")));
2114
+ Object.assign(hashMap, JSON.parse(readFileSync9(resolve9(projectRoot, magicDir, HASH_OUTPUT_FILE), "utf-8")));
1834
2115
  } catch {
1835
2116
  }
1836
2117
  let written = 0;
1837
2118
  for (const [relPath, content] of stackRelated) {
1838
2119
  for (const t of [".add", magicDir]) {
1839
- const targetPath = resolve8(projectRoot, relPath.replace(/^\.add/, t));
1840
- mkdirSync6(join5(targetPath, ".."), { recursive: true });
2120
+ const targetPath = resolve9(projectRoot, relPath.replace(/^\.add/, t));
2121
+ mkdirSync6(join6(targetPath, ".."), { recursive: true });
1841
2122
  writeFileSync6(targetPath, content, "utf-8");
1842
2123
  hashMap[relPath.replace(/^\.add/, t)] = hash82(content);
1843
2124
  written++;
1844
2125
  }
1845
2126
  }
1846
- writeFileSync6(resolve8(projectRoot, magicDir, HASH_OUTPUT_FILE), JSON.stringify(hashMap, null, 2) + "\n", "utf-8");
2127
+ const fail = (msg) => {
2128
+ console.error(`\u2717 stack set \u5931\u8D25: ${msg}`);
2129
+ process.exit(1);
2130
+ };
2131
+ const registry = loadProfileRegistry();
2132
+ const isBuiltin = registry.some((p) => p.name === name);
2133
+ const profilePathMagic = resolve9(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`);
2134
+ const profilePathAdd = resolve9(projectRoot, ".add", "rules", "profiles", `${name}-profile.md`);
2135
+ const projectRulesPath = resolve9(projectRoot, magicDir, "rules", "project_rules.md");
2136
+ const projectRulesContent = existsSync12(projectRulesPath) ? readFileSync9(projectRulesPath, "utf-8") : "";
2137
+ if (written === 0) fail(`\u672A\u6E32\u67D3\u4EFB\u4F55 stack \u76F8\u5173\u6587\u4EF6\uFF08${name}\uFF09\u2014\u2014Windows \u8DEF\u5F84\u5339\u914D\u5931\u6548\u9057\u7559\u95EE\u9898`);
2138
+ if (isBuiltin && !existsSync12(profilePathAdd)) fail(`profile \u672A\u5199\u5165 .add: ${profilePathAdd}`);
2139
+ if (!existsSync12(profilePathMagic)) fail(`profile \u672A\u5199\u5165 ${magicDir}: ${profilePathMagic}`);
2140
+ if (!projectRulesContent.includes("**\u5F53\u524D\u6280\u672F\u6808**") || !projectRulesContent.includes(name)) fail(`project_rules.md \u672A\u5305\u542B ${name} \u5F15\u7528`);
2141
+ writeFileSync6(resolve9(projectRoot, magicDir, HASH_OUTPUT_FILE), JSON.stringify(hashMap, null, 2) + "\n", "utf-8");
1847
2142
  console.log(`\u2705 \u6280\u672F\u6808\u5DF2\u8BBE\u7F6E\u4E3A ${name}`);
1848
2143
  console.log(` ${magicDir}/stack.json \u2192 ${name}`);
1849
- console.log(` ${magicDir}/rules/profiles/${name}-profile.md ${existsSync11(resolve8(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`)) ? "\u5DF2\u5C31\u4F4D" : "\uFF08\u81EA\u5B9A\u4E49 profile\uFF0C\u9879\u76EE\u4FA7\u6587\u4EF6\uFF09"}`);
2144
+ console.log(` ${magicDir}/rules/profiles/${name}-profile.md ${existsSync12(resolve9(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`)) ? "\u5DF2\u5C31\u4F4D" : "\uFF08\u81EA\u5B9A\u4E49 profile\uFF0C\u9879\u76EE\u4FA7\u6587\u4EF6\uFF09"}`);
1850
2145
  console.log(` project_rules.md \u5F15\u7528\u884C\u5DF2\u66F4\u65B0 + hash \u5DF2\u5237\u65B0\uFF08${written} \u4E2A\u6587\u4EF6\uFF09`);
1851
2146
  }
1852
2147
 
1853
2148
  // src/cli/index.ts
1854
2149
  var { version } = JSON.parse(
1855
- readFileSync9(new URL("../package.json", import.meta.url), "utf-8")
2150
+ readFileSync10(new URL("../package.json", import.meta.url), "utf-8")
1856
2151
  );
2152
+ async function modelDownloadCommand(options) {
2153
+ try {
2154
+ const r = await ensureEmbeddingModel({ force: options.force });
2155
+ console.log(`\u6A21\u578B\u9884\u4E0B\u8F7D: ${r.status}${r.model ? ` (${r.model})` : ""}`);
2156
+ if (r.cacheDir) console.log(`\u7F13\u5B58\u4F4D\u7F6E: ${r.cacheDir}`);
2157
+ console.log("\u63D0\u793A: \u8FD0\u884C\u65F6 DPS \u4F7F\u7528\u7684\u6A21\u578B\u914D\u7F6E\u4EE5 `add-coder generate` \u751F\u6210\u7684\u914D\u7F6E\u4E3A\u51C6");
2158
+ } catch (e) {
2159
+ console.error(`\u6A21\u578B\u9884\u4E0B\u8F7D\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`);
2160
+ process.exit(1);
2161
+ }
2162
+ }
1857
2163
  var program = new Command();
1858
2164
  program.name("add-coder").description("\u521D\u59CB\u5316 ADD \u8303\u5F0F\u5DE5\u4F5C\u6D41\u6A21\u677F").version(version);
1859
- program.command("init").description("\u521D\u59CB\u5316 ADD \u6A21\u677F\u5230\u5F53\u524D\u9879\u76EE").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode | trae | codex").option("--config <path>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--force", "\u8986\u76D6\u5DF2\u6709\u6587\u4EF6\uFF0C\u4E0D\u4EA4\u4E92").option("--dry-run", "\u9884\u89C8\u6A21\u5F0F\uFF0C\u4E0D\u5B9E\u9645\u5199\u5165").option("--stack <name>", "\u6280\u672F\u6808\u7EA6\u675F profile \u540D\uFF08\u5982 machineserver\uFF0C\u53EF\u9009\uFF09").action(initCommand);
1860
- program.command("sync").description("\u589E\u91CF\u540C\u6B65\u7F3A\u5931\u6587\u4EF6\uFF08--patch \u8986\u76D6\u5DF2\u6709\u6A21\u677F\uFF09").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode | trae | codex").option("--patch", "\u8986\u76D6\u5DF2\u6709\u6A21\u677F\u6587\u4EF6\uFF08\u4E0D\u78B0 plans/specs/reviews\uFF09").option("-i, --interactive", "\u4EA4\u4E92\u5F0F\u9009\u62E9\u8981\u540C\u6B65\u7684\u6587\u4EF6").action(syncCommand);
2165
+ program.command("init").description("\u521D\u59CB\u5316 ADD \u6A21\u677F\u5230\u5F53\u524D\u9879\u76EE").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode | trae | codex").option("--config <path>", "\u6307\u5B9A\u914D\u7F6E\u6587\u4EF6\u8DEF\u5F84").option("--force", "\u8986\u76D6\u5DF2\u6709\u6587\u4EF6\uFF0C\u4E0D\u4EA4\u4E92").option("--dry-run", "\u9884\u89C8\u6A21\u5F0F\uFF0C\u4E0D\u5B9E\u9645\u5199\u5165").option("--stack <name>", "\u6280\u672F\u6808\u7EA6\u675F profile \u540D\uFF08\u5982 machineserver\uFF0C\u53EF\u9009\uFF09").option("--skip-model", "\u8DF3\u8FC7 embedding \u6A21\u578B\u9884\u4E0B\u8F7D").action(initCommand);
2166
+ program.command("sync").description("\u589E\u91CF\u540C\u6B65\u7F3A\u5931\u6587\u4EF6\uFF08--patch \u8986\u76D6\u5DF2\u6709\u6A21\u677F\uFF09").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode | trae | codex").option("--patch", "\u8986\u76D6\u5DF2\u6709\u6A21\u677F\u6587\u4EF6\uFF08\u4E0D\u78B0 plans/specs/reviews\uFF09").option("-i, --interactive", "\u4EA4\u4E92\u5F0F\u9009\u62E9\u8981\u540C\u6B65\u7684\u6587\u4EF6").option("--model", "\u68C0\u6D4B\u5230\u7F3A\u5931\u65F6\u4E0B\u8F7D embedding \u6A21\u578B").action(syncCommand);
1861
2167
  program.command("status").description("\u68C0\u67E5 ADD \u6A21\u677F\u5B8C\u6574\u6027").action(statusCommand);
1862
2168
  program.command("stack").description("\u7BA1\u7406\u6280\u672F\u6808\u7EA6\u675F profile\uFF08list / set <name> / show / --clear\uFF09").argument("[sub]", "list | set | show").argument("[name]", "set <name>: profile \u540D\uFF08\u5185\u7F6E\u6216\u81EA\u5B9A\u4E49\uFF09").option("--adapter <type>", "\u76EE\u6807 IDE: claude | qoder | vscode | trae | codex").option("--clear", "\u6E05\u9664\u6280\u672F\u6808\u8BBE\u7F6E\uFF08\u4E2D\u6027\uFF09").action(
1863
2169
  (sub, name, options) => stackCommand(sub, name, options)
1864
2170
  );
2171
+ program.command("model:download").description("\u9884\u4E0B\u8F7D embedding \u6A21\u578B\uFF08\u9996\u6B21 DPS \u8C03\u7528\u4F1A\u81EA\u52A8\u4E0B\u8F7D\uFF0C\u672C\u547D\u4EE4\u63D0\u524D\u62C9\u53D6\uFF09").option("--force", "\u5F3A\u5236\u91CD\u65B0\u4E0B\u8F7D\uFF08\u5373\u4F7F\u7F13\u5B58\u5DF2\u5B58\u5728\uFF09").action(modelDownloadCommand);
1865
2172
  program.parse();