ai-project-manage-cli 8.1.5 → 8.1.7

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/README.md CHANGED
@@ -39,6 +39,8 @@ apm connect
39
39
  apm connect --server https://your-server.com
40
40
  ```
41
41
 
42
+ 启动前会检测 npm registry 上当前大版本的最新包;若落后则先 `npm install -g` 更新,再以新版本重新连接。跨大版本请手动执行 `apm update --major`。
43
+
42
44
  同一时刻仅允许一个 connect。
43
45
 
44
46
  `apm connect` 处理完 WebIDE 消息后,会将 `.apm/project/<项目ID>/` 下的项目文档变更推回平台。
package/dist/index.js CHANGED
@@ -287,6 +287,11 @@ var requestConfig = {
287
287
  method: "POST",
288
288
  path: "/cli/webide/actions"
289
289
  }),
290
+ /** CLI 准备步骤实时回写到「待→进行中」转移线 */
291
+ webideUpdateTransitionPrepEvents: defineEndpoint({
292
+ method: "PUT",
293
+ path: "/cli/webide/transition-prep-events"
294
+ }),
290
295
  webideUpsertMessageLog: defineEndpoint({
291
296
  method: "PUT",
292
297
  path: "/cli/webide/message-logs"
@@ -319,6 +324,14 @@ var requestConfig = {
319
324
  method: "PUT",
320
325
  path: "/cli/webide/plan"
321
326
  }),
327
+ webideUpsertChecklist: defineEndpoint({
328
+ method: "PUT",
329
+ path: "/cli/webide/checklist"
330
+ }),
331
+ webideGetChecklist: defineEndpoint({
332
+ method: "GET",
333
+ path: "/cli/webide/checklist"
334
+ }),
322
335
  webideReplaceTestCases: defineEndpoint({
323
336
  method: "PUT",
324
337
  path: "/cli/webide/test-cases"
@@ -507,6 +520,8 @@ async function runLogin(opts) {
507
520
 
508
521
  // src/commands/update.ts
509
522
  import { spawnSync } from "child_process";
523
+ import { existsSync as existsSync2 } from "fs";
524
+ import { join as join4 } from "path";
510
525
 
511
526
  // src/version.ts
512
527
  import { readFileSync as readFileSync2 } from "fs";
@@ -525,6 +540,7 @@ function readCliVersion() {
525
540
  }
526
541
 
527
542
  // src/commands/update.ts
543
+ var APM_SKIP_SELF_UPDATE_ENV = "APM_SKIP_SELF_UPDATE";
528
544
  var useNpmShell = process.platform === "win32";
529
545
  function runNpm(args, options = {}) {
530
546
  return spawnSync(useNpmShell ? "npm.cmd" : "npm", args, {
@@ -617,10 +633,17 @@ function npmAvailable() {
617
633
  return !r.error && r.status === 0;
618
634
  }
619
635
  async function runUpdate(options = {}) {
636
+ if (process.env[APM_SKIP_SELF_UPDATE_ENV] === "1") {
637
+ return { didUpdate: false };
638
+ }
620
639
  const current = readCliVersion();
621
640
  const currentMajor = parseMajorVersion(current);
622
641
  const globalLatest = await fetchLatestPublishedVersion();
623
642
  const latestInMajor = options.allowMajorUpgrade ? null : await fetchLatestPublishedVersionInMajor(currentMajor);
643
+ if (options.quietIfCurrent && !options.allowMajorUpgrade && !latestInMajor && !globalLatest) {
644
+ console.warn(`[apm] \u65E0\u6CD5\u68C0\u6D4B\u6700\u65B0\u7248\u672C\uFF0C\u7EE7\u7EED\u4F7F\u7528\u5F53\u524D ${current}`);
645
+ return { didUpdate: false };
646
+ }
624
647
  const resolved = resolveUpdateTarget({
625
648
  current,
626
649
  allowMajorUpgrade: options.allowMajorUpgrade,
@@ -628,8 +651,10 @@ async function runUpdate(options = {}) {
628
651
  globalLatest
629
652
  });
630
653
  if (resolved.action === "skip") {
631
- console.log(`[apm] \u5DF2\u662F\u6700\u65B0\u7248\u672C ${current}`);
632
- return;
654
+ if (!options.quietIfCurrent) {
655
+ console.log(`[apm] \u5DF2\u662F\u6700\u65B0\u7248\u672C ${current}`);
656
+ }
657
+ return { didUpdate: false };
633
658
  }
634
659
  if (resolved.majorUpgradeAvailable) {
635
660
  console.log(
@@ -666,14 +691,57 @@ async function runUpdate(options = {}) {
666
691
  } else {
667
692
  console.log(`[apm] \u5DF2\u662F\u6700\u65B0\u7248\u672C ${current}`);
668
693
  }
694
+ return { didUpdate: expectedBump };
695
+ }
696
+ function resolveApmEntryPath(entryArg = process.argv[1]) {
697
+ const npmResult = runNpm(["root", "-g"], {
698
+ encoding: "utf8",
699
+ stdio: ["ignore", "pipe", "pipe"]
700
+ });
701
+ if (npmResult.status === 0) {
702
+ const globalRoot = npmResult.stdout?.toString().trim();
703
+ if (globalRoot) {
704
+ const candidate = join4(globalRoot, CLI_PACKAGE_NAME, "dist", "index.js");
705
+ if (existsSync2(candidate)) {
706
+ return candidate;
707
+ }
708
+ }
709
+ }
710
+ const fromArgv = entryArg?.trim();
711
+ if (fromArgv && existsSync2(fromArgv)) {
712
+ return fromArgv;
713
+ }
714
+ if (fromArgv) {
715
+ return fromArgv;
716
+ }
717
+ console.error("[apm] \u65E0\u6CD5\u89E3\u6790 apm \u5165\u53E3\u8DEF\u5F84");
718
+ process.exit(1);
719
+ }
720
+ function reexecConnectAfterUpdate(options) {
721
+ console.log("[apm] \u66F4\u65B0\u5B8C\u6210\uFF0C\u6B63\u5728\u4EE5\u65B0\u7248\u672C\u91CD\u65B0\u8FDE\u63A5\u2026");
722
+ const apmScript = resolveApmEntryPath();
723
+ const args = [apmScript, "connect"];
724
+ const server = options.server?.trim();
725
+ if (server) {
726
+ args.push("--server", server);
727
+ }
728
+ const result = spawnSync(process.execPath, args, {
729
+ stdio: "inherit",
730
+ env: { ...process.env, [APM_SKIP_SELF_UPDATE_ENV]: "1" }
731
+ });
732
+ if (result.error) {
733
+ console.error("[apm] \u91CD\u542F connect \u5931\u8D25:", result.error.message);
734
+ process.exit(1);
735
+ }
736
+ process.exit(result.status ?? 0);
669
737
  }
670
738
 
671
739
  // src/commands/update-skills.ts
672
- import { existsSync as existsSync2, statSync as statSync2 } from "fs";
740
+ import { existsSync as existsSync3, statSync as statSync2 } from "fs";
673
741
  async function syncWorkspaceSkills(workdir) {
674
742
  const apmDir = workspaceApmDir(workdir);
675
743
  const fsApmDir = toFsPath(apmDir);
676
- if (!existsSync2(fsApmDir)) {
744
+ if (!existsSync3(fsApmDir)) {
677
745
  throw new Error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
678
746
  }
679
747
  const apmStat = statSync2(fsApmDir);
@@ -685,7 +753,7 @@ async function syncWorkspaceSkills(workdir) {
685
753
  }
686
754
  async function runUpdateSkills() {
687
755
  const apmDir = workspaceApmDir();
688
- if (!existsSync2(apmDir)) {
756
+ if (!existsSync3(apmDir)) {
689
757
  console.error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
690
758
  process.exit(1);
691
759
  }
@@ -698,14 +766,14 @@ async function runUpdateSkills() {
698
766
 
699
767
  // src/utils/project-documents.ts
700
768
  import {
701
- existsSync as existsSync3,
769
+ existsSync as existsSync4,
702
770
  readdirSync as readdirSync2,
703
771
  readFileSync as readFileSync3,
704
772
  rmSync,
705
773
  writeFileSync as writeFileSync2
706
774
  } from "fs";
707
775
  import { createHash } from "crypto";
708
- import { dirname as dirname3, join as join4, relative, sep } from "path";
776
+ import { dirname as dirname3, join as join5, relative, sep } from "path";
709
777
 
710
778
  // src/utils/git.ts
711
779
  import { execFile as execFile2 } from "child_process";
@@ -779,11 +847,11 @@ function normalizeProjectIdForPath(projectId) {
779
847
  }
780
848
  function projectDocumentsDir(apmRoot, projectId) {
781
849
  const id = normalizeProjectIdForPath(projectId);
782
- return join4(apmRoot ?? workspaceApmDir(), "project", id);
850
+ return join5(apmRoot ?? workspaceApmDir(), "project", id);
783
851
  }
784
852
  function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
785
853
  const normalized = normalizeLocalDocumentPath(documentPath);
786
- return join4(
854
+ return join5(
787
855
  projectDocumentsDir(apmRoot, projectId),
788
856
  ...normalized.split("/")
789
857
  );
@@ -803,11 +871,11 @@ function hashLocalFileContent(content) {
803
871
  return createHash("sha256").update(content, "utf8").digest("hex");
804
872
  }
805
873
  function readLocalManifest(apmRoot, projectId) {
806
- const manifestPath = join4(
874
+ const manifestPath = join5(
807
875
  projectDocumentsDir(apmRoot, projectId),
808
876
  MANIFEST_FILE
809
877
  );
810
- if (!existsSync3(manifestPath)) {
878
+ if (!existsSync4(manifestPath)) {
811
879
  return null;
812
880
  }
813
881
  try {
@@ -820,13 +888,13 @@ function readLocalManifest(apmRoot, projectId) {
820
888
  }
821
889
  function listLocalDocumentPaths(apmRoot, projectId) {
822
890
  const root = projectDocumentsDir(apmRoot, projectId);
823
- if (!existsSync3(root)) {
891
+ if (!existsSync4(root)) {
824
892
  return [];
825
893
  }
826
894
  const paths = [];
827
895
  const walk = (dir) => {
828
896
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
829
- const abs = join4(dir, entry.name);
897
+ const abs = join5(dir, entry.name);
830
898
  if (entry.isDirectory()) {
831
899
  walk(abs);
832
900
  continue;
@@ -955,13 +1023,13 @@ ${diagnostic ?? ""}`);
955
1023
  const absPath = toFsPath(
956
1024
  projectDocumentLocalPath(targetApmDir, projectId, path)
957
1025
  );
958
- if (existsSync3(absPath)) {
1026
+ if (existsSync4(absPath)) {
959
1027
  rmSync(absPath, { force: true });
960
1028
  deleted += 1;
961
1029
  }
962
1030
  }
963
1031
  writeFileSync2(
964
- toFsPath(join4(docsDir, MANIFEST_FILE)),
1032
+ toFsPath(join5(docsDir, MANIFEST_FILE)),
965
1033
  `${JSON.stringify(remoteManifest, null, 2)}
966
1034
  `,
967
1035
  "utf8"
@@ -1279,7 +1347,7 @@ function handleCancelAction(payload, ctx) {
1279
1347
  }
1280
1348
 
1281
1349
  // src/commands/connect/core/worker/worker.ts
1282
- import { dirname as dirname4, join as join5 } from "path";
1350
+ import { dirname as dirname4, join as join6 } from "path";
1283
1351
  import { fileURLToPath as fileURLToPath3 } from "url";
1284
1352
  import { Worker } from "node:worker_threads";
1285
1353
 
@@ -1305,7 +1373,7 @@ function resolveWebIdeWorkerHeapMb(env = process.env, totalMemoryBytes = totalme
1305
1373
  }
1306
1374
 
1307
1375
  // src/commands/connect/core/worker/worker.ts
1308
- var workerFile = join5(
1376
+ var workerFile = join6(
1309
1377
  dirname4(fileURLToPath3(import.meta.url)),
1310
1378
  "worker-script.js"
1311
1379
  );
@@ -1658,14 +1726,14 @@ function dispatchConnectAction(payload, ctx) {
1658
1726
 
1659
1727
  // src/commands/connect-lock.ts
1660
1728
  import {
1661
- existsSync as existsSync4,
1729
+ existsSync as existsSync5,
1662
1730
  mkdirSync as mkdirSync3,
1663
1731
  readFileSync as readFileSync4,
1664
1732
  unlinkSync,
1665
1733
  writeFileSync as writeFileSync3
1666
1734
  } from "fs";
1667
- import { join as join6 } from "path";
1668
- var CONNECT_LOCK_PATH = join6(APM_CONFIG_DIR, "connect.lock");
1735
+ import { join as join7 } from "path";
1736
+ var CONNECT_LOCK_PATH = join7(APM_CONFIG_DIR, "connect.lock");
1669
1737
  function isProcessAlive(pid) {
1670
1738
  if (!Number.isInteger(pid) || pid <= 0) return false;
1671
1739
  try {
@@ -1676,7 +1744,7 @@ function isProcessAlive(pid) {
1676
1744
  }
1677
1745
  }
1678
1746
  function readConnectLock() {
1679
- if (!existsSync4(CONNECT_LOCK_PATH)) return null;
1747
+ if (!existsSync5(CONNECT_LOCK_PATH)) return null;
1680
1748
  try {
1681
1749
  const raw = readFileSync4(CONNECT_LOCK_PATH, "utf8");
1682
1750
  const parsed = JSON.parse(raw);
@@ -1842,6 +1910,10 @@ function resolveConnectServerOption(options) {
1842
1910
  return void 0;
1843
1911
  }
1844
1912
  async function runConnect(options) {
1913
+ const { didUpdate } = await runUpdate({ quietIfCurrent: true });
1914
+ if (didUpdate) {
1915
+ reexecConnectAfterUpdate(options);
1916
+ }
1845
1917
  const cfg = await ensureLoggedConfig();
1846
1918
  const serverOverride = resolveConnectServerOption(options);
1847
1919
  if (serverOverride) {
@@ -1968,7 +2040,7 @@ function buildProgram() {
1968
2040
  await runSyncProjectDocuments(opts);
1969
2041
  });
1970
2042
  program.command("connect").description(
1971
- "\u8FDE\u63A5\u5E73\u53F0 WebSocket\uFF08/ws/agent\uFF09\uFF0C\u7EF4\u6301\u5FC3\u8DF3\u5E76\u5904\u7406\u4E0B\u884C webide-message"
2043
+ "\u8FDE\u63A5\u524D\u68C0\u6D4B\u5E76\u66F4\u65B0\u5230\u5F53\u524D\u5927\u7248\u672C\u6700\u65B0 CLI\uFF0C\u518D\u8FDE\u63A5\u5E73\u53F0 WebSocket\uFF08/ws/agent\uFF09"
1972
2044
  ).option("--server <url>", "API \u6839\u5730\u5740\uFF0C\u8986\u76D6 config \u4E2D\u7684 baseUrl").action(async (opts) => {
1973
2045
  await runConnect({ server: opts.server });
1974
2046
  });
@@ -35,6 +35,11 @@ var requestConfig = {
35
35
  method: "POST",
36
36
  path: "/cli/webide/actions"
37
37
  }),
38
+ /** CLI 准备步骤实时回写到「待→进行中」转移线 */
39
+ webideUpdateTransitionPrepEvents: defineEndpoint({
40
+ method: "PUT",
41
+ path: "/cli/webide/transition-prep-events"
42
+ }),
38
43
  webideUpsertMessageLog: defineEndpoint({
39
44
  method: "PUT",
40
45
  path: "/cli/webide/message-logs"
@@ -67,6 +72,14 @@ var requestConfig = {
67
72
  method: "PUT",
68
73
  path: "/cli/webide/plan"
69
74
  }),
75
+ webideUpsertChecklist: defineEndpoint({
76
+ method: "PUT",
77
+ path: "/cli/webide/checklist"
78
+ }),
79
+ webideGetChecklist: defineEndpoint({
80
+ method: "GET",
81
+ path: "/cli/webide/checklist"
82
+ }),
70
83
  webideReplaceTestCases: defineEndpoint({
71
84
  method: "PUT",
72
85
  path: "/cli/webide/test-cases"
@@ -1216,6 +1229,16 @@ var EventSession = class {
1216
1229
  });
1217
1230
  this.markDirty(this.events.length - 1);
1218
1231
  }
1232
+ /** 导出当前 CLI 准备步骤快照(供 start-* 写入转移日志) */
1233
+ getCliSteps() {
1234
+ return this.events.filter((e) => e.type === "cli_step" && typeof e.step === "string").map((e) => ({
1235
+ step: String(e.step),
1236
+ status: typeof e.status === "string" ? e.status : "ok",
1237
+ text: typeof e.text === "string" ? e.text : "",
1238
+ ...typeof e.at === "string" ? { at: e.at } : {},
1239
+ ...typeof e.durationMs === "number" && Number.isFinite(e.durationMs) ? { durationMs: e.durationMs } : {}
1240
+ }));
1241
+ }
1219
1242
  formatEvent(event) {
1220
1243
  switch (event.type) {
1221
1244
  case "assistant": {
@@ -1484,7 +1507,7 @@ function createMergeWebIdePullRequestsTool(options) {
1484
1507
  const { webideMergePullRequests } = options;
1485
1508
  return {
1486
1509
  MergeWebIdePullRequests: {
1487
- description: "Merge all open pull requests for this WebIDE task. Call once with confirmedConflictFree=true. No baseline sync or review is required before calling.",
1510
+ description: "Merge all open pull requests for this WebIDE task. During accept, call UpsertWebIdeChecklist first with the full acceptance checklist, then call this once with confirmedConflictFree=true. No baseline sync is required before merging.",
1488
1511
  inputSchema: {
1489
1512
  type: "object",
1490
1513
  properties: {
@@ -1571,6 +1594,63 @@ function createUpsertWebIdePlanTool(options) {
1571
1594
  }
1572
1595
  };
1573
1596
  }
1597
+ function createUpsertWebIdeChecklistTool(options) {
1598
+ const { webideUpsertChecklist } = options;
1599
+ return {
1600
+ UpsertWebIdeChecklist: {
1601
+ description: "Persist or replace the acceptance checklist markdown for this WebIDE task. Call with the full checklist (what changed, which repos/files, how to verify) before merging PRs. Use during accept so humans can review later.",
1602
+ inputSchema: {
1603
+ type: "object",
1604
+ properties: {
1605
+ content: {
1606
+ type: "string",
1607
+ description: "Full acceptance checklist in Markdown (Chinese): summary of code changes, scope by repo, key files, verification notes"
1608
+ }
1609
+ },
1610
+ required: ["content"]
1611
+ },
1612
+ execute: async (args) => {
1613
+ const content = asString(args.content);
1614
+ if (!content) {
1615
+ throw new Error("UpsertWebIdeChecklist \u7F3A\u5C11 content");
1616
+ }
1617
+ await webideUpsertChecklist({ content });
1618
+ return JSON.stringify({ ok: true, chars: content.length }, null, 2);
1619
+ }
1620
+ }
1621
+ };
1622
+ }
1623
+ function createGetWebIdeChecklistTool(options) {
1624
+ const { boundTaskId, webideGetChecklist } = options;
1625
+ return {
1626
+ GetWebIdeChecklist: {
1627
+ description: "Fetch the acceptance checklist markdown for a WebIDE task by taskId. Returns content=null if none has been written yet. Use when you need to read or revise an existing checklist.",
1628
+ inputSchema: {
1629
+ type: "object",
1630
+ properties: {
1631
+ taskId: {
1632
+ type: "string",
1633
+ description: "WebIDE task id whose checklist to load"
1634
+ }
1635
+ },
1636
+ required: ["taskId"]
1637
+ },
1638
+ execute: async (args) => {
1639
+ const taskId = asString(args.taskId);
1640
+ if (!taskId) {
1641
+ throw new Error("GetWebIdeChecklist \u7F3A\u5C11 taskId");
1642
+ }
1643
+ if (taskId !== boundTaskId) {
1644
+ throw new Error(
1645
+ `GetWebIdeChecklist \u7684 taskId \u987B\u4E3A\u5F53\u524D\u4EFB\u52A1 ${boundTaskId}`
1646
+ );
1647
+ }
1648
+ const result = await webideGetChecklist({ taskId });
1649
+ return JSON.stringify(result, null, 2);
1650
+ }
1651
+ }
1652
+ };
1653
+ }
1574
1654
 
1575
1655
  // src/commands/connect/tools/webide-test-case-tools.ts
1576
1656
  function asString2(value) {
@@ -1603,7 +1683,7 @@ function createUpsertWebIdeTestCasesTool(options) {
1603
1683
  const { webideReplaceTestCases } = options;
1604
1684
  return {
1605
1685
  UpsertWebIdeTestCases: {
1606
- description: "Persist generated automated test cases for this WebIDE task (full replace) and mark the workflow TEST_READY. Call once with the complete case list after reviewing the task and code changes.",
1686
+ description: "Persist generated automated test cases for this WebIDE task (full replace). Call once with the complete case list after reviewing the task and code changes. Does not change workflow phase.",
1607
1687
  inputSchema: {
1608
1688
  type: "object",
1609
1689
  properties: {
@@ -2349,6 +2429,23 @@ function createCursorCustomTools(cfg2, options) {
2349
2429
  })
2350
2430
  );
2351
2431
  }
2432
+ if (wantsTool("UpsertWebIdeChecklist", options, enablePlan)) {
2433
+ Object.assign(
2434
+ tools,
2435
+ createUpsertWebIdeChecklistTool({
2436
+ webideUpsertChecklist: (args) => cli.webideUpsertChecklist({ taskId, ...args })
2437
+ })
2438
+ );
2439
+ }
2440
+ if (wantsTool("GetWebIdeChecklist", options, enablePlan)) {
2441
+ Object.assign(
2442
+ tools,
2443
+ createGetWebIdeChecklistTool({
2444
+ boundTaskId: taskId,
2445
+ webideGetChecklist: (args) => cli.webideGetChecklist(args)
2446
+ })
2447
+ );
2448
+ }
2352
2449
  if (wantsTool("UpsertWebIdeTestCases", options, enablePlan)) {
2353
2450
  Object.assign(
2354
2451
  tools,
@@ -4130,70 +4227,6 @@ async function cleanWebIdeWorkspaceCache(taskId, workdir, cfg2) {
4130
4227
  }
4131
4228
  }
4132
4229
 
4133
- // src/commands/connect/webide-run-action.ts
4134
- function startActionForInbound(action) {
4135
- switch (action) {
4136
- case "request-assessment":
4137
- return "start-assessment";
4138
- case "request-design":
4139
- case "request-revise-design":
4140
- return "start-design";
4141
- case "request-write-plan":
4142
- case "request-revise-plan":
4143
- return "start-write-plan";
4144
- case "request-develop":
4145
- case "skip-plan":
4146
- return "start-develop";
4147
- case "request-start-project":
4148
- return "start-project";
4149
- case "request-execute-sql":
4150
- return "start-execute-sql";
4151
- case "request-bugfix":
4152
- return "start-bugfix";
4153
- case "request-organize-code":
4154
- case "request-patch":
4155
- return "start-organize-code";
4156
- case "request-accept":
4157
- return "start-accept";
4158
- default:
4159
- return null;
4160
- }
4161
- }
4162
- function completeActionForInbound(action) {
4163
- switch (action) {
4164
- case "request-assessment":
4165
- case "confirm-assumptions":
4166
- return "complete-assessment";
4167
- case "request-design":
4168
- case "request-revise-design":
4169
- return "complete-design";
4170
- case "request-write-plan":
4171
- case "request-revise-plan":
4172
- return "complete-write-plan";
4173
- case "request-develop":
4174
- case "skip-plan":
4175
- return "complete-develop";
4176
- case "request-start-project":
4177
- return "complete-project";
4178
- case "request-execute-sql":
4179
- return "complete-execute-sql";
4180
- case "request-bugfix":
4181
- return "complete-bugfix";
4182
- case "request-organize-code":
4183
- case "request-patch":
4184
- return "complete-organize-code";
4185
- case "request-accept":
4186
- return "complete-accept";
4187
- default:
4188
- return null;
4189
- }
4190
- }
4191
- async function webIdeRunAction(cfg2, taskId, action, params) {
4192
- const api = createApmApiClient(cfg2);
4193
- await api.cli.webideRunAction({ taskId, action, params });
4194
- console.log(`[apm] webide runAction ok action=${action} taskId=${taskId}`);
4195
- }
4196
-
4197
4230
  // src/commands/connect/webide-ask-question.ts
4198
4231
  function asString5(value) {
4199
4232
  return typeof value === "string" ? value.trim() : "";
@@ -4253,9 +4286,8 @@ async function fetchAssumptionAnswersAndContinueAssessment(options) {
4253
4286
  resolution: row.resolution
4254
4287
  });
4255
4288
  }
4256
- await webIdeRunAction(cfg2, taskId, "continue-assessment");
4257
4289
  console.log(
4258
- `[apm] \u5047\u8BBE\u7ED3\u679C\u5DF2\u62C9\u53D6\u5E76\u7EE7\u7EED\u8BC4\u4F30 taskId=${taskId} count=${answers.length}`
4290
+ `[apm] \u5047\u8BBE\u7ED3\u679C\u5DF2\u62C9\u53D6 taskId=${taskId} count=${answers.length}`
4259
4291
  );
4260
4292
  return JSON.stringify({ title, answers }, null, 2);
4261
4293
  }
@@ -4745,6 +4777,102 @@ function parseWebIdePromptPayload(content) {
4745
4777
  return null;
4746
4778
  }
4747
4779
 
4780
+ // src/commands/connect/webide-run-action.ts
4781
+ function startActionForInbound(action) {
4782
+ switch (action) {
4783
+ case "request-assessment":
4784
+ return "start-assessment";
4785
+ case "request-design":
4786
+ case "request-revise-design":
4787
+ return "start-design";
4788
+ case "request-write-plan":
4789
+ case "request-revise-plan":
4790
+ return "start-write-plan";
4791
+ case "request-develop":
4792
+ case "skip-plan":
4793
+ return "start-develop";
4794
+ case "request-start-project":
4795
+ return "start-project";
4796
+ case "request-execute-sql":
4797
+ return "start-execute-sql";
4798
+ case "request-bugfix":
4799
+ return "start-bugfix";
4800
+ case "request-organize-code":
4801
+ case "request-patch":
4802
+ return "start-organize-code";
4803
+ case "request-accept":
4804
+ return "start-accept";
4805
+ case "confirm-assumptions":
4806
+ return "continue-assessment";
4807
+ default:
4808
+ return null;
4809
+ }
4810
+ }
4811
+ function completeActionForInbound(action) {
4812
+ switch (action) {
4813
+ case "request-assessment":
4814
+ case "confirm-assumptions":
4815
+ return "complete-assessment";
4816
+ case "request-design":
4817
+ case "request-revise-design":
4818
+ return "complete-design";
4819
+ case "request-write-plan":
4820
+ case "request-revise-plan":
4821
+ return "complete-write-plan";
4822
+ case "request-develop":
4823
+ case "skip-plan":
4824
+ return "complete-develop";
4825
+ case "request-start-project":
4826
+ return "complete-project";
4827
+ case "request-execute-sql":
4828
+ return "complete-execute-sql";
4829
+ case "request-bugfix":
4830
+ return "complete-bugfix";
4831
+ case "request-organize-code":
4832
+ case "request-patch":
4833
+ return "complete-organize-code";
4834
+ case "request-accept":
4835
+ return "complete-accept";
4836
+ default:
4837
+ return null;
4838
+ }
4839
+ }
4840
+ async function webIdeRunAction(cfg2, taskId, action, params) {
4841
+ const api = createApmApiClient(cfg2);
4842
+ await api.cli.webideRunAction({ taskId, action, params });
4843
+ console.log(`[apm] webide runAction ok action=${action} taskId=${taskId}`);
4844
+ }
4845
+ async function webIdeSyncTransitionPrepEvents(cfg2, taskId, startAction, prepEvents) {
4846
+ try {
4847
+ const api = createApmApiClient(cfg2);
4848
+ await api.cli.webideUpdateTransitionPrepEvents({
4849
+ taskId,
4850
+ startAction,
4851
+ prepEvents
4852
+ });
4853
+ } catch (err) {
4854
+ console.warn(
4855
+ `[apm] sync transition prepEvents failed taskId=${taskId}:`,
4856
+ err instanceof Error ? err.message : err
4857
+ );
4858
+ }
4859
+ }
4860
+ async function webIdeDiscardTransitionPrepDraft(cfg2, taskId, startAction) {
4861
+ try {
4862
+ const api = createApmApiClient(cfg2);
4863
+ await api.cli.webideUpdateTransitionPrepEvents({
4864
+ taskId,
4865
+ startAction,
4866
+ discardDraft: true
4867
+ });
4868
+ } catch (err) {
4869
+ console.warn(
4870
+ `[apm] discard transition prep draft failed taskId=${taskId}:`,
4871
+ err instanceof Error ? err.message : err
4872
+ );
4873
+ }
4874
+ }
4875
+
4748
4876
  // src/commands/connect/core/worker/handlers/webide-message.ts
4749
4877
  var WEBIDE_CODE_CHANGE_ACTIONS = /* @__PURE__ */ new Set([
4750
4878
  "skip-plan",
@@ -4805,11 +4933,22 @@ async function handleWebIdeInboundMessage(cfg2, msg2, signal) {
4805
4933
  }
4806
4934
  )
4807
4935
  };
4936
+ let pausedForAssumptions = false;
4937
+ let startAction = null;
4808
4938
  const syncPrepLog = async () => {
4809
4939
  const sync = logSyncRef.current;
4810
- if (!sync) return;
4811
- sync.schedule(eventSession);
4812
- await sync.flush(eventSession);
4940
+ if (sync) {
4941
+ sync.schedule(eventSession);
4942
+ await sync.flush(eventSession);
4943
+ }
4944
+ if (startAction) {
4945
+ await webIdeSyncTransitionPrepEvents(
4946
+ cfg2,
4947
+ taskId,
4948
+ startAction,
4949
+ eventSession.getCliSteps()
4950
+ );
4951
+ }
4813
4952
  };
4814
4953
  const runPrepStep = async (step, work, formatOk) => {
4815
4954
  eventSession.addCliStep(step, "\u8FDB\u884C\u4E2D\u2026", "running");
@@ -4826,12 +4965,8 @@ async function handleWebIdeInboundMessage(cfg2, msg2, signal) {
4826
4965
  throw err;
4827
4966
  }
4828
4967
  };
4829
- let pausedForAssumptions = false;
4830
4968
  try {
4831
- const startAction = startActionForInbound(msg2.action);
4832
- if (startAction) {
4833
- await webIdeRunAction(cfg2, taskId, startAction);
4834
- }
4969
+ startAction = startActionForInbound(msg2.action);
4835
4970
  if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
4836
4971
  let assumptionAnswersText;
4837
4972
  if (msg2.action === "confirm-assumptions") {
@@ -5092,6 +5227,15 @@ ${assumptionAnswersText}` : msg2.content,
5092
5227
  logSyncRef.current?.schedule(eventSession);
5093
5228
  await logSyncRef.current?.markRun(runId, "running");
5094
5229
  await logSyncRef.current?.flush(eventSession);
5230
+ if (startAction) {
5231
+ await webIdeSyncTransitionPrepEvents(
5232
+ cfg2,
5233
+ taskId,
5234
+ startAction,
5235
+ eventSession.getCliSteps()
5236
+ );
5237
+ await webIdeRunAction(cfg2, taskId, startAction, { runId });
5238
+ }
5095
5239
  }
5096
5240
  }
5097
5241
  );
@@ -5306,6 +5450,9 @@ ${assumptionAnswersText}` : msg2.content,
5306
5450
  } catch (err) {
5307
5451
  const detail = err instanceof Error ? err.message : String(err);
5308
5452
  console.error(`[apm] webide-message \u5931\u8D25: ${detail}`);
5453
+ if (startAction) {
5454
+ await webIdeDiscardTransitionPrepDraft(cfg2, taskId, startAction);
5455
+ }
5309
5456
  if (detail.includes("\u590D\u7528 Agent") || detail.includes("resume")) {
5310
5457
  clearWebIdeAgentId(workdir, taskId);
5311
5458
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "8.1.5",
3
+ "version": "8.1.7",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,
@@ -17,7 +17,7 @@
17
17
  - `webide_sql_execute.md`:execute-sql 轮次连库执行 SQL 时 Read
18
18
  - `webide_terminal.md`:长驻进程禁止用 Shell;本地服务由顶栏「启动项目」触发,开发任务不要自行起服务
19
19
  - `webide_testcase.md`:生成测试用例并调用 `UpsertWebIdeTestCases`
20
- - `webide_merge.md`:验收通过后调用 `MergeWebIdePullRequests`
20
+ - `webide_merge.md`:验收通过后先 `UpsertWebIdeChecklist` 写验收清单,再 `MergeWebIdePullRequests`
21
21
 
22
22
  ### 目录
23
23
 
@@ -25,4 +25,4 @@
25
25
  - 任务附件:`.apm/webide/<taskId>/attachments/`(prompt 出现「任务附件」时按路径查看)
26
26
  - 仓库清单:`.apm/workspace-repos.json`(先确认单仓 / 多仓;多仓必须写清改动范围并点名仓库)
27
27
 
28
- 计划、用例、合并 PR 走 WebIDE 专用工具(`AskQuestion`、`RecommendPlan`、`UpsertWebIdePlan`、`UpsertWebIdeTestCases`、`MergeWebIdePullRequests` 等)。本地服务由用户在顶栏「启动项目」启动,开发任务不要自行起长驻进程。
28
+ 计划、用例、验收清单、合并 PR 走 WebIDE 专用工具(`AskQuestion`、`RecommendPlan`、`UpsertWebIdePlan`、`UpsertWebIdeChecklist`、`GetWebIdeChecklist`、`UpsertWebIdeTestCases`、`MergeWebIdePullRequests` 等)。本地服务由用户在顶栏「启动项目」启动,开发任务不要自行起长驻进程。
@@ -1,16 +1,24 @@
1
1
  ## WebIDE 合并规范
2
2
 
3
- 适用场景:WebIDE 验收通过(`accept`)后合并本任务 PR。
3
+ 适用场景:WebIDE 验收通过(`accept` / `request-accept`)后,先落库验收清单,再合并本任务 PR。
4
4
 
5
5
  ### 目标
6
6
 
7
- 将本任务各仓库特性分支上的变更合并进基线分支(通常为 `main`)。
7
+ 1. 留下一份可事后查阅的「验收清单」(描述本任务实际改了什么)。
8
+ 2. 将各仓库特性分支上的变更合并进基线分支(通常为 `main`)。
8
9
 
9
10
  ### 步骤
10
11
 
11
- 1. 调用一次 `MergeWebIdePullRequests`(`confirmedConflictFree=true`),由平台合并本任务全部 OPEN PR。
12
- 2. 用 `AppendMessage` 汇总合并结果(PR 编号、仓库)。
12
+ 1. **先写验收清单**:调用一次 `UpsertWebIdeChecklist`,提交完整 Markdown 全文(不要只提交 diff)。若需对照已有清单,可先 `GetWebIdeChecklist`(传当前 `taskId`)。建议结构:
13
+ - 改动摘要
14
+ - 按仓库的改动范围
15
+ - 关键改动点(模块 / 文件 / 接口 / 页面)
16
+ - 如何验收 / 回归点
17
+ - 备注(已知限制、未做事项、SQL/配置;无则写「无」)
18
+ 2. 调用一次 `MergeWebIdePullRequests`(`confirmedConflictFree=true`),由平台合并本任务全部 OPEN PR。
19
+ 3. 用 `AppendMessage` 汇总:清单已落库 + 合并结果(PR 编号、仓库)。
13
20
 
14
21
  ### 禁止
15
22
 
23
+ - **禁止**跳过 `UpsertWebIdeChecklist` 直接合并
16
24
  - 在合并之外自行执行同步基线、代码审核、部署等额外流程