ai-project-manage-cli 6.0.34 → 6.0.36

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
@@ -1012,6 +1012,7 @@ async function runAppendMessage(options) {
1012
1012
  // src/commands/update-message-status.ts
1013
1013
  var VALID_STATUSES = [
1014
1014
  "CREATED",
1015
+ "QUEUED",
1015
1016
  "TYPING",
1016
1017
  "SUCCESS",
1017
1018
  "FAILED",
@@ -1439,6 +1440,19 @@ var logCtx = (ctx, agentId) => ({
1439
1440
  messageId: ctx.messageId,
1440
1441
  agentId
1441
1442
  });
1443
+ function formatCursorRunFailure(runId, options) {
1444
+ const details = [
1445
+ options?.statusError?.trim(),
1446
+ options?.resultText?.trim()
1447
+ ].filter((value, index, arr) => {
1448
+ if (!value) return false;
1449
+ return arr.indexOf(value) === index;
1450
+ });
1451
+ if (details.length === 0) {
1452
+ return `Cursor run \u5931\u8D25: ${runId}`;
1453
+ }
1454
+ return `Cursor run \u5931\u8D25: ${runId} \u2014 ${details.join("\uFF1B")}`;
1455
+ }
1442
1456
  async function runCursorAgent(cfg, ctx, options) {
1443
1457
  const signal = options?.signal;
1444
1458
  logAbortSignalStats(signal, "runCursorAgent:start");
@@ -1486,18 +1500,33 @@ async function runCursorAgent(cfg, ctx, options) {
1486
1500
  activeRun = run;
1487
1501
  logAbortSignalStats(signal, "runCursorAgent:after-send");
1488
1502
  console.log(`[apm] Cursor run id=${run.id} agentId=${agent.agentId}`);
1503
+ let lastRunErrorStatus;
1489
1504
  for await (const event of run.stream()) {
1490
1505
  if (signal?.aborted) {
1491
1506
  abortRun();
1492
1507
  throw new Error("\u8FDE\u63A5\u5DF2\u5173\u95ED\uFF0C\u4EFB\u52A1\u4E2D\u65AD");
1493
1508
  }
1509
+ if (event.type === "status" && event.status === "ERROR") {
1510
+ const message = event.message?.trim();
1511
+ if (message) {
1512
+ lastRunErrorStatus = message;
1513
+ console.error(
1514
+ `[apm] Cursor run status=ERROR runId=${run.id}: ${message}`
1515
+ );
1516
+ }
1517
+ }
1494
1518
  eventSession.addEvent(event);
1495
1519
  syncRemoteLog.schedule(eventSession);
1496
1520
  }
1497
1521
  await syncRemoteLog.flush(eventSession);
1498
1522
  const result = await run.wait();
1499
1523
  if (result.status === "error") {
1500
- throw new Error(`Cursor run \u5931\u8D25: ${result.id}`);
1524
+ const failureMessage = formatCursorRunFailure(result.id, {
1525
+ statusError: lastRunErrorStatus,
1526
+ resultText: result.result
1527
+ });
1528
+ console.error(`[apm] ${failureMessage}`);
1529
+ throw new Error(failureMessage);
1501
1530
  }
1502
1531
  if (result.status === "cancelled") {
1503
1532
  throw new Error(`Cursor run \u5DF2\u53D6\u6D88: ${result.id}`);
@@ -1518,6 +1547,54 @@ async function runCursorAgent(cfg, ctx, options) {
1518
1547
  }
1519
1548
  }
1520
1549
 
1550
+ // src/commands/connect/pre-step-cache.ts
1551
+ var PULL_TTL_MS = 3e4;
1552
+ var lastBranchSessionId = null;
1553
+ var lastPullAtBySession = /* @__PURE__ */ new Map();
1554
+ function shouldRunBranch(sessionId) {
1555
+ return lastBranchSessionId !== sessionId;
1556
+ }
1557
+ function markBranchDone(sessionId) {
1558
+ lastBranchSessionId = sessionId;
1559
+ }
1560
+ function shouldRunPull(sessionId) {
1561
+ const last = lastPullAtBySession.get(sessionId);
1562
+ if (last == null) {
1563
+ return true;
1564
+ }
1565
+ return Date.now() - last >= PULL_TTL_MS;
1566
+ }
1567
+ function markPullDone(sessionId) {
1568
+ lastPullAtBySession.set(sessionId, Date.now());
1569
+ }
1570
+
1571
+ // src/commands/connect/run-slot-pool.ts
1572
+ var DEFAULT_MAX_CONCURRENT = 2;
1573
+ function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT) {
1574
+ let active = 0;
1575
+ const waiters = [];
1576
+ const acquire = () => {
1577
+ if (active < maxConcurrent) {
1578
+ active += 1;
1579
+ return Promise.resolve();
1580
+ }
1581
+ return new Promise((resolve5) => {
1582
+ waiters.push(() => {
1583
+ active += 1;
1584
+ resolve5();
1585
+ });
1586
+ });
1587
+ };
1588
+ const release = () => {
1589
+ active = Math.max(0, active - 1);
1590
+ const next = waiters.shift();
1591
+ if (next) {
1592
+ next();
1593
+ }
1594
+ };
1595
+ return { acquire, release };
1596
+ }
1597
+
1521
1598
  // src/commands/connect.ts
1522
1599
  var HEARTBEAT_MS = 3e4;
1523
1600
  async function updateMessageStatus(cfg, messageId, status) {
@@ -1539,34 +1616,54 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
1539
1616
  if (signal.aborted) return;
1540
1617
  const messageId = msg.messageId;
1541
1618
  const runStep = async (step, fn) => {
1619
+ const startedAt = Date.now();
1542
1620
  try {
1543
- return await fn();
1621
+ const result = await fn();
1622
+ console.log(`[apm] step=${step} elapsed=${Date.now() - startedAt}ms`);
1623
+ return result;
1544
1624
  } catch (err) {
1545
1625
  const detail = err instanceof Error ? err.message : String(err);
1546
1626
  throw new Error(`[${step}] ${detail}`);
1547
1627
  }
1548
1628
  };
1549
1629
  try {
1550
- if (signal.aborted) return;
1551
- await runStep(
1552
- "branch",
1553
- () => runBranch(msg.sessionId, { cwd: msg.workdir })
1554
- );
1555
- if (signal.aborted) return;
1556
- await runStep(
1557
- "pull",
1558
- () => runPull(msg.sessionId, workspaceApmDir(msg.workdir))
1559
- );
1560
- if (signal.aborted) return;
1561
- await runStep(
1562
- "commit-pull",
1563
- () => commitWorkingTreeIfDirty(msg.workdir, "fix: apm pull")
1564
- );
1565
1630
  if (signal.aborted) return;
1566
1631
  await runStep(
1567
1632
  "status-typing",
1568
1633
  () => updateMessageStatus(cfg, messageId, "TYPING")
1569
1634
  );
1635
+ if (shouldRunBranch(msg.sessionId)) {
1636
+ if (signal.aborted) return;
1637
+ await runStep(
1638
+ "branch",
1639
+ () => runBranch(msg.sessionId, { cwd: msg.workdir })
1640
+ );
1641
+ markBranchDone(msg.sessionId);
1642
+ } else {
1643
+ console.log(`[apm] step=branch skipped sessionId=${msg.sessionId}`);
1644
+ }
1645
+ let pullRan = false;
1646
+ if (shouldRunPull(msg.sessionId)) {
1647
+ if (signal.aborted) return;
1648
+ await runStep(
1649
+ "pull",
1650
+ () => runPull(msg.sessionId, workspaceApmDir(msg.workdir))
1651
+ );
1652
+ markPullDone(msg.sessionId);
1653
+ pullRan = true;
1654
+ } else {
1655
+ console.log(`[apm] step=pull skipped sessionId=${msg.sessionId}`);
1656
+ }
1657
+ if (pullRan) {
1658
+ if (signal.aborted) return;
1659
+ await runStep(
1660
+ "commit-pull",
1661
+ () => commitWorkingTreeIfDirty(msg.workdir, "fix: apm pull")
1662
+ );
1663
+ } else {
1664
+ console.log(`[apm] step=commit-pull skipped sessionId=${msg.sessionId}`);
1665
+ }
1666
+ if (signal.aborted) return;
1570
1667
  await runStep(
1571
1668
  "cursor-agent",
1572
1669
  () => runCursorAgent(
@@ -1604,7 +1701,7 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
1604
1701
  }
1605
1702
  console.error(
1606
1703
  "[apm] \u5904\u7406\u6D88\u606F\u5931\u8D25:",
1607
- err instanceof Error ? err.message : err
1704
+ err instanceof Error ? err.message : String(err)
1608
1705
  );
1609
1706
  if (err instanceof Error && err.stack) {
1610
1707
  console.error(err.stack);
@@ -1656,7 +1753,8 @@ async function runConnect(options) {
1656
1753
  let stopHeartbeat;
1657
1754
  let shuttingDown = false;
1658
1755
  const shutdownAbort = new AbortController();
1659
- let inboundQueue = Promise.resolve();
1756
+ const runSlots = createRunSlotPool();
1757
+ const activeTasks = /* @__PURE__ */ new Set();
1660
1758
  const activeRuns = /* @__PURE__ */ new Map();
1661
1759
  const pendingCancels = /* @__PURE__ */ new Set();
1662
1760
  const shutdown = async (code = 0) => {
@@ -1674,7 +1772,7 @@ async function runConnect(options) {
1674
1772
  }
1675
1773
  try {
1676
1774
  await Promise.race([
1677
- inboundQueue,
1775
+ Promise.all(activeTasks),
1678
1776
  new Promise((r) => setTimeout(r, SHUTDOWN_DRAIN_MS))
1679
1777
  ]);
1680
1778
  } catch {
@@ -1727,9 +1825,19 @@ async function runConnect(options) {
1727
1825
  shutdownSignal: shutdownAbort.signal,
1728
1826
  perMessageSignal: perMessageController.signal
1729
1827
  };
1730
- inboundQueue = inboundQueue.then(() => handleInboundMessage(cfg, msg, signal, ctx)).finally(() => {
1731
- activeRuns.delete(msg.messageId);
1732
- pendingCancels.delete(msg.messageId);
1828
+ const task = (async () => {
1829
+ await runSlots.acquire();
1830
+ try {
1831
+ await handleInboundMessage(cfg, msg, signal, ctx);
1832
+ } finally {
1833
+ runSlots.release();
1834
+ activeRuns.delete(msg.messageId);
1835
+ pendingCancels.delete(msg.messageId);
1836
+ }
1837
+ })();
1838
+ activeTasks.add(task);
1839
+ void task.finally(() => {
1840
+ activeTasks.delete(task);
1733
1841
  });
1734
1842
  });
1735
1843
  ws.on("close", (code, reason) => {
@@ -2945,7 +3053,10 @@ function buildProgram() {
2945
3053
  program.command("append-message").description("\u5411\u5E73\u53F0\u4F1A\u8BDD\u6D88\u606F\u8FFD\u52A0\u5185\u5BB9\uFF08PUT /api/v1/cli/messages/content\uFF09").requiredOption("--id <messageId>", "\u6D88\u606F ID").requiredOption("--content <content>", "\u8981\u8FFD\u52A0\u7684\u6D88\u606F\u5185\u5BB9").action(async (opts) => {
2946
3054
  await runAppendMessage(opts);
2947
3055
  });
2948
- program.command("update-message-status").description("\u66F4\u65B0\u5E73\u53F0\u4F1A\u8BDD\u6D88\u606F\u72B6\u6001").requiredOption("--id <messageId>", "\u6D88\u606F ID").requiredOption("--status <status>", "CREATED | TYPING | SUCCESS | FAILED").action(async (opts) => {
3056
+ program.command("update-message-status").description("\u66F4\u65B0\u5E73\u53F0\u4F1A\u8BDD\u6D88\u606F\u72B6\u6001").requiredOption("--id <messageId>", "\u6D88\u606F ID").requiredOption(
3057
+ "--status <status>",
3058
+ "CREATED | QUEUED | TYPING | SUCCESS | FAILED | CANCELLED"
3059
+ ).action(async (opts) => {
2949
3060
  await runUpdateMessageStatus(opts);
2950
3061
  });
2951
3062
  program.command("connect").description(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "6.0.34",
3
+ "version": "6.0.36",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,
@@ -2,11 +2,24 @@
2
2
 
3
3
  ### 工作流程
4
4
 
5
- 1. 读取 `.apm/sessions/<会话ID>/session.yaml`,必要时读取`messages.xml` 了解当前会话状态与历史消息
5
+ 按任务轻重选择路径:
6
+
7
+ #### 轻量回复(被 @ 询问、确认、同步进展)
8
+
9
+ 1. 读取 `.apm/sessions/<会话ID>/session.yaml`
10
+ 2. 读取 `.apm/rules/reply.md`
11
+ 3. **立即**用 `apm append-message` 回复(可先简短确认,再补充)
12
+ 4. 按需阅读 `docs/` 下的文档,有进展继续 append-message
13
+
14
+ #### 重任务(开发、写方案、评审、部署)
15
+
16
+ 1. 读取 `.apm/sessions/<会话ID>/session.yaml`,必要时读取 `messages.xml` 了解历史
6
17
  2. 根据你的名字从 `session.yaml` 的 `members` 中找到你对应的 **description**(智能体描述,非人设提示词)
7
18
  3. 根据角色描述完成用户指定的任务
8
- 4. 任务有阶段行进展或者任务完成后必须回复消息(具体见规则 `reply.md`)
9
- 5. 写工作日志(具体见规则 `write_doc.md`)
19
+ 4. 任务有阶段性进展或者任务完成后必须回复消息(具体见规则 `reply.md`)
20
+ 5. 写工作日志(具体见规则 `write_doc.md`)
21
+
22
+ **禁止**在未回复前先读完所有 docs。有进展就先 append-message。
10
23
 
11
24
  ### 目录指引
12
25
 
@@ -23,5 +36,5 @@
23
36
  - 附件列表: `attachments/*`,当有文档中提及附件时,从这里查找
24
37
  - 协作规则: `RULE.md`,在这里可以看到不同成员的协作规则,从而找其他成员协助你一起解决问题,按需阅读
25
38
  - 协作 TODO: `TODO.md`,跨轮次任务清单(待办与已完成项),按需阅读
26
- - 历史消息记录: `message.xml`,历史消息列表,数据量很大,按需阅读
39
+ - 历史消息记录: `messages.xml`,历史消息列表,数据量很大,按需阅读
27
40
  - 初始目标: `TASK.md`,最初的目标,不一定具体,团队成员会有人负责让这个任务变得具体,仅供参考。如果你是相关的角色,则你需要先读取这个目标,然后规划接下来的动作。
@@ -2,14 +2,14 @@
2
2
 
3
3
  1. 如果回复的内容包含文档地址,不要把前缀也写出来,直接用文档名称即可。例如: 我已经把待处理的内容更新到了 `PRD.md` 中,请查阅。
4
4
  2. 回复的内容并非对你工作的总结,要尽可能简洁,不要长篇大论,如果同步的内容确实较多,你可以先写文档,然后指引用户去阅读你写的文档。
5
- 3. 只要你有任何进展都想都可以调用下面的命令进行回复,你可以一直补充内容。
5
+ 3. 只要你有任何进展都可以调用下面的命令进行回复,你可以一直补充内容。**被 @ 提及时,收到后必须先回复一句简短确认(不超过 50 字),再开始读文档或执行任务。**
6
6
  4. 如果有需要指定相关人,必须在消息内容中@指定出来
7
7
 
8
8
  ## 执行回复的方法
9
9
 
10
10
  命令: `apm append-message --id=<消息ID> --content=<你要回复的消息内容>`
11
11
 
12
- 示例: apm append-message --id=xxxxx --content="我已经把待处理的内容更新到了 `PRD.md`,请查阅"
12
+ 示例: apm append-message --id=xxxxx --content="收到,正在分析后端改动范围。"
13
13
 
14
14
  ## 写文档的方法
15
15