ai-project-manage-cli 7.1.38 → 8.0.1
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 +6 -8
- package/dist/index.js +3978 -7394
- package/dist/webide-message-worker.js +289 -703
- package/package.json +1 -2
- package/template/AGENTS.webide.md +27 -0
- package/template/AGENTS.md +0 -56
- package/template/apm.config.json +0 -3
- package/template/project/.gitkeep +0 -0
- package/template/rules/reply.md +0 -16
- package/template/rules/write_doc.md +0 -39
- package/template/sessions/.gitkeep +0 -0
- package/template/skills/apm-apply-change/SKILL.md +0 -116
- package/template/skills/apm-confirm-assumptions/SKILL.md +0 -3
- package/template/skills/apm-dev/SKILL.md +0 -80
- package/template/skills/apm-diff-review/SKILL.md +0 -63
- package/template/skills/apm-propose/SKILL.md +0 -52
- package/template/skills/apm-propose/design.md +0 -37
- package/template/skills/apm-propose/proposal.md +0 -31
- package/template/skills/apm-propose/specs.md +0 -39
- package/template/skills/apm-propose/tasks.md +0 -16
- package/template/skills/apm-recap/SKILL.md +0 -77
- package/template/skills/apm-recap/recap-template.md +0 -121
- package/template/skills/apm-update-plan/SKILL.md +0 -26
- package/template/skills/apm-write-assumptions/SKILL.md +0 -78
- package/template/skills/apm-write-assumptions/assumptions-template.md +0 -18
- package/template/skills/apm-write-backend-api/SKILL.md +0 -40
- package/template/skills/apm-write-backend-api/api-template.md +0 -35
- package/template/skills/apm-write-backend-api/backend-template.md +0 -41
- package/template/skills/apm-write-checklist/SKILL.md +0 -42
- package/template/skills/apm-write-checklist/checklist-template.md +0 -34
- package/template/skills/apm-write-frontend-plan/SKILL.md +0 -27
- package/template/skills/apm-write-frontend-plan/plan-template.md +0 -40
- package/template/skills/apm-write-plan/SKILL.md +0 -51
- package/template/skills/apm-write-plan/api-template.md +0 -35
- package/template/skills/apm-write-plan/plan-template.md +0 -72
- package/template/skills/apm-write-prd/SKILL.md +0 -14
- package/template/skills/apm-write-prd/template.md +0 -134
|
@@ -14,52 +14,6 @@ var requestConfig = {
|
|
|
14
14
|
path: "/cli/me"
|
|
15
15
|
}
|
|
16
16
|
),
|
|
17
|
-
sessionDetail: defineEndpoint({
|
|
18
|
-
method: "GET",
|
|
19
|
-
path: "/cli/sessions/detail"
|
|
20
|
-
}),
|
|
21
|
-
sessionMembers: defineEndpoint({
|
|
22
|
-
method: "GET",
|
|
23
|
-
path: "/cli/sessions/members"
|
|
24
|
-
}),
|
|
25
|
-
listSessionMessages: defineEndpoint({
|
|
26
|
-
method: "GET",
|
|
27
|
-
path: "/cli/messages"
|
|
28
|
-
}),
|
|
29
|
-
listDocuments: defineEndpoint({
|
|
30
|
-
method: "GET",
|
|
31
|
-
path: "/cli/documents"
|
|
32
|
-
}),
|
|
33
|
-
getDevGate: defineEndpoint({
|
|
34
|
-
method: "GET",
|
|
35
|
-
path: "/cli/dev-gate"
|
|
36
|
-
}),
|
|
37
|
-
listAttachments: defineEndpoint(
|
|
38
|
-
{
|
|
39
|
-
method: "GET",
|
|
40
|
-
path: "/cli/attachments"
|
|
41
|
-
}
|
|
42
|
-
),
|
|
43
|
-
upsertDocument: defineEndpoint({
|
|
44
|
-
method: "PUT",
|
|
45
|
-
path: "/cli/documents/upsert"
|
|
46
|
-
}),
|
|
47
|
-
appendMessageContent: defineEndpoint({
|
|
48
|
-
method: "PUT",
|
|
49
|
-
path: "/cli/messages/content"
|
|
50
|
-
}),
|
|
51
|
-
setMessageError: defineEndpoint({
|
|
52
|
-
method: "PUT",
|
|
53
|
-
path: "/cli/messages/error"
|
|
54
|
-
}),
|
|
55
|
-
upsertCursorMessageLog: defineEndpoint({
|
|
56
|
-
method: "PUT",
|
|
57
|
-
path: "/cli/cursor-message-logs"
|
|
58
|
-
}),
|
|
59
|
-
updateMessageStatus: defineEndpoint({
|
|
60
|
-
method: "PUT",
|
|
61
|
-
path: "/cli/messages/status"
|
|
62
|
-
}),
|
|
63
17
|
webideAppendMessageContent: defineEndpoint({
|
|
64
18
|
method: "PUT",
|
|
65
19
|
path: "/cli/webide/messages/content"
|
|
@@ -120,44 +74,20 @@ var requestConfig = {
|
|
|
120
74
|
method: "PUT",
|
|
121
75
|
path: "/cli/webide/terminals/meta"
|
|
122
76
|
}),
|
|
123
|
-
branchBaseline: defineEndpoint({
|
|
124
|
-
method: "GET",
|
|
125
|
-
path: "/cli/tasks/branch-baseline"
|
|
126
|
-
}),
|
|
127
77
|
projectBaseBranch: defineEndpoint(
|
|
128
78
|
{
|
|
129
79
|
method: "GET",
|
|
130
80
|
path: "/cli/tasks/project-base-branch"
|
|
131
81
|
}
|
|
132
82
|
),
|
|
133
|
-
listSessionsForBranchCleanup: defineEndpoint({
|
|
134
|
-
method: "GET",
|
|
135
|
-
path: "/cli/sessions/branch-cleanup"
|
|
136
|
-
}),
|
|
137
83
|
workspaceBaseline: defineEndpoint({
|
|
138
84
|
method: "GET",
|
|
139
85
|
path: "/cli/workspaces/baseline"
|
|
140
86
|
}),
|
|
141
|
-
getDeploymentConfiguration: defineEndpoint({
|
|
142
|
-
method: "GET",
|
|
143
|
-
path: "/cli/deployment-configurations"
|
|
144
|
-
}),
|
|
145
87
|
matchRepository: defineEndpoint({
|
|
146
88
|
method: "GET",
|
|
147
89
|
path: "/cli/repositories/match"
|
|
148
90
|
}),
|
|
149
|
-
listSkills: defineEndpoint({
|
|
150
|
-
method: "GET",
|
|
151
|
-
path: "/cli/skills"
|
|
152
|
-
}),
|
|
153
|
-
listRules: defineEndpoint({
|
|
154
|
-
method: "GET",
|
|
155
|
-
path: "/cli/rules"
|
|
156
|
-
}),
|
|
157
|
-
createPullRequest: defineEndpoint({
|
|
158
|
-
method: "POST",
|
|
159
|
-
path: "/cli/pull-requests"
|
|
160
|
-
}),
|
|
161
91
|
createWebIdeDraftPullRequest: defineEndpoint({
|
|
162
92
|
method: "POST",
|
|
163
93
|
path: "/cli/webide/draft-pull-requests"
|
|
@@ -338,6 +268,147 @@ function requireRemoteWorkdir(workdir) {
|
|
|
338
268
|
return resolveWorkdirPath(trimmed);
|
|
339
269
|
}
|
|
340
270
|
|
|
271
|
+
// src/command-utils.ts
|
|
272
|
+
import { copyFileSync, existsSync, mkdirSync as mkdirSync2, readdirSync, statSync } from "fs";
|
|
273
|
+
import { dirname, join as join2, resolve as resolve2 } from "path";
|
|
274
|
+
import { fileURLToPath } from "url";
|
|
275
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
276
|
+
var CLI_TEMPLATE_DIR = resolve2(__dirname, "../template");
|
|
277
|
+
function workspaceApmDir(cwd = resolveWorkdirPath()) {
|
|
278
|
+
return resolve2(resolve2(cwd), ".apm");
|
|
279
|
+
}
|
|
280
|
+
function isWorkspaceApmInitialized(workdir) {
|
|
281
|
+
const apmDir = workspaceApmDir(workdir);
|
|
282
|
+
const fsApmDir = toFsPath(apmDir);
|
|
283
|
+
if (!existsSync(fsApmDir)) {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
const st = statSync(fsApmDir);
|
|
287
|
+
if (!st.isDirectory()) {
|
|
288
|
+
throw new Error(
|
|
289
|
+
`\u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u4E0B\u7684 .apm \u4E0D\u662F\u76EE\u5F55\uFF0C\u8BF7\u68C0\u67E5\u672C\u5730\u63A5\u5165\u72B6\u6001\u3002`
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
return readdirSync(fsApmDir).length > 0;
|
|
293
|
+
}
|
|
294
|
+
var WEBIDE_SUBDIR = "webide";
|
|
295
|
+
var WEBIDE_ATTACHMENTS_SUBDIR = "attachments";
|
|
296
|
+
function webideTaskDir(taskId, workdir) {
|
|
297
|
+
return join2(workdir, ".apm", WEBIDE_SUBDIR, taskId);
|
|
298
|
+
}
|
|
299
|
+
function webideAttachmentsDir(taskId, workdir) {
|
|
300
|
+
return join2(webideTaskDir(taskId, workdir), WEBIDE_ATTACHMENTS_SUBDIR);
|
|
301
|
+
}
|
|
302
|
+
async function ensureLoggedConfig() {
|
|
303
|
+
const cfg = await ensureApmConfig();
|
|
304
|
+
if (!resolveApiKey(cfg)) {
|
|
305
|
+
console.error(
|
|
306
|
+
"[apm] \u672A\u68C0\u6D4B\u5230 API Key\uFF0C\u8BF7\u5148\u6267\u884C: apm login --api-key cm_xxx"
|
|
307
|
+
);
|
|
308
|
+
process.exit(1);
|
|
309
|
+
}
|
|
310
|
+
return cfg;
|
|
311
|
+
}
|
|
312
|
+
async function ensureDirExists(dir) {
|
|
313
|
+
mkdirSync2(dir, { recursive: true });
|
|
314
|
+
}
|
|
315
|
+
async function ensureWorkspaceApmDirForInit(cwd = resolveWorkdirPath()) {
|
|
316
|
+
const dir = workspaceApmDir(cwd);
|
|
317
|
+
const fsDir = toFsPath(dir);
|
|
318
|
+
if (existsSync(fsDir) && !statSync(fsDir).isDirectory()) {
|
|
319
|
+
throw new Error(`[apm] \u8DEF\u5F84\u5DF2\u5B58\u5728\u4F46\u4E0D\u662F\u76EE\u5F55: ${dir}`);
|
|
320
|
+
}
|
|
321
|
+
mkdirSync2(fsDir, { recursive: true });
|
|
322
|
+
}
|
|
323
|
+
var WEBIDE_AGENTS_TEMPLATE_NAME = "AGENTS.webide.md";
|
|
324
|
+
function shouldSkipTemplateEntry(name) {
|
|
325
|
+
return name === ".DS_Store" || name === "Thumbs.db";
|
|
326
|
+
}
|
|
327
|
+
function isWebIdeRuleFileName(name) {
|
|
328
|
+
return name.startsWith("webide");
|
|
329
|
+
}
|
|
330
|
+
function copyTemplateEntry(src, dest) {
|
|
331
|
+
const fsSrc = toFsPath(src);
|
|
332
|
+
const fsDest = toFsPath(dest);
|
|
333
|
+
const st = statSync(fsSrc);
|
|
334
|
+
if (st.isDirectory()) {
|
|
335
|
+
mkdirSync2(fsDest, { recursive: true });
|
|
336
|
+
for (const name of readdirSync(fsSrc)) {
|
|
337
|
+
if (shouldSkipTemplateEntry(name)) continue;
|
|
338
|
+
copyTemplateEntry(join2(src, name), join2(dest, name));
|
|
339
|
+
}
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
if (!st.isFile()) return;
|
|
343
|
+
mkdirSync2(toFsPath(dirname(dest)), { recursive: true });
|
|
344
|
+
copyFileSync(fsSrc, fsDest);
|
|
345
|
+
}
|
|
346
|
+
function listWebIdeRuleFileNames(rulesDir) {
|
|
347
|
+
const fsRulesDir = toFsPath(rulesDir);
|
|
348
|
+
if (!existsSync(fsRulesDir) || !statSync(fsRulesDir).isDirectory()) {
|
|
349
|
+
return [];
|
|
350
|
+
}
|
|
351
|
+
return readdirSync(fsRulesDir).filter((name) => {
|
|
352
|
+
if (shouldSkipTemplateEntry(name) || !isWebIdeRuleFileName(name)) {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
return statSync(toFsPath(join2(rulesDir, name))).isFile();
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
function assertWebIdeTemplateCopiedToApm(apmDir, workdir) {
|
|
359
|
+
const required = ["AGENTS.md", "rules"];
|
|
360
|
+
for (const item of required) {
|
|
361
|
+
const path = join2(apmDir, item);
|
|
362
|
+
if (!existsSync(toFsPath(path))) {
|
|
363
|
+
throw new Error(`[apm] WebIDE \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11: ${path}`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const rulesDir = join2(apmDir, "rules");
|
|
367
|
+
const ruleNames = listWebIdeRuleFileNames(rulesDir);
|
|
368
|
+
if (ruleNames.length === 0) {
|
|
369
|
+
throw new Error(`[apm] WebIDE \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11 webide \u89C4\u5219: ${rulesDir}`);
|
|
370
|
+
}
|
|
371
|
+
const leakedRules = join2(workdir, "rules");
|
|
372
|
+
const apmRules = join2(apmDir, "rules");
|
|
373
|
+
if (existsSync(toFsPath(leakedRules)) && !existsSync(toFsPath(join2(apmRules, "webide_reply.md")))) {
|
|
374
|
+
throw new Error(
|
|
375
|
+
`[apm] \u6A21\u677F\u88AB\u590D\u5236\u5230\u9519\u8BEF\u4F4D\u7F6E: ${leakedRules}\uFF08\u5E94\u5728 ${apmRules}\uFF09`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
async function copyWebIdeTemplateFiles(targetDir, workdir = resolveWorkdirPath()) {
|
|
380
|
+
const resolvedTarget = resolve2(targetDir);
|
|
381
|
+
const templateDir = resolve2(CLI_TEMPLATE_DIR);
|
|
382
|
+
const fsTemplateDir = toFsPath(templateDir);
|
|
383
|
+
if (!existsSync(fsTemplateDir) || !statSync(fsTemplateDir).isDirectory()) {
|
|
384
|
+
throw new Error(`[apm] \u672A\u627E\u5230 CLI \u6A21\u677F\u76EE\u5F55: ${templateDir}`);
|
|
385
|
+
}
|
|
386
|
+
const agentsSrc = join2(templateDir, WEBIDE_AGENTS_TEMPLATE_NAME);
|
|
387
|
+
if (!existsSync(toFsPath(agentsSrc))) {
|
|
388
|
+
throw new Error(`[apm] \u672A\u627E\u5230 WebIDE \u6307\u5357: ${agentsSrc}`);
|
|
389
|
+
}
|
|
390
|
+
const rulesSrc = join2(templateDir, "rules");
|
|
391
|
+
const ruleNames = listWebIdeRuleFileNames(rulesSrc);
|
|
392
|
+
if (ruleNames.length === 0) {
|
|
393
|
+
throw new Error(`[apm] CLI \u6A21\u677F\u4E2D\u6CA1\u6709 webide \u89C4\u5219: ${rulesSrc}`);
|
|
394
|
+
}
|
|
395
|
+
mkdirSync2(toFsPath(resolvedTarget), { recursive: true });
|
|
396
|
+
copyFileSync(
|
|
397
|
+
toFsPath(agentsSrc),
|
|
398
|
+
toFsPath(join2(resolvedTarget, "AGENTS.md"))
|
|
399
|
+
);
|
|
400
|
+
const rulesDest = join2(resolvedTarget, "rules");
|
|
401
|
+
mkdirSync2(toFsPath(rulesDest), { recursive: true });
|
|
402
|
+
for (const name of ruleNames) {
|
|
403
|
+
copyTemplateEntry(join2(rulesSrc, name), join2(rulesDest, name));
|
|
404
|
+
}
|
|
405
|
+
assertWebIdeTemplateCopiedToApm(resolvedTarget, resolve2(workdir));
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// src/git-utils.ts
|
|
409
|
+
import { execFile as execFile2 } from "child_process";
|
|
410
|
+
import { promisify as promisify2 } from "util";
|
|
411
|
+
|
|
341
412
|
// src/git-remote.ts
|
|
342
413
|
import { execFile } from "child_process";
|
|
343
414
|
import { promisify } from "util";
|
|
@@ -392,8 +463,6 @@ async function tryReadHttpsGitOriginUrl(cwd) {
|
|
|
392
463
|
}
|
|
393
464
|
|
|
394
465
|
// src/git-utils.ts
|
|
395
|
-
import { execFile as execFile2 } from "child_process";
|
|
396
|
-
import { promisify as promisify2 } from "util";
|
|
397
466
|
var execFileAsync2 = promisify2(execFile2);
|
|
398
467
|
function gitExecEnv() {
|
|
399
468
|
const existing = process.env.GIT_SSH_COMMAND?.trim();
|
|
@@ -491,7 +560,7 @@ async function branchHasChangesAgainstBase(cwd, baseBranch, headBranch) {
|
|
|
491
560
|
return true;
|
|
492
561
|
}
|
|
493
562
|
}
|
|
494
|
-
var FEATURE_BRANCH_RE = /^feat\/
|
|
563
|
+
var FEATURE_BRANCH_RE = /^feat\/task-.+/;
|
|
495
564
|
function isFeatureBranchName(branch) {
|
|
496
565
|
return FEATURE_BRANCH_RE.test(branch.trim().replace(/^origin\//, ""));
|
|
497
566
|
}
|
|
@@ -601,132 +670,6 @@ async function pushCurrentBranch(cwd) {
|
|
|
601
670
|
return true;
|
|
602
671
|
}
|
|
603
672
|
|
|
604
|
-
// src/command-utils.ts
|
|
605
|
-
import { copyFileSync, existsSync, mkdirSync as mkdirSync2, readdirSync, statSync } from "fs";
|
|
606
|
-
import { basename, dirname, extname, join as join2, resolve as resolve2 } from "path";
|
|
607
|
-
import { fileURLToPath } from "url";
|
|
608
|
-
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
609
|
-
var CLI_TEMPLATE_DIR = resolve2(__dirname, "../template");
|
|
610
|
-
function workspaceApmDir(cwd = resolveWorkdirPath()) {
|
|
611
|
-
return resolve2(resolve2(cwd), ".apm");
|
|
612
|
-
}
|
|
613
|
-
function isWorkspaceApmInitialized(workdir) {
|
|
614
|
-
const apmDir = workspaceApmDir(workdir);
|
|
615
|
-
const fsApmDir = toFsPath(apmDir);
|
|
616
|
-
if (!existsSync(fsApmDir)) {
|
|
617
|
-
return false;
|
|
618
|
-
}
|
|
619
|
-
const st = statSync(fsApmDir);
|
|
620
|
-
if (!st.isDirectory()) {
|
|
621
|
-
throw new Error(
|
|
622
|
-
`\u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u4E0B\u7684 .apm \u4E0D\u662F\u76EE\u5F55\uFF0C\u8BF7\u68C0\u67E5\u672C\u5730\u63A5\u5165\u72B6\u6001\u3002`
|
|
623
|
-
);
|
|
624
|
-
}
|
|
625
|
-
return readdirSync(fsApmDir).length > 0;
|
|
626
|
-
}
|
|
627
|
-
var SESSION_ATTACHMENTS_SUBDIR = "attachments";
|
|
628
|
-
var WEBIDE_SUBDIR = "webide";
|
|
629
|
-
function webideTaskDir(taskId, workdir) {
|
|
630
|
-
return join2(workdir, ".apm", WEBIDE_SUBDIR, taskId);
|
|
631
|
-
}
|
|
632
|
-
function webideAttachmentsDir(taskId, workdir) {
|
|
633
|
-
return join2(webideTaskDir(taskId, workdir), SESSION_ATTACHMENTS_SUBDIR);
|
|
634
|
-
}
|
|
635
|
-
async function ensureLoggedConfig() {
|
|
636
|
-
const cfg = await ensureApmConfig();
|
|
637
|
-
if (!resolveApiKey(cfg)) {
|
|
638
|
-
console.error(
|
|
639
|
-
"[apm] \u672A\u68C0\u6D4B\u5230 API Key\uFF0C\u8BF7\u5148\u6267\u884C: apm login --api-key cm_xxx"
|
|
640
|
-
);
|
|
641
|
-
process.exit(1);
|
|
642
|
-
}
|
|
643
|
-
return cfg;
|
|
644
|
-
}
|
|
645
|
-
async function ensureDirExists(dir) {
|
|
646
|
-
mkdirSync2(dir, { recursive: true });
|
|
647
|
-
}
|
|
648
|
-
async function ensureWorkspaceApmDirForInit(cwd = resolveWorkdirPath()) {
|
|
649
|
-
const dir = workspaceApmDir(cwd);
|
|
650
|
-
const fsDir = toFsPath(dir);
|
|
651
|
-
if (existsSync(fsDir) && !statSync(fsDir).isDirectory()) {
|
|
652
|
-
throw new Error(`[apm] \u8DEF\u5F84\u5DF2\u5B58\u5728\u4F46\u4E0D\u662F\u76EE\u5F55: ${dir}`);
|
|
653
|
-
}
|
|
654
|
-
mkdirSync2(fsDir, { recursive: true });
|
|
655
|
-
}
|
|
656
|
-
var WORKSPACE_TEMPLATE_SUBDIRS = [
|
|
657
|
-
"sessions",
|
|
658
|
-
"skills",
|
|
659
|
-
"rules",
|
|
660
|
-
"deploy"
|
|
661
|
-
];
|
|
662
|
-
function shouldSkipTemplateEntry(name) {
|
|
663
|
-
return name === ".DS_Store" || name === "Thumbs.db";
|
|
664
|
-
}
|
|
665
|
-
function copyTemplateEntry(src, dest) {
|
|
666
|
-
const fsSrc = toFsPath(src);
|
|
667
|
-
const fsDest = toFsPath(dest);
|
|
668
|
-
const st = statSync(fsSrc);
|
|
669
|
-
if (st.isDirectory()) {
|
|
670
|
-
mkdirSync2(fsDest, { recursive: true });
|
|
671
|
-
for (const name of readdirSync(fsSrc)) {
|
|
672
|
-
if (shouldSkipTemplateEntry(name)) continue;
|
|
673
|
-
copyTemplateEntry(join2(src, name), join2(dest, name));
|
|
674
|
-
}
|
|
675
|
-
return;
|
|
676
|
-
}
|
|
677
|
-
if (!st.isFile()) return;
|
|
678
|
-
mkdirSync2(toFsPath(dirname(dest)), { recursive: true });
|
|
679
|
-
copyFileSync(fsSrc, fsDest);
|
|
680
|
-
}
|
|
681
|
-
function assertTemplateCopiedToApm(apmDir, workdir) {
|
|
682
|
-
const required = [
|
|
683
|
-
"AGENTS.md",
|
|
684
|
-
"apm.config.json",
|
|
685
|
-
"rules",
|
|
686
|
-
"skills",
|
|
687
|
-
"sessions"
|
|
688
|
-
];
|
|
689
|
-
for (const item of required) {
|
|
690
|
-
const path = join2(apmDir, item);
|
|
691
|
-
if (!existsSync(toFsPath(path))) {
|
|
692
|
-
throw new Error(`[apm] \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11: ${path}`);
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
const leakedRules = join2(workdir, "rules");
|
|
696
|
-
const apmRules = join2(apmDir, "rules");
|
|
697
|
-
if (existsSync(toFsPath(leakedRules)) && !existsSync(toFsPath(join2(apmRules, "reply.md")))) {
|
|
698
|
-
throw new Error(
|
|
699
|
-
`[apm] \u6A21\u677F\u88AB\u590D\u5236\u5230\u9519\u8BEF\u4F4D\u7F6E: ${leakedRules}\uFF08\u5E94\u5728 ${apmRules}\uFF09`
|
|
700
|
-
);
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
async function copyTemplateFiles(targetDir, workdir = resolveWorkdirPath()) {
|
|
704
|
-
const resolvedTarget = resolve2(targetDir);
|
|
705
|
-
const templateDir = resolve2(CLI_TEMPLATE_DIR);
|
|
706
|
-
const fsTemplateDir = toFsPath(templateDir);
|
|
707
|
-
if (!existsSync(fsTemplateDir)) {
|
|
708
|
-
throw new Error(`[apm] \u672A\u627E\u5230 CLI \u6A21\u677F\u76EE\u5F55: ${templateDir}`);
|
|
709
|
-
}
|
|
710
|
-
const dirStat = statSync(fsTemplateDir);
|
|
711
|
-
if (!dirStat.isDirectory()) {
|
|
712
|
-
throw new Error(`[apm] CLI \u6A21\u677F\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55: ${templateDir}`);
|
|
713
|
-
}
|
|
714
|
-
const entries = readdirSync(fsTemplateDir).filter(
|
|
715
|
-
(name) => !shouldSkipTemplateEntry(name)
|
|
716
|
-
);
|
|
717
|
-
if (entries.length === 0) {
|
|
718
|
-
throw new Error(`[apm] CLI \u6A21\u677F\u76EE\u5F55\u4E3A\u7A7A: ${templateDir}`);
|
|
719
|
-
}
|
|
720
|
-
mkdirSync2(toFsPath(resolvedTarget), { recursive: true });
|
|
721
|
-
for (const name of entries) {
|
|
722
|
-
copyTemplateEntry(join2(templateDir, name), join2(resolvedTarget, name));
|
|
723
|
-
}
|
|
724
|
-
for (const subdir of WORKSPACE_TEMPLATE_SUBDIRS) {
|
|
725
|
-
mkdirSync2(toFsPath(join2(resolvedTarget, subdir)), { recursive: true });
|
|
726
|
-
}
|
|
727
|
-
assertTemplateCopiedToApm(resolvedTarget, resolve2(workdir));
|
|
728
|
-
}
|
|
729
|
-
|
|
730
673
|
// src/workspace-repos.ts
|
|
731
674
|
import {
|
|
732
675
|
existsSync as existsSync2,
|
|
@@ -736,7 +679,7 @@ import {
|
|
|
736
679
|
statSync as statSync2,
|
|
737
680
|
writeFileSync as writeFileSync2
|
|
738
681
|
} from "fs";
|
|
739
|
-
import { basename
|
|
682
|
+
import { basename, dirname as dirname2, join as join3, relative, resolve as resolve3 } from "path";
|
|
740
683
|
var WORKSPACE_REPOS_MANIFEST = "workspace-repos.json";
|
|
741
684
|
var WORKSPACE_REPOS_VERSION = 1;
|
|
742
685
|
var SKIP_DIR_NAMES = /* @__PURE__ */ new Set([".apm", "node_modules"]);
|
|
@@ -960,7 +903,7 @@ function resolveWorkspaceRepoAbsolutePaths(manifest) {
|
|
|
960
903
|
}
|
|
961
904
|
function formatRepoLabel(workdir, repoAbs) {
|
|
962
905
|
const rel = toRelativeRepoPath(resolveWorkdirPath(workdir), repoAbs);
|
|
963
|
-
return rel === "." ?
|
|
906
|
+
return rel === "." ? basename(repoAbs) || repoAbs : rel;
|
|
964
907
|
}
|
|
965
908
|
|
|
966
909
|
// src/commands/branch.ts
|
|
@@ -1033,57 +976,47 @@ function branchNameForTask(taskId) {
|
|
|
1033
976
|
async function ensureFeatureBranch(branch, baselineBranch, options) {
|
|
1034
977
|
const cwd = options.cwd ?? process.cwd();
|
|
1035
978
|
const gitRoot = await resolveGitRepoRoot(cwd);
|
|
1036
|
-
const commitMessage = options.message?.trim() || `chore(apm): \u540C\u6B65\u5DE5\u4F5C\u533A (${branch})`;
|
|
1037
979
|
await ensureGitRepo(gitRoot);
|
|
1038
|
-
await ensureRemoteBaselineBranch(gitRoot, baselineBranch);
|
|
1039
980
|
const current = await getCurrentBranch(gitRoot);
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
await commitWorkingTreeIfDirty(gitRoot, commitMessage);
|
|
1044
|
-
} else {
|
|
1045
|
-
await execGit(gitRoot, [
|
|
1046
|
-
"stash",
|
|
1047
|
-
"push",
|
|
1048
|
-
"-u",
|
|
1049
|
-
"-m",
|
|
1050
|
-
`apm: switch to ${branch}`
|
|
1051
|
-
]);
|
|
1052
|
-
}
|
|
981
|
+
if (current === branch) {
|
|
982
|
+
console.log(`[apm] \u5DF2\u5728\u5206\u652F ${branch}\uFF0C\u8DF3\u8FC7`);
|
|
983
|
+
return branch;
|
|
1053
984
|
}
|
|
1054
|
-
|
|
1055
|
-
if (
|
|
1056
|
-
await execGit(gitRoot, [
|
|
1057
|
-
|
|
985
|
+
await ensureRemoteBaselineBranch(gitRoot, baselineBranch);
|
|
986
|
+
if (await isWorkingTreeDirty(gitRoot)) {
|
|
987
|
+
await execGit(gitRoot, [
|
|
988
|
+
"stash",
|
|
989
|
+
"push",
|
|
990
|
+
"-u",
|
|
991
|
+
"-m",
|
|
992
|
+
`apm: switch to ${branch}`
|
|
993
|
+
]);
|
|
994
|
+
}
|
|
995
|
+
const remoteExists = await remoteBranchExists(gitRoot, branch);
|
|
996
|
+
if (remoteExists) {
|
|
997
|
+
await execGit(gitRoot, ["fetch", "origin", branch]);
|
|
998
|
+
await execGit(gitRoot, ["checkout", "-B", branch, `origin/${branch}`]);
|
|
999
|
+
} else if (await localBranchExists(gitRoot, branch)) {
|
|
1000
|
+
await execGit(gitRoot, ["checkout", branch]);
|
|
1001
|
+
console.log(`[apm] \u5206\u652F ${branch} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u521B\u5EFA`);
|
|
1058
1002
|
} else {
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
await execGit(gitRoot, [
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
}
|
|
1069
|
-
await
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
"checkout",
|
|
1073
|
-
"-b",
|
|
1074
|
-
branch,
|
|
1075
|
-
`origin/${baselineBranch}`
|
|
1076
|
-
]);
|
|
1077
|
-
await execGit(gitRoot, ["push", "-u", "origin", branch]);
|
|
1078
|
-
} catch (err) {
|
|
1079
|
-
if (await localBranchExists(gitRoot, branch)) {
|
|
1080
|
-
if (await getCurrentBranch(gitRoot) !== branch) {
|
|
1081
|
-
await execGit(gitRoot, ["checkout", branch]);
|
|
1082
|
-
}
|
|
1083
|
-
console.log(`[apm] \u5206\u652F ${branch} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u521B\u5EFA`);
|
|
1084
|
-
} else {
|
|
1085
|
-
throw err;
|
|
1003
|
+
await execGit(gitRoot, ["fetch", "origin", baselineBranch]);
|
|
1004
|
+
try {
|
|
1005
|
+
await execGit(gitRoot, [
|
|
1006
|
+
"checkout",
|
|
1007
|
+
"-b",
|
|
1008
|
+
branch,
|
|
1009
|
+
`origin/${baselineBranch}`
|
|
1010
|
+
]);
|
|
1011
|
+
await execGit(gitRoot, ["push", "-u", "origin", branch]);
|
|
1012
|
+
} catch (err) {
|
|
1013
|
+
if (await localBranchExists(gitRoot, branch)) {
|
|
1014
|
+
if (await getCurrentBranch(gitRoot) !== branch) {
|
|
1015
|
+
await execGit(gitRoot, ["checkout", branch]);
|
|
1086
1016
|
}
|
|
1017
|
+
console.log(`[apm] \u5206\u652F ${branch} \u5DF2\u5B58\u5728\uFF0C\u8DF3\u8FC7\u521B\u5EFA`);
|
|
1018
|
+
} else {
|
|
1019
|
+
throw err;
|
|
1087
1020
|
}
|
|
1088
1021
|
}
|
|
1089
1022
|
}
|
|
@@ -1102,9 +1035,7 @@ async function runTaskBranch(taskId, options = {}) {
|
|
|
1102
1035
|
});
|
|
1103
1036
|
const baselineBranch = projectBaseline.baseBranch?.trim() ?? "";
|
|
1104
1037
|
if (!baselineBranch) {
|
|
1105
|
-
throw new Error(
|
|
1106
|
-
"[apm] \u9879\u76EE\u672A\u914D\u7F6E\u57FA\u7840\u5206\u652F\uFF0C\u65E0\u6CD5\u521B\u5EFA WebIDE \u7279\u6027\u5206\u652F"
|
|
1107
|
-
);
|
|
1038
|
+
throw new Error("[apm] \u9879\u76EE\u672A\u914D\u7F6E\u57FA\u7840\u5206\u652F\uFF0C\u65E0\u6CD5\u521B\u5EFA WebIDE \u7279\u6027\u5206\u652F");
|
|
1108
1039
|
}
|
|
1109
1040
|
const cwd = options.cwd ?? process.cwd();
|
|
1110
1041
|
const workdirPath = resolveWorkdirPath(cwd);
|
|
@@ -1434,132 +1365,6 @@ ${JSON.stringify(event, null, 2)}
|
|
|
1434
1365
|
\`\`\``;
|
|
1435
1366
|
}
|
|
1436
1367
|
|
|
1437
|
-
// src/commands/connect/agent-session-registry.ts
|
|
1438
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
1439
|
-
import { dirname as dirname3, resolve as resolve4 } from "node:path";
|
|
1440
|
-
function registryPath(workdir, sessionId) {
|
|
1441
|
-
return resolve4(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
|
|
1442
|
-
}
|
|
1443
|
-
function readRegistry(path) {
|
|
1444
|
-
if (!existsSync3(path)) {
|
|
1445
|
-
return {};
|
|
1446
|
-
}
|
|
1447
|
-
try {
|
|
1448
|
-
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
1449
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1450
|
-
const result = {};
|
|
1451
|
-
for (const [key, value] of Object.entries(
|
|
1452
|
-
parsed
|
|
1453
|
-
)) {
|
|
1454
|
-
if (typeof value === "string" && value.trim()) {
|
|
1455
|
-
result[key] = value.trim();
|
|
1456
|
-
}
|
|
1457
|
-
}
|
|
1458
|
-
return result;
|
|
1459
|
-
}
|
|
1460
|
-
} catch {
|
|
1461
|
-
}
|
|
1462
|
-
return {};
|
|
1463
|
-
}
|
|
1464
|
-
function writeRegistry(path, registry) {
|
|
1465
|
-
mkdirSync4(dirname3(path), { recursive: true });
|
|
1466
|
-
writeFileSync3(path, `${JSON.stringify(registry, null, 2)}
|
|
1467
|
-
`, "utf8");
|
|
1468
|
-
}
|
|
1469
|
-
function loadSessionAgentId(workdir, sessionId, user) {
|
|
1470
|
-
const registry = readRegistry(registryPath(workdir, sessionId));
|
|
1471
|
-
return registry[user];
|
|
1472
|
-
}
|
|
1473
|
-
function saveSessionAgentId(workdir, sessionId, user, agentId) {
|
|
1474
|
-
const path = registryPath(workdir, sessionId);
|
|
1475
|
-
const registry = readRegistry(path);
|
|
1476
|
-
registry[user] = agentId;
|
|
1477
|
-
writeRegistry(path, registry);
|
|
1478
|
-
}
|
|
1479
|
-
function clearSessionAgentId(workdir, sessionId, user) {
|
|
1480
|
-
const path = registryPath(workdir, sessionId);
|
|
1481
|
-
const registry = readRegistry(path);
|
|
1482
|
-
if (!(user in registry)) {
|
|
1483
|
-
return;
|
|
1484
|
-
}
|
|
1485
|
-
delete registry[user];
|
|
1486
|
-
writeRegistry(path, registry);
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
// src/commands/connect/cursor-message-log.ts
|
|
1490
|
-
var CURSOR_MESSAGE_LOG_SYNC_INTERVAL_MS = 2e3;
|
|
1491
|
-
function createThrottledCursorMessageLogSync(cfg, ctx, onError) {
|
|
1492
|
-
let lastRunAt = 0;
|
|
1493
|
-
let timer;
|
|
1494
|
-
let latestSession;
|
|
1495
|
-
let syncChain = Promise.resolve();
|
|
1496
|
-
const syncDirtyEventsOnce = async (session) => {
|
|
1497
|
-
const events = session.getDirtyEvents();
|
|
1498
|
-
if (events.length === 0) {
|
|
1499
|
-
return;
|
|
1500
|
-
}
|
|
1501
|
-
lastRunAt = Date.now();
|
|
1502
|
-
try {
|
|
1503
|
-
await syncCursorMessageLog(cfg, ctx, events);
|
|
1504
|
-
session.clearDirtyIfUnchanged(events);
|
|
1505
|
-
} catch (err) {
|
|
1506
|
-
onError(err);
|
|
1507
|
-
}
|
|
1508
|
-
};
|
|
1509
|
-
const drainDirtyEvents = async (session) => {
|
|
1510
|
-
while (session.getDirtyEvents().length > 0) {
|
|
1511
|
-
await syncDirtyEventsOnce(session);
|
|
1512
|
-
}
|
|
1513
|
-
};
|
|
1514
|
-
const enqueueSync = (session) => {
|
|
1515
|
-
syncChain = syncChain.then(() => drainDirtyEvents(session));
|
|
1516
|
-
};
|
|
1517
|
-
return {
|
|
1518
|
-
schedule(session) {
|
|
1519
|
-
latestSession = session;
|
|
1520
|
-
const now = Date.now();
|
|
1521
|
-
const elapsed = now - lastRunAt;
|
|
1522
|
-
if (elapsed >= CURSOR_MESSAGE_LOG_SYNC_INTERVAL_MS) {
|
|
1523
|
-
if (timer) {
|
|
1524
|
-
clearTimeout(timer);
|
|
1525
|
-
timer = void 0;
|
|
1526
|
-
}
|
|
1527
|
-
enqueueSync(session);
|
|
1528
|
-
return;
|
|
1529
|
-
}
|
|
1530
|
-
if (timer) {
|
|
1531
|
-
return;
|
|
1532
|
-
}
|
|
1533
|
-
timer = setTimeout(() => {
|
|
1534
|
-
timer = void 0;
|
|
1535
|
-
enqueueSync(latestSession);
|
|
1536
|
-
}, CURSOR_MESSAGE_LOG_SYNC_INTERVAL_MS - elapsed);
|
|
1537
|
-
},
|
|
1538
|
-
async flush(session) {
|
|
1539
|
-
latestSession = session;
|
|
1540
|
-
if (timer) {
|
|
1541
|
-
clearTimeout(timer);
|
|
1542
|
-
timer = void 0;
|
|
1543
|
-
}
|
|
1544
|
-
await syncChain;
|
|
1545
|
-
await drainDirtyEvents(session);
|
|
1546
|
-
}
|
|
1547
|
-
};
|
|
1548
|
-
}
|
|
1549
|
-
async function syncCursorMessageLog(cfg, ctx, events) {
|
|
1550
|
-
const agentId = ctx.agentId.trim();
|
|
1551
|
-
if (!agentId || events.length === 0) {
|
|
1552
|
-
return;
|
|
1553
|
-
}
|
|
1554
|
-
const api = createApmApiClient(cfg);
|
|
1555
|
-
await api.cli.upsertCursorMessageLog({
|
|
1556
|
-
sessionId: ctx.sessionId,
|
|
1557
|
-
messageId: ctx.messageId,
|
|
1558
|
-
agentId,
|
|
1559
|
-
events
|
|
1560
|
-
});
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
1368
|
// src/commands/connect/abort-signal-debug.ts
|
|
1564
1369
|
import {
|
|
1565
1370
|
getEventListeners,
|
|
@@ -1623,25 +1428,11 @@ ${stack}`
|
|
|
1623
1428
|
);
|
|
1624
1429
|
}
|
|
1625
1430
|
|
|
1626
|
-
// src/commands/append-message.ts
|
|
1627
|
-
async function appendMessageContent(cfg, messageId, content) {
|
|
1628
|
-
const trimmedId = messageId.trim();
|
|
1629
|
-
if (!trimmedId) {
|
|
1630
|
-
throw new Error("messageId \u4E0D\u80FD\u4E3A\u7A7A");
|
|
1631
|
-
}
|
|
1632
|
-
if (!content) {
|
|
1633
|
-
throw new Error("content \u4E0D\u80FD\u4E3A\u7A7A");
|
|
1634
|
-
}
|
|
1635
|
-
const api = createApmApiClient(cfg);
|
|
1636
|
-
await api.cli.appendMessageContent({ id: trimmedId, content });
|
|
1637
|
-
}
|
|
1638
|
-
|
|
1639
1431
|
// src/commands/connect/append-message-tool.ts
|
|
1640
|
-
function createAppendMessageCustomTools(
|
|
1641
|
-
const doAppend = appendContent2 ?? ((content) => appendMessageContent(cfg, messageId, content));
|
|
1432
|
+
function createAppendMessageCustomTools(messageId, appendContent2) {
|
|
1642
1433
|
return {
|
|
1643
1434
|
AppendMessage: {
|
|
1644
|
-
description: "\u5411\u5F53\u524D
|
|
1435
|
+
description: "\u5411\u5F53\u524D WebIDE \u6D88\u606F\u8FFD\u52A0\u7528\u6237\u53EF\u89C1\u56DE\u590D\u3002\u53EF\u591A\u6B21\u8C03\u7528\u8865\u5145\u8FDB\u5C55\uFF1B\u88AB @ \u65F6\u6536\u5230\u540E\u5E94\u5148\u7B80\u77ED\u786E\u8BA4\u518D\u6267\u884C\u4EFB\u52A1\u3002\u7ED3\u8BBA\u4E0E\u8FDB\u5C55\u5E94\u5199\u5728\u672C\u5DE5\u5177\u4E2D\uFF1B\u6700\u7EC8\u81EA\u7136\u8BED\u8A00\u6536\u5C3E\u52FF\u518D\u590D\u8FF0\u6B64\u5904\u5185\u5BB9\u3002",
|
|
1645
1436
|
inputSchema: {
|
|
1646
1437
|
type: "object",
|
|
1647
1438
|
properties: {
|
|
@@ -1661,7 +1452,7 @@ function createAppendMessageCustomTools(cfg, messageId, appendContent2) {
|
|
|
1661
1452
|
};
|
|
1662
1453
|
}
|
|
1663
1454
|
try {
|
|
1664
|
-
await
|
|
1455
|
+
await appendContent2(content);
|
|
1665
1456
|
console.log(`[apm] AppendMessage \u5DF2\u8FFD\u52A0: messageId=${messageId}`);
|
|
1666
1457
|
return "\u5DF2\u8FFD\u52A0\u6D88\u606F\u5185\u5BB9";
|
|
1667
1458
|
} catch (err) {
|
|
@@ -2081,11 +1872,7 @@ var WORKSPACE_BOUNDARY_HINT = `[\u5DE5\u4F5C\u533A\u8FB9\u754C]
|
|
|
2081
1872
|
\u7981\u6B62\u8BBF\u95EE\u5DE5\u4F5C\u533A\u5916\u8DEF\u5F84\uFF08\u5982\u5176\u5B83\u9879\u76EE\u3001~/.cursor\u3001agent-transcripts\u3001\u7CFB\u7EDF\u76EE\u5F55\u7B49\uFF09\u3002`;
|
|
2082
1873
|
function createCursorCustomTools(cfg, messageId, options) {
|
|
2083
1874
|
const tools = {
|
|
2084
|
-
...createAppendMessageCustomTools(
|
|
2085
|
-
cfg,
|
|
2086
|
-
messageId,
|
|
2087
|
-
options?.appendMessageContent
|
|
2088
|
-
),
|
|
1875
|
+
...createAppendMessageCustomTools(messageId, options.appendMessageContent),
|
|
2089
1876
|
AskQuestion: createAskQuestionTool({
|
|
2090
1877
|
onInvoke: options?.onAskQuestion,
|
|
2091
1878
|
execute: options?.askQuestionExecute
|
|
@@ -2145,11 +1932,6 @@ var noopRemoteLogSync = {
|
|
|
2145
1932
|
async flush(_session) {
|
|
2146
1933
|
}
|
|
2147
1934
|
};
|
|
2148
|
-
var logCtx = (ctx, agentId) => ({
|
|
2149
|
-
sessionId: ctx.sessionId,
|
|
2150
|
-
messageId: ctx.messageId,
|
|
2151
|
-
agentId
|
|
2152
|
-
});
|
|
2153
1935
|
function formatCursorRunFailure(runId, options) {
|
|
2154
1936
|
const details = [
|
|
2155
1937
|
options?.statusError?.trim(),
|
|
@@ -2163,19 +1945,6 @@ function formatCursorRunFailure(runId, options) {
|
|
|
2163
1945
|
}
|
|
2164
1946
|
return `Cursor run \u5931\u8D25: ${runId} \u2014 ${details.join("\uFF1B")}`;
|
|
2165
1947
|
}
|
|
2166
|
-
function persistSessionAgentId(ctx, agentId) {
|
|
2167
|
-
if (ctx.skipSessionAgentRegistry || !ctx.user) return;
|
|
2168
|
-
saveSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user, agentId);
|
|
2169
|
-
}
|
|
2170
|
-
function invalidatePersistedAgentId(ctx) {
|
|
2171
|
-
if (ctx.skipSessionAgentRegistry) {
|
|
2172
|
-
ctx.onInvalidatePersistedAgentId?.();
|
|
2173
|
-
return;
|
|
2174
|
-
}
|
|
2175
|
-
if (ctx.user) {
|
|
2176
|
-
clearSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user);
|
|
2177
|
-
}
|
|
2178
|
-
}
|
|
2179
1948
|
async function obtainAgent(ctx) {
|
|
2180
1949
|
const agentOptions = {
|
|
2181
1950
|
apiKey: ctx.apiKey,
|
|
@@ -2192,29 +1961,22 @@ async function obtainAgent(ctx) {
|
|
|
2192
1961
|
...ctx.mode ? { mode: ctx.mode } : {},
|
|
2193
1962
|
...ctx.mcpServers ? { mcpServers: ctx.mcpServers } : {}
|
|
2194
1963
|
};
|
|
2195
|
-
const
|
|
2196
|
-
const savedAgentId = explicitAgentId || (!ctx.skipSessionAgentRegistry && ctx.user ? loadSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user) : void 0);
|
|
1964
|
+
const savedAgentId = ctx.resumeAgentId?.trim();
|
|
2197
1965
|
if (savedAgentId) {
|
|
2198
1966
|
try {
|
|
2199
1967
|
const agent = await Agent.resume(savedAgentId, agentOptions);
|
|
2200
|
-
console.log(
|
|
2201
|
-
`[apm] \u590D\u7528 Agent user=${ctx.user} agentId=${savedAgentId}${explicitAgentId ? "\uFF08\u53C2\u6570\u6307\u5B9A\uFF09" : ""}`
|
|
2202
|
-
);
|
|
2203
|
-
persistSessionAgentId(ctx, agent.agentId);
|
|
1968
|
+
console.log(`[apm] \u590D\u7528 Agent user=${ctx.user} agentId=${savedAgentId}`);
|
|
2204
1969
|
return { agent, resumed: true };
|
|
2205
1970
|
} catch (err) {
|
|
2206
1971
|
console.warn(
|
|
2207
1972
|
`[apm] \u590D\u7528 Agent \u5931\u8D25\uFF08agentId=${savedAgentId}\uFF09\uFF0C\u56DE\u9000\u4E3A\u65B0\u5EFA:`,
|
|
2208
1973
|
err instanceof Error ? err.message : err
|
|
2209
1974
|
);
|
|
2210
|
-
|
|
2211
|
-
invalidatePersistedAgentId(ctx);
|
|
2212
|
-
}
|
|
1975
|
+
ctx.onInvalidatePersistedAgentId?.();
|
|
2213
1976
|
}
|
|
2214
1977
|
}
|
|
2215
1978
|
try {
|
|
2216
1979
|
const agent = await Agent.create(agentOptions);
|
|
2217
|
-
persistSessionAgentId(ctx, agent.agentId);
|
|
2218
1980
|
return { agent, resumed: false };
|
|
2219
1981
|
} catch (err) {
|
|
2220
1982
|
if (ctx.enableSandbox && err instanceof Error && /sandbox/i.test(err.message)) {
|
|
@@ -2225,7 +1987,7 @@ async function obtainAgent(ctx) {
|
|
|
2225
1987
|
}
|
|
2226
1988
|
}
|
|
2227
1989
|
async function runCursorAgent(cfg, ctx, options) {
|
|
2228
|
-
const signal = options
|
|
1990
|
+
const signal = options.signal;
|
|
2229
1991
|
logAbortSignalStats(signal, "runCursorAgent:start");
|
|
2230
1992
|
if (signal?.aborted) {
|
|
2231
1993
|
throw new Error("\u8FDE\u63A5\u5DF2\u5173\u95ED\uFF0C\u4EFB\u52A1\u4E2D\u65AD");
|
|
@@ -2236,54 +1998,36 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2236
1998
|
}
|
|
2237
1999
|
const workdir = resolveWorkdirPath(ctx.workdir);
|
|
2238
2000
|
const customTools = createCursorCustomTools(cfg, ctx.messageId, {
|
|
2239
|
-
onAskQuestion: options
|
|
2240
|
-
appendMessageContent: options
|
|
2241
|
-
askQuestionExecute: options
|
|
2242
|
-
enableWebIdePlanTools: options
|
|
2243
|
-
taskId: options
|
|
2001
|
+
onAskQuestion: options.onAskQuestion,
|
|
2002
|
+
appendMessageContent: options.appendMessageContent,
|
|
2003
|
+
askQuestionExecute: options.askQuestionExecute,
|
|
2004
|
+
enableWebIdePlanTools: options.enableWebIdePlanTools,
|
|
2005
|
+
taskId: options.taskId,
|
|
2244
2006
|
workdir,
|
|
2245
|
-
terminalRpc: options
|
|
2007
|
+
terminalRpc: options.terminalRpc
|
|
2246
2008
|
});
|
|
2247
|
-
const enableSandbox = Boolean(options
|
|
2009
|
+
const enableSandbox = Boolean(options.enableSandbox);
|
|
2248
2010
|
let prompt = withPlanModeToolHint(ctx.prompt, ctx.mode);
|
|
2249
2011
|
prompt = withWorkspaceBoundaryHint(prompt);
|
|
2250
2012
|
console.log(
|
|
2251
|
-
`[apm] Cursor Agent \u5F00\u59CB messageId=${ctx.messageId}
|
|
2013
|
+
`[apm] Cursor Agent \u5F00\u59CB messageId=${ctx.messageId} cwd=${workdir} sandbox=${enableSandbox}`
|
|
2252
2014
|
);
|
|
2253
2015
|
const { agent, resumed } = await obtainAgent({
|
|
2254
2016
|
apiKey,
|
|
2255
2017
|
model: ctx.model,
|
|
2256
2018
|
cwd: workdir,
|
|
2257
|
-
workdir,
|
|
2258
|
-
sessionId: ctx.sessionId,
|
|
2259
2019
|
user: ctx.user,
|
|
2260
2020
|
mode: ctx.mode,
|
|
2261
2021
|
resumeAgentId: ctx.resumeAgentId,
|
|
2262
2022
|
customTools,
|
|
2263
2023
|
enableSandbox,
|
|
2264
|
-
|
|
2265
|
-
onInvalidatePersistedAgentId: options?.onInvalidatePersistedAgentId
|
|
2024
|
+
onInvalidatePersistedAgentId: options.onInvalidatePersistedAgentId
|
|
2266
2025
|
});
|
|
2267
2026
|
const invalidateAgentMapping = () => {
|
|
2268
|
-
|
|
2269
|
-
workdir,
|
|
2270
|
-
sessionId: ctx.sessionId,
|
|
2271
|
-
user: ctx.user,
|
|
2272
|
-
skipSessionAgentRegistry: options?.skipSessionAgentRegistry,
|
|
2273
|
-
onInvalidatePersistedAgentId: options?.onInvalidatePersistedAgentId
|
|
2274
|
-
});
|
|
2027
|
+
options.onInvalidatePersistedAgentId?.();
|
|
2275
2028
|
};
|
|
2276
|
-
const eventSession = options
|
|
2277
|
-
const syncRemoteLog = options
|
|
2278
|
-
cfg,
|
|
2279
|
-
logCtx(ctx, agent.agentId),
|
|
2280
|
-
(err) => {
|
|
2281
|
-
console.warn(
|
|
2282
|
-
"[apm] \u540C\u6B65 Cursor \u6D88\u606F\u65E5\u5FD7\u5931\u8D25:",
|
|
2283
|
-
err instanceof Error ? err.message : err
|
|
2284
|
-
);
|
|
2285
|
-
}
|
|
2286
|
-
);
|
|
2029
|
+
const eventSession = options.eventSession ?? new EventSession(prompt);
|
|
2030
|
+
const syncRemoteLog = options.createRemoteLogSync ? options.createRemoteLogSync(agent.agentId) : noopRemoteLogSync;
|
|
2287
2031
|
let activeRun;
|
|
2288
2032
|
const abortRun = () => {
|
|
2289
2033
|
if (!activeRun?.supports("cancel")) return;
|
|
@@ -2295,14 +2039,14 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2295
2039
|
const run = await agent.send(prompt, {
|
|
2296
2040
|
...ctx.mode ? { mode: ctx.mode } : {},
|
|
2297
2041
|
local: {
|
|
2298
|
-
...options
|
|
2042
|
+
...options.forceSend ? { force: true } : {},
|
|
2299
2043
|
customTools
|
|
2300
2044
|
}
|
|
2301
2045
|
});
|
|
2302
2046
|
activeRun = run;
|
|
2303
2047
|
logAbortSignalStats(signal, "runCursorAgent:after-send");
|
|
2304
2048
|
console.log(`[apm] Cursor run id=${run.id} agentId=${agent.agentId}`);
|
|
2305
|
-
await options
|
|
2049
|
+
await options.onRunStarted?.({ agentId: agent.agentId, runId: run.id });
|
|
2306
2050
|
let lastRunErrorStatus;
|
|
2307
2051
|
for await (const event of run.stream()) {
|
|
2308
2052
|
if (signal?.aborted) {
|
|
@@ -2318,7 +2062,7 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2318
2062
|
);
|
|
2319
2063
|
}
|
|
2320
2064
|
}
|
|
2321
|
-
options
|
|
2065
|
+
options.onStreamEvent?.(event);
|
|
2322
2066
|
eventSession.addEvent(event);
|
|
2323
2067
|
syncRemoteLog.schedule(eventSession);
|
|
2324
2068
|
}
|
|
@@ -2395,17 +2139,17 @@ async function runCursorAgent(cfg, ctx, options) {
|
|
|
2395
2139
|
}
|
|
2396
2140
|
|
|
2397
2141
|
// src/commands/connect/webide-agent-registry.ts
|
|
2398
|
-
import { existsSync as
|
|
2399
|
-
import { dirname as
|
|
2400
|
-
function
|
|
2401
|
-
return
|
|
2142
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
2143
|
+
import { dirname as dirname3, resolve as resolve4 } from "node:path";
|
|
2144
|
+
function registryPath(workdir, taskId) {
|
|
2145
|
+
return resolve4(workdir, ".apm", "webide", taskId, "cursor-agent.json");
|
|
2402
2146
|
}
|
|
2403
|
-
function
|
|
2404
|
-
if (!
|
|
2147
|
+
function readRegistry(path) {
|
|
2148
|
+
if (!existsSync3(path)) {
|
|
2405
2149
|
return {};
|
|
2406
2150
|
}
|
|
2407
2151
|
try {
|
|
2408
|
-
const parsed = JSON.parse(
|
|
2152
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
2409
2153
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
2410
2154
|
const raw = parsed;
|
|
2411
2155
|
const state = {};
|
|
@@ -2433,16 +2177,16 @@ function readRegistry2(path) {
|
|
|
2433
2177
|
}
|
|
2434
2178
|
return {};
|
|
2435
2179
|
}
|
|
2436
|
-
function
|
|
2437
|
-
|
|
2438
|
-
|
|
2180
|
+
function writeRegistry(path, registry) {
|
|
2181
|
+
mkdirSync4(dirname3(path), { recursive: true });
|
|
2182
|
+
writeFileSync3(path, `${JSON.stringify(registry, null, 2)}
|
|
2439
2183
|
`, "utf8");
|
|
2440
2184
|
}
|
|
2441
2185
|
function syncWebIdeTaskState(workdir, taskId, patch) {
|
|
2442
2186
|
const trimmedTaskId = taskId.trim();
|
|
2443
2187
|
if (!trimmedTaskId) return;
|
|
2444
|
-
const path =
|
|
2445
|
-
const current =
|
|
2188
|
+
const path = registryPath(workdir, trimmedTaskId);
|
|
2189
|
+
const current = readRegistry(path);
|
|
2446
2190
|
const next = {
|
|
2447
2191
|
...current,
|
|
2448
2192
|
taskId: trimmedTaskId,
|
|
@@ -2466,30 +2210,30 @@ function syncWebIdeTaskState(workdir, taskId, patch) {
|
|
|
2466
2210
|
if (patch.status !== void 0) {
|
|
2467
2211
|
next.status = patch.status;
|
|
2468
2212
|
}
|
|
2469
|
-
|
|
2213
|
+
writeRegistry(path, next);
|
|
2470
2214
|
}
|
|
2471
2215
|
function loadWebIdeAgentId(workdir, taskId) {
|
|
2472
|
-
return
|
|
2216
|
+
return readRegistry(registryPath(workdir, taskId)).agentId;
|
|
2473
2217
|
}
|
|
2474
2218
|
function saveWebIdeAgentId(workdir, taskId, agentId) {
|
|
2475
2219
|
syncWebIdeTaskState(workdir, taskId, { agentId });
|
|
2476
2220
|
}
|
|
2477
2221
|
function clearWebIdeAgentId(workdir, taskId) {
|
|
2478
|
-
const path =
|
|
2479
|
-
if (!
|
|
2222
|
+
const path = registryPath(workdir, taskId);
|
|
2223
|
+
if (!existsSync3(path)) return;
|
|
2480
2224
|
syncWebIdeTaskState(workdir, taskId, { agentId: "" });
|
|
2481
2225
|
}
|
|
2482
2226
|
|
|
2483
2227
|
// src/commands/clean-webide-cache.ts
|
|
2484
|
-
import { existsSync as
|
|
2485
|
-
import { resolve as
|
|
2228
|
+
import { existsSync as existsSync4, rmSync } from "node:fs";
|
|
2229
|
+
import { resolve as resolve5 } from "node:path";
|
|
2486
2230
|
import { getDefaultSdkStateRoot } from "@cursor/sdk";
|
|
2487
2231
|
async function purgeCursorAgentStoreForAgent(workdir, agentId) {
|
|
2488
2232
|
const trimmedAgentId = agentId.trim();
|
|
2489
2233
|
const trimmedWorkdir = workdir.trim();
|
|
2490
2234
|
if (!trimmedAgentId || !trimmedWorkdir) return false;
|
|
2491
2235
|
const stateRoot = getDefaultSdkStateRoot(trimmedWorkdir);
|
|
2492
|
-
if (!
|
|
2236
|
+
if (!existsSync4(stateRoot)) return false;
|
|
2493
2237
|
const { SqliteLocalAgentStore } = await import(
|
|
2494
2238
|
/* @vite-ignore */
|
|
2495
2239
|
"@cursor/sdk/sqlite"
|
|
@@ -2553,8 +2297,8 @@ async function cleanWebIdeWorkspaceCache(taskId, workdir) {
|
|
|
2553
2297
|
`[apm] \u65E0 WebIDE agentId\uFF0C\u8DF3\u8FC7 agent store \u6E05\u7406 taskId=${trimmedTaskId}`
|
|
2554
2298
|
);
|
|
2555
2299
|
}
|
|
2556
|
-
const dir =
|
|
2557
|
-
if (
|
|
2300
|
+
const dir = resolve5(trimmedWorkdir, ".apm", "webide", trimmedTaskId);
|
|
2301
|
+
if (existsSync4(dir)) {
|
|
2558
2302
|
rmSync(dir, { recursive: true, force: true });
|
|
2559
2303
|
console.log(`[apm] \u5DF2\u6E05\u7406 WebIDE \u7F13\u5B58 ${dir}`);
|
|
2560
2304
|
} else {
|
|
@@ -2676,14 +2420,14 @@ var MinioClient = class {
|
|
|
2676
2420
|
async deleteObjectsByPrefix(bucket, prefix) {
|
|
2677
2421
|
const objectsStream = this.inner.listObjectsV2(bucket, prefix, true);
|
|
2678
2422
|
const keys = [];
|
|
2679
|
-
await new Promise((
|
|
2423
|
+
await new Promise((resolve6, reject) => {
|
|
2680
2424
|
objectsStream.on("data", (obj) => {
|
|
2681
2425
|
if (obj.name) {
|
|
2682
2426
|
keys.push(obj.name);
|
|
2683
2427
|
}
|
|
2684
2428
|
});
|
|
2685
2429
|
objectsStream.on("error", reject);
|
|
2686
|
-
objectsStream.on("end",
|
|
2430
|
+
objectsStream.on("end", resolve6);
|
|
2687
2431
|
});
|
|
2688
2432
|
const chunkSize = 500;
|
|
2689
2433
|
for (let i = 0; i < keys.length; i += chunkSize) {
|
|
@@ -2872,48 +2616,33 @@ function resolveMessageReplyFallback(fallback) {
|
|
|
2872
2616
|
}
|
|
2873
2617
|
|
|
2874
2618
|
// src/commands/init.ts
|
|
2875
|
-
|
|
2876
|
-
import { readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
|
|
2877
|
-
async function initializeWorkspace(workdir, options) {
|
|
2619
|
+
async function initializeWorkspace(workdir) {
|
|
2878
2620
|
await ensureWorkspaceApmDirForInit(workdir);
|
|
2879
2621
|
const apmDir = workspaceApmDir(workdir);
|
|
2880
|
-
await
|
|
2881
|
-
|
|
2882
|
-
if (trimmedName) {
|
|
2883
|
-
const apmConfigPath = toFsPath(join4(apmDir, "apm.config.json"));
|
|
2884
|
-
const config = readFileSync5(apmConfigPath, "utf8");
|
|
2885
|
-
const configJson = JSON.parse(config);
|
|
2886
|
-
configJson.name = trimmedName;
|
|
2887
|
-
writeFileSync5(
|
|
2888
|
-
apmConfigPath,
|
|
2889
|
-
`${JSON.stringify(configJson, null, 2)}
|
|
2890
|
-
`,
|
|
2891
|
-
"utf8"
|
|
2892
|
-
);
|
|
2893
|
-
}
|
|
2894
|
-
console.log(`[apm] \u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`);
|
|
2622
|
+
await copyWebIdeTemplateFiles(apmDir, workdir);
|
|
2623
|
+
console.log(`[apm] \u5DF2\u6309 WebIDE \u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`);
|
|
2895
2624
|
return { didInit: true };
|
|
2896
2625
|
}
|
|
2897
|
-
async function ensureWorkspaceInitialized(workdir
|
|
2626
|
+
async function ensureWorkspaceInitialized(workdir) {
|
|
2898
2627
|
if (isWorkspaceApmInitialized(workdir)) {
|
|
2899
2628
|
return { didInit: false };
|
|
2900
2629
|
}
|
|
2901
2630
|
console.log(
|
|
2902
|
-
`[apm] \u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u672A\u68C0\u6D4B\u5230\u5DF2\u521D\u59CB\u5316\u7684 .apm\uFF0C\u6B63\u5728\u81EA\u52A8\u521D\u59CB\u5316\u2026`
|
|
2631
|
+
`[apm] \u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u672A\u68C0\u6D4B\u5230\u5DF2\u521D\u59CB\u5316\u7684 .apm\uFF0C\u6B63\u5728\u6309 WebIDE \u81EA\u52A8\u521D\u59CB\u5316\u2026`
|
|
2903
2632
|
);
|
|
2904
|
-
return initializeWorkspace(workdir
|
|
2633
|
+
return initializeWorkspace(workdir);
|
|
2905
2634
|
}
|
|
2906
2635
|
|
|
2907
2636
|
// src/commands/connect/pre-step-cache.ts
|
|
2908
|
-
function
|
|
2909
|
-
return `${
|
|
2637
|
+
function taskWorkdirKey(taskId, workdir) {
|
|
2638
|
+
return `${taskId}\0${workdir}`;
|
|
2910
2639
|
}
|
|
2911
2640
|
var lastBranchKey = null;
|
|
2912
|
-
function shouldRunBranch(
|
|
2913
|
-
return lastBranchKey !==
|
|
2641
|
+
function shouldRunBranch(taskId, workdir) {
|
|
2642
|
+
return lastBranchKey !== taskWorkdirKey(taskId, workdir);
|
|
2914
2643
|
}
|
|
2915
|
-
function markBranchDone(
|
|
2916
|
-
lastBranchKey =
|
|
2644
|
+
function markBranchDone(taskId, workdir) {
|
|
2645
|
+
lastBranchKey = taskWorkdirKey(taskId, workdir);
|
|
2917
2646
|
}
|
|
2918
2647
|
|
|
2919
2648
|
// src/commands/connect/webide-pull-requests.ts
|
|
@@ -3035,7 +2764,7 @@ async function ensureWebIdePullRequests(cfg, taskId, workdir) {
|
|
|
3035
2764
|
try {
|
|
3036
2765
|
const current = await getCurrentBranch(gitRoot);
|
|
3037
2766
|
if (!isFeatureBranchName(current)) {
|
|
3038
|
-
const tip = `${label}\uFF1A\u5F53\u524D\u5206\u652F ${current} \u4E0D\u662F feat/task
|
|
2767
|
+
const tip = `${label}\uFF1A\u5F53\u524D\u5206\u652F ${current} \u4E0D\u662F feat/task-*\uFF0C\u4E0D\u7B26\u5408\u521B\u5EFA PR \u6761\u4EF6`;
|
|
3039
2768
|
console.warn(`[apm] WebIDE PR\uFF1A${tip}`);
|
|
3040
2769
|
tips.push(tip);
|
|
3041
2770
|
skipped += 1;
|
|
@@ -3153,164 +2882,23 @@ async function ensureWebIdePullRequests(cfg, taskId, workdir) {
|
|
|
3153
2882
|
}
|
|
3154
2883
|
|
|
3155
2884
|
// src/version.ts
|
|
3156
|
-
import { readFileSync as
|
|
3157
|
-
import { dirname as
|
|
2885
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
2886
|
+
import { dirname as dirname4, join as join4 } from "path";
|
|
3158
2887
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3159
2888
|
function readCliVersion() {
|
|
3160
2889
|
try {
|
|
3161
|
-
const dir =
|
|
3162
|
-
const pkgPath =
|
|
3163
|
-
const pkg = JSON.parse(
|
|
2890
|
+
const dir = dirname4(fileURLToPath2(import.meta.url));
|
|
2891
|
+
const pkgPath = join4(dir, "..", "package.json");
|
|
2892
|
+
const pkg = JSON.parse(readFileSync4(pkgPath, "utf8"));
|
|
3164
2893
|
return pkg.version ?? "0.0.0";
|
|
3165
2894
|
} catch {
|
|
3166
2895
|
return "0.0.0";
|
|
3167
2896
|
}
|
|
3168
2897
|
}
|
|
3169
2898
|
|
|
3170
|
-
// src/commands/
|
|
3171
|
-
import { existsSync as
|
|
3172
|
-
import { join as
|
|
3173
|
-
|
|
3174
|
-
// src/skills-sync.ts
|
|
3175
|
-
import {
|
|
3176
|
-
copyFileSync as copyFileSync2,
|
|
3177
|
-
cpSync,
|
|
3178
|
-
existsSync as existsSync6,
|
|
3179
|
-
mkdirSync as mkdirSync6,
|
|
3180
|
-
readdirSync as readdirSync3,
|
|
3181
|
-
rmSync as rmSync2,
|
|
3182
|
-
statSync as statSync3,
|
|
3183
|
-
writeFileSync as writeFileSync6
|
|
3184
|
-
} from "fs";
|
|
3185
|
-
import { join as join6 } from "path";
|
|
3186
|
-
var AGENTS_TEMPLATE_PATH = join6(CLI_TEMPLATE_DIR, "AGENTS.md");
|
|
3187
|
-
var BASE_SKILLS_TEMPLATE_DIR = join6(CLI_TEMPLATE_DIR, "skills");
|
|
3188
|
-
var BASE_RULES_TEMPLATE_DIR = join6(CLI_TEMPLATE_DIR, "rules");
|
|
3189
|
-
function sanitizeSkillDirName(name) {
|
|
3190
|
-
const trimmed = name.trim();
|
|
3191
|
-
if (!trimmed) return "skill";
|
|
3192
|
-
return trimmed.replace(/[/\\:*?"<>|]/g, "_");
|
|
3193
|
-
}
|
|
3194
|
-
function listBaseSkillDirNames() {
|
|
3195
|
-
if (!existsSync6(BASE_SKILLS_TEMPLATE_DIR)) return [];
|
|
3196
|
-
return readdirSync3(BASE_SKILLS_TEMPLATE_DIR).filter((name) => {
|
|
3197
|
-
const path = join6(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
3198
|
-
return statSync3(path).isDirectory();
|
|
3199
|
-
});
|
|
3200
|
-
}
|
|
3201
|
-
function syncAgentsGuide(apmDir) {
|
|
3202
|
-
if (!existsSync6(AGENTS_TEMPLATE_PATH)) return false;
|
|
3203
|
-
mkdirSync6(apmDir, { recursive: true });
|
|
3204
|
-
copyFileSync2(AGENTS_TEMPLATE_PATH, join6(apmDir, "AGENTS.md"));
|
|
3205
|
-
return true;
|
|
3206
|
-
}
|
|
3207
|
-
function listBaseRuleFileNames() {
|
|
3208
|
-
if (!existsSync6(BASE_RULES_TEMPLATE_DIR)) return [];
|
|
3209
|
-
return readdirSync3(BASE_RULES_TEMPLATE_DIR).filter((name) => {
|
|
3210
|
-
const path = join6(BASE_RULES_TEMPLATE_DIR, name);
|
|
3211
|
-
return statSync3(path).isFile();
|
|
3212
|
-
});
|
|
3213
|
-
}
|
|
3214
|
-
function syncBaseRules(rulesDir) {
|
|
3215
|
-
mkdirSync6(rulesDir, { recursive: true });
|
|
3216
|
-
const names = listBaseRuleFileNames();
|
|
3217
|
-
for (const name of names) {
|
|
3218
|
-
const src = join6(BASE_RULES_TEMPLATE_DIR, name);
|
|
3219
|
-
const dest = join6(rulesDir, name);
|
|
3220
|
-
copyFileSync2(src, dest);
|
|
3221
|
-
}
|
|
3222
|
-
return names;
|
|
3223
|
-
}
|
|
3224
|
-
function syncBaseSkills(skillsDir) {
|
|
3225
|
-
mkdirSync6(skillsDir, { recursive: true });
|
|
3226
|
-
const names = listBaseSkillDirNames();
|
|
3227
|
-
for (const name of names) {
|
|
3228
|
-
const src = join6(BASE_SKILLS_TEMPLATE_DIR, name);
|
|
3229
|
-
const dest = join6(skillsDir, name);
|
|
3230
|
-
cpSync(src, dest, { recursive: true, force: true });
|
|
3231
|
-
}
|
|
3232
|
-
return names;
|
|
3233
|
-
}
|
|
3234
|
-
function syncSupplementarySkills(skillsDir, list) {
|
|
3235
|
-
const baseNames = new Set(listBaseSkillDirNames());
|
|
3236
|
-
const apiDirNames = /* @__PURE__ */ new Set();
|
|
3237
|
-
const written = [];
|
|
3238
|
-
const skipped = [];
|
|
3239
|
-
for (const skill of list) {
|
|
3240
|
-
const dirName = sanitizeSkillDirName(skill.name);
|
|
3241
|
-
apiDirNames.add(dirName);
|
|
3242
|
-
if (baseNames.has(dirName)) {
|
|
3243
|
-
skipped.push(dirName);
|
|
3244
|
-
continue;
|
|
3245
|
-
}
|
|
3246
|
-
const skillDir = join6(skillsDir, dirName);
|
|
3247
|
-
mkdirSync6(skillDir, { recursive: true });
|
|
3248
|
-
writeFileSync6(join6(skillDir, "SKILL.md"), skill.content ?? "", "utf8");
|
|
3249
|
-
written.push(dirName);
|
|
3250
|
-
}
|
|
3251
|
-
const removed = [];
|
|
3252
|
-
if (!existsSync6(skillsDir)) return { written, skipped, removed };
|
|
3253
|
-
for (const entry of readdirSync3(skillsDir)) {
|
|
3254
|
-
const full = join6(skillsDir, entry);
|
|
3255
|
-
if (!statSync3(full).isDirectory()) continue;
|
|
3256
|
-
if (baseNames.has(entry)) continue;
|
|
3257
|
-
if (apiDirNames.has(entry)) continue;
|
|
3258
|
-
rmSync2(full, { recursive: true, force: true });
|
|
3259
|
-
removed.push(entry);
|
|
3260
|
-
}
|
|
3261
|
-
return { written, skipped, removed };
|
|
3262
|
-
}
|
|
3263
|
-
|
|
3264
|
-
// src/commands/update-skills.ts
|
|
3265
|
-
async function syncWorkspaceSkills(cfg, workdir) {
|
|
3266
|
-
const apmDir = workspaceApmDir(workdir);
|
|
3267
|
-
const fsApmDir = toFsPath(apmDir);
|
|
3268
|
-
if (!existsSync7(fsApmDir)) {
|
|
3269
|
-
throw new Error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
|
|
3270
|
-
}
|
|
3271
|
-
const apmStat = statSync4(fsApmDir);
|
|
3272
|
-
if (!apmStat.isDirectory()) {
|
|
3273
|
-
throw new Error(`[apm] \u8DEF\u5F84\u5DF2\u5B58\u5728\u4F46\u4E0D\u662F\u76EE\u5F55: ${apmDir}`);
|
|
3274
|
-
}
|
|
3275
|
-
const api = createApmApiClient(cfg);
|
|
3276
|
-
const { list } = await api.cli.listSkills({});
|
|
3277
|
-
if (syncAgentsGuide(apmDir)) {
|
|
3278
|
-
console.log("[apm] \u5DF2\u540C\u6B65 APM \u6307\u5357: .apm/AGENTS.md");
|
|
3279
|
-
}
|
|
3280
|
-
const rulesDir = join7(apmDir, "rules");
|
|
3281
|
-
const ruleNames = syncBaseRules(rulesDir);
|
|
3282
|
-
for (const name of ruleNames) {
|
|
3283
|
-
console.log(`[apm] \u5DF2\u540C\u6B65\u57FA\u7840\u89C4\u5219: rules/${name}`);
|
|
3284
|
-
}
|
|
3285
|
-
const skillsDir = join7(apmDir, "skills");
|
|
3286
|
-
mkdirSync7(toFsPath(skillsDir), { recursive: true });
|
|
3287
|
-
const baseNames = syncBaseSkills(skillsDir);
|
|
3288
|
-
for (const name of baseNames) {
|
|
3289
|
-
console.log(`[apm] \u5DF2\u540C\u6B65\u57FA\u7840\u6280\u80FD: skills/${name}/`);
|
|
3290
|
-
}
|
|
3291
|
-
const { written, skipped, removed } = syncSupplementarySkills(
|
|
3292
|
-
skillsDir,
|
|
3293
|
-
list
|
|
3294
|
-
);
|
|
3295
|
-
for (const name of written) {
|
|
3296
|
-
console.log(`[apm] \u5DF2\u5199\u5165\u8865\u5145\u6280\u80FD: skills/${name}/SKILL.md`);
|
|
3297
|
-
}
|
|
3298
|
-
for (const name of skipped) {
|
|
3299
|
-
console.log(
|
|
3300
|
-
`[apm] \u5DF2\u8DF3\u8FC7\u4E0E\u57FA\u7840\u6280\u80FD\u540C\u540D\u7684\u8865\u5145\u6280\u80FD: ${name}\uFF08\u4FDD\u7559\u6A21\u677F\u7248\u672C\uFF09`
|
|
3301
|
-
);
|
|
3302
|
-
}
|
|
3303
|
-
for (const name of removed) {
|
|
3304
|
-
console.log(`[apm] \u5DF2\u79FB\u9664\u5DF2\u4E0B\u7EBF\u7684\u8865\u5145\u6280\u80FD: skills/${name}/`);
|
|
3305
|
-
}
|
|
3306
|
-
console.log(
|
|
3307
|
-
`[apm] \u540C\u6B65\u5B8C\u6210\uFF1A${ruleNames.length} \u4E2A\u57FA\u7840\u89C4\u5219\uFF0C${baseNames.length} \u4E2A\u57FA\u7840\u6280\u80FD\uFF0C${written.length} \u4E2A\u8865\u5145\u6280\u80FD`
|
|
3308
|
-
);
|
|
3309
|
-
}
|
|
3310
|
-
|
|
3311
|
-
// src/commands/sync-session-attachments.ts
|
|
3312
|
-
import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "fs";
|
|
3313
|
-
import { join as join8 } from "path";
|
|
2899
|
+
// src/commands/sync-webide-attachments.ts
|
|
2900
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
2901
|
+
import { join as join5 } from "path";
|
|
3314
2902
|
var MANIFEST_FILE = ".sync-manifest.json";
|
|
3315
2903
|
async function downloadAttachment(cfg, attachmentId) {
|
|
3316
2904
|
const base = cfg.baseUrl.trim().replace(/\/+$/, "");
|
|
@@ -3326,13 +2914,13 @@ async function downloadAttachment(cfg, attachmentId) {
|
|
|
3326
2914
|
return Buffer.from(await res.arrayBuffer());
|
|
3327
2915
|
}
|
|
3328
2916
|
function loadManifest(dir) {
|
|
3329
|
-
const path =
|
|
3330
|
-
if (!
|
|
2917
|
+
const path = join5(dir, MANIFEST_FILE);
|
|
2918
|
+
if (!existsSync5(path)) {
|
|
3331
2919
|
return { version: 1, attachments: {} };
|
|
3332
2920
|
}
|
|
3333
2921
|
try {
|
|
3334
2922
|
const parsed = JSON.parse(
|
|
3335
|
-
|
|
2923
|
+
readFileSync5(path, "utf8")
|
|
3336
2924
|
);
|
|
3337
2925
|
if (parsed?.version === 1 && parsed.attachments && typeof parsed.attachments === "object") {
|
|
3338
2926
|
return parsed;
|
|
@@ -3342,15 +2930,15 @@ function loadManifest(dir) {
|
|
|
3342
2930
|
return { version: 1, attachments: {} };
|
|
3343
2931
|
}
|
|
3344
2932
|
function saveManifest(dir, manifest) {
|
|
3345
|
-
|
|
3346
|
-
|
|
2933
|
+
writeFileSync4(
|
|
2934
|
+
join5(dir, MANIFEST_FILE),
|
|
3347
2935
|
`${JSON.stringify(manifest, null, 2)}
|
|
3348
2936
|
`,
|
|
3349
2937
|
"utf8"
|
|
3350
2938
|
);
|
|
3351
2939
|
}
|
|
3352
2940
|
function isAttachmentUpToDate(entry, item, dest) {
|
|
3353
|
-
if (!entry || !
|
|
2941
|
+
if (!entry || !existsSync5(dest)) return false;
|
|
3354
2942
|
if (entry.name !== item.name) return false;
|
|
3355
2943
|
const createdAt = item.createdAt ?? "";
|
|
3356
2944
|
return entry.createdAt === createdAt;
|
|
@@ -3365,7 +2953,7 @@ async function syncAttachmentsToDirectory(cfg, attachments, dir, logLabel) {
|
|
|
3365
2953
|
const nextManifest = { version: 1, attachments: {} };
|
|
3366
2954
|
const names = [];
|
|
3367
2955
|
for (const item of attachments) {
|
|
3368
|
-
const dest =
|
|
2956
|
+
const dest = join5(dir, item.name);
|
|
3369
2957
|
const entry = manifest.attachments[item.id];
|
|
3370
2958
|
const createdAt = item.createdAt ?? "";
|
|
3371
2959
|
names.push(item.name);
|
|
@@ -3375,7 +2963,7 @@ async function syncAttachmentsToDirectory(cfg, attachments, dir, logLabel) {
|
|
|
3375
2963
|
continue;
|
|
3376
2964
|
}
|
|
3377
2965
|
const buffer = await downloadAttachment(cfg, item.id);
|
|
3378
|
-
|
|
2966
|
+
writeFileSync4(dest, buffer);
|
|
3379
2967
|
nextManifest.attachments[item.id] = {
|
|
3380
2968
|
name: item.name,
|
|
3381
2969
|
createdAt
|
|
@@ -3397,14 +2985,14 @@ async function syncWebIdeAttachments(cfg, taskId, workdir, attachments) {
|
|
|
3397
2985
|
|
|
3398
2986
|
// src/project-documents-sync.ts
|
|
3399
2987
|
import {
|
|
3400
|
-
existsSync as
|
|
3401
|
-
readdirSync as
|
|
3402
|
-
readFileSync as
|
|
3403
|
-
rmSync as
|
|
3404
|
-
writeFileSync as
|
|
2988
|
+
existsSync as existsSync6,
|
|
2989
|
+
readdirSync as readdirSync3,
|
|
2990
|
+
readFileSync as readFileSync6,
|
|
2991
|
+
rmSync as rmSync2,
|
|
2992
|
+
writeFileSync as writeFileSync5
|
|
3405
2993
|
} from "fs";
|
|
3406
2994
|
import { createHash } from "crypto";
|
|
3407
|
-
import { dirname as
|
|
2995
|
+
import { dirname as dirname5, join as join6, relative as relative2, sep } from "path";
|
|
3408
2996
|
var MANIFEST_FILE2 = "manifest.json";
|
|
3409
2997
|
function normalizeProjectIdForPath(projectId) {
|
|
3410
2998
|
const id = projectId.trim();
|
|
@@ -3418,11 +3006,11 @@ function normalizeProjectIdForPath(projectId) {
|
|
|
3418
3006
|
}
|
|
3419
3007
|
function projectDocumentsDir(apmRoot, projectId) {
|
|
3420
3008
|
const id = normalizeProjectIdForPath(projectId);
|
|
3421
|
-
return
|
|
3009
|
+
return join6(apmRoot ?? workspaceApmDir(), "project", id);
|
|
3422
3010
|
}
|
|
3423
3011
|
function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
|
|
3424
3012
|
const normalized = normalizeLocalDocumentPath(documentPath);
|
|
3425
|
-
return
|
|
3013
|
+
return join6(
|
|
3426
3014
|
projectDocumentsDir(apmRoot, projectId),
|
|
3427
3015
|
...normalized.split("/")
|
|
3428
3016
|
);
|
|
@@ -3442,16 +3030,16 @@ function hashLocalFileContent(content) {
|
|
|
3442
3030
|
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
3443
3031
|
}
|
|
3444
3032
|
function readLocalManifest(apmRoot, projectId) {
|
|
3445
|
-
const manifestPath3 =
|
|
3033
|
+
const manifestPath3 = join6(
|
|
3446
3034
|
projectDocumentsDir(apmRoot, projectId),
|
|
3447
3035
|
MANIFEST_FILE2
|
|
3448
3036
|
);
|
|
3449
|
-
if (!
|
|
3037
|
+
if (!existsSync6(manifestPath3)) {
|
|
3450
3038
|
return null;
|
|
3451
3039
|
}
|
|
3452
3040
|
try {
|
|
3453
3041
|
return JSON.parse(
|
|
3454
|
-
|
|
3042
|
+
readFileSync6(manifestPath3, "utf8")
|
|
3455
3043
|
);
|
|
3456
3044
|
} catch {
|
|
3457
3045
|
return null;
|
|
@@ -3459,13 +3047,13 @@ function readLocalManifest(apmRoot, projectId) {
|
|
|
3459
3047
|
}
|
|
3460
3048
|
function listLocalDocumentPaths(apmRoot, projectId) {
|
|
3461
3049
|
const root = projectDocumentsDir(apmRoot, projectId);
|
|
3462
|
-
if (!
|
|
3050
|
+
if (!existsSync6(root)) {
|
|
3463
3051
|
return [];
|
|
3464
3052
|
}
|
|
3465
3053
|
const paths = [];
|
|
3466
3054
|
const walk = (dir) => {
|
|
3467
|
-
for (const entry of
|
|
3468
|
-
const abs =
|
|
3055
|
+
for (const entry of readdirSync3(dir, { withFileTypes: true })) {
|
|
3056
|
+
const abs = join6(dir, entry.name);
|
|
3469
3057
|
if (entry.isDirectory()) {
|
|
3470
3058
|
walk(abs);
|
|
3471
3059
|
continue;
|
|
@@ -3583,8 +3171,8 @@ ${diagnostic ?? ""}`);
|
|
|
3583
3171
|
const absPath = toFsPath(
|
|
3584
3172
|
projectDocumentLocalPath(targetApmDir, projectId, doc.path)
|
|
3585
3173
|
);
|
|
3586
|
-
await ensureDirExists(
|
|
3587
|
-
|
|
3174
|
+
await ensureDirExists(dirname5(absPath));
|
|
3175
|
+
writeFileSync5(absPath, doc.content, "utf8");
|
|
3588
3176
|
downloaded += 1;
|
|
3589
3177
|
}
|
|
3590
3178
|
}
|
|
@@ -3593,13 +3181,13 @@ ${diagnostic ?? ""}`);
|
|
|
3593
3181
|
const absPath = toFsPath(
|
|
3594
3182
|
projectDocumentLocalPath(targetApmDir, projectId, path)
|
|
3595
3183
|
);
|
|
3596
|
-
if (
|
|
3597
|
-
|
|
3184
|
+
if (existsSync6(absPath)) {
|
|
3185
|
+
rmSync2(absPath, { force: true });
|
|
3598
3186
|
deleted += 1;
|
|
3599
3187
|
}
|
|
3600
3188
|
}
|
|
3601
|
-
|
|
3602
|
-
toFsPath(
|
|
3189
|
+
writeFileSync5(
|
|
3190
|
+
toFsPath(join6(docsDir, MANIFEST_FILE2)),
|
|
3603
3191
|
`${JSON.stringify(remoteManifest, null, 2)}
|
|
3604
3192
|
`,
|
|
3605
3193
|
"utf8"
|
|
@@ -3642,7 +3230,7 @@ async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
|
|
|
3642
3230
|
const absPath = toFsPath(
|
|
3643
3231
|
projectDocumentLocalPath(targetApmDir, projectId, path)
|
|
3644
3232
|
);
|
|
3645
|
-
const content =
|
|
3233
|
+
const content = readFileSync6(absPath, "utf8");
|
|
3646
3234
|
const contentHash = hashLocalFileContent(content);
|
|
3647
3235
|
if (remoteHashByPath.get(path) === contentHash) {
|
|
3648
3236
|
continue;
|
|
@@ -3663,20 +3251,20 @@ async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
|
|
|
3663
3251
|
}
|
|
3664
3252
|
|
|
3665
3253
|
// src/commands/connect/cli-version-sync.ts
|
|
3666
|
-
import { existsSync as
|
|
3667
|
-
import { join as
|
|
3254
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
3255
|
+
import { join as join7 } from "path";
|
|
3668
3256
|
var CLI_VERSION_FILE = ".cli-version.json";
|
|
3669
3257
|
function manifestPath2(apmDir) {
|
|
3670
|
-
return
|
|
3258
|
+
return join7(apmDir, CLI_VERSION_FILE);
|
|
3671
3259
|
}
|
|
3672
3260
|
function loadManifest2(apmDir) {
|
|
3673
3261
|
const path = toFsPath(manifestPath2(apmDir));
|
|
3674
|
-
if (!
|
|
3262
|
+
if (!existsSync7(path)) {
|
|
3675
3263
|
return null;
|
|
3676
3264
|
}
|
|
3677
3265
|
try {
|
|
3678
3266
|
const parsed = JSON.parse(
|
|
3679
|
-
|
|
3267
|
+
readFileSync7(path, "utf8")
|
|
3680
3268
|
);
|
|
3681
3269
|
if (parsed?.version === 1 && typeof parsed.cliVersion === "string" && parsed.cliVersion.trim()) {
|
|
3682
3270
|
return parsed;
|
|
@@ -3687,7 +3275,7 @@ function loadManifest2(apmDir) {
|
|
|
3687
3275
|
}
|
|
3688
3276
|
function saveManifest2(apmDir, cliVersion) {
|
|
3689
3277
|
const manifest = { version: 1, cliVersion };
|
|
3690
|
-
|
|
3278
|
+
writeFileSync6(
|
|
3691
3279
|
toFsPath(manifestPath2(apmDir)),
|
|
3692
3280
|
`${JSON.stringify(manifest, null, 2)}
|
|
3693
3281
|
`,
|
|
@@ -3747,7 +3335,7 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3747
3335
|
const workdir = requireRemoteWorkdir(msg.workdir);
|
|
3748
3336
|
const messageId = msg.messageId;
|
|
3749
3337
|
const taskId = msg.taskId;
|
|
3750
|
-
const branchCacheKey =
|
|
3338
|
+
const branchCacheKey = taskId;
|
|
3751
3339
|
console.log(
|
|
3752
3340
|
`[apm] webide-message action=${msg.action} taskId=${taskId} messageId=${messageId}`
|
|
3753
3341
|
);
|
|
@@ -3802,9 +3390,9 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3802
3390
|
if (shouldSyncSkillsForCliVersion(workdir, cliVersion)) {
|
|
3803
3391
|
if (signal.aborted) throw new Error("\u4EFB\u52A1\u5DF2\u53D6\u6D88");
|
|
3804
3392
|
await runPrepStep(
|
|
3805
|
-
"\u540C\u6B65
|
|
3393
|
+
"\u540C\u6B65 WebIDE \u89C4\u5219",
|
|
3806
3394
|
async () => {
|
|
3807
|
-
await
|
|
3395
|
+
await copyWebIdeTemplateFiles(workspaceApmDir(workdir), workdir);
|
|
3808
3396
|
markSkillsSyncedForCliVersion(workdir, cliVersion);
|
|
3809
3397
|
return cliVersion;
|
|
3810
3398
|
},
|
|
@@ -3812,7 +3400,7 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3812
3400
|
);
|
|
3813
3401
|
} else {
|
|
3814
3402
|
eventSession.addCliStep(
|
|
3815
|
-
"\u540C\u6B65
|
|
3403
|
+
"\u540C\u6B65 WebIDE \u89C4\u5219",
|
|
3816
3404
|
`\u7248\u672C\u4E00\u81F4\uFF08${cliVersion}\uFF09\uFF0C\u8DF3\u8FC7`,
|
|
3817
3405
|
"skip"
|
|
3818
3406
|
);
|
|
@@ -3937,7 +3525,6 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3937
3525
|
cfg,
|
|
3938
3526
|
{
|
|
3939
3527
|
messageId,
|
|
3940
|
-
sessionId: `webide:${taskId}`,
|
|
3941
3528
|
prompt: msg.content,
|
|
3942
3529
|
model: msg.model,
|
|
3943
3530
|
apiKey: msg.apiKey,
|
|
@@ -3957,7 +3544,6 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3957
3544
|
}),
|
|
3958
3545
|
enableWebIdePlanTools: true,
|
|
3959
3546
|
enableSandbox: false,
|
|
3960
|
-
skipSessionAgentRegistry: true,
|
|
3961
3547
|
onInvalidatePersistedAgentId: () => clearWebIdeAgentId(workdir, taskId),
|
|
3962
3548
|
taskId,
|
|
3963
3549
|
terminalRpc: options?.terminalRpc,
|
|
@@ -4168,12 +3754,12 @@ function installTerminalRpcListener() {
|
|
|
4168
3754
|
}
|
|
4169
3755
|
function call(op, args) {
|
|
4170
3756
|
const id = nextId++;
|
|
4171
|
-
return new Promise((
|
|
3757
|
+
return new Promise((resolve6, reject) => {
|
|
4172
3758
|
if (!parentPort) {
|
|
4173
3759
|
reject(new Error("parentPort \u4E0D\u53EF\u7528"));
|
|
4174
3760
|
return;
|
|
4175
3761
|
}
|
|
4176
|
-
pending.set(id, { resolve:
|
|
3762
|
+
pending.set(id, { resolve: resolve6, reject });
|
|
4177
3763
|
parentPort.postMessage({
|
|
4178
3764
|
type: "terminal-rpc",
|
|
4179
3765
|
id,
|