add-coder 0.3.19 → 0.3.20-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/index.js +380 -128
- package/package.json +3 -3
- package/templates/.add-coder-src-hash.json +12 -11
- package/templates/adapters/qoder/sync-policy.json +1 -1
- package/templates/core/scripts/mcp-server/resources/add-coder-version.ts +4 -3
- package/templates/core/scripts/mcp-server/shared/fs.ts +9 -4
- package/templates/core/scripts/mcp-server/shared/prisma.ts +10 -0
- package/templates/core/scripts/mcp-server/shared/run-command.ts +57 -0
- package/templates/core/scripts/mcp-server/tools/gateway/check_dps.ts +4 -1
- package/templates/core/scripts/mcp-server/tools/gateway/check_rahs.ts +4 -5
- package/templates/core/scripts/mcp-server/tools/gateway/check_spec_sync.ts +4 -5
- package/templates/core/scripts/mcp-server/tools/gateway/helpers.ts +7 -0
- package/templates/core/templates/01-/346/236/266/346/236/204//343/200/212ADD/345/274/200/345/217/221/345/267/245/344/275/234/350/267/257/345/276/204/344/270/216/346/226/207/346/241/243/345/215/217/345/220/214/350/247/204/350/214/203/343/200/213.md +1 -1
- package/templates/core/vocabulary/add-governance-vocabulary.md +1 -1
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
|
|
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((
|
|
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
|
-
|
|
228
|
+
resolve10(result);
|
|
229
229
|
});
|
|
230
230
|
});
|
|
231
231
|
}
|
|
@@ -649,9 +649,38 @@ import { resolve as resolve4, dirname as dirname3 } from "path";
|
|
|
649
649
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
650
650
|
|
|
651
651
|
// src/caijuehub/strategies/prisma.strategy.ts
|
|
652
|
-
import { spawnSync } from "child_process";
|
|
653
652
|
import { copyFileSync, existsSync as existsSync7, readFileSync as readFileSync5, unlinkSync, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
|
|
654
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
|
|
655
684
|
var PRISMA_CONFIG = {
|
|
656
685
|
onMissing: "ask",
|
|
657
686
|
onExistingAddPrisma: "ask",
|
|
@@ -679,12 +708,10 @@ function ensurePrismaConfig(projectRoot) {
|
|
|
679
708
|
].join("\n") + "\n", "utf-8");
|
|
680
709
|
}
|
|
681
710
|
function backupAddTables(projectRoot) {
|
|
682
|
-
|
|
683
|
-
if (pgDump.status !== 0) return null;
|
|
711
|
+
if (!commandExists("pg_dump")) return null;
|
|
684
712
|
const bak = resolve3(projectRoot, `add-backup-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19)}.sql`);
|
|
685
|
-
const r =
|
|
713
|
+
const r = runCommand("pg_dump", ["--table=AddUser", "--table=DevOperation", "--table=AuditLog", "--if-exists"], {
|
|
686
714
|
cwd: projectRoot,
|
|
687
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
688
715
|
timeout: 3e4
|
|
689
716
|
});
|
|
690
717
|
if (r.stdout.length > 0) {
|
|
@@ -695,16 +722,18 @@ function backupAddTables(projectRoot) {
|
|
|
695
722
|
return null;
|
|
696
723
|
}
|
|
697
724
|
function runPrismaInit(projectRoot, provider, schemaPath) {
|
|
698
|
-
console.log("\u6267\u884C
|
|
725
|
+
console.log("\u6267\u884C prisma init ...");
|
|
699
726
|
const pm = detectPm(projectRoot);
|
|
700
|
-
const initArgs = pm === "pnpm" ? ["dlx", "prisma", "init", "--datasource-provider", provider] : ["prisma", "init", "--datasource-provider", provider];
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
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
|
+
}
|
|
706
735
|
if (initResult.status !== 0 || !existsSync7(schemaPath)) {
|
|
707
|
-
console.
|
|
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`);
|
|
708
737
|
const prismaDir = resolve3(projectRoot, "prisma");
|
|
709
738
|
if (!existsSync7(prismaDir)) mkdirSync3(prismaDir, { recursive: true });
|
|
710
739
|
const content = `generator client {
|
|
@@ -744,6 +773,28 @@ function postInitSetup(projectRoot, schemaPath, addPrismaTemplate, destPath) {
|
|
|
744
773
|
}
|
|
745
774
|
copyFileSync(addPrismaTemplate, destPath);
|
|
746
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");
|
|
747
798
|
}
|
|
748
799
|
async function injectPrisma(projectRoot, addPrismaTemplate, options = {}) {
|
|
749
800
|
const C = PRISMA_CONFIG;
|
|
@@ -808,10 +859,10 @@ async function injectPrisma(projectRoot, addPrismaTemplate, options = {}) {
|
|
|
808
859
|
ensurePrismaConfig(projectRoot);
|
|
809
860
|
backupAddTables(projectRoot);
|
|
810
861
|
const pm = detectPm(projectRoot);
|
|
811
|
-
const args = pm === "pnpm" ? ["dlx", "prisma", "db", "push"] : ["prisma", "db", "push"];
|
|
862
|
+
const args = pm === "pnpm" ? ["dlx", "prisma", "db", "push"] : ["exec", "prisma", "--", "db", "push"];
|
|
812
863
|
if (C.schemaArg) args.push(C.schemaArg);
|
|
813
864
|
console.log(`\u6267\u884C ${pm} ${args.join(" ")} ...`);
|
|
814
|
-
const r =
|
|
865
|
+
const r = runCommand(pm, args, { cwd: projectRoot });
|
|
815
866
|
if (r.status !== 0) throw new Error(`prisma db push \u9000\u51FA\u7801: ${r.status}`);
|
|
816
867
|
} catch (err) {
|
|
817
868
|
if (C.onMigrateFail === "keep") {
|
|
@@ -827,8 +878,14 @@ async function injectPrisma(projectRoot, addPrismaTemplate, options = {}) {
|
|
|
827
878
|
}
|
|
828
879
|
if (C.autoGenerate) {
|
|
829
880
|
const pm = detectPm(projectRoot);
|
|
881
|
+
const genArgs = pm === "pnpm" ? ["dlx", "prisma", "generate"] : ["exec", "prisma", "--", "generate"];
|
|
830
882
|
console.log("\u6267\u884C prisma generate ...");
|
|
831
|
-
|
|
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
|
+
}
|
|
832
889
|
}
|
|
833
890
|
console.log("ADD \u6CBB\u7406\u6A21\u578B\u5DF2\u5C31\u7EEA");
|
|
834
891
|
return true;
|
|
@@ -843,11 +900,88 @@ async function injectPrisma2(projectRoot, options = {}) {
|
|
|
843
900
|
}
|
|
844
901
|
|
|
845
902
|
// src/cli/commands/init.ts
|
|
846
|
-
import { readFileSync as
|
|
903
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync9, mkdirSync as mkdirSync4, copyFileSync as copyFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
847
904
|
import { createHash } from "crypto";
|
|
848
|
-
import { resolve as
|
|
849
|
-
import { spawnSync as spawnSync2 } from "child_process";
|
|
905
|
+
import { resolve as resolve6 } from "path";
|
|
850
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
|
|
851
985
|
var ADAPTER_RENDERERS = {
|
|
852
986
|
claude: renderAdapter,
|
|
853
987
|
qoder: renderAdapter2,
|
|
@@ -866,9 +1000,17 @@ async function initCommand(options) {
|
|
|
866
1000
|
console.log(`[dry-run] \u5C06\u5199\u5165 ${ctx.magicDir}/stack.json \u2192 ${ctx.stack}`);
|
|
867
1001
|
}
|
|
868
1002
|
const result = await renderAndWrite(ctx);
|
|
869
|
-
await deployDatabase(ctx);
|
|
1003
|
+
const dbFail = await deployDatabase(ctx);
|
|
870
1004
|
deployDocs(ctx);
|
|
871
|
-
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
|
+
}
|
|
872
1014
|
}
|
|
873
1015
|
async function resolveAdapter(projectRoot, specified) {
|
|
874
1016
|
if (specified) {
|
|
@@ -923,27 +1065,31 @@ function portInUse(port) {
|
|
|
923
1065
|
}
|
|
924
1066
|
function hasPgIsready() {
|
|
925
1067
|
try {
|
|
926
|
-
const containers =
|
|
1068
|
+
const containers = runCommand("podman", ["ps", "--filter", "publish=5433", "--format", "{{.Names}}"], { timeout: 3e3 });
|
|
927
1069
|
const name = containers.stdout.toString().trim().split("\n")[0];
|
|
928
|
-
if (name &&
|
|
1070
|
+
if (name && runCommand("podman", ["exec", name, "pg_isready", "--version"], { timeout: 2e3 }).status === 0) return true;
|
|
929
1071
|
} catch {
|
|
930
1072
|
}
|
|
931
|
-
return
|
|
1073
|
+
return commandExists("pg_isready");
|
|
932
1074
|
}
|
|
933
1075
|
function testPostgresConnection(port, user, password, dbName) {
|
|
934
1076
|
if (!hasPgIsready()) {
|
|
935
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");
|
|
936
1078
|
return true;
|
|
937
1079
|
}
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
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
|
+
}
|
|
947
1093
|
}
|
|
948
1094
|
async function resolveDbCredentials(force) {
|
|
949
1095
|
const d = { user: "admin", password: "change-me-in-production", port: "5433" };
|
|
@@ -1006,8 +1152,8 @@ networks:
|
|
|
1006
1152
|
`;
|
|
1007
1153
|
}
|
|
1008
1154
|
function writeSqliteExportScript(projectRoot, dryRun) {
|
|
1009
|
-
const scriptsDir =
|
|
1010
|
-
const scriptPath =
|
|
1155
|
+
const scriptsDir = resolve6(projectRoot, "scripts");
|
|
1156
|
+
const scriptPath = resolve6(scriptsDir, "export-db.ts");
|
|
1011
1157
|
const content = `import { PrismaClient } from "@prisma/client";
|
|
1012
1158
|
import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
1013
1159
|
import { resolve } from "path";
|
|
@@ -1031,18 +1177,18 @@ main().catch((e) => { console.error(e); process.exit(1); });
|
|
|
1031
1177
|
console.log(`[dry-run] \u5C06\u5199\u5165 ${scriptPath}`);
|
|
1032
1178
|
return;
|
|
1033
1179
|
}
|
|
1034
|
-
if (!
|
|
1180
|
+
if (!existsSync9(scriptsDir)) mkdirSync4(scriptsDir, { recursive: true });
|
|
1035
1181
|
writeFileSync4(scriptPath, content, "utf-8");
|
|
1036
1182
|
console.log("\u5DF2\u751F\u6210 scripts/export-db.ts");
|
|
1037
1183
|
}
|
|
1038
1184
|
function injectDbExportScript(projectRoot, dryRun) {
|
|
1039
|
-
const pkgPath =
|
|
1040
|
-
if (!
|
|
1185
|
+
const pkgPath = resolve6(projectRoot, "package.json");
|
|
1186
|
+
if (!existsSync9(pkgPath)) return;
|
|
1041
1187
|
if (dryRun) {
|
|
1042
1188
|
console.log("[dry-run] \u5C06\u6CE8\u5165 db:export");
|
|
1043
1189
|
return;
|
|
1044
1190
|
}
|
|
1045
|
-
const pkg = JSON.parse(
|
|
1191
|
+
const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
|
|
1046
1192
|
if (!pkg.scripts) pkg.scripts = {};
|
|
1047
1193
|
if (!pkg.scripts["db:export"]) {
|
|
1048
1194
|
pkg.scripts["db:export"] = "npx tsx scripts/export-db.ts";
|
|
@@ -1089,15 +1235,15 @@ function writeComposeEnv(ctx) {
|
|
|
1089
1235
|
if (db.engine !== "postgresql" || !db.container || db.container === "manual") return;
|
|
1090
1236
|
if (!db.reuseExisting) {
|
|
1091
1237
|
const composeName = db.container === "podman" ? "podman-compose.add.yml" : "docker-compose.add.yml";
|
|
1092
|
-
const composePath =
|
|
1093
|
-
if (!options.dryRun && (!
|
|
1238
|
+
const composePath = resolve6(projectRoot, composeName);
|
|
1239
|
+
if (!options.dryRun && (!existsSync9(composePath) || options.force)) {
|
|
1094
1240
|
writeFileSync4(composePath, composeContent(config.projectName || "add-project"), "utf-8");
|
|
1095
1241
|
console.log(`\u5DF2\u521B\u5EFA ${composeName}`);
|
|
1096
1242
|
}
|
|
1097
1243
|
}
|
|
1098
|
-
const devEnvPath =
|
|
1099
|
-
if (!options.dryRun &&
|
|
1100
|
-
const existing =
|
|
1244
|
+
const devEnvPath = resolve6(projectRoot, ".env.development");
|
|
1245
|
+
if (!options.dryRun && existsSync9(devEnvPath)) {
|
|
1246
|
+
const existing = readFileSync7(devEnvPath, "utf-8");
|
|
1101
1247
|
if (!/^DATABASE_USER=/m.test(existing)) {
|
|
1102
1248
|
writeFileSync4(devEnvPath, existing + `
|
|
1103
1249
|
DATABASE_USER=${db.user || "admin"}
|
|
@@ -1136,8 +1282,8 @@ async function renderAndWrite(ctx) {
|
|
|
1136
1282
|
console.log(`claude adapter (via Agent Host): ${claudeFiles.size} \u6587\u4EF6`);
|
|
1137
1283
|
}
|
|
1138
1284
|
for (const d of [".add", magicDir]) {
|
|
1139
|
-
const reviewsDir =
|
|
1140
|
-
if (!
|
|
1285
|
+
const reviewsDir = resolve6(projectRoot, d, "reviews");
|
|
1286
|
+
if (!existsSync9(reviewsDir)) {
|
|
1141
1287
|
if (dry) {
|
|
1142
1288
|
console.log(`[dry-run] \u5C06\u521B\u5EFA ${reviewsDir}/`);
|
|
1143
1289
|
} else {
|
|
@@ -1149,16 +1295,16 @@ async function renderAndWrite(ctx) {
|
|
|
1149
1295
|
const hashMap = {};
|
|
1150
1296
|
let npmVer = "";
|
|
1151
1297
|
try {
|
|
1152
|
-
npmVer = JSON.parse(
|
|
1298
|
+
npmVer = JSON.parse(readFileSync7(resolve6(projectRoot, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json"), "utf-8"))._version ?? "";
|
|
1153
1299
|
} catch {
|
|
1154
1300
|
}
|
|
1155
1301
|
for (const [rp, c] of allFiles) {
|
|
1156
1302
|
hashMap[rp] = createHash("sha256").update(c).digest("hex").slice(0, 8);
|
|
1157
1303
|
}
|
|
1158
|
-
const hashOut =
|
|
1304
|
+
const hashOut = resolve6(projectRoot, magicDir, ".add-coder-hash.json");
|
|
1159
1305
|
writeFileSync4(hashOut, JSON.stringify(hashMap, null, 2) + "\n", "utf-8");
|
|
1160
1306
|
if (npmVer) {
|
|
1161
|
-
writeFileSync4(
|
|
1307
|
+
writeFileSync4(resolve6(projectRoot, magicDir, ".add-coder-version"), npmVer + "\n", "utf-8");
|
|
1162
1308
|
}
|
|
1163
1309
|
console.log(`hash: ${Object.keys(hashMap).length} entries \u2192 ${magicDir}/.add-coder-hash.json`);
|
|
1164
1310
|
}
|
|
@@ -1166,22 +1312,37 @@ async function renderAndWrite(ctx) {
|
|
|
1166
1312
|
}
|
|
1167
1313
|
async function deployDatabase(ctx) {
|
|
1168
1314
|
const { projectRoot, options, magicDir, config, db } = ctx;
|
|
1169
|
-
if (options.dryRun) return;
|
|
1315
|
+
if (options.dryRun) return null;
|
|
1316
|
+
let fail = null;
|
|
1170
1317
|
if (db.engine === "postgresql" && db.container && db.container !== "manual") {
|
|
1171
|
-
const dbScript =
|
|
1318
|
+
const dbScript = resolve6(projectRoot, magicDir, "scripts", "db-ensure.sh");
|
|
1172
1319
|
const dbEnv = { ...process.env, DATABASE_USER: db.user, DATABASE_PASSWORD: db.password, DATABASE_PORT: db.port, PROJECT_NAME: config.projectName };
|
|
1173
1320
|
const mode = db.reuseExisting ? "manual" : db.container;
|
|
1174
1321
|
console.log(db.reuseExisting ? "\u590D\u7528\u5DF2\u6709 PostgreSQL ..." : `\u90E8\u7F72\u6570\u636E\u5E93 (${db.container}) ...`);
|
|
1175
|
-
|
|
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
|
+
}
|
|
1176
1329
|
try {
|
|
1177
1330
|
await injectPrisma2(projectRoot, { force: !!options.force });
|
|
1178
1331
|
} catch (e) {
|
|
1179
|
-
|
|
1332
|
+
fail = `Prisma \u540C\u6B65\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`;
|
|
1180
1333
|
}
|
|
1181
1334
|
}
|
|
1182
1335
|
if (db.engine === "postgresql" && db.container === "manual") {
|
|
1183
|
-
const dbScript =
|
|
1184
|
-
if (
|
|
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
|
+
}
|
|
1185
1346
|
console.log([
|
|
1186
1347
|
"",
|
|
1187
1348
|
"\u2501".repeat(30),
|
|
@@ -1209,25 +1370,26 @@ async function deployDatabase(ctx) {
|
|
|
1209
1370
|
try {
|
|
1210
1371
|
await injectPrisma2(projectRoot, { force: !!options.force, datasource: "sqlite" });
|
|
1211
1372
|
} catch (e) {
|
|
1212
|
-
|
|
1373
|
+
fail = `SQLite \u540C\u6B65\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`;
|
|
1213
1374
|
}
|
|
1214
1375
|
}
|
|
1376
|
+
return fail;
|
|
1215
1377
|
}
|
|
1216
1378
|
function deployDocs(ctx) {
|
|
1217
1379
|
const { projectRoot, options, config } = ctx;
|
|
1218
1380
|
if (options.dryRun) return;
|
|
1219
1381
|
const pn = config.projectName || "add-project";
|
|
1220
|
-
const docsBase =
|
|
1221
|
-
const groundingSrc =
|
|
1382
|
+
const docsBase = resolve6(projectRoot, "docs", pn, "knowledge");
|
|
1383
|
+
const groundingSrc = resolve6(import.meta.dirname, "../templates/core/templates");
|
|
1222
1384
|
for (const d of ["00-\u9700\u6C42", "01-\u67B6\u6784", "02-\u89C4\u8303"]) {
|
|
1223
|
-
const srcDir =
|
|
1224
|
-
const destDir =
|
|
1225
|
-
if (!
|
|
1226
|
-
if (!
|
|
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;
|
|
1227
1389
|
for (const f of readdirSync2(srcDir)) {
|
|
1228
|
-
const src =
|
|
1229
|
-
const dest =
|
|
1230
|
-
if (
|
|
1390
|
+
const src = resolve6(srcDir, f);
|
|
1391
|
+
const dest = resolve6(destDir, f);
|
|
1392
|
+
if (existsSync9(dest)) continue;
|
|
1231
1393
|
try {
|
|
1232
1394
|
copyFileSync2(src, dest);
|
|
1233
1395
|
} catch {
|
|
@@ -1235,30 +1397,51 @@ function deployDocs(ctx) {
|
|
|
1235
1397
|
}
|
|
1236
1398
|
}
|
|
1237
1399
|
}
|
|
1238
|
-
function finalize(ctx, result) {
|
|
1400
|
+
function finalize(ctx, result, dbFail) {
|
|
1239
1401
|
const { projectRoot, options, db } = ctx;
|
|
1240
|
-
|
|
1402
|
+
if (options.dryRun) {
|
|
1403
|
+
console.log(`
|
|
1241
1404
|
\u5B8C\u6210: \u65B0\u5EFA ${result.created}, \u8DF3\u8FC7 ${result.skipped}, \u8986\u76D6 ${result.overwritten}`);
|
|
1242
|
-
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1243
1407
|
if (db.engine === "sqlite") console.log("\u6570\u636E\u5907\u4EFD: npm run db:export \u2192 data/exports/");
|
|
1244
|
-
const pkg = JSON.parse(
|
|
1408
|
+
const pkg = JSON.parse(readFileSync7(resolve6(import.meta.dirname, "../package.json"), "utf-8"));
|
|
1245
1409
|
const peerNames = Object.keys(pkg.peerDependencies || {});
|
|
1246
1410
|
if (peerNames.length > 0) {
|
|
1247
1411
|
console.log(`
|
|
1248
1412
|
\u5B89\u88C5 peer \u4F9D\u8D56 (${peerNames.join(" ")}) ...`);
|
|
1249
1413
|
const pm = detectPm(projectRoot);
|
|
1250
|
-
|
|
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
|
+
}
|
|
1251
1421
|
}
|
|
1252
1422
|
if (db.engine !== "manual" && (db.engine !== "postgresql" || db.container !== "manual")) {
|
|
1253
1423
|
console.log("\u63D0\u793A: \u91CD\u542F IDE \u4EE5\u52A0\u8F7D hook \u914D\u7F6E");
|
|
1254
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}`);
|
|
1255
1433
|
}
|
|
1256
1434
|
|
|
1257
1435
|
// src/cli/commands/sync.ts
|
|
1258
|
-
import { existsSync as
|
|
1259
|
-
import { resolve as
|
|
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";
|
|
1260
1438
|
import { createHash as createHash2 } from "crypto";
|
|
1261
1439
|
|
|
1440
|
+
// src/lib/path-normalize.ts
|
|
1441
|
+
function normalizeRelPath(p) {
|
|
1442
|
+
return p.replaceAll("\\", "/");
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1262
1445
|
// src/caijuehub/strategies/sync.strategy.ts
|
|
1263
1446
|
var SYNC_CONFIG = {
|
|
1264
1447
|
PATCH_GUARD: [/[/]plans[/]/, /[/]specs[/]/, /[/]reviews[/]/, /[/]rules[/]profiles[/]/],
|
|
@@ -1302,32 +1485,65 @@ function resolveAdapter2(projectRoot, specified) {
|
|
|
1302
1485
|
return "qoder";
|
|
1303
1486
|
}
|
|
1304
1487
|
function isUserData(p) {
|
|
1305
|
-
return SYNC_CONFIG.PATCH_GUARD.some((r) => r.test(p));
|
|
1488
|
+
return SYNC_CONFIG.PATCH_GUARD.some((r) => r.test(normalizeRelPath(p)));
|
|
1306
1489
|
}
|
|
1307
1490
|
function hash8(c) {
|
|
1308
1491
|
return createHash2("sha256").update(c).digest("hex").slice(0, SYNC_CONFIG.HASH_HEX_LENGTH);
|
|
1309
1492
|
}
|
|
1310
1493
|
function loadHashFile(root, magic) {
|
|
1311
1494
|
try {
|
|
1312
|
-
|
|
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;
|
|
1313
1499
|
} catch {
|
|
1314
1500
|
return {};
|
|
1315
1501
|
}
|
|
1316
1502
|
}
|
|
1317
1503
|
function loadVersionFile(root, magic) {
|
|
1318
1504
|
try {
|
|
1319
|
-
return
|
|
1505
|
+
return readFileSync8(resolve7(root, magic, SYNC_CONFIG.VERSION_SENTINEL), "utf-8").trim();
|
|
1320
1506
|
} catch {
|
|
1321
1507
|
return "";
|
|
1322
1508
|
}
|
|
1323
1509
|
}
|
|
1324
1510
|
function saveVersionFile(root, magic, version2) {
|
|
1325
|
-
writeFileSync5(
|
|
1511
|
+
writeFileSync5(resolve7(root, magic, SYNC_CONFIG.VERSION_SENTINEL), version2 + "\n", "utf-8");
|
|
1326
1512
|
}
|
|
1327
1513
|
function saveHashFile(root, magic, files) {
|
|
1328
1514
|
const m = {};
|
|
1329
|
-
for (const [p, c] of files) m[p] =
|
|
1330
|
-
writeFileSync5(
|
|
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
|
+
}
|
|
1331
1547
|
}
|
|
1332
1548
|
async function syncCommand(options = {}) {
|
|
1333
1549
|
const projectRoot = process.cwd();
|
|
@@ -1370,10 +1586,10 @@ async function syncCommand(options = {}) {
|
|
|
1370
1586
|
candidates.set(p, c);
|
|
1371
1587
|
}
|
|
1372
1588
|
const outHash = loadHashFile(projectRoot, magicDir);
|
|
1373
|
-
const srcHashPath =
|
|
1589
|
+
const srcHashPath = resolve7(projectRoot, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json");
|
|
1374
1590
|
let npmVersion = "";
|
|
1375
1591
|
try {
|
|
1376
|
-
npmVersion = JSON.parse(
|
|
1592
|
+
npmVersion = JSON.parse(readFileSync8(srcHashPath, "utf-8"))._version ?? "";
|
|
1377
1593
|
} catch {
|
|
1378
1594
|
}
|
|
1379
1595
|
const installedVersion = loadVersionFile(projectRoot, magicDir);
|
|
@@ -1392,18 +1608,19 @@ async function syncCommand(options = {}) {
|
|
|
1392
1608
|
const conflictFiles = /* @__PURE__ */ new Map();
|
|
1393
1609
|
let sameCount = 0;
|
|
1394
1610
|
for (const [relPath, content] of candidates) {
|
|
1395
|
-
const
|
|
1396
|
-
|
|
1397
|
-
|
|
1611
|
+
const key = normalizeRelPath(relPath);
|
|
1612
|
+
const absPath = resolve7(projectRoot, relPath);
|
|
1613
|
+
if (!existsSync10(absPath)) {
|
|
1614
|
+
missingFiles.set(key, content);
|
|
1398
1615
|
} else if (establishBaseline) {
|
|
1399
|
-
missingFiles.set(
|
|
1616
|
+
missingFiles.set(key, content);
|
|
1400
1617
|
} else {
|
|
1401
|
-
const curH = hash8(
|
|
1402
|
-
const storedH = outHash[
|
|
1618
|
+
const curH = hash8(readFileSync8(absPath, "utf-8"));
|
|
1619
|
+
const storedH = outHash[key];
|
|
1403
1620
|
if (storedH && curH === storedH) {
|
|
1404
1621
|
sameCount++;
|
|
1405
1622
|
} else {
|
|
1406
|
-
conflictFiles.set(
|
|
1623
|
+
conflictFiles.set(key, content);
|
|
1407
1624
|
}
|
|
1408
1625
|
}
|
|
1409
1626
|
}
|
|
@@ -1424,14 +1641,20 @@ async function syncCommand(options = {}) {
|
|
|
1424
1641
|
if (missingFiles.size === 0 && conflictFiles.size === 0) {
|
|
1425
1642
|
console.log(SYNC_CONFIG.PROMPT_PATCH_DONE);
|
|
1426
1643
|
}
|
|
1427
|
-
|
|
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);
|
|
1428
1650
|
saveVersionFile(projectRoot, magicDir, npmVersion);
|
|
1429
1651
|
await checkPrismaDiff(projectRoot, options);
|
|
1652
|
+
await maybeModelDownload(options);
|
|
1430
1653
|
return;
|
|
1431
1654
|
}
|
|
1432
1655
|
const missing = /* @__PURE__ */ new Map();
|
|
1433
1656
|
for (const [relPath, content] of allFiles) {
|
|
1434
|
-
if (!
|
|
1657
|
+
if (!existsSync10(resolve7(projectRoot, relPath))) {
|
|
1435
1658
|
missing.set(relPath, content);
|
|
1436
1659
|
}
|
|
1437
1660
|
}
|
|
@@ -1450,10 +1673,11 @@ async function syncCommand(options = {}) {
|
|
|
1450
1673
|
const result = await writeFiles2(projectRoot, filesToWrite, { yes: true });
|
|
1451
1674
|
console.log(`\u540C\u6B65\u5B8C\u6210: \u65B0\u5EFA ${result.created}, \u8DF3\u8FC7 ${result.skipped}`);
|
|
1452
1675
|
await checkPrismaDiff(projectRoot, options);
|
|
1676
|
+
await maybeModelDownload(options);
|
|
1453
1677
|
}
|
|
1454
1678
|
function printMigrateGuidance(targetPath, changed) {
|
|
1455
1679
|
const g = SYNC_PRISMA_CONFIG.POST_SYNC;
|
|
1456
|
-
const isManaged =
|
|
1680
|
+
const isManaged = existsSync10(resolve7(dirname4(targetPath), "migrations"));
|
|
1457
1681
|
const actions = isManaged ? g.MANAGED_ACTIONS : g.UNMANAGED_ACTIONS;
|
|
1458
1682
|
console.log(` \u25B6 \u5DF2\u5199\u5165 ${changed} \u5904\u53D8\u66F4\uFF0C${g.HEADER}`);
|
|
1459
1683
|
for (const a of actions) {
|
|
@@ -1475,9 +1699,9 @@ function printMigrateGuidance(targetPath, changed) {
|
|
|
1475
1699
|
}
|
|
1476
1700
|
async function checkPrismaDiff(projectRoot, options) {
|
|
1477
1701
|
if (!options.patch) return;
|
|
1478
|
-
const basePath =
|
|
1479
|
-
const targetPath =
|
|
1480
|
-
if (!
|
|
1702
|
+
const basePath = resolve7(projectRoot, SYNC_PRISMA_CONFIG.BASE_SCHEMA);
|
|
1703
|
+
const targetPath = resolve7(projectRoot, SYNC_PRISMA_CONFIG.TARGET_PATTERN);
|
|
1704
|
+
if (!existsSync10(basePath)) {
|
|
1481
1705
|
console.log(`
|
|
1482
1706
|
\u26A0\uFE0F \u57FA\u51C6 schema \u4E0D\u5B58\u5728: ${basePath}`);
|
|
1483
1707
|
console.log(` \u8BF7\u786E\u4FDD add-coder \u5DF2\u6B63\u786E\u5B89\u88C5\u3002`);
|
|
@@ -1489,7 +1713,7 @@ async function checkPrismaDiff(projectRoot, options) {
|
|
|
1489
1713
|
\u2705 Prisma schema \u4E0E add-coder \u6807\u51C6\u4E00\u81F4\uFF0C\u65E0\u9700\u540C\u6B65\u3002`);
|
|
1490
1714
|
return;
|
|
1491
1715
|
}
|
|
1492
|
-
const targetExists =
|
|
1716
|
+
const targetExists = existsSync10(targetPath);
|
|
1493
1717
|
let modifiedCount = 0;
|
|
1494
1718
|
console.log(`
|
|
1495
1719
|
\u26A0\uFE0F Prisma schema \u5DEE\u5F02\u68C0\u6D4B:`);
|
|
@@ -1640,7 +1864,7 @@ async function handleDiffAction(action, ctx) {
|
|
|
1640
1864
|
}
|
|
1641
1865
|
}
|
|
1642
1866
|
function injectMissingModels(targetPath, models) {
|
|
1643
|
-
let content =
|
|
1867
|
+
let content = readFileSync8(targetPath, "utf-8");
|
|
1644
1868
|
content = content.replace(/\n*$/, "\n");
|
|
1645
1869
|
content += `
|
|
1646
1870
|
// ===== \u7531 add-coder sync --patch \u81EA\u52A8\u6CE8\u5165 (${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}) =====
|
|
@@ -1653,7 +1877,7 @@ function injectMissingModels(targetPath, models) {
|
|
|
1653
1877
|
return models.length;
|
|
1654
1878
|
}
|
|
1655
1879
|
function getBaseFieldLines(basePath, modelName) {
|
|
1656
|
-
const content =
|
|
1880
|
+
const content = readFileSync8(basePath, "utf-8");
|
|
1657
1881
|
const blocks = parseSchemaBlocks(content);
|
|
1658
1882
|
const block = blocks.get(`model:${modelName}`) ?? blocks.get(`enum:${modelName}`);
|
|
1659
1883
|
if (!block) return {};
|
|
@@ -1676,7 +1900,7 @@ function injectFieldLines(targetPath, basePath, modelName, fieldKeys) {
|
|
|
1676
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`);
|
|
1677
1901
|
return 0;
|
|
1678
1902
|
}
|
|
1679
|
-
const content =
|
|
1903
|
+
const content = readFileSync8(targetPath, "utf-8");
|
|
1680
1904
|
const lines = content.split("\n");
|
|
1681
1905
|
const blocks = parseSchemaBlocks(content);
|
|
1682
1906
|
const target = blocks.get(`model:${modelName}`) ?? blocks.get(`enum:${modelName}`);
|
|
@@ -1723,7 +1947,7 @@ function injectFieldLines(targetPath, basePath, modelName, fieldKeys) {
|
|
|
1723
1947
|
function overwriteFieldLines(targetPath, basePath, modelName, conflicts) {
|
|
1724
1948
|
const baseFields = getBaseFieldLines(basePath, modelName);
|
|
1725
1949
|
if (Object.keys(baseFields).length === 0) return 0;
|
|
1726
|
-
let content =
|
|
1950
|
+
let content = readFileSync8(targetPath, "utf-8");
|
|
1727
1951
|
let count = 0;
|
|
1728
1952
|
for (const { fieldName } of conflicts) {
|
|
1729
1953
|
const baseLine = baseFields[fieldName];
|
|
@@ -1739,8 +1963,8 @@ function overwriteFieldLines(targetPath, basePath, modelName, conflicts) {
|
|
|
1739
1963
|
}
|
|
1740
1964
|
|
|
1741
1965
|
// src/cli/commands/status.ts
|
|
1742
|
-
import { existsSync as
|
|
1743
|
-
import { resolve as
|
|
1966
|
+
import { existsSync as existsSync11 } from "fs";
|
|
1967
|
+
import { resolve as resolve8 } from "path";
|
|
1744
1968
|
async function statusCommand() {
|
|
1745
1969
|
const projectRoot = process.cwd();
|
|
1746
1970
|
const config = await loadConfig(projectRoot);
|
|
@@ -1749,7 +1973,7 @@ async function statusCommand() {
|
|
|
1749
1973
|
const missing = [];
|
|
1750
1974
|
const present = [];
|
|
1751
1975
|
for (const [relPath] of coreFiles) {
|
|
1752
|
-
if (
|
|
1976
|
+
if (existsSync11(resolve8(projectRoot, relPath))) {
|
|
1753
1977
|
present.push(relPath);
|
|
1754
1978
|
} else {
|
|
1755
1979
|
missing.push(relPath);
|
|
@@ -1760,14 +1984,15 @@ async function statusCommand() {
|
|
|
1760
1984
|
if (missing.length > 0) {
|
|
1761
1985
|
console.log(` \u7F3A\u5931: ${missing.length} \u6587\u4EF6`);
|
|
1762
1986
|
missing.forEach((f) => console.log(` - ${f}`));
|
|
1987
|
+
process.exit(1);
|
|
1763
1988
|
} else {
|
|
1764
1989
|
console.log(" \u6240\u6709\u6587\u4EF6\u5B8C\u6574\u3002");
|
|
1765
1990
|
}
|
|
1766
1991
|
}
|
|
1767
1992
|
|
|
1768
1993
|
// src/cli/commands/stack.ts
|
|
1769
|
-
import { readdirSync as readdirSync3, readFileSync as
|
|
1770
|
-
import { resolve as
|
|
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";
|
|
1771
1996
|
import { createHash as createHash3 } from "crypto";
|
|
1772
1997
|
var MAGIC_DIR_MAP3 = { claude: ".claude", qoder: ".qoder", vscode: ".vscode", trae: ".trae", codex: ".codex" };
|
|
1773
1998
|
var HASH_OUTPUT_FILE = ".add-coder-hash.json";
|
|
@@ -1784,14 +2009,14 @@ function resolveMagicDir(projectRoot, specified) {
|
|
|
1784
2009
|
return MAGIC_DIR_MAP3[adapter];
|
|
1785
2010
|
}
|
|
1786
2011
|
function listCustomProfiles(projectRoot, magicDir, registryNames) {
|
|
1787
|
-
const dir =
|
|
1788
|
-
if (!
|
|
2012
|
+
const dir = resolve9(projectRoot, magicDir, "rules", "profiles");
|
|
2013
|
+
if (!existsSync12(dir)) return [];
|
|
1789
2014
|
return readdirSync3(dir).filter((f) => f.endsWith(".md") && !registryNames.has(f.replace(/-profile\.md$/, ""))).sort();
|
|
1790
2015
|
}
|
|
1791
2016
|
function profileExists(projectRoot, magicDir, name) {
|
|
1792
2017
|
const registry = loadProfileRegistry();
|
|
1793
2018
|
if (registry.some((p) => p.name === name)) return true;
|
|
1794
|
-
return
|
|
2019
|
+
return existsSync12(resolve9(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`));
|
|
1795
2020
|
}
|
|
1796
2021
|
function stackCommand(sub, name, options = {}) {
|
|
1797
2022
|
const projectRoot = process.cwd();
|
|
@@ -1828,12 +2053,12 @@ function stackCommand(sub, name, options = {}) {
|
|
|
1828
2053
|
console.log("\u6280\u672F\u6808: \u672A\u8BBE\u7F6E\uFF08\u4E2D\u6027\uFF0C\u65E0\u6280\u672F\u6808\u5047\u8BBE\uFF09");
|
|
1829
2054
|
return;
|
|
1830
2055
|
}
|
|
1831
|
-
const profilePath =
|
|
1832
|
-
const stat =
|
|
2056
|
+
const profilePath = resolve9(projectRoot, magicDir, "rules", "profiles", `${current}-profile.md`);
|
|
2057
|
+
const stat = existsSync12(profilePath) ? readFileSync9(profilePath, "utf-8").length : 0;
|
|
1833
2058
|
console.log(`\u6280\u672F\u6808: ${current}`);
|
|
1834
|
-
console.log(`profile \u6587\u4EF6: ${profilePath}${
|
|
2059
|
+
console.log(`profile \u6587\u4EF6: ${profilePath}${existsSync12(profilePath) ? ` (${stat} \u5B57\u7B26)` : "\uFF08\u7F3A\u5931\uFF09"}`);
|
|
1835
2060
|
try {
|
|
1836
|
-
const raw = JSON.parse(
|
|
2061
|
+
const raw = JSON.parse(readFileSync9(resolve9(projectRoot, magicDir, "stack.json"), "utf-8"));
|
|
1837
2062
|
if (raw.updatedAt) console.log(`\u66F4\u65B0\u65F6\u95F4: ${raw.updatedAt}`);
|
|
1838
2063
|
} catch {
|
|
1839
2064
|
}
|
|
@@ -1858,9 +2083,9 @@ function buildConfig(projectRoot, magicDir, stack) {
|
|
|
1858
2083
|
stack
|
|
1859
2084
|
};
|
|
1860
2085
|
try {
|
|
1861
|
-
const pkgPath =
|
|
1862
|
-
if (
|
|
1863
|
-
const pkg = JSON.parse(
|
|
2086
|
+
const pkgPath = resolve9(projectRoot, "package.json");
|
|
2087
|
+
if (existsSync12(pkgPath)) {
|
|
2088
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
1864
2089
|
if (pkg.name) config.projectName = pkg.name;
|
|
1865
2090
|
}
|
|
1866
2091
|
} catch {
|
|
@@ -1869,9 +2094,9 @@ function buildConfig(projectRoot, magicDir, stack) {
|
|
|
1869
2094
|
}
|
|
1870
2095
|
function applyStack(projectRoot, magicDir, name) {
|
|
1871
2096
|
if (!profileExists(projectRoot, magicDir, name)) {
|
|
1872
|
-
const
|
|
2097
|
+
const registry2 = loadProfileRegistry();
|
|
1873
2098
|
console.error(`\u2717 profile \u4E0D\u5B58\u5728: ${name}`);
|
|
1874
|
-
console.error(` \u5185\u7F6E: ${
|
|
2099
|
+
console.error(` \u5185\u7F6E: ${registry2.map((p) => p.name).join(" | ")}\uFF08\u6216\u81EA\u5B9A\u4E49: ${magicDir}/rules/profiles/{name}-profile.md\uFF09`);
|
|
1875
2100
|
process.exit(1);
|
|
1876
2101
|
}
|
|
1877
2102
|
const config = buildConfig(projectRoot, magicDir, name);
|
|
@@ -1879,42 +2104,69 @@ function applyStack(projectRoot, magicDir, name) {
|
|
|
1879
2104
|
const coreFiles = renderCore(config, false);
|
|
1880
2105
|
const stackRelated = /* @__PURE__ */ new Map();
|
|
1881
2106
|
for (const [relPath, content] of coreFiles) {
|
|
1882
|
-
|
|
1883
|
-
|
|
2107
|
+
const rp = normalizeRelPath(relPath);
|
|
2108
|
+
if (rp.includes("/rules/profiles/") || rp.endsWith("/rules/project_rules.md")) {
|
|
2109
|
+
stackRelated.set(rp, content);
|
|
1884
2110
|
}
|
|
1885
2111
|
}
|
|
1886
2112
|
const hashMap = {};
|
|
1887
2113
|
try {
|
|
1888
|
-
Object.assign(hashMap, JSON.parse(
|
|
2114
|
+
Object.assign(hashMap, JSON.parse(readFileSync9(resolve9(projectRoot, magicDir, HASH_OUTPUT_FILE), "utf-8")));
|
|
1889
2115
|
} catch {
|
|
1890
2116
|
}
|
|
1891
2117
|
let written = 0;
|
|
1892
2118
|
for (const [relPath, content] of stackRelated) {
|
|
1893
2119
|
for (const t of [".add", magicDir]) {
|
|
1894
|
-
const targetPath =
|
|
1895
|
-
mkdirSync6(
|
|
2120
|
+
const targetPath = resolve9(projectRoot, relPath.replace(/^\.add/, t));
|
|
2121
|
+
mkdirSync6(join6(targetPath, ".."), { recursive: true });
|
|
1896
2122
|
writeFileSync6(targetPath, content, "utf-8");
|
|
1897
2123
|
hashMap[relPath.replace(/^\.add/, t)] = hash82(content);
|
|
1898
2124
|
written++;
|
|
1899
2125
|
}
|
|
1900
2126
|
}
|
|
1901
|
-
|
|
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");
|
|
1902
2142
|
console.log(`\u2705 \u6280\u672F\u6808\u5DF2\u8BBE\u7F6E\u4E3A ${name}`);
|
|
1903
2143
|
console.log(` ${magicDir}/stack.json \u2192 ${name}`);
|
|
1904
|
-
console.log(` ${magicDir}/rules/profiles/${name}-profile.md ${
|
|
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"}`);
|
|
1905
2145
|
console.log(` project_rules.md \u5F15\u7528\u884C\u5DF2\u66F4\u65B0 + hash \u5DF2\u5237\u65B0\uFF08${written} \u4E2A\u6587\u4EF6\uFF09`);
|
|
1906
2146
|
}
|
|
1907
2147
|
|
|
1908
2148
|
// src/cli/index.ts
|
|
1909
2149
|
var { version } = JSON.parse(
|
|
1910
|
-
|
|
2150
|
+
readFileSync10(new URL("../package.json", import.meta.url), "utf-8")
|
|
1911
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
|
+
}
|
|
1912
2163
|
var program = new Command();
|
|
1913
2164
|
program.name("add-coder").description("\u521D\u59CB\u5316 ADD \u8303\u5F0F\u5DE5\u4F5C\u6D41\u6A21\u677F").version(version);
|
|
1914
|
-
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);
|
|
1915
|
-
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);
|
|
1916
2167
|
program.command("status").description("\u68C0\u67E5 ADD \u6A21\u677F\u5B8C\u6574\u6027").action(statusCommand);
|
|
1917
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(
|
|
1918
2169
|
(sub, name, options) => stackCommand(sub, name, options)
|
|
1919
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);
|
|
1920
2172
|
program.parse();
|