ai-project-manage-cli 6.0.89 → 6.0.91

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
@@ -244,6 +244,9 @@ function sessionYamlPath(sessionId, apmRoot) {
244
244
  function sessionMessagesXmlPath(sessionId, apmRoot) {
245
245
  return join2(sessionDir(sessionId, apmRoot), "messages.xml");
246
246
  }
247
+ function sessionAssumptionsJsonPath(sessionId, apmRoot) {
248
+ return join2(sessionDir(sessionId, apmRoot), "assumptions.json");
249
+ }
247
250
  function documentLocalFileName(platformName) {
248
251
  const trimmed = platformName.trim();
249
252
  if (!trimmed) return "document.md";
@@ -399,6 +402,14 @@ var requestConfig = {
399
402
  method: "GET",
400
403
  path: "/cli/documents"
401
404
  }),
405
+ listAssumptions: defineEndpoint({
406
+ method: "GET",
407
+ path: "/cli/assumptions"
408
+ }),
409
+ markAssumptionsBackfilled: defineEndpoint({
410
+ method: "POST",
411
+ path: "/cli/assumptions/mark-backfilled"
412
+ }),
402
413
  listAttachments: defineEndpoint(
403
414
  {
404
415
  method: "GET",
@@ -481,12 +492,10 @@ var requestConfig = {
481
492
  method: "PUT",
482
493
  path: "/cli/task-deployments/status"
483
494
  }),
484
- syncTaskDeploymentLog: defineEndpoint(
485
- {
486
- method: "PUT",
487
- path: "/cli/task-deployments/log"
488
- }
489
- ),
495
+ syncTaskDeploymentLog: defineEndpoint({
496
+ method: "PUT",
497
+ path: "/cli/task-deployments/log"
498
+ }),
490
499
  completeTaskDeployment: defineEndpoint({
491
500
  method: "PUT",
492
501
  path: "/cli/task-deployments/complete"
@@ -1706,15 +1715,14 @@ async function runPull(sessionId, remoteWorkdir) {
1706
1715
  const api = createApmApiClient(cfg);
1707
1716
  const workdir = remoteWorkdir === void 0 ? resolveWorkdirPath() : requireRemoteWorkdir(remoteWorkdir);
1708
1717
  const apmRoot = workspaceApmDir(workdir);
1709
- const [detail, members, documents, attachments, messages] = await Promise.all(
1710
- [
1711
- api.cli.sessionDetail({ sessionId: trimmedId }),
1712
- api.cli.sessionMembers({ sessionId: trimmedId }),
1713
- api.cli.listDocuments({ sessionId: trimmedId }),
1714
- api.cli.listAttachments({ sessionId: trimmedId }),
1715
- api.cli.listSessionMessages({ sessionId: trimmedId })
1716
- ]
1717
- );
1718
+ const [detail, members, documents, attachments, messages, assumptions] = await Promise.all([
1719
+ api.cli.sessionDetail({ sessionId: trimmedId }),
1720
+ api.cli.sessionMembers({ sessionId: trimmedId }),
1721
+ api.cli.listDocuments({ sessionId: trimmedId }),
1722
+ api.cli.listAttachments({ sessionId: trimmedId }),
1723
+ api.cli.listSessionMessages({ sessionId: trimmedId }),
1724
+ api.cli.listAssumptions({ sessionId: trimmedId }).catch(() => null)
1725
+ ]);
1718
1726
  const dir = sessionDir(trimmedId, apmRoot);
1719
1727
  const docsDir = sessionDocsDir(trimmedId, apmRoot);
1720
1728
  await ensureDirExists(docsDir);
@@ -1759,6 +1767,20 @@ async function runPull(sessionId, remoteWorkdir) {
1759
1767
  formatSessionMessagesXml(trimmedId, messages),
1760
1768
  "utf8"
1761
1769
  );
1770
+ writeFileSync9(
1771
+ sessionAssumptionsJsonPath(trimmedId, apmRoot),
1772
+ JSON.stringify(
1773
+ assumptions ?? {
1774
+ sessionId: trimmedId,
1775
+ clearedAt: null,
1776
+ backfilledAt: null,
1777
+ assumptions: []
1778
+ },
1779
+ null,
1780
+ 2
1781
+ ),
1782
+ "utf8"
1783
+ );
1762
1784
  await syncSessionAttachments(cfg, trimmedId, attachments, apmRoot);
1763
1785
  await syncPlatformRules(cfg, trimmedId, workdir, apmRoot);
1764
1786
  await syncRemoteDeploymentConfig(workdir, apmRoot);
@@ -2128,6 +2150,30 @@ async function runSyncDocument(sessionId, options) {
2128
2150
  console.log(`[apm] \u5DF2\u540C\u6B65\u6587\u6863: ${doc.name} (id=${doc.id})`);
2129
2151
  }
2130
2152
 
2153
+ // src/commands/assumptions.ts
2154
+ async function runAssumptionsList(sessionId) {
2155
+ const trimmedId = sessionId.trim();
2156
+ if (!trimmedId) {
2157
+ console.error("[apm] sessionId \u4E0D\u80FD\u4E3A\u7A7A");
2158
+ process.exit(1);
2159
+ }
2160
+ const cfg = await ensureLoggedConfig();
2161
+ const api = createApmApiClient(cfg);
2162
+ const data = await api.cli.listAssumptions({ sessionId: trimmedId });
2163
+ console.log(JSON.stringify(data, null, 2));
2164
+ }
2165
+ async function runAssumptionsMarkBackfilled(sessionId) {
2166
+ const trimmedId = sessionId.trim();
2167
+ if (!trimmedId) {
2168
+ console.error("[apm] sessionId \u4E0D\u80FD\u4E3A\u7A7A");
2169
+ process.exit(1);
2170
+ }
2171
+ const cfg = await ensureLoggedConfig();
2172
+ const api = createApmApiClient(cfg);
2173
+ await api.cli.markAssumptionsBackfilled({ sessionId: trimmedId });
2174
+ console.log(`[apm] \u5DF2\u6807\u8BB0\u5047\u8BBE\u56DE\u586B\u5B8C\u6210: sessionId=${trimmedId}`);
2175
+ }
2176
+
2131
2177
  // src/commands/append-message.ts
2132
2178
  async function appendMessageContent(cfg, messageId, content) {
2133
2179
  const trimmedId = messageId.trim();
@@ -2623,9 +2669,14 @@ function createDeployLogSyncer(api, deploymentRunId) {
2623
2669
  if (!latestLog || latestLog === lastSyncedLog) {
2624
2670
  return;
2625
2671
  }
2672
+ const delta = latestLog.startsWith(lastSyncedLog) && lastSyncedLog.length > 0 ? latestLog.slice(lastSyncedLog.length) : latestLog;
2673
+ if (!delta) {
2674
+ return;
2675
+ }
2626
2676
  await api.cli.syncTaskDeploymentLog({
2627
2677
  id: deploymentRunId,
2628
- log: latestLog
2678
+ log: delta,
2679
+ append: lastSyncedLog.length > 0
2629
2680
  });
2630
2681
  lastSyncedLog = latestLog;
2631
2682
  };
@@ -2678,8 +2729,7 @@ async function handleInboundDeploy(cfg, msg, signal) {
2678
2729
  await logSyncer.flush();
2679
2730
  await api.cli.completeTaskDeployment({
2680
2731
  id: deploymentRunId,
2681
- status: "SUCCESS",
2682
- log: log2
2732
+ status: "SUCCESS"
2683
2733
  });
2684
2734
  console.log(`[apm] deploy success id=${deploymentRunId}`);
2685
2735
  } catch (error) {
@@ -2690,7 +2740,6 @@ async function handleInboundDeploy(cfg, msg, signal) {
2690
2740
  await api.cli.completeTaskDeployment({
2691
2741
  id: deploymentRunId,
2692
2742
  status: "FAILED",
2693
- log: log2,
2694
2743
  error: detail
2695
2744
  });
2696
2745
  console.error(`[apm] deploy failed id=${deploymentRunId}: ${detail}`);
@@ -5946,7 +5995,6 @@ async function completeTrackedDeploy(api, deploymentRunId, logSyncer, input) {
5946
5995
  await api.cli.completeTaskDeployment({
5947
5996
  id: deploymentRunId,
5948
5997
  status: input.status,
5949
- log: input.log,
5950
5998
  error: input.error
5951
5999
  });
5952
6000
  }
@@ -6973,6 +7021,13 @@ function buildProgram() {
6973
7021
  ).argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").action(async (sessionId) => {
6974
7022
  await runPull(sessionId);
6975
7023
  });
7024
+ program.command("assumptions").description("\u5047\u8BBE\u786E\u8BA4\uFF1A\u5217\u51FA\u6216\u6807\u8BB0\u56DE\u586B\u5B8C\u6210").argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").option("--mark-backfilled", "\u6807\u8BB0\u5047\u8BBE\u5DF2\u5199\u56DE\u8BA1\u5212").action(async (sessionId, opts) => {
7025
+ if (opts.markBackfilled) {
7026
+ await runAssumptionsMarkBackfilled(sessionId);
7027
+ return;
7028
+ }
7029
+ await runAssumptionsList(sessionId);
7030
+ });
6976
7031
  program.command("sync-document").description("\u5C06\u672C\u5730 Markdown \u8986\u76D6\u5F0F upsert \u5230\u5E73\u53F0\u4EFB\u52A1\u6587\u6863").argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").requiredOption(
6977
7032
  "--file <name>",
6978
7033
  "\u6587\u6863\u540D\u79F0\uFF08\u5982 PRD \u6216 PRD.md\uFF09\uFF0C\u8BFB\u53D6 .apm/sessions/<sessionId>/docs/ \u4E0B\u5BF9\u5E94\u6587\u4EF6"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "6.0.89",
3
+ "version": "6.0.91",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,
@@ -0,0 +1,21 @@
1
+ # apm-confirm-assumptions:按 PM 确认结果回填计划
2
+
3
+ ## 何时使用
4
+
5
+ 收到项目经理会话消息「已回复所有假设,可以继续后面的流程」后;或 `assumptions.json` 中 `clearedAt` 有值且 `backfilledAt` 为空。
6
+
7
+ ## 工作流程
8
+
9
+ 1. **Read** `.apm/sessions/<会话ID>/assumptions.json`;不存在或 `backfilledAt` 已有 → 退出。
10
+ 2. **Read** 本 Skill;过滤 **本端** 条目(后端只看 `BACKEND_PLAN`,前端只看 `FRONTEND_PLAN`)。
11
+ 3. **Read** 本端计划:`docs/BACKEND-PLAN.md` 或 `docs/FRONTEND-PLAN.md`。
12
+ 4. 逐条按 `resolution` 更新计划:
13
+ - 移入「依据」表,来源写「项目经理确认」;
14
+ - 从「假设」章节删除该题;
15
+ - `customAnswer` 或影响实现范围时 → 修订「实现步骤 / 改动文件白名单」;后端涉及接口则同步 `docs/API.md`。
16
+ 5. **`apm sync-document`** 同步计划(及 API 文档)。
17
+ 6. **Read/Update** `TODO.md`:勾选「等待项目经理确认假设」等相关项。
18
+ 7. **`append_message`**:摘要回填结果;声明「假设已清零,可进入测试要点编写」。
19
+ 8. 执行 **`apm assumptions <会话ID> --mark-backfilled`**。
20
+
21
+ **禁止**从 `messages.xml` 解析 PM 对假设的回复——只认 `assumptions.json`。
@@ -0,0 +1,12 @@
1
+ # apm-rewrite-assumptions:重写 PM 不友好的假设问法
2
+
3
+ ## 何时使用
4
+
5
+ `apm sync-document` 后平台标记假设 `quality=NEEDS_REWRITE`,或计划被退回需重写「问 / 场景 / 选项」。
6
+
7
+ ## 工作流程
8
+
9
+ 1. **Read** 本端计划文档的「假设」章节与 `qualityIssues`(若有 assumptions.json 中的 technicalNote)。
10
+ 2. 将研发术语移入 **研发备注**;**问** 改为 PM 可理解的业务句子;补全 **场景** 与 **≥2 个选项**(含 id + label)。
11
+ 3. **禁止多选**;复合口径说明写在 Other 指引中。
12
+ 4. 重新 **`apm sync-document`** 同步计划。
@@ -20,7 +20,7 @@
20
20
  逐条对照需求原文与计划:
21
21
 
22
22
  - **计划与需求矛盾、或需求关键点在计划中没有覆盖**:不要写清单,先用 `append_message` 指出问题并 `@` 对应工程师打回,等计划修订后再写。
23
- - **计划中的「假设」尚未被项目经理确认**:在回复中提醒,但可以先写清单(清单按需求原文出)。
23
+ - **计划中的「假设」尚未确认或尚未回填计划**(查 `assumptions.json`:`PENDING` 或 `clearedAt && !backfilledAt`):**不要写清单**,先提醒 PM / 执行 `apm-confirm-assumptions`。
24
24
  - 计划覆盖完整:进入步骤 3。
25
25
 
26
26
  ### 步骤 3:按模板写清单
@@ -4,14 +4,10 @@
4
4
 
5
5
  前端 / 后端工程师在任务启动后**直接读原始需求写实现计划**,不经过 PRD。
6
6
 
7
- 本技能合并了原 `apm-write-prd`、`apm-review`、`apm-write-frontend-plan`、`apm-write-backend-api` 四个技能的职能:评审(判断是否参与、发现口径缺口)和方案(怎么改、改哪些文件)一步完成。
8
-
9
- | 角色 | 产出文档 | 路径 |
10
- | ---- | ------------------------------------------------------------- | ------------------------------------- |
11
- | 后端 | `BACKEND-PLAN.md`(实现计划)+ `API.md`(联调契约,单独产出) | `docs/BACKEND-PLAN.md`、`docs/API.md` |
12
- | 前端 | `FRONTEND-PLAN.md` | `docs/FRONTEND-PLAN.md` |
13
-
14
- **两份后端文档禁止合并**:`BACKEND-PLAN.md` 不写完整参数表 / JSON 示例;`API.md` 不写 Service / SQL 等实现细节。
7
+ | 角色 | 产出文档 | 路径 |
8
+ | ---- | -------- | ---- |
9
+ | 后端 | `BACKEND-PLAN.md` + `API.md` | `docs/BACKEND-PLAN.md`、`docs/API.md` |
10
+ | 前端 | `FRONTEND-PLAN.md` | `docs/FRONTEND-PLAN.md` |
15
11
 
16
12
  ---
17
13
 
@@ -19,61 +15,42 @@
19
15
 
20
16
  ### 步骤 1:判断本端是否需要参与
21
17
 
22
- **Read** `.apm/sessions/<会话ID>/TASK.md`(必要时结合群消息中项目经理的补充说明)。
18
+ **Read** `.apm/sessions/<会话ID>/TASK.md`。
23
19
 
24
- - **不涉及本端改动**:立即用 `append_message` 回复「本需求与前端/后端无关,理由:xxx」(一句话说明理由),**流程到此结束,禁止写任何文档、禁止改任何代码**。
20
+ - **不涉及本端改动**:`append_message` 回复理由后结束。
25
21
  - **涉及本端改动**:进入步骤 2。
26
22
 
27
- ### 步骤 2:有限调研(必须遵守预算)
28
-
29
- 1. **优先读现成结论**:先读 `docs/` 下已有的模块档案、历史工作日志、其他成员已同步的文档,能复用就不要重新调研。
30
- 2. **再调研代码**:只看与需求直接相关的页面 / 接口 / 表。**调研预算:最多读 15 个代码文件**,禁止全库考古、禁止顺藤摸瓜阅读无关模块。
31
- 3. **口径不清不要自己拍板**:调研中发现需求没写清的业务口径(例如字段含义、互斥规则、历史数据兼容),一律记入计划的「依据与假设」章节,禁止编造业务规则。
23
+ ### 步骤 2:有限调研
32
24
 
33
- **前端额外要求**:若本次涉及接口对接,**Read** `docs/API.md`;不存在则先 `@后端` 产出 `API.md`,**禁止在计划中自行编写或抄写接口定义**。
25
+ 1. 优先读 `docs/` 现成结论。
26
+ 2. 代码调研预算:**最多 15 个文件**。
27
+ 3. 口径不清 → 记入「假设」章节,禁止编造。
34
28
 
35
29
  ### 步骤 3:按模板写计划
36
30
 
37
- **Read** `.apm/skills/apm-write-plan/plan-template.md`,按模板 **Write** 对应的计划文档。硬性要求,缺一不可:
31
+ **Read** `.apm/skills/apm-write-plan/plan-template.md`,**Write** 计划文档。
38
32
 
39
- 1. **依据与假设**:每条关键口径标注来源(需求原文第几条 / 现有代码行为 / 已有文档);标不出来源的就是「假设」,单独列出。
40
- 2. **改动文件白名单**:本次允许改动的文件完整列表。后续开发与 diff 评审都以此为准,**开发时改了白名单之外的文件会被打回**。
41
- 3. **后端专属——同步产出 API.md**(涉及接口变更时):**Read** `.apm/skills/apm-write-plan/api-template.md`,按模板 **Write** `docs/API.md`。这是前端联调的唯一契约来源,前端直接阅读本文档,禁止各写一份。
42
- 4. **后端专属——SQL 变更声明**(仅当涉及表结构/数据变更时):在「实现步骤」中注明开发须产出 `docs/SQL.md`;计划本身不写大段 SQL,完整语句由后端开发阶段写入该文档。
43
- 5. **前端专属——接口对接要点**:只写「何时调、关键点」,引用 `API.md` 章节,**禁止复制参数表 / JSON 示例**。
33
+ ### 步骤 3.5:假设 PM 友好自检(sync 前必做)
44
34
 
45
- ### 步骤 4:回复
35
+ 每条假设必须含 **问 / 场景 / 选项(≥2 项,id + label)**:
46
36
 
47
- 回复消息(遵守 `reply.md`):
37
+ - **问** 禁止:`status=`、字段名、表名、类路径、SQL。
38
+ - **禁止多选**;复合口径由 PM 在 Web「其他」中填写。
39
+ - 能自己查清的代码含义 → 移入「依据」,删除假设。
40
+ - 技术发现 → 写进 **研发备注**,问句用业务语言。
48
41
 
49
- - **「假设」章节非空**:把假设逐条列进回复内容,`@项目经理` 请其确认;明确说「以上假设确认前不开始开发」。
50
- - **无假设**:回复计划已就绪,可进入测试要点编写。
51
- - **后端**:涉及接口变更时,回复中 `@前端` 阅读 `API.md` 并编写 `FRONTEND-PLAN.md`。
52
- - **前端**:`API.md` 尚未就绪时,回复 `@后端` 先产出 `API.md`,**禁止自行编写接口契约**。
42
+ ### 步骤 4:同步与回复
53
43
 
54
- ### 步骤 5:假设回填(项目经理确认后必须执行)
44
+ 1. `apm sync-document` 同步计划(及 API 文档)。
45
+ 2. **有假设**:`@项目经理` 请其在 Web **「待您确认」** Panel 点选;说明「以上假设确认前不开始开发」。**禁止**在聊天里粘贴研发术语。
46
+ 3. **无假设**:回复计划已就绪,可进入测试要点编写。
55
47
 
56
- 当你被安排「根据项目经理的确认更新计划」,或发现项目经理已回复假设确认时:
48
+ ### 假设回填
57
49
 
58
- 1. **Read** `.apm/sessions/<会话ID>/messages.xml`,找到项目经理针对假设的最新回复,**逐条对照你列出的假设**,不要只看 speakContext 的转述。
59
- 2. 更新计划文档:
60
- - 被确认的假设 → **移入「依据」表格**,来源写「项目经理确认(第 N 轮)」;
61
- - 被否定或修正的假设 → 按项目经理给出的口径**修订「实现步骤」与「改动文件白名单」**;
62
- - 项目经理没有回应的假设 → 保留在「假设」中,再次 `@项目经理` 追问。
63
- 3. **后端**:假设修订涉及接口口径时,同步更新 `docs/API.md`。
64
- 4. 重新执行 `apm sync-document` 同步计划与 API 文档。
65
- 5. 回复消息:逐条说明每个假设的处理结果(确认采纳 / 按口径修订了什么),全部解决则声明「假设已清零,可进入测试要点编写」。
66
-
67
- **禁止**跳过回填直接开发:澄清结论必须落进计划文档(接口口径落进 `API.md`),后续开发与 diff 评审都只认文档,不认聊天记录。
50
+ 项目经理确认后由 **`apm-confirm-assumptions`** 技能执行(读 `assumptions.json`,**禁止**读 messages.xml PM 回复)。
68
51
 
69
52
  ---
70
53
 
71
- ## 写作要求
72
-
73
- - 篇幅 **40 ~ 100 行**,宁可少写;不要伪代码、不要大段 SQL(完整语句写入 `docs/SQL.md`,由后端开发阶段产出)。
74
- - 用产品语言描述行为,文件路径只出现在「改动文件白名单」。
75
- - 前后端可同轮并行编写计划;前端接口部分依赖 `API.md`,后端须先或同步产出。
76
-
77
54
  ## 何时使用
78
55
 
79
- 协作流程阶段 1(实现计划);或用户要求写 `BACKEND-PLAN.md` / `FRONTEND-PLAN.md` / `API.md`。
56
+ 协作流程阶段 1(实现计划)。
@@ -24,10 +24,15 @@
24
24
 
25
25
  ### 假设(待项目经理确认,确认前不开发)
26
26
 
27
- - [ ] A1:xxx(为什么需要确认)
28
- - [ ] A2:xxx
27
+ #### A1
28
+ - **问**(prompt):审批被驳回后,申请人能否修改并重新提交?
29
+ - **场景**:需求写了审批流程,未说明驳回后的操作
30
+ - **选项**:
31
+ - opt_resubmit:允许修改后重新提交
32
+ - opt_new:不允许,需新建申请
33
+ - **研发备注**:代码 `inspection_flow.status=5` 表示 REJECTED(仅研发可见)
29
34
 
30
- > 无假设时写「无,口径均有依据」。
35
+ > PM 若需表达复合口径,在 Web Panel 选 **「其他」** 并文字说明。
31
36
 
32
37
  ## 3. 实现步骤
33
38
 
@@ -70,7 +75,12 @@
70
75
 
71
76
  ### 假设(待项目经理确认,确认前不开发)
72
77
 
73
- - [ ] A1:xxx(为什么需要确认)
78
+ #### A1
79
+ - **问**(prompt):…
80
+ - **场景**:…
81
+ - **选项**:
82
+ - opt_a:…
83
+ - opt_b:…
74
84
 
75
85
  > 无假设时写「无,口径均有依据」。
76
86