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 +2 -0
- package/dist/index.js +94 -22
- package/dist/worker-script.js +223 -76
- package/package.json +1 -1
- package/template/AGENTS.webide.md +2 -2
- package/template/rules/webide_merge.md +12 -4
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
|
-
|
|
632
|
-
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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
|
|
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
|
|
850
|
+
return join5(apmRoot ?? workspaceApmDir(), "project", id);
|
|
783
851
|
}
|
|
784
852
|
function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
|
|
785
853
|
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
786
|
-
return
|
|
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 =
|
|
874
|
+
const manifestPath = join5(
|
|
807
875
|
projectDocumentsDir(apmRoot, projectId),
|
|
808
876
|
MANIFEST_FILE
|
|
809
877
|
);
|
|
810
|
-
if (!
|
|
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 (!
|
|
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 =
|
|
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 (
|
|
1026
|
+
if (existsSync4(absPath)) {
|
|
959
1027
|
rmSync(absPath, { force: true });
|
|
960
1028
|
deleted += 1;
|
|
961
1029
|
}
|
|
962
1030
|
}
|
|
963
1031
|
writeFileSync2(
|
|
964
|
-
toFsPath(
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
1668
|
-
var CONNECT_LOCK_PATH =
|
|
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 (!
|
|
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\
|
|
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
|
});
|
package/dist/worker-script.js
CHANGED
|
@@ -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.
|
|
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)
|
|
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
|
|
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 (
|
|
4811
|
-
|
|
4812
|
-
|
|
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
|
-
|
|
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
|
@@ -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
|
|
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
|
-
|
|
28
|
+
计划、用例、验收清单、合并 PR 走 WebIDE 专用工具(`AskQuestion`、`RecommendPlan`、`UpsertWebIdePlan`、`UpsertWebIdeChecklist`、`GetWebIdeChecklist`、`UpsertWebIdeTestCases`、`MergeWebIdePullRequests` 等)。本地服务由用户在顶栏「启动项目」启动,开发任务不要自行起长驻进程。
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
## WebIDE 合并规范
|
|
2
2
|
|
|
3
|
-
适用场景:WebIDE 验收通过(`accept
|
|
3
|
+
适用场景:WebIDE 验收通过(`accept` / `request-accept`)后,先落库验收清单,再合并本任务 PR。
|
|
4
4
|
|
|
5
5
|
### 目标
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
1. 留下一份可事后查阅的「验收清单」(描述本任务实际改了什么)。
|
|
8
|
+
2. 将各仓库特性分支上的变更合并进基线分支(通常为 `main`)。
|
|
8
9
|
|
|
9
10
|
### 步骤
|
|
10
11
|
|
|
11
|
-
1.
|
|
12
|
-
|
|
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
|
- 在合并之外自行执行同步基线、代码审核、部署等额外流程
|