ai-project-manage-cli 3.0.2 → 3.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +153 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -95,6 +95,10 @@ var requestConfig = {
|
|
|
95
95
|
method: "GET",
|
|
96
96
|
path: "/cli/requirements/pull"
|
|
97
97
|
}),
|
|
98
|
+
branchBaseline: defineEndpoint({
|
|
99
|
+
method: "GET",
|
|
100
|
+
path: "/cli/requirements/branch-baseline"
|
|
101
|
+
}),
|
|
98
102
|
comment: defineEndpoint({
|
|
99
103
|
method: "POST",
|
|
100
104
|
path: "/cli/requirements/comment"
|
|
@@ -307,6 +311,144 @@ async function runLogin(opts) {
|
|
|
307
311
|
console.log(JSON.stringify({ userId: cfg.userId, baseUrl: cfg.baseUrl }, null, 2));
|
|
308
312
|
}
|
|
309
313
|
|
|
314
|
+
// src/commands/branch.ts
|
|
315
|
+
import { execFile } from "child_process";
|
|
316
|
+
import { resolve as resolve2 } from "path";
|
|
317
|
+
import { promisify } from "util";
|
|
318
|
+
var execFileAsync = promisify(execFile);
|
|
319
|
+
async function fetchBaselineBranchFromApi(requirementId, cwd) {
|
|
320
|
+
const cfg = await ensureLoggedConfig();
|
|
321
|
+
const api = createApmApiClient(cfg);
|
|
322
|
+
const workdirPath = resolve2(cwd);
|
|
323
|
+
const { baselineBranch } = await api.cliRequirements.branchBaseline({
|
|
324
|
+
requirementId,
|
|
325
|
+
workdirPath
|
|
326
|
+
});
|
|
327
|
+
const name = baselineBranch.trim();
|
|
328
|
+
if (!name) {
|
|
329
|
+
throw new Error("[apm] \u5E73\u53F0\u8FD4\u56DE\u7684\u57FA\u7EBF\u5206\u652F\u540D\u4E3A\u7A7A");
|
|
330
|
+
}
|
|
331
|
+
return name;
|
|
332
|
+
}
|
|
333
|
+
function branchNameForRequirement(requirementId) {
|
|
334
|
+
const id = requirementId.trim();
|
|
335
|
+
if (!id) {
|
|
336
|
+
throw new Error("[apm] \u9700\u6C42 ID \u4E0D\u80FD\u4E3A\u7A7A");
|
|
337
|
+
}
|
|
338
|
+
if (/[\s/\\]/.test(id)) {
|
|
339
|
+
throw new Error(
|
|
340
|
+
"[apm] \u9700\u6C42 ID \u4E0D\u80FD\u5305\u542B\u7A7A\u767D\u6216\u8DEF\u5F84\u5206\u9694\u7B26\uFF0C\u8BF7\u4F7F\u7528\u5B57\u6BCD\u3001\u6570\u5B57\u3001._- \u7B49"
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
return `feat/req-${id}`;
|
|
344
|
+
}
|
|
345
|
+
async function execGit(cwd, args, quiet) {
|
|
346
|
+
try {
|
|
347
|
+
const { stdout, stderr } = await execFileAsync("git", args, {
|
|
348
|
+
cwd,
|
|
349
|
+
encoding: "utf8",
|
|
350
|
+
maxBuffer: 10 * 1024 * 1024
|
|
351
|
+
});
|
|
352
|
+
if (!quiet && stderr.trim()) {
|
|
353
|
+
process.stderr.write(stderr);
|
|
354
|
+
}
|
|
355
|
+
return stdout;
|
|
356
|
+
} catch (err) {
|
|
357
|
+
const e = err;
|
|
358
|
+
const detail = (e.stderr ?? e.message ?? String(err)).trim();
|
|
359
|
+
throw new Error(
|
|
360
|
+
`[apm] git ${args.join(" ")} \u5931\u8D25${detail ? `: ${detail}` : ""}`
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
async function ensureGitRepo(cwd) {
|
|
365
|
+
await execGit(cwd, ["rev-parse", "--git-dir"], true);
|
|
366
|
+
}
|
|
367
|
+
async function getCurrentBranch(cwd) {
|
|
368
|
+
const name = (await execGit(cwd, ["rev-parse", "--abbrev-ref", "HEAD"], true)).trim();
|
|
369
|
+
return name;
|
|
370
|
+
}
|
|
371
|
+
async function isWorkingTreeDirty(cwd) {
|
|
372
|
+
const out = await execGit(cwd, ["status", "--porcelain"], true);
|
|
373
|
+
return out.trim().length > 0;
|
|
374
|
+
}
|
|
375
|
+
async function remoteHeadBranchExists(cwd, branch) {
|
|
376
|
+
const out = await execGit(
|
|
377
|
+
cwd,
|
|
378
|
+
["ls-remote", "--heads", "origin", branch],
|
|
379
|
+
true
|
|
380
|
+
);
|
|
381
|
+
return out.trim().length > 0;
|
|
382
|
+
}
|
|
383
|
+
async function localBranchExists(cwd, branch) {
|
|
384
|
+
try {
|
|
385
|
+
await execGit(
|
|
386
|
+
cwd,
|
|
387
|
+
["show-ref", "--verify", "--quiet", `refs/heads/${branch}`],
|
|
388
|
+
true
|
|
389
|
+
);
|
|
390
|
+
return true;
|
|
391
|
+
} catch {
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
async function runBranch(requirementId, options = {}) {
|
|
396
|
+
const cwd = options.cwd ?? process.cwd();
|
|
397
|
+
const branch = branchNameForRequirement(requirementId);
|
|
398
|
+
const commitMessage = options.message?.trim() || `chore(apm): \u540C\u6B65\u5DE5\u4F5C\u533A (${branch})`;
|
|
399
|
+
await ensureGitRepo(cwd);
|
|
400
|
+
const current = await getCurrentBranch(cwd);
|
|
401
|
+
const dirty = await isWorkingTreeDirty(cwd);
|
|
402
|
+
if (dirty) {
|
|
403
|
+
if (current === branch) {
|
|
404
|
+
await execGit(cwd, ["add", "-A"]);
|
|
405
|
+
await execGit(cwd, ["commit", "-m", commitMessage]);
|
|
406
|
+
} else {
|
|
407
|
+
await execGit(cwd, [
|
|
408
|
+
"stash",
|
|
409
|
+
"push",
|
|
410
|
+
"-u",
|
|
411
|
+
"-m",
|
|
412
|
+
`apm: switch to ${branch}`
|
|
413
|
+
]);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
const remoteExists = await remoteHeadBranchExists(cwd, branch);
|
|
417
|
+
if (remoteExists) {
|
|
418
|
+
await execGit(cwd, ["fetch", "origin", branch]);
|
|
419
|
+
const hasLocal = await localBranchExists(cwd, branch);
|
|
420
|
+
if (hasLocal) {
|
|
421
|
+
await execGit(cwd, ["checkout", branch]);
|
|
422
|
+
await execGit(cwd, ["pull", "--no-edit"]);
|
|
423
|
+
} else {
|
|
424
|
+
await execGit(cwd, ["checkout", "-b", branch, `origin/${branch}`]);
|
|
425
|
+
}
|
|
426
|
+
} else {
|
|
427
|
+
const onBranch = await getCurrentBranch(cwd) === branch;
|
|
428
|
+
if (!onBranch) {
|
|
429
|
+
const hasLocal = await localBranchExists(cwd, branch);
|
|
430
|
+
if (hasLocal) {
|
|
431
|
+
await execGit(cwd, ["checkout", branch]);
|
|
432
|
+
} else {
|
|
433
|
+
const baselineBranch = await fetchBaselineBranchFromApi(
|
|
434
|
+
requirementId,
|
|
435
|
+
cwd
|
|
436
|
+
);
|
|
437
|
+
await execGit(cwd, ["fetch", "origin", baselineBranch]);
|
|
438
|
+
await execGit(cwd, [
|
|
439
|
+
"checkout",
|
|
440
|
+
"-b",
|
|
441
|
+
branch,
|
|
442
|
+
`origin/${baselineBranch}`
|
|
443
|
+
]);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
await execGit(cwd, ["push", "-u", "origin", branch]);
|
|
447
|
+
}
|
|
448
|
+
console.log(`[apm] \u5DF2\u5C31\u7EEA\u5206\u652F ${branch}`);
|
|
449
|
+
return branch;
|
|
450
|
+
}
|
|
451
|
+
|
|
310
452
|
// src/commands/pull.ts
|
|
311
453
|
import { writeFileSync as writeFileSync3 } from "fs";
|
|
312
454
|
import { join as join4 } from "path";
|
|
@@ -364,9 +506,8 @@ async function runPull(requirementId) {
|
|
|
364
506
|
{
|
|
365
507
|
id: req.id,
|
|
366
508
|
status: req.status,
|
|
367
|
-
...req.urgency != null && req.urgency !== "" ? { urgency: req.urgency } : {},
|
|
368
509
|
title: req.title,
|
|
369
|
-
|
|
510
|
+
env: req.envName || "",
|
|
370
511
|
tasks: tasksForStatusYaml(data.tasks ?? [])
|
|
371
512
|
},
|
|
372
513
|
{ lineWidth: 0 }
|
|
@@ -476,6 +617,16 @@ function buildProgram() {
|
|
|
476
617
|
program.command("pull").description("GET /api/cli/requirements/pull\uFF0C\u540C\u6B65\u6570\u636E\u5230 .apm \u76EE\u5F55").argument("<requirementId>", "\u9700\u6C42 ID").action(async (requirementId) => {
|
|
477
618
|
await runPull(requirementId);
|
|
478
619
|
});
|
|
620
|
+
program.command("branch").description(
|
|
621
|
+
"\u5207\u6362\u6216\u521B\u5EFA\u9700\u6C42\u5206\u652F feat/req-<ID>\uFF1A\u8FDC\u7AEF\u5B58\u5728\u5219\u62C9\u53D6\u6700\u65B0\uFF1B\u8FDC\u7AEF\u5C1A\u65E0\u8BE5\u5206\u652F\u4E14\u672C\u5730\u4E5F\u65E0\u540C\u540D\u5206\u652F\u65F6\uFF0C\u9700\u5DF2 login\uFF0C\u5E76\u7531\u5E73\u53F0\u6839\u636E\u5F53\u524D\u76EE\u5F55\u8DEF\u5F84\u89E3\u6790\u4ED3\u5E93\u57FA\u7EBF\u5206\u652F\u540E\u4ECE origin \u68C0\u51FA\u518D\u63A8\u9001\uFF1B\u6709\u672C\u5730\u672A\u63D0\u4EA4\u6539\u52A8\u65F6\u5728\u975E\u76EE\u6807\u5206\u652F\u5148 stash\uFF08\u4E0D\u81EA\u52A8\u6062\u590D\uFF09\uFF0C\u5728\u76EE\u6807\u5206\u652F\u5219\u5148 commit"
|
|
622
|
+
).argument("<requirementId>", "\u9700\u6C42 ID").option(
|
|
623
|
+
"-m, --message <text>",
|
|
624
|
+
"\u5DF2\u5728\u76EE\u6807\u5206\u652F\u4E14\u9700\u63D0\u4EA4\u672C\u5730\u6539\u52A8\u65F6\u4F7F\u7528\u7684\u63D0\u4EA4\u8BF4\u660E\uFF08\u9ED8\u8BA4\u81EA\u52A8\u751F\u6210\uFF09"
|
|
625
|
+
).action(
|
|
626
|
+
async (requirementId, opts) => {
|
|
627
|
+
await runBranch(requirementId, { message: opts.message });
|
|
628
|
+
}
|
|
629
|
+
);
|
|
479
630
|
program.command("comment").description("POST /api/cli/requirements/comment\uFF08\u6B63\u6587\u6765\u81EA\u6587\u4EF6\uFF09").argument("<requirementId>", "\u9700\u6C42 ID").requiredOption("--file <path>", "\u8BC4\u8BBA\u6B63\u6587\u6587\u4EF6\u8DEF\u5F84").option("--model <model>", "\u8BC4\u8BBA\u6A21\u578B").action(
|
|
480
631
|
async (requirementId, options) => {
|
|
481
632
|
await runComment(requirementId, options.file, options.model);
|