ai-project-manage-cli 7.0.1 → 7.0.2

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.
Files changed (2) hide show
  1. package/dist/index.js +132 -23
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1449,6 +1449,86 @@ import {
1449
1449
  } from "@cursor/sdk";
1450
1450
  import { setMaxListeners as setMaxListeners2 } from "node:events";
1451
1451
 
1452
+ // src/commands/connect/plan-document.ts
1453
+ import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync7 } from "node:fs";
1454
+ import { join as join11 } from "node:path";
1455
+ function normalizePlanContent(plan) {
1456
+ return plan.replace(/\\n/g, "\n").trim();
1457
+ }
1458
+ function extractPlanFromArgs(args) {
1459
+ if (args == null) {
1460
+ return void 0;
1461
+ }
1462
+ let parsed = args;
1463
+ if (typeof args === "string") {
1464
+ const trimmed = args.trim();
1465
+ if (!trimmed) {
1466
+ return void 0;
1467
+ }
1468
+ try {
1469
+ parsed = JSON.parse(trimmed);
1470
+ } catch {
1471
+ return normalizePlanContent(trimmed);
1472
+ }
1473
+ }
1474
+ if (typeof parsed === "object" && parsed !== null && "plan" in parsed) {
1475
+ const plan = parsed.plan;
1476
+ if (typeof plan === "string" && plan.trim()) {
1477
+ return normalizePlanContent(plan);
1478
+ }
1479
+ }
1480
+ return void 0;
1481
+ }
1482
+ function memberPlanFileName(displayName) {
1483
+ const sanitized = displayName.trim().replace(/[/\\:*?"<>|]/g, "-").replace(/\s+/g, " ").trim() || "member";
1484
+ return `${sanitized}-PLAN.md`;
1485
+ }
1486
+ function collectCreatePlanContent(events) {
1487
+ for (let i = events.length - 1; i >= 0; i--) {
1488
+ const event = events[i];
1489
+ if (event.type !== "tool_call") continue;
1490
+ if (event.name !== "createPlan") continue;
1491
+ if (event.status !== "completed") continue;
1492
+ const content = extractPlanFromArgs(event.args);
1493
+ if (content) {
1494
+ return content;
1495
+ }
1496
+ }
1497
+ return void 0;
1498
+ }
1499
+ function extractCreatePlanFromSessionEvents(events) {
1500
+ for (let i = events.length - 1; i >= 0; i--) {
1501
+ const event = events[i];
1502
+ if (event.type !== "tool_call") continue;
1503
+ if (event.name !== "createPlan") continue;
1504
+ if (event.status !== "completed") continue;
1505
+ const content = extractPlanFromArgs(event.args);
1506
+ if (content) {
1507
+ return content;
1508
+ }
1509
+ }
1510
+ return void 0;
1511
+ }
1512
+ function saveMemberPlanDocument(input) {
1513
+ const trimmedContent = input.content.trim();
1514
+ if (!trimmedContent) {
1515
+ throw new Error("\u8BA1\u5212\u5185\u5BB9\u4E3A\u7A7A\uFF0C\u65E0\u6CD5\u4FDD\u5B58\u6587\u6863");
1516
+ }
1517
+ const fileName = memberPlanFileName(input.memberDisplayName);
1518
+ const docsDir = taskDocsDir(
1519
+ input.taskId,
1520
+ workspaceApmDir(input.workdir),
1521
+ input.workdir
1522
+ );
1523
+ mkdirSync5(toFsPath(docsDir), { recursive: true });
1524
+ const filePath = join11(docsDir, fileName);
1525
+ const normalized = trimmedContent.endsWith("\n") ? trimmedContent : `${trimmedContent}
1526
+ `;
1527
+ writeFileSync7(toFsPath(filePath), normalized, "utf8");
1528
+ console.log(`[apm] \u5DF2\u4FDD\u5B58\u8BA1\u5212\u6587\u6863: ${fileName}`);
1529
+ return fileName;
1530
+ }
1531
+
1452
1532
  // src/event-session.ts
1453
1533
  var EventSession = class {
1454
1534
  events = [];
@@ -1573,6 +1653,9 @@ var EventSession = class {
1573
1653
  getAssistantText() {
1574
1654
  return this.events.filter((e) => e.type === "assistant").map((e) => String(e.content ?? "")).join("\n").trim();
1575
1655
  }
1656
+ getCreatePlanContent() {
1657
+ return extractCreatePlanFromSessionEvents(this.events);
1658
+ }
1576
1659
  };
1577
1660
 
1578
1661
  // src/commands/connect/abort-signal-debug.ts
@@ -1599,7 +1682,7 @@ function logAbortSignalStats(signal, label) {
1599
1682
  }
1600
1683
 
1601
1684
  // src/commands/connect/agent-task-registry.ts
1602
- import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "node:fs";
1685
+ import { existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "node:fs";
1603
1686
  import { dirname as dirname4, resolve as resolve3 } from "node:path";
1604
1687
  function registryPath(workdir, taskId) {
1605
1688
  return resolve3(workdir, ".apm", "tasks", taskId, "cursor-agents.json");
@@ -1626,8 +1709,8 @@ function readRegistry(path10) {
1626
1709
  return {};
1627
1710
  }
1628
1711
  function writeRegistry(path10, registry) {
1629
- mkdirSync5(dirname4(path10), { recursive: true });
1630
- writeFileSync7(path10, `${JSON.stringify(registry, null, 2)}
1712
+ mkdirSync6(dirname4(path10), { recursive: true });
1713
+ writeFileSync8(path10, `${JSON.stringify(registry, null, 2)}
1631
1714
  `, "utf8");
1632
1715
  }
1633
1716
  function loadTaskAgentId(workdir, taskId, user) {
@@ -1766,7 +1849,10 @@ async function obtainAgent(ctx) {
1766
1849
  }
1767
1850
  }
1768
1851
  }
1769
- const agent = await Agent.create(agentOptions);
1852
+ const agent = await Agent.create({
1853
+ ...agentOptions,
1854
+ ...ctx.mode ? { mode: ctx.mode } : {}
1855
+ });
1770
1856
  if (ctx.user) {
1771
1857
  saveTaskAgentId(ctx.workdir, ctx.taskId, ctx.user, agent.agentId);
1772
1858
  }
@@ -1788,8 +1874,9 @@ async function runCursorAgent(cfg, ctx, options) {
1788
1874
 
1789
1875
  ---
1790
1876
  \u8BF7\u5B8C\u6210\u4E0A\u8FF0\u4EFB\u52A1\u3002\u6267\u884C\u8FC7\u7A0B\u4E2D\u8BF7\u7528 append_mail_reply \u589E\u91CF\u66F4\u65B0\u7ED9\u53D1\u4FE1\u4EBA\u7684\u56DE\u590D\uFF08\u53EF\u5148\u7B80\u77ED\u786E\u8BA4\uFF0C\u6709\u8FDB\u5C55\u7EE7\u7EED\u8FFD\u52A0\uFF09\uFF1BAgent \u6B63\u5E38\u7ED3\u675F\u540E\u4F1A\u81EA\u52A8\u63D0\u4EA4\u5B8C\u6574\u56DE\u4FE1\u3002\u5982\u9700\u7F16\u5199\u6587\u6863\uFF0C\u4FDD\u5B58\u5230 \`.apm/tasks/${ctx.taskId}/docs/\` \u76EE\u5F55\u3002`;
1877
+ const mode = ctx.mode ?? "agent";
1791
1878
  console.log(
1792
- `[apm] Cursor Agent \u5F00\u59CB mailId=${ctx.mailId} taskId=${ctx.taskId} cwd=${workdir}`
1879
+ `[apm] Cursor Agent \u5F00\u59CB mailId=${ctx.mailId} taskId=${ctx.taskId} mode=${mode} cwd=${workdir}`
1793
1880
  );
1794
1881
  const { agent, resumed } = await obtainAgent({
1795
1882
  apiKey,
@@ -1797,6 +1884,7 @@ async function runCursorAgent(cfg, ctx, options) {
1797
1884
  workdir,
1798
1885
  taskId: ctx.taskId,
1799
1886
  user: ctx.user,
1887
+ mode,
1800
1888
  customTools
1801
1889
  });
1802
1890
  const eventSession = new EventSession(prompt);
@@ -1822,8 +1910,13 @@ async function runCursorAgent(cfg, ctx, options) {
1822
1910
  signal?.addEventListener("abort", abortRun, { once: true });
1823
1911
  const streamEvents = [];
1824
1912
  try {
1913
+ const forceSend = options?.forceSend ?? resumed;
1825
1914
  const run = await agent.send(prompt, {
1826
- local: { customTools }
1915
+ mode,
1916
+ local: {
1917
+ customTools,
1918
+ ...forceSend ? { force: true } : {}
1919
+ }
1827
1920
  });
1828
1921
  activeRun = run;
1829
1922
  console.log(`[apm] Cursor run id=${run.id} agentId=${agent.agentId}`);
@@ -1839,6 +1932,7 @@ async function runCursorAgent(cfg, ctx, options) {
1839
1932
  await syncRemoteLog.flush(eventSession);
1840
1933
  const result = await run.wait();
1841
1934
  const assistantText = eventSession.getAssistantText() || collectAssistantText(streamEvents);
1935
+ const createPlanContent = eventSession.getCreatePlanContent() || collectCreatePlanContent(streamEvents);
1842
1936
  if (result.status === "error") {
1843
1937
  if (resumed) {
1844
1938
  clearTaskAgentId(workdir, ctx.taskId, ctx.user);
@@ -1855,7 +1949,8 @@ async function runCursorAgent(cfg, ctx, options) {
1855
1949
  runId: result.id,
1856
1950
  agentId: agent.agentId,
1857
1951
  status: result.status,
1858
- assistantText: assistantText || result.result || ""
1952
+ assistantText: assistantText || result.result || "",
1953
+ ...createPlanContent ? { createPlanContent } : {}
1859
1954
  };
1860
1955
  } catch (err) {
1861
1956
  if (err instanceof CursorAgentError) {
@@ -1947,13 +2042,13 @@ function createMailReplyDraft() {
1947
2042
  }
1948
2043
 
1949
2044
  // src/commands/connect/task-pull.ts
1950
- import { writeFileSync as writeFileSync9 } from "fs";
1951
- import { join as join12 } from "path";
2045
+ import { writeFileSync as writeFileSync10 } from "fs";
2046
+ import { join as join13 } from "path";
1952
2047
  import { stringify as yamlStringify } from "yaml";
1953
2048
 
1954
2049
  // src/rules-sync.ts
1955
- import { basename as basename2, extname, join as join11 } from "path";
1956
- import { existsSync as existsSync8, readFileSync as readFileSync9, rmSync as rmSync3, writeFileSync as writeFileSync8 } from "fs";
2050
+ import { basename as basename2, extname, join as join12 } from "path";
2051
+ import { existsSync as existsSync8, readFileSync as readFileSync9, rmSync as rmSync3, writeFileSync as writeFileSync9 } from "fs";
1957
2052
  var MANIFEST_FILE2 = ".rules-sync-manifest.json";
1958
2053
  function ruleLocalFileName(ruleName) {
1959
2054
  const trimmed = ruleName.trim();
@@ -1963,7 +2058,7 @@ function ruleLocalFileName(ruleName) {
1963
2058
  return `${sanitized}.md`;
1964
2059
  }
1965
2060
  function loadManifest(rulesDir) {
1966
- const path10 = join11(rulesDir, MANIFEST_FILE2);
2061
+ const path10 = join12(rulesDir, MANIFEST_FILE2);
1967
2062
  if (!existsSync8(toFsPath(path10))) {
1968
2063
  return { version: 1, rules: {} };
1969
2064
  }
@@ -1979,8 +2074,8 @@ function loadManifest(rulesDir) {
1979
2074
  return { version: 1, rules: {} };
1980
2075
  }
1981
2076
  function saveManifest(rulesDir, manifest) {
1982
- writeFileSync8(
1983
- toFsPath(join11(rulesDir, MANIFEST_FILE2)),
2077
+ writeFileSync9(
2078
+ toFsPath(join12(rulesDir, MANIFEST_FILE2)),
1984
2079
  `${JSON.stringify(manifest, null, 2)}
1985
2080
  `,
1986
2081
  "utf8"
@@ -2001,7 +2096,7 @@ async function syncPlatformRules(cfg, workdirPath, apmRoot) {
2001
2096
  const api = createApmApiClient(cfg);
2002
2097
  const baseline = await api.cli.workspaceBaseline({ workdirPath });
2003
2098
  const repositoryId = baseline.repositoryId;
2004
- const rulesDir = join11(apmRoot ?? workspaceApmDir(workdirPath), "rules");
2099
+ const rulesDir = join12(apmRoot ?? workspaceApmDir(workdirPath), "rules");
2005
2100
  await ensureDirExists(rulesDir);
2006
2101
  if (!repositoryId) {
2007
2102
  console.log(
@@ -2018,7 +2113,7 @@ async function syncPlatformRules(cfg, workdirPath, apmRoot) {
2018
2113
  for (const rule of list) {
2019
2114
  remoteIds.add(rule.id);
2020
2115
  const fileName = ruleLocalFileName(rule.name);
2021
- const dest = join11(rulesDir, fileName);
2116
+ const dest = join12(rulesDir, fileName);
2022
2117
  const entry = manifest.rules[rule.id];
2023
2118
  const updatedAt = rule.updatedAt ?? "";
2024
2119
  if (isRuleUpToDate(entry, rule, dest)) {
@@ -2027,7 +2122,7 @@ async function syncPlatformRules(cfg, workdirPath, apmRoot) {
2027
2122
  console.log(`[apm] \u89C4\u5219\u65E0\u53D8\u5316\uFF0C\u5DF2\u8DF3\u8FC7: rules/${fileName}`);
2028
2123
  continue;
2029
2124
  }
2030
- writeFileSync8(toFsPath(dest), rule.content ?? "", "utf8");
2125
+ writeFileSync9(toFsPath(dest), rule.content ?? "", "utf8");
2031
2126
  nextManifest.rules[rule.id] = { fileName, updatedAt };
2032
2127
  written.push(fileName);
2033
2128
  console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u89C4\u5219: rules/${fileName}`);
@@ -2036,7 +2131,7 @@ async function syncPlatformRules(cfg, workdirPath, apmRoot) {
2036
2131
  for (const [ruleId, entry] of Object.entries(manifest.rules)) {
2037
2132
  if (remoteIds.has(ruleId)) continue;
2038
2133
  if (isBaseRuleFileName(entry.fileName)) continue;
2039
- const dest = join11(rulesDir, entry.fileName);
2134
+ const dest = join12(rulesDir, entry.fileName);
2040
2135
  if (existsSync8(toFsPath(dest))) {
2041
2136
  rmSync3(toFsPath(dest), { force: true });
2042
2137
  }
@@ -2055,15 +2150,15 @@ async function runTaskPull(cfg, detail) {
2055
2150
  const dir = taskDir(taskId, apmRoot, workdir);
2056
2151
  const docsDir = taskDocsDir(taskId, apmRoot, workdir);
2057
2152
  await ensureDirExists(docsDir);
2058
- writeFileSync9(taskRulePath(taskId, apmRoot, workdir), "", "utf8");
2059
- writeFileSync9(
2153
+ writeFileSync10(taskRulePath(taskId, apmRoot, workdir), "", "utf8");
2154
+ writeFileSync10(
2060
2155
  taskTaskPath(taskId, apmRoot, workdir),
2061
2156
  detail.task.description ?? "",
2062
2157
  "utf8"
2063
2158
  );
2064
2159
  for (const doc of detail.documents) {
2065
2160
  const fileName = documentLocalFileName(doc.name);
2066
- writeFileSync9(join12(docsDir, fileName), doc.content ?? "", "utf8");
2161
+ writeFileSync10(join13(docsDir, fileName), doc.content ?? "", "utf8");
2067
2162
  }
2068
2163
  const members = detail.studio?.members ?? [];
2069
2164
  const taskYaml = yamlStringify(
@@ -2081,7 +2176,7 @@ async function runTaskPull(cfg, detail) {
2081
2176
  },
2082
2177
  { lineWidth: 0 }
2083
2178
  );
2084
- writeFileSync9(
2179
+ writeFileSync10(
2085
2180
  taskYamlPath(taskId, apmRoot, workdir),
2086
2181
  taskYaml.endsWith("\n") ? taskYaml : `${taskYaml}
2087
2182
  `,
@@ -2095,6 +2190,9 @@ async function runTaskPull(cfg, detail) {
2095
2190
  }
2096
2191
 
2097
2192
  // src/commands/connect/mail-processor.ts
2193
+ function resolveCursorAgentMode(phase) {
2194
+ return phase === "PLANNING" ? "plan" : "agent";
2195
+ }
2098
2196
  async function processMail(mail, options) {
2099
2197
  const api = createApmApiClient(options.cfg);
2100
2198
  console.log(`[apm] \u5F00\u59CB\u5904\u7406\u4FE1\u4EF6 id=${mail.id}`);
@@ -2151,6 +2249,7 @@ async function processMail(mail, options) {
2151
2249
  hasReplyContent
2152
2250
  } = createMailReplyDraft();
2153
2251
  try {
2252
+ const cursorMode = resolveCursorAgentMode(detail.studio?.phase);
2154
2253
  const result = await runCursorAgent(
2155
2254
  options.cfg,
2156
2255
  {
@@ -2160,7 +2259,8 @@ async function processMail(mail, options) {
2160
2259
  model: detail.recipient.model?.trim() || "default",
2161
2260
  apiKey: cursorApiKey,
2162
2261
  workdir: detail.workdir,
2163
- user: detail.recipient.displayName
2262
+ user: detail.recipient.displayName,
2263
+ mode: cursorMode
2164
2264
  },
2165
2265
  {
2166
2266
  signal: options.signal,
@@ -2171,6 +2271,14 @@ async function processMail(mail, options) {
2171
2271
  if (!replyContent) {
2172
2272
  throw new Error("Agent \u672A\u4EA7\u51FA\u53EF\u63D0\u4EA4\u7684\u56DE\u4FE1\u5185\u5BB9");
2173
2273
  }
2274
+ if (cursorMode === "plan" && result.createPlanContent) {
2275
+ saveMemberPlanDocument({
2276
+ workdir: detail.workdir,
2277
+ taskId: detail.taskId,
2278
+ memberDisplayName: detail.recipient.displayName,
2279
+ content: result.createPlanContent
2280
+ });
2281
+ }
2174
2282
  await api.cli.completeMailboxMessage({
2175
2283
  id: detail.id,
2176
2284
  status: "SUCCEEDED",
@@ -2197,6 +2305,7 @@ async function processMail(mail, options) {
2197
2305
  completeErr instanceof Error ? completeErr.message : completeErr
2198
2306
  );
2199
2307
  }
2308
+ markMailReplied(detail.id);
2200
2309
  }
2201
2310
  }
2202
2311
  function createMailProcessor(options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "7.0.1",
3
+ "version": "7.0.2",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,