add-coder 0.3.35 → 0.3.38
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.en.md +116 -43
- package/README.md +80 -21
- package/dist/index.js +465 -87
- package/package.json +7 -3
- package/templates/.add-coder-src-hash.json +76 -75
- package/templates/adapters/claude/hooks/doc-format-guard.mjs +4 -0
- package/templates/adapters/claude/hooks/notification.mjs +4 -0
- package/templates/adapters/claude/hooks/permission-denied.mjs +4 -0
- package/templates/adapters/claude/hooks/permission-gate.mjs +4 -0
- package/templates/adapters/claude/hooks/post-tool-use.mjs +4 -0
- package/templates/adapters/claude/hooks/pre-compact.mjs +4 -0
- package/templates/adapters/claude/hooks/pre-tool-use.mjs +4 -0
- package/templates/adapters/claude/hooks/prompt-submit.mjs +4 -0
- package/templates/adapters/claude/hooks/session-end.mjs +4 -0
- package/templates/adapters/claude/hooks/session-start.mjs +4 -0
- package/templates/adapters/claude/hooks/stop-check.mjs +87 -6
- package/templates/adapters/claude/hooks/stop-failure.mjs +4 -0
- package/templates/adapters/claude/hooks/subagent-guard.mjs +4 -0
- package/templates/adapters/claude/hooks/subagent-stop.mjs +4 -0
- package/templates/adapters/codex/hooks/doc-format-guard.mjs +4 -0
- package/templates/adapters/codex/hooks/notification.mjs +4 -0
- package/templates/adapters/codex/hooks/permission-gate.mjs +4 -0
- package/templates/adapters/codex/hooks/post-tool-failure.mjs +4 -0
- package/templates/adapters/codex/hooks/post-tool-use.mjs +4 -0
- package/templates/adapters/codex/hooks/pre-compact.mjs +4 -0
- package/templates/adapters/codex/hooks/pre-tool-use.mjs +4 -0
- package/templates/adapters/codex/hooks/prompt-submit.mjs +4 -0
- package/templates/adapters/codex/hooks/session-end.mjs +4 -0
- package/templates/adapters/codex/hooks/session-start.mjs +4 -0
- package/templates/adapters/codex/hooks/stop-check.mjs +87 -6
- package/templates/adapters/codex/hooks/subagent-guard.mjs +4 -0
- package/templates/adapters/codex/hooks/subagent-stop.mjs +4 -0
- package/templates/adapters/qoder/hooks/doc-format-guard.mjs +4 -0
- package/templates/adapters/qoder/hooks/notification.mjs +4 -0
- package/templates/adapters/qoder/hooks/permission-gate.mjs +4 -0
- package/templates/adapters/qoder/hooks/post-tool-failure.mjs +4 -0
- package/templates/adapters/qoder/hooks/post-tool-use.mjs +4 -0
- package/templates/adapters/qoder/hooks/pre-compact.mjs +4 -0
- package/templates/adapters/qoder/hooks/pre-tool-use.mjs +4 -0
- package/templates/adapters/qoder/hooks/prompt-submit.mjs +4 -0
- package/templates/adapters/qoder/hooks/review-checklist.mjs +4 -0
- package/templates/adapters/qoder/hooks/session-end.mjs +4 -0
- package/templates/adapters/qoder/hooks/session-start.mjs +4 -0
- package/templates/adapters/qoder/hooks/stop-check.mjs +87 -6
- package/templates/adapters/qoder/hooks/subagent-guard.mjs +4 -0
- package/templates/adapters/qoder/hooks/subagent-stop.mjs +4 -0
- package/templates/adapters/trae/hooks/doc-format-guard.mjs +4 -0
- package/templates/adapters/trae/hooks/notification.mjs +4 -0
- package/templates/adapters/trae/hooks/permission-gate.mjs +4 -0
- package/templates/adapters/trae/hooks/post-tool-failure.mjs +4 -0
- package/templates/adapters/trae/hooks/post-tool-use.mjs +4 -0
- package/templates/adapters/trae/hooks/pre-compact.mjs +4 -0
- package/templates/adapters/trae/hooks/pre-tool-use.mjs +4 -0
- package/templates/adapters/trae/hooks/prompt-submit.mjs +4 -0
- package/templates/adapters/trae/hooks/session-end.mjs +4 -0
- package/templates/adapters/trae/hooks/session-start.mjs +4 -0
- package/templates/adapters/trae/hooks/stop-check.mjs +87 -6
- package/templates/adapters/trae/hooks/subagent-guard.mjs +4 -0
- package/templates/adapters/trae/hooks/subagent-stop.mjs +4 -0
- package/templates/adapters/vscode/hooks/doc-format-guard.mjs +4 -0
- package/templates/adapters/vscode/hooks/notification.mjs +4 -0
- package/templates/adapters/vscode/hooks/permission-gate.mjs +4 -0
- package/templates/adapters/vscode/hooks/post-tool-failure.mjs +4 -0
- package/templates/adapters/vscode/hooks/post-tool-use.mjs +4 -0
- package/templates/adapters/vscode/hooks/pre-compact.mjs +4 -0
- package/templates/adapters/vscode/hooks/pre-tool-use.mjs +4 -0
- package/templates/adapters/vscode/hooks/prompt-submit.mjs +4 -0
- package/templates/adapters/vscode/hooks/session-end.mjs +4 -0
- package/templates/adapters/vscode/hooks/session-start.mjs +4 -0
- package/templates/adapters/vscode/hooks/stop-check.mjs +87 -6
- package/templates/adapters/vscode/hooks/subagent-guard.mjs +4 -0
- package/templates/adapters/vscode/hooks/subagent-stop.mjs +4 -0
- package/templates/core/governance/rules.ts +4 -0
- package/templates/core/governance/stop-router.ts +101 -7
- package/templates/core/scripts/db-ensure.sh +51 -4
- package/templates/core/scripts/mcp-server/shared/dps-spec-ref.ts +17 -0
- package/templates/core/scripts/mcp-server/shared/memory/retrieval/fts/sqlite-fts5.sql +7 -18
- package/templates/core/scripts/mcp-server/tools/gateway/check_dps.ts +3 -4
- package/templates/core/scripts/mcp-server/tools/gateway/check_rahs.ts +2 -1
- package/templates/core/scripts/mcp-server/tools/gateway/check_spec_sync.ts +7 -3
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 readFileSync14 } from "fs";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
|
|
7
7
|
// src/caijuehub/strategies/detect.strategy.ts
|
|
@@ -60,6 +60,24 @@ function magicDirFor(adapter) {
|
|
|
60
60
|
if (!m) throw new Error(`\u672A\u77E5 adapter: ${adapter}`);
|
|
61
61
|
return m;
|
|
62
62
|
}
|
|
63
|
+
function here() {
|
|
64
|
+
return import.meta.dirname;
|
|
65
|
+
}
|
|
66
|
+
function resolveMagicDir(cwd) {
|
|
67
|
+
const fromEnv = process.env.MAGIC_DIR;
|
|
68
|
+
if (fromEnv) return fromEnv;
|
|
69
|
+
const base = cwd ?? here();
|
|
70
|
+
const ide = detectIDE2(base);
|
|
71
|
+
if (ide !== "auto") {
|
|
72
|
+
const m = MAGIC_DIR_MAP[ide];
|
|
73
|
+
if (m && findUpSync(m, { cwd: base, type: "directory" })) return m;
|
|
74
|
+
}
|
|
75
|
+
for (const magic of Object.values(MAGIC_DIR_MAP)) {
|
|
76
|
+
const hit = findUpSync(magic, { cwd: base, type: "directory" });
|
|
77
|
+
if (hit) return magic;
|
|
78
|
+
}
|
|
79
|
+
return MAGIC_DIR_MAP.qoder;
|
|
80
|
+
}
|
|
63
81
|
|
|
64
82
|
// src/cli/config-loader.ts
|
|
65
83
|
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
@@ -200,7 +218,7 @@ async function selectFiles(projectRoot, files) {
|
|
|
200
218
|
process.stdout.write(renderList(items, selected, scrollIdx, VISIBLE));
|
|
201
219
|
}
|
|
202
220
|
draw();
|
|
203
|
-
return new Promise((
|
|
221
|
+
return new Promise((resolve14) => {
|
|
204
222
|
input.on("keypress", (_char, key) => {
|
|
205
223
|
if (!key) return;
|
|
206
224
|
if (key.name === "up") {
|
|
@@ -241,7 +259,7 @@ async function selectFiles(projectRoot, files) {
|
|
|
241
259
|
if (selected.has(idx)) result.set(relPath, content);
|
|
242
260
|
idx++;
|
|
243
261
|
}
|
|
244
|
-
|
|
262
|
+
resolve14(result);
|
|
245
263
|
});
|
|
246
264
|
});
|
|
247
265
|
}
|
|
@@ -1119,7 +1137,7 @@ async function ensureSplitDb(projectRoot, opts) {
|
|
|
1119
1137
|
`POSTGRES_DB=${dbName}`,
|
|
1120
1138
|
"-p",
|
|
1121
1139
|
`127.0.0.1:${port}:5432`,
|
|
1122
|
-
"docker.io/
|
|
1140
|
+
"docker.io/pgvector/pgvector:pg16"
|
|
1123
1141
|
], { timeout: 6e4 });
|
|
1124
1142
|
if (r.status !== 0) {
|
|
1125
1143
|
throw new Error(`\u72EC\u7ACB ADD \u5E93\u5BB9\u5668\u542F\u52A8\u5931\u8D25\uFF08\u9000\u51FA\u7801 ${r.status}\uFF09: ${r.stderr.trim().slice(0, 200)}`);
|
|
@@ -1183,7 +1201,7 @@ async function provisionDevUrl(projectRoot) {
|
|
|
1183
1201
|
"POSTGRES_DB=dev",
|
|
1184
1202
|
"-p",
|
|
1185
1203
|
`127.0.0.1:${port}:5432`,
|
|
1186
|
-
"docker.io/
|
|
1204
|
+
"docker.io/pgvector/pgvector:pg16"
|
|
1187
1205
|
], { timeout: 6e4 });
|
|
1188
1206
|
if (r.status !== 0) return null;
|
|
1189
1207
|
const url = `postgresql://postgres:postgres@127.0.0.1:${port}/dev?schema=public`;
|
|
@@ -1530,9 +1548,9 @@ async function injectPrisma2(projectRoot, options = {}) {
|
|
|
1530
1548
|
}
|
|
1531
1549
|
|
|
1532
1550
|
// src/cli/commands/init.ts
|
|
1533
|
-
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as
|
|
1551
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync6, existsSync as existsSync13, mkdirSync as mkdirSync6, copyFileSync as copyFileSync2, readdirSync as readdirSync5 } from "fs";
|
|
1534
1552
|
import { createHash } from "crypto";
|
|
1535
|
-
import { resolve as
|
|
1553
|
+
import { resolve as resolve9, dirname as dirname3, join as pathJoin } from "path";
|
|
1536
1554
|
import { homedir as homedir2 } from "os";
|
|
1537
1555
|
import { createConnection as createConnection2 } from "net";
|
|
1538
1556
|
|
|
@@ -1612,6 +1630,42 @@ async function ensureEmbeddingModel(options) {
|
|
|
1612
1630
|
return { status: "downloaded", model, cacheDir };
|
|
1613
1631
|
}
|
|
1614
1632
|
|
|
1633
|
+
// src/lib/memory-expected-state.ts
|
|
1634
|
+
import { existsSync as existsSync12 } from "fs";
|
|
1635
|
+
import { join as join9, resolve as resolve8 } from "path";
|
|
1636
|
+
function expectedStateSqlRelPath(magicDir) {
|
|
1637
|
+
return join9(magicDir, "scripts", "mcp-server", "shared", "memory", "retrieval", "fts", "sqlite-fts5.sql");
|
|
1638
|
+
}
|
|
1639
|
+
function applyMemoryExpectedState(opts) {
|
|
1640
|
+
const { projectRoot, magicDir, engine } = opts;
|
|
1641
|
+
const relSql = expectedStateSqlRelPath(magicDir);
|
|
1642
|
+
const manualCmd = `npx prisma db execute --file ${relSql}`;
|
|
1643
|
+
if (engine !== "sqlite") return { applicable: false, ok: true, relSql, manualCmd };
|
|
1644
|
+
const absSql = resolve8(projectRoot, relSql);
|
|
1645
|
+
if (!existsSync12(absSql)) {
|
|
1646
|
+
return {
|
|
1647
|
+
applicable: true,
|
|
1648
|
+
ok: false,
|
|
1649
|
+
relSql,
|
|
1650
|
+
manualCmd,
|
|
1651
|
+
detail: `\u671F\u671B\u6001 SQL \u672A\u627E\u5230\uFF08${relSql}\uFF09\u2014\u2014\u8BF7\u5148\u6267\u884C add-coder sync \u540E\u91CD\u8DD1`
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
const pm = detectPm(projectRoot);
|
|
1655
|
+
const argv = pm === "pnpm" ? ["exec", "prisma", "db", "execute", "--file", absSql] : ["exec", "prisma", "--", "db", "execute", "--file", absSql];
|
|
1656
|
+
const run = opts.run ?? ((a, cwd) => runCommand(pm, a, { cwd, timeout: opts.timeoutMs ?? 6e4 }));
|
|
1657
|
+
try {
|
|
1658
|
+
const r = run(argv, projectRoot);
|
|
1659
|
+
if (r.status !== 0) {
|
|
1660
|
+
const why = (r.stderr || "").trim().split("\n").slice(0, 2).join(" | ");
|
|
1661
|
+
return { applicable: true, ok: false, relSql, manualCmd, detail: `\u9000\u51FA\u7801 ${r.status}${why ? `: ${why}` : ""}` };
|
|
1662
|
+
}
|
|
1663
|
+
return { applicable: true, ok: true, relSql, manualCmd };
|
|
1664
|
+
} catch (e) {
|
|
1665
|
+
return { applicable: true, ok: false, relSql, manualCmd, detail: e instanceof Error ? e.message : String(e) };
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1615
1669
|
// src/cli/commands/init.ts
|
|
1616
1670
|
var ADAPTER_RENDERERS = {
|
|
1617
1671
|
claude: renderAdapter,
|
|
@@ -1678,7 +1732,7 @@ async function handleCodexMcpConfig(options) {
|
|
|
1678
1732
|
console.log(`[dry-run] \u5C06\u5199\u5165 ${userConfigPath}`);
|
|
1679
1733
|
return;
|
|
1680
1734
|
}
|
|
1681
|
-
if (
|
|
1735
|
+
if (existsSync13(userConfigPath) && readFileSync10(userConfigPath, "utf-8").includes("mcp_servers.add_coder")) {
|
|
1682
1736
|
console.log(`\u26A0\uFE0F ${userConfigPath} \u5DF2\u5B58\u5728 mcp_servers.add_coder \u914D\u7F6E\uFF0C\u8DF3\u8FC7\uFF08\u907F\u514D\u91CD\u590D\uFF09`);
|
|
1683
1737
|
return;
|
|
1684
1738
|
}
|
|
@@ -1687,7 +1741,7 @@ async function handleCodexMcpConfig(options) {
|
|
|
1687
1741
|
console.log("\u5DF2\u53D6\u6D88\u5199\u5165");
|
|
1688
1742
|
return;
|
|
1689
1743
|
}
|
|
1690
|
-
if (
|
|
1744
|
+
if (existsSync13(userConfigPath)) {
|
|
1691
1745
|
const backup = `${userConfigPath}.bak-${Date.now()}`;
|
|
1692
1746
|
copyFileSync2(userConfigPath, backup);
|
|
1693
1747
|
console.log(`\u5DF2\u5907\u4EFD\u539F\u914D\u7F6E \u2192 ${backup}`);
|
|
@@ -1810,7 +1864,7 @@ async function resolveDbCredentials(force) {
|
|
|
1810
1864
|
function composeContent(projectName) {
|
|
1811
1865
|
return `services:
|
|
1812
1866
|
postgres:
|
|
1813
|
-
image: docker.io/
|
|
1867
|
+
image: docker.io/pgvector/pgvector:pg16
|
|
1814
1868
|
container_name: \${PROJECT_NAME:-${projectName}}-postgres
|
|
1815
1869
|
restart: unless-stopped
|
|
1816
1870
|
ports:
|
|
@@ -1838,8 +1892,8 @@ networks:
|
|
|
1838
1892
|
`;
|
|
1839
1893
|
}
|
|
1840
1894
|
function writeSqliteExportScript(projectRoot, dryRun) {
|
|
1841
|
-
const scriptsDir =
|
|
1842
|
-
const scriptPath =
|
|
1895
|
+
const scriptsDir = resolve9(projectRoot, "scripts");
|
|
1896
|
+
const scriptPath = resolve9(scriptsDir, "export-db.ts");
|
|
1843
1897
|
const content = `import { PrismaClient } from "@prisma/client";
|
|
1844
1898
|
import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
1845
1899
|
import { resolve } from "path";
|
|
@@ -1863,13 +1917,13 @@ main().catch((e) => { console.error(e); process.exit(1); });
|
|
|
1863
1917
|
console.log(`[dry-run] \u5C06\u5199\u5165 ${scriptPath}`);
|
|
1864
1918
|
return;
|
|
1865
1919
|
}
|
|
1866
|
-
if (!
|
|
1920
|
+
if (!existsSync13(scriptsDir)) mkdirSync6(scriptsDir, { recursive: true });
|
|
1867
1921
|
writeFileSync6(scriptPath, content, "utf-8");
|
|
1868
1922
|
console.log("\u5DF2\u751F\u6210 scripts/export-db.ts");
|
|
1869
1923
|
}
|
|
1870
1924
|
function injectDbExportScript(projectRoot, dryRun) {
|
|
1871
|
-
const pkgPath =
|
|
1872
|
-
if (!
|
|
1925
|
+
const pkgPath = resolve9(projectRoot, "package.json");
|
|
1926
|
+
if (!existsSync13(pkgPath)) return;
|
|
1873
1927
|
if (dryRun) {
|
|
1874
1928
|
console.log("[dry-run] \u5C06\u6CE8\u5165 db:export");
|
|
1875
1929
|
return;
|
|
@@ -1921,14 +1975,14 @@ function writeComposeEnv(ctx) {
|
|
|
1921
1975
|
if (db.engine !== "postgresql" || !db.container || db.container === "manual") return;
|
|
1922
1976
|
if (!db.reuseExisting) {
|
|
1923
1977
|
const composeName = db.container === "podman" ? "podman-compose.add.yml" : "docker-compose.add.yml";
|
|
1924
|
-
const composePath =
|
|
1925
|
-
if (!options.dryRun && (!
|
|
1978
|
+
const composePath = resolve9(projectRoot, composeName);
|
|
1979
|
+
if (!options.dryRun && (!existsSync13(composePath) || options.force)) {
|
|
1926
1980
|
writeFileSync6(composePath, composeContent(config.projectName || "add-project"), "utf-8");
|
|
1927
1981
|
console.log(`\u5DF2\u521B\u5EFA ${composeName}`);
|
|
1928
1982
|
}
|
|
1929
1983
|
}
|
|
1930
|
-
const devEnvPath =
|
|
1931
|
-
if (!options.dryRun &&
|
|
1984
|
+
const devEnvPath = resolve9(projectRoot, ".env.development");
|
|
1985
|
+
if (!options.dryRun && existsSync13(devEnvPath)) {
|
|
1932
1986
|
const existing = readFileSync10(devEnvPath, "utf-8");
|
|
1933
1987
|
if (!/^DATABASE_USER=/m.test(existing)) {
|
|
1934
1988
|
writeFileSync6(devEnvPath, existing + `
|
|
@@ -1975,8 +2029,8 @@ async function renderAndWrite(ctx) {
|
|
|
1975
2029
|
}
|
|
1976
2030
|
const reviewTargets = target === "codex" ? [magicDir] : [ADD_DIR, magicDir];
|
|
1977
2031
|
for (const d of reviewTargets) {
|
|
1978
|
-
const reviewsDir =
|
|
1979
|
-
if (!
|
|
2032
|
+
const reviewsDir = resolve9(projectRoot, d, "reviews");
|
|
2033
|
+
if (!existsSync13(reviewsDir)) {
|
|
1980
2034
|
if (dry) {
|
|
1981
2035
|
console.log(`[dry-run] \u5C06\u521B\u5EFA ${reviewsDir}/`);
|
|
1982
2036
|
} else {
|
|
@@ -1988,16 +2042,16 @@ async function renderAndWrite(ctx) {
|
|
|
1988
2042
|
const hashMap = {};
|
|
1989
2043
|
let npmVer = "";
|
|
1990
2044
|
try {
|
|
1991
|
-
npmVer = JSON.parse(readFileSync10(
|
|
2045
|
+
npmVer = JSON.parse(readFileSync10(resolve9(projectRoot, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json"), "utf-8"))._version ?? "";
|
|
1992
2046
|
} catch {
|
|
1993
2047
|
}
|
|
1994
2048
|
for (const [rp, c] of allFiles) {
|
|
1995
2049
|
hashMap[rp] = createHash("sha256").update(c).digest("hex").slice(0, 8);
|
|
1996
2050
|
}
|
|
1997
|
-
const hashOut =
|
|
2051
|
+
const hashOut = resolve9(projectRoot, magicDir, ".add-coder-hash.json");
|
|
1998
2052
|
writeFileSync6(hashOut, JSON.stringify(hashMap, null, 2) + "\n", "utf-8");
|
|
1999
2053
|
if (npmVer) {
|
|
2000
|
-
writeFileSync6(
|
|
2054
|
+
writeFileSync6(resolve9(projectRoot, magicDir, ".add-coder-version"), npmVer + "\n", "utf-8");
|
|
2001
2055
|
}
|
|
2002
2056
|
console.log(`hash: ${Object.keys(hashMap).length} entries \u2192 ${magicDir}/.add-coder-hash.json`);
|
|
2003
2057
|
}
|
|
@@ -2008,7 +2062,7 @@ async function deployDatabase(ctx) {
|
|
|
2008
2062
|
if (options.dryRun) return null;
|
|
2009
2063
|
let fail = null;
|
|
2010
2064
|
if (db.engine === "postgresql" && db.container && db.container !== "manual") {
|
|
2011
|
-
const dbScript =
|
|
2065
|
+
const dbScript = resolve9(projectRoot, magicDir, "scripts", "db-ensure.sh");
|
|
2012
2066
|
const dbEnv = { ...process.env, DATABASE_USER: db.user, DATABASE_PASSWORD: db.password, DATABASE_PORT: db.port, PROJECT_NAME: config.projectName };
|
|
2013
2067
|
const mode = db.reuseExisting ? "manual" : db.container;
|
|
2014
2068
|
console.log(db.reuseExisting ? "\u590D\u7528\u5DF2\u6709 PostgreSQL ..." : `\u90E8\u7F72\u6570\u636E\u5E93 (${db.container}) ...`);
|
|
@@ -2026,8 +2080,8 @@ ${bashRun.stderr.trim().split("\n").slice(0, 5).join("\n")}` : ""}`;
|
|
|
2026
2080
|
}
|
|
2027
2081
|
}
|
|
2028
2082
|
if (db.engine === "postgresql" && db.container === "manual") {
|
|
2029
|
-
const dbScript =
|
|
2030
|
-
if (
|
|
2083
|
+
const dbScript = resolve9(projectRoot, magicDir, "scripts", "db-ensure.sh");
|
|
2084
|
+
if (existsSync13(dbScript)) {
|
|
2031
2085
|
try {
|
|
2032
2086
|
const bashRun = runCommand("bash", [dbScript, "postgresql", "manual"], { cwd: projectRoot, stdio: "inherit" });
|
|
2033
2087
|
if (bashRun.status !== 0) fail = `db-ensure.sh \u9000\u51FA\u7801: ${bashRun.status}${bashRun.stderr ? `
|
|
@@ -2062,6 +2116,14 @@ ${bashRun.stderr.trim().split("\n").slice(0, 5).join("\n")}` : ""}`;
|
|
|
2062
2116
|
injectDbExportScript(projectRoot, false);
|
|
2063
2117
|
try {
|
|
2064
2118
|
await injectPrisma2(projectRoot, { force: !!options.force, datasource: "sqlite" });
|
|
2119
|
+
const expected = applyMemoryExpectedState({ projectRoot, magicDir, engine: db.engine });
|
|
2120
|
+
if (expected.applicable && !expected.ok) {
|
|
2121
|
+
console.warn(`\u26A0\uFE0F \u8BB0\u5FC6 FTS5 \u671F\u671B\u6001\u5E94\u7528\u5931\u8D25\uFF08\u4E0D\u963B\u65AD init\uFF09\uFF1A${expected.detail ?? "\u672A\u77E5\u539F\u56E0"}`);
|
|
2122
|
+
console.warn(` \u540E\u7EED recall_memory \u4F1A\u62A5 sqlite-fts \u4E0D\u53EF\u7528\uFF1B\u624B\u5DE5\u6062\u590D: ${expected.manualCmd}`);
|
|
2123
|
+
console.warn(` \u6216\u8FD0\u884C: add-coder memory:reindex --apply`);
|
|
2124
|
+
} else if (expected.applicable) {
|
|
2125
|
+
console.log(`\u2705 \u8BB0\u5FC6 FTS5 \u671F\u671B\u6001\u5DF2\u5E94\u7528\uFF08${expected.relSql}\uFF09`);
|
|
2126
|
+
}
|
|
2065
2127
|
} catch (e) {
|
|
2066
2128
|
fail = `SQLite \u540C\u6B65\u5931\u8D25: ${e instanceof Error ? e.message : String(e)}`;
|
|
2067
2129
|
}
|
|
@@ -2072,17 +2134,17 @@ function deployDocs(ctx) {
|
|
|
2072
2134
|
const { projectRoot, options, config } = ctx;
|
|
2073
2135
|
if (options.dryRun) return;
|
|
2074
2136
|
const pn = config.projectName || "add-project";
|
|
2075
|
-
const docsBase =
|
|
2076
|
-
const groundingSrc =
|
|
2137
|
+
const docsBase = resolve9(projectRoot, "docs", pn, "knowledge");
|
|
2138
|
+
const groundingSrc = resolve9(import.meta.dirname, "../templates/core/templates");
|
|
2077
2139
|
for (const d of ["00-\u9700\u6C42", "01-\u67B6\u6784", "02-\u89C4\u8303"]) {
|
|
2078
|
-
const srcDir =
|
|
2079
|
-
const destDir =
|
|
2080
|
-
if (!
|
|
2081
|
-
if (!
|
|
2140
|
+
const srcDir = resolve9(groundingSrc, d);
|
|
2141
|
+
const destDir = resolve9(docsBase, d);
|
|
2142
|
+
if (!existsSync13(destDir)) mkdirSync6(destDir, { recursive: true });
|
|
2143
|
+
if (!existsSync13(srcDir)) continue;
|
|
2082
2144
|
for (const f of readdirSync5(srcDir)) {
|
|
2083
|
-
const src =
|
|
2084
|
-
const dest =
|
|
2085
|
-
if (
|
|
2145
|
+
const src = resolve9(srcDir, f);
|
|
2146
|
+
const dest = resolve9(destDir, f);
|
|
2147
|
+
if (existsSync13(dest)) continue;
|
|
2086
2148
|
try {
|
|
2087
2149
|
copyFileSync2(src, dest);
|
|
2088
2150
|
} catch {
|
|
@@ -2098,7 +2160,7 @@ function finalize(ctx, result, dbFail) {
|
|
|
2098
2160
|
return;
|
|
2099
2161
|
}
|
|
2100
2162
|
if (db.engine === "sqlite") console.log("\u6570\u636E\u5907\u4EFD: npm run db:export \u2192 data/exports/");
|
|
2101
|
-
const pkg = JSON.parse(readFileSync10(
|
|
2163
|
+
const pkg = JSON.parse(readFileSync10(resolve9(import.meta.dirname, "../package.json"), "utf-8"));
|
|
2102
2164
|
const peerNames = Object.keys(pkg.peerDependencies || {});
|
|
2103
2165
|
if (peerNames.length > 0) {
|
|
2104
2166
|
console.log(`
|
|
@@ -2131,8 +2193,8 @@ function finalize(ctx, result, dbFail) {
|
|
|
2131
2193
|
}
|
|
2132
2194
|
|
|
2133
2195
|
// src/cli/commands/sync.ts
|
|
2134
|
-
import { existsSync as
|
|
2135
|
-
import { resolve as
|
|
2196
|
+
import { existsSync as existsSync14, readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
|
|
2197
|
+
import { resolve as resolve10, dirname as dirname4 } from "path";
|
|
2136
2198
|
import { createHash as createHash2 } from "crypto";
|
|
2137
2199
|
|
|
2138
2200
|
// src/lib/path-normalize.ts
|
|
@@ -2205,7 +2267,7 @@ function resolvePatchConflictMode(yes = false, isTTY = Boolean(process.stdin.isT
|
|
|
2205
2267
|
}
|
|
2206
2268
|
function loadHashFile(root, magic) {
|
|
2207
2269
|
try {
|
|
2208
|
-
const raw = JSON.parse(readFileSync11(
|
|
2270
|
+
const raw = JSON.parse(readFileSync11(resolve10(root, magic, SYNC_CONFIG.HASH_OUTPUT_FILE), "utf-8"));
|
|
2209
2271
|
const normalized = {};
|
|
2210
2272
|
for (const [k, v] of Object.entries(raw)) normalized[normalizeRelPath(k)] = v;
|
|
2211
2273
|
return normalized;
|
|
@@ -2215,19 +2277,19 @@ function loadHashFile(root, magic) {
|
|
|
2215
2277
|
}
|
|
2216
2278
|
function loadVersionFile(root, magic) {
|
|
2217
2279
|
try {
|
|
2218
|
-
return readFileSync11(
|
|
2280
|
+
return readFileSync11(resolve10(root, magic, SYNC_CONFIG.VERSION_SENTINEL), "utf-8").trim();
|
|
2219
2281
|
} catch {
|
|
2220
2282
|
return "";
|
|
2221
2283
|
}
|
|
2222
2284
|
}
|
|
2223
2285
|
function saveVersionFile(root, magic, version2) {
|
|
2224
|
-
writeFileSync7(
|
|
2286
|
+
writeFileSync7(resolve10(root, magic, SYNC_CONFIG.VERSION_SENTINEL), version2 + "\n", "utf-8");
|
|
2225
2287
|
}
|
|
2226
2288
|
function loadTemplateVersion(root) {
|
|
2227
2289
|
const candidates = [
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2290
|
+
resolve10(root, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json"),
|
|
2291
|
+
resolve10(import.meta.dirname, "../templates/.add-coder-src-hash.json"),
|
|
2292
|
+
resolve10(import.meta.dirname, "../../../templates/.add-coder-src-hash.json")
|
|
2231
2293
|
];
|
|
2232
2294
|
for (const candidate of candidates) {
|
|
2233
2295
|
try {
|
|
@@ -2240,7 +2302,7 @@ function loadTemplateVersion(root) {
|
|
|
2240
2302
|
function saveHashFile(root, magic, files) {
|
|
2241
2303
|
const m = {};
|
|
2242
2304
|
for (const [p, c] of files) m[p] = c;
|
|
2243
|
-
writeFileSync7(
|
|
2305
|
+
writeFileSync7(resolve10(root, magic, SYNC_CONFIG.HASH_OUTPUT_FILE), JSON.stringify(m, null, 2) + "\n", "utf-8");
|
|
2244
2306
|
}
|
|
2245
2307
|
function mergeFullHash(outHash, candidates, readDiskHash, preserveKeys = /* @__PURE__ */ new Set()) {
|
|
2246
2308
|
const finalHash = /* @__PURE__ */ new Map();
|
|
@@ -2341,8 +2403,8 @@ async function syncCommand(options = {}) {
|
|
|
2341
2403
|
let sameCount = 0;
|
|
2342
2404
|
for (const [relPath, content] of candidates) {
|
|
2343
2405
|
const key = normalizeRelPath(relPath);
|
|
2344
|
-
const absPath =
|
|
2345
|
-
if (!
|
|
2406
|
+
const absPath = resolve10(projectRoot, relPath);
|
|
2407
|
+
if (!existsSync14(absPath)) {
|
|
2346
2408
|
missingFiles.set(key, content);
|
|
2347
2409
|
} else if (establishBaseline) {
|
|
2348
2410
|
missingFiles.set(key, content);
|
|
@@ -2388,8 +2450,8 @@ async function syncCommand(options = {}) {
|
|
|
2388
2450
|
}
|
|
2389
2451
|
let finalHash = mergeFullHash(
|
|
2390
2452
|
outHash,
|
|
2391
|
-
[...candidates].map(([relPath]) => ({ relPath, absPath:
|
|
2392
|
-
(absPath) =>
|
|
2453
|
+
[...candidates].map(([relPath]) => ({ relPath, absPath: resolve10(projectRoot, relPath) })),
|
|
2454
|
+
(absPath) => existsSync14(absPath) ? hash8(readFileSync11(absPath, "utf-8")) : null,
|
|
2393
2455
|
unresolvedConflictKeys
|
|
2394
2456
|
);
|
|
2395
2457
|
if (target === "codex") {
|
|
@@ -2404,7 +2466,7 @@ async function syncCommand(options = {}) {
|
|
|
2404
2466
|
}
|
|
2405
2467
|
const missing = /* @__PURE__ */ new Map();
|
|
2406
2468
|
for (const [relPath, content] of allFiles) {
|
|
2407
|
-
if (!
|
|
2469
|
+
if (!existsSync14(resolve10(projectRoot, relPath))) {
|
|
2408
2470
|
missing.set(relPath, content);
|
|
2409
2471
|
}
|
|
2410
2472
|
}
|
|
@@ -2428,7 +2490,7 @@ async function syncCommand(options = {}) {
|
|
|
2428
2490
|
}
|
|
2429
2491
|
function printMigrateGuidance(targetPath, changed) {
|
|
2430
2492
|
const g = SYNC_PRISMA_CONFIG.POST_SYNC;
|
|
2431
|
-
const isManaged =
|
|
2493
|
+
const isManaged = existsSync14(resolve10(dirname4(targetPath), "migrations"));
|
|
2432
2494
|
const actions = isManaged ? g.MANAGED_ACTIONS : g.UNMANAGED_ACTIONS;
|
|
2433
2495
|
console.log(` \u25B6 \u5DF2\u5199\u5165 ${changed} \u5904\u53D8\u66F4\uFF0C${g.HEADER}`);
|
|
2434
2496
|
for (const a of actions) {
|
|
@@ -2467,7 +2529,7 @@ async function ensureAtlasCapability(projectRoot, yes = false) {
|
|
|
2467
2529
|
console.log(" \u6587\u6863: README.md \u2192 \u7AE0\u8282\u300CAtlas \u6570\u636E\u5E93\u540C\u6B65\u80FD\u529B\u300D/ DEVELOPMENT.md \xA7\u4E5D");
|
|
2468
2530
|
return;
|
|
2469
2531
|
}
|
|
2470
|
-
const pm =
|
|
2532
|
+
const pm = existsSync14(resolve10(projectRoot, "pnpm-lock.yaml")) ? "pnpm" : "npm";
|
|
2471
2533
|
console.log(` \u5B89\u88C5 ${pm} add -D @ariga/atlas ...`);
|
|
2472
2534
|
const r = runCommand(pm, ["add", "-D", "@ariga/atlas"], { cwd: projectRoot, timeout: 18e4 });
|
|
2473
2535
|
if (r.status !== 0) {
|
|
@@ -2479,8 +2541,8 @@ async function ensureAtlasCapability(projectRoot, yes = false) {
|
|
|
2479
2541
|
console.log(" \u2705 @ariga/atlas \u5DF2\u5B89\u88C5\uFF0CAtlas \u80FD\u529B\u5C31\u7EEA\uFF08node_modules/.bin/atlas\uFF09");
|
|
2480
2542
|
}
|
|
2481
2543
|
function checkHostAtlasSegment(projectRoot) {
|
|
2482
|
-
const script =
|
|
2483
|
-
if (!
|
|
2544
|
+
const script = resolve10(projectRoot, "scripts", "db-ensure.sh");
|
|
2545
|
+
if (!existsSync14(script)) return;
|
|
2484
2546
|
const content = readFileSync11(script, "utf-8");
|
|
2485
2547
|
if (content.includes("atlas_sync")) return;
|
|
2486
2548
|
console.warn(" \u26A0\uFE0F \u5BBF\u4E3B scripts/db-ensure.sh \u672A\u5305\u542B Atlas \u540C\u6B65\u6BB5\uFF08\u65E5\u5E38 db-ensure \u5C06\u7F3A\u5C11 ADD \u6CBB\u7406\u6A21\u578B\u540C\u6B65\uFF09");
|
|
@@ -2493,9 +2555,9 @@ function checkHostAtlasSegment(projectRoot) {
|
|
|
2493
2555
|
}
|
|
2494
2556
|
async function checkPrismaDiff(projectRoot, options) {
|
|
2495
2557
|
if (!options.patch) return;
|
|
2496
|
-
const basePath =
|
|
2497
|
-
const targetPath =
|
|
2498
|
-
if (!
|
|
2558
|
+
const basePath = resolve10(projectRoot, SYNC_PRISMA_CONFIG.BASE_SCHEMA);
|
|
2559
|
+
const targetPath = resolve10(projectRoot, SYNC_PRISMA_CONFIG.TARGET_PATTERN);
|
|
2560
|
+
if (!existsSync14(basePath)) {
|
|
2499
2561
|
console.log(`
|
|
2500
2562
|
\u26A0\uFE0F \u57FA\u51C6 schema \u4E0D\u5B58\u5728: ${basePath}`);
|
|
2501
2563
|
console.log(` \u8BF7\u786E\u4FDD add-coder \u5DF2\u6B63\u786E\u5B89\u88C5\u3002`);
|
|
@@ -2509,7 +2571,7 @@ async function checkPrismaDiff(projectRoot, options) {
|
|
|
2509
2571
|
\u2705 Prisma schema \u4E0E add-coder \u6807\u51C6\u4E00\u81F4\uFF0C\u65E0\u9700\u540C\u6B65\u3002`);
|
|
2510
2572
|
return;
|
|
2511
2573
|
}
|
|
2512
|
-
const targetExists =
|
|
2574
|
+
const targetExists = existsSync14(targetPath);
|
|
2513
2575
|
let modifiedCount = 0;
|
|
2514
2576
|
console.log(`
|
|
2515
2577
|
\u26A0\uFE0F Prisma schema \u5DEE\u5F02\u68C0\u6D4B:`);
|
|
@@ -2759,8 +2821,8 @@ function overwriteFieldLines(targetPath, basePath, modelName, conflicts) {
|
|
|
2759
2821
|
}
|
|
2760
2822
|
|
|
2761
2823
|
// src/cli/commands/status.ts
|
|
2762
|
-
import { existsSync as
|
|
2763
|
-
import { resolve as
|
|
2824
|
+
import { existsSync as existsSync15 } from "fs";
|
|
2825
|
+
import { resolve as resolve11 } from "path";
|
|
2764
2826
|
async function statusCommand() {
|
|
2765
2827
|
const projectRoot = process.cwd();
|
|
2766
2828
|
const config = await loadConfig(projectRoot);
|
|
@@ -2769,7 +2831,7 @@ async function statusCommand() {
|
|
|
2769
2831
|
const missing = [];
|
|
2770
2832
|
const present = [];
|
|
2771
2833
|
for (const [relPath] of coreFiles) {
|
|
2772
|
-
if (
|
|
2834
|
+
if (existsSync15(resolve11(projectRoot, relPath))) {
|
|
2773
2835
|
present.push(relPath);
|
|
2774
2836
|
} else {
|
|
2775
2837
|
missing.push(relPath);
|
|
@@ -2787,14 +2849,14 @@ async function statusCommand() {
|
|
|
2787
2849
|
}
|
|
2788
2850
|
|
|
2789
2851
|
// src/cli/commands/stack.ts
|
|
2790
|
-
import { readdirSync as readdirSync6, readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as
|
|
2791
|
-
import { resolve as
|
|
2852
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as existsSync16, mkdirSync as mkdirSync8 } from "fs";
|
|
2853
|
+
import { resolve as resolve12, join as join10 } from "path";
|
|
2792
2854
|
import { createHash as createHash3 } from "crypto";
|
|
2793
2855
|
var HASH_OUTPUT_FILE = ".add-coder-hash.json";
|
|
2794
2856
|
function hash82(c) {
|
|
2795
2857
|
return createHash3("sha256").update(c).digest("hex").slice(0, 8);
|
|
2796
2858
|
}
|
|
2797
|
-
function
|
|
2859
|
+
function resolveMagicDir2(projectRoot, specified) {
|
|
2798
2860
|
if (specified) {
|
|
2799
2861
|
return magicDirFor(specified);
|
|
2800
2862
|
}
|
|
@@ -2803,18 +2865,18 @@ function resolveMagicDir(projectRoot, specified) {
|
|
|
2803
2865
|
return magicDirFor(adapter);
|
|
2804
2866
|
}
|
|
2805
2867
|
function listCustomProfiles(projectRoot, magicDir, registryNames) {
|
|
2806
|
-
const dir =
|
|
2807
|
-
if (!
|
|
2868
|
+
const dir = resolve12(projectRoot, magicDir, "rules", "profiles");
|
|
2869
|
+
if (!existsSync16(dir)) return [];
|
|
2808
2870
|
return readdirSync6(dir).filter((f) => f.endsWith(".md") && !registryNames.has(f.replace(/-profile\.md$/, ""))).sort();
|
|
2809
2871
|
}
|
|
2810
2872
|
function profileExists(projectRoot, magicDir, name) {
|
|
2811
2873
|
const registry = loadProfileRegistry();
|
|
2812
2874
|
if (registry.some((p) => p.name === name)) return true;
|
|
2813
|
-
return
|
|
2875
|
+
return existsSync16(resolve12(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`));
|
|
2814
2876
|
}
|
|
2815
2877
|
function stackCommand(sub, name, options = {}) {
|
|
2816
2878
|
const projectRoot = process.cwd();
|
|
2817
|
-
const magicDir =
|
|
2879
|
+
const magicDir = resolveMagicDir2(projectRoot, options.adapter);
|
|
2818
2880
|
const registry = loadProfileRegistry();
|
|
2819
2881
|
if (options.clear) {
|
|
2820
2882
|
saveStack(projectRoot, magicDir, "");
|
|
@@ -2847,12 +2909,12 @@ function stackCommand(sub, name, options = {}) {
|
|
|
2847
2909
|
console.log("\u6280\u672F\u6808: \u672A\u8BBE\u7F6E\uFF08\u4E2D\u6027\uFF0C\u65E0\u6280\u672F\u6808\u5047\u8BBE\uFF09");
|
|
2848
2910
|
return;
|
|
2849
2911
|
}
|
|
2850
|
-
const profilePath =
|
|
2851
|
-
const stat =
|
|
2912
|
+
const profilePath = resolve12(projectRoot, magicDir, "rules", "profiles", `${current}-profile.md`);
|
|
2913
|
+
const stat = existsSync16(profilePath) ? readFileSync12(profilePath, "utf-8").length : 0;
|
|
2852
2914
|
console.log(`\u6280\u672F\u6808: ${current}`);
|
|
2853
|
-
console.log(`profile \u6587\u4EF6: ${profilePath}${
|
|
2915
|
+
console.log(`profile \u6587\u4EF6: ${profilePath}${existsSync16(profilePath) ? ` (${stat} \u5B57\u7B26)` : "\uFF08\u7F3A\u5931\uFF09"}`);
|
|
2854
2916
|
try {
|
|
2855
|
-
const raw = JSON.parse(readFileSync12(
|
|
2917
|
+
const raw = JSON.parse(readFileSync12(resolve12(projectRoot, magicDir, "stack.json"), "utf-8"));
|
|
2856
2918
|
if (raw.updatedAt) console.log(`\u66F4\u65B0\u65F6\u95F4: ${raw.updatedAt}`);
|
|
2857
2919
|
} catch {
|
|
2858
2920
|
}
|
|
@@ -2877,8 +2939,8 @@ function buildConfig(projectRoot, magicDir, stack) {
|
|
|
2877
2939
|
stack
|
|
2878
2940
|
};
|
|
2879
2941
|
try {
|
|
2880
|
-
const pkgPath =
|
|
2881
|
-
if (
|
|
2942
|
+
const pkgPath = resolve12(projectRoot, "package.json");
|
|
2943
|
+
if (existsSync16(pkgPath)) {
|
|
2882
2944
|
const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
2883
2945
|
if (pkg.name) config.projectName = pkg.name;
|
|
2884
2946
|
}
|
|
@@ -2906,13 +2968,13 @@ function applyStack(projectRoot, magicDir, name) {
|
|
|
2906
2968
|
}
|
|
2907
2969
|
const hashMap = {};
|
|
2908
2970
|
try {
|
|
2909
|
-
Object.assign(hashMap, JSON.parse(readFileSync12(
|
|
2971
|
+
Object.assign(hashMap, JSON.parse(readFileSync12(resolve12(projectRoot, magicDir, HASH_OUTPUT_FILE), "utf-8")));
|
|
2910
2972
|
} catch {
|
|
2911
2973
|
}
|
|
2912
2974
|
let written = 0;
|
|
2913
2975
|
for (const [relPath, content] of stackRelated) {
|
|
2914
|
-
const targetPath =
|
|
2915
|
-
mkdirSync8(
|
|
2976
|
+
const targetPath = resolve12(projectRoot, relPath);
|
|
2977
|
+
mkdirSync8(join10(targetPath, ".."), { recursive: true });
|
|
2916
2978
|
writeFileSync8(targetPath, content, "utf-8");
|
|
2917
2979
|
hashMap[relPath] = hash82(content);
|
|
2918
2980
|
written++;
|
|
@@ -2923,24 +2985,331 @@ function applyStack(projectRoot, magicDir, name) {
|
|
|
2923
2985
|
};
|
|
2924
2986
|
const registry = loadProfileRegistry();
|
|
2925
2987
|
const isBuiltin = registry.some((p) => p.name === name);
|
|
2926
|
-
const profilePathMagic =
|
|
2927
|
-
const profilePathAdd =
|
|
2928
|
-
const projectRulesPath =
|
|
2929
|
-
const projectRulesContent =
|
|
2988
|
+
const profilePathMagic = resolve12(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`);
|
|
2989
|
+
const profilePathAdd = resolve12(projectRoot, ADD_DIR, "rules", "profiles", `${name}-profile.md`);
|
|
2990
|
+
const projectRulesPath = resolve12(projectRoot, magicDir, "rules", "project_rules.md");
|
|
2991
|
+
const projectRulesContent = existsSync16(projectRulesPath) ? readFileSync12(projectRulesPath, "utf-8") : "";
|
|
2930
2992
|
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`);
|
|
2931
|
-
if (magicDir !== magicDirFor("codex") && isBuiltin && !
|
|
2932
|
-
if (!
|
|
2993
|
+
if (magicDir !== magicDirFor("codex") && isBuiltin && !existsSync16(profilePathAdd)) fail(`profile \u672A\u5199\u5165 ${ADD_DIR}: ${profilePathAdd}`);
|
|
2994
|
+
if (!existsSync16(profilePathMagic)) fail(`profile \u672A\u5199\u5165 ${magicDir}: ${profilePathMagic}`);
|
|
2933
2995
|
if (!projectRulesContent.includes("**\u5F53\u524D\u6280\u672F\u6808**") || !projectRulesContent.includes(name)) fail(`project_rules.md \u672A\u5305\u542B ${name} \u5F15\u7528`);
|
|
2934
|
-
writeFileSync8(
|
|
2996
|
+
writeFileSync8(resolve12(projectRoot, magicDir, HASH_OUTPUT_FILE), JSON.stringify(hashMap, null, 2) + "\n", "utf-8");
|
|
2935
2997
|
console.log(`\u2705 \u6280\u672F\u6808\u5DF2\u8BBE\u7F6E\u4E3A ${name}`);
|
|
2936
2998
|
console.log(` ${magicDir}/stack.json \u2192 ${name}`);
|
|
2937
|
-
console.log(` ${magicDir}/rules/profiles/${name}-profile.md ${
|
|
2999
|
+
console.log(` ${magicDir}/rules/profiles/${name}-profile.md ${existsSync16(resolve12(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`)) ? "\u5DF2\u5C31\u4F4D" : "\uFF08\u81EA\u5B9A\u4E49 profile\uFF0C\u9879\u76EE\u4FA7\u6587\u4EF6\uFF09"}`);
|
|
2938
3000
|
console.log(` project_rules.md \u5F15\u7528\u884C\u5DF2\u66F4\u65B0 + hash \u5DF2\u5237\u65B0\uFF08${written} \u4E2A\u6587\u4EF6\uFF09`);
|
|
2939
3001
|
}
|
|
2940
3002
|
|
|
3003
|
+
// src/lib/memory-fts-objects.ts
|
|
3004
|
+
var PG_FTS_OBJECTS = [
|
|
3005
|
+
{ name: "pg_trgm", kind: "extension", ddl: `CREATE EXTENSION IF NOT EXISTS pg_trgm;` },
|
|
3006
|
+
{
|
|
3007
|
+
name: "AddMemory_topic_trgm_idx",
|
|
3008
|
+
kind: "index",
|
|
3009
|
+
ddl: `CREATE INDEX IF NOT EXISTS "AddMemory_topic_trgm_idx" ON "public"."AddMemory" USING GIN ("topic" gin_trgm_ops);`
|
|
3010
|
+
},
|
|
3011
|
+
{
|
|
3012
|
+
name: "AddMemory_content_trgm_idx",
|
|
3013
|
+
kind: "index",
|
|
3014
|
+
ddl: `CREATE INDEX IF NOT EXISTS "AddMemory_content_trgm_idx" ON "public"."AddMemory" USING GIN ("content" gin_trgm_ops);`
|
|
3015
|
+
},
|
|
3016
|
+
{
|
|
3017
|
+
name: "AddMemoryEvidence_excerpt_trgm_idx",
|
|
3018
|
+
kind: "index",
|
|
3019
|
+
ddl: `CREATE INDEX IF NOT EXISTS "AddMemoryEvidence_excerpt_trgm_idx" ON "public"."AddMemoryEvidence" USING GIN ("excerpt" gin_trgm_ops);`
|
|
3020
|
+
}
|
|
3021
|
+
];
|
|
3022
|
+
var SQLITE_FTS_OBJECTS = [
|
|
3023
|
+
{
|
|
3024
|
+
name: "add_memory_fts",
|
|
3025
|
+
kind: "table",
|
|
3026
|
+
ddl: `CREATE VIRTUAL TABLE IF NOT EXISTS add_memory_fts USING fts5(memory_id UNINDEXED, topic, content, tokenize = 'trigram');`
|
|
3027
|
+
},
|
|
3028
|
+
{
|
|
3029
|
+
name: "add_memory_fts_ai",
|
|
3030
|
+
kind: "trigger",
|
|
3031
|
+
ddl: `CREATE TRIGGER IF NOT EXISTS add_memory_fts_ai AFTER INSERT ON "AddMemory" BEGIN INSERT INTO add_memory_fts(memory_id, topic, content) VALUES (new.id, new.topic, new.content); END;`
|
|
3032
|
+
},
|
|
3033
|
+
{
|
|
3034
|
+
name: "add_memory_fts_au",
|
|
3035
|
+
kind: "trigger",
|
|
3036
|
+
ddl: `CREATE TRIGGER IF NOT EXISTS add_memory_fts_au AFTER UPDATE ON "AddMemory" BEGIN DELETE FROM add_memory_fts WHERE memory_id = old.id; INSERT INTO add_memory_fts(memory_id, topic, content) VALUES (new.id, new.topic, new.content); END;`
|
|
3037
|
+
},
|
|
3038
|
+
{
|
|
3039
|
+
name: "add_memory_fts_ad",
|
|
3040
|
+
kind: "trigger",
|
|
3041
|
+
ddl: `CREATE TRIGGER IF NOT EXISTS add_memory_fts_ad AFTER DELETE ON "AddMemory" BEGIN DELETE FROM add_memory_fts WHERE memory_id = old.id; END;`
|
|
3042
|
+
}
|
|
3043
|
+
];
|
|
3044
|
+
function resolveFtsObjects(backend) {
|
|
3045
|
+
return backend === "postgres" ? PG_FTS_OBJECTS : SQLITE_FTS_OBJECTS;
|
|
3046
|
+
}
|
|
3047
|
+
function detectBackend(databaseUrl) {
|
|
3048
|
+
const url = (databaseUrl ?? "").trim().toLowerCase();
|
|
3049
|
+
if (url.startsWith("postgres://") || url.startsWith("postgresql://")) return "postgres";
|
|
3050
|
+
if (url.startsWith("sqlite:") || url.startsWith("file:") || url.endsWith(".db") || url.endsWith(".sqlite")) {
|
|
3051
|
+
return "sqlite";
|
|
3052
|
+
}
|
|
3053
|
+
throw new Error(`\u65E0\u6CD5\u8BC6\u522B\u7684 DATABASE_URL \u540E\u7AEF: ${databaseUrl}`);
|
|
3054
|
+
}
|
|
3055
|
+
var PG_MISSING_MARKER = "ADDCODER_FTS_MISSING:";
|
|
3056
|
+
function renderPgProbeSql(expected) {
|
|
3057
|
+
const values = expected.map((s) => ` ('${s.name.replace(/'/g, "''")}', '${s.kind === "extension" ? "extension" : "relation"}')`).join(",\n");
|
|
3058
|
+
return `-- \u7531 add-coder memory:reindex \u751F\u6210\uFF08\u771F\u6E90\uFF1Asrc/lib/memory-fts-objects.ts \u7684 PG_FTS_OBJECTS\uFF09
|
|
3059
|
+
DO $$
|
|
3060
|
+
DECLARE missing text;
|
|
3061
|
+
BEGIN
|
|
3062
|
+
SELECT string_agg(n.name, ',') INTO missing
|
|
3063
|
+
FROM (VALUES
|
|
3064
|
+
${values}
|
|
3065
|
+
) AS n(name, kind)
|
|
3066
|
+
WHERE NOT (
|
|
3067
|
+
(n.kind = 'extension' AND EXISTS (SELECT 1 FROM pg_extension e WHERE e.extname = n.name))
|
|
3068
|
+
OR (n.kind = 'relation' AND EXISTS (SELECT 1 FROM pg_indexes i WHERE i.indexname = n.name))
|
|
3069
|
+
);
|
|
3070
|
+
IF missing IS NOT NULL THEN
|
|
3071
|
+
RAISE EXCEPTION '${PG_MISSING_MARKER} %', missing;
|
|
3072
|
+
END IF;
|
|
3073
|
+
END $$;
|
|
3074
|
+
`;
|
|
3075
|
+
}
|
|
3076
|
+
function parsePgProbeMissing(stderr, expected) {
|
|
3077
|
+
const at = (stderr ?? "").indexOf(PG_MISSING_MARKER);
|
|
3078
|
+
if (at < 0) return [];
|
|
3079
|
+
const tail = stderr.slice(at + PG_MISSING_MARKER.length);
|
|
3080
|
+
const known = new Set(expected.map((s) => s.name));
|
|
3081
|
+
return tail.split(/[\s"',\n]+/).map((s) => s.trim()).filter((s) => known.has(s));
|
|
3082
|
+
}
|
|
3083
|
+
|
|
3084
|
+
// src/lib/memory-fts-runtime.ts
|
|
3085
|
+
import { existsSync as existsSync17, mkdtempSync, readFileSync as readFileSync13, rmSync as rmSync2, writeFileSync as writeFileSync9 } from "fs";
|
|
3086
|
+
import { createRequire } from "module";
|
|
3087
|
+
import { tmpdir } from "os";
|
|
3088
|
+
import { isAbsolute, join as join11, resolve as resolve13 } from "path";
|
|
3089
|
+
var requireBuiltin = createRequire(import.meta.url);
|
|
3090
|
+
function loadNodeSqlite() {
|
|
3091
|
+
return requireBuiltin("node:sqlite");
|
|
3092
|
+
}
|
|
3093
|
+
async function probeVia(adapter, expected) {
|
|
3094
|
+
const specs = expected ?? resolveFtsObjects(adapter.backend);
|
|
3095
|
+
const existing = await adapter.listExisting();
|
|
3096
|
+
const missing = specs.filter((s) => !existing.has(s.name)).map((s) => s.name);
|
|
3097
|
+
const present = specs.length - missing.length;
|
|
3098
|
+
return {
|
|
3099
|
+
backend: adapter.backend,
|
|
3100
|
+
source: adapter.source,
|
|
3101
|
+
missing,
|
|
3102
|
+
rebuilt: [],
|
|
3103
|
+
present,
|
|
3104
|
+
total: specs.length,
|
|
3105
|
+
progress: specs.length === 0 ? 100 : Math.round(present / specs.length * 100)
|
|
3106
|
+
};
|
|
3107
|
+
}
|
|
3108
|
+
async function reindexVia(adapter, expected) {
|
|
3109
|
+
const specs = expected ?? resolveFtsObjects(adapter.backend);
|
|
3110
|
+
const first = await probeVia(adapter, specs);
|
|
3111
|
+
const rebuilt = [];
|
|
3112
|
+
for (const name of first.missing) {
|
|
3113
|
+
const spec = specs.find((s) => s.name === name);
|
|
3114
|
+
if (!spec) continue;
|
|
3115
|
+
await adapter.apply(spec.ddl);
|
|
3116
|
+
rebuilt.push(spec.name);
|
|
3117
|
+
}
|
|
3118
|
+
const after = await probeVia(adapter, specs);
|
|
3119
|
+
return { ...after, rebuilt };
|
|
3120
|
+
}
|
|
3121
|
+
function sqlitePathCandidates(databaseUrl, projectRoot) {
|
|
3122
|
+
const raw = (databaseUrl ?? "").replace(/^file:/, "").replace(/^sqlite:/, "").split("?")[0];
|
|
3123
|
+
if (!raw) return [];
|
|
3124
|
+
if (isAbsolute(raw)) return [raw];
|
|
3125
|
+
return [resolve13(projectRoot, raw), resolve13(projectRoot, "prisma", raw)];
|
|
3126
|
+
}
|
|
3127
|
+
function createSqliteAdapter(opts) {
|
|
3128
|
+
const candidates = opts.dbPath ? [opts.dbPath] : sqlitePathCandidates(opts.databaseUrl, opts.projectRoot);
|
|
3129
|
+
const dbPath = candidates.find((p) => existsSync17(p));
|
|
3130
|
+
const source = dbPath ?? `${opts.databaseUrl}\uFF08\u672A\u627E\u5230\u5E93\u6587\u4EF6\uFF0C\u5DF2\u8BD5\uFF1A${candidates.join(" / ")}\uFF09`;
|
|
3131
|
+
function withDb(fn) {
|
|
3132
|
+
if (!dbPath) {
|
|
3133
|
+
throw new Error(
|
|
3134
|
+
`SQLite \u5E93\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${opts.databaseUrl}\u3002\u5148\u8FD0\u884C add-coder init\uFF08\u6216\u786E\u8BA4 .env.development \u7684 DATABASE_URL\uFF09\u2014\u2014\u5DF2\u8BD5\uFF1A${candidates.join(" / ")}`
|
|
3135
|
+
);
|
|
3136
|
+
}
|
|
3137
|
+
let sqlite;
|
|
3138
|
+
try {
|
|
3139
|
+
sqlite = loadNodeSqlite();
|
|
3140
|
+
} catch (e) {
|
|
3141
|
+
throw new Error(
|
|
3142
|
+
`\u5F53\u524D Node \u4E0D\u652F\u6301 node:sqlite\uFF08\u9700 \u226522.5\uFF0C\u5B9E\u6D4B ${process.version}\uFF09\uFF1A${e instanceof Error ? e.message : String(e)}\uFF1B\u53EF\u6539\u7528 SQL \u6587\u4EF6\u624B\u5DE5\u5E94\u7528\uFF08npx prisma db execute --file <magicDir>/scripts/mcp-server/shared/memory/retrieval/fts/sqlite-fts5.sql --schema prisma/schema.prisma\uFF09`
|
|
3143
|
+
);
|
|
3144
|
+
}
|
|
3145
|
+
const db = new sqlite.DatabaseSync(dbPath);
|
|
3146
|
+
try {
|
|
3147
|
+
return fn(db);
|
|
3148
|
+
} finally {
|
|
3149
|
+
db.close();
|
|
3150
|
+
}
|
|
3151
|
+
}
|
|
3152
|
+
return {
|
|
3153
|
+
backend: "sqlite",
|
|
3154
|
+
source,
|
|
3155
|
+
listExisting: () => Promise.resolve().then(
|
|
3156
|
+
() => withDb((db) => {
|
|
3157
|
+
const rows = db.prepare("SELECT name FROM sqlite_master WHERE type IN ('table','trigger')").all();
|
|
3158
|
+
return new Set(rows.map((r) => r.name));
|
|
3159
|
+
})
|
|
3160
|
+
),
|
|
3161
|
+
apply: (ddl) => Promise.resolve().then(
|
|
3162
|
+
() => withDb((db) => {
|
|
3163
|
+
db.exec(ddl);
|
|
3164
|
+
})
|
|
3165
|
+
)
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
3168
|
+
function readPrismaDatasourceProvider(projectRoot) {
|
|
3169
|
+
for (const rel of ["prisma/schema.prisma", "prisma/add.prisma"]) {
|
|
3170
|
+
const p = resolve13(projectRoot, rel);
|
|
3171
|
+
if (!existsSync17(p)) continue;
|
|
3172
|
+
const m = readFileSync13(p, "utf-8").match(/datasource\s+\w+\s*\{[\s\S]*?provider\s*=\s*"([^"]+)"/);
|
|
3173
|
+
if (m) return m[1];
|
|
3174
|
+
}
|
|
3175
|
+
return null;
|
|
3176
|
+
}
|
|
3177
|
+
function backendForProvider(provider) {
|
|
3178
|
+
const p = provider.trim().toLowerCase();
|
|
3179
|
+
if (p === "postgresql" || p === "postgres") return "postgres";
|
|
3180
|
+
if (p === "sqlite") return "sqlite";
|
|
3181
|
+
throw new Error(`\u4E0D\u652F\u6301\u7684 datasource provider: ${provider}\uFF08\u672C\u547D\u4EE4\u4EC5\u652F\u6301 postgresql / sqlite\uFF09`);
|
|
3182
|
+
}
|
|
3183
|
+
function resolveProjectDatabaseUrl(projectRoot, env = process.env) {
|
|
3184
|
+
if (env.DATABASE_URL) return env.DATABASE_URL;
|
|
3185
|
+
for (const rel of [".env.development", ".env"]) {
|
|
3186
|
+
const p = resolve13(projectRoot, rel);
|
|
3187
|
+
if (!existsSync17(p)) continue;
|
|
3188
|
+
const m = readFileSync13(p, "utf-8").match(/^\s*DATABASE_URL\s*=\s*["']?([^"'\n]+)["']?\s*$/m);
|
|
3189
|
+
if (m) return m[1].trim();
|
|
3190
|
+
}
|
|
3191
|
+
return null;
|
|
3192
|
+
}
|
|
3193
|
+
function createPrismaCliAdapter(opts) {
|
|
3194
|
+
const expected = opts.expected ?? PG_FTS_OBJECTS;
|
|
3195
|
+
const pm = detectPm(opts.projectRoot);
|
|
3196
|
+
const base = pm === "pnpm" ? ["exec", "prisma"] : ["exec", "prisma", "--"];
|
|
3197
|
+
const run = opts.run ?? ((argv, cwd) => runCommand(pm, argv, { cwd, timeout: opts.timeoutMs ?? 12e4 }));
|
|
3198
|
+
const source = opts.describeSource ?? "prisma db execute\uFF08\u9879\u76EE DATABASE_URL\uFF09";
|
|
3199
|
+
function executeSql(sql, tag) {
|
|
3200
|
+
const dir = mkdtempSync(join11(tmpdir(), "add-coder-fts-"));
|
|
3201
|
+
const file = join11(dir, `${tag}.sql`);
|
|
3202
|
+
writeFileSync9(file, sql, "utf-8");
|
|
3203
|
+
try {
|
|
3204
|
+
const r = run([...base, "db", "execute", "--file", file], opts.projectRoot);
|
|
3205
|
+
return { status: r.status, stderr: r.stderr ?? "" };
|
|
3206
|
+
} finally {
|
|
3207
|
+
rmSync2(dir, { recursive: true, force: true });
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
return {
|
|
3211
|
+
backend: "postgres",
|
|
3212
|
+
source,
|
|
3213
|
+
listExisting: () => {
|
|
3214
|
+
const r = executeSql(renderPgProbeSql(expected), "probe");
|
|
3215
|
+
if (r.status === 0) return Promise.resolve(new Set(expected.map((s) => s.name)));
|
|
3216
|
+
const missing = parsePgProbeMissing(r.stderr, expected);
|
|
3217
|
+
if (missing.length > 0) {
|
|
3218
|
+
return Promise.resolve(new Set(expected.map((s) => s.name).filter((n) => !missing.includes(n))));
|
|
3219
|
+
}
|
|
3220
|
+
return Promise.reject(
|
|
3221
|
+
new Error(
|
|
3222
|
+
`PG FTS \u63A2\u6D4B\u5931\u8D25\uFF08\u9000\u51FA\u7801 ${r.status}\uFF09\uFF1A${r.stderr.trim().split("\n").slice(0, 3).join(" | ") || "\u65E0 stderr"}`
|
|
3223
|
+
)
|
|
3224
|
+
);
|
|
3225
|
+
},
|
|
3226
|
+
apply: (ddl) => {
|
|
3227
|
+
const r = executeSql(`${ddl}
|
|
3228
|
+
`, "apply");
|
|
3229
|
+
if (r.status === 0) return Promise.resolve();
|
|
3230
|
+
return Promise.reject(
|
|
3231
|
+
new Error(
|
|
3232
|
+
`PG FTS \u671F\u671B\u6001\u5E94\u7528\u5931\u8D25\uFF08\u9000\u51FA\u7801 ${r.status}\uFF09\uFF1A${r.stderr.trim().split("\n").slice(0, 3).join(" | ") || "\u65E0 stderr"}`
|
|
3233
|
+
)
|
|
3234
|
+
);
|
|
3235
|
+
}
|
|
3236
|
+
};
|
|
3237
|
+
}
|
|
3238
|
+
|
|
3239
|
+
// src/cli/commands/memory-reindex.ts
|
|
3240
|
+
function resolveAdapter3(projectRoot, options, deps) {
|
|
3241
|
+
const databaseUrl = resolveProjectDatabaseUrl(projectRoot, deps.env ?? process.env);
|
|
3242
|
+
let backend;
|
|
3243
|
+
if (options.backend) {
|
|
3244
|
+
backend = options.backend;
|
|
3245
|
+
} else {
|
|
3246
|
+
const provider = readPrismaDatasourceProvider(projectRoot);
|
|
3247
|
+
if (provider) backend = backendForProvider(provider);
|
|
3248
|
+
else if (databaseUrl) backend = detectBackend(databaseUrl);
|
|
3249
|
+
else {
|
|
3250
|
+
throw new Error(
|
|
3251
|
+
"\u65E0\u6CD5\u5224\u5B9A\u540E\u7AEF\uFF1A\u672A\u6307\u5B9A --backend\uFF0C\u4E14\u9879\u76EE\u5185\u65E2\u65E0 prisma/schema.prisma \u7684 datasource \u58F0\u660E\uFF0C\u4E5F\u65E0 .env.development \u7684 DATABASE_URL"
|
|
3252
|
+
);
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
const magicDir = resolveMagicDir(projectRoot);
|
|
3256
|
+
if (backend === "sqlite") {
|
|
3257
|
+
if (!databaseUrl) {
|
|
3258
|
+
throw new Error(`SQLite \u540E\u7AEF\u9700\u8981 DATABASE_URL\uFF08.env.development \u6216\u8FDB\u7A0B\u73AF\u5883\uFF09\uFF1BmagicDir=${magicDir}`);
|
|
3259
|
+
}
|
|
3260
|
+
return createSqliteAdapter({ databaseUrl, projectRoot });
|
|
3261
|
+
}
|
|
3262
|
+
return createPrismaCliAdapter({
|
|
3263
|
+
projectRoot,
|
|
3264
|
+
run: deps.run,
|
|
3265
|
+
describeSource: databaseUrl ? `prisma db execute\uFF08DATABASE_URL=${maskUrl(databaseUrl)}\uFF09` : "prisma db execute\uFF08schema \u5185 env\uFF09"
|
|
3266
|
+
});
|
|
3267
|
+
}
|
|
3268
|
+
function maskUrl(url) {
|
|
3269
|
+
return url.replace(/:\/\/([^:/@]+):[^@]*@/, "://$1:***@");
|
|
3270
|
+
}
|
|
3271
|
+
function printReport(report, mode, io) {
|
|
3272
|
+
const head = mode === "apply" ? "\u8BB0\u5FC6 FTS \u671F\u671B\u6001 \xB7 \u91CD\u5EFA" : "\u8BB0\u5FC6 FTS \u671F\u671B\u6001 \xB7 \u63A2\u6D4B";
|
|
3273
|
+
io.log(`${head}\uFF08backend=${report.backend}\uFF0Csource=${report.source}\uFF09`);
|
|
3274
|
+
io.log(` \u5B8C\u6210\u5EA6 ${report.present}/${report.total}\uFF08${report.progress}%\uFF09`);
|
|
3275
|
+
if (mode === "apply" && report.rebuilt.length > 0) {
|
|
3276
|
+
io.log(` \u672C\u6B21\u91CD\u5EFA\uFF08${report.rebuilt.length}\uFF09\uFF1A${report.rebuilt.join(", ")}`);
|
|
3277
|
+
}
|
|
3278
|
+
if (report.missing.length > 0) {
|
|
3279
|
+
io.log(` \u7F3A\u5931\uFF08${report.missing.length}\uFF09\uFF1A${report.missing.join(", ")}`);
|
|
3280
|
+
io.log(mode === "apply" ? " \u274C \u91CD\u5EFA\u540E\u4ECD\u7F3A\u5931\u2014\u2014\u8BF7\u68C0\u67E5 DDL \u662F\u5426\u88AB\u62D2\uFF08\u6743\u9650/\u6269\u5C55\u4E0D\u53EF\u7528\uFF09" : " \u2192 \u4FEE\u590D\uFF1Aadd-coder memory:reindex --apply");
|
|
3281
|
+
} else {
|
|
3282
|
+
io.log(" \u2705 \u671F\u671B\u6001\u5B8C\u6574");
|
|
3283
|
+
}
|
|
3284
|
+
}
|
|
3285
|
+
async function memoryReindexCommand(options, deps = {}) {
|
|
3286
|
+
const io = deps.io ?? console;
|
|
3287
|
+
const projectRoot = options.cwd ?? process.cwd();
|
|
3288
|
+
if (options.apply && options.probe) {
|
|
3289
|
+
io.error(JSON.stringify({ ok: false, mode: "n/a", reason: "flags-conflict", detail: "--probe \u4E0E --apply \u4E92\u65A5\uFF08\u9ED8\u8BA4 probe\uFF09", exitCode: 2 }));
|
|
3290
|
+
return 2;
|
|
3291
|
+
}
|
|
3292
|
+
const mode = options.apply ? "apply" : "probe";
|
|
3293
|
+
try {
|
|
3294
|
+
const adapter = resolveAdapter3(projectRoot, options, deps);
|
|
3295
|
+
const report = mode === "apply" ? await reindexVia(adapter) : await probeVia(adapter);
|
|
3296
|
+
if (options.json) io.log(JSON.stringify(report, null, 2));
|
|
3297
|
+
else printReport(report, mode, io);
|
|
3298
|
+
if (mode === "probe") return 0;
|
|
3299
|
+
return report.missing.length > 0 ? 1 : 0;
|
|
3300
|
+
} catch (e) {
|
|
3301
|
+
const detail = e instanceof Error ? e.message : String(e);
|
|
3302
|
+
const context = { ok: false, mode, backend: options.backend ?? "auto", exitCode: 2, error: detail };
|
|
3303
|
+
io.error(
|
|
3304
|
+
options.json ? JSON.stringify(context) : `\u8BB0\u5FC6 FTS ${mode === "apply" ? "\u91CD\u5EFA" : "\u63A2\u6D4B"}\u5931\u8D25\uFF1A${detail}\uFF08backend=${context.backend}, exitCode=2\uFF09`
|
|
3305
|
+
);
|
|
3306
|
+
return 2;
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
|
|
2941
3310
|
// src/cli/index.ts
|
|
2942
3311
|
var { version } = JSON.parse(
|
|
2943
|
-
|
|
3312
|
+
readFileSync14(new URL("../package.json", import.meta.url), "utf-8")
|
|
2944
3313
|
);
|
|
2945
3314
|
async function modelDownloadCommand(options) {
|
|
2946
3315
|
try {
|
|
@@ -2962,4 +3331,13 @@ program.command("stack").description("\u7BA1\u7406\u6280\u672F\u6808\u7EA6\u675F
|
|
|
2962
3331
|
(sub, name, options) => stackCommand(sub, name, options)
|
|
2963
3332
|
);
|
|
2964
3333
|
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);
|
|
3334
|
+
program.command("memory:reindex").description("\u8BB0\u5FC6 FTS \u671F\u671B\u6001\u63A2\u6D4B/\u91CD\u5EFA\uFF08\u81EA\u52A9\u4FEE\u590D\u5165\u53E3\uFF1A\u7F3A\u5BF9\u8C61\u5219\u8865\u9F50\uFF0C\u53CC\u540E\u7AEF postgres/sqlite\uFF09").option("--probe", "\u53EA\u8BFB\u63A2\u6D4B\u7F3A\u5931\u5BF9\u8C61\uFF08\u9ED8\u8BA4\u884C\u4E3A\uFF09").option("--apply", "\u5E94\u7528\u7F3A\u5931\u5BF9\u8C61\u5E76\u590D\u63A2\uFF08\u4ECD\u6709\u7F3A\u5931\u5219\u9000\u51FA\u7801 1\uFF09").option("--backend <type>", "\u540E\u7AEF: postgres | sqlite\uFF08\u7F3A\u7701\u6309\u9879\u76EE Prisma datasource \u81EA\u52A8\u5224\u5B9A\uFF09").option("--json", "\u4EE5 JSON \u8F93\u51FA\u62A5\u544A\uFF08\u4F9B\u81EA\u52A8\u5316\u6D88\u8D39\uFF09").action(async (options) => {
|
|
3335
|
+
const backend = options.backend === "postgres" || options.backend === "sqlite" ? options.backend : void 0;
|
|
3336
|
+
if (options.backend && !backend) {
|
|
3337
|
+
console.error(`\u672A\u77E5 backend: ${options.backend}\uFF08\u53EF\u9009 postgres | sqlite\uFF09`);
|
|
3338
|
+
process.exitCode = 2;
|
|
3339
|
+
return;
|
|
3340
|
+
}
|
|
3341
|
+
process.exitCode = await memoryReindexCommand({ ...options, backend });
|
|
3342
|
+
});
|
|
2965
3343
|
program.parse();
|