add-coder 0.3.38 → 0.3.40
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 +16 -3
- package/README.md +16 -3
- package/dist/index.js +421 -70
- package/package.json +3 -2
- package/templates/.add-coder-src-hash.json +74 -61
- package/templates/adapters/claude/hooks/post-tool-use.mjs +1 -1
- package/templates/adapters/claude/hooks/prompt-submit.mjs +1 -1
- package/templates/adapters/claude/hooks/session-start.mjs +25 -5
- package/templates/adapters/claude/hooks/stop-check.mjs +2 -2
- package/templates/adapters/codex/hooks/post-tool-use.mjs +1 -1
- package/templates/adapters/codex/hooks/prompt-submit.mjs +1 -1
- package/templates/adapters/codex/hooks/session-start.mjs +25 -5
- package/templates/adapters/codex/hooks/stop-check.mjs +2 -2
- package/templates/adapters/qoder/hooks/post-tool-use.mjs +1 -1
- package/templates/adapters/qoder/hooks/prompt-submit.mjs +1 -1
- package/templates/adapters/qoder/hooks/session-start.mjs +25 -5
- package/templates/adapters/qoder/hooks/stop-check.mjs +2 -2
- package/templates/adapters/trae/hooks/post-tool-use.mjs +1 -1
- package/templates/adapters/trae/hooks/prompt-submit.mjs +1 -1
- package/templates/adapters/trae/hooks/session-start.mjs +25 -5
- package/templates/adapters/trae/hooks/stop-check.mjs +2 -2
- package/templates/adapters/vscode/hooks/post-tool-use.mjs +1 -1
- package/templates/adapters/vscode/hooks/prompt-submit.mjs +1 -1
- package/templates/adapters/vscode/hooks/session-start.mjs +25 -5
- package/templates/adapters/vscode/hooks/stop-check.mjs +2 -2
- package/templates/core/AGENTS.md +46 -0
- package/templates/core/docs/ADD-governance-claude-code.md +52 -0
- package/templates/core/docs/ADD-governance-codex.md +62 -0
- package/templates/core/docs/ADD-governance-qoder-cn.md +30 -0
- package/templates/core/docs/ADD-governance-trae.md +42 -0
- package/templates/core/docs/ADD-governance-vscode-copilot.md +88 -0
- package/templates/core/governance/rules.ts +2 -2
- package/templates/core/governance/session-start-guard.ts +28 -4
- package/templates/core/prisma/add.prisma +4 -0
- package/templates/core/scripts/mcp-server/resources/hitl-approval-widget.ts +4 -2
- package/templates/core/scripts/mcp-server/resources/index.ts +2 -0
- package/templates/core/scripts/mcp-server/resources/widget-probe.ts +53 -0
- package/templates/core/scripts/mcp-server/shared/db-types.ts +5 -1
- package/templates/core/scripts/mcp-server/shared/dev-operation.ts +97 -0
- package/templates/core/scripts/mcp-server/shared/hitl-ui.ts +46 -1
- package/templates/core/scripts/mcp-server/shared/memory/jobs/snapshot.ts +9 -0
- package/templates/core/scripts/mcp-server/shared/memory/retrieval/cjk-segmenter.ts +140 -0
- package/templates/core/scripts/mcp-server/shared/memory/retrieval/cjk-tokenize.ts +62 -0
- package/templates/core/scripts/mcp-server/shared/memory/retrieval/fts/pg.ts +68 -16
- package/templates/core/scripts/mcp-server/shared/memory/retrieval/fts/sqlite-fts5.sql +3 -3
- package/templates/core/scripts/mcp-server/shared/memory/retrieval/fts/sqlite.ts +23 -6
- package/templates/core/scripts/mcp-server/shared/memory/retrieval/fts-fingerprint.ts +116 -0
- package/templates/core/scripts/mcp-server/shared/memory/retrieval/query-terms.ts +9 -25
- package/templates/core/scripts/mcp-server/shared/memory/switches.ts +4 -2
- package/templates/core/scripts/mcp-server/shared/plan-lifecycle.ts +16 -3
- package/templates/core/scripts/mcp-server/shared/server-capabilities.ts +65 -0
- package/templates/core/scripts/mcp-server/tools/audit.ts +16 -34
- package/templates/core/scripts/mcp-server/tools/gateway/check_add_route_completeness.ts +17 -4
- package/templates/core/scripts/mcp-server/tools/gateway/check_add_route_status.ts +34 -6
- package/templates/core/scripts/mcp-server/tools/gateway/check_dps.ts +15 -25
- package/templates/core/scripts/mcp-server/tools/gateway/check_rahs.ts +23 -26
- package/templates/core/scripts/mcp-server/tools/gateway/check_spec_sync.ts +56 -38
- package/templates/core/scripts/mcp-server/tools/gateway/plan-resolve.ts +270 -0
- package/templates/core/scripts/mcp-server/tools/hitl.ts +18 -8
- package/templates/core/scripts/mcp-server/tools/index.ts +7 -2
- package/templates/core/scripts/mcp-server/tools/memory.ts +235 -1
- package/templates/core/scripts/mcp-server/tools/plan-lifecycle.ts +219 -0
- package/templates/core/scripts/mcp-server/tools/plan.ts +2 -0
- package/templates/core/scripts/mcp-server/tools/widget-probe.ts +53 -0
- package/templates/core/scripts/mcp-server.ts +5 -1
- package/templates/core/scripts/memory/backfill-search-text.ts +132 -0
- package/templates/core/scripts/memory/memory-jobs.ts +124 -0
- package/templates/core/scripts/plan-close.ts +85 -0
- package/templates/core/skills/add-paradigm/SKILL.md +19 -4
- package/templates/core/skills/session-init/SKILL.md +27 -0
- package/templates/core/templates/add-route-template-heavyweight.md +2 -0
- package/templates/core/templates/add-route-template.md +4 -0
- package/templates/core/templates/checklist-template.md +2 -0
- package/templates/core/templates/handoff-multi-round-template.md +2 -0
- package/templates/core/templates/review-implementation-template.md +18 -0
- package/templates/core/templates/review-implementation-template.schema.json +7 -1
- package/templates/core/templates/simple-plan-template.md +4 -0
- package/templates/core/vocabulary/add-governance-vocabulary.md +2 -0
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 readFileSync17 } from "fs";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
|
|
7
7
|
// src/caijuehub/strategies/detect.strategy.ts
|
|
@@ -1548,7 +1548,7 @@ async function injectPrisma2(projectRoot, options = {}) {
|
|
|
1548
1548
|
}
|
|
1549
1549
|
|
|
1550
1550
|
// src/cli/commands/init.ts
|
|
1551
|
-
import { readFileSync as
|
|
1551
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, existsSync as existsSync14, mkdirSync as mkdirSync6, copyFileSync as copyFileSync2, readdirSync as readdirSync5 } from "fs";
|
|
1552
1552
|
import { createHash } from "crypto";
|
|
1553
1553
|
import { resolve as resolve9, dirname as dirname3, join as pathJoin } from "path";
|
|
1554
1554
|
import { homedir as homedir2 } from "os";
|
|
@@ -1666,6 +1666,68 @@ function applyMemoryExpectedState(opts) {
|
|
|
1666
1666
|
}
|
|
1667
1667
|
}
|
|
1668
1668
|
|
|
1669
|
+
// src/lib/memory-snapshot-install.ts
|
|
1670
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
|
|
1671
|
+
import { join as join10 } from "path";
|
|
1672
|
+
var ENV_FILE_CANDIDATES = [".env.development.local", ".env.development", ".env.local", ".env"];
|
|
1673
|
+
function readDatabaseUrlFromEnvFiles(projectRoot) {
|
|
1674
|
+
for (const f of ENV_FILE_CANDIDATES) {
|
|
1675
|
+
const p = join10(projectRoot, f);
|
|
1676
|
+
if (!existsSync13(p)) continue;
|
|
1677
|
+
const text = readFileSync10(p, "utf-8");
|
|
1678
|
+
const m = text.match(/^\s*DATABASE_URL\s*=\s*"?([^"\n\r]+)"?\s*$/m);
|
|
1679
|
+
if (m?.[1]) return m[1].trim();
|
|
1680
|
+
}
|
|
1681
|
+
return void 0;
|
|
1682
|
+
}
|
|
1683
|
+
function memoryJobsScriptRelPath(magicDir) {
|
|
1684
|
+
return `${magicDir}/scripts/memory/memory-jobs.ts`;
|
|
1685
|
+
}
|
|
1686
|
+
function refreshL1SnapshotOnInstall(opts) {
|
|
1687
|
+
const relScript = memoryJobsScriptRelPath(opts.magicDir);
|
|
1688
|
+
const absScript = join10(opts.projectRoot, opts.magicDir, "scripts", "memory", "memory-jobs.ts");
|
|
1689
|
+
const cmd = `npx tsx ${relScript} refresh-l1`;
|
|
1690
|
+
if (!existsSync13(absScript)) {
|
|
1691
|
+
return {
|
|
1692
|
+
ok: false,
|
|
1693
|
+
relScript,
|
|
1694
|
+
cmd,
|
|
1695
|
+
detail: `\u8BB0\u5FC6\u5165\u53E3\u811A\u672C\u7F3A\u5931: ${relScript}\uFF08\u6A21\u677F\u672A\u5C31\u7EEA\uFF1A\u5148\u6267\u884C add-coder sync\uFF0C\u6216\u786E\u8BA4 add-coder \u7248\u672C \u2265 \u672C Plan \u7248\u672C\uFF09`
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
const env = opts.env ?? process.env;
|
|
1699
|
+
const databaseUrl = env.DATABASE_URL || readDatabaseUrlFromEnvFiles(opts.projectRoot);
|
|
1700
|
+
if (!databaseUrl) {
|
|
1701
|
+
return {
|
|
1702
|
+
ok: false,
|
|
1703
|
+
relScript,
|
|
1704
|
+
cmd,
|
|
1705
|
+
detail: `\u672A\u627E\u5230 DATABASE_URL\uFF08\u68C0\u67E5 ${ENV_FILE_CANDIDATES.join(" / ")}\uFF0C\u6216\u5728\u73AF\u5883\u4E2D\u663E\u5F0F\u5BFC\u51FA\uFF09`
|
|
1706
|
+
};
|
|
1707
|
+
}
|
|
1708
|
+
const runner = opts.run ?? ((c, a, o) => runCommand(c, a, o));
|
|
1709
|
+
const result = runner(
|
|
1710
|
+
"npx",
|
|
1711
|
+
["tsx", absScript, "refresh-l1"],
|
|
1712
|
+
{
|
|
1713
|
+
cwd: opts.projectRoot,
|
|
1714
|
+
env: { ...env, PROJECT_ROOT: opts.projectRoot, MAGIC_DIR: opts.magicDir, DATABASE_URL: databaseUrl },
|
|
1715
|
+
timeout: opts.timeoutMs ?? 12e4
|
|
1716
|
+
}
|
|
1717
|
+
);
|
|
1718
|
+
if (result.status !== 0) {
|
|
1719
|
+
const why = (result.stderr || "").trim().split("\n").slice(0, 4).join(" | ");
|
|
1720
|
+
return {
|
|
1721
|
+
ok: false,
|
|
1722
|
+
relScript,
|
|
1723
|
+
cmd,
|
|
1724
|
+
detail: `memory-jobs refresh-l1 \u9000\u51FA\u7801 ${result.status}${why ? ` \u2014\u2014 ${why}` : "\uFF08\u65E0 stderr \u8F93\u51FA\uFF09"}`,
|
|
1725
|
+
stdout: result.stdout
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1728
|
+
return { ok: true, relScript, cmd, stdout: result.stdout };
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1669
1731
|
// src/cli/commands/init.ts
|
|
1670
1732
|
var ADAPTER_RENDERERS = {
|
|
1671
1733
|
claude: renderAdapter,
|
|
@@ -1697,6 +1759,17 @@ async function initCommand(options) {
|
|
|
1697
1759
|
const dbFail = await deployDatabase(ctx);
|
|
1698
1760
|
ensurePortsContract(ctx.projectRoot, ctx.config, !!options.dryRun);
|
|
1699
1761
|
deployDocs(ctx);
|
|
1762
|
+
if (!options.dryRun) {
|
|
1763
|
+
const snap = refreshL1SnapshotOnInstall({ projectRoot: ctx.projectRoot, magicDir: ctx.magicDir });
|
|
1764
|
+
if (!snap.ok) {
|
|
1765
|
+
console.error(`
|
|
1766
|
+
\u2717 \u8BB0\u5FC6\u5FEB\u7167\u672A\u5C31\u7EEA: ${snap.detail}`);
|
|
1767
|
+
console.error(` \u590D\u73B0\u547D\u4EE4: ${snap.cmd}`);
|
|
1768
|
+
console.error(" \u4FEE\u590D\u540E\u91CD\u65B0\u8FD0\u884C add-coder init\uFF08\u8BE5\u6B65\u9AA4\u4E3A\u786C\u95E8\u7981\uFF09\u3002");
|
|
1769
|
+
process.exit(1);
|
|
1770
|
+
}
|
|
1771
|
+
console.log(`\u2705 \u8BB0\u5FC6 L1 \u5FEB\u7167\u5DF2\u5237\u65B0: ${snap.relScript}`);
|
|
1772
|
+
}
|
|
1700
1773
|
finalize(ctx, result, dbFail);
|
|
1701
1774
|
if (!options.dryRun) {
|
|
1702
1775
|
try {
|
|
@@ -1732,7 +1805,7 @@ async function handleCodexMcpConfig(options) {
|
|
|
1732
1805
|
console.log(`[dry-run] \u5C06\u5199\u5165 ${userConfigPath}`);
|
|
1733
1806
|
return;
|
|
1734
1807
|
}
|
|
1735
|
-
if (
|
|
1808
|
+
if (existsSync14(userConfigPath) && readFileSync11(userConfigPath, "utf-8").includes("mcp_servers.add_coder")) {
|
|
1736
1809
|
console.log(`\u26A0\uFE0F ${userConfigPath} \u5DF2\u5B58\u5728 mcp_servers.add_coder \u914D\u7F6E\uFF0C\u8DF3\u8FC7\uFF08\u907F\u514D\u91CD\u590D\uFF09`);
|
|
1737
1810
|
return;
|
|
1738
1811
|
}
|
|
@@ -1741,7 +1814,7 @@ async function handleCodexMcpConfig(options) {
|
|
|
1741
1814
|
console.log("\u5DF2\u53D6\u6D88\u5199\u5165");
|
|
1742
1815
|
return;
|
|
1743
1816
|
}
|
|
1744
|
-
if (
|
|
1817
|
+
if (existsSync14(userConfigPath)) {
|
|
1745
1818
|
const backup = `${userConfigPath}.bak-${Date.now()}`;
|
|
1746
1819
|
copyFileSync2(userConfigPath, backup);
|
|
1747
1820
|
console.log(`\u5DF2\u5907\u4EFD\u539F\u914D\u7F6E \u2192 ${backup}`);
|
|
@@ -1917,18 +1990,18 @@ main().catch((e) => { console.error(e); process.exit(1); });
|
|
|
1917
1990
|
console.log(`[dry-run] \u5C06\u5199\u5165 ${scriptPath}`);
|
|
1918
1991
|
return;
|
|
1919
1992
|
}
|
|
1920
|
-
if (!
|
|
1993
|
+
if (!existsSync14(scriptsDir)) mkdirSync6(scriptsDir, { recursive: true });
|
|
1921
1994
|
writeFileSync6(scriptPath, content, "utf-8");
|
|
1922
1995
|
console.log("\u5DF2\u751F\u6210 scripts/export-db.ts");
|
|
1923
1996
|
}
|
|
1924
1997
|
function injectDbExportScript(projectRoot, dryRun) {
|
|
1925
1998
|
const pkgPath = resolve9(projectRoot, "package.json");
|
|
1926
|
-
if (!
|
|
1999
|
+
if (!existsSync14(pkgPath)) return;
|
|
1927
2000
|
if (dryRun) {
|
|
1928
2001
|
console.log("[dry-run] \u5C06\u6CE8\u5165 db:export");
|
|
1929
2002
|
return;
|
|
1930
2003
|
}
|
|
1931
|
-
const pkg = JSON.parse(
|
|
2004
|
+
const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
|
|
1932
2005
|
if (!pkg.scripts) pkg.scripts = {};
|
|
1933
2006
|
if (!pkg.scripts["db:export"]) {
|
|
1934
2007
|
pkg.scripts["db:export"] = "npx tsx scripts/export-db.ts";
|
|
@@ -1976,14 +2049,14 @@ function writeComposeEnv(ctx) {
|
|
|
1976
2049
|
if (!db.reuseExisting) {
|
|
1977
2050
|
const composeName = db.container === "podman" ? "podman-compose.add.yml" : "docker-compose.add.yml";
|
|
1978
2051
|
const composePath = resolve9(projectRoot, composeName);
|
|
1979
|
-
if (!options.dryRun && (!
|
|
2052
|
+
if (!options.dryRun && (!existsSync14(composePath) || options.force)) {
|
|
1980
2053
|
writeFileSync6(composePath, composeContent(config.projectName || "add-project"), "utf-8");
|
|
1981
2054
|
console.log(`\u5DF2\u521B\u5EFA ${composeName}`);
|
|
1982
2055
|
}
|
|
1983
2056
|
}
|
|
1984
2057
|
const devEnvPath = resolve9(projectRoot, ".env.development");
|
|
1985
|
-
if (!options.dryRun &&
|
|
1986
|
-
const existing =
|
|
2058
|
+
if (!options.dryRun && existsSync14(devEnvPath)) {
|
|
2059
|
+
const existing = readFileSync11(devEnvPath, "utf-8");
|
|
1987
2060
|
if (!/^DATABASE_USER=/m.test(existing)) {
|
|
1988
2061
|
writeFileSync6(devEnvPath, existing + `
|
|
1989
2062
|
DATABASE_USER=${db.user || "admin"}
|
|
@@ -2030,7 +2103,7 @@ async function renderAndWrite(ctx) {
|
|
|
2030
2103
|
const reviewTargets = target === "codex" ? [magicDir] : [ADD_DIR, magicDir];
|
|
2031
2104
|
for (const d of reviewTargets) {
|
|
2032
2105
|
const reviewsDir = resolve9(projectRoot, d, "reviews");
|
|
2033
|
-
if (!
|
|
2106
|
+
if (!existsSync14(reviewsDir)) {
|
|
2034
2107
|
if (dry) {
|
|
2035
2108
|
console.log(`[dry-run] \u5C06\u521B\u5EFA ${reviewsDir}/`);
|
|
2036
2109
|
} else {
|
|
@@ -2042,7 +2115,7 @@ async function renderAndWrite(ctx) {
|
|
|
2042
2115
|
const hashMap = {};
|
|
2043
2116
|
let npmVer = "";
|
|
2044
2117
|
try {
|
|
2045
|
-
npmVer = JSON.parse(
|
|
2118
|
+
npmVer = JSON.parse(readFileSync11(resolve9(projectRoot, "node_modules", "add-coder", "templates", ".add-coder-src-hash.json"), "utf-8"))._version ?? "";
|
|
2046
2119
|
} catch {
|
|
2047
2120
|
}
|
|
2048
2121
|
for (const [rp, c] of allFiles) {
|
|
@@ -2081,7 +2154,7 @@ ${bashRun.stderr.trim().split("\n").slice(0, 5).join("\n")}` : ""}`;
|
|
|
2081
2154
|
}
|
|
2082
2155
|
if (db.engine === "postgresql" && db.container === "manual") {
|
|
2083
2156
|
const dbScript = resolve9(projectRoot, magicDir, "scripts", "db-ensure.sh");
|
|
2084
|
-
if (
|
|
2157
|
+
if (existsSync14(dbScript)) {
|
|
2085
2158
|
try {
|
|
2086
2159
|
const bashRun = runCommand("bash", [dbScript, "postgresql", "manual"], { cwd: projectRoot, stdio: "inherit" });
|
|
2087
2160
|
if (bashRun.status !== 0) fail = `db-ensure.sh \u9000\u51FA\u7801: ${bashRun.status}${bashRun.stderr ? `
|
|
@@ -2139,12 +2212,12 @@ function deployDocs(ctx) {
|
|
|
2139
2212
|
for (const d of ["00-\u9700\u6C42", "01-\u67B6\u6784", "02-\u89C4\u8303"]) {
|
|
2140
2213
|
const srcDir = resolve9(groundingSrc, d);
|
|
2141
2214
|
const destDir = resolve9(docsBase, d);
|
|
2142
|
-
if (!
|
|
2143
|
-
if (!
|
|
2215
|
+
if (!existsSync14(destDir)) mkdirSync6(destDir, { recursive: true });
|
|
2216
|
+
if (!existsSync14(srcDir)) continue;
|
|
2144
2217
|
for (const f of readdirSync5(srcDir)) {
|
|
2145
2218
|
const src = resolve9(srcDir, f);
|
|
2146
2219
|
const dest = resolve9(destDir, f);
|
|
2147
|
-
if (
|
|
2220
|
+
if (existsSync14(dest)) continue;
|
|
2148
2221
|
try {
|
|
2149
2222
|
copyFileSync2(src, dest);
|
|
2150
2223
|
} catch {
|
|
@@ -2160,7 +2233,7 @@ function finalize(ctx, result, dbFail) {
|
|
|
2160
2233
|
return;
|
|
2161
2234
|
}
|
|
2162
2235
|
if (db.engine === "sqlite") console.log("\u6570\u636E\u5907\u4EFD: npm run db:export \u2192 data/exports/");
|
|
2163
|
-
const pkg = JSON.parse(
|
|
2236
|
+
const pkg = JSON.parse(readFileSync11(resolve9(import.meta.dirname, "../package.json"), "utf-8"));
|
|
2164
2237
|
const peerNames = Object.keys(pkg.peerDependencies || {});
|
|
2165
2238
|
if (peerNames.length > 0) {
|
|
2166
2239
|
console.log(`
|
|
@@ -2193,7 +2266,7 @@ function finalize(ctx, result, dbFail) {
|
|
|
2193
2266
|
}
|
|
2194
2267
|
|
|
2195
2268
|
// src/cli/commands/sync.ts
|
|
2196
|
-
import { existsSync as
|
|
2269
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7 } from "fs";
|
|
2197
2270
|
import { resolve as resolve10, dirname as dirname4 } from "path";
|
|
2198
2271
|
import { createHash as createHash2 } from "crypto";
|
|
2199
2272
|
|
|
@@ -2267,7 +2340,7 @@ function resolvePatchConflictMode(yes = false, isTTY = Boolean(process.stdin.isT
|
|
|
2267
2340
|
}
|
|
2268
2341
|
function loadHashFile(root, magic) {
|
|
2269
2342
|
try {
|
|
2270
|
-
const raw = JSON.parse(
|
|
2343
|
+
const raw = JSON.parse(readFileSync12(resolve10(root, magic, SYNC_CONFIG.HASH_OUTPUT_FILE), "utf-8"));
|
|
2271
2344
|
const normalized = {};
|
|
2272
2345
|
for (const [k, v] of Object.entries(raw)) normalized[normalizeRelPath(k)] = v;
|
|
2273
2346
|
return normalized;
|
|
@@ -2277,7 +2350,7 @@ function loadHashFile(root, magic) {
|
|
|
2277
2350
|
}
|
|
2278
2351
|
function loadVersionFile(root, magic) {
|
|
2279
2352
|
try {
|
|
2280
|
-
return
|
|
2353
|
+
return readFileSync12(resolve10(root, magic, SYNC_CONFIG.VERSION_SENTINEL), "utf-8").trim();
|
|
2281
2354
|
} catch {
|
|
2282
2355
|
return "";
|
|
2283
2356
|
}
|
|
@@ -2293,7 +2366,7 @@ function loadTemplateVersion(root) {
|
|
|
2293
2366
|
];
|
|
2294
2367
|
for (const candidate of candidates) {
|
|
2295
2368
|
try {
|
|
2296
|
-
return JSON.parse(
|
|
2369
|
+
return JSON.parse(readFileSync12(candidate, "utf-8"))._version ?? "";
|
|
2297
2370
|
} catch {
|
|
2298
2371
|
}
|
|
2299
2372
|
}
|
|
@@ -2404,13 +2477,13 @@ async function syncCommand(options = {}) {
|
|
|
2404
2477
|
for (const [relPath, content] of candidates) {
|
|
2405
2478
|
const key = normalizeRelPath(relPath);
|
|
2406
2479
|
const absPath = resolve10(projectRoot, relPath);
|
|
2407
|
-
if (!
|
|
2480
|
+
if (!existsSync15(absPath)) {
|
|
2408
2481
|
missingFiles.set(key, content);
|
|
2409
2482
|
} else if (establishBaseline) {
|
|
2410
2483
|
missingFiles.set(key, content);
|
|
2411
2484
|
} else {
|
|
2412
2485
|
const classification = classifyPatchCandidate(
|
|
2413
|
-
|
|
2486
|
+
readFileSync12(absPath, "utf-8"),
|
|
2414
2487
|
content,
|
|
2415
2488
|
outHash[key]
|
|
2416
2489
|
);
|
|
@@ -2451,7 +2524,7 @@ async function syncCommand(options = {}) {
|
|
|
2451
2524
|
let finalHash = mergeFullHash(
|
|
2452
2525
|
outHash,
|
|
2453
2526
|
[...candidates].map(([relPath]) => ({ relPath, absPath: resolve10(projectRoot, relPath) })),
|
|
2454
|
-
(absPath) =>
|
|
2527
|
+
(absPath) => existsSync15(absPath) ? hash8(readFileSync12(absPath, "utf-8")) : null,
|
|
2455
2528
|
unresolvedConflictKeys
|
|
2456
2529
|
);
|
|
2457
2530
|
if (target === "codex") {
|
|
@@ -2462,16 +2535,18 @@ async function syncCommand(options = {}) {
|
|
|
2462
2535
|
await checkPrismaDiff(projectRoot, options);
|
|
2463
2536
|
ensurePortsContract(projectRoot, config);
|
|
2464
2537
|
await maybeModelDownload(options);
|
|
2538
|
+
refreshSnapshotOrExit(projectRoot, magicDir);
|
|
2465
2539
|
return;
|
|
2466
2540
|
}
|
|
2467
2541
|
const missing = /* @__PURE__ */ new Map();
|
|
2468
2542
|
for (const [relPath, content] of allFiles) {
|
|
2469
|
-
if (!
|
|
2543
|
+
if (!existsSync15(resolve10(projectRoot, relPath))) {
|
|
2470
2544
|
missing.set(relPath, content);
|
|
2471
2545
|
}
|
|
2472
2546
|
}
|
|
2473
2547
|
if (missing.size === 0) {
|
|
2474
2548
|
console.log(SYNC_CONFIG.PROMPT_FULL);
|
|
2549
|
+
refreshSnapshotOrExit(projectRoot, magicDir);
|
|
2475
2550
|
return;
|
|
2476
2551
|
}
|
|
2477
2552
|
let filesToWrite = missing;
|
|
@@ -2487,10 +2562,22 @@ async function syncCommand(options = {}) {
|
|
|
2487
2562
|
await checkPrismaDiff(projectRoot, options);
|
|
2488
2563
|
ensurePortsContract(projectRoot, config);
|
|
2489
2564
|
await maybeModelDownload(options);
|
|
2565
|
+
refreshSnapshotOrExit(projectRoot, magicDir);
|
|
2566
|
+
}
|
|
2567
|
+
function refreshSnapshotOrExit(projectRoot, magicDir) {
|
|
2568
|
+
const snap = refreshL1SnapshotOnInstall({ projectRoot, magicDir });
|
|
2569
|
+
if (!snap.ok) {
|
|
2570
|
+
console.error(`
|
|
2571
|
+
\u2717 \u8BB0\u5FC6\u5FEB\u7167\u672A\u5C31\u7EEA: ${snap.detail}`);
|
|
2572
|
+
console.error(` \u590D\u73B0\u547D\u4EE4: ${snap.cmd}`);
|
|
2573
|
+
console.error(" \u4FEE\u590D\u540E\u91CD\u65B0\u8FD0\u884C add-coder sync\uFF08\u8BE5\u6B65\u9AA4\u4E3A\u786C\u95E8\u7981\uFF09\u3002");
|
|
2574
|
+
process.exit(1);
|
|
2575
|
+
}
|
|
2576
|
+
console.log(`\u2705 \u8BB0\u5FC6 L1 \u5FEB\u7167\u5DF2\u5237\u65B0: ${snap.relScript}`);
|
|
2490
2577
|
}
|
|
2491
2578
|
function printMigrateGuidance(targetPath, changed) {
|
|
2492
2579
|
const g = SYNC_PRISMA_CONFIG.POST_SYNC;
|
|
2493
|
-
const isManaged =
|
|
2580
|
+
const isManaged = existsSync15(resolve10(dirname4(targetPath), "migrations"));
|
|
2494
2581
|
const actions = isManaged ? g.MANAGED_ACTIONS : g.UNMANAGED_ACTIONS;
|
|
2495
2582
|
console.log(` \u25B6 \u5DF2\u5199\u5165 ${changed} \u5904\u53D8\u66F4\uFF0C${g.HEADER}`);
|
|
2496
2583
|
for (const a of actions) {
|
|
@@ -2529,7 +2616,7 @@ async function ensureAtlasCapability(projectRoot, yes = false) {
|
|
|
2529
2616
|
console.log(" \u6587\u6863: README.md \u2192 \u7AE0\u8282\u300CAtlas \u6570\u636E\u5E93\u540C\u6B65\u80FD\u529B\u300D/ DEVELOPMENT.md \xA7\u4E5D");
|
|
2530
2617
|
return;
|
|
2531
2618
|
}
|
|
2532
|
-
const pm =
|
|
2619
|
+
const pm = existsSync15(resolve10(projectRoot, "pnpm-lock.yaml")) ? "pnpm" : "npm";
|
|
2533
2620
|
console.log(` \u5B89\u88C5 ${pm} add -D @ariga/atlas ...`);
|
|
2534
2621
|
const r = runCommand(pm, ["add", "-D", "@ariga/atlas"], { cwd: projectRoot, timeout: 18e4 });
|
|
2535
2622
|
if (r.status !== 0) {
|
|
@@ -2542,8 +2629,8 @@ async function ensureAtlasCapability(projectRoot, yes = false) {
|
|
|
2542
2629
|
}
|
|
2543
2630
|
function checkHostAtlasSegment(projectRoot) {
|
|
2544
2631
|
const script = resolve10(projectRoot, "scripts", "db-ensure.sh");
|
|
2545
|
-
if (!
|
|
2546
|
-
const content =
|
|
2632
|
+
if (!existsSync15(script)) return;
|
|
2633
|
+
const content = readFileSync12(script, "utf-8");
|
|
2547
2634
|
if (content.includes("atlas_sync")) return;
|
|
2548
2635
|
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");
|
|
2549
2636
|
console.warn(" \u804C\u8D23\u8FB9\u754C: add-coder \u53EA\u540C\u6B65 ADD \u6CBB\u7406\u6A21\u578B(7 \u8868)\uFF1B\u5BBF\u4E3B\u4E1A\u52A1\u8868 diff \u63A8\u8350 Atlas \u4F46\u4E0D\u5F3A\u6C42");
|
|
@@ -2557,7 +2644,7 @@ async function checkPrismaDiff(projectRoot, options) {
|
|
|
2557
2644
|
if (!options.patch) return;
|
|
2558
2645
|
const basePath = resolve10(projectRoot, SYNC_PRISMA_CONFIG.BASE_SCHEMA);
|
|
2559
2646
|
const targetPath = resolve10(projectRoot, SYNC_PRISMA_CONFIG.TARGET_PATTERN);
|
|
2560
|
-
if (!
|
|
2647
|
+
if (!existsSync15(basePath)) {
|
|
2561
2648
|
console.log(`
|
|
2562
2649
|
\u26A0\uFE0F \u57FA\u51C6 schema \u4E0D\u5B58\u5728: ${basePath}`);
|
|
2563
2650
|
console.log(` \u8BF7\u786E\u4FDD add-coder \u5DF2\u6B63\u786E\u5B89\u88C5\u3002`);
|
|
@@ -2571,7 +2658,7 @@ async function checkPrismaDiff(projectRoot, options) {
|
|
|
2571
2658
|
\u2705 Prisma schema \u4E0E add-coder \u6807\u51C6\u4E00\u81F4\uFF0C\u65E0\u9700\u540C\u6B65\u3002`);
|
|
2572
2659
|
return;
|
|
2573
2660
|
}
|
|
2574
|
-
const targetExists =
|
|
2661
|
+
const targetExists = existsSync15(targetPath);
|
|
2575
2662
|
let modifiedCount = 0;
|
|
2576
2663
|
console.log(`
|
|
2577
2664
|
\u26A0\uFE0F Prisma schema \u5DEE\u5F02\u68C0\u6D4B:`);
|
|
@@ -2722,7 +2809,7 @@ async function handleDiffAction(action, ctx) {
|
|
|
2722
2809
|
}
|
|
2723
2810
|
}
|
|
2724
2811
|
function injectMissingModels(targetPath, models) {
|
|
2725
|
-
let content =
|
|
2812
|
+
let content = readFileSync12(targetPath, "utf-8");
|
|
2726
2813
|
content = content.replace(/\n*$/, "\n");
|
|
2727
2814
|
content += `
|
|
2728
2815
|
// ===== \u7531 add-coder sync --patch \u81EA\u52A8\u6CE8\u5165 (${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}) =====
|
|
@@ -2735,7 +2822,7 @@ function injectMissingModels(targetPath, models) {
|
|
|
2735
2822
|
return models.length;
|
|
2736
2823
|
}
|
|
2737
2824
|
function getBaseFieldLines(basePath, modelName) {
|
|
2738
|
-
const content =
|
|
2825
|
+
const content = readFileSync12(basePath, "utf-8");
|
|
2739
2826
|
const blocks = parseSchemaBlocks(content);
|
|
2740
2827
|
const block = blocks.get(`model:${modelName}`) ?? blocks.get(`enum:${modelName}`);
|
|
2741
2828
|
if (!block) return {};
|
|
@@ -2758,7 +2845,7 @@ function injectFieldLines(targetPath, basePath, modelName, fieldKeys) {
|
|
|
2758
2845
|
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`);
|
|
2759
2846
|
return 0;
|
|
2760
2847
|
}
|
|
2761
|
-
const content =
|
|
2848
|
+
const content = readFileSync12(targetPath, "utf-8");
|
|
2762
2849
|
const lines = content.split("\n");
|
|
2763
2850
|
const blocks = parseSchemaBlocks(content);
|
|
2764
2851
|
const target = blocks.get(`model:${modelName}`) ?? blocks.get(`enum:${modelName}`);
|
|
@@ -2805,7 +2892,7 @@ function injectFieldLines(targetPath, basePath, modelName, fieldKeys) {
|
|
|
2805
2892
|
function overwriteFieldLines(targetPath, basePath, modelName, conflicts) {
|
|
2806
2893
|
const baseFields = getBaseFieldLines(basePath, modelName);
|
|
2807
2894
|
if (Object.keys(baseFields).length === 0) return 0;
|
|
2808
|
-
let content =
|
|
2895
|
+
let content = readFileSync12(targetPath, "utf-8");
|
|
2809
2896
|
let count = 0;
|
|
2810
2897
|
for (const { fieldName } of conflicts) {
|
|
2811
2898
|
const baseLine = baseFields[fieldName];
|
|
@@ -2821,8 +2908,176 @@ function overwriteFieldLines(targetPath, basePath, modelName, conflicts) {
|
|
|
2821
2908
|
}
|
|
2822
2909
|
|
|
2823
2910
|
// src/cli/commands/status.ts
|
|
2824
|
-
import { existsSync as
|
|
2825
|
-
import {
|
|
2911
|
+
import { existsSync as existsSync16, readFileSync as readFileSync13 } from "fs";
|
|
2912
|
+
import { homedir as homedir3 } from "os";
|
|
2913
|
+
import { join as join11, resolve as resolve11 } from "path";
|
|
2914
|
+
var VIRTUAL_TOOLS_SETTING_KEY = "github.copilot.chat.virtualTools.threshold";
|
|
2915
|
+
function stripJsonComments(text) {
|
|
2916
|
+
let out = "";
|
|
2917
|
+
let inString = false;
|
|
2918
|
+
let escaped = false;
|
|
2919
|
+
for (let i = 0; i < text.length; i++) {
|
|
2920
|
+
const ch = text[i];
|
|
2921
|
+
if (inString) {
|
|
2922
|
+
out += ch;
|
|
2923
|
+
if (escaped) escaped = false;
|
|
2924
|
+
else if (ch === "\\") escaped = true;
|
|
2925
|
+
else if (ch === '"') inString = false;
|
|
2926
|
+
continue;
|
|
2927
|
+
}
|
|
2928
|
+
if (ch === '"') {
|
|
2929
|
+
inString = true;
|
|
2930
|
+
out += ch;
|
|
2931
|
+
continue;
|
|
2932
|
+
}
|
|
2933
|
+
if (ch === "/" && text[i + 1] === "/") {
|
|
2934
|
+
while (i < text.length && text[i] !== "\n") i++;
|
|
2935
|
+
out += "\n";
|
|
2936
|
+
continue;
|
|
2937
|
+
}
|
|
2938
|
+
if (ch === "/" && text[i + 1] === "*") {
|
|
2939
|
+
i += 2;
|
|
2940
|
+
while (i < text.length && !(text[i] === "*" && text[i + 1] === "/")) i++;
|
|
2941
|
+
i++;
|
|
2942
|
+
out += " ";
|
|
2943
|
+
continue;
|
|
2944
|
+
}
|
|
2945
|
+
out += ch;
|
|
2946
|
+
}
|
|
2947
|
+
return out;
|
|
2948
|
+
}
|
|
2949
|
+
function stripTrailingCommas(text) {
|
|
2950
|
+
let out = "";
|
|
2951
|
+
let inString = false;
|
|
2952
|
+
let escaped = false;
|
|
2953
|
+
for (let i = 0; i < text.length; i++) {
|
|
2954
|
+
const ch = text[i];
|
|
2955
|
+
if (inString) {
|
|
2956
|
+
out += ch;
|
|
2957
|
+
if (escaped) escaped = false;
|
|
2958
|
+
else if (ch === "\\") escaped = true;
|
|
2959
|
+
else if (ch === '"') inString = false;
|
|
2960
|
+
continue;
|
|
2961
|
+
}
|
|
2962
|
+
if (ch === '"') {
|
|
2963
|
+
inString = true;
|
|
2964
|
+
out += ch;
|
|
2965
|
+
continue;
|
|
2966
|
+
}
|
|
2967
|
+
if (ch === ",") {
|
|
2968
|
+
let j = i + 1;
|
|
2969
|
+
while (j < text.length && /\s/.test(text[j])) j++;
|
|
2970
|
+
if (text[j] === "}" || text[j] === "]") continue;
|
|
2971
|
+
}
|
|
2972
|
+
out += ch;
|
|
2973
|
+
}
|
|
2974
|
+
return out;
|
|
2975
|
+
}
|
|
2976
|
+
function parseJsonc(text) {
|
|
2977
|
+
return JSON.parse(stripTrailingCommas(stripJsonComments(text)));
|
|
2978
|
+
}
|
|
2979
|
+
function vscodeSettingsCandidates(input) {
|
|
2980
|
+
const platform = input.platform ?? process.platform;
|
|
2981
|
+
const home = input.homeDir ?? homedir3();
|
|
2982
|
+
const env = input.env ?? process.env;
|
|
2983
|
+
const variants = ["Code", "Code - Insiders"];
|
|
2984
|
+
let userRoots;
|
|
2985
|
+
if (platform === "win32") {
|
|
2986
|
+
const appData = env.APPDATA || join11(home, "AppData", "Roaming");
|
|
2987
|
+
userRoots = variants.map((v) => join11(appData, v, "User"));
|
|
2988
|
+
} else if (platform === "darwin") {
|
|
2989
|
+
userRoots = variants.map((v) => join11(home, "Library", "Application Support", v, "User"));
|
|
2990
|
+
} else {
|
|
2991
|
+
userRoots = variants.map((v) => join11(home, ".config", v, "User"));
|
|
2992
|
+
}
|
|
2993
|
+
return [
|
|
2994
|
+
join11(input.projectRoot, ".vscode", "settings.json"),
|
|
2995
|
+
...userRoots.map((root) => join11(root, "settings.json"))
|
|
2996
|
+
];
|
|
2997
|
+
}
|
|
2998
|
+
function checkVsCodeVirtualTools(input) {
|
|
2999
|
+
const candidates = vscodeSettingsCandidates(input).map((path) => {
|
|
3000
|
+
if (!existsSync16(path)) return { path, exists: false, hasKey: false, threshold: null };
|
|
3001
|
+
let parsed;
|
|
3002
|
+
try {
|
|
3003
|
+
parsed = parseJsonc(readFileSync13(path, "utf-8"));
|
|
3004
|
+
} catch (e) {
|
|
3005
|
+
return {
|
|
3006
|
+
path,
|
|
3007
|
+
exists: true,
|
|
3008
|
+
hasKey: false,
|
|
3009
|
+
threshold: null,
|
|
3010
|
+
parseError: e instanceof Error ? e.message : String(e)
|
|
3011
|
+
};
|
|
3012
|
+
}
|
|
3013
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
3014
|
+
return { path, exists: true, hasKey: false, threshold: null, parseError: "\u9876\u5C42\u4E0D\u662F JSON \u5BF9\u8C61" };
|
|
3015
|
+
}
|
|
3016
|
+
const raw = parsed[VIRTUAL_TOOLS_SETTING_KEY];
|
|
3017
|
+
if (raw === void 0) return { path, exists: true, hasKey: false, threshold: null };
|
|
3018
|
+
return {
|
|
3019
|
+
path,
|
|
3020
|
+
exists: true,
|
|
3021
|
+
hasKey: true,
|
|
3022
|
+
threshold: typeof raw === "number" ? raw : null
|
|
3023
|
+
};
|
|
3024
|
+
});
|
|
3025
|
+
const ok = candidates.some((c) => c.threshold === 0);
|
|
3026
|
+
const state = ok ? "ok" : candidates.some((c) => c.hasKey) ? "non-zero" : "missing";
|
|
3027
|
+
return { state, candidates };
|
|
3028
|
+
}
|
|
3029
|
+
function isVsCodeTarget(config, projectRoot) {
|
|
3030
|
+
if (config.magicDir === ".vscode" || config.adapters.includes("vscode")) return true;
|
|
3031
|
+
const root = projectRoot || config.projectRoot;
|
|
3032
|
+
if (!root) return false;
|
|
3033
|
+
if (existsSync16(join11(root, ".vscode", "mcp.json"))) return true;
|
|
3034
|
+
const settingsPath = join11(root, ".vscode", "settings.json");
|
|
3035
|
+
if (!existsSync16(settingsPath)) return false;
|
|
3036
|
+
try {
|
|
3037
|
+
const parsed = parseJsonc(readFileSync13(settingsPath, "utf-8"));
|
|
3038
|
+
if (typeof parsed !== "object" || parsed === null) return false;
|
|
3039
|
+
const obj = parsed;
|
|
3040
|
+
if (obj.mcp !== void 0) return true;
|
|
3041
|
+
return Object.keys(obj).some((k) => k.startsWith("github.copilot.chat."));
|
|
3042
|
+
} catch {
|
|
3043
|
+
return false;
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
function virtualToolsNoticeLines(check, ctx) {
|
|
3047
|
+
const lines = [];
|
|
3048
|
+
const describe = (c) => {
|
|
3049
|
+
const where = c.path === join11(ctx.projectRoot, ".vscode", "settings.json") ? "\u5DE5\u4F5C\u533A" : "\u7528\u6237\u7EA7";
|
|
3050
|
+
if (!c.exists) return `${where}: ${c.path}\uFF08\u6587\u4EF6\u4E0D\u5B58\u5728\uFF09`;
|
|
3051
|
+
if (c.parseError) return `${where}: ${c.path}\uFF08\u89E3\u6790\u5931\u8D25: ${c.parseError}\uFF09`;
|
|
3052
|
+
if (!c.hasKey) return `${where}: ${c.path}\uFF08\u952E\u7F3A\u5931\uFF09`;
|
|
3053
|
+
return `${where}: ${c.path}\uFF08\u5F53\u524D\u503C ${c.threshold ?? "\u975E\u6570\u503C"}\uFF09`;
|
|
3054
|
+
};
|
|
3055
|
+
if (check.state === "ok") {
|
|
3056
|
+
const hit = check.candidates.find((c) => c.threshold === 0);
|
|
3057
|
+
lines.push(` \u2705 VS Code \u865A\u62DF\u5DE5\u5177\u6298\u53E0\u5DF2\u5173\u95ED\uFF08${hit ? hit.path : VIRTUAL_TOOLS_SETTING_KEY + " = 0"}\uFF09`);
|
|
3058
|
+
return lines;
|
|
3059
|
+
}
|
|
3060
|
+
lines.push(` \u26A0\uFE0F VS Code \u5BBF\u4E3B\u9002\u914D\uFF1A\u672A\u68C0\u6D4B\u5230 "${VIRTUAL_TOOLS_SETTING_KEY}": 0`);
|
|
3061
|
+
check.candidates.forEach((c) => lines.push(` - ${describe(c)}`));
|
|
3062
|
+
if (check.state === "non-zero") {
|
|
3063
|
+
lines.push(" \u8BE5\u952E\u5B58\u5728\u4F46\u53D6\u503C\u975E 0 \u21D2 \u6298\u53E0\u4ECD\u4F1A\u89E6\u53D1\uFF08\u89E6\u53D1\u7EBF = \u6240\u6709 MCP \u670D\u52A1\u5668\u5DE5\u5177\u603B\u6570 \u2265 \u9608\u503C/2\uFF0C\u9ED8\u8BA4 64\uFF09");
|
|
3064
|
+
} else {
|
|
3065
|
+
lines.push(" \u82E5\u4F60\u7684 Copilot / VS Code \u7248\u672C\u5DF2\u4E0D\u518D\u63D0\u4F9B\u8BE5\u8BBE\u7F6E\u9879\uFF0C\u53EF\u5FFD\u7565\u672C\u544A\u8B66\uFF08\u672C\u9879\u4E3A\u5EFA\u8BAE\u6027\u68C0\u67E5\uFF0C\u4E0D\u5F71\u54CD\u9000\u51FA\u7801\uFF09");
|
|
3066
|
+
}
|
|
3067
|
+
lines.push(" \u4FEE\u590D: \u5728\u7528\u6237\u7EA7\u6216\u5DE5\u4F5C\u533A\u7EA7 settings.json \u52A0\u5165\u4E0B\u9762\u8FD9\u884C\uFF0C\u7136\u540E\u300C\u91CD\u8F7D VS Code \u7A97\u53E3\u300D\uFF08Developer: Reload Window\uFF09");
|
|
3068
|
+
lines.push(` "${VIRTUAL_TOOLS_SETTING_KEY}": 0`);
|
|
3069
|
+
lines.push(' \u539F\u56E0: \u5DE5\u5177\u88AB\u5BBF\u4E3B\u6298\u53E0\u65F6\uFF0C\u672A\u6FC0\u6D3B\u4EE3\u7406\u5C31\u8C03\u7528\u4F1A\u8BEF\u62A5 "currently disabled by the user"\uFF0C');
|
|
3070
|
+
lines.push(" HITL \u5BA1\u6279\u94FE\uFF08update_hitl/status_hitl\uFF09\u4E0E ADD-7 \u5BA1\u8BA1\u94FE\uFF08record_dev_operation/query_audit_logs\uFF09\u4F1A\u540C\u65F6\u4E2D\u65AD");
|
|
3071
|
+
lines.push(` \u8BF4\u660E: ${ctx.docPath}\uFF08\u672C\u9879\u4E3A\u5EFA\u8BAE\u6027\u68C0\u67E5\uFF0C\u4E0D\u4F1A\u4FEE\u6539\u4F60\u7684\u8BBE\u7F6E\u6587\u4EF6\uFF09`);
|
|
3072
|
+
return lines;
|
|
3073
|
+
}
|
|
3074
|
+
function printVirtualToolsCheck(config) {
|
|
3075
|
+
const check = checkVsCodeVirtualTools({ projectRoot: config.projectRoot });
|
|
3076
|
+
const docPath = join11(config.magicDir || ".vscode", "docs", "ADD-governance-vscode-copilot.md");
|
|
3077
|
+
virtualToolsNoticeLines(check, { projectRoot: config.projectRoot, docPath }).forEach(
|
|
3078
|
+
(line) => console.log(line)
|
|
3079
|
+
);
|
|
3080
|
+
}
|
|
2826
3081
|
async function statusCommand() {
|
|
2827
3082
|
const projectRoot = process.cwd();
|
|
2828
3083
|
const config = await loadConfig(projectRoot);
|
|
@@ -2831,7 +3086,7 @@ async function statusCommand() {
|
|
|
2831
3086
|
const missing = [];
|
|
2832
3087
|
const present = [];
|
|
2833
3088
|
for (const [relPath] of coreFiles) {
|
|
2834
|
-
if (
|
|
3089
|
+
if (existsSync16(resolve11(projectRoot, relPath))) {
|
|
2835
3090
|
present.push(relPath);
|
|
2836
3091
|
} else {
|
|
2837
3092
|
missing.push(relPath);
|
|
@@ -2842,15 +3097,22 @@ async function statusCommand() {
|
|
|
2842
3097
|
if (missing.length > 0) {
|
|
2843
3098
|
console.log(` \u7F3A\u5931: ${missing.length} \u6587\u4EF6`);
|
|
2844
3099
|
missing.forEach((f) => console.log(` - ${f}`));
|
|
2845
|
-
process.exit(1);
|
|
2846
3100
|
} else {
|
|
2847
3101
|
console.log(" \u6240\u6709\u6587\u4EF6\u5B8C\u6574\u3002");
|
|
2848
3102
|
}
|
|
3103
|
+
if (isVsCodeTarget(config, projectRoot)) {
|
|
3104
|
+
console.log("");
|
|
3105
|
+
console.log("\u5BBF\u4E3B\u9002\u914D\u81EA\u68C0\uFF08VS Code Copilot\uFF09:");
|
|
3106
|
+
printVirtualToolsCheck(config);
|
|
3107
|
+
}
|
|
3108
|
+
if (missing.length > 0) {
|
|
3109
|
+
process.exit(1);
|
|
3110
|
+
}
|
|
2849
3111
|
}
|
|
2850
3112
|
|
|
2851
3113
|
// src/cli/commands/stack.ts
|
|
2852
|
-
import { readdirSync as readdirSync6, readFileSync as
|
|
2853
|
-
import { resolve as resolve12, join as
|
|
3114
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync8, existsSync as existsSync17, mkdirSync as mkdirSync8 } from "fs";
|
|
3115
|
+
import { resolve as resolve12, join as join12 } from "path";
|
|
2854
3116
|
import { createHash as createHash3 } from "crypto";
|
|
2855
3117
|
var HASH_OUTPUT_FILE = ".add-coder-hash.json";
|
|
2856
3118
|
function hash82(c) {
|
|
@@ -2866,13 +3128,13 @@ function resolveMagicDir2(projectRoot, specified) {
|
|
|
2866
3128
|
}
|
|
2867
3129
|
function listCustomProfiles(projectRoot, magicDir, registryNames) {
|
|
2868
3130
|
const dir = resolve12(projectRoot, magicDir, "rules", "profiles");
|
|
2869
|
-
if (!
|
|
3131
|
+
if (!existsSync17(dir)) return [];
|
|
2870
3132
|
return readdirSync6(dir).filter((f) => f.endsWith(".md") && !registryNames.has(f.replace(/-profile\.md$/, ""))).sort();
|
|
2871
3133
|
}
|
|
2872
3134
|
function profileExists(projectRoot, magicDir, name) {
|
|
2873
3135
|
const registry = loadProfileRegistry();
|
|
2874
3136
|
if (registry.some((p) => p.name === name)) return true;
|
|
2875
|
-
return
|
|
3137
|
+
return existsSync17(resolve12(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`));
|
|
2876
3138
|
}
|
|
2877
3139
|
function stackCommand(sub, name, options = {}) {
|
|
2878
3140
|
const projectRoot = process.cwd();
|
|
@@ -2910,11 +3172,11 @@ function stackCommand(sub, name, options = {}) {
|
|
|
2910
3172
|
return;
|
|
2911
3173
|
}
|
|
2912
3174
|
const profilePath = resolve12(projectRoot, magicDir, "rules", "profiles", `${current}-profile.md`);
|
|
2913
|
-
const stat =
|
|
3175
|
+
const stat = existsSync17(profilePath) ? readFileSync14(profilePath, "utf-8").length : 0;
|
|
2914
3176
|
console.log(`\u6280\u672F\u6808: ${current}`);
|
|
2915
|
-
console.log(`profile \u6587\u4EF6: ${profilePath}${
|
|
3177
|
+
console.log(`profile \u6587\u4EF6: ${profilePath}${existsSync17(profilePath) ? ` (${stat} \u5B57\u7B26)` : "\uFF08\u7F3A\u5931\uFF09"}`);
|
|
2916
3178
|
try {
|
|
2917
|
-
const raw = JSON.parse(
|
|
3179
|
+
const raw = JSON.parse(readFileSync14(resolve12(projectRoot, magicDir, "stack.json"), "utf-8"));
|
|
2918
3180
|
if (raw.updatedAt) console.log(`\u66F4\u65B0\u65F6\u95F4: ${raw.updatedAt}`);
|
|
2919
3181
|
} catch {
|
|
2920
3182
|
}
|
|
@@ -2940,8 +3202,8 @@ function buildConfig(projectRoot, magicDir, stack) {
|
|
|
2940
3202
|
};
|
|
2941
3203
|
try {
|
|
2942
3204
|
const pkgPath = resolve12(projectRoot, "package.json");
|
|
2943
|
-
if (
|
|
2944
|
-
const pkg = JSON.parse(
|
|
3205
|
+
if (existsSync17(pkgPath)) {
|
|
3206
|
+
const pkg = JSON.parse(readFileSync14(pkgPath, "utf-8"));
|
|
2945
3207
|
if (pkg.name) config.projectName = pkg.name;
|
|
2946
3208
|
}
|
|
2947
3209
|
} catch {
|
|
@@ -2968,13 +3230,13 @@ function applyStack(projectRoot, magicDir, name) {
|
|
|
2968
3230
|
}
|
|
2969
3231
|
const hashMap = {};
|
|
2970
3232
|
try {
|
|
2971
|
-
Object.assign(hashMap, JSON.parse(
|
|
3233
|
+
Object.assign(hashMap, JSON.parse(readFileSync14(resolve12(projectRoot, magicDir, HASH_OUTPUT_FILE), "utf-8")));
|
|
2972
3234
|
} catch {
|
|
2973
3235
|
}
|
|
2974
3236
|
let written = 0;
|
|
2975
3237
|
for (const [relPath, content] of stackRelated) {
|
|
2976
3238
|
const targetPath = resolve12(projectRoot, relPath);
|
|
2977
|
-
mkdirSync8(
|
|
3239
|
+
mkdirSync8(join12(targetPath, ".."), { recursive: true });
|
|
2978
3240
|
writeFileSync8(targetPath, content, "utf-8");
|
|
2979
3241
|
hashMap[relPath] = hash82(content);
|
|
2980
3242
|
written++;
|
|
@@ -2988,21 +3250,26 @@ function applyStack(projectRoot, magicDir, name) {
|
|
|
2988
3250
|
const profilePathMagic = resolve12(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`);
|
|
2989
3251
|
const profilePathAdd = resolve12(projectRoot, ADD_DIR, "rules", "profiles", `${name}-profile.md`);
|
|
2990
3252
|
const projectRulesPath = resolve12(projectRoot, magicDir, "rules", "project_rules.md");
|
|
2991
|
-
const projectRulesContent =
|
|
3253
|
+
const projectRulesContent = existsSync17(projectRulesPath) ? readFileSync14(projectRulesPath, "utf-8") : "";
|
|
2992
3254
|
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`);
|
|
2993
|
-
if (magicDir !== magicDirFor("codex") && isBuiltin && !
|
|
2994
|
-
if (!
|
|
3255
|
+
if (magicDir !== magicDirFor("codex") && isBuiltin && !existsSync17(profilePathAdd)) fail(`profile \u672A\u5199\u5165 ${ADD_DIR}: ${profilePathAdd}`);
|
|
3256
|
+
if (!existsSync17(profilePathMagic)) fail(`profile \u672A\u5199\u5165 ${magicDir}: ${profilePathMagic}`);
|
|
2995
3257
|
if (!projectRulesContent.includes("**\u5F53\u524D\u6280\u672F\u6808**") || !projectRulesContent.includes(name)) fail(`project_rules.md \u672A\u5305\u542B ${name} \u5F15\u7528`);
|
|
2996
3258
|
writeFileSync8(resolve12(projectRoot, magicDir, HASH_OUTPUT_FILE), JSON.stringify(hashMap, null, 2) + "\n", "utf-8");
|
|
2997
3259
|
console.log(`\u2705 \u6280\u672F\u6808\u5DF2\u8BBE\u7F6E\u4E3A ${name}`);
|
|
2998
3260
|
console.log(` ${magicDir}/stack.json \u2192 ${name}`);
|
|
2999
|
-
console.log(` ${magicDir}/rules/profiles/${name}-profile.md ${
|
|
3261
|
+
console.log(` ${magicDir}/rules/profiles/${name}-profile.md ${existsSync17(resolve12(projectRoot, magicDir, "rules", "profiles", `${name}-profile.md`)) ? "\u5DF2\u5C31\u4F4D" : "\uFF08\u81EA\u5B9A\u4E49 profile\uFF0C\u9879\u76EE\u4FA7\u6587\u4EF6\uFF09"}`);
|
|
3000
3262
|
console.log(` project_rules.md \u5F15\u7528\u884C\u5DF2\u66F4\u65B0 + hash \u5DF2\u5237\u65B0\uFF08${written} \u4E2A\u6587\u4EF6\uFF09`);
|
|
3001
3263
|
}
|
|
3002
3264
|
|
|
3003
3265
|
// src/lib/memory-fts-objects.ts
|
|
3004
3266
|
var PG_FTS_OBJECTS = [
|
|
3005
3267
|
{ name: "pg_trgm", kind: "extension", ddl: `CREATE EXTENSION IF NOT EXISTS pg_trgm;` },
|
|
3268
|
+
{
|
|
3269
|
+
name: "AddMemory_searchText_tsv_idx",
|
|
3270
|
+
kind: "index",
|
|
3271
|
+
ddl: `CREATE INDEX IF NOT EXISTS "AddMemory_searchText_tsv_idx" ON "public"."AddMemory" USING GIN (to_tsvector('simple', "searchText"));`
|
|
3272
|
+
},
|
|
3006
3273
|
{
|
|
3007
3274
|
name: "AddMemory_topic_trgm_idx",
|
|
3008
3275
|
kind: "index",
|
|
@@ -3023,17 +3290,17 @@ var SQLITE_FTS_OBJECTS = [
|
|
|
3023
3290
|
{
|
|
3024
3291
|
name: "add_memory_fts",
|
|
3025
3292
|
kind: "table",
|
|
3026
|
-
ddl: `CREATE VIRTUAL TABLE IF NOT EXISTS add_memory_fts USING fts5(memory_id UNINDEXED,
|
|
3293
|
+
ddl: `CREATE VIRTUAL TABLE IF NOT EXISTS add_memory_fts USING fts5(memory_id UNINDEXED, searchText, tokenize = 'unicode61');`
|
|
3027
3294
|
},
|
|
3028
3295
|
{
|
|
3029
3296
|
name: "add_memory_fts_ai",
|
|
3030
3297
|
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,
|
|
3298
|
+
ddl: `CREATE TRIGGER IF NOT EXISTS add_memory_fts_ai AFTER INSERT ON "AddMemory" BEGIN INSERT INTO add_memory_fts(memory_id, searchText) VALUES (new.id, new.searchText); END;`
|
|
3032
3299
|
},
|
|
3033
3300
|
{
|
|
3034
3301
|
name: "add_memory_fts_au",
|
|
3035
3302
|
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,
|
|
3303
|
+
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, searchText) VALUES (new.id, new.searchText); END;`
|
|
3037
3304
|
},
|
|
3038
3305
|
{
|
|
3039
3306
|
name: "add_memory_fts_ad",
|
|
@@ -3081,12 +3348,73 @@ function parsePgProbeMissing(stderr, expected) {
|
|
|
3081
3348
|
return tail.split(/[\s"',\n]+/).map((s) => s.trim()).filter((s) => known.has(s));
|
|
3082
3349
|
}
|
|
3083
3350
|
|
|
3084
|
-
//
|
|
3085
|
-
import {
|
|
3351
|
+
// templates/core/scripts/mcp-server/shared/memory/retrieval/fts-fingerprint.ts
|
|
3352
|
+
import { createHash as createHash4 } from "crypto";
|
|
3353
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync9 } from "fs";
|
|
3086
3354
|
import { createRequire } from "module";
|
|
3355
|
+
import { dirname as dirname5, join as join13 } from "path";
|
|
3356
|
+
var TOKENIZATION_CONTRACT_VERSION = 1;
|
|
3357
|
+
function computeFtsFingerprint(input) {
|
|
3358
|
+
const userDictPath = input.userDictPath ?? join13(input.projectRoot, input.magicDir, "data", "jieba", "userdict.txt");
|
|
3359
|
+
let userDict = "none";
|
|
3360
|
+
if (existsSync18(userDictPath)) {
|
|
3361
|
+
userDict = createHash4("sha256").update(readFileSync15(userDictPath)).digest("hex").slice(0, 12);
|
|
3362
|
+
}
|
|
3363
|
+
const jieba = input.jiebaVersion ?? detectJiebaVersion();
|
|
3364
|
+
const value = createHash4("sha256").update(`tokenization-v${TOKENIZATION_CONTRACT_VERSION}|${jieba ?? "bigram"}|${userDict}`).digest("hex").slice(0, 12);
|
|
3365
|
+
return { value, parts: { jieba: jieba ?? "bigram", userDict, contract: TOKENIZATION_CONTRACT_VERSION } };
|
|
3366
|
+
}
|
|
3367
|
+
function detectJiebaVersion() {
|
|
3368
|
+
try {
|
|
3369
|
+
const req = createRequire(import.meta.url);
|
|
3370
|
+
const pkg = req("@node-rs/jieba/package.json");
|
|
3371
|
+
return pkg.version ?? "unknown";
|
|
3372
|
+
} catch {
|
|
3373
|
+
return null;
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
function fingerprintMarkerPath(projectRoot, magicDir) {
|
|
3377
|
+
return join13(projectRoot, magicDir, "memory", "fts-fingerprint.json");
|
|
3378
|
+
}
|
|
3379
|
+
function readRecordedFingerprint(projectRoot, magicDir) {
|
|
3380
|
+
const p = fingerprintMarkerPath(projectRoot, magicDir);
|
|
3381
|
+
if (!existsSync18(p)) return null;
|
|
3382
|
+
try {
|
|
3383
|
+
return JSON.parse(readFileSync15(p, "utf-8"));
|
|
3384
|
+
} catch {
|
|
3385
|
+
return null;
|
|
3386
|
+
}
|
|
3387
|
+
}
|
|
3388
|
+
function writeRecordedFingerprint(projectRoot, magicDir, fingerprint, now = /* @__PURE__ */ new Date()) {
|
|
3389
|
+
const p = fingerprintMarkerPath(projectRoot, magicDir);
|
|
3390
|
+
mkdirSync9(dirname5(p), { recursive: true });
|
|
3391
|
+
const payload = {
|
|
3392
|
+
fingerprint: fingerprint.value,
|
|
3393
|
+
recordedAt: now.toISOString(),
|
|
3394
|
+
parts: fingerprint.parts
|
|
3395
|
+
};
|
|
3396
|
+
writeFileSync9(p, JSON.stringify(payload, null, 2) + "\n", "utf-8");
|
|
3397
|
+
return p;
|
|
3398
|
+
}
|
|
3399
|
+
function checkFtsFingerprint(projectRoot, magicDir) {
|
|
3400
|
+
const current = computeFtsFingerprint({ projectRoot, magicDir });
|
|
3401
|
+
const recorded = readRecordedFingerprint(projectRoot, magicDir);
|
|
3402
|
+
if (!recorded) {
|
|
3403
|
+
return { requiresReindex: true, reason: "\u672A\u8BB0\u5F55\u6307\u7EB9\uFF08\u65E0\u6CD5\u8BC1\u660E\u5386\u53F2 searchText \u4E0E\u5F53\u524D\u5206\u8BCD\u5668\u540C\u6E90\uFF09", current, recorded };
|
|
3404
|
+
}
|
|
3405
|
+
if (recorded.fingerprint !== current.value) {
|
|
3406
|
+
const diff = ["jieba", "userDict", "contract"].filter((k) => recorded.parts?.[k] !== current.parts[k]).map((k) => `${k}: ${String(recorded.parts?.[k])} \u2192 ${String(current.parts[k])}`).join("; ");
|
|
3407
|
+
return { requiresReindex: true, reason: `\u6307\u7EB9\u4E0D\u4E00\u81F4\uFF08${diff}\uFF09`, current, recorded };
|
|
3408
|
+
}
|
|
3409
|
+
return { requiresReindex: false, reason: "\u6307\u7EB9\u4E00\u81F4", current, recorded };
|
|
3410
|
+
}
|
|
3411
|
+
|
|
3412
|
+
// src/lib/memory-fts-runtime.ts
|
|
3413
|
+
import { existsSync as existsSync19, mkdtempSync, readFileSync as readFileSync16, rmSync as rmSync2, writeFileSync as writeFileSync10 } from "fs";
|
|
3414
|
+
import { createRequire as createRequire2 } from "module";
|
|
3087
3415
|
import { tmpdir } from "os";
|
|
3088
|
-
import { isAbsolute, join as
|
|
3089
|
-
var requireBuiltin =
|
|
3416
|
+
import { isAbsolute, join as join14, resolve as resolve13 } from "path";
|
|
3417
|
+
var requireBuiltin = createRequire2(import.meta.url);
|
|
3090
3418
|
function loadNodeSqlite() {
|
|
3091
3419
|
return requireBuiltin("node:sqlite");
|
|
3092
3420
|
}
|
|
@@ -3126,7 +3454,7 @@ function sqlitePathCandidates(databaseUrl, projectRoot) {
|
|
|
3126
3454
|
}
|
|
3127
3455
|
function createSqliteAdapter(opts) {
|
|
3128
3456
|
const candidates = opts.dbPath ? [opts.dbPath] : sqlitePathCandidates(opts.databaseUrl, opts.projectRoot);
|
|
3129
|
-
const dbPath = candidates.find((p) =>
|
|
3457
|
+
const dbPath = candidates.find((p) => existsSync19(p));
|
|
3130
3458
|
const source = dbPath ?? `${opts.databaseUrl}\uFF08\u672A\u627E\u5230\u5E93\u6587\u4EF6\uFF0C\u5DF2\u8BD5\uFF1A${candidates.join(" / ")}\uFF09`;
|
|
3131
3459
|
function withDb(fn) {
|
|
3132
3460
|
if (!dbPath) {
|
|
@@ -3168,8 +3496,8 @@ function createSqliteAdapter(opts) {
|
|
|
3168
3496
|
function readPrismaDatasourceProvider(projectRoot) {
|
|
3169
3497
|
for (const rel of ["prisma/schema.prisma", "prisma/add.prisma"]) {
|
|
3170
3498
|
const p = resolve13(projectRoot, rel);
|
|
3171
|
-
if (!
|
|
3172
|
-
const m =
|
|
3499
|
+
if (!existsSync19(p)) continue;
|
|
3500
|
+
const m = readFileSync16(p, "utf-8").match(/datasource\s+\w+\s*\{[\s\S]*?provider\s*=\s*"([^"]+)"/);
|
|
3173
3501
|
if (m) return m[1];
|
|
3174
3502
|
}
|
|
3175
3503
|
return null;
|
|
@@ -3184,8 +3512,8 @@ function resolveProjectDatabaseUrl(projectRoot, env = process.env) {
|
|
|
3184
3512
|
if (env.DATABASE_URL) return env.DATABASE_URL;
|
|
3185
3513
|
for (const rel of [".env.development", ".env"]) {
|
|
3186
3514
|
const p = resolve13(projectRoot, rel);
|
|
3187
|
-
if (!
|
|
3188
|
-
const m =
|
|
3515
|
+
if (!existsSync19(p)) continue;
|
|
3516
|
+
const m = readFileSync16(p, "utf-8").match(/^\s*DATABASE_URL\s*=\s*["']?([^"'\n]+)["']?\s*$/m);
|
|
3189
3517
|
if (m) return m[1].trim();
|
|
3190
3518
|
}
|
|
3191
3519
|
return null;
|
|
@@ -3197,9 +3525,9 @@ function createPrismaCliAdapter(opts) {
|
|
|
3197
3525
|
const run = opts.run ?? ((argv, cwd) => runCommand(pm, argv, { cwd, timeout: opts.timeoutMs ?? 12e4 }));
|
|
3198
3526
|
const source = opts.describeSource ?? "prisma db execute\uFF08\u9879\u76EE DATABASE_URL\uFF09";
|
|
3199
3527
|
function executeSql(sql, tag) {
|
|
3200
|
-
const dir = mkdtempSync(
|
|
3201
|
-
const file =
|
|
3202
|
-
|
|
3528
|
+
const dir = mkdtempSync(join14(tmpdir(), "add-coder-fts-"));
|
|
3529
|
+
const file = join14(dir, `${tag}.sql`);
|
|
3530
|
+
writeFileSync10(file, sql, "utf-8");
|
|
3203
3531
|
try {
|
|
3204
3532
|
const r = run([...base, "db", "execute", "--file", file], opts.projectRoot);
|
|
3205
3533
|
return { status: r.status, stderr: r.stderr ?? "" };
|
|
@@ -3272,6 +3600,18 @@ function printReport(report, mode, io) {
|
|
|
3272
3600
|
const head = mode === "apply" ? "\u8BB0\u5FC6 FTS \u671F\u671B\u6001 \xB7 \u91CD\u5EFA" : "\u8BB0\u5FC6 FTS \u671F\u671B\u6001 \xB7 \u63A2\u6D4B";
|
|
3273
3601
|
io.log(`${head}\uFF08backend=${report.backend}\uFF0Csource=${report.source}\uFF09`);
|
|
3274
3602
|
io.log(` \u5B8C\u6210\u5EA6 ${report.present}/${report.total}\uFF08${report.progress}%\uFF09`);
|
|
3603
|
+
try {
|
|
3604
|
+
const projectRoot = process.cwd();
|
|
3605
|
+
const check = checkFtsFingerprint(projectRoot, resolveMagicDir(projectRoot));
|
|
3606
|
+
if (check.requiresReindex) {
|
|
3607
|
+
io.log(` \u26A0\uFE0F \u68C0\u7D22\u6307\u7EB9\uFF1A\u9700\u91CD\u7D22\u5F15 \u2014\u2014 ${check.reason}`);
|
|
3608
|
+
io.log(" \u2192 \u5904\u7F6E\uFF1Aadd-coder memory:reindex --apply \u540E\u7531\u56DE\u586B\u811A\u672C\u91CD\u7B97 searchText\uFF08\u6307\u7EB9\u968F\u91CD\u5EFA\u5199\u5165\uFF09");
|
|
3609
|
+
} else {
|
|
3610
|
+
io.log(` \u2705 \u68C0\u7D22\u6307\u7EB9\u4E00\u81F4\uFF08${check.current.value}\uFF09`);
|
|
3611
|
+
}
|
|
3612
|
+
} catch (error) {
|
|
3613
|
+
io.log(` \u26A0\uFE0F \u68C0\u7D22\u6307\u7EB9\uFF1A\u65E0\u6CD5\u5224\u5B9A\uFF08${error instanceof Error ? error.message : String(error)}\uFF09`);
|
|
3614
|
+
}
|
|
3275
3615
|
if (mode === "apply" && report.rebuilt.length > 0) {
|
|
3276
3616
|
io.log(` \u672C\u6B21\u91CD\u5EFA\uFF08${report.rebuilt.length}\uFF09\uFF1A${report.rebuilt.join(", ")}`);
|
|
3277
3617
|
}
|
|
@@ -3293,8 +3633,19 @@ async function memoryReindexCommand(options, deps = {}) {
|
|
|
3293
3633
|
try {
|
|
3294
3634
|
const adapter = resolveAdapter3(projectRoot, options, deps);
|
|
3295
3635
|
const report = mode === "apply" ? await reindexVia(adapter) : await probeVia(adapter);
|
|
3636
|
+
let fingerprintNote = null;
|
|
3637
|
+
if (mode === "apply" && report.missing.length === 0) {
|
|
3638
|
+
try {
|
|
3639
|
+
const magicDir = resolveMagicDir(projectRoot);
|
|
3640
|
+
const path = writeRecordedFingerprint(projectRoot, magicDir, computeFtsFingerprint({ projectRoot, magicDir }));
|
|
3641
|
+
fingerprintNote = ` \u68C0\u7D22\u6307\u7EB9\uFF1A\u5DF2\u8BB0\u5F55\uFF08${path}\uFF09`;
|
|
3642
|
+
} catch (e) {
|
|
3643
|
+
fingerprintNote = ` \u26A0\uFE0F \u68C0\u7D22\u6307\u7EB9\u5199\u5165\u5931\u8D25\uFF1A${e instanceof Error ? e.message : String(e)}\uFF08\u4E0B\u6B21 probe \u4ECD\u4F1A\u63D0\u793A\u9700\u91CD\u7D22\u5F15\uFF09`;
|
|
3644
|
+
}
|
|
3645
|
+
}
|
|
3296
3646
|
if (options.json) io.log(JSON.stringify(report, null, 2));
|
|
3297
3647
|
else printReport(report, mode, io);
|
|
3648
|
+
if (fingerprintNote && !options.json) io.log(fingerprintNote);
|
|
3298
3649
|
if (mode === "probe") return 0;
|
|
3299
3650
|
return report.missing.length > 0 ? 1 : 0;
|
|
3300
3651
|
} catch (e) {
|
|
@@ -3309,7 +3660,7 @@ async function memoryReindexCommand(options, deps = {}) {
|
|
|
3309
3660
|
|
|
3310
3661
|
// src/cli/index.ts
|
|
3311
3662
|
var { version } = JSON.parse(
|
|
3312
|
-
|
|
3663
|
+
readFileSync17(new URL("../package.json", import.meta.url), "utf-8")
|
|
3313
3664
|
);
|
|
3314
3665
|
async function modelDownloadCommand(options) {
|
|
3315
3666
|
try {
|