ai-project-manage-cli 8.1.23 → 8.1.25

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
@@ -1430,9 +1430,77 @@ async function runConnect(options) {
1430
1430
  }
1431
1431
  }
1432
1432
 
1433
+ // src/utils/home-repository.ts
1434
+ import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
1435
+ import { join as join8, resolve as resolve3 } from "path";
1436
+ var DEFAULT_REPOS_MANIFEST = "/home/repository.json";
1437
+ function isSimpleChildDirName(name) {
1438
+ if (!name || name === "." || name === "..") return false;
1439
+ if (name.includes("/") || name.includes("\\")) return false;
1440
+ return true;
1441
+ }
1442
+ function resolveReposManifestPath(explicit) {
1443
+ const fromOpt = explicit?.trim() || "";
1444
+ if (fromOpt) return fromOpt;
1445
+ const fromEnv = process.env.APM_REPOS_MANIFEST?.trim() || "";
1446
+ if (fromEnv) return fromEnv;
1447
+ return null;
1448
+ }
1449
+ function resolveCliReposManifestPath(explicit) {
1450
+ return resolveReposManifestPath(explicit) ?? DEFAULT_REPOS_MANIFEST;
1451
+ }
1452
+ function loadHomeRepositoryEntries(manifestPath2) {
1453
+ const label = manifestPath2.trim();
1454
+ if (!label) {
1455
+ throw new Error("[apm] \u591A\u4ED3\u6E05\u5355\u8DEF\u5F84\u4E3A\u7A7A");
1456
+ }
1457
+ const path = toFsPath(label);
1458
+ if (!existsSync6(path)) {
1459
+ throw new Error(`[apm] \u672A\u627E\u5230\u591A\u4ED3\u6E05\u5355 ${label}`);
1460
+ }
1461
+ let parsed;
1462
+ try {
1463
+ parsed = JSON.parse(readFileSync5(path, "utf8"));
1464
+ } catch (err) {
1465
+ const detail = err instanceof Error ? err.message : String(err);
1466
+ throw new Error(`[apm] \u89E3\u6790 ${label} \u5931\u8D25: ${detail}`);
1467
+ }
1468
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1469
+ throw new Error(
1470
+ `[apm] ${label} \u987B\u4E3A\u5BF9\u8C61\uFF1Akey=\u4E0B\u7EA7\u76EE\u5F55\uFF0Cvalue=git \u5730\u5740`
1471
+ );
1472
+ }
1473
+ const entries = [];
1474
+ for (const [rawKey, rawVal] of Object.entries(
1475
+ parsed
1476
+ )) {
1477
+ const pathKey = rawKey.trim();
1478
+ if (!isSimpleChildDirName(pathKey)) {
1479
+ throw new Error(
1480
+ `[apm] ${label} \u975E\u6CD5\u76EE\u5F55 key: ${JSON.stringify(rawKey)}\uFF08\u987B\u4E3A\u5F53\u524D\u4E0B\u7EA7\u76EE\u5F55\u540D\uFF09`
1481
+ );
1482
+ }
1483
+ if (typeof rawVal !== "string" || !rawVal.trim()) {
1484
+ throw new Error(
1485
+ `[apm] ${label}[${pathKey}] \u987B\u4E3A\u975E\u7A7A git \u5730\u5740\u5B57\u7B26\u4E32`
1486
+ );
1487
+ }
1488
+ entries.push({ path: pathKey, remoteUrl: rawVal.trim() });
1489
+ }
1490
+ entries.sort((a, b) => a.path.localeCompare(b.path));
1491
+ if (entries.length === 0) {
1492
+ throw new Error(`[apm] ${label} \u4E3A\u7A7A\uFF0C\u65E0\u4ED3\u5E93\u53EF\u786E\u8BA4`);
1493
+ }
1494
+ return entries;
1495
+ }
1496
+ function resolveHomeRepositoryAbsolutePaths(workdirInput, entries) {
1497
+ const workdir = resolveWorkdirPath(workdirInput);
1498
+ return entries.map((entry) => resolve3(join8(workdir, entry.path)));
1499
+ }
1500
+
1433
1501
  // src/utils/git.ts
1434
1502
  import { execFile } from "child_process";
1435
- import { dirname as dirname3, join as join8 } from "path";
1503
+ import { dirname as dirname3, join as join9 } from "path";
1436
1504
  import { fileURLToPath as fileURLToPath3 } from "url";
1437
1505
  import { promisify } from "util";
1438
1506
  var execFileAsync = promisify(execFile);
@@ -1516,7 +1584,7 @@ async function resolveGitRepoRoot(cwd) {
1516
1584
  return (await execGit(cwd, ["rev-parse", "--show-toplevel"], true)).trim();
1517
1585
  }
1518
1586
  function resolveGitScript(name) {
1519
- return join8(dirname3(fileURLToPath3(import.meta.url)), "scripts", name);
1587
+ return join9(dirname3(fileURLToPath3(import.meta.url)), "scripts", name);
1520
1588
  }
1521
1589
  async function runGitScript(name, options) {
1522
1590
  const skipCode = options.skipCode ?? 2;
@@ -1573,69 +1641,18 @@ async function pushCurrentBranch(cwd) {
1573
1641
  });
1574
1642
  }
1575
1643
 
1576
- // src/utils/home-repository.ts
1577
- import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
1578
- import { join as join9, resolve as resolve3 } from "path";
1579
- var DEFAULT_REPOS_MANIFEST = "/home/repository.json";
1580
- function isSimpleChildDirName(name) {
1581
- if (!name || name === "." || name === "..") return false;
1582
- if (name.includes("/") || name.includes("\\")) return false;
1583
- return true;
1584
- }
1585
- function resolveReposManifestPath(explicit) {
1586
- const fromOpt = explicit?.trim() || "";
1587
- if (fromOpt) return fromOpt;
1588
- const fromEnv = process.env.APM_REPOS_MANIFEST?.trim() || "";
1589
- if (fromEnv) return fromEnv;
1590
- return DEFAULT_REPOS_MANIFEST;
1591
- }
1592
- function loadHomeRepositoryEntries(manifestPath) {
1593
- const label = manifestPath.trim() || DEFAULT_REPOS_MANIFEST;
1594
- const path = toFsPath(label);
1595
- if (!existsSync6(path)) {
1596
- throw new Error(`[apm] \u672A\u627E\u5230\u591A\u4ED3\u6E05\u5355 ${label}`);
1597
- }
1598
- let parsed;
1599
- try {
1600
- parsed = JSON.parse(readFileSync5(path, "utf8"));
1601
- } catch (err) {
1602
- const detail = err instanceof Error ? err.message : String(err);
1603
- throw new Error(`[apm] \u89E3\u6790 ${label} \u5931\u8D25: ${detail}`);
1604
- }
1605
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1606
- throw new Error(
1607
- `[apm] ${label} \u987B\u4E3A\u5BF9\u8C61\uFF1Akey=\u4E0B\u7EA7\u76EE\u5F55\uFF0Cvalue=git \u5730\u5740`
1608
- );
1609
- }
1610
- const entries = [];
1611
- for (const [rawKey, rawVal] of Object.entries(
1612
- parsed
1613
- )) {
1614
- const pathKey = rawKey.trim();
1615
- if (!isSimpleChildDirName(pathKey)) {
1616
- throw new Error(
1617
- `[apm] ${label} \u975E\u6CD5\u76EE\u5F55 key: ${JSON.stringify(rawKey)}\uFF08\u987B\u4E3A\u5F53\u524D\u4E0B\u7EA7\u76EE\u5F55\u540D\uFF09`
1618
- );
1619
- }
1620
- if (typeof rawVal !== "string" || !rawVal.trim()) {
1621
- throw new Error(
1622
- `[apm] ${label}[${pathKey}] \u987B\u4E3A\u975E\u7A7A git \u5730\u5740\u5B57\u7B26\u4E32`
1623
- );
1624
- }
1625
- entries.push({ path: pathKey, remoteUrl: rawVal.trim() });
1626
- }
1627
- entries.sort((a, b) => a.path.localeCompare(b.path));
1628
- if (entries.length === 0) {
1629
- throw new Error(`[apm] ${label} \u4E3A\u7A7A\uFF0C\u65E0\u4ED3\u5E93\u53EF\u786E\u8BA4`);
1630
- }
1631
- return entries;
1632
- }
1633
- function resolveHomeRepositoryAbsolutePaths(workdirInput, entries) {
1634
- const workdir = resolveWorkdirPath(workdirInput);
1635
- return entries.map((entry) => resolve3(join9(workdir, entry.path)));
1636
- }
1644
+ // src/commands/branch.ts
1645
+ import { basename as basename2 } from "path";
1637
1646
 
1638
1647
  // src/utils/workspace-repos.ts
1648
+ import {
1649
+ existsSync as existsSync7,
1650
+ mkdirSync as mkdirSync5,
1651
+ readFileSync as readFileSync6,
1652
+ readdirSync as readdirSync3,
1653
+ statSync as statSync3,
1654
+ writeFileSync as writeFileSync4
1655
+ } from "fs";
1639
1656
  import { basename, dirname as dirname4, join as join10, relative, resolve as resolve4 } from "path";
1640
1657
 
1641
1658
  // src/utils/git-remote.ts
@@ -1644,15 +1661,103 @@ import { promisify as promisify2 } from "util";
1644
1661
  var execFileAsync2 = promisify2(execFile2);
1645
1662
 
1646
1663
  // src/utils/workspace-repos.ts
1664
+ var WORKSPACE_REPOS_MANIFEST = "workspace-repos.json";
1665
+ var WORKSPACE_REPOS_VERSION = 1;
1666
+ function manifestPath(workdir) {
1667
+ return join10(workspaceApmDir(workdir), WORKSPACE_REPOS_MANIFEST);
1668
+ }
1669
+ function absoluteRepoPath(workdir, entry) {
1670
+ return resolve4(workdir, entry.path);
1671
+ }
1647
1672
  function toRelativeRepoPath(workdir, repoAbs) {
1648
1673
  return relative(workdir, repoAbs).replace(/\\/g, "/");
1649
1674
  }
1675
+ function normalizeRepoEntry(raw) {
1676
+ if (!raw || typeof raw !== "object") return null;
1677
+ const o = raw;
1678
+ if (typeof o.path !== "string" || !o.path.trim()) return null;
1679
+ const path = o.path.trim().replace(/\\/g, "/");
1680
+ if (path === "." || path.includes("/")) return null;
1681
+ const entry = { path };
1682
+ if (typeof o.remoteUrl === "string" && o.remoteUrl.trim()) {
1683
+ entry.remoteUrl = o.remoteUrl.trim();
1684
+ } else if (o.remoteUrl === null) {
1685
+ entry.remoteUrl = null;
1686
+ }
1687
+ return entry;
1688
+ }
1689
+ function readManifest(workdir) {
1690
+ const path = toFsPath(manifestPath(workdir));
1691
+ if (!existsSync7(path)) {
1692
+ return null;
1693
+ }
1694
+ try {
1695
+ const raw = JSON.parse(
1696
+ readFileSync6(path, "utf8")
1697
+ );
1698
+ if (raw?.version !== WORKSPACE_REPOS_VERSION) {
1699
+ return null;
1700
+ }
1701
+ if (raw.kind !== "multi") {
1702
+ return null;
1703
+ }
1704
+ if (!Array.isArray(raw.repos) || raw.repos.length === 0) {
1705
+ return null;
1706
+ }
1707
+ if (typeof raw.workdir !== "string" || !raw.workdir.trim()) {
1708
+ return null;
1709
+ }
1710
+ const repos = raw.repos.map((item) => normalizeRepoEntry(item)).filter((item) => item != null);
1711
+ if (repos.length === 0) {
1712
+ return null;
1713
+ }
1714
+ return {
1715
+ version: WORKSPACE_REPOS_VERSION,
1716
+ kind: "multi",
1717
+ workdir: raw.workdir,
1718
+ repos,
1719
+ scannedAt: typeof raw.scannedAt === "string" && raw.scannedAt.trim() ? raw.scannedAt : (/* @__PURE__ */ new Date()).toISOString()
1720
+ };
1721
+ } catch {
1722
+ return null;
1723
+ }
1724
+ }
1725
+ function loadWorkspaceReposCache(workdirInput) {
1726
+ const workdir = resolveWorkdirPath(workdirInput);
1727
+ const cached = readManifest(workdir);
1728
+ if (!cached) {
1729
+ throw new Error(
1730
+ `[apm] \u672A\u627E\u5230\u5DE5\u4F5C\u533A\u4ED3\u5E93\u7F13\u5B58 .apm/${WORKSPACE_REPOS_MANIFEST}\uFF0C\u8BF7\u5148\u6267\u884C\u4ED3\u5E93\u68C0\u6D4B`
1731
+ );
1732
+ }
1733
+ if (normalizeWorkdirPath(cached.workdir) !== normalizeWorkdirPath(workdir)) {
1734
+ throw new Error(
1735
+ `[apm] \u5DE5\u4F5C\u533A\u4ED3\u5E93\u7F13\u5B58 workdir \u4E0D\u5339\u914D\uFF1A\u7F13\u5B58=${cached.workdir} \u5F53\u524D=${workdir}\uFF0C\u8BF7\u5F3A\u5236\u91CD\u65B0\u626B\u63CF`
1736
+ );
1737
+ }
1738
+ if (!Array.isArray(cached.repos) || cached.repos.length === 0) {
1739
+ throw new Error(`[apm] \u5DE5\u4F5C\u533A\u4ED3\u5E93\u7F13\u5B58\u65E0\u6548\uFF08repos \u4E3A\u7A7A\uFF09\uFF0C\u8BF7\u5F3A\u5236\u91CD\u65B0\u626B\u63CF`);
1740
+ }
1741
+ console.log(
1742
+ `[apm] \u8BFB\u53D6\u5DE5\u4F5C\u533A\u4ED3\u5E93\u7F13\u5B58 repos=${cached.repos.map((r) => r.path).join(", ")}`
1743
+ );
1744
+ return {
1745
+ ...cached,
1746
+ workdir
1747
+ };
1748
+ }
1749
+ function resolveWorkspaceRepoAbsolutePaths(manifest) {
1750
+ return manifest.repos.map(
1751
+ (entry) => absoluteRepoPath(manifest.workdir, entry)
1752
+ );
1753
+ }
1650
1754
  function formatRepoLabel(workdir, repoAbs) {
1651
1755
  const rel = toRelativeRepoPath(resolveWorkdirPath(workdir), repoAbs);
1652
1756
  return rel || basename(repoAbs) || repoAbs;
1653
1757
  }
1654
1758
 
1655
1759
  // src/commands/branch.ts
1760
+ import { existsSync as existsSync8 } from "fs";
1656
1761
  var TASK_BRANCH_PREFIX = "feat/task-";
1657
1762
  async function syncTaskRepos(workdir, options = {}) {
1658
1763
  const opts = typeof options === "string" ? { message: options } : options;
@@ -1724,12 +1829,32 @@ async function ensureTaskFeatureBranch(branch, projectId, options) {
1724
1829
  await ensureFeatureBranch(gitRoot, branch, projectId);
1725
1830
  return branch;
1726
1831
  }
1832
+ function isWorkspaceReposManifestFile(manifestPath2) {
1833
+ return basename2(manifestPath2.replace(/\\/g, "/")) === WORKSPACE_REPOS_MANIFEST;
1834
+ }
1727
1835
  function resolveTaskBranchRepoRoots(workdirPath, reposManifest) {
1728
- const manifestPath = resolveReposManifestPath(reposManifest);
1729
- const entries = loadHomeRepositoryEntries(manifestPath);
1730
- const roots = resolveHomeRepositoryAbsolutePaths(workdirPath, entries);
1836
+ const manifestPath2 = resolveReposManifestPath(reposManifest);
1837
+ if (manifestPath2 && !isWorkspaceReposManifestFile(manifestPath2)) {
1838
+ const entries = loadHomeRepositoryEntries(manifestPath2);
1839
+ const roots2 = resolveHomeRepositoryAbsolutePaths(workdirPath, entries);
1840
+ console.log(
1841
+ `[apm] \u8BFB\u53D6\u5916\u90E8\u591A\u4ED3\u6E05\u5355 ${manifestPath2} repos=${entries.map((e) => `${e.path}\u2192${e.remoteUrl}`).join(", ")}`
1842
+ );
1843
+ return roots2;
1844
+ }
1845
+ if (manifestPath2 && isWorkspaceReposManifestFile(manifestPath2)) {
1846
+ const fsPath = toFsPath(manifestPath2);
1847
+ if (!existsSync8(fsPath)) {
1848
+ throw new Error(`[apm] \u672A\u627E\u5230\u5DE5\u4F5C\u533A\u4ED3\u5E93\u6E05\u5355 ${manifestPath2}`);
1849
+ }
1850
+ console.log(`[apm] \u4F7F\u7528\u663E\u5F0F\u5DE5\u4F5C\u533A\u4ED3\u5E93\u6E05\u5355 ${manifestPath2}`);
1851
+ }
1852
+ const workspace = loadWorkspaceReposCache(workdirPath);
1853
+ const roots = resolveWorkspaceRepoAbsolutePaths(workspace);
1731
1854
  console.log(
1732
- `[apm] \u8BFB\u53D6 ${manifestPath} repos=${entries.map((e) => `${e.path}\u2192${e.remoteUrl}`).join(", ")}`
1855
+ `[apm] \u8BFB\u53D6\u5DE5\u4F5C\u533A\u4ED3\u5E93\u6E05\u5355 repos=${workspace.repos.map(
1856
+ (r) => r.remoteUrl ? `${r.path}\u2192${r.remoteUrl}` : r.path
1857
+ ).join(", ")}`
1733
1858
  );
1734
1859
  return roots;
1735
1860
  }
@@ -1799,7 +1924,7 @@ async function runEnsureTaskBranchCli(options = {}) {
1799
1924
  }
1800
1925
  const result = await runTaskBranch(taskId, projectId, {
1801
1926
  cwd: workdir,
1802
- reposManifest: options.reposManifest
1927
+ reposManifest: resolveCliReposManifestPath(options.reposManifest)
1803
1928
  });
1804
1929
  console.log(
1805
1930
  `[apm] \u7279\u6027\u5206\u652F\u5DF2\u5C31\u7EEA\uFF08\u5168\u90E8 ${result.repos.length} \u4E2A\u4ED3\uFF09 branch=${result.branch} projectId=${result.projectId}`
@@ -1808,7 +1933,7 @@ async function runEnsureTaskBranchCli(options = {}) {
1808
1933
 
1809
1934
  // src/utils/user-prompt-cache.ts
1810
1935
  import { createHash } from "crypto";
1811
- import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
1936
+ import { existsSync as existsSync9, mkdirSync as mkdirSync6, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
1812
1937
  import { join as join11 } from "path";
1813
1938
  import { tmpdir } from "os";
1814
1939
  var CACHE_ROOT = join11(tmpdir(), "apm-hooks");
@@ -1830,9 +1955,9 @@ function formatUserPromptAsCommitMessage(prompt) {
1830
1955
  }
1831
1956
  function loadUserPromptCommitMessage(workdirInput) {
1832
1957
  const path = toFsPath(userPromptCachePath(workdirInput));
1833
- if (!existsSync7(path)) return null;
1958
+ if (!existsSync9(path)) return null;
1834
1959
  try {
1835
- const raw = readFileSync6(path, "utf8");
1960
+ const raw = readFileSync7(path, "utf8");
1836
1961
  const msg = formatUserPromptAsCommitMessage(raw);
1837
1962
  return msg || null;
1838
1963
  } catch {
@@ -1873,14 +1998,14 @@ async function runCommitTaskReposCli(options = {}) {
1873
1998
  }
1874
1999
  const result = await syncTaskRepos(workdir, {
1875
2000
  message,
1876
- reposManifest: options.reposManifest
2001
+ reposManifest: resolveCliReposManifestPath(options.reposManifest)
1877
2002
  });
1878
2003
  console.log(
1879
2004
  `[apm] \u591A\u4ED3\u6536\u5C3E\u5B8C\u6210 committed=${result.committed} commitSkipped=${result.commitSkipped} pushed=${result.pushed} pushSkipped=${result.pushSkipped} failed=${result.failed.length} repos=${result.repos.length}`
1880
2005
  );
1881
2006
  if (result.failed.length > 0) {
1882
2007
  throw new Error(
1883
- `[apm] \u4EE5\u4E0B\u4ED3\u5E93 commit/push \u5931\u8D25: ${result.failed.join(", ")}`
2008
+ `[apm] \u591A\u4ED3\u6536\u5C3E\u90E8\u5206\u5931\u8D25: ${result.failed.join(", ")}`
1884
2009
  );
1885
2010
  }
1886
2011
  }
@@ -518,7 +518,6 @@ async function pushCurrentBranch(cwd) {
518
518
  // src/utils/home-repository.ts
519
519
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
520
520
  import { join as join4, resolve as resolve3 } from "path";
521
- var DEFAULT_REPOS_MANIFEST = "/home/repository.json";
522
521
  function isSimpleChildDirName(name) {
523
522
  if (!name || name === "." || name === "..") return false;
524
523
  if (name.includes("/") || name.includes("\\")) return false;
@@ -529,10 +528,13 @@ function resolveReposManifestPath(explicit) {
529
528
  if (fromOpt) return fromOpt;
530
529
  const fromEnv = process.env.APM_REPOS_MANIFEST?.trim() || "";
531
530
  if (fromEnv) return fromEnv;
532
- return DEFAULT_REPOS_MANIFEST;
531
+ return null;
533
532
  }
534
533
  function loadHomeRepositoryEntries(manifestPath2) {
535
- const label = manifestPath2.trim() || DEFAULT_REPOS_MANIFEST;
534
+ const label = manifestPath2.trim();
535
+ if (!label) {
536
+ throw new Error("[apm] \u591A\u4ED3\u6E05\u5355\u8DEF\u5F84\u4E3A\u7A7A");
537
+ }
536
538
  const path = toFsPath(label);
537
539
  if (!existsSync2(path)) {
538
540
  throw new Error(`[apm] \u672A\u627E\u5230\u591A\u4ED3\u6E05\u5355 ${label}`);
@@ -577,6 +579,9 @@ function resolveHomeRepositoryAbsolutePaths(workdirInput, entries) {
577
579
  return entries.map((entry) => resolve3(join4(workdir, entry.path)));
578
580
  }
579
581
 
582
+ // src/commands/branch.ts
583
+ import { basename as basename2 } from "path";
584
+
580
585
  // src/utils/workspace-repos.ts
581
586
  import {
582
587
  existsSync as existsSync3,
@@ -859,6 +864,7 @@ function formatRepoLabel(workdir, repoAbs) {
859
864
  }
860
865
 
861
866
  // src/commands/branch.ts
867
+ import { existsSync as existsSync4 } from "fs";
862
868
  var TASK_BRANCH_PREFIX = "feat/task-";
863
869
  async function commitWorkspaceReposIfDirty(workdir, message, reposManifest) {
864
870
  const result = await commitTaskReposIfDirty(workdir, message, reposManifest);
@@ -968,12 +974,32 @@ async function resolveProjectIdForTask(taskId) {
968
974
  }
969
975
  return projectId;
970
976
  }
977
+ function isWorkspaceReposManifestFile(manifestPath2) {
978
+ return basename2(manifestPath2.replace(/\\/g, "/")) === WORKSPACE_REPOS_MANIFEST;
979
+ }
971
980
  function resolveTaskBranchRepoRoots(workdirPath, reposManifest) {
972
981
  const manifestPath2 = resolveReposManifestPath(reposManifest);
973
- const entries = loadHomeRepositoryEntries(manifestPath2);
974
- const roots = resolveHomeRepositoryAbsolutePaths(workdirPath, entries);
982
+ if (manifestPath2 && !isWorkspaceReposManifestFile(manifestPath2)) {
983
+ const entries = loadHomeRepositoryEntries(manifestPath2);
984
+ const roots2 = resolveHomeRepositoryAbsolutePaths(workdirPath, entries);
985
+ console.log(
986
+ `[apm] \u8BFB\u53D6\u5916\u90E8\u591A\u4ED3\u6E05\u5355 ${manifestPath2} repos=${entries.map((e) => `${e.path}\u2192${e.remoteUrl}`).join(", ")}`
987
+ );
988
+ return roots2;
989
+ }
990
+ if (manifestPath2 && isWorkspaceReposManifestFile(manifestPath2)) {
991
+ const fsPath = toFsPath(manifestPath2);
992
+ if (!existsSync4(fsPath)) {
993
+ throw new Error(`[apm] \u672A\u627E\u5230\u5DE5\u4F5C\u533A\u4ED3\u5E93\u6E05\u5355 ${manifestPath2}`);
994
+ }
995
+ console.log(`[apm] \u4F7F\u7528\u663E\u5F0F\u5DE5\u4F5C\u533A\u4ED3\u5E93\u6E05\u5355 ${manifestPath2}`);
996
+ }
997
+ const workspace = loadWorkspaceReposCache(workdirPath);
998
+ const roots = resolveWorkspaceRepoAbsolutePaths(workspace);
975
999
  console.log(
976
- `[apm] \u8BFB\u53D6 ${manifestPath2} repos=${entries.map((e) => `${e.path}\u2192${e.remoteUrl}`).join(", ")}`
1000
+ `[apm] \u8BFB\u53D6\u5DE5\u4F5C\u533A\u4ED3\u5E93\u6E05\u5355 repos=${workspace.repos.map(
1001
+ (r) => r.remoteUrl ? `${r.path}\u2192${r.remoteUrl}` : r.path
1002
+ ).join(", ")}`
977
1003
  );
978
1004
  return roots;
979
1005
  }
@@ -1678,7 +1704,7 @@ function createUpsertWebIdeTestCasesTool(options) {
1678
1704
  }
1679
1705
 
1680
1706
  // src/commands/connect/tools/mysql-tools.ts
1681
- import { existsSync as existsSync4, readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
1707
+ import { existsSync as existsSync5, readFileSync as readFileSync4, statSync as statSync3 } from "node:fs";
1682
1708
  import { isAbsolute, relative as relative2, resolve as resolve5 } from "node:path";
1683
1709
  import mysql from "mysql2/promise";
1684
1710
  var MAX_RESULT_ROWS = 100;
@@ -1757,7 +1783,7 @@ function resolveSqlArtifactPath(workdir, absolutePath) {
1757
1783
  return { ok: false, error: "path \u5FC5\u987B\u4F4D\u4E8E\u5F53\u524D\u5DE5\u4F5C\u533A\u5185" };
1758
1784
  }
1759
1785
  const fsPath = toFsPath(targetAbs);
1760
- if (!existsSync4(fsPath)) {
1786
+ if (!existsSync5(fsPath)) {
1761
1787
  return { ok: false, error: `\u6587\u4EF6\u4E0D\u5B58\u5728: ${absolutePath}` };
1762
1788
  }
1763
1789
  let st;
@@ -2632,14 +2658,14 @@ async function runCursorAgent(cfg2, ctx, options) {
2632
2658
  }
2633
2659
 
2634
2660
  // src/commands/connect/webide-agent-registry.ts
2635
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
2661
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "node:fs";
2636
2662
  import { dirname as dirname3, resolve as resolve6 } from "node:path";
2637
2663
  function registryPath(workdir, taskId, fileName = "cursor-agent.json") {
2638
2664
  return resolve6(workdir, ".apm", "webide", taskId, fileName);
2639
2665
  }
2640
2666
  var DESIGN_AGENT_FILE = "design-agent.json";
2641
2667
  function readRegistry(path) {
2642
- if (!existsSync5(path)) {
2668
+ if (!existsSync6(path)) {
2643
2669
  return {};
2644
2670
  }
2645
2671
  try {
@@ -2714,7 +2740,7 @@ function saveWebIdeAgentId(workdir, taskId, agentId) {
2714
2740
  }
2715
2741
  function clearWebIdeAgentId(workdir, taskId) {
2716
2742
  const path = registryPath(workdir, taskId);
2717
- if (!existsSync5(path)) return;
2743
+ if (!existsSync6(path)) return;
2718
2744
  syncWebIdeTaskState(workdir, taskId, { agentId: "" });
2719
2745
  }
2720
2746
  function loadWebIdeDesignAgentId(workdir, taskId) {
@@ -2734,7 +2760,7 @@ function saveWebIdeDesignAgentId(workdir, taskId, agentId) {
2734
2760
  }
2735
2761
  function clearWebIdeDesignAgentId(workdir, taskId) {
2736
2762
  const path = registryPath(workdir, taskId, DESIGN_AGENT_FILE);
2737
- if (!existsSync5(path)) return;
2763
+ if (!existsSync6(path)) return;
2738
2764
  const current = readRegistry(path);
2739
2765
  const next = { ...current };
2740
2766
  delete next.agentId;
@@ -2790,24 +2816,27 @@ async function fetchAssumptionAnswersAndContinueAssessment(options) {
2790
2816
  if (ids.length === 0) {
2791
2817
  throw new Error("\u6CA1\u6709\u5DF2\u786E\u8BA4\u7684\u5047\u8BBE");
2792
2818
  }
2793
- const answers = [];
2819
+ const lines = [];
2820
+ if (title?.trim()) {
2821
+ lines.push(title.trim(), "");
2822
+ }
2823
+ let index = 0;
2794
2824
  for (const id of ids) {
2795
2825
  const row = byId.get(id);
2796
2826
  if (!row || row.status !== "RESOLVED") {
2797
2827
  throw new Error(`\u5047\u8BBE\u5C1A\u672A\u5168\u90E8\u786E\u8BA4: ${id}`);
2798
2828
  }
2799
- answers.push({
2800
- questionId: id,
2801
- prompt: row.prompt,
2802
- selectedOptionId: row.selectedOptionId,
2803
- customAnswer: row.customAnswer,
2804
- resolution: row.resolution
2805
- });
2829
+ const text = asString4(row.resolution) || asString4(row.customAnswer) || asString4(row.selectedOptionId);
2830
+ if (!text) {
2831
+ throw new Error(`\u5047\u8BBE\u7B54\u6848\u4E3A\u7A7A: ${id}`);
2832
+ }
2833
+ index += 1;
2834
+ lines.push(`${index}. ${text}`);
2806
2835
  }
2807
2836
  console.log(
2808
- `[apm] \u5047\u8BBE\u7ED3\u679C\u5DF2\u62C9\u53D6 taskId=${taskId} count=${answers.length}`
2837
+ `[apm] \u5047\u8BBE\u7ED3\u679C\u5DF2\u62C9\u53D6 taskId=${taskId} count=${index}`
2809
2838
  );
2810
- return JSON.stringify({ title, answers }, null, 2);
2839
+ return lines.join("\n");
2811
2840
  }
2812
2841
  function createWebIdeAskQuestionExecute(options) {
2813
2842
  const { cfg: cfg2, taskId, onAwaitingAssumptions } = options;
@@ -3071,7 +3100,7 @@ function resolveMessageReplyFallback(fallback) {
3071
3100
 
3072
3101
  // src/commands/sync-webide-agent.ts
3073
3102
  import {
3074
- existsSync as existsSync6,
3103
+ existsSync as existsSync7,
3075
3104
  mkdirSync as mkdirSync5,
3076
3105
  readdirSync as readdirSync3,
3077
3106
  readFileSync as readFileSync6,
@@ -3087,7 +3116,7 @@ function withTrailingNewline(text) {
3087
3116
  }
3088
3117
  function readRevision(apmDir) {
3089
3118
  const path = toFsPath(join6(apmDir, REVISION_FILE));
3090
- if (!existsSync6(path)) return null;
3119
+ if (!existsSync7(path)) return null;
3091
3120
  try {
3092
3121
  const parsed = JSON.parse(readFileSync6(path, "utf8"));
3093
3122
  if (parsed?.version === 1 && parsed.revision?.trim()) {
@@ -3388,7 +3417,7 @@ async function ensureWebIdePullRequests(cfg2, taskId, workdir) {
3388
3417
  }
3389
3418
 
3390
3419
  // src/commands/sync-webide-attachments.ts
3391
- import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
3420
+ import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
3392
3421
  import { join as join7 } from "path";
3393
3422
  var MANIFEST_FILE = ".sync-manifest.json";
3394
3423
  async function downloadAttachment(cfg2, attachmentId) {
@@ -3406,7 +3435,7 @@ async function downloadAttachment(cfg2, attachmentId) {
3406
3435
  }
3407
3436
  function loadManifest(dir) {
3408
3437
  const path = join7(dir, MANIFEST_FILE);
3409
- if (!existsSync7(path)) {
3438
+ if (!existsSync8(path)) {
3410
3439
  return { version: 1, attachments: {} };
3411
3440
  }
3412
3441
  try {
@@ -3429,7 +3458,7 @@ function saveManifest(dir, manifest) {
3429
3458
  );
3430
3459
  }
3431
3460
  function isAttachmentUpToDate(entry, item, dest) {
3432
- if (!entry || !existsSync7(dest)) return false;
3461
+ if (!entry || !existsSync8(dest)) return false;
3433
3462
  if (entry.name !== item.name) return false;
3434
3463
  const createdAt = item.createdAt ?? "";
3435
3464
  return entry.createdAt === createdAt;
@@ -3476,7 +3505,7 @@ async function syncWebIdeAttachments(cfg2, taskId, workdir, attachments) {
3476
3505
 
3477
3506
  // src/commands/sync-webide-design.ts
3478
3507
  import { createHash } from "crypto";
3479
- import { existsSync as existsSync8, readdirSync as readdirSync4, readFileSync as readFileSync8, statSync as statSync4, writeFileSync as writeFileSync6 } from "fs";
3508
+ import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync8, statSync as statSync4, writeFileSync as writeFileSync6 } from "fs";
3480
3509
  import { join as join8 } from "path";
3481
3510
 
3482
3511
  // src/opc-shared-types.ts
@@ -3499,7 +3528,7 @@ function hashBuffer(buf) {
3499
3528
  }
3500
3529
  function loadManifest2(dir) {
3501
3530
  const path = join8(dir, MANIFEST_FILE2);
3502
- if (!existsSync8(path)) {
3531
+ if (!existsSync9(path)) {
3503
3532
  return { version: 1, files: {} };
3504
3533
  }
3505
3534
  try {
@@ -3543,7 +3572,7 @@ async function syncWebIdeDesign(cfg2, taskId, workdir, artifacts) {
3543
3572
  if (!name || !isWebIdeDesignArtifactName(name)) continue;
3544
3573
  const localPath = join8(dir, name);
3545
3574
  const entry = manifest.files[name];
3546
- if (entry?.contentHash === art.contentHash && existsSync8(localPath)) {
3575
+ if (entry?.contentHash === art.contentHash && existsSync9(localPath)) {
3547
3576
  continue;
3548
3577
  }
3549
3578
  const buf = await downloadDesignFile(cfg2, taskId, name);
@@ -3556,7 +3585,7 @@ async function syncWebIdeDesign(cfg2, taskId, workdir, artifacts) {
3556
3585
  }
3557
3586
  function listLocalDesignArtifactFiles(taskId, workdir) {
3558
3587
  const dir = webideDesignDir(taskId, workdir);
3559
- if (!existsSync8(dir)) return [];
3588
+ if (!existsSync9(dir)) return [];
3560
3589
  const out = [];
3561
3590
  for (const name of readdirSync4(dir)) {
3562
3591
  if (!isWebIdeDesignArtifactName(name)) continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "8.1.23",
3
+ "version": "8.1.25",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,