ai-project-manage-cli 6.0.34 → 6.0.35
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 +106 -23
- package/package.json +1 -1
- package/template/AGENTS.md +17 -4
- package/template/rules/reply.md +2 -2
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",
|
|
@@ -1518,6 +1519,54 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
1518
1519
|
}
|
|
1519
1520
|
}
|
|
1520
1521
|
|
|
1522
|
+
// src/commands/connect/pre-step-cache.ts
|
|
1523
|
+
var PULL_TTL_MS = 3e4;
|
|
1524
|
+
var lastBranchSessionId = null;
|
|
1525
|
+
var lastPullAtBySession = /* @__PURE__ */ new Map();
|
|
1526
|
+
function shouldRunBranch(sessionId) {
|
|
1527
|
+
return lastBranchSessionId !== sessionId;
|
|
1528
|
+
}
|
|
1529
|
+
function markBranchDone(sessionId) {
|
|
1530
|
+
lastBranchSessionId = sessionId;
|
|
1531
|
+
}
|
|
1532
|
+
function shouldRunPull(sessionId) {
|
|
1533
|
+
const last = lastPullAtBySession.get(sessionId);
|
|
1534
|
+
if (last == null) {
|
|
1535
|
+
return true;
|
|
1536
|
+
}
|
|
1537
|
+
return Date.now() - last >= PULL_TTL_MS;
|
|
1538
|
+
}
|
|
1539
|
+
function markPullDone(sessionId) {
|
|
1540
|
+
lastPullAtBySession.set(sessionId, Date.now());
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
// src/commands/connect/run-slot-pool.ts
|
|
1544
|
+
var DEFAULT_MAX_CONCURRENT = 2;
|
|
1545
|
+
function createRunSlotPool(maxConcurrent = DEFAULT_MAX_CONCURRENT) {
|
|
1546
|
+
let active = 0;
|
|
1547
|
+
const waiters = [];
|
|
1548
|
+
const acquire = () => {
|
|
1549
|
+
if (active < maxConcurrent) {
|
|
1550
|
+
active += 1;
|
|
1551
|
+
return Promise.resolve();
|
|
1552
|
+
}
|
|
1553
|
+
return new Promise((resolve5) => {
|
|
1554
|
+
waiters.push(() => {
|
|
1555
|
+
active += 1;
|
|
1556
|
+
resolve5();
|
|
1557
|
+
});
|
|
1558
|
+
});
|
|
1559
|
+
};
|
|
1560
|
+
const release = () => {
|
|
1561
|
+
active = Math.max(0, active - 1);
|
|
1562
|
+
const next = waiters.shift();
|
|
1563
|
+
if (next) {
|
|
1564
|
+
next();
|
|
1565
|
+
}
|
|
1566
|
+
};
|
|
1567
|
+
return { acquire, release };
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1521
1570
|
// src/commands/connect.ts
|
|
1522
1571
|
var HEARTBEAT_MS = 3e4;
|
|
1523
1572
|
async function updateMessageStatus(cfg, messageId, status) {
|
|
@@ -1539,34 +1588,54 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
|
|
|
1539
1588
|
if (signal.aborted) return;
|
|
1540
1589
|
const messageId = msg.messageId;
|
|
1541
1590
|
const runStep = async (step, fn) => {
|
|
1591
|
+
const startedAt = Date.now();
|
|
1542
1592
|
try {
|
|
1543
|
-
|
|
1593
|
+
const result = await fn();
|
|
1594
|
+
console.log(`[apm] step=${step} elapsed=${Date.now() - startedAt}ms`);
|
|
1595
|
+
return result;
|
|
1544
1596
|
} catch (err) {
|
|
1545
1597
|
const detail = err instanceof Error ? err.message : String(err);
|
|
1546
1598
|
throw new Error(`[${step}] ${detail}`);
|
|
1547
1599
|
}
|
|
1548
1600
|
};
|
|
1549
1601
|
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
1602
|
if (signal.aborted) return;
|
|
1566
1603
|
await runStep(
|
|
1567
1604
|
"status-typing",
|
|
1568
1605
|
() => updateMessageStatus(cfg, messageId, "TYPING")
|
|
1569
1606
|
);
|
|
1607
|
+
if (shouldRunBranch(msg.sessionId)) {
|
|
1608
|
+
if (signal.aborted) return;
|
|
1609
|
+
await runStep(
|
|
1610
|
+
"branch",
|
|
1611
|
+
() => runBranch(msg.sessionId, { cwd: msg.workdir })
|
|
1612
|
+
);
|
|
1613
|
+
markBranchDone(msg.sessionId);
|
|
1614
|
+
} else {
|
|
1615
|
+
console.log(`[apm] step=branch skipped sessionId=${msg.sessionId}`);
|
|
1616
|
+
}
|
|
1617
|
+
let pullRan = false;
|
|
1618
|
+
if (shouldRunPull(msg.sessionId)) {
|
|
1619
|
+
if (signal.aborted) return;
|
|
1620
|
+
await runStep(
|
|
1621
|
+
"pull",
|
|
1622
|
+
() => runPull(msg.sessionId, workspaceApmDir(msg.workdir))
|
|
1623
|
+
);
|
|
1624
|
+
markPullDone(msg.sessionId);
|
|
1625
|
+
pullRan = true;
|
|
1626
|
+
} else {
|
|
1627
|
+
console.log(`[apm] step=pull skipped sessionId=${msg.sessionId}`);
|
|
1628
|
+
}
|
|
1629
|
+
if (pullRan) {
|
|
1630
|
+
if (signal.aborted) return;
|
|
1631
|
+
await runStep(
|
|
1632
|
+
"commit-pull",
|
|
1633
|
+
() => commitWorkingTreeIfDirty(msg.workdir, "fix: apm pull")
|
|
1634
|
+
);
|
|
1635
|
+
} else {
|
|
1636
|
+
console.log(`[apm] step=commit-pull skipped sessionId=${msg.sessionId}`);
|
|
1637
|
+
}
|
|
1638
|
+
if (signal.aborted) return;
|
|
1570
1639
|
await runStep(
|
|
1571
1640
|
"cursor-agent",
|
|
1572
1641
|
() => runCursorAgent(
|
|
@@ -1604,7 +1673,7 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
|
|
|
1604
1673
|
}
|
|
1605
1674
|
console.error(
|
|
1606
1675
|
"[apm] \u5904\u7406\u6D88\u606F\u5931\u8D25:",
|
|
1607
|
-
err instanceof Error ? err.message : err
|
|
1676
|
+
err instanceof Error ? err.message : String(err)
|
|
1608
1677
|
);
|
|
1609
1678
|
if (err instanceof Error && err.stack) {
|
|
1610
1679
|
console.error(err.stack);
|
|
@@ -1656,7 +1725,8 @@ async function runConnect(options) {
|
|
|
1656
1725
|
let stopHeartbeat;
|
|
1657
1726
|
let shuttingDown = false;
|
|
1658
1727
|
const shutdownAbort = new AbortController();
|
|
1659
|
-
|
|
1728
|
+
const runSlots = createRunSlotPool();
|
|
1729
|
+
const activeTasks = /* @__PURE__ */ new Set();
|
|
1660
1730
|
const activeRuns = /* @__PURE__ */ new Map();
|
|
1661
1731
|
const pendingCancels = /* @__PURE__ */ new Set();
|
|
1662
1732
|
const shutdown = async (code = 0) => {
|
|
@@ -1674,7 +1744,7 @@ async function runConnect(options) {
|
|
|
1674
1744
|
}
|
|
1675
1745
|
try {
|
|
1676
1746
|
await Promise.race([
|
|
1677
|
-
|
|
1747
|
+
Promise.all(activeTasks),
|
|
1678
1748
|
new Promise((r) => setTimeout(r, SHUTDOWN_DRAIN_MS))
|
|
1679
1749
|
]);
|
|
1680
1750
|
} catch {
|
|
@@ -1727,9 +1797,19 @@ async function runConnect(options) {
|
|
|
1727
1797
|
shutdownSignal: shutdownAbort.signal,
|
|
1728
1798
|
perMessageSignal: perMessageController.signal
|
|
1729
1799
|
};
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1800
|
+
const task = (async () => {
|
|
1801
|
+
await runSlots.acquire();
|
|
1802
|
+
try {
|
|
1803
|
+
await handleInboundMessage(cfg, msg, signal, ctx);
|
|
1804
|
+
} finally {
|
|
1805
|
+
runSlots.release();
|
|
1806
|
+
activeRuns.delete(msg.messageId);
|
|
1807
|
+
pendingCancels.delete(msg.messageId);
|
|
1808
|
+
}
|
|
1809
|
+
})();
|
|
1810
|
+
activeTasks.add(task);
|
|
1811
|
+
void task.finally(() => {
|
|
1812
|
+
activeTasks.delete(task);
|
|
1733
1813
|
});
|
|
1734
1814
|
});
|
|
1735
1815
|
ws.on("close", (code, reason) => {
|
|
@@ -2945,7 +3025,10 @@ function buildProgram() {
|
|
|
2945
3025
|
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
3026
|
await runAppendMessage(opts);
|
|
2947
3027
|
});
|
|
2948
|
-
program.command("update-message-status").description("\u66F4\u65B0\u5E73\u53F0\u4F1A\u8BDD\u6D88\u606F\u72B6\u6001").requiredOption("--id <messageId>", "\u6D88\u606F ID").requiredOption(
|
|
3028
|
+
program.command("update-message-status").description("\u66F4\u65B0\u5E73\u53F0\u4F1A\u8BDD\u6D88\u606F\u72B6\u6001").requiredOption("--id <messageId>", "\u6D88\u606F ID").requiredOption(
|
|
3029
|
+
"--status <status>",
|
|
3030
|
+
"CREATED | QUEUED | TYPING | SUCCESS | FAILED | CANCELLED"
|
|
3031
|
+
).action(async (opts) => {
|
|
2949
3032
|
await runUpdateMessageStatus(opts);
|
|
2950
3033
|
});
|
|
2951
3034
|
program.command("connect").description(
|
package/package.json
CHANGED
package/template/AGENTS.md
CHANGED
|
@@ -2,11 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
### 工作流程
|
|
4
4
|
|
|
5
|
-
|
|
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.
|
|
9
|
-
5.
|
|
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
|
-
- 历史消息记录: `
|
|
39
|
+
- 历史消息记录: `messages.xml`,历史消息列表,数据量很大,按需阅读
|
|
27
40
|
- 初始目标: `TASK.md`,最初的目标,不一定具体,团队成员会有人负责让这个任务变得具体,仅供参考。如果你是相关的角色,则你需要先读取这个目标,然后规划接下来的动作。
|
package/template/rules/reply.md
CHANGED
|
@@ -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="
|
|
12
|
+
示例: apm append-message --id=xxxxx --content="收到,正在分析后端改动范围。"
|
|
13
13
|
|
|
14
14
|
## 写文档的方法
|
|
15
15
|
|