ai-project-manage-cli 6.0.41 → 6.0.43

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
@@ -127,6 +127,13 @@ function resolveWorkdirPath(cwd = process.cwd()) {
127
127
  return normalizeWorkdirPath(absolute);
128
128
  }
129
129
  }
130
+ function requireRemoteWorkdir(workdir) {
131
+ const trimmed = typeof workdir === "string" ? workdir.trim() : "";
132
+ if (!trimmed) {
133
+ throw new Error("[apm] \u8FDC\u7A0B\u6D88\u606F\u7F3A\u5C11\u5DE5\u4F5C\u76EE\u5F55 workdir");
134
+ }
135
+ return resolveWorkdirPath(trimmed);
136
+ }
130
137
 
131
138
  // src/command-utils.ts
132
139
  var __dirname = dirname(fileURLToPath(import.meta.url));
@@ -611,7 +618,7 @@ async function runBranch(sessionId, options = {}) {
611
618
 
612
619
  // src/commands/pull.ts
613
620
  import { writeFileSync as writeFileSync6 } from "fs";
614
- import { dirname as dirname2, join as join7 } from "path";
621
+ import { join as join7 } from "path";
615
622
  import { stringify as yamlStringify } from "yaml";
616
623
 
617
624
  // src/session-messages-xml.ts
@@ -908,13 +915,7 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
908
915
  }
909
916
 
910
917
  // src/commands/pull.ts
911
- function resolvePullWorkdir(apmRoot) {
912
- if (apmRoot) {
913
- return resolveWorkdirPath(dirname2(apmRoot));
914
- }
915
- return resolveWorkdirPath();
916
- }
917
- async function runPull(sessionId, apmRoot) {
918
+ async function runPull(sessionId, remoteWorkdir) {
918
919
  const trimmedId = sessionId.trim();
919
920
  if (!trimmedId) {
920
921
  console.error("[apm] sessionId \u4E0D\u80FD\u4E3A\u7A7A");
@@ -922,6 +923,8 @@ async function runPull(sessionId, apmRoot) {
922
923
  }
923
924
  const cfg = await ensureLoggedConfig();
924
925
  const api = createApmApiClient(cfg);
926
+ const workdir = remoteWorkdir === void 0 ? resolveWorkdirPath() : requireRemoteWorkdir(remoteWorkdir);
927
+ const apmRoot = workspaceApmDir(workdir);
925
928
  const [detail, members, documents, attachments, messages] = await Promise.all(
926
929
  [
927
930
  api.cli.sessionDetail({ sessionId: trimmedId }),
@@ -976,8 +979,7 @@ async function runPull(sessionId, apmRoot) {
976
979
  "utf8"
977
980
  );
978
981
  await syncSessionAttachments(cfg, trimmedId, attachments, apmRoot);
979
- const workdirPath = resolvePullWorkdir(apmRoot);
980
- await syncPlatformRules(cfg, trimmedId, workdirPath, apmRoot);
982
+ await syncPlatformRules(cfg, trimmedId, workdir, apmRoot);
981
983
  console.log(`[apm] \u5DF2\u540C\u6B65\u4F1A\u8BDD\u5DE5\u4F5C\u533A: ${dir}`);
982
984
  return dir;
983
985
  }
@@ -987,12 +989,12 @@ import { spawnSync } from "child_process";
987
989
 
988
990
  // src/version.ts
989
991
  import { readFileSync as readFileSync5 } from "fs";
990
- import { dirname as dirname3, join as join8 } from "path";
992
+ import { dirname as dirname2, join as join8 } from "path";
991
993
  import { fileURLToPath as fileURLToPath2 } from "url";
992
994
  var CLI_PACKAGE_NAME = "ai-project-manage-cli";
993
995
  function readCliVersion() {
994
996
  try {
995
- const dir = dirname3(fileURLToPath2(import.meta.url));
997
+ const dir = dirname2(fileURLToPath2(import.meta.url));
996
998
  const pkgPath = join8(dir, "..", "package.json");
997
999
  const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
998
1000
  return pkg.version ?? "0.0.0";
@@ -1211,6 +1213,17 @@ async function runSyncDocument(sessionId, options) {
1211
1213
  }
1212
1214
 
1213
1215
  // src/commands/append-message.ts
1216
+ async function appendMessageContent(cfg, messageId, content) {
1217
+ const trimmedId = messageId.trim();
1218
+ if (!trimmedId) {
1219
+ throw new Error("messageId \u4E0D\u80FD\u4E3A\u7A7A");
1220
+ }
1221
+ if (!content) {
1222
+ throw new Error("content \u4E0D\u80FD\u4E3A\u7A7A");
1223
+ }
1224
+ const api = createApmApiClient(cfg);
1225
+ await api.cli.appendMessageContent({ id: trimmedId, content });
1226
+ }
1214
1227
  async function runAppendMessage(options) {
1215
1228
  const messageId = options.id?.trim();
1216
1229
  if (!messageId) {
@@ -1223,8 +1236,7 @@ async function runAppendMessage(options) {
1223
1236
  process.exit(1);
1224
1237
  }
1225
1238
  const cfg = await ensureLoggedConfig();
1226
- const api = createApmApiClient(cfg);
1227
- await api.cli.appendMessageContent({ id: messageId, content });
1239
+ await appendMessageContent(cfg, messageId, content);
1228
1240
  console.log(`[apm] \u5DF2\u8FFD\u52A0\u6D88\u606F\u5185\u5BB9: ${messageId}`);
1229
1241
  }
1230
1242
 
@@ -1415,9 +1427,11 @@ ${stack}`
1415
1427
  }
1416
1428
 
1417
1429
  // src/commands/connect/cursor-agent.ts
1418
- import { Agent, CursorAgentError } from "@cursor/sdk";
1430
+ import {
1431
+ Agent,
1432
+ CursorAgentError
1433
+ } from "@cursor/sdk";
1419
1434
  import { setMaxListeners as setMaxListeners2 } from "node:events";
1420
- import { resolve as resolve4 } from "path";
1421
1435
 
1422
1436
  // src/session-utils.ts
1423
1437
  var EventSession = class {
@@ -1579,7 +1593,7 @@ ${JSON.stringify(event, null, 2)}
1579
1593
 
1580
1594
  // src/commands/connect/agent-session-registry.ts
1581
1595
  import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "node:fs";
1582
- import { dirname as dirname4, resolve as resolve3 } from "node:path";
1596
+ import { dirname as dirname3, resolve as resolve3 } from "node:path";
1583
1597
  function registryPath(workdir, sessionId) {
1584
1598
  return resolve3(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
1585
1599
  }
@@ -1605,7 +1619,7 @@ function readRegistry(path10) {
1605
1619
  return {};
1606
1620
  }
1607
1621
  function writeRegistry(path10, registry) {
1608
- mkdirSync5(dirname4(path10), { recursive: true });
1622
+ mkdirSync5(dirname3(path10), { recursive: true });
1609
1623
  writeFileSync7(path10, `${JSON.stringify(registry, null, 2)}
1610
1624
  `, "utf8");
1611
1625
  }
@@ -1703,6 +1717,45 @@ async function syncCursorMessageLog(cfg, ctx, events) {
1703
1717
  });
1704
1718
  }
1705
1719
 
1720
+ // src/commands/connect/append-message-tool.ts
1721
+ function createAppendMessageCustomTools(cfg, messageId) {
1722
+ return {
1723
+ append_message: {
1724
+ description: "\u5411\u5F53\u524D\u4F1A\u8BDD\u6D88\u606F\u8FFD\u52A0\u56DE\u590D\u5185\u5BB9\u3002\u53EF\u591A\u6B21\u8C03\u7528\u8865\u5145\u8FDB\u5C55\uFF1B\u88AB @ \u65F6\u6536\u5230\u540E\u5E94\u5148\u7B80\u77ED\u786E\u8BA4\u518D\u6267\u884C\u4EFB\u52A1\u3002",
1725
+ inputSchema: {
1726
+ type: "object",
1727
+ properties: {
1728
+ content: {
1729
+ type: "string",
1730
+ description: "\u8981\u53D1\u9001\u5230\u7FA4\u91CC\u7684\u56DE\u590D\u5185\u5BB9"
1731
+ }
1732
+ },
1733
+ required: ["content"]
1734
+ },
1735
+ execute: async (args) => {
1736
+ const content = typeof args.content === "string" ? args.content.trim() : "";
1737
+ if (!content) {
1738
+ return {
1739
+ content: [{ type: "text", text: "content \u4E0D\u80FD\u4E3A\u7A7A" }],
1740
+ isError: true
1741
+ };
1742
+ }
1743
+ try {
1744
+ await appendMessageContent(cfg, messageId, content);
1745
+ console.log(`[apm] append_message \u5DF2\u8FFD\u52A0: messageId=${messageId}`);
1746
+ return "\u5DF2\u8FFD\u52A0\u6D88\u606F\u5185\u5BB9";
1747
+ } catch (err) {
1748
+ const detail = err instanceof Error ? err.message : String(err);
1749
+ return {
1750
+ content: [{ type: "text", text: `\u8FFD\u52A0\u6D88\u606F\u5931\u8D25: ${detail}` }],
1751
+ isError: true
1752
+ };
1753
+ }
1754
+ }
1755
+ }
1756
+ };
1757
+ }
1758
+
1706
1759
  // src/commands/connect/cursor-agent.ts
1707
1760
  setMaxListeners2(50);
1708
1761
  installAbortSignalDebug();
@@ -1729,8 +1782,7 @@ async function obtainAgent(ctx) {
1729
1782
  apiKey: ctx.apiKey,
1730
1783
  model: { id: ctx.model || "default" },
1731
1784
  local: {
1732
- cwd: ctx.cwd,
1733
- settingSources: []
1785
+ cwd: ctx.cwd
1734
1786
  }
1735
1787
  };
1736
1788
  const savedAgentId = ctx.user ? loadSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user) : void 0;
@@ -1765,16 +1817,16 @@ async function runCursorAgent(cfg, ctx, options) {
1765
1817
  if (!apiKey) {
1766
1818
  throw new Error("\u7F3A\u5C11 apiKey\uFF0C\u65E0\u6CD5\u8C03\u7528 Cursor SDK");
1767
1819
  }
1768
- const cwd = resolve4(ctx.workdir);
1820
+ const workdir = resolveWorkdirPath(ctx.workdir);
1769
1821
  const prompt = ctx.prompt;
1770
1822
  console.log(
1771
- `[apm] Cursor Agent \u5F00\u59CB messageId=${ctx.messageId} sessionId=${ctx.sessionId} cwd=${cwd}`
1823
+ `[apm] Cursor Agent \u5F00\u59CB messageId=${ctx.messageId} sessionId=${ctx.sessionId} cwd=${workdir}`
1772
1824
  );
1773
1825
  const { agent, resumed } = await obtainAgent({
1774
1826
  apiKey,
1775
1827
  model: ctx.model,
1776
- cwd,
1777
- workdir: ctx.workdir,
1828
+ cwd: workdir,
1829
+ workdir,
1778
1830
  sessionId: ctx.sessionId,
1779
1831
  user: ctx.user
1780
1832
  });
@@ -1798,7 +1850,11 @@ async function runCursorAgent(cfg, ctx, options) {
1798
1850
  signal?.addEventListener("abort", abortRun, { once: true });
1799
1851
  logAbortSignalStats(signal, "runCursorAgent:after-addListener");
1800
1852
  try {
1801
- const run = await agent.send(prompt);
1853
+ const run = await agent.send(prompt, {
1854
+ local: {
1855
+ customTools: createAppendMessageCustomTools(cfg, ctx.messageId)
1856
+ }
1857
+ });
1802
1858
  activeRun = run;
1803
1859
  logAbortSignalStats(signal, "runCursorAgent:after-send");
1804
1860
  console.log(`[apm] Cursor run id=${run.id} agentId=${agent.agentId}`);
@@ -1829,7 +1885,7 @@ async function runCursorAgent(cfg, ctx, options) {
1829
1885
  });
1830
1886
  console.error(`[apm] ${failureMessage}`);
1831
1887
  if (resumed) {
1832
- clearSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user);
1888
+ clearSessionAgentId(workdir, ctx.sessionId, ctx.user);
1833
1889
  }
1834
1890
  throw new Error(failureMessage);
1835
1891
  }
@@ -1840,7 +1896,7 @@ async function runCursorAgent(cfg, ctx, options) {
1840
1896
  } catch (err) {
1841
1897
  if (err instanceof CursorAgentError) {
1842
1898
  if (resumed) {
1843
- clearSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user);
1899
+ clearSessionAgentId(workdir, ctx.sessionId, ctx.user);
1844
1900
  }
1845
1901
  throw new Error(
1846
1902
  `Cursor \u542F\u52A8\u5931\u8D25: ${err.message}${err.isRetryable ? "\uFF08\u53EF\u91CD\u8BD5\uFF09" : ""}`
@@ -1857,23 +1913,27 @@ async function runCursorAgent(cfg, ctx, options) {
1857
1913
 
1858
1914
  // src/commands/connect/pre-step-cache.ts
1859
1915
  var PULL_TTL_MS = 3e4;
1860
- var lastBranchSessionId = null;
1861
- var lastPullAtBySession = /* @__PURE__ */ new Map();
1862
- function shouldRunBranch(sessionId) {
1863
- return lastBranchSessionId !== sessionId;
1916
+ function sessionWorkdirKey(sessionId, workdir) {
1917
+ return `${sessionId}\0${workdir}`;
1864
1918
  }
1865
- function markBranchDone(sessionId) {
1866
- lastBranchSessionId = sessionId;
1919
+ var lastBranchKey = null;
1920
+ var lastPullAtByKey = /* @__PURE__ */ new Map();
1921
+ function shouldRunBranch(sessionId, workdir) {
1922
+ return lastBranchKey !== sessionWorkdirKey(sessionId, workdir);
1867
1923
  }
1868
- function shouldRunPull(sessionId) {
1869
- const last = lastPullAtBySession.get(sessionId);
1924
+ function markBranchDone(sessionId, workdir) {
1925
+ lastBranchKey = sessionWorkdirKey(sessionId, workdir);
1926
+ }
1927
+ function shouldRunPull(sessionId, workdir) {
1928
+ const key = sessionWorkdirKey(sessionId, workdir);
1929
+ const last = lastPullAtByKey.get(key);
1870
1930
  if (last == null) {
1871
1931
  return true;
1872
1932
  }
1873
1933
  return Date.now() - last >= PULL_TTL_MS;
1874
1934
  }
1875
- function markPullDone(sessionId) {
1876
- lastPullAtBySession.set(sessionId, Date.now());
1935
+ function markPullDone(sessionId, workdir) {
1936
+ lastPullAtByKey.set(sessionWorkdirKey(sessionId, workdir), Date.now());
1877
1937
  }
1878
1938
 
1879
1939
  // src/commands/connect/run-slot-pool.ts
@@ -1886,10 +1946,10 @@ function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT) {
1886
1946
  active += 1;
1887
1947
  return Promise.resolve();
1888
1948
  }
1889
- return new Promise((resolve6) => {
1949
+ return new Promise((resolve5) => {
1890
1950
  waiters.push(() => {
1891
1951
  active += 1;
1892
- resolve6();
1952
+ resolve5();
1893
1953
  });
1894
1954
  });
1895
1955
  };
@@ -1923,6 +1983,8 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
1923
1983
  if (isUserCancelled(ctx)) return;
1924
1984
  if (signal.aborted) return;
1925
1985
  const messageId = msg.messageId;
1986
+ const workdir = requireRemoteWorkdir(msg.workdir);
1987
+ const apmRoot = workspaceApmDir(workdir);
1926
1988
  const runStep = async (step, fn) => {
1927
1989
  const startedAt = Date.now();
1928
1990
  try {
@@ -1940,24 +2002,18 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
1940
2002
  "status-typing",
1941
2003
  () => updateMessageStatus(cfg, messageId, "TYPING")
1942
2004
  );
1943
- if (shouldRunBranch(msg.sessionId)) {
2005
+ if (shouldRunBranch(msg.sessionId, workdir)) {
1944
2006
  if (signal.aborted) return;
1945
- await runStep(
1946
- "branch",
1947
- () => runBranch(msg.sessionId, { cwd: msg.workdir })
1948
- );
1949
- markBranchDone(msg.sessionId);
2007
+ await runStep("branch", () => runBranch(msg.sessionId, { cwd: workdir }));
2008
+ markBranchDone(msg.sessionId, workdir);
1950
2009
  } else {
1951
2010
  console.log(`[apm] step=branch skipped sessionId=${msg.sessionId}`);
1952
2011
  }
1953
2012
  let pullRan = false;
1954
- if (shouldRunPull(msg.sessionId)) {
2013
+ if (shouldRunPull(msg.sessionId, workdir)) {
1955
2014
  if (signal.aborted) return;
1956
- await runStep(
1957
- "pull",
1958
- () => runPull(msg.sessionId, workspaceApmDir(msg.workdir))
1959
- );
1960
- markPullDone(msg.sessionId);
2015
+ await runStep("pull", () => runPull(msg.sessionId, workdir));
2016
+ markPullDone(msg.sessionId, workdir);
1961
2017
  pullRan = true;
1962
2018
  } else {
1963
2019
  console.log(`[apm] step=pull skipped sessionId=${msg.sessionId}`);
@@ -1966,7 +2022,7 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
1966
2022
  if (signal.aborted) return;
1967
2023
  await runStep(
1968
2024
  "commit-pull",
1969
- () => commitWorkingTreeIfDirty(msg.workdir, "fix: apm pull")
2025
+ () => commitWorkingTreeIfDirty(workdir, "fix: apm pull")
1970
2026
  );
1971
2027
  } else {
1972
2028
  console.log(`[apm] step=commit-pull skipped sessionId=${msg.sessionId}`);
@@ -1982,7 +2038,7 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
1982
2038
  prompt: msg.content,
1983
2039
  model: msg.model,
1984
2040
  apiKey: msg.apiKey,
1985
- workdir: msg.workdir,
2041
+ workdir,
1986
2042
  user: msg.user
1987
2043
  },
1988
2044
  { signal }
@@ -1990,14 +2046,11 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
1990
2046
  );
1991
2047
  await runStep(
1992
2048
  "sync-documents",
1993
- () => syncSessionDocuments(cfg, msg.sessionId, workspaceApmDir(msg.workdir))
2049
+ () => syncSessionDocuments(cfg, msg.sessionId, apmRoot)
1994
2050
  );
1995
2051
  await runStep(
1996
- "commit-documents",
1997
- () => commitWorkingTreeIfDirty(
1998
- msg.workdir,
1999
- "chore(apm): sync session documents"
2000
- )
2052
+ "commit-files",
2053
+ () => commitWorkingTreeIfDirty(workdir, "chore(apm): commit working tree")
2001
2054
  );
2002
2055
  await runStep(
2003
2056
  "status-success",
@@ -2057,7 +2110,7 @@ async function runConnect(options) {
2057
2110
  }
2058
2111
  const url = buildAgentWsUrl(cfg.baseUrl, resolveApiKey(cfg));
2059
2112
  console.log(`[apm] \u8FDE\u63A5 ${cfg.baseUrl} \u2026`);
2060
- await new Promise((resolve6, reject) => {
2113
+ await new Promise((resolve5, reject) => {
2061
2114
  const ws = new WebSocket(url);
2062
2115
  let stopHeartbeat;
2063
2116
  let shuttingDown = false;
@@ -2086,7 +2139,7 @@ async function runConnect(options) {
2086
2139
  ]);
2087
2140
  } catch {
2088
2141
  }
2089
- resolve6();
2142
+ resolve5();
2090
2143
  process.exit(code);
2091
2144
  };
2092
2145
  ws.on("open", () => {
@@ -2174,11 +2227,11 @@ import path5 from "node:path";
2174
2227
 
2175
2228
  // src/commands/deploy/internal/apm-config.ts
2176
2229
  import { existsSync as existsSync9, readFileSync as readFileSync8 } from "node:fs";
2177
- import { resolve as resolve5 } from "node:path";
2230
+ import { resolve as resolve4 } from "node:path";
2178
2231
  function loadApmConfig(options) {
2179
- const p = resolve5(
2232
+ const p = resolve4(
2180
2233
  process.cwd(),
2181
- options?.configPath ?? resolve5(workspaceApmDir(), "apm.config.json")
2234
+ options?.configPath ?? resolve4(workspaceApmDir(), "apm.config.json")
2182
2235
  );
2183
2236
  if (!existsSync9(p)) {
2184
2237
  console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
@@ -2445,17 +2498,17 @@ var DockerodeClient = class {
2445
2498
  await this.client.getImage(image).remove({ force: true });
2446
2499
  }
2447
2500
  async pullImage(image, auth) {
2448
- const stream = await new Promise((resolve6, reject) => {
2501
+ const stream = await new Promise((resolve5, reject) => {
2449
2502
  const pullOptions = auth ? { authconfig: auth } : void 0;
2450
2503
  this.client.pull(image, pullOptions, (err, output) => {
2451
2504
  if (err || !output) {
2452
2505
  reject(err ?? new Error("docker pull \u8FD4\u56DE\u7A7A\u8F93\u51FA"));
2453
2506
  return;
2454
2507
  }
2455
- resolve6(output);
2508
+ resolve5(output);
2456
2509
  });
2457
2510
  });
2458
- await new Promise((resolve6, reject) => {
2511
+ await new Promise((resolve5, reject) => {
2459
2512
  this.client.modem.followProgress(
2460
2513
  stream,
2461
2514
  (err) => {
@@ -2463,7 +2516,7 @@ var DockerodeClient = class {
2463
2516
  reject(err);
2464
2517
  return;
2465
2518
  }
2466
- resolve6();
2519
+ resolve5();
2467
2520
  },
2468
2521
  () => void 0
2469
2522
  );
@@ -2954,14 +3007,14 @@ var MinioClient = class {
2954
3007
  async deleteObjectsByPrefix(bucket, prefix) {
2955
3008
  const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
2956
3009
  const keys = [];
2957
- await new Promise((resolve6, reject) => {
3010
+ await new Promise((resolve5, reject) => {
2958
3011
  objectsStream.on("data", (obj) => {
2959
3012
  if (obj.name) {
2960
3013
  keys.push(obj.name);
2961
3014
  }
2962
3015
  });
2963
3016
  objectsStream.on("error", reject);
2964
- objectsStream.on("end", resolve6);
3017
+ objectsStream.on("end", resolve5);
2965
3018
  });
2966
3019
  const chunkSize = 500;
2967
3020
  for (let i = 0; i < keys.length; i += chunkSize) {
@@ -3199,7 +3252,7 @@ async function ensureRemoteDir(sftp, dir) {
3199
3252
  }
3200
3253
  }
3201
3254
  function execCommand(client, command) {
3202
- return new Promise((resolve6, reject) => {
3255
+ return new Promise((resolve5, reject) => {
3203
3256
  client.exec(command, (err, stream) => {
3204
3257
  if (err) return reject(err);
3205
3258
  let stdout = "";
@@ -3209,7 +3262,7 @@ function execCommand(client, command) {
3209
3262
  reject(new Error(`\u8FDC\u7A0B\u547D\u4EE4\u5931\u8D25 (${code}): ${stderr || stdout}`));
3210
3263
  return;
3211
3264
  }
3212
- resolve6(stdout);
3265
+ resolve5(stdout);
3213
3266
  }).on("data", (data) => {
3214
3267
  stdout += data.toString();
3215
3268
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "6.0.41",
3
+ "version": "6.0.43",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,
@@ -8,8 +8,8 @@
8
8
 
9
9
  1. 读取 `.apm/sessions/<会话ID>/session.yaml`
10
10
  2. 读取 `.apm/rules/reply.md`
11
- 3. **立即**用 `apm append-message` 回复(可先简短确认,再补充)
12
- 4. 按需阅读 `docs/` 下的文档,有进展继续 append-message
11
+ 3. **立即**用 `append_message` 工具回复(可先简短确认,再补充)
12
+ 4. 按需阅读 `docs/` 下的文档,有进展继续调用 `append_message`
13
13
 
14
14
  #### 重任务(开发、写方案、评审、部署)
15
15
 
@@ -19,7 +19,7 @@
19
19
  4. 任务有阶段性进展或者任务完成后必须回复消息(具体见规则 `reply.md`)
20
20
  5. 写工作日志(具体见规则 `write_doc.md`)
21
21
 
22
- **禁止**在未回复前先读完所有 docs。有进展就先 append-message。
22
+ **禁止**在未回复前先读完所有 docs。有进展就先调用 `append_message`。
23
23
 
24
24
  ### 目录指引
25
25
 
@@ -7,9 +7,9 @@
7
7
 
8
8
  ## 执行回复的方法
9
9
 
10
- 命令: `apm append-message --id=<消息ID> --content=<你要回复的消息内容>`
10
+ 调用 `append_message` 工具,`content` 传入你要回复的消息内容。可多次调用补充进展。
11
11
 
12
- 示例: apm append-message --id=xxxxx --content="收到,正在分析后端改动范围。"
12
+ 示例: `append_message(content="收到,正在分析后端改动范围。")`
13
13
 
14
14
  ## 写文档的方法
15
15
 
@@ -16,13 +16,9 @@
16
16
  保存位置: .apm/sessions/<会话 ID>/docs/<文件名>
17
17
  文档内容格式: 根据你的主题来,不限制,禁止记流水账。
18
18
 
19
- ## 在保存完成之后需要同步到远程
19
+ ## 文档同步
20
20
 
21
- 通过 `apm connect` 连接平台时,每轮 Agent 结束后会自动推送 `docs/` 下变更的 Markdown,一般无需手动同步。
22
-
23
- 手动同步命令: `apm sync-document <会话 ID> --file=<文档名称>`
24
-
25
- 示例: `apm sync-document <会话ID> --file=张三-工作日志.md`
21
+ 保存到 `docs/` 后,`apm connect` 会在每轮 Agent 结束时自动推送到平台,无需额外操作。
26
22
 
27
23
  ## 注意事项
28
24
 
@@ -30,7 +30,7 @@
30
30
  - 使用 **Task** 工具,`subagent_type: generalPurpose`,**readonly: false**,委派子 Agent:
31
31
  - 按需 **Read** 本端计划文档。
32
32
  - 按计划直接改代码;遵守本仓库构建与依赖约定(AGENTS.md)。
33
- - **白名单约束**:只改计划白名单内的文件。开发中确需新增文件或改动白名单外文件,先更新计划文档的白名单(写明原因)并同步,再动手;**禁止悄悄越界**。
33
+ - **白名单约束**:只改计划白名单内的文件。开发中确需新增文件或改动白名单外文件,先更新计划文档的白名单(写明原因),再动手;**禁止悄悄越界**。
34
34
  - **Git**:实现与自洽验收通过后,若有代码改动,**立即 `git add` + `git commit` 一次**(Quick 通常为单次交付,**一次实现 = 一个 commit**;勿拆成无意义碎 commit)。提交信息建议包含 `sessionId`(可从 `session.yaml` 获取)与需求摘要。
35
35
  - 完成后在返回中说明:改了哪些路径、与白名单的对账结果(逐文件列出)、是否通过本地可执行的检查(若子 Agent 跑了构建/测试则写明结果);若有 commit,写明 **short-sha** 与 **subject**,无代码改动则注明跳过 commit。
36
36
 
@@ -56,4 +56,4 @@
56
56
 
57
57
  **注意:不做联调。** 前后端各自按 API 契约交付,接口对不上属于契约或实现问题,由 diff 评审与人工验收暴露后打回修复;禁止自行发起「联调」「接口实测」类的开放式动作。
58
58
 
59
- 完成后 `apm append-message` 回复:改动概述 + 白名单对账 + 构建结果 + 测试环境地址,并 `@` 评审角色进行 diff 评审。
59
+ 完成后用 `append_message` 回复:改动概述 + 白名单对账 + 构建结果 + 测试环境地址,并 `@` 评审角色进行 diff 评审。
@@ -18,11 +18,11 @@
18
18
 
19
19
  ### 步骤 2:三项检查
20
20
 
21
- | 检查项 | 判定 |
22
- |--------|------|
23
- | **白名单对账** | diff 中出现白名单之外的文件,且计划未更新说明 → **不通过** |
21
+ | 检查项 | 判定 |
22
+ | -------------- | -------------------------------------------------------------------- |
23
+ | **白名单对账** | diff 中出现白名单之外的文件,且计划未更新说明 → **不通过** |
24
24
  | **需求相关性** | 存在与本需求无关的改动(顺手重构、改格式、动了无关逻辑)→ **不通过** |
25
- | **计划落实** | 计划「实现步骤」中的关键点在 diff 中找不到对应实现 → **不通过** |
25
+ | **计划落实** | 计划「实现步骤」中的关键点在 diff 中找不到对应实现 → **不通过** |
26
26
 
27
27
  注意事项:
28
28
 
@@ -31,13 +31,13 @@
31
31
 
32
32
  ### 步骤 3:输出结论
33
33
 
34
- **通过**:`apm append-message` 回复评审结论,并 `@项目经理` 交人验收,回复中必须包含:
34
+ **通过**:用 `append_message` 回复评审结论,并 `@项目经理` 交人验收,回复中必须包含:
35
35
 
36
36
  1. 「diff 评审通过」+ 一句话改动概述;
37
37
  2. 测试环境地址(从开发的回复或工作日志中获取);
38
38
  3. 提示按 `CHECKLIST.md` 逐条验收。
39
39
 
40
- **不通过**:`apm append-message` 输出问题清单(每条注明文件 + 问题 + 依据哪条计划/需求),`@` 对应工程师打回修改。**禁止自己动手改。**
40
+ **不通过**:用 `append_message` 输出问题清单(每条注明文件 + 问题 + 依据哪条计划/需求),`@` 对应工程师打回修改。**禁止自己动手改。**
41
41
 
42
42
  ---
43
43
 
@@ -2,10 +2,10 @@
2
2
 
3
3
  后端工程师在需求评审通过后,编写两份文档:
4
4
 
5
- | 文档 | 路径 | 定位 |
6
- |------|------|------|
5
+ | 文档 | 路径 | 定位 |
6
+ | ------------ | ----------------- | ---------------------------------------- |
7
7
  | `BACKEND.md` | `docs/BACKEND.md` | **Plan**:后端怎么改、分几步、动哪些文件 |
8
- | `API.md` | `docs/API.md` | **联调契约**:给前端看的 URL、参数、示例 |
8
+ | `API.md` | `docs/API.md` | **联调契约**:给前端看的 URL、参数、示例 |
9
9
 
10
10
  两份文档禁止合并;`API.md` 不写 Service/SQL 等实现细节。
11
11
 
@@ -17,7 +17,7 @@
17
17
  2. 按需调研代码库,确认现网接口与表结构。
18
18
  3. **Read** `backend-template.md`,按模板 **Write** `docs/BACKEND.md`。
19
19
  4. **Read** `api-template.md`,按模板 **Write** `docs/API.md`。
20
- 5. 执行 `apm sync-document` 同步两份文档,@ 前端阅读 `API.md` 并编写 `FRONTEND.md`。
20
+ 5. @ 前端阅读 `API.md` 并编写 `FRONTEND.md`。
21
21
 
22
22
  (模板路径:`.apm/skills/apm-write-backend-api/`)
23
23
 
@@ -25,7 +25,7 @@
25
25
 
26
26
  ## 写作要求
27
27
 
28
- **BACKEND.md**(对齐 Cursor Plan,通常 **30~80 行**)
28
+ **BACKEND.md**(对齐 Cursor Plan,通常 **30 80 行**)
29
29
 
30
30
  - 背景 → 实现步骤 → 涉及文件 → 数据与规则 → 验收
31
31
  - 可写表名、关键字段;不要大段 SQL、不要完整参数表(那些放 `API.md`)
@@ -19,7 +19,7 @@
19
19
 
20
20
  逐条对照需求原文与计划:
21
21
 
22
- - **计划与需求矛盾、或需求关键点在计划中没有覆盖**:不要写清单,先 `apm append-message` 指出问题并 `@` 对应工程师打回,等计划修订后再写。
22
+ - **计划与需求矛盾、或需求关键点在计划中没有覆盖**:不要写清单,先用 `append_message` 指出问题并 `@` 对应工程师打回,等计划修订后再写。
23
23
  - **计划中的「假设」尚未被项目经理确认**:在回复中提醒,但可以先写清单(清单按需求原文出)。
24
24
  - 计划覆盖完整:进入步骤 3。
25
25
 
@@ -29,13 +29,13 @@
29
29
 
30
30
  要求:
31
31
 
32
- - **5~15 条**,关键点级别,不写入参出参细节、不写接口路径。
32
+ - **5 15 条**,关键点级别,不写入参出参细节、不写接口路径。
33
33
  - 每条 = 在哪个页面、做什么操作、预期看到什么。
34
34
  - 必须覆盖:需求的每个功能点至少 1 条、关键互斥/边界规则至少 1 条、对既有功能的回归至少 1 条(确认没改坏原有逻辑)。
35
35
 
36
- ### 步骤 4:同步并回复
36
+ ### 步骤 4:回复
37
37
 
38
- 执行 `apm sync-document <会话ID> --file=CHECKLIST.md`,回复消息说明清单已就绪,可进入开发。
38
+ 回复消息说明清单已就绪,可进入开发。
39
39
 
40
40
  ## 何时使用
41
41
 
@@ -11,7 +11,7 @@
11
11
  1. **Read** `docs/PRD.md`、`docs/API.md`;`API.md` 不存在则退出并 @ 后端。
12
12
  2. 按需调研代码库,确认改动入口。
13
13
  3. **Read** `.apm/skills/apm-write-frontend-plan/plan-template.md`,按模板 **Write** `docs/FRONTEND.md`。
14
- 4. 执行 `apm sync-document <会话ID> --file=FRONTEND.md`,回复消息通知可进入开发。
14
+ 4. 回复消息通知可进入开发。
15
15
 
16
16
  ---
17
17
 
@@ -6,10 +6,10 @@
6
6
 
7
7
  本技能合并了原 `apm-write-prd`、`apm-review`、`apm-write-frontend-plan`、`apm-write-backend-api` 四个技能的职能:评审(判断是否参与、发现口径缺口)和方案(怎么改、改哪些文件)一步完成。
8
8
 
9
- | 角色 | 产出文档 | 路径 |
10
- |------|----------|------|
11
- | 后端 | `BACKEND-PLAN.md`(含「API 契约」章节) | `docs/BACKEND-PLAN.md` |
12
- | 前端 | `FRONTEND-PLAN.md` | `docs/FRONTEND-PLAN.md` |
9
+ | 角色 | 产出文档 | 路径 |
10
+ | ---- | --------------------------------------- | ----------------------- |
11
+ | 后端 | `BACKEND-PLAN.md`(含「API 契约」章节) | `docs/BACKEND-PLAN.md` |
12
+ | 前端 | `FRONTEND-PLAN.md` | `docs/FRONTEND-PLAN.md` |
13
13
 
14
14
  ---
15
15
 
@@ -19,7 +19,7 @@
19
19
 
20
20
  **Read** `.apm/sessions/<会话ID>/TASK.md`(必要时结合群消息中项目经理的补充说明)。
21
21
 
22
- - **不涉及本端改动**:立即 `apm append-message` 回复「本需求与前端/后端无关,理由:xxx」(一句话说明理由),**流程到此结束,禁止写任何文档、禁止改任何代码**。
22
+ - **不涉及本端改动**:立即用 `append_message` 回复「本需求与前端/后端无关,理由:xxx」(一句话说明理由),**流程到此结束,禁止写任何文档、禁止改任何代码**。
23
23
  - **涉及本端改动**:进入步骤 2。
24
24
 
25
25
  ### 步骤 2:有限调研(必须遵守预算)
@@ -36,19 +36,19 @@
36
36
  2. **改动文件白名单**:本次允许改动的文件完整列表。后续开发与 diff 评审都以此为准,**开发时改了白名单之外的文件会被打回**。
37
37
  3. **后端专属——API 契约**:给前端看的接口定义(Path、参数、响应示例、错误码)。前端开发以契约为准,不等后端部署完成。
38
38
 
39
- ### 步骤 4:同步文档并回复
39
+ ### 步骤 4:回复
40
40
 
41
- 1. 执行 `apm sync-document <会话ID> --file=<文档名>`。
42
- 2. 回复消息(遵守 `reply.md`):
43
- - **「假设」章节非空**:把假设逐条列进回复内容,`@项目经理` 请其确认;明确说「以上假设确认前不开始开发」。
44
- - **无假设**:回复计划已就绪,可进入测试要点编写。
45
- - 前端计划依赖的接口后端契约还没出:在计划「期望接口」小节写出前端期望的接口形态,回复时 `@后端` 对齐,不要空等。
41
+ 回复消息(遵守 `reply.md`):
42
+
43
+ - **「假设」章节非空**:把假设逐条列进回复内容,`@项目经理` 请其确认;明确说「以上假设确认前不开始开发」。
44
+ - **无假设**:回复计划已就绪,可进入测试要点编写。
45
+ - 前端计划依赖的接口后端契约还没出:在计划「期望接口」小节写出前端期望的接口形态,回复时 `@后端` 对齐,不要空等。
46
46
 
47
47
  ---
48
48
 
49
49
  ## 写作要求
50
50
 
51
- - 篇幅 **40~100 行**,宁可少写;不要伪代码、不要大段 SQL。
51
+ - 篇幅 **40 100 行**,宁可少写;不要伪代码、不要大段 SQL。
52
52
  - 用产品语言描述行为,文件路径只出现在「改动文件白名单」。
53
53
  - 前后端可同轮并行编写计划,不互相阻塞。
54
54
 
@@ -12,7 +12,3 @@
12
12
  **不管是第几版需求,都要当成第一版来看,禁止有历史版本或者修订版本或者第几版更新的字样**
13
13
 
14
14
  **可读性**:信息完整保留,但避免整段只靠长句堆砌。对流程分支、状态条件、按钮对照、范围边界等多步骤/多条件内容,按模板中的「图示原则」**适当补充 Mermaid 图或简表**(每个需求点 0 ~ 1 张);图示用于快速扫读,**可验收细节仍以 bullet 为准**,不得因配图而删减文字要求。
15
-
16
- ### 步骤 3: 同步 PRD 到远程
17
-
18
- 执行命令:`apm sync-document <会话 ID> --file=PRD.md`