ai-project-manage-cli 7.1.36 → 7.1.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/dist/index.js CHANGED
@@ -1000,20 +1000,12 @@ import { Command } from "commander";
1000
1000
  init_config();
1001
1001
 
1002
1002
  // src/commands/init.ts
1003
- import { join as join6 } from "path";
1004
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "fs";
1003
+ import { join as join3 } from "path";
1004
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
1005
1005
 
1006
1006
  // src/command-utils.ts
1007
1007
  init_config();
1008
- import {
1009
- copyFileSync,
1010
- existsSync,
1011
- mkdirSync as mkdirSync2,
1012
- readFileSync as readFileSync2,
1013
- readdirSync,
1014
- statSync,
1015
- writeFileSync as writeFileSync2
1016
- } from "fs";
1008
+ import { copyFileSync, existsSync, mkdirSync as mkdirSync2, readdirSync, statSync } from "fs";
1017
1009
  import { basename, dirname, extname, join as join2, resolve as resolve2 } from "path";
1018
1010
  import { fileURLToPath } from "url";
1019
1011
 
@@ -1084,63 +1076,6 @@ function isWorkspaceApmInitialized(workdir) {
1084
1076
  }
1085
1077
  return readdirSync(fsApmDir).length > 0;
1086
1078
  }
1087
- var APM_GITIGNORE_PATTERNS = [
1088
- /^\.apm\/?$/,
1089
- /^\.apm\/\*\*$/,
1090
- /^\*\*\/\.apm\/?$/,
1091
- /^\*\*\/\.apm\/\*\*$/,
1092
- /^\/\.apm\/?$/,
1093
- /^\/\.apm\/\*\*$/
1094
- ];
1095
- function normalizeGitignorePattern(line) {
1096
- const trimmed = line.trim();
1097
- if (!trimmed || trimmed.startsWith("#")) return "";
1098
- if (trimmed.startsWith("!")) return "";
1099
- const hashIndex = trimmed.indexOf("#");
1100
- return (hashIndex >= 0 ? trimmed.slice(0, hashIndex) : trimmed).trim();
1101
- }
1102
- function gitignoreIgnoresApm(line) {
1103
- const pattern = normalizeGitignorePattern(line);
1104
- if (!pattern) return false;
1105
- return APM_GITIGNORE_PATTERNS.some((re) => re.test(pattern));
1106
- }
1107
- var APM_GITIGNORE_LINE = "**/.apm/**";
1108
- function ensureApmGitignoredInRepo(workdir) {
1109
- const gitignorePath = join2(workdir, ".gitignore");
1110
- const fsGitignorePath = toFsPath(gitignorePath);
1111
- if (!existsSync(fsGitignorePath)) {
1112
- writeFileSync2(fsGitignorePath, `${APM_GITIGNORE_LINE}
1113
- `, "utf8");
1114
- return true;
1115
- }
1116
- const content = readFileSync2(fsGitignorePath, "utf8");
1117
- if (content.split(/\r?\n/).some(gitignoreIgnoresApm)) {
1118
- return false;
1119
- }
1120
- const suffix = content.endsWith("\n") || content.length === 0 ? "" : "\n";
1121
- writeFileSync2(
1122
- fsGitignorePath,
1123
- `${content}${suffix}${APM_GITIGNORE_LINE}
1124
- `,
1125
- "utf8"
1126
- );
1127
- return true;
1128
- }
1129
- function assertApmGitignoredInRepo(workdir) {
1130
- const gitignorePath = join2(workdir, ".gitignore");
1131
- const fsGitignorePath = toFsPath(gitignorePath);
1132
- if (!existsSync(fsGitignorePath)) {
1133
- throw new Error(
1134
- `\u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u7F3A\u5C11 .gitignore \u6587\u4EF6\uFF0C\u8BF7\u6DFB\u52A0\u5BF9 .apm \u7684\u5FFD\u7565\u89C4\u5219\uFF08\u4F8B\u5982 **/.apm/**\uFF09\uFF0C\u907F\u514D\u672C\u5730\u4F1A\u8BDD\u4E0E\u90E8\u7F72\u51ED\u636E\u88AB\u63D0\u4EA4\u3002`
1135
- );
1136
- }
1137
- const content = readFileSync2(fsGitignorePath, "utf8");
1138
- if (!content.split(/\r?\n/).some(gitignoreIgnoresApm)) {
1139
- throw new Error(
1140
- `\u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u7684 .gitignore \u672A\u5FFD\u7565 .apm\uFF0C\u8BF7\u6DFB\u52A0 **/.apm/** \u6216 .apm/\uFF0C\u907F\u514D\u672C\u5730\u4F1A\u8BDD\u4E0E\u90E8\u7F72\u51ED\u636E\u88AB\u63D0\u4EA4\u3002`
1141
- );
1142
- }
1143
- }
1144
1079
  var SESSIONS_SUBDIR = "sessions";
1145
1080
  var SESSION_DOCS_SUBDIR = "docs";
1146
1081
  var SESSION_ATTACHMENTS_SUBDIR = "attachments";
@@ -1199,19 +1134,10 @@ async function ensureDirExists(dir) {
1199
1134
  async function ensureWorkspaceApmDirForInit(cwd = resolveWorkdirPath()) {
1200
1135
  const dir = workspaceApmDir(cwd);
1201
1136
  const fsDir = toFsPath(dir);
1202
- if (!existsSync(fsDir)) {
1203
- mkdirSync2(fsDir, { recursive: true });
1204
- return;
1205
- }
1206
- const st = statSync(fsDir);
1207
- if (!st.isDirectory()) {
1137
+ if (existsSync(fsDir) && !statSync(fsDir).isDirectory()) {
1208
1138
  throw new Error(`[apm] \u8DEF\u5F84\u5DF2\u5B58\u5728\u4F46\u4E0D\u662F\u76EE\u5F55: ${dir}`);
1209
1139
  }
1210
- if (readdirSync(fsDir).length > 0) {
1211
- throw new Error(
1212
- "[apm] .apm \u76EE\u5F55\u5DF2\u5B58\u5728\u4E14\u975E\u7A7A\uFF0C\u8BF7\u5148\u5907\u4EFD\u3001\u6E05\u7A7A\u6216\u5220\u9664\u540E\u518D\u6267\u884C init"
1213
- );
1214
- }
1140
+ mkdirSync2(fsDir, { recursive: true });
1215
1141
  }
1216
1142
  var WORKSPACE_TEMPLATE_SUBDIRS = [
1217
1143
  "sessions",
@@ -1287,9 +1213,51 @@ async function copyTemplateFiles(targetDir, workdir = resolveWorkdirPath()) {
1287
1213
  assertTemplateCopiedToApm(resolvedTarget, resolve2(workdir));
1288
1214
  }
1289
1215
 
1216
+ // src/commands/init.ts
1217
+ async function initializeWorkspace(workdir, options) {
1218
+ await ensureWorkspaceApmDirForInit(workdir);
1219
+ const apmDir = workspaceApmDir(workdir);
1220
+ await copyTemplateFiles(apmDir, workdir);
1221
+ const trimmedName = options?.name?.trim();
1222
+ if (trimmedName) {
1223
+ const apmConfigPath = toFsPath(join3(apmDir, "apm.config.json"));
1224
+ const config = readFileSync2(apmConfigPath, "utf8");
1225
+ const configJson = JSON.parse(config);
1226
+ configJson.name = trimmedName;
1227
+ writeFileSync2(
1228
+ apmConfigPath,
1229
+ `${JSON.stringify(configJson, null, 2)}
1230
+ `,
1231
+ "utf8"
1232
+ );
1233
+ }
1234
+ console.log(`[apm] \u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`);
1235
+ return { didInit: true };
1236
+ }
1237
+ async function ensureWorkspaceInitialized(workdir, options) {
1238
+ if (isWorkspaceApmInitialized(workdir)) {
1239
+ return { didInit: false };
1240
+ }
1241
+ console.log(
1242
+ `[apm] \u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u672A\u68C0\u6D4B\u5230\u5DF2\u521D\u59CB\u5316\u7684 .apm\uFF0C\u6B63\u5728\u81EA\u52A8\u521D\u59CB\u5316\u2026`
1243
+ );
1244
+ return initializeWorkspace(workdir, options);
1245
+ }
1246
+ async function runInit(name) {
1247
+ const workdir = resolveWorkdirPath();
1248
+ await initializeWorkspace(workdir, { name });
1249
+ console.log(`[apm] \u5DE5\u4F5C\u76EE\u5F55\u8DEF\u5F84\uFF1A${workdir}`);
1250
+ }
1251
+
1252
+ // src/commands/login.ts
1253
+ init_config();
1254
+ init_client();
1255
+ import { existsSync as existsSync3 } from "fs";
1256
+ import { ApiError } from "listpage-http";
1257
+
1290
1258
  // src/deployment-config-sync.ts
1291
1259
  init_client();
1292
- import { join as join4 } from "path";
1260
+ import { join as join5 } from "path";
1293
1261
  import { writeFileSync as writeFileSync4 } from "fs";
1294
1262
 
1295
1263
  // src/git-remote.ts
@@ -1438,46 +1406,6 @@ async function resolveDefaultRemoteBranch(cwd) {
1438
1406
  "[apm] \u65E0\u6CD5\u786E\u5B9A\u8FDC\u7A0B\u9ED8\u8BA4\u5206\u652F\uFF08origin/HEAD\u3001main\u3001master \u5747\u4E0D\u53EF\u7528\uFF09"
1439
1407
  );
1440
1408
  }
1441
- async function hasUpstream(cwd) {
1442
- try {
1443
- await execGit(cwd, ["rev-parse", "--abbrev-ref", "@{upstream}"], true);
1444
- return true;
1445
- } catch {
1446
- return false;
1447
- }
1448
- }
1449
- async function pushCurrentBranch(cwd) {
1450
- if (!await isGitRepo(cwd)) {
1451
- return false;
1452
- }
1453
- const originUrl = await tryReadGitOriginUrl(cwd);
1454
- if (!originUrl) {
1455
- console.log("[apm] \u672A\u914D\u7F6E remote.origin\uFF0C\u8DF3\u8FC7 push");
1456
- return false;
1457
- }
1458
- if (await hasUpstream(cwd)) {
1459
- await execGit(cwd, ["push"]);
1460
- } else {
1461
- await execGit(cwd, ["push", "-u", "origin", "HEAD"]);
1462
- }
1463
- return true;
1464
- }
1465
- var GITIGNORE_COMMIT_MESSAGE = "chore(apm): ignore .apm directory";
1466
- async function commitAndPushGitignore(workdir) {
1467
- if (!await isGitRepo(workdir)) {
1468
- console.log("[apm] \u5F53\u524D\u76EE\u5F55\u4E0D\u662F git \u4ED3\u5E93\uFF0C\u8BF7\u624B\u52A8\u63D0\u4EA4 .gitignore");
1469
- return;
1470
- }
1471
- await execGit(workdir, ["add", "--", ".gitignore"]);
1472
- await execGit(workdir, ["commit", "-m", GITIGNORE_COMMIT_MESSAGE]);
1473
- console.log(`[apm] \u5DF2\u63D0\u4EA4 .gitignore: ${GITIGNORE_COMMIT_MESSAGE}`);
1474
- const pushed = await pushCurrentBranch(workdir);
1475
- if (pushed) {
1476
- console.log("[apm] \u5DF2\u63A8\u9001 .gitignore");
1477
- } else {
1478
- console.log("[apm] \u672A\u914D\u7F6E remote.origin\uFF0C\u8BF7\u7A0D\u540E\u624B\u52A8 push .gitignore");
1479
- }
1480
- }
1481
1409
 
1482
1410
  // src/baseline-resolve.ts
1483
1411
  function formatBaselineDiagnostic(workdirPath, baselineWorkdirPath, diagnostic) {
@@ -1574,11 +1502,11 @@ import {
1574
1502
  statSync as statSync2,
1575
1503
  writeFileSync as writeFileSync3
1576
1504
  } from "fs";
1577
- import { basename as basename2, dirname as dirname2, join as join3, relative, resolve as resolve3 } from "path";
1505
+ import { basename as basename2, dirname as dirname2, join as join4, relative, resolve as resolve3 } from "path";
1578
1506
  var WORKSPACE_REPOS_MANIFEST = "workspace-repos.json";
1579
1507
  var WORKSPACE_REPOS_VERSION = 1;
1580
1508
  function manifestPath(workdir) {
1581
- return join3(workspaceApmDir(workdir), WORKSPACE_REPOS_MANIFEST);
1509
+ return join4(workspaceApmDir(workdir), WORKSPACE_REPOS_MANIFEST);
1582
1510
  }
1583
1511
  function absoluteRepoPath(workdir, entry) {
1584
1512
  if (entry.path === "." || entry.path === "") {
@@ -1793,7 +1721,7 @@ async function writeDeploymentConfigContent(apmDir, content, configName) {
1793
1721
  );
1794
1722
  return false;
1795
1723
  }
1796
- const apmConfigPath = toFsPath(join4(apmDir, "apm.config.json"));
1724
+ const apmConfigPath = toFsPath(join5(apmDir, "apm.config.json"));
1797
1725
  writeFileSync4(apmConfigPath, `${JSON.stringify(parsed, null, 2)}
1798
1726
  `, "utf8");
1799
1727
  console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u90E8\u7F72\u914D\u7F6E: ${configName}`);
@@ -1846,456 +1774,130 @@ ${diagnostic ?? ""}
1846
1774
  return { synced: true, repositoryId, configName: config.name };
1847
1775
  }
1848
1776
 
1849
- // src/project-documents-sync.ts
1850
- init_client();
1851
- init_config();
1852
- import {
1853
- existsSync as existsSync3,
1854
- readdirSync as readdirSync3,
1855
- readFileSync as readFileSync4,
1856
- rmSync,
1857
- writeFileSync as writeFileSync5
1858
- } from "fs";
1859
- import { createHash } from "crypto";
1860
- import { dirname as dirname3, join as join5, relative as relative2, sep } from "path";
1861
- var MANIFEST_FILE = "manifest.json";
1862
- function normalizeProjectIdForPath(projectId) {
1863
- const id = projectId.trim();
1864
- if (!id) {
1865
- throw new Error("projectId \u4E0D\u80FD\u4E3A\u7A7A");
1777
+ // src/commands/login.ts
1778
+ async function runLogin(opts) {
1779
+ const baseUrl = (opts.server?.trim() || process.env.AI_PM_SERVER?.trim() || DEFAULT_BASE_URL).replace(/\/+$/, "");
1780
+ const apiKey = (opts.apiKey?.trim() || process.env.APM_API_KEY?.trim() || "").replace(/^Bearer\s+/i, "");
1781
+ if (!apiKey.startsWith("cm_")) {
1782
+ console.error("[apm] \u8BF7\u63D0\u4F9B\u6709\u6548\u7684 API Key\uFF08cm_ \u5F00\u5934\uFF09");
1783
+ console.error("[apm] \u7528\u6CD5: apm login --api-key cm_xxx [--server URL]");
1784
+ process.exit(1);
1866
1785
  }
1867
- if (id.includes("..") || id.includes("/") || id.includes("\\") || id.includes("\0")) {
1868
- throw new Error(`\u975E\u6CD5 projectId: ${projectId}`);
1786
+ const api = createApmApiClient({
1787
+ baseUrl,
1788
+ clientMachineId: "",
1789
+ apiKey
1790
+ });
1791
+ let data;
1792
+ try {
1793
+ data = await api.cli.me({});
1794
+ } catch (raw) {
1795
+ if (raw instanceof ApiError) {
1796
+ const err = raw;
1797
+ console.error(
1798
+ `[apm] API Key \u9A8C\u8BC1\u5931\u8D25 (${err.httpStatus ?? err.code}):`,
1799
+ err.message
1800
+ );
1801
+ process.exit(1);
1802
+ }
1803
+ console.error("[apm] \u8BF7\u6C42\u5931\u8D25:", raw instanceof Error ? raw.message : raw);
1804
+ process.exit(1);
1869
1805
  }
1870
- return id;
1871
- }
1872
- function projectDocumentsDir(apmRoot, projectId) {
1873
- const id = normalizeProjectIdForPath(projectId);
1874
- return join5(apmRoot ?? workspaceApmDir(), "project", id);
1875
- }
1876
- function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
1877
- const normalized = normalizeLocalDocumentPath(documentPath);
1878
- return join5(
1879
- projectDocumentsDir(apmRoot, projectId),
1880
- ...normalized.split("/")
1806
+ const clientMachineId = data?.clientMachineId;
1807
+ if (!clientMachineId) {
1808
+ console.error("[apm] \u54CD\u5E94\u7F3A\u5C11 clientMachineId");
1809
+ process.exit(1);
1810
+ }
1811
+ const cfg = {
1812
+ baseUrl,
1813
+ clientMachineId,
1814
+ apiKey
1815
+ };
1816
+ await writeApmConfig(cfg);
1817
+ console.error(`[apm] \u5DF2\u4FDD\u5B58\u767B\u5F55\u4FE1\u606F: ${APM_CONFIG_PATH}`);
1818
+ console.log(
1819
+ JSON.stringify(
1820
+ {
1821
+ clientMachineId: cfg.clientMachineId,
1822
+ clientName: data.clientName,
1823
+ baseUrl: cfg.baseUrl
1824
+ },
1825
+ null,
1826
+ 2
1827
+ )
1881
1828
  );
1829
+ const workdir = resolveWorkdirPath();
1830
+ const apmDir = workspaceApmDir(workdir);
1831
+ if (existsSync3(apmDir)) {
1832
+ await syncRemoteDeploymentConfig(workdir, apmDir);
1833
+ }
1882
1834
  }
1883
- function normalizeLocalDocumentPath(path19) {
1884
- const trimmed = path19.trim().replace(/\\/g, "/");
1885
- if (!trimmed || trimmed.startsWith("/") || /^[a-zA-Z]:/.test(trimmed)) {
1886
- throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path19}`);
1835
+
1836
+ // src/commands/branch.ts
1837
+ init_client();
1838
+ var SESSION_BRANCH_PREFIX = "feat/session-";
1839
+ function branchNameForSession(sessionId) {
1840
+ const id = sessionId.trim();
1841
+ if (!id) {
1842
+ throw new Error("[apm] \u4F1A\u8BDD ID \u4E0D\u80FD\u4E3A\u7A7A");
1887
1843
  }
1888
- const segments = trimmed.split("/").filter(Boolean);
1889
- if (segments.some((segment) => segment === ".." || segment === ".")) {
1890
- throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path19}`);
1844
+ if (/[\s/\\]/.test(id)) {
1845
+ throw new Error(
1846
+ "[apm] \u4F1A\u8BDD ID \u4E0D\u80FD\u5305\u542B\u7A7A\u767D\u6216\u8DEF\u5F84\u5206\u9694\u7B26\uFF0C\u8BF7\u4F7F\u7528\u5B57\u6BCD\u3001\u6570\u5B57\u3001._- \u7B49"
1847
+ );
1891
1848
  }
1892
- return segments.join("/");
1893
- }
1894
- function hashLocalFileContent(content) {
1895
- return createHash("sha256").update(content, "utf8").digest("hex");
1849
+ return `${SESSION_BRANCH_PREFIX}${id}`;
1896
1850
  }
1897
- function readLocalManifest(apmRoot, projectId) {
1898
- const manifestPath3 = join5(
1899
- projectDocumentsDir(apmRoot, projectId),
1900
- MANIFEST_FILE
1901
- );
1902
- if (!existsSync3(manifestPath3)) {
1851
+ function sessionIdFromBranchName(branch) {
1852
+ const name = branch.trim().replace(/^origin\//, "");
1853
+ if (!name.startsWith(SESSION_BRANCH_PREFIX)) {
1903
1854
  return null;
1904
1855
  }
1856
+ const sessionId = name.slice(SESSION_BRANCH_PREFIX.length).trim();
1857
+ return sessionId || null;
1858
+ }
1859
+ async function localBranchExists(cwd, branch) {
1905
1860
  try {
1906
- return JSON.parse(
1907
- readFileSync4(manifestPath3, "utf8")
1861
+ await execGit(
1862
+ cwd,
1863
+ ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`],
1864
+ true
1908
1865
  );
1866
+ return true;
1909
1867
  } catch {
1910
- return null;
1868
+ return false;
1911
1869
  }
1912
1870
  }
1913
- function listLocalDocumentPaths(apmRoot, projectId) {
1914
- const root = projectDocumentsDir(apmRoot, projectId);
1915
- if (!existsSync3(root)) {
1916
- return [];
1871
+ async function commitWorkingTreeIfDirty(cwd, message) {
1872
+ await ensureGitRepo(cwd);
1873
+ if (!await isWorkingTreeDirty(cwd)) {
1874
+ return false;
1917
1875
  }
1918
- const paths = [];
1919
- const walk = (dir) => {
1920
- for (const entry of readdirSync3(dir, { withFileTypes: true })) {
1921
- const abs = join5(dir, entry.name);
1922
- if (entry.isDirectory()) {
1923
- walk(abs);
1924
- continue;
1925
- }
1926
- if (entry.isFile() && entry.name === MANIFEST_FILE) {
1927
- continue;
1928
- }
1929
- const rel = relative2(root, abs).split(sep).join("/");
1930
- paths.push(rel);
1931
- }
1932
- };
1933
- walk(root);
1934
- return paths.sort();
1876
+ const commitMessage = message?.trim() || `chore(apm): \u540C\u6B65\u5DE5\u4F5C\u533A (${await getCurrentBranch(cwd)})`;
1877
+ await execGit(cwd, ["add", "-A"]);
1878
+ await execGit(cwd, ["commit", "-m", commitMessage]);
1879
+ console.log(`[apm] \u5DF2\u63D0\u4EA4\u5DE5\u4F5C\u533A\u53D8\u66F4: ${commitMessage}`);
1880
+ return true;
1935
1881
  }
1936
- function diffManifestPaths(remote, local) {
1937
- const remoteMap = new Map(
1938
- (remote?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
1939
- );
1940
- const localMap = new Map(
1941
- (local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
1942
- );
1943
- const download = [];
1944
- for (const [path19, hash] of remoteMap) {
1945
- if (localMap.get(path19) !== hash) {
1946
- download.push(path19);
1947
- }
1948
- }
1949
- const deleteLocal = [];
1950
- for (const path19 of localMap.keys()) {
1951
- if (!remoteMap.has(path19)) {
1952
- deleteLocal.push(path19);
1953
- }
1954
- }
1955
- return { download, deleteLocal };
1956
- }
1957
- async function resolveProjectIdForSync(api, workdirPath, options) {
1958
- const explicit = options?.projectId?.trim();
1959
- if (explicit) {
1960
- return { projectId: explicit, diagnostic: null };
1961
- }
1962
- try {
1963
- const baseline = await api.cli.workspaceBaseline({ workdirPath });
1964
- const projectId = baseline.projectId?.trim() || null;
1965
- if (projectId) {
1966
- return { projectId, diagnostic: null };
1967
- }
1968
- const projectIds = baseline.projectIds ?? [];
1969
- if (projectIds.length === 0) {
1970
- return {
1971
- projectId: null,
1972
- diagnostic: baseline.diagnostic?.message ?? `\u5DE5\u4F5C\u7A7A\u95F4\u672A\u5173\u8054\u9879\u76EE\uFF0C\u65E0\u6CD5\u540C\u6B65\u9879\u76EE\u6587\u6863\uFF08\u76EE\u5F55\uFF1A${workdirPath}\uFF09\u3002
1973
- \u8BF7\u5728\u5E73\u53F0\u5C06\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u5230\u552F\u4E00\u9879\u76EE\u3002`
1974
- };
1975
- }
1976
- return {
1977
- projectId: null,
1978
- diagnostic: `\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u4E86 ${projectIds.length} \u4E2A\u9879\u76EE\uFF0C\u65E0\u6CD5\u786E\u5B9A\u540C\u6B65\u76EE\u6807\u3002
1979
- \u8BF7\u786E\u4FDD\u8BE5\u5DE5\u4F5C\u7A7A\u95F4\u53EA\u5173\u8054\u4E00\u4E2A\u9879\u76EE\u540E\u518D\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002`
1980
- };
1981
- } catch (err) {
1982
- const detail = err instanceof Error ? err.message : String(err);
1983
- return {
1984
- projectId: null,
1985
- diagnostic: detail.replace(/^\[apm\]\s*/, "")
1986
- };
1987
- }
1988
- }
1989
- async function syncProjectDocumentsPull(workdirPath, apmDir, options) {
1990
- const empty = {
1991
- synced: false,
1992
- projectId: null,
1993
- downloaded: 0,
1994
- deleted: 0
1995
- };
1996
- const cfg = await tryReadApmConfig();
1997
- if (!cfg || !resolveApiKey(cfg)) {
1998
- console.log(
1999
- "[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8DF3\u8FC7\u9879\u76EE\u6587\u6863\u540C\u6B65\u3002\n[apm] \u8BF7\u5148\u6267\u884C apm login\u3002"
2000
- );
2001
- return empty;
2002
- }
2003
- const api = createApmApiClient(cfg);
2004
- const { projectId, diagnostic } = await resolveProjectIdForSync(
2005
- api,
2006
- workdirPath,
2007
- options
2008
- );
2009
- if (!projectId) {
2010
- console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
2011
- ${diagnostic ?? ""}`);
2012
- return empty;
2013
- }
2014
- const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
2015
- const docsDir = projectDocumentsDir(targetApmDir, projectId);
2016
- await ensureDirExists(docsDir);
2017
- const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
2018
- { projectId }
2019
- );
2020
- if (!remoteManifest) {
2021
- console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
2022
- return { ...empty, projectId };
2023
- }
2024
- const localManifest = readLocalManifest(targetApmDir, projectId);
2025
- const { download, deleteLocal } = diffManifestPaths(
2026
- remoteManifest,
2027
- localManifest
2028
- );
2029
- let downloaded = 0;
2030
- if (download.length > 0) {
2031
- const { list } = await api.cli.listProjectDocuments({
2032
- projectId,
2033
- paths: download.join(",")
2034
- });
2035
- for (const doc of list) {
2036
- const absPath = toFsPath(
2037
- projectDocumentLocalPath(targetApmDir, projectId, doc.path)
2038
- );
2039
- await ensureDirExists(dirname3(absPath));
2040
- writeFileSync5(absPath, doc.content, "utf8");
2041
- downloaded += 1;
2042
- }
2043
- }
2044
- let deleted = 0;
2045
- for (const path19 of deleteLocal) {
2046
- const absPath = toFsPath(
2047
- projectDocumentLocalPath(targetApmDir, projectId, path19)
2048
- );
2049
- if (existsSync3(absPath)) {
2050
- rmSync(absPath, { force: true });
2051
- deleted += 1;
2052
- }
2053
- }
2054
- writeFileSync5(
2055
- toFsPath(join5(docsDir, MANIFEST_FILE)),
2056
- `${JSON.stringify(remoteManifest, null, 2)}
2057
- `,
2058
- "utf8"
2059
- );
2060
- console.log(
2061
- `[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: projectId=${projectId} \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted} \u2192 .apm/project/${projectId}/`
2062
- );
2063
- return {
2064
- synced: true,
2065
- projectId,
2066
- downloaded,
2067
- deleted
2068
- };
2069
- }
2070
- async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
2071
- const api = createApmApiClient(cfg);
2072
- const { projectId } = await resolveProjectIdForSync(
2073
- api,
2074
- workdirPath,
2075
- options
2076
- );
2077
- if (!projectId) {
2078
- return 0;
2079
- }
2080
- const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
2081
- const localPaths = listLocalDocumentPaths(targetApmDir, projectId);
2082
- if (localPaths.length === 0) {
2083
- console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
2084
- return 0;
2085
- }
2086
- const remoteManifest = (await api.cli.getProjectDocumentManifest({ projectId })).manifest ?? null;
2087
- const remoteHashByPath = new Map(
2088
- (remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
2089
- );
2090
- const remoteDescriptionByPath = new Map(
2091
- (remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.description])
2092
- );
2093
- let synced = 0;
2094
- for (const path19 of localPaths) {
2095
- const absPath = toFsPath(
2096
- projectDocumentLocalPath(targetApmDir, projectId, path19)
2097
- );
2098
- const content = readFileSync4(absPath, "utf8");
2099
- const contentHash = hashLocalFileContent(content);
2100
- if (remoteHashByPath.get(path19) === contentHash) {
2101
- continue;
2102
- }
2103
- await api.cli.upsertProjectDocument({
2104
- projectId,
2105
- path: path19,
2106
- content,
2107
- description: remoteDescriptionByPath.get(path19) ?? void 0
2108
- });
2109
- synced += 1;
2110
- console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path19}`);
2111
- }
2112
- if (synced === 0) {
2113
- console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
2114
- }
2115
- return synced;
2116
- }
2117
-
2118
- // src/commands/init.ts
2119
- async function ensureWorkspaceInitialized(workdir, options) {
2120
- if (isWorkspaceApmInitialized(workdir)) {
2121
- return { didInit: false };
2122
- }
2123
- console.log(
2124
- `[apm] \u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u672A\u68C0\u6D4B\u5230\u5DF2\u521D\u59CB\u5316\u7684 .apm\uFF0C\u6B63\u5728\u81EA\u52A8\u521D\u59CB\u5316\u2026`
2125
- );
2126
- await ensureWorkspaceApmDirForInit(workdir);
2127
- if (ensureApmGitignoredInRepo(workdir)) {
2128
- console.log("[apm] \u5DF2\u5728 .gitignore \u4E2D\u6DFB\u52A0 **/.apm/**");
2129
- await commitAndPushGitignore(workdir);
2130
- }
2131
- const apmDir = workspaceApmDir(workdir);
2132
- await copyTemplateFiles(apmDir, workdir);
2133
- const syncResult = await syncRemoteDeploymentConfig(workdir, apmDir);
2134
- await syncProjectDocumentsPull(workdir, apmDir);
2135
- const trimmedName = options?.name?.trim();
2136
- if (trimmedName) {
2137
- const apmConfigPath = toFsPath(join6(apmDir, "apm.config.json"));
2138
- const config = readFileSync5(apmConfigPath, "utf8");
2139
- const configJson = JSON.parse(config);
2140
- configJson.name = trimmedName;
2141
- writeFileSync6(
2142
- apmConfigPath,
2143
- `${JSON.stringify(configJson, null, 2)}
2144
- `,
2145
- "utf8"
2146
- );
2147
- }
2148
- console.log(`[apm] \u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`);
2149
- return { didInit: true, syncResult };
2150
- }
2151
- async function runInit(name) {
2152
- const workdir = resolveWorkdirPath();
2153
- await ensureWorkspaceApmDirForInit(workdir);
2154
- const { didInit, syncResult } = await ensureWorkspaceInitialized(workdir, {
2155
- name
2156
- });
2157
- if (!didInit) {
2158
- throw new Error(
2159
- "[apm] .apm \u76EE\u5F55\u5DF2\u5B58\u5728\u4E14\u975E\u7A7A\uFF0C\u8BF7\u5148\u5907\u4EFD\u3001\u6E05\u7A7A\u6216\u5220\u9664\u540E\u518D\u6267\u884C init"
2160
- );
2161
- }
2162
- console.log(`[apm] \u5DE5\u4F5C\u76EE\u5F55\u8DEF\u5F84\uFF1A${workdir}`);
2163
- if (syncResult && !syncResult.synced) {
2164
- console.log(
2165
- "[apm] \u5F53\u524D .apm/apm.config.json \u4E3A\u6A21\u677F\u9ED8\u8BA4\u503C\uFF1B\u5B8C\u6210\u5E73\u53F0\u767B\u8BB0\u540E\u6267\u884C apm sync-deploy-config"
2166
- );
2167
- }
2168
- console.log("[apm] \u8BF7\u5728\u5E73\u53F0\u300C\u63A5\u5165\u7BA1\u7406 \u2192 \u5DE5\u4F5C\u7A7A\u95F4\u300D\u767B\u8BB0\u4E0A\u8FF0\u76EE\u5F55\u8DEF\u5F84");
2169
- }
2170
-
2171
- // src/commands/login.ts
2172
- init_config();
2173
- init_client();
2174
- import { existsSync as existsSync4 } from "fs";
2175
- import { ApiError } from "listpage-http";
2176
- async function runLogin(opts) {
2177
- const baseUrl = (opts.server?.trim() || process.env.AI_PM_SERVER?.trim() || DEFAULT_BASE_URL).replace(/\/+$/, "");
2178
- const apiKey = (opts.apiKey?.trim() || process.env.APM_API_KEY?.trim() || "").replace(/^Bearer\s+/i, "");
2179
- if (!apiKey.startsWith("cm_")) {
2180
- console.error("[apm] \u8BF7\u63D0\u4F9B\u6709\u6548\u7684 API Key\uFF08cm_ \u5F00\u5934\uFF09");
2181
- console.error("[apm] \u7528\u6CD5: apm login --api-key cm_xxx [--server URL]");
2182
- process.exit(1);
2183
- }
2184
- const api = createApmApiClient({
2185
- baseUrl,
2186
- clientMachineId: "",
2187
- apiKey
2188
- });
2189
- let data;
2190
- try {
2191
- data = await api.cli.me({});
2192
- } catch (raw) {
2193
- if (raw instanceof ApiError) {
2194
- const err = raw;
2195
- console.error(
2196
- `[apm] API Key \u9A8C\u8BC1\u5931\u8D25 (${err.httpStatus ?? err.code}):`,
2197
- err.message
2198
- );
2199
- process.exit(1);
2200
- }
2201
- console.error("[apm] \u8BF7\u6C42\u5931\u8D25:", raw instanceof Error ? raw.message : raw);
2202
- process.exit(1);
2203
- }
2204
- const clientMachineId = data?.clientMachineId;
2205
- if (!clientMachineId) {
2206
- console.error("[apm] \u54CD\u5E94\u7F3A\u5C11 clientMachineId");
2207
- process.exit(1);
2208
- }
2209
- const cfg = {
2210
- baseUrl,
2211
- clientMachineId,
2212
- apiKey
2213
- };
2214
- await writeApmConfig(cfg);
2215
- console.error(`[apm] \u5DF2\u4FDD\u5B58\u767B\u5F55\u4FE1\u606F: ${APM_CONFIG_PATH}`);
2216
- console.log(
2217
- JSON.stringify(
2218
- {
2219
- clientMachineId: cfg.clientMachineId,
2220
- clientName: data.clientName,
2221
- baseUrl: cfg.baseUrl
2222
- },
2223
- null,
2224
- 2
2225
- )
2226
- );
2227
- const workdir = resolveWorkdirPath();
2228
- const apmDir = workspaceApmDir(workdir);
2229
- if (existsSync4(apmDir)) {
2230
- await syncRemoteDeploymentConfig(workdir, apmDir);
2231
- }
2232
- }
2233
-
2234
- // src/commands/branch.ts
2235
- init_client();
2236
- var SESSION_BRANCH_PREFIX = "feat/session-";
2237
- function branchNameForSession(sessionId) {
2238
- const id = sessionId.trim();
2239
- if (!id) {
2240
- throw new Error("[apm] \u4F1A\u8BDD ID \u4E0D\u80FD\u4E3A\u7A7A");
2241
- }
2242
- if (/[\s/\\]/.test(id)) {
2243
- throw new Error(
2244
- "[apm] \u4F1A\u8BDD ID \u4E0D\u80FD\u5305\u542B\u7A7A\u767D\u6216\u8DEF\u5F84\u5206\u9694\u7B26\uFF0C\u8BF7\u4F7F\u7528\u5B57\u6BCD\u3001\u6570\u5B57\u3001._- \u7B49"
2245
- );
2246
- }
2247
- return `${SESSION_BRANCH_PREFIX}${id}`;
2248
- }
2249
- function sessionIdFromBranchName(branch) {
2250
- const name = branch.trim().replace(/^origin\//, "");
2251
- if (!name.startsWith(SESSION_BRANCH_PREFIX)) {
2252
- return null;
2253
- }
2254
- const sessionId = name.slice(SESSION_BRANCH_PREFIX.length).trim();
2255
- return sessionId || null;
2256
- }
2257
- async function localBranchExists(cwd, branch) {
2258
- try {
2259
- await execGit(
2260
- cwd,
2261
- ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`],
2262
- true
2263
- );
2264
- return true;
2265
- } catch {
2266
- return false;
2267
- }
2268
- }
2269
- async function commitWorkingTreeIfDirty(cwd, message) {
2270
- await ensureGitRepo(cwd);
2271
- if (!await isWorkingTreeDirty(cwd)) {
2272
- return false;
2273
- }
2274
- const commitMessage = message?.trim() || `chore(apm): \u540C\u6B65\u5DE5\u4F5C\u533A (${await getCurrentBranch(cwd)})`;
2275
- await execGit(cwd, ["add", "-A"]);
2276
- await execGit(cwd, ["commit", "-m", commitMessage]);
2277
- console.log(`[apm] \u5DF2\u63D0\u4EA4\u5DE5\u4F5C\u533A\u53D8\u66F4: ${commitMessage}`);
2278
- return true;
2279
- }
2280
- async function ensureFeatureBranch(branch, baselineBranch, options) {
2281
- const cwd = options.cwd ?? process.cwd();
2282
- const gitRoot = await resolveGitRepoRoot(cwd);
2283
- const commitMessage = options.message?.trim() || `chore(apm): \u540C\u6B65\u5DE5\u4F5C\u533A (${branch})`;
2284
- await ensureGitRepo(gitRoot);
2285
- await ensureRemoteBaselineBranch(gitRoot, baselineBranch);
2286
- const current = await getCurrentBranch(gitRoot);
2287
- const dirty = await isWorkingTreeDirty(gitRoot);
2288
- if (dirty) {
2289
- if (current === branch) {
2290
- await commitWorkingTreeIfDirty(gitRoot, commitMessage);
2291
- } else {
2292
- await execGit(gitRoot, [
2293
- "stash",
2294
- "push",
2295
- "-u",
2296
- "-m",
2297
- `apm: switch to ${branch}`
2298
- ]);
1882
+ async function ensureFeatureBranch(branch, baselineBranch, options) {
1883
+ const cwd = options.cwd ?? process.cwd();
1884
+ const gitRoot = await resolveGitRepoRoot(cwd);
1885
+ const commitMessage = options.message?.trim() || `chore(apm): \u540C\u6B65\u5DE5\u4F5C\u533A (${branch})`;
1886
+ await ensureGitRepo(gitRoot);
1887
+ await ensureRemoteBaselineBranch(gitRoot, baselineBranch);
1888
+ const current = await getCurrentBranch(gitRoot);
1889
+ const dirty = await isWorkingTreeDirty(gitRoot);
1890
+ if (dirty) {
1891
+ if (current === branch) {
1892
+ await commitWorkingTreeIfDirty(gitRoot, commitMessage);
1893
+ } else {
1894
+ await execGit(gitRoot, [
1895
+ "stash",
1896
+ "push",
1897
+ "-u",
1898
+ "-m",
1899
+ `apm: switch to ${branch}`
1900
+ ]);
2299
1901
  }
2300
1902
  }
2301
1903
  const onTargetBranch = await getCurrentBranch(gitRoot) === branch;
@@ -2682,7 +2284,7 @@ async function runCleanBranches(options = {}) {
2682
2284
 
2683
2285
  // src/commands/pull.ts
2684
2286
  init_client();
2685
- import { writeFileSync as writeFileSync10 } from "fs";
2287
+ import { writeFileSync as writeFileSync9 } from "fs";
2686
2288
  import { join as join10 } from "path";
2687
2289
  import { stringify as yamlStringify } from "yaml";
2688
2290
 
@@ -2716,9 +2318,9 @@ function formatSessionMessagesXml(sessionId, messages) {
2716
2318
  }
2717
2319
 
2718
2320
  // src/commands/sync-session-attachments.ts
2719
- import { existsSync as existsSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync7 } from "fs";
2720
- import { join as join7 } from "path";
2721
- var MANIFEST_FILE2 = ".sync-manifest.json";
2321
+ import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "fs";
2322
+ import { join as join6 } from "path";
2323
+ var MANIFEST_FILE = ".sync-manifest.json";
2722
2324
  async function downloadAttachment(cfg, attachmentId) {
2723
2325
  const base = cfg.baseUrl.trim().replace(/\/+$/, "");
2724
2326
  const url = `${base}/api/v1/tasks/attachments/file?${new URLSearchParams({
@@ -2733,13 +2335,13 @@ async function downloadAttachment(cfg, attachmentId) {
2733
2335
  return Buffer.from(await res.arrayBuffer());
2734
2336
  }
2735
2337
  function loadManifest(dir) {
2736
- const path19 = join7(dir, MANIFEST_FILE2);
2737
- if (!existsSync5(path19)) {
2338
+ const path19 = join6(dir, MANIFEST_FILE);
2339
+ if (!existsSync4(path19)) {
2738
2340
  return { version: 1, attachments: {} };
2739
2341
  }
2740
2342
  try {
2741
2343
  const parsed = JSON.parse(
2742
- readFileSync6(path19, "utf8")
2344
+ readFileSync4(path19, "utf8")
2743
2345
  );
2744
2346
  if (parsed?.version === 1 && parsed.attachments && typeof parsed.attachments === "object") {
2745
2347
  return parsed;
@@ -2749,15 +2351,15 @@ function loadManifest(dir) {
2749
2351
  return { version: 1, attachments: {} };
2750
2352
  }
2751
2353
  function saveManifest(dir, manifest) {
2752
- writeFileSync7(
2753
- join7(dir, MANIFEST_FILE2),
2354
+ writeFileSync5(
2355
+ join6(dir, MANIFEST_FILE),
2754
2356
  `${JSON.stringify(manifest, null, 2)}
2755
2357
  `,
2756
2358
  "utf8"
2757
2359
  );
2758
2360
  }
2759
2361
  function isAttachmentUpToDate(entry, item, dest) {
2760
- if (!entry || !existsSync5(dest)) return false;
2362
+ if (!entry || !existsSync4(dest)) return false;
2761
2363
  if (entry.name !== item.name) return false;
2762
2364
  const createdAt = item.createdAt ?? "";
2763
2365
  return entry.createdAt === createdAt;
@@ -2772,7 +2374,7 @@ async function syncAttachmentsToDirectory(cfg, attachments, dir, logLabel) {
2772
2374
  const nextManifest = { version: 1, attachments: {} };
2773
2375
  const names = [];
2774
2376
  for (const item of attachments) {
2775
- const dest = join7(dir, item.name);
2377
+ const dest = join6(dir, item.name);
2776
2378
  const entry = manifest.attachments[item.id];
2777
2379
  const createdAt = item.createdAt ?? "";
2778
2380
  names.push(item.name);
@@ -2782,7 +2384,7 @@ async function syncAttachmentsToDirectory(cfg, attachments, dir, logLabel) {
2782
2384
  continue;
2783
2385
  }
2784
2386
  const buffer = await downloadAttachment(cfg, item.id);
2785
- writeFileSync7(dest, buffer);
2387
+ writeFileSync5(dest, buffer);
2786
2388
  nextManifest.attachments[item.id] = {
2787
2389
  name: item.name,
2788
2390
  createdAt
@@ -2793,7 +2395,7 @@ async function syncAttachmentsToDirectory(cfg, attachments, dir, logLabel) {
2793
2395
  return names;
2794
2396
  }
2795
2397
  async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
2796
- const dir = join7(sessionDir(sessionId, apmRoot), SESSION_ATTACHMENTS_SUBDIR);
2398
+ const dir = join6(sessionDir(sessionId, apmRoot), SESSION_ATTACHMENTS_SUBDIR);
2797
2399
  return syncAttachmentsToDirectory(
2798
2400
  cfg,
2799
2401
  attachments,
@@ -2804,46 +2406,46 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
2804
2406
 
2805
2407
  // src/rules-sync.ts
2806
2408
  init_client();
2807
- import { basename as basename3, extname as extname2, join as join9 } from "path";
2808
- import { existsSync as existsSync7, readFileSync as readFileSync7, rmSync as rmSync3, writeFileSync as writeFileSync9 } from "fs";
2409
+ import { basename as basename3, extname as extname2, join as join8 } from "path";
2410
+ import { existsSync as existsSync6, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync7 } from "fs";
2809
2411
 
2810
2412
  // src/skills-sync.ts
2811
2413
  import {
2812
2414
  copyFileSync as copyFileSync2,
2813
2415
  cpSync,
2814
- existsSync as existsSync6,
2416
+ existsSync as existsSync5,
2815
2417
  mkdirSync as mkdirSync4,
2816
- readdirSync as readdirSync4,
2817
- rmSync as rmSync2,
2418
+ readdirSync as readdirSync3,
2419
+ rmSync,
2818
2420
  statSync as statSync3,
2819
- writeFileSync as writeFileSync8
2421
+ writeFileSync as writeFileSync6
2820
2422
  } from "fs";
2821
- import { join as join8 } from "path";
2822
- var AGENTS_TEMPLATE_PATH = join8(CLI_TEMPLATE_DIR, "AGENTS.md");
2823
- var BASE_SKILLS_TEMPLATE_DIR = join8(CLI_TEMPLATE_DIR, "skills");
2824
- var BASE_RULES_TEMPLATE_DIR = join8(CLI_TEMPLATE_DIR, "rules");
2423
+ import { join as join7 } from "path";
2424
+ var AGENTS_TEMPLATE_PATH = join7(CLI_TEMPLATE_DIR, "AGENTS.md");
2425
+ var BASE_SKILLS_TEMPLATE_DIR = join7(CLI_TEMPLATE_DIR, "skills");
2426
+ var BASE_RULES_TEMPLATE_DIR = join7(CLI_TEMPLATE_DIR, "rules");
2825
2427
  function sanitizeSkillDirName(name) {
2826
2428
  const trimmed = name.trim();
2827
2429
  if (!trimmed) return "skill";
2828
2430
  return trimmed.replace(/[/\\:*?"<>|]/g, "_");
2829
2431
  }
2830
2432
  function listBaseSkillDirNames() {
2831
- if (!existsSync6(BASE_SKILLS_TEMPLATE_DIR)) return [];
2832
- return readdirSync4(BASE_SKILLS_TEMPLATE_DIR).filter((name) => {
2833
- const path19 = join8(BASE_SKILLS_TEMPLATE_DIR, name);
2433
+ if (!existsSync5(BASE_SKILLS_TEMPLATE_DIR)) return [];
2434
+ return readdirSync3(BASE_SKILLS_TEMPLATE_DIR).filter((name) => {
2435
+ const path19 = join7(BASE_SKILLS_TEMPLATE_DIR, name);
2834
2436
  return statSync3(path19).isDirectory();
2835
2437
  });
2836
2438
  }
2837
2439
  function syncAgentsGuide(apmDir) {
2838
- if (!existsSync6(AGENTS_TEMPLATE_PATH)) return false;
2440
+ if (!existsSync5(AGENTS_TEMPLATE_PATH)) return false;
2839
2441
  mkdirSync4(apmDir, { recursive: true });
2840
- copyFileSync2(AGENTS_TEMPLATE_PATH, join8(apmDir, "AGENTS.md"));
2442
+ copyFileSync2(AGENTS_TEMPLATE_PATH, join7(apmDir, "AGENTS.md"));
2841
2443
  return true;
2842
2444
  }
2843
2445
  function listBaseRuleFileNames() {
2844
- if (!existsSync6(BASE_RULES_TEMPLATE_DIR)) return [];
2845
- return readdirSync4(BASE_RULES_TEMPLATE_DIR).filter((name) => {
2846
- const path19 = join8(BASE_RULES_TEMPLATE_DIR, name);
2446
+ if (!existsSync5(BASE_RULES_TEMPLATE_DIR)) return [];
2447
+ return readdirSync3(BASE_RULES_TEMPLATE_DIR).filter((name) => {
2448
+ const path19 = join7(BASE_RULES_TEMPLATE_DIR, name);
2847
2449
  return statSync3(path19).isFile();
2848
2450
  });
2849
2451
  }
@@ -2851,144 +2453,413 @@ function syncBaseRules(rulesDir) {
2851
2453
  mkdirSync4(rulesDir, { recursive: true });
2852
2454
  const names = listBaseRuleFileNames();
2853
2455
  for (const name of names) {
2854
- const src = join8(BASE_RULES_TEMPLATE_DIR, name);
2855
- const dest = join8(rulesDir, name);
2456
+ const src = join7(BASE_RULES_TEMPLATE_DIR, name);
2457
+ const dest = join7(rulesDir, name);
2856
2458
  copyFileSync2(src, dest);
2857
2459
  }
2858
- return names;
2460
+ return names;
2461
+ }
2462
+ function syncBaseSkills(skillsDir) {
2463
+ mkdirSync4(skillsDir, { recursive: true });
2464
+ const names = listBaseSkillDirNames();
2465
+ for (const name of names) {
2466
+ const src = join7(BASE_SKILLS_TEMPLATE_DIR, name);
2467
+ const dest = join7(skillsDir, name);
2468
+ cpSync(src, dest, { recursive: true, force: true });
2469
+ }
2470
+ return names;
2471
+ }
2472
+ function syncSupplementarySkills(skillsDir, list) {
2473
+ const baseNames = new Set(listBaseSkillDirNames());
2474
+ const apiDirNames = /* @__PURE__ */ new Set();
2475
+ const written = [];
2476
+ const skipped = [];
2477
+ for (const skill of list) {
2478
+ const dirName = sanitizeSkillDirName(skill.name);
2479
+ apiDirNames.add(dirName);
2480
+ if (baseNames.has(dirName)) {
2481
+ skipped.push(dirName);
2482
+ continue;
2483
+ }
2484
+ const skillDir = join7(skillsDir, dirName);
2485
+ mkdirSync4(skillDir, { recursive: true });
2486
+ writeFileSync6(join7(skillDir, "SKILL.md"), skill.content ?? "", "utf8");
2487
+ written.push(dirName);
2488
+ }
2489
+ const removed = [];
2490
+ if (!existsSync5(skillsDir)) return { written, skipped, removed };
2491
+ for (const entry of readdirSync3(skillsDir)) {
2492
+ const full = join7(skillsDir, entry);
2493
+ if (!statSync3(full).isDirectory()) continue;
2494
+ if (baseNames.has(entry)) continue;
2495
+ if (apiDirNames.has(entry)) continue;
2496
+ rmSync(full, { recursive: true, force: true });
2497
+ removed.push(entry);
2498
+ }
2499
+ return { written, skipped, removed };
2500
+ }
2501
+
2502
+ // src/rules-sync.ts
2503
+ var MANIFEST_FILE2 = ".rules-sync-manifest.json";
2504
+ function ruleLocalFileName(ruleName) {
2505
+ const trimmed = ruleName.trim();
2506
+ if (!trimmed) return "rule.md";
2507
+ const sanitized = trimmed.replace(/[/\\:*?"<>|]/g, "_");
2508
+ if (extname2(sanitized).toLowerCase() === ".md") return sanitized;
2509
+ return `${sanitized}.md`;
2510
+ }
2511
+ function loadManifest2(rulesDir) {
2512
+ const path19 = join8(rulesDir, MANIFEST_FILE2);
2513
+ if (!existsSync6(toFsPath(path19))) {
2514
+ return { version: 1, rules: {} };
2515
+ }
2516
+ try {
2517
+ const parsed = JSON.parse(
2518
+ readFileSync5(toFsPath(path19), "utf8")
2519
+ );
2520
+ if (parsed?.version === 1 && parsed.rules && typeof parsed.rules === "object") {
2521
+ return parsed;
2522
+ }
2523
+ } catch {
2524
+ }
2525
+ return { version: 1, rules: {} };
2526
+ }
2527
+ function saveManifest2(rulesDir, manifest) {
2528
+ writeFileSync7(
2529
+ toFsPath(join8(rulesDir, MANIFEST_FILE2)),
2530
+ `${JSON.stringify(manifest, null, 2)}
2531
+ `,
2532
+ "utf8"
2533
+ );
2534
+ }
2535
+ function isBaseRuleFileName(fileName) {
2536
+ return listBaseRuleFileNames().includes(basename3(fileName));
2537
+ }
2538
+ function isRuleUpToDate(entry, rule, dest) {
2539
+ if (!entry || !existsSync6(toFsPath(dest))) return false;
2540
+ if (entry.fileName !== ruleLocalFileName(rule.name)) return false;
2541
+ const updatedAt = rule.updatedAt ?? "";
2542
+ if (entry.updatedAt !== updatedAt) return false;
2543
+ const localContent = readFileSync5(toFsPath(dest), "utf8");
2544
+ return localContent === (rule.content ?? "");
2545
+ }
2546
+ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
2547
+ const api = createApmApiClient(cfg);
2548
+ const baseline = await resolveBranchBaseline(api, sessionId, workdirPath);
2549
+ const repositoryId = baseline.repositoryId;
2550
+ const rulesDir = join8(apmRoot ?? workspaceApmDir(workdirPath), "rules");
2551
+ await ensureDirExists(rulesDir);
2552
+ if (!repositoryId) {
2553
+ console.log(
2554
+ `[apm] \u672A\u5339\u914D\u5230\u7ED1\u5B9A\u4ED3\u5E93\u7684\u5DE5\u4F5C\u7A7A\u95F4\uFF0C\u8DF3\u8FC7\u5E73\u53F0\u89C4\u5219\u540C\u6B65\uFF08\u8DEF\u5F84\uFF1A${workdirPath}\uFF09`
2555
+ );
2556
+ return { written: [], skipped: [], removed: [], repositoryId: null };
2557
+ }
2558
+ const { list } = await api.cli.listRules({ repositoryId });
2559
+ const manifest = loadManifest2(rulesDir);
2560
+ const nextManifest = { version: 1, rules: {} };
2561
+ const remoteIds = /* @__PURE__ */ new Set();
2562
+ const written = [];
2563
+ const skipped = [];
2564
+ for (const rule of list) {
2565
+ remoteIds.add(rule.id);
2566
+ const fileName = ruleLocalFileName(rule.name);
2567
+ const dest = join8(rulesDir, fileName);
2568
+ const entry = manifest.rules[rule.id];
2569
+ const updatedAt = rule.updatedAt ?? "";
2570
+ if (isRuleUpToDate(entry, rule, dest)) {
2571
+ nextManifest.rules[rule.id] = entry;
2572
+ skipped.push(fileName);
2573
+ console.log(`[apm] \u89C4\u5219\u65E0\u53D8\u5316\uFF0C\u5DF2\u8DF3\u8FC7: rules/${fileName}`);
2574
+ continue;
2575
+ }
2576
+ writeFileSync7(toFsPath(dest), rule.content ?? "", "utf8");
2577
+ nextManifest.rules[rule.id] = { fileName, updatedAt };
2578
+ written.push(fileName);
2579
+ console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u89C4\u5219: rules/${fileName}`);
2580
+ }
2581
+ const removed = [];
2582
+ for (const [ruleId, entry] of Object.entries(manifest.rules)) {
2583
+ if (remoteIds.has(ruleId)) continue;
2584
+ if (isBaseRuleFileName(entry.fileName)) continue;
2585
+ const dest = join8(rulesDir, entry.fileName);
2586
+ if (existsSync6(toFsPath(dest))) {
2587
+ rmSync2(toFsPath(dest), { force: true });
2588
+ }
2589
+ removed.push(entry.fileName);
2590
+ console.log(`[apm] \u5DF2\u79FB\u9664\u5DF2\u4E0B\u7EBF\u7684\u5E73\u53F0\u89C4\u5219: rules/${entry.fileName}`);
2591
+ }
2592
+ saveManifest2(rulesDir, nextManifest);
2593
+ return { written, skipped, removed, repositoryId };
2594
+ }
2595
+
2596
+ // src/project-documents-sync.ts
2597
+ init_client();
2598
+ init_config();
2599
+ import {
2600
+ existsSync as existsSync7,
2601
+ readdirSync as readdirSync4,
2602
+ readFileSync as readFileSync6,
2603
+ rmSync as rmSync3,
2604
+ writeFileSync as writeFileSync8
2605
+ } from "fs";
2606
+ import { createHash } from "crypto";
2607
+ import { dirname as dirname3, join as join9, relative as relative2, sep } from "path";
2608
+ var MANIFEST_FILE3 = "manifest.json";
2609
+ function normalizeProjectIdForPath(projectId) {
2610
+ const id = projectId.trim();
2611
+ if (!id) {
2612
+ throw new Error("projectId \u4E0D\u80FD\u4E3A\u7A7A");
2613
+ }
2614
+ if (id.includes("..") || id.includes("/") || id.includes("\\") || id.includes("\0")) {
2615
+ throw new Error(`\u975E\u6CD5 projectId: ${projectId}`);
2616
+ }
2617
+ return id;
2618
+ }
2619
+ function projectDocumentsDir(apmRoot, projectId) {
2620
+ const id = normalizeProjectIdForPath(projectId);
2621
+ return join9(apmRoot ?? workspaceApmDir(), "project", id);
2622
+ }
2623
+ function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
2624
+ const normalized = normalizeLocalDocumentPath(documentPath);
2625
+ return join9(
2626
+ projectDocumentsDir(apmRoot, projectId),
2627
+ ...normalized.split("/")
2628
+ );
2629
+ }
2630
+ function normalizeLocalDocumentPath(path19) {
2631
+ const trimmed = path19.trim().replace(/\\/g, "/");
2632
+ if (!trimmed || trimmed.startsWith("/") || /^[a-zA-Z]:/.test(trimmed)) {
2633
+ throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path19}`);
2634
+ }
2635
+ const segments = trimmed.split("/").filter(Boolean);
2636
+ if (segments.some((segment) => segment === ".." || segment === ".")) {
2637
+ throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path19}`);
2638
+ }
2639
+ return segments.join("/");
2640
+ }
2641
+ function hashLocalFileContent(content) {
2642
+ return createHash("sha256").update(content, "utf8").digest("hex");
2643
+ }
2644
+ function readLocalManifest(apmRoot, projectId) {
2645
+ const manifestPath3 = join9(
2646
+ projectDocumentsDir(apmRoot, projectId),
2647
+ MANIFEST_FILE3
2648
+ );
2649
+ if (!existsSync7(manifestPath3)) {
2650
+ return null;
2651
+ }
2652
+ try {
2653
+ return JSON.parse(
2654
+ readFileSync6(manifestPath3, "utf8")
2655
+ );
2656
+ } catch {
2657
+ return null;
2658
+ }
2659
+ }
2660
+ function listLocalDocumentPaths(apmRoot, projectId) {
2661
+ const root = projectDocumentsDir(apmRoot, projectId);
2662
+ if (!existsSync7(root)) {
2663
+ return [];
2664
+ }
2665
+ const paths = [];
2666
+ const walk = (dir) => {
2667
+ for (const entry of readdirSync4(dir, { withFileTypes: true })) {
2668
+ const abs = join9(dir, entry.name);
2669
+ if (entry.isDirectory()) {
2670
+ walk(abs);
2671
+ continue;
2672
+ }
2673
+ if (entry.isFile() && entry.name === MANIFEST_FILE3) {
2674
+ continue;
2675
+ }
2676
+ const rel = relative2(root, abs).split(sep).join("/");
2677
+ paths.push(rel);
2678
+ }
2679
+ };
2680
+ walk(root);
2681
+ return paths.sort();
2682
+ }
2683
+ function diffManifestPaths(remote, local) {
2684
+ const remoteMap = new Map(
2685
+ (remote?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
2686
+ );
2687
+ const localMap = new Map(
2688
+ (local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
2689
+ );
2690
+ const download = [];
2691
+ for (const [path19, hash] of remoteMap) {
2692
+ if (localMap.get(path19) !== hash) {
2693
+ download.push(path19);
2694
+ }
2695
+ }
2696
+ const deleteLocal = [];
2697
+ for (const path19 of localMap.keys()) {
2698
+ if (!remoteMap.has(path19)) {
2699
+ deleteLocal.push(path19);
2700
+ }
2701
+ }
2702
+ return { download, deleteLocal };
2859
2703
  }
2860
- function syncBaseSkills(skillsDir) {
2861
- mkdirSync4(skillsDir, { recursive: true });
2862
- const names = listBaseSkillDirNames();
2863
- for (const name of names) {
2864
- const src = join8(BASE_SKILLS_TEMPLATE_DIR, name);
2865
- const dest = join8(skillsDir, name);
2866
- cpSync(src, dest, { recursive: true, force: true });
2704
+ async function resolveProjectIdForSync(api, workdirPath, options) {
2705
+ const explicit = options?.projectId?.trim();
2706
+ if (explicit) {
2707
+ return { projectId: explicit, diagnostic: null };
2867
2708
  }
2868
- return names;
2869
- }
2870
- function syncSupplementarySkills(skillsDir, list) {
2871
- const baseNames = new Set(listBaseSkillDirNames());
2872
- const apiDirNames = /* @__PURE__ */ new Set();
2873
- const written = [];
2874
- const skipped = [];
2875
- for (const skill of list) {
2876
- const dirName = sanitizeSkillDirName(skill.name);
2877
- apiDirNames.add(dirName);
2878
- if (baseNames.has(dirName)) {
2879
- skipped.push(dirName);
2880
- continue;
2709
+ try {
2710
+ const baseline = await api.cli.workspaceBaseline({ workdirPath });
2711
+ const projectId = baseline.projectId?.trim() || null;
2712
+ if (projectId) {
2713
+ return { projectId, diagnostic: null };
2881
2714
  }
2882
- const skillDir = join8(skillsDir, dirName);
2883
- mkdirSync4(skillDir, { recursive: true });
2884
- writeFileSync8(join8(skillDir, "SKILL.md"), skill.content ?? "", "utf8");
2885
- written.push(dirName);
2886
- }
2887
- const removed = [];
2888
- if (!existsSync6(skillsDir)) return { written, skipped, removed };
2889
- for (const entry of readdirSync4(skillsDir)) {
2890
- const full = join8(skillsDir, entry);
2891
- if (!statSync3(full).isDirectory()) continue;
2892
- if (baseNames.has(entry)) continue;
2893
- if (apiDirNames.has(entry)) continue;
2894
- rmSync2(full, { recursive: true, force: true });
2895
- removed.push(entry);
2715
+ const projectIds = baseline.projectIds ?? [];
2716
+ if (projectIds.length === 0) {
2717
+ return {
2718
+ projectId: null,
2719
+ diagnostic: baseline.diagnostic?.message ?? `\u5DE5\u4F5C\u7A7A\u95F4\u672A\u5173\u8054\u9879\u76EE\uFF0C\u65E0\u6CD5\u540C\u6B65\u9879\u76EE\u6587\u6863\uFF08\u76EE\u5F55\uFF1A${workdirPath}\uFF09\u3002
2720
+ \u8BF7\u5728\u5E73\u53F0\u5C06\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u5230\u552F\u4E00\u9879\u76EE\u3002`
2721
+ };
2722
+ }
2723
+ return {
2724
+ projectId: null,
2725
+ diagnostic: `\u5DE5\u4F5C\u7A7A\u95F4\u5173\u8054\u4E86 ${projectIds.length} \u4E2A\u9879\u76EE\uFF0C\u65E0\u6CD5\u786E\u5B9A\u540C\u6B65\u76EE\u6807\u3002
2726
+ \u8BF7\u786E\u4FDD\u8BE5\u5DE5\u4F5C\u7A7A\u95F4\u53EA\u5173\u8054\u4E00\u4E2A\u9879\u76EE\u540E\u518D\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002`
2727
+ };
2728
+ } catch (err) {
2729
+ const detail = err instanceof Error ? err.message : String(err);
2730
+ return {
2731
+ projectId: null,
2732
+ diagnostic: detail.replace(/^\[apm\]\s*/, "")
2733
+ };
2896
2734
  }
2897
- return { written, skipped, removed };
2898
- }
2899
-
2900
- // src/rules-sync.ts
2901
- var MANIFEST_FILE3 = ".rules-sync-manifest.json";
2902
- function ruleLocalFileName(ruleName) {
2903
- const trimmed = ruleName.trim();
2904
- if (!trimmed) return "rule.md";
2905
- const sanitized = trimmed.replace(/[/\\:*?"<>|]/g, "_");
2906
- if (extname2(sanitized).toLowerCase() === ".md") return sanitized;
2907
- return `${sanitized}.md`;
2908
2735
  }
2909
- function loadManifest2(rulesDir) {
2910
- const path19 = join9(rulesDir, MANIFEST_FILE3);
2911
- if (!existsSync7(toFsPath(path19))) {
2912
- return { version: 1, rules: {} };
2736
+ async function syncProjectDocumentsPull(workdirPath, apmDir, options) {
2737
+ const empty = {
2738
+ synced: false,
2739
+ projectId: null,
2740
+ downloaded: 0,
2741
+ deleted: 0
2742
+ };
2743
+ const cfg = await tryReadApmConfig();
2744
+ if (!cfg || !resolveApiKey(cfg)) {
2745
+ console.log(
2746
+ "[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8DF3\u8FC7\u9879\u76EE\u6587\u6863\u540C\u6B65\u3002\n[apm] \u8BF7\u5148\u6267\u884C apm login\u3002"
2747
+ );
2748
+ return empty;
2913
2749
  }
2914
- try {
2915
- const parsed = JSON.parse(
2916
- readFileSync7(toFsPath(path19), "utf8")
2750
+ const api = createApmApiClient(cfg);
2751
+ const { projectId, diagnostic } = await resolveProjectIdForSync(
2752
+ api,
2753
+ workdirPath,
2754
+ options
2755
+ );
2756
+ if (!projectId) {
2757
+ console.log(`[apm] \u672A\u80FD\u540C\u6B65\u9879\u76EE\u6587\u6863\u3002
2758
+ ${diagnostic ?? ""}`);
2759
+ return empty;
2760
+ }
2761
+ const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
2762
+ const docsDir = projectDocumentsDir(targetApmDir, projectId);
2763
+ await ensureDirExists(docsDir);
2764
+ const { manifest: remoteManifest } = await api.cli.getProjectDocumentManifest(
2765
+ { projectId }
2766
+ );
2767
+ if (!remoteManifest) {
2768
+ console.log(`[apm] \u9879\u76EE ${projectId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`);
2769
+ return { ...empty, projectId };
2770
+ }
2771
+ const localManifest = readLocalManifest(targetApmDir, projectId);
2772
+ const { download, deleteLocal } = diffManifestPaths(
2773
+ remoteManifest,
2774
+ localManifest
2775
+ );
2776
+ let downloaded = 0;
2777
+ if (download.length > 0) {
2778
+ const { list } = await api.cli.listProjectDocuments({
2779
+ projectId,
2780
+ paths: download.join(",")
2781
+ });
2782
+ for (const doc of list) {
2783
+ const absPath = toFsPath(
2784
+ projectDocumentLocalPath(targetApmDir, projectId, doc.path)
2785
+ );
2786
+ await ensureDirExists(dirname3(absPath));
2787
+ writeFileSync8(absPath, doc.content, "utf8");
2788
+ downloaded += 1;
2789
+ }
2790
+ }
2791
+ let deleted = 0;
2792
+ for (const path19 of deleteLocal) {
2793
+ const absPath = toFsPath(
2794
+ projectDocumentLocalPath(targetApmDir, projectId, path19)
2917
2795
  );
2918
- if (parsed?.version === 1 && parsed.rules && typeof parsed.rules === "object") {
2919
- return parsed;
2796
+ if (existsSync7(absPath)) {
2797
+ rmSync3(absPath, { force: true });
2798
+ deleted += 1;
2920
2799
  }
2921
- } catch {
2922
2800
  }
2923
- return { version: 1, rules: {} };
2924
- }
2925
- function saveManifest2(rulesDir, manifest) {
2926
- writeFileSync9(
2927
- toFsPath(join9(rulesDir, MANIFEST_FILE3)),
2928
- `${JSON.stringify(manifest, null, 2)}
2801
+ writeFileSync8(
2802
+ toFsPath(join9(docsDir, MANIFEST_FILE3)),
2803
+ `${JSON.stringify(remoteManifest, null, 2)}
2929
2804
  `,
2930
2805
  "utf8"
2931
2806
  );
2807
+ console.log(
2808
+ `[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: projectId=${projectId} \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted} \u2192 .apm/project/${projectId}/`
2809
+ );
2810
+ return {
2811
+ synced: true,
2812
+ projectId,
2813
+ downloaded,
2814
+ deleted
2815
+ };
2932
2816
  }
2933
- function isBaseRuleFileName(fileName) {
2934
- return listBaseRuleFileNames().includes(basename3(fileName));
2935
- }
2936
- function isRuleUpToDate(entry, rule, dest) {
2937
- if (!entry || !existsSync7(toFsPath(dest))) return false;
2938
- if (entry.fileName !== ruleLocalFileName(rule.name)) return false;
2939
- const updatedAt = rule.updatedAt ?? "";
2940
- if (entry.updatedAt !== updatedAt) return false;
2941
- const localContent = readFileSync7(toFsPath(dest), "utf8");
2942
- return localContent === (rule.content ?? "");
2943
- }
2944
- async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
2817
+ async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
2945
2818
  const api = createApmApiClient(cfg);
2946
- const baseline = await resolveBranchBaseline(api, sessionId, workdirPath);
2947
- const repositoryId = baseline.repositoryId;
2948
- const rulesDir = join9(apmRoot ?? workspaceApmDir(workdirPath), "rules");
2949
- await ensureDirExists(rulesDir);
2950
- if (!repositoryId) {
2951
- console.log(
2952
- `[apm] \u672A\u5339\u914D\u5230\u7ED1\u5B9A\u4ED3\u5E93\u7684\u5DE5\u4F5C\u7A7A\u95F4\uFF0C\u8DF3\u8FC7\u5E73\u53F0\u89C4\u5219\u540C\u6B65\uFF08\u8DEF\u5F84\uFF1A${workdirPath}\uFF09`
2953
- );
2954
- return { written: [], skipped: [], removed: [], repositoryId: null };
2819
+ const { projectId } = await resolveProjectIdForSync(
2820
+ api,
2821
+ workdirPath,
2822
+ options
2823
+ );
2824
+ if (!projectId) {
2825
+ return 0;
2955
2826
  }
2956
- const { list } = await api.cli.listRules({ repositoryId });
2957
- const manifest = loadManifest2(rulesDir);
2958
- const nextManifest = { version: 1, rules: {} };
2959
- const remoteIds = /* @__PURE__ */ new Set();
2960
- const written = [];
2961
- const skipped = [];
2962
- for (const rule of list) {
2963
- remoteIds.add(rule.id);
2964
- const fileName = ruleLocalFileName(rule.name);
2965
- const dest = join9(rulesDir, fileName);
2966
- const entry = manifest.rules[rule.id];
2967
- const updatedAt = rule.updatedAt ?? "";
2968
- if (isRuleUpToDate(entry, rule, dest)) {
2969
- nextManifest.rules[rule.id] = entry;
2970
- skipped.push(fileName);
2971
- console.log(`[apm] \u89C4\u5219\u65E0\u53D8\u5316\uFF0C\u5DF2\u8DF3\u8FC7: rules/${fileName}`);
2827
+ const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
2828
+ const localPaths = listLocalDocumentPaths(targetApmDir, projectId);
2829
+ if (localPaths.length === 0) {
2830
+ console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
2831
+ return 0;
2832
+ }
2833
+ const remoteManifest = (await api.cli.getProjectDocumentManifest({ projectId })).manifest ?? null;
2834
+ const remoteHashByPath = new Map(
2835
+ (remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
2836
+ );
2837
+ const remoteDescriptionByPath = new Map(
2838
+ (remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.description])
2839
+ );
2840
+ let synced = 0;
2841
+ for (const path19 of localPaths) {
2842
+ const absPath = toFsPath(
2843
+ projectDocumentLocalPath(targetApmDir, projectId, path19)
2844
+ );
2845
+ const content = readFileSync6(absPath, "utf8");
2846
+ const contentHash = hashLocalFileContent(content);
2847
+ if (remoteHashByPath.get(path19) === contentHash) {
2972
2848
  continue;
2973
2849
  }
2974
- writeFileSync9(toFsPath(dest), rule.content ?? "", "utf8");
2975
- nextManifest.rules[rule.id] = { fileName, updatedAt };
2976
- written.push(fileName);
2977
- console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u89C4\u5219: rules/${fileName}`);
2850
+ await api.cli.upsertProjectDocument({
2851
+ projectId,
2852
+ path: path19,
2853
+ content,
2854
+ description: remoteDescriptionByPath.get(path19) ?? void 0
2855
+ });
2856
+ synced += 1;
2857
+ console.log(`[apm] \u5DF2\u540C\u6B65\u9879\u76EE\u6587\u6863: ${path19}`);
2978
2858
  }
2979
- const removed = [];
2980
- for (const [ruleId, entry] of Object.entries(manifest.rules)) {
2981
- if (remoteIds.has(ruleId)) continue;
2982
- if (isBaseRuleFileName(entry.fileName)) continue;
2983
- const dest = join9(rulesDir, entry.fileName);
2984
- if (existsSync7(toFsPath(dest))) {
2985
- rmSync3(toFsPath(dest), { force: true });
2986
- }
2987
- removed.push(entry.fileName);
2988
- console.log(`[apm] \u5DF2\u79FB\u9664\u5DF2\u4E0B\u7EBF\u7684\u5E73\u53F0\u89C4\u5219: rules/${entry.fileName}`);
2859
+ if (synced === 0) {
2860
+ console.log("[apm] \u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
2989
2861
  }
2990
- saveManifest2(rulesDir, nextManifest);
2991
- return { written, skipped, removed, repositoryId };
2862
+ return synced;
2992
2863
  }
2993
2864
 
2994
2865
  // src/commands/pull.ts
@@ -3012,20 +2883,20 @@ async function runPull(sessionId, remoteWorkdir) {
3012
2883
  const dir = sessionDir(trimmedId, apmRoot);
3013
2884
  const docsDir = sessionDocsDir(trimmedId, apmRoot);
3014
2885
  await ensureDirExists(docsDir);
3015
- writeFileSync10(
2886
+ writeFileSync9(
3016
2887
  sessionRulePath(trimmedId, apmRoot),
3017
2888
  detail.description ?? "",
3018
2889
  "utf8"
3019
2890
  );
3020
- writeFileSync10(
2891
+ writeFileSync9(
3021
2892
  sessionTaskPath(trimmedId, apmRoot),
3022
2893
  detail.task.description ?? "",
3023
2894
  "utf8"
3024
2895
  );
3025
- writeFileSync10(sessionTodoPath(trimmedId, apmRoot), detail.todo ?? "", "utf8");
2896
+ writeFileSync9(sessionTodoPath(trimmedId, apmRoot), detail.todo ?? "", "utf8");
3026
2897
  for (const doc of documents) {
3027
2898
  const fileName = documentLocalFileName(doc.name);
3028
- writeFileSync10(join10(docsDir, fileName), doc.content ?? "", "utf8");
2899
+ writeFileSync9(join10(docsDir, fileName), doc.content ?? "", "utf8");
3029
2900
  }
3030
2901
  const sessionYaml = yamlStringify(
3031
2902
  {
@@ -3042,13 +2913,13 @@ async function runPull(sessionId, remoteWorkdir) {
3042
2913
  },
3043
2914
  { lineWidth: 0 }
3044
2915
  );
3045
- writeFileSync10(
2916
+ writeFileSync9(
3046
2917
  sessionYamlPath(trimmedId, apmRoot),
3047
2918
  sessionYaml.endsWith("\n") ? sessionYaml : `${sessionYaml}
3048
2919
  `,
3049
2920
  "utf8"
3050
2921
  );
3051
- writeFileSync10(
2922
+ writeFileSync9(
3052
2923
  sessionMessagesXmlPath(trimmedId, apmRoot),
3053
2924
  formatSessionMessagesXml(trimmedId, messages),
3054
2925
  "utf8"
@@ -3065,7 +2936,7 @@ async function runPull(sessionId, remoteWorkdir) {
3065
2936
  import { spawnSync } from "child_process";
3066
2937
 
3067
2938
  // src/version.ts
3068
- import { readFileSync as readFileSync8 } from "fs";
2939
+ import { readFileSync as readFileSync7 } from "fs";
3069
2940
  import { dirname as dirname4, join as join11 } from "path";
3070
2941
  import { fileURLToPath as fileURLToPath2 } from "url";
3071
2942
  var CLI_PACKAGE_NAME = "ai-project-manage-cli";
@@ -3073,7 +2944,7 @@ function readCliVersion() {
3073
2944
  try {
3074
2945
  const dir = dirname4(fileURLToPath2(import.meta.url));
3075
2946
  const pkgPath = join11(dir, "..", "package.json");
3076
- const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
2947
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
3077
2948
  return pkg.version ?? "0.0.0";
3078
2949
  } catch {
3079
2950
  return "0.0.0";
@@ -3328,7 +3199,7 @@ async function runSyncProjectDocuments(options) {
3328
3199
  }
3329
3200
 
3330
3201
  // src/commands/sync-document.ts
3331
- import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
3202
+ import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
3332
3203
  import { basename as basename4 } from "path";
3333
3204
 
3334
3205
  // src/assumptions/local-validate.ts
@@ -3499,7 +3370,7 @@ init_client();
3499
3370
 
3500
3371
  // src/commands/sync-session-documents.ts
3501
3372
  init_client();
3502
- import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync9 } from "fs";
3373
+ import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync8 } from "fs";
3503
3374
  import { join as join13 } from "path";
3504
3375
  function listLocalMarkdownFiles(docsDir) {
3505
3376
  if (!existsSync10(docsDir)) {
@@ -3518,7 +3389,7 @@ function remoteDocumentByLocalName(remoteDocuments, localFileName) {
3518
3389
  }
3519
3390
  async function upsertLocalDocumentFile(api, sessionId, docsDir, fileName) {
3520
3391
  const absPath = join13(docsDir, fileName);
3521
- const content = readFileSync9(absPath, "utf8");
3392
+ const content = readFileSync8(absPath, "utf8");
3522
3393
  const name = documentPlatformName(absPath);
3523
3394
  return api.cli.upsertDocument({
3524
3395
  sessionId,
@@ -3541,7 +3412,7 @@ async function syncSessionDocuments(cfg, sessionId, apmRoot, options) {
3541
3412
  let synced = 0;
3542
3413
  for (const fileName of localFiles) {
3543
3414
  const absPath = join13(docsDir, fileName);
3544
- const content = readFileSync9(absPath, "utf8");
3415
+ const content = readFileSync8(absPath, "utf8");
3545
3416
  const remote = remoteDocumentByLocalName(remoteDocuments, fileName);
3546
3417
  if (remote && remote.content === content) {
3547
3418
  continue;
@@ -3585,7 +3456,7 @@ async function runSyncDocument(sessionId, options) {
3585
3456
  const fileName = basename4(absPath);
3586
3457
  const assumptionSource = resolveAssumptionSourceFromFileName(fileName);
3587
3458
  if (assumptionSource) {
3588
- const content = readFileSync10(absPath, "utf8");
3459
+ const content = readFileSync9(absPath, "utf8");
3589
3460
  const validation = validateAssumptionsMarkdown(content, assumptionSource);
3590
3461
  if (!validation.ok) {
3591
3462
  printAssumptionValidationResult(validation);
@@ -3948,7 +3819,7 @@ init_client();
3948
3819
  import path11 from "node:path";
3949
3820
 
3950
3821
  // src/commands/deploy/internal/apm-config.ts
3951
- import { existsSync as existsSync13, readFileSync as readFileSync12 } from "node:fs";
3822
+ import { existsSync as existsSync13, readFileSync as readFileSync11 } from "node:fs";
3952
3823
  import { resolve as resolve5 } from "node:path";
3953
3824
 
3954
3825
  // src/commands/deploy/internal/config/config-validation.ts
@@ -4041,7 +3912,7 @@ function resolveFrontendDeployFromApmConfig(cfg) {
4041
3912
  }
4042
3913
 
4043
3914
  // src/commands/deploy/internal/config/maven-repo.ts
4044
- import { existsSync as existsSync12, readFileSync as readFileSync11 } from "node:fs";
3915
+ import { existsSync as existsSync12, readFileSync as readFileSync10 } from "node:fs";
4045
3916
  import { homedir as homedir2 } from "node:os";
4046
3917
  import { join as join14, resolve as resolve4 } from "node:path";
4047
3918
  var MAVEN_REPO_ENV_KEYS = [
@@ -4105,7 +3976,7 @@ function readMavenLocalRepoFromSettings() {
4105
3976
  return null;
4106
3977
  }
4107
3978
  try {
4108
- const xml = readFileSync11(settingsPath, "utf8");
3979
+ const xml = readFileSync10(settingsPath, "utf8");
4109
3980
  const match = xml.match(
4110
3981
  /<localRepository>\s*([^<]+?)\s*<\/localRepository>/
4111
3982
  );
@@ -4241,7 +4112,7 @@ function loadApmConfig(options) {
4241
4112
  process.exit(1);
4242
4113
  }
4243
4114
  try {
4244
- const raw = readFileSync12(p, "utf8");
4115
+ const raw = readFileSync11(p, "utf8");
4245
4116
  return JSON.parse(raw);
4246
4117
  } catch (e) {
4247
4118
  console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
@@ -4412,7 +4283,7 @@ import { dirname as dirname5, join as join17 } from "node:path";
4412
4283
  init_config();
4413
4284
  import { spawnSync as spawnSync2 } from "child_process";
4414
4285
  import { setTimeout as delay } from "node:timers/promises";
4415
- import { existsSync as existsSync15, mkdirSync as mkdirSync7, unlinkSync as unlinkSync2, writeFileSync as writeFileSync12 } from "fs";
4286
+ import { existsSync as existsSync15, mkdirSync as mkdirSync7, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "fs";
4416
4287
  import { join as join16 } from "path";
4417
4288
 
4418
4289
  // src/commands/connect-lock.ts
@@ -4420,9 +4291,9 @@ init_config();
4420
4291
  import {
4421
4292
  existsSync as existsSync14,
4422
4293
  mkdirSync as mkdirSync6,
4423
- readFileSync as readFileSync13,
4294
+ readFileSync as readFileSync12,
4424
4295
  unlinkSync,
4425
- writeFileSync as writeFileSync11
4296
+ writeFileSync as writeFileSync10
4426
4297
  } from "fs";
4427
4298
  import { join as join15 } from "path";
4428
4299
  var CONNECT_LOCK_PATH = join15(APM_CONFIG_DIR, "connect.lock");
@@ -4454,7 +4325,7 @@ function waitForPm2LockHandoff(timeoutMs = 5e3) {
4454
4325
  function readConnectLock() {
4455
4326
  if (!existsSync14(CONNECT_LOCK_PATH)) return null;
4456
4327
  try {
4457
- const raw = readFileSync13(CONNECT_LOCK_PATH, "utf8");
4328
+ const raw = readFileSync12(CONNECT_LOCK_PATH, "utf8");
4458
4329
  const parsed = JSON.parse(raw);
4459
4330
  if (typeof parsed.pid !== "number" || parsed.mode !== "foreground" && parsed.mode !== "pm2" || typeof parsed.startedAt !== "string") {
4460
4331
  return null;
@@ -4495,7 +4366,7 @@ function acquireConnectLock(mode) {
4495
4366
  mode,
4496
4367
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
4497
4368
  };
4498
- writeFileSync11(
4369
+ writeFileSync10(
4499
4370
  CONNECT_LOCK_PATH,
4500
4371
  JSON.stringify(lock, null, 2) + "\n",
4501
4372
  "utf8"
@@ -5009,7 +4880,7 @@ async function resolveBaseUrl(server) {
5009
4880
  }
5010
4881
  function writeConnectLaunchFiles(options) {
5011
4882
  mkdirSync7(APM_CONFIG_DIR, { recursive: true });
5012
- writeFileSync12(PM2_CONNECT_ENTRY_PATH, CONNECT_ENTRY_SCRIPT, "utf8");
4883
+ writeFileSync11(PM2_CONNECT_ENTRY_PATH, CONNECT_ENTRY_SCRIPT, "utf8");
5013
4884
  const env = {
5014
4885
  ...collectPm2LaunchEnv()
5015
4886
  };
@@ -5024,7 +4895,7 @@ function writeConnectLaunchFiles(options) {
5024
4895
  cwd: options.cwd,
5025
4896
  env
5026
4897
  };
5027
- writeFileSync12(
4898
+ writeFileSync11(
5028
4899
  PM2_CONNECT_LAUNCH_PATH,
5029
4900
  JSON.stringify(launch, null, 2) + "\n",
5030
4901
  "utf8"
@@ -5036,7 +4907,7 @@ function writeEcosystemFile(options) {
5036
4907
  entryScript: PM2_CONNECT_ENTRY_PATH,
5037
4908
  baseUrl: options.baseUrl
5038
4909
  });
5039
- writeFileSync12(
4910
+ writeFileSync11(
5040
4911
  PM2_ECOSYSTEM_PATH,
5041
4912
  formatConnectPm2EcosystemFile(ecosystem),
5042
4913
  "utf8"
@@ -5457,7 +5328,7 @@ function logWisdomFrontendDeployContext(input) {
5457
5328
  }
5458
5329
 
5459
5330
  // src/commands/deploy/internal/wisdom-deploy.ts
5460
- import { existsSync as existsSync21, readFileSync as readFileSync16, statSync as statSync10 } from "node:fs";
5331
+ import { existsSync as existsSync21, readFileSync as readFileSync15, statSync as statSync10 } from "node:fs";
5461
5332
  import path10 from "node:path";
5462
5333
 
5463
5334
  // src/commands/deploy/deploy-shell-run.ts
@@ -5557,9 +5428,9 @@ import path9 from "node:path";
5557
5428
  import {
5558
5429
  mkdirSync as mkdirSync9,
5559
5430
  readdirSync as readdirSync6,
5560
- readFileSync as readFileSync15,
5431
+ readFileSync as readFileSync14,
5561
5432
  statSync as statSync8,
5562
- writeFileSync as writeFileSync14
5433
+ writeFileSync as writeFileSync13
5563
5434
  } from "node:fs";
5564
5435
  import path6 from "node:path";
5565
5436
  import JSZip2 from "jszip";
@@ -5568,9 +5439,9 @@ import JSZip2 from "jszip";
5568
5439
  import {
5569
5440
  existsSync as existsSync18,
5570
5441
  mkdirSync as mkdirSync8,
5571
- readFileSync as readFileSync14,
5442
+ readFileSync as readFileSync13,
5572
5443
  statSync as statSync7,
5573
- writeFileSync as writeFileSync13
5444
+ writeFileSync as writeFileSync12
5574
5445
  } from "node:fs";
5575
5446
  import path2 from "node:path";
5576
5447
  function deployCacheDir() {
@@ -5591,12 +5462,12 @@ function loadManifest3() {
5591
5462
  if (!existsSync18(manifestPath3)) {
5592
5463
  return {};
5593
5464
  }
5594
- return JSON.parse(readFileSync14(manifestPath3, "utf8"));
5465
+ return JSON.parse(readFileSync13(manifestPath3, "utf8"));
5595
5466
  }
5596
5467
  function saveManifest3(manifest) {
5597
5468
  const dir = deployCacheDir();
5598
5469
  mkdirSync8(dir, { recursive: true });
5599
- writeFileSync13(manifestFilePath(), JSON.stringify(manifest, null, 2), "utf8");
5470
+ writeFileSync12(manifestFilePath(), JSON.stringify(manifest, null, 2), "utf8");
5600
5471
  }
5601
5472
  function updateManifestEntries(manifest, entries, projectRoot) {
5602
5473
  for (const entry of entries) {
@@ -6010,7 +5881,7 @@ async function createUpdatePackage(entries, packageName) {
6010
5881
  log(`\u521B\u5EFA\u66F4\u65B0\u5305: ${path6.basename(zipPath)}\uFF08${entries.length} \u4E2A\u6587\u4EF6\uFF09`);
6011
5882
  const zip = new JSZip2();
6012
5883
  for (const entry of entries) {
6013
- const content = readFileSync15(entry.path);
5884
+ const content = readFileSync14(entry.path);
6014
5885
  zip.file(entry.arcname, content);
6015
5886
  log(` \u6253\u5305: ${entry.arcname} (${entry.reason})`);
6016
5887
  }
@@ -6019,7 +5890,7 @@ async function createUpdatePackage(entries, packageName) {
6019
5890
  compression: "DEFLATE",
6020
5891
  compressionOptions: { level: 6 }
6021
5892
  });
6022
- writeFileSync14(zipPath, buffer);
5893
+ writeFileSync13(zipPath, buffer);
6023
5894
  return zipPath;
6024
5895
  }
6025
5896
  function listAllLibFilesForArchive(libDir) {
@@ -6593,7 +6464,7 @@ function readPackageScripts(cwd) {
6593
6464
  return {};
6594
6465
  }
6595
6466
  try {
6596
- const raw = readFileSync16(pkgPath, "utf8");
6467
+ const raw = readFileSync15(pkgPath, "utf8");
6597
6468
  const parsed = JSON.parse(raw);
6598
6469
  return parsed.scripts ?? {};
6599
6470
  } catch {
@@ -7261,7 +7132,7 @@ import { resolve as resolve7 } from "node:path";
7261
7132
  import { getDefaultSdkStateRoot } from "@cursor/sdk";
7262
7133
 
7263
7134
  // src/commands/connect/webide-agent-registry.ts
7264
- import { existsSync as existsSync23, mkdirSync as mkdirSync10, readFileSync as readFileSync17, writeFileSync as writeFileSync15 } from "node:fs";
7135
+ import { existsSync as existsSync23, mkdirSync as mkdirSync10, readFileSync as readFileSync16, writeFileSync as writeFileSync14 } from "node:fs";
7265
7136
  import { dirname as dirname7, resolve as resolve6 } from "node:path";
7266
7137
  function registryPath(workdir, taskId) {
7267
7138
  return resolve6(workdir, ".apm", "webide", taskId, "cursor-agent.json");
@@ -7271,7 +7142,7 @@ function readRegistry(path19) {
7271
7142
  return {};
7272
7143
  }
7273
7144
  try {
7274
- const parsed = JSON.parse(readFileSync17(path19, "utf8"));
7145
+ const parsed = JSON.parse(readFileSync16(path19, "utf8"));
7275
7146
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
7276
7147
  const raw = parsed;
7277
7148
  const state = {};
@@ -7756,7 +7627,7 @@ ${JSON.stringify(event, null, 2)}
7756
7627
  }
7757
7628
 
7758
7629
  // src/commands/connect/agent-session-registry.ts
7759
- import { existsSync as existsSync25, mkdirSync as mkdirSync11, readFileSync as readFileSync18, writeFileSync as writeFileSync16 } from "node:fs";
7630
+ import { existsSync as existsSync25, mkdirSync as mkdirSync11, readFileSync as readFileSync17, writeFileSync as writeFileSync15 } from "node:fs";
7760
7631
  import { dirname as dirname8, resolve as resolve8 } from "node:path";
7761
7632
  function registryPath2(workdir, sessionId) {
7762
7633
  return resolve8(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
@@ -7766,7 +7637,7 @@ function readRegistry2(path19) {
7766
7637
  return {};
7767
7638
  }
7768
7639
  try {
7769
- const parsed = JSON.parse(readFileSync18(path19, "utf8"));
7640
+ const parsed = JSON.parse(readFileSync17(path19, "utf8"));
7770
7641
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
7771
7642
  const result = {};
7772
7643
  for (const [key, value] of Object.entries(
@@ -7784,7 +7655,7 @@ function readRegistry2(path19) {
7784
7655
  }
7785
7656
  function writeRegistry(path19, registry) {
7786
7657
  mkdirSync11(dirname8(path19), { recursive: true });
7787
- writeFileSync16(path19, `${JSON.stringify(registry, null, 2)}
7658
+ writeFileSync15(path19, `${JSON.stringify(registry, null, 2)}
7788
7659
  `, "utf8");
7789
7660
  }
7790
7661
  function loadSessionAgentId(workdir, sessionId, user) {
@@ -8682,7 +8553,7 @@ async function ensureMessageHasReply(cfg, sessionId, messageId, fallback) {
8682
8553
  }
8683
8554
 
8684
8555
  // src/commands/connect/cli-version-sync.ts
8685
- import { existsSync as existsSync26, readFileSync as readFileSync19, writeFileSync as writeFileSync17 } from "fs";
8556
+ import { existsSync as existsSync26, readFileSync as readFileSync18, writeFileSync as writeFileSync16 } from "fs";
8686
8557
  import { join as join19 } from "path";
8687
8558
  var CLI_VERSION_FILE = ".cli-version.json";
8688
8559
  function manifestPath2(apmDir) {
@@ -8695,7 +8566,7 @@ function loadManifest4(apmDir) {
8695
8566
  }
8696
8567
  try {
8697
8568
  const parsed = JSON.parse(
8698
- readFileSync19(path19, "utf8")
8569
+ readFileSync18(path19, "utf8")
8699
8570
  );
8700
8571
  if (parsed?.version === 1 && typeof parsed.cliVersion === "string" && parsed.cliVersion.trim()) {
8701
8572
  return parsed;
@@ -8706,7 +8577,7 @@ function loadManifest4(apmDir) {
8706
8577
  }
8707
8578
  function saveManifest4(apmDir, cliVersion) {
8708
8579
  const manifest = { version: 1, cliVersion };
8709
- writeFileSync17(
8580
+ writeFileSync16(
8710
8581
  toFsPath(manifestPath2(apmDir)),
8711
8582
  `${JSON.stringify(manifest, null, 2)}
8712
8583
  `,
@@ -8983,13 +8854,7 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
8983
8854
  };
8984
8855
  try {
8985
8856
  if (signal.aborted) return;
8986
- const { didInit } = await runStep(
8987
- "workspace-init",
8988
- () => ensureWorkspaceInitialized(workdir)
8989
- );
8990
- if (!didInit) {
8991
- assertApmGitignoredInRepo(workdir);
8992
- }
8857
+ await runStep("workspace-init", () => ensureWorkspaceInitialized(workdir));
8993
8858
  if (shouldRunBranch(msg.sessionId, workdir)) {
8994
8859
  if (signal.aborted) return;
8995
8860
  await runStep("branch", () => runBranch(msg.sessionId, { cwd: workdir }));
@@ -9862,7 +9727,7 @@ import path15 from "node:path";
9862
9727
  import Docker from "dockerode";
9863
9728
 
9864
9729
  // src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
9865
- import { existsSync as existsSync27, readFileSync as readFileSync20 } from "node:fs";
9730
+ import { existsSync as existsSync27, readFileSync as readFileSync19 } from "node:fs";
9866
9731
  import path12 from "node:path";
9867
9732
  function asOptionalTlsBuffer(value) {
9868
9733
  if (typeof value !== "string") {
@@ -9875,7 +9740,7 @@ function asOptionalTlsBuffer(value) {
9875
9740
  return void 0;
9876
9741
  }
9877
9742
  if (existsSync27(normalized)) {
9878
- return readFileSync20(normalized);
9743
+ return readFileSync19(normalized);
9879
9744
  }
9880
9745
  const looksLikePath = /[\\/]/.test(normalized) || normalized.endsWith(".pem");
9881
9746
  if (looksLikePath) {
@@ -10085,7 +9950,7 @@ var DockerodeClient = class {
10085
9950
  var createDockerodeClient = (config) => new DockerodeClient(config);
10086
9951
 
10087
9952
  // src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
10088
- import { existsSync as existsSync28, readFileSync as readFileSync21, statSync as statSync11 } from "node:fs";
9953
+ import { existsSync as existsSync28, readFileSync as readFileSync20, statSync as statSync11 } from "node:fs";
10089
9954
  import path13 from "node:path";
10090
9955
  function stripSurroundingQuotes(value) {
10091
9956
  const t = value.trim();
@@ -10105,7 +9970,7 @@ function loadEnvFromFile(envFilePath) {
10105
9970
  if (!existsSync28(targetPath) || !statSync11(targetPath).isFile()) {
10106
9971
  return {};
10107
9972
  }
10108
- const raw = readFileSync21(targetPath, "utf-8");
9973
+ const raw = readFileSync20(targetPath, "utf-8");
10109
9974
  const result = {};
10110
9975
  for (const line of raw.split(/\r?\n/)) {
10111
9976
  const normalized = line.trim();
@@ -10686,9 +10551,7 @@ function buildProgram() {
10686
10551
  ).option("--api-key <key>", "\u5BA2\u6237\u673A API Key\uFF08cm_ \u5F00\u5934\uFF09").option("--server <url>", "API \u6839\u5730\u5740\uFF0C\u4F8B\u5982 http://127.0.0.1:3000").action(async (opts) => {
10687
10552
  await runLogin(opts);
10688
10553
  });
10689
- program.command("init").description(
10690
- "\u5728\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u521D\u59CB\u5316 .apm \u6A21\u677F\uFF08\u82E5 .apm \u5DF2\u5B58\u5728\u4E14\u975E\u7A7A\u5219\u62A5\u9519\uFF09\uFF1B\u5DF2\u767B\u5F55\u4E14\u5E73\u53F0\u6709\u5339\u914D\u90E8\u7F72\u914D\u7F6E\u65F6\u4F1A\u540C\u6B65 apm.config.json"
10691
- ).option("--name <name>", "\u5DE5\u4F5C\u76EE\u5F55\u540D\u79F0").action(async (opts) => {
10554
+ program.command("init").description("\u5728\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u521B\u5EFA .apm \u5E76\u521D\u59CB\u5316\u6A21\u677F").option("--name <name>", "\u5DE5\u4F5C\u76EE\u5F55\u540D\u79F0").action(async (opts) => {
10692
10555
  await runInit(opts.name);
10693
10556
  });
10694
10557
  program.command("update").description(