ai-project-manage-cli 6.0.55 → 6.0.57

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 CHANGED
@@ -999,6 +999,19 @@ function reasonForCleanup(sessionId, sessionStatusById) {
999
999
  }
1000
1000
  return "\u4FDD\u7559";
1001
1001
  }
1002
+ async function isBranchMergedIntoDefault(cwd, branch, defaultBranch) {
1003
+ const ref = await localBranchExists2(cwd, branch) ? branch : `origin/${branch}`;
1004
+ try {
1005
+ await execGit3(
1006
+ cwd,
1007
+ ["merge-base", "--is-ancestor", ref, `origin/${defaultBranch}`],
1008
+ true
1009
+ );
1010
+ return true;
1011
+ } catch {
1012
+ return false;
1013
+ }
1014
+ }
1002
1015
  async function runCleanBranches(options = {}) {
1003
1016
  const cwd = options.cwd ?? process.cwd();
1004
1017
  const dryRun = options.dryRun ?? false;
@@ -1035,11 +1048,20 @@ async function runCleanBranches(options = {}) {
1035
1048
  }
1036
1049
  let currentBranch = await getCurrentBranch2(cwd);
1037
1050
  let defaultBranch = null;
1051
+ if (dryRun) {
1052
+ defaultBranch = await resolveDefaultBranch(cwd);
1053
+ }
1038
1054
  for (const item of toDelete) {
1039
1055
  const { branch, sessionId, reason } = item;
1040
1056
  const label = `${branch} (${sessionId}: ${reason})`;
1041
1057
  if (dryRun) {
1042
- console.log(`[apm] [dry-run] \u5C06\u5220\u9664 ${label}`);
1058
+ const merged = await isBranchMergedIntoDefault(
1059
+ cwd,
1060
+ branch,
1061
+ defaultBranch
1062
+ );
1063
+ const mergeTag = merged ? "\u5DF2\u5408\u5E76" : "\u672A\u5408\u5E76";
1064
+ console.log(`[apm] [dry-run] \u5C06\u5220\u9664 ${label} [${mergeTag}]`);
1043
1065
  continue;
1044
1066
  }
1045
1067
  if (currentBranch === branch) {
@@ -1518,17 +1540,16 @@ async function runUpdate() {
1518
1540
  // src/commands/update-skills.ts
1519
1541
  import { existsSync as existsSync6, mkdirSync as mkdirSync4, statSync as statSync3 } from "fs";
1520
1542
  import { join as join10 } from "path";
1521
- async function runUpdateSkills() {
1522
- const apmDir = workspaceApmDir();
1523
- if (!existsSync6(apmDir)) {
1524
- console.error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
1525
- process.exit(1);
1543
+ async function syncWorkspaceSkills(cfg, workdir) {
1544
+ const apmDir = workspaceApmDir(workdir);
1545
+ const fsApmDir = toFsPath(apmDir);
1546
+ if (!existsSync6(fsApmDir)) {
1547
+ throw new Error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
1526
1548
  }
1527
- const apmStat = statSync3(apmDir);
1549
+ const apmStat = statSync3(fsApmDir);
1528
1550
  if (!apmStat.isDirectory()) {
1529
1551
  throw new Error(`[apm] \u8DEF\u5F84\u5DF2\u5B58\u5728\u4F46\u4E0D\u662F\u76EE\u5F55: ${apmDir}`);
1530
1552
  }
1531
- const cfg = await ensureLoggedConfig();
1532
1553
  const api = createApmApiClient(cfg);
1533
1554
  const { list } = await api.cli.listSkills({});
1534
1555
  if (syncAgentsGuide(apmDir)) {
@@ -1540,7 +1561,7 @@ async function runUpdateSkills() {
1540
1561
  console.log(`[apm] \u5DF2\u540C\u6B65\u57FA\u7840\u89C4\u5219: rules/${name}`);
1541
1562
  }
1542
1563
  const skillsDir = join10(apmDir, "skills");
1543
- mkdirSync4(skillsDir, { recursive: true });
1564
+ mkdirSync4(toFsPath(skillsDir), { recursive: true });
1544
1565
  const baseNames = syncBaseSkills(skillsDir);
1545
1566
  for (const name of baseNames) {
1546
1567
  console.log(`[apm] \u5DF2\u540C\u6B65\u57FA\u7840\u6280\u80FD: skills/${name}/`);
@@ -1564,6 +1585,19 @@ async function runUpdateSkills() {
1564
1585
  `[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`
1565
1586
  );
1566
1587
  }
1588
+ async function runUpdateSkills() {
1589
+ const apmDir = workspaceApmDir();
1590
+ if (!existsSync6(apmDir)) {
1591
+ console.error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
1592
+ process.exit(1);
1593
+ }
1594
+ const apmStat = statSync3(apmDir);
1595
+ if (!apmStat.isDirectory()) {
1596
+ throw new Error(`[apm] \u8DEF\u5F84\u5DF2\u5B58\u5728\u4F46\u4E0D\u662F\u76EE\u5F55: ${apmDir}`);
1597
+ }
1598
+ const cfg = await ensureLoggedConfig();
1599
+ await syncWorkspaceSkills(cfg, resolveWorkdirPath());
1600
+ }
1567
1601
 
1568
1602
  // src/commands/sync-deploy-config.ts
1569
1603
  import { existsSync as existsSync7, statSync as statSync4 } from "fs";
@@ -2397,6 +2431,56 @@ async function runCursorAgent(cfg, ctx, options) {
2397
2431
  }
2398
2432
  }
2399
2433
 
2434
+ // src/commands/connect/cli-version-sync.ts
2435
+ import { existsSync as existsSync11, readFileSync as readFileSync9, writeFileSync as writeFileSync10 } from "fs";
2436
+ import { join as join12 } from "path";
2437
+ var CLI_VERSION_FILE = ".cli-version.json";
2438
+ function manifestPath(apmDir) {
2439
+ return join12(apmDir, CLI_VERSION_FILE);
2440
+ }
2441
+ function loadManifest3(apmDir) {
2442
+ const path10 = toFsPath(manifestPath(apmDir));
2443
+ if (!existsSync11(path10)) {
2444
+ return null;
2445
+ }
2446
+ try {
2447
+ const parsed = JSON.parse(
2448
+ readFileSync9(path10, "utf8")
2449
+ );
2450
+ if (parsed?.version === 1 && typeof parsed.cliVersion === "string" && parsed.cliVersion.trim()) {
2451
+ return parsed;
2452
+ }
2453
+ } catch {
2454
+ }
2455
+ return null;
2456
+ }
2457
+ function saveManifest3(apmDir, cliVersion) {
2458
+ const manifest = { version: 1, cliVersion };
2459
+ writeFileSync10(
2460
+ toFsPath(manifestPath(apmDir)),
2461
+ `${JSON.stringify(manifest, null, 2)}
2462
+ `,
2463
+ "utf8"
2464
+ );
2465
+ }
2466
+ var syncedInSession = /* @__PURE__ */ new Map();
2467
+ function shouldSyncSkillsForCliVersion(workdir, currentVersion) {
2468
+ const cached = syncedInSession.get(workdir);
2469
+ if (cached === currentVersion) {
2470
+ return false;
2471
+ }
2472
+ const stored = loadManifest3(workspaceApmDir(workdir));
2473
+ if (stored?.cliVersion === currentVersion) {
2474
+ syncedInSession.set(workdir, currentVersion);
2475
+ return false;
2476
+ }
2477
+ return true;
2478
+ }
2479
+ function markSkillsSyncedForCliVersion(workdir, cliVersion) {
2480
+ saveManifest3(workspaceApmDir(workdir), cliVersion);
2481
+ syncedInSession.set(workdir, cliVersion);
2482
+ }
2483
+
2400
2484
  // src/commands/connect/pre-step-cache.ts
2401
2485
  var PULL_TTL_MS = 3e4;
2402
2486
  function sessionWorkdirKey(sessionId, workdir) {
@@ -2515,6 +2599,19 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
2515
2599
  } else {
2516
2600
  console.log(`[apm] step=commit-pull skipped sessionId=${msg.sessionId}`);
2517
2601
  }
2602
+ const cliVersion = readCliVersion();
2603
+ if (shouldSyncSkillsForCliVersion(workdir, cliVersion)) {
2604
+ if (signal.aborted) return;
2605
+ console.log(
2606
+ `[apm] CLI \u7248\u672C ${cliVersion} \u4E0E\u5DE5\u4F5C\u533A\u8BB0\u5F55\u4E0D\u4E00\u81F4\uFF0C\u6267\u884C update-skills`
2607
+ );
2608
+ await runStep("update-skills", async () => {
2609
+ await syncWorkspaceSkills(cfg, workdir);
2610
+ markSkillsSyncedForCliVersion(workdir, cliVersion);
2611
+ });
2612
+ } else {
2613
+ console.log(`[apm] step=update-skills skipped workdir=${workdir}`);
2614
+ }
2518
2615
  if (signal.aborted) return;
2519
2616
  await runStep(
2520
2617
  "cursor-agent",
@@ -2755,19 +2852,19 @@ async function runCreatePr(options) {
2755
2852
  import path5 from "node:path";
2756
2853
 
2757
2854
  // src/commands/deploy/internal/apm-config.ts
2758
- import { existsSync as existsSync11, readFileSync as readFileSync9 } from "node:fs";
2855
+ import { existsSync as existsSync12, readFileSync as readFileSync10 } from "node:fs";
2759
2856
  import { resolve as resolve4 } from "node:path";
2760
2857
  function loadApmConfig(options) {
2761
2858
  const p = resolve4(
2762
2859
  process.cwd(),
2763
2860
  options?.configPath ?? resolve4(workspaceApmDir(), "apm.config.json")
2764
2861
  );
2765
- if (!existsSync11(p)) {
2862
+ if (!existsSync12(p)) {
2766
2863
  console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
2767
2864
  process.exit(1);
2768
2865
  }
2769
2866
  try {
2770
- const raw = readFileSync9(p, "utf8");
2867
+ const raw = readFileSync10(p, "utf8");
2771
2868
  return JSON.parse(raw);
2772
2869
  } catch (e) {
2773
2870
  console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
@@ -2889,7 +2986,7 @@ import path4 from "node:path";
2889
2986
  import Docker from "dockerode";
2890
2987
 
2891
2988
  // src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
2892
- import { existsSync as existsSync12, readFileSync as readFileSync10 } from "node:fs";
2989
+ import { existsSync as existsSync13, readFileSync as readFileSync11 } from "node:fs";
2893
2990
  import path from "node:path";
2894
2991
  function asOptionalTlsBuffer(value) {
2895
2992
  if (typeof value !== "string") {
@@ -2901,8 +2998,8 @@ function asOptionalTlsBuffer(value) {
2901
2998
  if (normalized === "") {
2902
2999
  return void 0;
2903
3000
  }
2904
- if (existsSync12(normalized)) {
2905
- return readFileSync10(normalized);
3001
+ if (existsSync13(normalized)) {
3002
+ return readFileSync11(normalized);
2906
3003
  }
2907
3004
  const looksLikePath = /[\\/]/.test(normalized) || normalized.endsWith(".pem");
2908
3005
  if (looksLikePath) {
@@ -3112,7 +3209,7 @@ var DockerodeClient = class {
3112
3209
  var createDockerodeClient = (config) => new DockerodeClient(config);
3113
3210
 
3114
3211
  // src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
3115
- import { existsSync as existsSync13, readFileSync as readFileSync11, statSync as statSync5 } from "node:fs";
3212
+ import { existsSync as existsSync14, readFileSync as readFileSync12, statSync as statSync5 } from "node:fs";
3116
3213
  import path2 from "node:path";
3117
3214
  function stripSurroundingQuotes(value) {
3118
3215
  const t = value.trim();
@@ -3129,10 +3226,10 @@ function loadEnvFromFile(envFilePath) {
3129
3226
  return {};
3130
3227
  }
3131
3228
  const targetPath = path2.resolve(envFilePath);
3132
- if (!existsSync13(targetPath) || !statSync5(targetPath).isFile()) {
3229
+ if (!existsSync14(targetPath) || !statSync5(targetPath).isFile()) {
3133
3230
  return {};
3134
3231
  }
3135
- const raw = readFileSync11(targetPath, "utf-8");
3232
+ const raw = readFileSync12(targetPath, "utf-8");
3136
3233
  const result = {};
3137
3234
  for (const line of raw.split(/\r?\n/)) {
3138
3235
  const normalized = line.trim();
@@ -3303,12 +3400,12 @@ function dockerPushImage(params, cwd) {
3303
3400
  }
3304
3401
 
3305
3402
  // src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
3306
- import { existsSync as existsSync14 } from "node:fs";
3403
+ import { existsSync as existsSync15 } from "node:fs";
3307
3404
  import path3 from "node:path";
3308
3405
  function resolveDockerBuildPaths(cwd) {
3309
3406
  const dockerfilePath = path3.join(cwd, "Dockerfile");
3310
3407
  Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
3311
- if (!existsSync14(dockerfilePath)) {
3408
+ if (!existsSync15(dockerfilePath)) {
3312
3409
  throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
3313
3410
  }
3314
3411
  Logger.info("\u2713 Dockerfile \u5B58\u5728");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "6.0.55",
3
+ "version": "6.0.57",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,
@@ -25,6 +25,7 @@
25
25
 
26
26
  - 须标注**目标数据库**(库名/实例名、类型如 MySQL;同一变更涉及多库时分别标注)
27
27
  - 待执行的 SQL 语句须放在 ` ```sql ` 代码块中,按执行顺序逐条列出
28
+ - SQL 须写完整、可直接复制执行,**禁止**用 `...` 或「省略」占位代替表名、字段名、条件等任何片段
28
29
 
29
30
  ## 文档同步
30
31
 
@@ -52,9 +52,9 @@
52
52
 
53
53
  - **目标数据库**(库名/实例名、类型如 MySQL;同一变更涉及多库时分别标注)
54
54
  - 变更摘要(改了什么表/数据、为什么)
55
- - 完整 SQL 语句(按执行顺序排列;**每条须放在 ` ```sql ` 代码块中**,便于复制执行)
55
+ - 完整 SQL 语句(按执行顺序排列;**每条须放在 ` ```sql ` 代码块中**,便于复制执行;**禁止**用 `...` 或「省略」占位代替任何片段)
56
56
  - 执行环境说明(测试/生产是否一致、是否需人工执行)
57
- - 回滚方案(如适用;回滚 SQL 同样用 ` ```sql ` 代码块)
57
+ - 回滚方案(如适用;回滚 SQL 同样用 ` ```sql ` 代码块,须完整可执行,禁止省略)
58
58
 
59
59
  示例:
60
60
 
@@ -18,12 +18,12 @@
18
18
 
19
19
  ### 步骤 2:四项检查
20
20
 
21
- | 检查项 | 判定 |
22
- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
23
- | **白名单对账** | diff 中出现白名单之外的文件,且计划未更新说明 → **不通过** |
24
- | **需求相关性** | 存在与本需求无关的改动(顺手重构、改格式、动了无关逻辑)→ **不通过** |
25
- | **计划落实** | 计划「实现步骤」中的关键点在 diff 中找不到对应实现 → **不通过** |
26
- | **SQL 文档** | **仅后端**:diff 涉及 SQL 改动(DDL/DML、表结构、Mapper/XML 中 SQL 等),但 `docs/SQL.md` 缺失、未标注目标数据库、或与改动不一致 → **不通过** |
21
+ | 检查项 | 判定 |
22
+ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
23
+ | **白名单对账** | diff 中出现白名单之外的文件,且计划未更新说明 → **不通过** |
24
+ | **需求相关性** | 存在与本需求无关的改动(顺手重构、改格式、动了无关逻辑)→ **不通过** |
25
+ | **计划落实** | 计划「实现步骤」中的关键点在 diff 中找不到对应实现 → **不通过** |
26
+ | **SQL 文档** | **仅后端**:diff 涉及 SQL 改动(DDL/DML、表结构、Mapper/XML 中 SQL 等),但 `docs/SQL.md` 缺失、未标注目标数据库、与改动不一致、或 SQL 代码块含 `...`/「省略」占位 → **不通过** |
27
27
 
28
28
  注意事项:
29
29