ai-project-manage-cli 7.1.19 → 7.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/webide-message-worker.js +306 -17
- package/package.json +1 -1
|
@@ -452,6 +452,116 @@ async function ensureRemoteBaselineBranch(cwd, baselineBranch) {
|
|
|
452
452
|
`[apm] \u8FDC\u7A0B\u4E0D\u5B58\u5728\u57FA\u7EBF\u5206\u652F origin/${baselineBranch}\uFF0C\u8BF7\u786E\u8BA4\u4ED3\u5E93\u9ED8\u8BA4\u5206\u652F\u5DF2\u63A8\u9001\u5230 origin`
|
|
453
453
|
);
|
|
454
454
|
}
|
|
455
|
+
async function branchHasChangesAgainstBase(cwd, baseBranch, headBranch) {
|
|
456
|
+
const base = baseBranch.trim().replace(/^origin\//, "");
|
|
457
|
+
const head = headBranch.trim().replace(/^origin\//, "");
|
|
458
|
+
if (!base || !head) return false;
|
|
459
|
+
await execGit(cwd, ["fetch", "origin", base], true);
|
|
460
|
+
let headRef = head;
|
|
461
|
+
try {
|
|
462
|
+
await execGit(cwd, ["fetch", "origin", head], true);
|
|
463
|
+
await execGit(cwd, ["rev-parse", "--verify", `origin/${head}`], true);
|
|
464
|
+
headRef = `origin/${head}`;
|
|
465
|
+
} catch {
|
|
466
|
+
await execGit(cwd, ["rev-parse", "--verify", head], true);
|
|
467
|
+
}
|
|
468
|
+
const baseRef = `origin/${base}`;
|
|
469
|
+
const ahead = (await execGit(cwd, ["rev-list", "--count", `${baseRef}..${headRef}`], true)).trim();
|
|
470
|
+
if ((Number.parseInt(ahead, 10) || 0) <= 0) {
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
try {
|
|
474
|
+
await execGit(cwd, ["diff", "--quiet", `${baseRef}...${headRef}`], true);
|
|
475
|
+
return false;
|
|
476
|
+
} catch {
|
|
477
|
+
return true;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
var FEATURE_BRANCH_RE = /^feat\/(task|session)-.+/;
|
|
481
|
+
function isFeatureBranchName(branch) {
|
|
482
|
+
return FEATURE_BRANCH_RE.test(branch.trim().replace(/^origin\//, ""));
|
|
483
|
+
}
|
|
484
|
+
async function resolveFeatureBranchBaseFromGit(cwd, headBranch) {
|
|
485
|
+
const head = headBranch.trim().replace(/^origin\//, "");
|
|
486
|
+
if (!head) {
|
|
487
|
+
throw new Error("[apm] head \u5206\u652F\u540D\u4E3A\u7A7A\uFF0C\u65E0\u6CD5\u63A8\u65AD\u57FA\u7EBF");
|
|
488
|
+
}
|
|
489
|
+
try {
|
|
490
|
+
const reflog = await execGit(
|
|
491
|
+
cwd,
|
|
492
|
+
["reflog", "show", "--date=iso", head],
|
|
493
|
+
true
|
|
494
|
+
);
|
|
495
|
+
for (const line of reflog.split("\n")) {
|
|
496
|
+
const created = line.match(
|
|
497
|
+
/Created from (?:refs\/remotes\/origin\/|origin\/)?(\S+)/i
|
|
498
|
+
);
|
|
499
|
+
if (created?.[1]) {
|
|
500
|
+
const name = created[1].replace(/^origin\//, "");
|
|
501
|
+
if (name && name !== head && !isFeatureBranchName(name)) {
|
|
502
|
+
return name;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
const moving = line.match(
|
|
506
|
+
/checkout: moving from (\S+) to (?:origin\/)?\S+/i
|
|
507
|
+
);
|
|
508
|
+
if (moving?.[1]) {
|
|
509
|
+
const from = moving[1].replace(/^origin\//, "");
|
|
510
|
+
if (from && from !== head && !isFeatureBranchName(from)) {
|
|
511
|
+
return from;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
} catch {
|
|
516
|
+
}
|
|
517
|
+
await execGit(cwd, ["fetch", "origin", "--prune"], true);
|
|
518
|
+
let headRef = head;
|
|
519
|
+
try {
|
|
520
|
+
await execGit(cwd, ["rev-parse", "--verify", `origin/${head}`], true);
|
|
521
|
+
headRef = `origin/${head}`;
|
|
522
|
+
} catch {
|
|
523
|
+
await execGit(cwd, ["rev-parse", "--verify", head], true);
|
|
524
|
+
}
|
|
525
|
+
const refsOut = await execGit(
|
|
526
|
+
cwd,
|
|
527
|
+
["for-each-ref", "--format=%(refname:short)", "refs/remotes/origin/"],
|
|
528
|
+
true
|
|
529
|
+
);
|
|
530
|
+
let best = null;
|
|
531
|
+
for (const ref of refsOut.split("\n").map((s) => s.trim()).filter(Boolean)) {
|
|
532
|
+
const name = ref.replace(/^origin\//, "");
|
|
533
|
+
if (!name || name === "HEAD" || name === head || isFeatureBranchName(name)) {
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
try {
|
|
537
|
+
await execGit(
|
|
538
|
+
cwd,
|
|
539
|
+
["merge-base", "--is-ancestor", `origin/${name}`, headRef],
|
|
540
|
+
true
|
|
541
|
+
);
|
|
542
|
+
const ahead = Number.parseInt(
|
|
543
|
+
(await execGit(
|
|
544
|
+
cwd,
|
|
545
|
+
["rev-list", "--count", `origin/${name}..${headRef}`],
|
|
546
|
+
true
|
|
547
|
+
)).trim(),
|
|
548
|
+
10
|
|
549
|
+
);
|
|
550
|
+
if (!Number.isFinite(ahead) || ahead < 0) continue;
|
|
551
|
+
if (!best || ahead < best.ahead) {
|
|
552
|
+
best = { name, ahead };
|
|
553
|
+
}
|
|
554
|
+
} catch {
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
if (best) {
|
|
559
|
+
return best.name;
|
|
560
|
+
}
|
|
561
|
+
throw new Error(
|
|
562
|
+
`[apm] \u65E0\u6CD5\u63A8\u65AD ${head} \u57FA\u4E8E\u54EA\u6761\u5206\u652F\u521B\u5EFA\uFF0C\u8BF7\u786E\u8BA4\u672C\u5730 reflog \u6216 origin \u8FDC\u7A0B\u5206\u652F`
|
|
563
|
+
);
|
|
564
|
+
}
|
|
455
565
|
async function hasUpstream(cwd) {
|
|
456
566
|
try {
|
|
457
567
|
await execGit(cwd, ["rev-parse", "--abbrev-ref", "@{upstream}"], true);
|
|
@@ -3108,52 +3218,198 @@ function shouldEnsureWebIdePullRequests(action) {
|
|
|
3108
3218
|
function shouldEnsureWebIdePullRequestsAfterAgent(action) {
|
|
3109
3219
|
return POST_AGENT_PR_ACTIONS.has(action);
|
|
3110
3220
|
}
|
|
3221
|
+
function formatWebIdePullRequestSummary(result) {
|
|
3222
|
+
const lines = [];
|
|
3223
|
+
lines.push("## PR \u521B\u5EFA\u7ED3\u679C");
|
|
3224
|
+
lines.push(
|
|
3225
|
+
`\u5408\u8BA1\uFF1A\u6210\u529F ${result.ensured} \u6761\uFF0C\u65E0\u53D8\u66F4\u8DF3\u8FC7 ${result.skippedNoChanges} \u6761\uFF0C\u5176\u4ED6\u8DF3\u8FC7 ${result.skipped} \u6761\uFF0C\u5931\u8D25 ${result.failed} \u6761\u3002`
|
|
3226
|
+
);
|
|
3227
|
+
if (result.items.length === 0) {
|
|
3228
|
+
lines.push("\u672A\u5904\u7406\u4EFB\u4F55\u4ED3\u5E93\u3002");
|
|
3229
|
+
return lines.join("\n");
|
|
3230
|
+
}
|
|
3231
|
+
lines.push("");
|
|
3232
|
+
for (const item of result.items) {
|
|
3233
|
+
if (item.outcome === "created") {
|
|
3234
|
+
const num = item.number != null && item.number > 0 ? `#${item.number}` : "\uFF08\u65E0\u7F16\u53F7\uFF09";
|
|
3235
|
+
const branch = item.headBranch && item.baseBranch ? `${item.headBranch} \u2192 ${item.baseBranch}` : item.baseBranch ? `\u76EE\u6807\u5206\u652F ${item.baseBranch}` : "";
|
|
3236
|
+
const state = item.state ? `\u72B6\u6001 ${item.state}` : "";
|
|
3237
|
+
const url = item.url?.trim() ? item.url.trim() : "";
|
|
3238
|
+
lines.push(
|
|
3239
|
+
`- **${item.label}**\uFF1A\u5DF2\u521B\u5EFA ${num}${branch ? `\uFF0C${branch}` : ""}${state ? `\uFF0C${state}` : ""}${url ? `\uFF0C${url}` : ""}`
|
|
3240
|
+
);
|
|
3241
|
+
continue;
|
|
3242
|
+
}
|
|
3243
|
+
if (item.outcome === "skipped_no_changes") {
|
|
3244
|
+
lines.push(
|
|
3245
|
+
`- **${item.label}**\uFF1A\u76F8\u5BF9\u57FA\u7EBF ${item.baseBranch || "\uFF08\u672A\u77E5\uFF09"} \u65E0\u4EE3\u7801\u53D8\u66F4\uFF0C\u672A\u521B\u5EFA PR`
|
|
3246
|
+
);
|
|
3247
|
+
continue;
|
|
3248
|
+
}
|
|
3249
|
+
if (item.outcome === "skipped") {
|
|
3250
|
+
lines.push(`- **${item.label}**\uFF1A\u5DF2\u8DF3\u8FC7 \u2014 ${item.detail}`);
|
|
3251
|
+
continue;
|
|
3252
|
+
}
|
|
3253
|
+
lines.push(`- **${item.label}**\uFF1A\u521B\u5EFA\u5931\u8D25 \u2014 ${item.detail}`);
|
|
3254
|
+
}
|
|
3255
|
+
return lines.join("\n");
|
|
3256
|
+
}
|
|
3111
3257
|
async function ensureWebIdePullRequests(cfg, taskId, workdir) {
|
|
3112
3258
|
const api = createApmApiClient(cfg);
|
|
3259
|
+
const empty = () => ({
|
|
3260
|
+
ensured: 0,
|
|
3261
|
+
skipped: 0,
|
|
3262
|
+
skippedNoChanges: 0,
|
|
3263
|
+
failed: 0,
|
|
3264
|
+
items: [],
|
|
3265
|
+
tips: []
|
|
3266
|
+
});
|
|
3113
3267
|
let repoRoots;
|
|
3114
3268
|
try {
|
|
3115
3269
|
const manifest = loadWorkspaceReposCache(workdir);
|
|
3116
3270
|
repoRoots = resolveWorkspaceRepoAbsolutePaths(manifest);
|
|
3117
3271
|
} catch (err) {
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3272
|
+
const tip = `\u8BFB\u53D6\u4ED3\u5E93\u6E05\u5355\u5931\u8D25: ${err instanceof Error ? err.message : err}`;
|
|
3273
|
+
console.warn(`[apm] WebIDE PR\uFF1A${tip}`);
|
|
3274
|
+
return {
|
|
3275
|
+
...empty(),
|
|
3276
|
+
failed: 1,
|
|
3277
|
+
tips: [tip],
|
|
3278
|
+
items: [
|
|
3279
|
+
{
|
|
3280
|
+
label: "\u5DE5\u4F5C\u533A",
|
|
3281
|
+
outcome: "failed",
|
|
3282
|
+
detail: tip
|
|
3283
|
+
}
|
|
3284
|
+
]
|
|
3285
|
+
};
|
|
3123
3286
|
}
|
|
3287
|
+
const expectedHead = `feat/task-${taskId}`;
|
|
3124
3288
|
console.log(
|
|
3125
|
-
`[apm] WebIDE PR\uFF1A\u51C6\u5907\
|
|
3289
|
+
`[apm] WebIDE PR\uFF1A\u51C6\u5907\u68C0\u67E5 ${repoRoots.length} \u4E2A\u4ED3\u5E93\uFF08\u671F\u671B\u7279\u6027\u5206\u652F ${expectedHead}\uFF09`
|
|
3126
3290
|
);
|
|
3291
|
+
let ensured = 0;
|
|
3292
|
+
let skipped = 0;
|
|
3293
|
+
let skippedNoChanges = 0;
|
|
3294
|
+
let failed = 0;
|
|
3295
|
+
const tips = [];
|
|
3296
|
+
const items = [];
|
|
3127
3297
|
for (const gitRoot of repoRoots) {
|
|
3128
3298
|
const label = formatRepoLabel(workdir, gitRoot);
|
|
3129
3299
|
try {
|
|
3300
|
+
const current = await getCurrentBranch(gitRoot);
|
|
3301
|
+
if (!isFeatureBranchName(current)) {
|
|
3302
|
+
const tip = `${label}\uFF1A\u5F53\u524D\u5206\u652F ${current} \u4E0D\u662F feat/task-* / feat/session-*\uFF0C\u4E0D\u7B26\u5408\u521B\u5EFA PR \u6761\u4EF6`;
|
|
3303
|
+
console.warn(`[apm] WebIDE PR\uFF1A${tip}`);
|
|
3304
|
+
tips.push(tip);
|
|
3305
|
+
skipped += 1;
|
|
3306
|
+
items.push({
|
|
3307
|
+
label,
|
|
3308
|
+
outcome: "skipped",
|
|
3309
|
+
headBranch: current,
|
|
3310
|
+
detail: tip
|
|
3311
|
+
});
|
|
3312
|
+
continue;
|
|
3313
|
+
}
|
|
3314
|
+
if (current !== expectedHead) {
|
|
3315
|
+
const tip = `${label}\uFF1A\u5F53\u524D\u4E3A ${current}\uFF0C\u671F\u671B ${expectedHead}\uFF0C\u5DF2\u8DF3\u8FC7`;
|
|
3316
|
+
console.warn(`[apm] WebIDE PR\uFF1A${tip}`);
|
|
3317
|
+
tips.push(tip);
|
|
3318
|
+
skipped += 1;
|
|
3319
|
+
items.push({
|
|
3320
|
+
label,
|
|
3321
|
+
outcome: "skipped",
|
|
3322
|
+
headBranch: current,
|
|
3323
|
+
detail: tip
|
|
3324
|
+
});
|
|
3325
|
+
continue;
|
|
3326
|
+
}
|
|
3327
|
+
const headBranch = current;
|
|
3130
3328
|
const originUrl = await tryReadGitOriginUrl(gitRoot);
|
|
3131
3329
|
if (!originUrl) {
|
|
3132
|
-
|
|
3330
|
+
const tip = `${label}\uFF1A\u65E0 remote.origin.url`;
|
|
3331
|
+
console.warn(`[apm] WebIDE PR\uFF1A\u8DF3\u8FC7 ${tip}`);
|
|
3332
|
+
tips.push(tip);
|
|
3333
|
+
skipped += 1;
|
|
3334
|
+
items.push({
|
|
3335
|
+
label,
|
|
3336
|
+
outcome: "skipped",
|
|
3337
|
+
headBranch,
|
|
3338
|
+
detail: tip
|
|
3339
|
+
});
|
|
3133
3340
|
continue;
|
|
3134
3341
|
}
|
|
3135
3342
|
const matched = await api.cli.matchRepository({ url: originUrl });
|
|
3136
3343
|
const repositoryId = matched.repositoryId?.trim();
|
|
3137
3344
|
if (!repositoryId) {
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
);
|
|
3345
|
+
const tip = `${label}\uFF1A\u5E73\u53F0\u672A\u767B\u8BB0\u4ED3\u5E93 ${originUrl}`;
|
|
3346
|
+
console.warn(`[apm] WebIDE PR\uFF1A\u8DF3\u8FC7 ${tip}`);
|
|
3347
|
+
tips.push(tip);
|
|
3348
|
+
skipped += 1;
|
|
3349
|
+
items.push({
|
|
3350
|
+
label,
|
|
3351
|
+
outcome: "skipped",
|
|
3352
|
+
headBranch,
|
|
3353
|
+
detail: tip
|
|
3354
|
+
});
|
|
3355
|
+
continue;
|
|
3356
|
+
}
|
|
3357
|
+
const baseBranch = await resolveFeatureBranchBaseFromGit(
|
|
3358
|
+
gitRoot,
|
|
3359
|
+
headBranch
|
|
3360
|
+
);
|
|
3361
|
+
const hasChanges = await branchHasChangesAgainstBase(
|
|
3362
|
+
gitRoot,
|
|
3363
|
+
baseBranch,
|
|
3364
|
+
headBranch
|
|
3365
|
+
);
|
|
3366
|
+
if (!hasChanges) {
|
|
3367
|
+
const tip = `${label}\uFF1A\u76F8\u5BF9\u57FA\u7EBF ${baseBranch} \u65E0\u4EE3\u7801\u53D8\u66F4\uFF0C\u65E0\u9700\u521B\u5EFA PR`;
|
|
3368
|
+
console.log(`[apm] WebIDE PR\uFF1A${tip}`);
|
|
3369
|
+
tips.push(tip);
|
|
3370
|
+
skippedNoChanges += 1;
|
|
3371
|
+
items.push({
|
|
3372
|
+
label,
|
|
3373
|
+
outcome: "skipped_no_changes",
|
|
3374
|
+
headBranch,
|
|
3375
|
+
baseBranch,
|
|
3376
|
+
detail: tip
|
|
3377
|
+
});
|
|
3141
3378
|
continue;
|
|
3142
3379
|
}
|
|
3143
3380
|
const pr = await api.cli.createWebIdeDraftPullRequest({
|
|
3144
3381
|
taskId,
|
|
3145
|
-
repositoryId
|
|
3382
|
+
repositoryId,
|
|
3383
|
+
headBranch,
|
|
3384
|
+
baseBranch
|
|
3385
|
+
});
|
|
3386
|
+
ensured += 1;
|
|
3387
|
+
items.push({
|
|
3388
|
+
label,
|
|
3389
|
+
outcome: "created",
|
|
3390
|
+
headBranch,
|
|
3391
|
+
baseBranch,
|
|
3392
|
+
number: pr.number,
|
|
3393
|
+
state: pr.state,
|
|
3394
|
+
url: pr.url,
|
|
3395
|
+
detail: `\u5DF2\u521B\u5EFA #${pr.number} ${headBranch} \u2192 ${baseBranch}`
|
|
3146
3396
|
});
|
|
3147
3397
|
console.log(
|
|
3148
|
-
`[apm] WebIDE PR\uFF1A${label} \u2192 #${pr.number} ${pr.state} ${pr.url || ""}`
|
|
3398
|
+
`[apm] WebIDE PR\uFF1A${label} \u2192 #${pr.number} ${pr.state} ${headBranch} \u2192 ${baseBranch} ${pr.url || ""}`
|
|
3149
3399
|
);
|
|
3150
3400
|
} catch (err) {
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
);
|
|
3401
|
+
failed += 1;
|
|
3402
|
+
const tip = `${label}\uFF1A${err instanceof Error ? err.message : err}`;
|
|
3403
|
+
console.warn(`[apm] WebIDE PR\uFF1A\u5931\u8D25 ${tip}`);
|
|
3404
|
+
tips.push(tip);
|
|
3405
|
+
items.push({
|
|
3406
|
+
label,
|
|
3407
|
+
outcome: "failed",
|
|
3408
|
+
detail: tip
|
|
3409
|
+
});
|
|
3155
3410
|
}
|
|
3156
3411
|
}
|
|
3412
|
+
return { ensured, skipped, skippedNoChanges, failed, items, tips };
|
|
3157
3413
|
}
|
|
3158
3414
|
|
|
3159
3415
|
// src/version.ts
|
|
@@ -3678,7 +3934,40 @@ async function handleWebIdeInboundMessage(cfg, msg, signal, options) {
|
|
|
3678
3934
|
`[apm] webide create-pr \u524D push action=${msg.action} repos=${pushed}`
|
|
3679
3935
|
);
|
|
3680
3936
|
}
|
|
3681
|
-
await ensureWebIdePullRequests(cfg, taskId, workdir);
|
|
3937
|
+
const prResult = await ensureWebIdePullRequests(cfg, taskId, workdir);
|
|
3938
|
+
if (prResult.tips.length > 0) {
|
|
3939
|
+
for (const tip of prResult.tips) {
|
|
3940
|
+
console.log(`[apm] webide create-pr \u63D0\u793A: ${tip}`);
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3943
|
+
try {
|
|
3944
|
+
await appendContent(
|
|
3945
|
+
cfg,
|
|
3946
|
+
messageId,
|
|
3947
|
+
formatWebIdePullRequestSummary(prResult)
|
|
3948
|
+
);
|
|
3949
|
+
} catch (appendErr) {
|
|
3950
|
+
console.warn(
|
|
3951
|
+
"[apm] webide create-pr \u8FFD\u52A0 PR \u7ED3\u679C\u5931\u8D25:",
|
|
3952
|
+
appendErr instanceof Error ? appendErr.message : appendErr
|
|
3953
|
+
);
|
|
3954
|
+
}
|
|
3955
|
+
if (prResult.ensured === 0) {
|
|
3956
|
+
if (prResult.failed > 0) {
|
|
3957
|
+
throw new Error(
|
|
3958
|
+
`\u521B\u5EFA PR \u5931\u8D25\uFF08${prResult.failed} \u4E2A\u4ED3\u5E93\u51FA\u9519\uFF09${prResult.tips[0] ? `\uFF1A${prResult.tips[0]}` : ""}`
|
|
3959
|
+
);
|
|
3960
|
+
}
|
|
3961
|
+
if (prResult.skippedNoChanges > 0 && prResult.skipped === 0) {
|
|
3962
|
+
console.log(
|
|
3963
|
+
"[apm] webide create-pr\uFF1A\u5404\u4ED3\u5E93\u76F8\u5BF9\u63A8\u65AD\u57FA\u7EBF\u5747\u65E0\u53D8\u66F4\uFF0C\u8DF3\u8FC7\u521B\u5EFA PR"
|
|
3964
|
+
);
|
|
3965
|
+
} else {
|
|
3966
|
+
throw new Error(
|
|
3967
|
+
prResult.tips[0] || "\u672A\u80FD\u521B\u5EFA PR\uFF1A\u8BF7\u786E\u8BA4\u5404\u4ED3\u5F53\u524D\u4F4D\u4E8E feat/task-* \u5206\u652F\uFF0C\u4E14 remote \u5DF2\u5728\u5E73\u53F0\u767B\u8BB0"
|
|
3968
|
+
);
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3682
3971
|
}
|
|
3683
3972
|
if (projectId) {
|
|
3684
3973
|
try {
|