ai-project-manage-cli 6.0.57 → 6.0.59

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 CHANGED
@@ -27,8 +27,12 @@ apm init [--name=我的项目]
27
27
  ```bash
28
28
  apm pull <sessionId>
29
29
  apm sync-document <sessionId> --file PRD
30
+ apm sync-project-documents
31
+ apm sync-project-documents --push
30
32
  apm update-skills
31
33
  apm branch <sessionId>
32
34
  ```
33
35
 
36
+ `apm pull` 会自动将平台登记的**仓库项目文档**同步到 `.apm/project/`(含 `manifest.json`)。Agent 修改 `.apm/project/` 下文件后,`apm connect` 处理完消息会自动推回平台。
37
+
34
38
  详见仓库根目录 [docs/CLI.md](../../docs/CLI.md)。
package/dist/index.js CHANGED
@@ -149,13 +149,11 @@ var CLI_TEMPLATE_DIR = resolve2(__dirname, "../template");
149
149
  function workspaceApmDir(cwd = resolveWorkdirPath()) {
150
150
  return resolve2(resolve2(cwd), ".apm");
151
151
  }
152
- function assertWorkspaceApmDirExists(workdir) {
152
+ function isWorkspaceApmInitialized(workdir) {
153
153
  const apmDir = workspaceApmDir(workdir);
154
154
  const fsApmDir = toFsPath(apmDir);
155
155
  if (!existsSync(fsApmDir)) {
156
- throw new Error(
157
- `\u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u4E0B\u672A\u68C0\u6D4B\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5728\u8BE5\u4ED3\u5E93\u6839\u76EE\u5F55\u6267\u884C apm init \u5B8C\u6210\u63A5\u5165\u3002`
158
- );
156
+ return false;
159
157
  }
160
158
  const st = statSync(fsApmDir);
161
159
  if (!st.isDirectory()) {
@@ -163,6 +161,7 @@ function assertWorkspaceApmDirExists(workdir) {
163
161
  `\u5DE5\u4F5C\u76EE\u5F55 ${workdir} \u4E0B\u7684 .apm \u4E0D\u662F\u76EE\u5F55\uFF0C\u8BF7\u68C0\u67E5\u672C\u5730\u63A5\u5165\u72B6\u6001\u3002`
164
162
  );
165
163
  }
164
+ return readdirSync(fsApmDir).length > 0;
166
165
  }
167
166
  var APM_GITIGNORE_PATTERNS = [
168
167
  /^\.apm\/?$/,
@@ -457,6 +456,30 @@ var requestConfig = {
457
456
  createPullRequest: defineEndpoint({
458
457
  method: "POST",
459
458
  path: "/cli/pull-requests"
459
+ }),
460
+ getRepositoryProjectDocumentManifest: defineEndpoint({
461
+ method: "GET",
462
+ path: "/cli/repository-project-documents/manifest"
463
+ }),
464
+ listRepositoryProjectDocuments: defineEndpoint({
465
+ method: "GET",
466
+ path: "/cli/repository-project-documents"
467
+ }),
468
+ upsertRepositoryProjectDocument: defineEndpoint({
469
+ method: "PUT",
470
+ path: "/cli/repository-project-documents/upsert"
471
+ }),
472
+ removeRepositoryProjectDocument: defineEndpoint({
473
+ method: "DELETE",
474
+ path: "/cli/repository-project-documents"
475
+ }),
476
+ updateCoordinatorDeploymentStatus: defineEndpoint({
477
+ method: "PUT",
478
+ path: "/cli/coordinator-deployments/status"
479
+ }),
480
+ completeCoordinatorDeployment: defineEndpoint({
481
+ method: "PUT",
482
+ path: "/cli/coordinator-deployments/complete"
460
483
  })
461
484
  }
462
485
  };
@@ -628,8 +651,13 @@ async function commitAndPushGitignore(workdir) {
628
651
  }
629
652
 
630
653
  // src/commands/init.ts
631
- async function runInit(name) {
632
- const workdir = resolveWorkdirPath();
654
+ async function ensureWorkspaceInitialized(workdir, options) {
655
+ if (isWorkspaceApmInitialized(workdir)) {
656
+ return { didInit: false };
657
+ }
658
+ console.log(
659
+ `[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`
660
+ );
633
661
  await ensureWorkspaceApmDirForInit(workdir);
634
662
  if (ensureApmGitignoredInRepo(workdir)) {
635
663
  console.log("[apm] \u5DF2\u5728 .gitignore \u4E2D\u6DFB\u52A0 **/.apm/**");
@@ -638,7 +666,7 @@ async function runInit(name) {
638
666
  const apmDir = workspaceApmDir(workdir);
639
667
  await copyTemplateFiles(apmDir, workdir);
640
668
  const syncResult = await syncRemoteDeploymentConfig(workdir, apmDir);
641
- const trimmedName = name?.trim();
669
+ const trimmedName = options?.name?.trim();
642
670
  if (trimmedName) {
643
671
  const apmConfigPath = toFsPath(join4(apmDir, "apm.config.json"));
644
672
  const config = readFileSync3(apmConfigPath, "utf8");
@@ -652,8 +680,21 @@ async function runInit(name) {
652
680
  );
653
681
  }
654
682
  console.log(`[apm] \u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`);
683
+ return { didInit: true, syncResult };
684
+ }
685
+ async function runInit(name) {
686
+ const workdir = resolveWorkdirPath();
687
+ await ensureWorkspaceApmDirForInit(workdir);
688
+ const { didInit, syncResult } = await ensureWorkspaceInitialized(workdir, {
689
+ name
690
+ });
691
+ if (!didInit) {
692
+ throw new Error(
693
+ "[apm] .apm \u76EE\u5F55\u5DF2\u5B58\u5728\u4E14\u975E\u7A7A\uFF0C\u8BF7\u5148\u5907\u4EFD\u3001\u6E05\u7A7A\u6216\u5220\u9664\u540E\u518D\u6267\u884C init"
694
+ );
695
+ }
655
696
  console.log(`[apm] \u5DE5\u4F5C\u76EE\u5F55\u8DEF\u5F84\uFF1A${workdir}`);
656
- if (!syncResult.synced) {
697
+ if (syncResult && !syncResult.synced) {
657
698
  console.log(
658
699
  "[apm] \u5F53\u524D .apm/apm.config.json \u4E0E .apm/deploy/README.md \u4E3A\u6A21\u677F\u9ED8\u8BA4\u503C\uFF1B\u5B8C\u6210\u5E73\u53F0\u767B\u8BB0\u540E\u6267\u884C apm sync-deploy-config"
659
700
  );
@@ -1086,8 +1127,8 @@ async function runCleanBranches(options = {}) {
1086
1127
  }
1087
1128
 
1088
1129
  // src/commands/pull.ts
1089
- import { writeFileSync as writeFileSync8 } from "fs";
1090
- import { join as join8 } from "path";
1130
+ import { writeFileSync as writeFileSync9 } from "fs";
1131
+ import { join as join9 } from "path";
1091
1132
  import { stringify as yamlStringify } from "yaml";
1092
1133
 
1093
1134
  // src/session-messages-xml.ts
@@ -1383,6 +1424,218 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
1383
1424
  return { written, skipped, removed, repositoryId };
1384
1425
  }
1385
1426
 
1427
+ // src/repository-project-documents-sync.ts
1428
+ import {
1429
+ existsSync as existsSync6,
1430
+ readdirSync as readdirSync3,
1431
+ readFileSync as readFileSync6,
1432
+ rmSync as rmSync3,
1433
+ writeFileSync as writeFileSync8
1434
+ } from "fs";
1435
+ import { createHash } from "crypto";
1436
+ import { dirname as dirname2, join as join8, relative, sep } from "path";
1437
+ var MANIFEST_FILE3 = "manifest.json";
1438
+ function projectDocumentsDir(apmRoot) {
1439
+ return join8(apmRoot ?? workspaceApmDir(), "project");
1440
+ }
1441
+ function projectDocumentLocalPath(apmRoot, documentPath) {
1442
+ const normalized = normalizeLocalDocumentPath(documentPath);
1443
+ return join8(projectDocumentsDir(apmRoot), ...normalized.split("/"));
1444
+ }
1445
+ function normalizeLocalDocumentPath(path10) {
1446
+ const trimmed = path10.trim().replace(/\\/g, "/");
1447
+ if (!trimmed || trimmed.startsWith("/") || /^[a-zA-Z]:/.test(trimmed)) {
1448
+ throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path10}`);
1449
+ }
1450
+ const segments = trimmed.split("/").filter(Boolean);
1451
+ if (segments.some((segment) => segment === ".." || segment === ".")) {
1452
+ throw new Error(`\u975E\u6CD5\u6587\u6863\u8DEF\u5F84: ${path10}`);
1453
+ }
1454
+ return segments.join("/");
1455
+ }
1456
+ function hashLocalFileContent(content) {
1457
+ return createHash("sha256").update(content, "utf8").digest("hex");
1458
+ }
1459
+ function readLocalManifest(apmRoot) {
1460
+ const manifestPath2 = join8(projectDocumentsDir(apmRoot), MANIFEST_FILE3);
1461
+ if (!existsSync6(manifestPath2)) {
1462
+ return null;
1463
+ }
1464
+ try {
1465
+ return JSON.parse(
1466
+ readFileSync6(manifestPath2, "utf8")
1467
+ );
1468
+ } catch {
1469
+ return null;
1470
+ }
1471
+ }
1472
+ function listLocalDocumentPaths(apmRoot) {
1473
+ const root = projectDocumentsDir(apmRoot);
1474
+ if (!existsSync6(root)) {
1475
+ return [];
1476
+ }
1477
+ const paths = [];
1478
+ const walk = (dir) => {
1479
+ for (const entry of readdirSync3(dir, { withFileTypes: true })) {
1480
+ const abs = join8(dir, entry.name);
1481
+ if (entry.isDirectory()) {
1482
+ walk(abs);
1483
+ continue;
1484
+ }
1485
+ if (entry.isFile() && entry.name === MANIFEST_FILE3) {
1486
+ continue;
1487
+ }
1488
+ const rel = relative(root, abs).split(sep).join("/");
1489
+ paths.push(rel);
1490
+ }
1491
+ };
1492
+ walk(root);
1493
+ return paths.sort();
1494
+ }
1495
+ function diffManifestPaths(remote, local) {
1496
+ const remoteMap = new Map(
1497
+ (remote?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
1498
+ );
1499
+ const localMap = new Map(
1500
+ (local?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
1501
+ );
1502
+ const download = [];
1503
+ for (const [path10, hash] of remoteMap) {
1504
+ if (localMap.get(path10) !== hash) {
1505
+ download.push(path10);
1506
+ }
1507
+ }
1508
+ const deleteLocal = [];
1509
+ for (const path10 of localMap.keys()) {
1510
+ if (!remoteMap.has(path10)) {
1511
+ deleteLocal.push(path10);
1512
+ }
1513
+ }
1514
+ return { download, deleteLocal };
1515
+ }
1516
+ async function syncRepositoryProjectDocumentsPull(workdirPath, apmDir) {
1517
+ const empty = {
1518
+ synced: false,
1519
+ repositoryId: null,
1520
+ downloaded: 0,
1521
+ deleted: 0
1522
+ };
1523
+ const cfg = await tryReadApmConfig();
1524
+ if (!cfg || !resolveApiKey(cfg)) {
1525
+ console.log(
1526
+ "[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8DF3\u8FC7\u4ED3\u5E93\u9879\u76EE\u6587\u6863\u540C\u6B65\u3002\n[apm] \u8BF7\u5148\u6267\u884C apm login\u3002"
1527
+ );
1528
+ return empty;
1529
+ }
1530
+ const api = createApmApiClient(cfg);
1531
+ const { repositoryId, diagnostic } = await resolveRepositoryIdForSync(
1532
+ api,
1533
+ workdirPath
1534
+ );
1535
+ if (!repositoryId) {
1536
+ console.log(
1537
+ `[apm] \u672A\u80FD\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863\u3002
1538
+ ${diagnostic ?? ""}`
1539
+ );
1540
+ return empty;
1541
+ }
1542
+ const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
1543
+ const projectDir = projectDocumentsDir(targetApmDir);
1544
+ await ensureDirExists(projectDir);
1545
+ const { manifest: remoteManifest } = await api.cli.getRepositoryProjectDocumentManifest({ repositoryId });
1546
+ if (!remoteManifest) {
1547
+ console.log(
1548
+ `[apm] \u4ED3\u5E93 ${repositoryId} \u65E0\u9879\u76EE\u6587\u6863 manifest\uFF0C\u8DF3\u8FC7\u540C\u6B65\u3002`
1549
+ );
1550
+ return { ...empty, repositoryId };
1551
+ }
1552
+ const localManifest = readLocalManifest(targetApmDir);
1553
+ const { download, deleteLocal } = diffManifestPaths(
1554
+ remoteManifest,
1555
+ localManifest
1556
+ );
1557
+ let downloaded = 0;
1558
+ if (download.length > 0) {
1559
+ const { list } = await api.cli.listRepositoryProjectDocuments({
1560
+ repositoryId,
1561
+ paths: download.join(",")
1562
+ });
1563
+ for (const doc of list) {
1564
+ const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, doc.path));
1565
+ await ensureDirExists(dirname2(absPath));
1566
+ writeFileSync8(absPath, doc.content, "utf8");
1567
+ downloaded += 1;
1568
+ }
1569
+ }
1570
+ let deleted = 0;
1571
+ for (const path10 of deleteLocal) {
1572
+ const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, path10));
1573
+ if (existsSync6(absPath)) {
1574
+ rmSync3(absPath, { force: true });
1575
+ deleted += 1;
1576
+ }
1577
+ }
1578
+ writeFileSync8(
1579
+ toFsPath(join8(projectDir, MANIFEST_FILE3)),
1580
+ `${JSON.stringify(remoteManifest, null, 2)}
1581
+ `,
1582
+ "utf8"
1583
+ );
1584
+ console.log(
1585
+ `[apm] \u5DF2\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863: \u4E0B\u8F7D ${downloaded}\uFF0C\u5220\u9664\u672C\u5730 ${deleted}`
1586
+ );
1587
+ return {
1588
+ synced: true,
1589
+ repositoryId,
1590
+ downloaded,
1591
+ deleted
1592
+ };
1593
+ }
1594
+ async function syncRepositoryProjectDocumentsPush(cfg, workdirPath, apmRoot) {
1595
+ const api = createApmApiClient(cfg);
1596
+ const { repositoryId } = await resolveRepositoryIdForSync(api, workdirPath);
1597
+ if (!repositoryId) {
1598
+ return 0;
1599
+ }
1600
+ const targetApmDir = apmRoot ?? workspaceApmDir(workdirPath);
1601
+ const localPaths = listLocalDocumentPaths(targetApmDir);
1602
+ if (localPaths.length === 0) {
1603
+ console.log("[apm] \u4ED3\u5E93\u9879\u76EE\u6587\u6863\u65E0\u672C\u5730\u6587\u4EF6\uFF0C\u8DF3\u8FC7\u63A8\u9001");
1604
+ return 0;
1605
+ }
1606
+ const remoteManifest = (await api.cli.getRepositoryProjectDocumentManifest({ repositoryId })).manifest ?? null;
1607
+ const remoteHashByPath = new Map(
1608
+ (remoteManifest?.documents ?? []).map((doc) => [doc.path, doc.contentHash])
1609
+ );
1610
+ const remoteDescriptionByPath = new Map(
1611
+ (remoteManifest?.documents ?? []).map((doc) => [
1612
+ doc.path,
1613
+ doc.description
1614
+ ])
1615
+ );
1616
+ let synced = 0;
1617
+ for (const path10 of localPaths) {
1618
+ const absPath = toFsPath(projectDocumentLocalPath(targetApmDir, path10));
1619
+ const content = readFileSync6(absPath, "utf8");
1620
+ const contentHash = hashLocalFileContent(content);
1621
+ if (remoteHashByPath.get(path10) === contentHash) {
1622
+ continue;
1623
+ }
1624
+ await api.cli.upsertRepositoryProjectDocument({
1625
+ repositoryId,
1626
+ path: path10,
1627
+ content,
1628
+ description: remoteDescriptionByPath.get(path10) ?? void 0
1629
+ });
1630
+ synced += 1;
1631
+ console.log(`[apm] \u5DF2\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863: ${path10}`);
1632
+ }
1633
+ if (synced === 0) {
1634
+ console.log("[apm] \u4ED3\u5E93\u9879\u76EE\u6587\u6863\u65E0\u53D8\u5316\uFF0C\u8DF3\u8FC7\u63A8\u9001");
1635
+ }
1636
+ return synced;
1637
+ }
1638
+
1386
1639
  // src/commands/pull.ts
1387
1640
  async function runPull(sessionId, remoteWorkdir) {
1388
1641
  const trimmedId = sessionId.trim();
@@ -1406,20 +1659,20 @@ async function runPull(sessionId, remoteWorkdir) {
1406
1659
  const dir = sessionDir(trimmedId, apmRoot);
1407
1660
  const docsDir = sessionDocsDir(trimmedId, apmRoot);
1408
1661
  await ensureDirExists(docsDir);
1409
- writeFileSync8(
1662
+ writeFileSync9(
1410
1663
  sessionRulePath(trimmedId, apmRoot),
1411
1664
  detail.description ?? "",
1412
1665
  "utf8"
1413
1666
  );
1414
- writeFileSync8(
1667
+ writeFileSync9(
1415
1668
  sessionTaskPath(trimmedId, apmRoot),
1416
1669
  detail.task.description ?? "",
1417
1670
  "utf8"
1418
1671
  );
1419
- writeFileSync8(sessionTodoPath(trimmedId, apmRoot), detail.todo ?? "", "utf8");
1672
+ writeFileSync9(sessionTodoPath(trimmedId, apmRoot), detail.todo ?? "", "utf8");
1420
1673
  for (const doc of documents) {
1421
1674
  const fileName = documentLocalFileName(doc.name);
1422
- writeFileSync8(join8(docsDir, fileName), doc.content ?? "", "utf8");
1675
+ writeFileSync9(join9(docsDir, fileName), doc.content ?? "", "utf8");
1423
1676
  }
1424
1677
  const sessionYaml = yamlStringify(
1425
1678
  {
@@ -1436,19 +1689,21 @@ async function runPull(sessionId, remoteWorkdir) {
1436
1689
  },
1437
1690
  { lineWidth: 0 }
1438
1691
  );
1439
- writeFileSync8(
1692
+ writeFileSync9(
1440
1693
  sessionYamlPath(trimmedId, apmRoot),
1441
1694
  sessionYaml.endsWith("\n") ? sessionYaml : `${sessionYaml}
1442
1695
  `,
1443
1696
  "utf8"
1444
1697
  );
1445
- writeFileSync8(
1698
+ writeFileSync9(
1446
1699
  sessionMessagesXmlPath(trimmedId, apmRoot),
1447
1700
  formatSessionMessagesXml(trimmedId, messages),
1448
1701
  "utf8"
1449
1702
  );
1450
1703
  await syncSessionAttachments(cfg, trimmedId, attachments, apmRoot);
1451
1704
  await syncPlatformRules(cfg, trimmedId, workdir, apmRoot);
1705
+ await syncRemoteDeploymentConfig(workdir, apmRoot);
1706
+ await syncRepositoryProjectDocumentsPull(workdir, apmRoot);
1452
1707
  console.log(`[apm] \u5DF2\u540C\u6B65\u4F1A\u8BDD\u5DE5\u4F5C\u533A: ${dir}`);
1453
1708
  return dir;
1454
1709
  }
@@ -1457,15 +1712,15 @@ async function runPull(sessionId, remoteWorkdir) {
1457
1712
  import { spawnSync } from "child_process";
1458
1713
 
1459
1714
  // src/version.ts
1460
- import { readFileSync as readFileSync6 } from "fs";
1461
- import { dirname as dirname2, join as join9 } from "path";
1715
+ import { readFileSync as readFileSync7 } from "fs";
1716
+ import { dirname as dirname3, join as join10 } from "path";
1462
1717
  import { fileURLToPath as fileURLToPath2 } from "url";
1463
1718
  var CLI_PACKAGE_NAME = "ai-project-manage-cli";
1464
1719
  function readCliVersion() {
1465
1720
  try {
1466
- const dir = dirname2(fileURLToPath2(import.meta.url));
1467
- const pkgPath = join9(dir, "..", "package.json");
1468
- const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
1721
+ const dir = dirname3(fileURLToPath2(import.meta.url));
1722
+ const pkgPath = join10(dir, "..", "package.json");
1723
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
1469
1724
  return pkg.version ?? "0.0.0";
1470
1725
  } catch {
1471
1726
  return "0.0.0";
@@ -1538,12 +1793,12 @@ async function runUpdate() {
1538
1793
  }
1539
1794
 
1540
1795
  // src/commands/update-skills.ts
1541
- import { existsSync as existsSync6, mkdirSync as mkdirSync4, statSync as statSync3 } from "fs";
1542
- import { join as join10 } from "path";
1796
+ import { existsSync as existsSync7, mkdirSync as mkdirSync4, statSync as statSync3 } from "fs";
1797
+ import { join as join11 } from "path";
1543
1798
  async function syncWorkspaceSkills(cfg, workdir) {
1544
1799
  const apmDir = workspaceApmDir(workdir);
1545
1800
  const fsApmDir = toFsPath(apmDir);
1546
- if (!existsSync6(fsApmDir)) {
1801
+ if (!existsSync7(fsApmDir)) {
1547
1802
  throw new Error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
1548
1803
  }
1549
1804
  const apmStat = statSync3(fsApmDir);
@@ -1555,12 +1810,12 @@ async function syncWorkspaceSkills(cfg, workdir) {
1555
1810
  if (syncAgentsGuide(apmDir)) {
1556
1811
  console.log("[apm] \u5DF2\u540C\u6B65 APM \u6307\u5357: .apm/AGENTS.md");
1557
1812
  }
1558
- const rulesDir = join10(apmDir, "rules");
1813
+ const rulesDir = join11(apmDir, "rules");
1559
1814
  const ruleNames = syncBaseRules(rulesDir);
1560
1815
  for (const name of ruleNames) {
1561
1816
  console.log(`[apm] \u5DF2\u540C\u6B65\u57FA\u7840\u89C4\u5219: rules/${name}`);
1562
1817
  }
1563
- const skillsDir = join10(apmDir, "skills");
1818
+ const skillsDir = join11(apmDir, "skills");
1564
1819
  mkdirSync4(toFsPath(skillsDir), { recursive: true });
1565
1820
  const baseNames = syncBaseSkills(skillsDir);
1566
1821
  for (const name of baseNames) {
@@ -1587,7 +1842,7 @@ async function syncWorkspaceSkills(cfg, workdir) {
1587
1842
  }
1588
1843
  async function runUpdateSkills() {
1589
1844
  const apmDir = workspaceApmDir();
1590
- if (!existsSync6(apmDir)) {
1845
+ if (!existsSync7(apmDir)) {
1591
1846
  console.error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
1592
1847
  process.exit(1);
1593
1848
  }
@@ -1600,11 +1855,11 @@ async function runUpdateSkills() {
1600
1855
  }
1601
1856
 
1602
1857
  // src/commands/sync-deploy-config.ts
1603
- import { existsSync as existsSync7, statSync as statSync4 } from "fs";
1858
+ import { existsSync as existsSync8, statSync as statSync4 } from "fs";
1604
1859
  async function runSyncDeployConfig() {
1605
1860
  const workdir = resolveWorkdirPath();
1606
1861
  const apmDir = workspaceApmDir(workdir);
1607
- if (!existsSync7(apmDir)) {
1862
+ if (!existsSync8(apmDir)) {
1608
1863
  console.error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
1609
1864
  process.exit(1);
1610
1865
  }
@@ -1619,18 +1874,33 @@ async function runSyncDeployConfig() {
1619
1874
  }
1620
1875
  }
1621
1876
 
1877
+ // src/commands/sync-project-documents.ts
1878
+ async function runSyncProjectDocuments(options) {
1879
+ const pull = options?.pull ?? !options?.push;
1880
+ const push = options?.push ?? false;
1881
+ const workdir = resolveWorkdirPath();
1882
+ const apmRoot = workspaceApmDir(workdir);
1883
+ if (pull) {
1884
+ await syncRepositoryProjectDocumentsPull(workdir, apmRoot);
1885
+ }
1886
+ if (push) {
1887
+ const cfg = await ensureLoggedConfig();
1888
+ await syncRepositoryProjectDocumentsPush(cfg, workdir, apmRoot);
1889
+ }
1890
+ }
1891
+
1622
1892
  // src/commands/sync-document.ts
1623
- import { existsSync as existsSync9 } from "fs";
1893
+ import { existsSync as existsSync10 } from "fs";
1624
1894
  import { basename as basename3 } from "path";
1625
1895
 
1626
1896
  // src/commands/sync-session-documents.ts
1627
- import { existsSync as existsSync8, readdirSync as readdirSync3, readFileSync as readFileSync7 } from "fs";
1628
- import { join as join11 } from "path";
1897
+ import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync8 } from "fs";
1898
+ import { join as join12 } from "path";
1629
1899
  function listLocalMarkdownFiles(docsDir) {
1630
- if (!existsSync8(docsDir)) {
1900
+ if (!existsSync9(docsDir)) {
1631
1901
  return [];
1632
1902
  }
1633
- return readdirSync3(docsDir).filter(
1903
+ return readdirSync4(docsDir).filter(
1634
1904
  (name) => name.toLowerCase().endsWith(".md")
1635
1905
  );
1636
1906
  }
@@ -1642,8 +1912,8 @@ function remoteDocumentByLocalName(remoteDocuments, localFileName) {
1642
1912
  });
1643
1913
  }
1644
1914
  async function upsertLocalDocumentFile(api, sessionId, docsDir, fileName) {
1645
- const absPath = join11(docsDir, fileName);
1646
- const content = readFileSync7(absPath, "utf8");
1915
+ const absPath = join12(docsDir, fileName);
1916
+ const content = readFileSync8(absPath, "utf8");
1647
1917
  const name = documentPlatformName(absPath);
1648
1918
  return api.cli.upsertDocument({
1649
1919
  sessionId,
@@ -1665,8 +1935,8 @@ async function syncSessionDocuments(cfg, sessionId, apmRoot, options) {
1665
1935
  const remoteDocuments = options?.remoteDocuments ?? await api.cli.listDocuments({ sessionId: trimmedSessionId });
1666
1936
  let synced = 0;
1667
1937
  for (const fileName of localFiles) {
1668
- const absPath = join11(docsDir, fileName);
1669
- const content = readFileSync7(absPath, "utf8");
1938
+ const absPath = join12(docsDir, fileName);
1939
+ const content = readFileSync8(absPath, "utf8");
1670
1940
  const remote = remoteDocumentByLocalName(remoteDocuments, fileName);
1671
1941
  if (remote && remote.content === content) {
1672
1942
  continue;
@@ -1699,7 +1969,7 @@ async function runSyncDocument(sessionId, options) {
1699
1969
  process.exit(1);
1700
1970
  }
1701
1971
  const absPath = resolveSessionDocumentPath(trimmedSessionId, fileArg);
1702
- if (!existsSync9(absPath)) {
1972
+ if (!existsSync10(absPath)) {
1703
1973
  const docsDir2 = sessionDocsDir(trimmedSessionId);
1704
1974
  console.error(
1705
1975
  `[apm] \u6587\u6863\u4E0D\u5B58\u5728: ${absPath}
@@ -1853,13 +2123,37 @@ function validateCancel(o) {
1853
2123
  data: { type: "cancel", messageId: o.messageId.trim() }
1854
2124
  };
1855
2125
  }
2126
+ function validateDeployPush(o) {
2127
+ if (o.type !== "deploy") {
2128
+ return { ok: false, reason: "\u671F\u671B deploy" };
2129
+ }
2130
+ if (!nonEmptyString(o.deploymentRunId)) {
2131
+ return { ok: false, reason: "deploy \u7F3A\u5C11 deploymentRunId" };
2132
+ }
2133
+ if (!nonEmptyString(o.workdir)) {
2134
+ return { ok: false, reason: "deploy \u7F3A\u5C11 workdir" };
2135
+ }
2136
+ const environment = o.environment;
2137
+ if (environment !== "test" && environment !== "online") {
2138
+ return { ok: false, reason: "deploy.environment \u65E0\u6548" };
2139
+ }
2140
+ return {
2141
+ ok: true,
2142
+ data: {
2143
+ type: "deploy",
2144
+ deploymentRunId: o.deploymentRunId.trim(),
2145
+ workdir: o.workdir.trim(),
2146
+ environment
2147
+ }
2148
+ };
2149
+ }
1856
2150
  function validateAgentWsMessage(value, kind) {
1857
2151
  if (typeof value !== "object" || value === null) {
1858
2152
  return { ok: false, reason: "\u6D88\u606F\u4F53\u4E0D\u662F JSON \u5BF9\u8C61" };
1859
2153
  }
1860
2154
  const o = value;
1861
2155
  const type = o.type;
1862
- if (type !== "heartbeat" && type !== "message" && type !== "cancel") {
2156
+ if (type !== "heartbeat" && type !== "message" && type !== "cancel" && type !== "deploy") {
1863
2157
  return { ok: false, reason: `\u672A\u77E5 type: ${String(type)}` };
1864
2158
  }
1865
2159
  if (kind === "heartbeat" || type === "heartbeat") {
@@ -1868,9 +2162,125 @@ function validateAgentWsMessage(value, kind) {
1868
2162
  if (type === "cancel") {
1869
2163
  return validateCancel(o);
1870
2164
  }
2165
+ if (type === "deploy") {
2166
+ return validateDeployPush(o);
2167
+ }
1871
2168
  return validateMessagePush(o);
1872
2169
  }
1873
2170
 
2171
+ // src/commands/connect/deploy-run.ts
2172
+ import { spawn } from "node:child_process";
2173
+ import { readFileSync as readFileSync9 } from "node:fs";
2174
+ import { join as join13 } from "node:path";
2175
+ function readDeployConfig(workdir) {
2176
+ const configPath = join13(workspaceApmDir(workdir), "apm.config.json");
2177
+ try {
2178
+ const raw = readFileSync9(configPath, "utf8");
2179
+ const parsed = JSON.parse(raw);
2180
+ return parsed.deploy;
2181
+ } catch {
2182
+ return void 0;
2183
+ }
2184
+ }
2185
+ function resolveDeployCommand(workdir, environment) {
2186
+ const command = readDeployConfig(workdir)?.[environment];
2187
+ if (typeof command === "string" && command.trim()) {
2188
+ return command.trim();
2189
+ }
2190
+ return null;
2191
+ }
2192
+ function missingDeployCommandMessage(environment) {
2193
+ return `deploy.${environment} \u90E8\u7F72\u547D\u4EE4\u672A\u914D\u7F6E\uFF0C\u8BF7\u5148\u914D\u7F6E`;
2194
+ }
2195
+ function runShellCommand(command, cwd, signal) {
2196
+ return new Promise((resolve5, reject) => {
2197
+ const child = spawn(command, {
2198
+ cwd,
2199
+ shell: true,
2200
+ env: process.env,
2201
+ windowsHide: true
2202
+ });
2203
+ let stdout = "";
2204
+ let stderr = "";
2205
+ const onAbort = () => {
2206
+ child.kill("SIGTERM");
2207
+ };
2208
+ if (signal.aborted) {
2209
+ onAbort();
2210
+ } else {
2211
+ signal.addEventListener("abort", onAbort, { once: true });
2212
+ }
2213
+ child.stdout.on("data", (chunk) => {
2214
+ stdout += String(chunk);
2215
+ });
2216
+ child.stderr.on("data", (chunk) => {
2217
+ stderr += String(chunk);
2218
+ });
2219
+ child.on("error", (error) => {
2220
+ signal.removeEventListener("abort", onAbort);
2221
+ reject(error);
2222
+ });
2223
+ child.on("close", (code) => {
2224
+ signal.removeEventListener("abort", onAbort);
2225
+ const log = [stdout, stderr].filter(Boolean).join("\n");
2226
+ if (code === 0) {
2227
+ resolve5({ log });
2228
+ return;
2229
+ }
2230
+ const error = new Error(
2231
+ `\u90E8\u7F72\u547D\u4EE4\u9000\u51FA\u7801 ${code ?? "unknown"}: ${command}`
2232
+ );
2233
+ error.log = log;
2234
+ reject(error);
2235
+ });
2236
+ });
2237
+ }
2238
+ async function handleInboundDeploy(cfg, msg, signal) {
2239
+ const api = createApmApiClient(cfg);
2240
+ const deploymentRunId = msg.deploymentRunId;
2241
+ if (signal.aborted) return;
2242
+ await api.cli.updateCoordinatorDeploymentStatus({
2243
+ id: deploymentRunId,
2244
+ status: "DEPLOYING"
2245
+ });
2246
+ const workdir = requireRemoteWorkdir(msg.workdir);
2247
+ const command = resolveDeployCommand(workdir, msg.environment);
2248
+ if (!command) {
2249
+ const error = missingDeployCommandMessage(msg.environment);
2250
+ console.error(`[apm] ${error}`);
2251
+ await api.cli.completeCoordinatorDeployment({
2252
+ id: deploymentRunId,
2253
+ status: "FAILED",
2254
+ log: error,
2255
+ error
2256
+ });
2257
+ return;
2258
+ }
2259
+ console.log(
2260
+ `[apm] deploy start id=${deploymentRunId} env=${msg.environment} cwd=${workdir}`
2261
+ );
2262
+ console.log(`[apm] deploy command: ${command}`);
2263
+ try {
2264
+ const { log } = await runShellCommand(command, workdir, signal);
2265
+ await api.cli.completeCoordinatorDeployment({
2266
+ id: deploymentRunId,
2267
+ status: "SUCCESS",
2268
+ log
2269
+ });
2270
+ console.log(`[apm] deploy success id=${deploymentRunId}`);
2271
+ } catch (error) {
2272
+ const detail = error instanceof Error ? error.message : String(error);
2273
+ const log = error && typeof error === "object" && "log" in error ? String(error.log ?? "") : "";
2274
+ await api.cli.completeCoordinatorDeployment({
2275
+ id: deploymentRunId,
2276
+ status: "FAILED",
2277
+ log,
2278
+ error: detail
2279
+ });
2280
+ console.error(`[apm] deploy failed id=${deploymentRunId}: ${detail}`);
2281
+ }
2282
+ }
2283
+
1874
2284
  // src/commands/connect/abort-signal-debug.ts
1875
2285
  import {
1876
2286
  getEventListeners,
@@ -2100,17 +2510,17 @@ ${JSON.stringify(event, null, 2)}
2100
2510
  }
2101
2511
 
2102
2512
  // src/commands/connect/agent-session-registry.ts
2103
- import { existsSync as existsSync10, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync9 } from "node:fs";
2104
- import { dirname as dirname3, resolve as resolve3 } from "node:path";
2513
+ import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync10 } from "node:fs";
2514
+ import { dirname as dirname4, resolve as resolve3 } from "node:path";
2105
2515
  function registryPath(workdir, sessionId) {
2106
2516
  return resolve3(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
2107
2517
  }
2108
2518
  function readRegistry(path10) {
2109
- if (!existsSync10(path10)) {
2519
+ if (!existsSync11(path10)) {
2110
2520
  return {};
2111
2521
  }
2112
2522
  try {
2113
- const parsed = JSON.parse(readFileSync8(path10, "utf8"));
2523
+ const parsed = JSON.parse(readFileSync10(path10, "utf8"));
2114
2524
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
2115
2525
  const result = {};
2116
2526
  for (const [key, value] of Object.entries(
@@ -2127,8 +2537,8 @@ function readRegistry(path10) {
2127
2537
  return {};
2128
2538
  }
2129
2539
  function writeRegistry(path10, registry) {
2130
- mkdirSync5(dirname3(path10), { recursive: true });
2131
- writeFileSync9(path10, `${JSON.stringify(registry, null, 2)}
2540
+ mkdirSync5(dirname4(path10), { recursive: true });
2541
+ writeFileSync10(path10, `${JSON.stringify(registry, null, 2)}
2132
2542
  `, "utf8");
2133
2543
  }
2134
2544
  function loadSessionAgentId(workdir, sessionId, user) {
@@ -2432,20 +2842,20 @@ async function runCursorAgent(cfg, ctx, options) {
2432
2842
  }
2433
2843
 
2434
2844
  // 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";
2845
+ import { existsSync as existsSync12, readFileSync as readFileSync11, writeFileSync as writeFileSync11 } from "fs";
2846
+ import { join as join14 } from "path";
2437
2847
  var CLI_VERSION_FILE = ".cli-version.json";
2438
2848
  function manifestPath(apmDir) {
2439
- return join12(apmDir, CLI_VERSION_FILE);
2849
+ return join14(apmDir, CLI_VERSION_FILE);
2440
2850
  }
2441
2851
  function loadManifest3(apmDir) {
2442
2852
  const path10 = toFsPath(manifestPath(apmDir));
2443
- if (!existsSync11(path10)) {
2853
+ if (!existsSync12(path10)) {
2444
2854
  return null;
2445
2855
  }
2446
2856
  try {
2447
2857
  const parsed = JSON.parse(
2448
- readFileSync9(path10, "utf8")
2858
+ readFileSync11(path10, "utf8")
2449
2859
  );
2450
2860
  if (parsed?.version === 1 && typeof parsed.cliVersion === "string" && parsed.cliVersion.trim()) {
2451
2861
  return parsed;
@@ -2456,7 +2866,7 @@ function loadManifest3(apmDir) {
2456
2866
  }
2457
2867
  function saveManifest3(apmDir, cliVersion) {
2458
2868
  const manifest = { version: 1, cliVersion };
2459
- writeFileSync10(
2869
+ writeFileSync11(
2460
2870
  toFsPath(manifestPath(apmDir)),
2461
2871
  `${JSON.stringify(manifest, null, 2)}
2462
2872
  `,
@@ -2568,8 +2978,13 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
2568
2978
  };
2569
2979
  try {
2570
2980
  if (signal.aborted) return;
2571
- assertWorkspaceApmDirExists(workdir);
2572
- assertApmGitignoredInRepo(workdir);
2981
+ const { didInit } = await runStep(
2982
+ "workspace-init",
2983
+ () => ensureWorkspaceInitialized(workdir)
2984
+ );
2985
+ if (!didInit) {
2986
+ assertApmGitignoredInRepo(workdir);
2987
+ }
2573
2988
  await runStep(
2574
2989
  "status-typing",
2575
2990
  () => updateMessageStatus(cfg, messageId, "TYPING")
@@ -2633,6 +3048,10 @@ async function handleInboundMessage(cfg, msg, signal, ctx) {
2633
3048
  "sync-documents",
2634
3049
  () => syncSessionDocuments(cfg, msg.sessionId, apmRoot)
2635
3050
  );
3051
+ await runStep(
3052
+ "sync-project-documents",
3053
+ () => syncRepositoryProjectDocumentsPush(cfg, workdir, apmRoot)
3054
+ );
2636
3055
  await runStep(
2637
3056
  "commit-files",
2638
3057
  () => commitWorkingTreeIfDirty(workdir, "chore(apm): commit working tree")
@@ -2770,6 +3189,27 @@ async function runConnect(options) {
2770
3189
  activeRuns.get(messageId)?.abort();
2771
3190
  return;
2772
3191
  }
3192
+ if (validated.data.type === "deploy") {
3193
+ const msg2 = validated.data;
3194
+ const perDeployController = new AbortController();
3195
+ const signal2 = AbortSignal.any([
3196
+ shutdownAbort.signal,
3197
+ perDeployController.signal
3198
+ ]);
3199
+ const task2 = (async () => {
3200
+ await runSlots.acquire();
3201
+ try {
3202
+ await handleInboundDeploy(cfg, msg2, signal2);
3203
+ } finally {
3204
+ runSlots.release();
3205
+ }
3206
+ })();
3207
+ activeTasks.add(task2);
3208
+ void task2.finally(() => {
3209
+ activeTasks.delete(task2);
3210
+ });
3211
+ return;
3212
+ }
2773
3213
  if (validated.data.type !== "message") {
2774
3214
  return;
2775
3215
  }
@@ -2852,19 +3292,19 @@ async function runCreatePr(options) {
2852
3292
  import path5 from "node:path";
2853
3293
 
2854
3294
  // src/commands/deploy/internal/apm-config.ts
2855
- import { existsSync as existsSync12, readFileSync as readFileSync10 } from "node:fs";
3295
+ import { existsSync as existsSync13, readFileSync as readFileSync12 } from "node:fs";
2856
3296
  import { resolve as resolve4 } from "node:path";
2857
3297
  function loadApmConfig(options) {
2858
3298
  const p = resolve4(
2859
3299
  process.cwd(),
2860
3300
  options?.configPath ?? resolve4(workspaceApmDir(), "apm.config.json")
2861
3301
  );
2862
- if (!existsSync12(p)) {
3302
+ if (!existsSync13(p)) {
2863
3303
  console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
2864
3304
  process.exit(1);
2865
3305
  }
2866
3306
  try {
2867
- const raw = readFileSync10(p, "utf8");
3307
+ const raw = readFileSync12(p, "utf8");
2868
3308
  return JSON.parse(raw);
2869
3309
  } catch (e) {
2870
3310
  console.error(`\u65E0\u6CD5\u89E3\u6790 apm.config.json\uFF1A${p}`, e);
@@ -2986,7 +3426,7 @@ import path4 from "node:path";
2986
3426
  import Docker from "dockerode";
2987
3427
 
2988
3428
  // src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
2989
- import { existsSync as existsSync13, readFileSync as readFileSync11 } from "node:fs";
3429
+ import { existsSync as existsSync14, readFileSync as readFileSync13 } from "node:fs";
2990
3430
  import path from "node:path";
2991
3431
  function asOptionalTlsBuffer(value) {
2992
3432
  if (typeof value !== "string") {
@@ -2998,8 +3438,8 @@ function asOptionalTlsBuffer(value) {
2998
3438
  if (normalized === "") {
2999
3439
  return void 0;
3000
3440
  }
3001
- if (existsSync13(normalized)) {
3002
- return readFileSync11(normalized);
3441
+ if (existsSync14(normalized)) {
3442
+ return readFileSync13(normalized);
3003
3443
  }
3004
3444
  const looksLikePath = /[\\/]/.test(normalized) || normalized.endsWith(".pem");
3005
3445
  if (looksLikePath) {
@@ -3209,7 +3649,7 @@ var DockerodeClient = class {
3209
3649
  var createDockerodeClient = (config) => new DockerodeClient(config);
3210
3650
 
3211
3651
  // src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
3212
- import { existsSync as existsSync14, readFileSync as readFileSync12, statSync as statSync5 } from "node:fs";
3652
+ import { existsSync as existsSync15, readFileSync as readFileSync14, statSync as statSync5 } from "node:fs";
3213
3653
  import path2 from "node:path";
3214
3654
  function stripSurroundingQuotes(value) {
3215
3655
  const t = value.trim();
@@ -3226,10 +3666,10 @@ function loadEnvFromFile(envFilePath) {
3226
3666
  return {};
3227
3667
  }
3228
3668
  const targetPath = path2.resolve(envFilePath);
3229
- if (!existsSync14(targetPath) || !statSync5(targetPath).isFile()) {
3669
+ if (!existsSync15(targetPath) || !statSync5(targetPath).isFile()) {
3230
3670
  return {};
3231
3671
  }
3232
- const raw = readFileSync12(targetPath, "utf-8");
3672
+ const raw = readFileSync14(targetPath, "utf-8");
3233
3673
  const result = {};
3234
3674
  for (const line of raw.split(/\r?\n/)) {
3235
3675
  const normalized = line.trim();
@@ -3400,12 +3840,12 @@ function dockerPushImage(params, cwd) {
3400
3840
  }
3401
3841
 
3402
3842
  // src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
3403
- import { existsSync as existsSync15 } from "node:fs";
3843
+ import { existsSync as existsSync16 } from "node:fs";
3404
3844
  import path3 from "node:path";
3405
3845
  function resolveDockerBuildPaths(cwd) {
3406
3846
  const dockerfilePath = path3.join(cwd, "Dockerfile");
3407
3847
  Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
3408
- if (!existsSync15(dockerfilePath)) {
3848
+ if (!existsSync16(dockerfilePath)) {
3409
3849
  throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
3410
3850
  }
3411
3851
  Logger.info("\u2713 Dockerfile \u5B58\u5728");
@@ -4034,8 +4474,13 @@ function buildProgram() {
4034
4474
  ).action(async () => {
4035
4475
  await runSyncDeployConfig();
4036
4476
  });
4477
+ program.command("sync-project-documents").description(
4478
+ "\u540C\u6B65\u4ED3\u5E93\u9879\u76EE\u6587\u6863\uFF1A\u9ED8\u8BA4 pull \u5230 .apm/project/\uFF1B\u52A0 --push \u5C06\u672C\u5730\u53D8\u66F4\u63A8\u9001\u5230\u5E73\u53F0"
4479
+ ).option("--push", "\u63A8\u9001\u672C\u5730 .apm/project/ \u5230\u5E73\u53F0").option("--pull", "\u4ECE\u5E73\u53F0\u62C9\u53D6\u5230 .apm/project/").action(async (opts) => {
4480
+ await runSyncProjectDocuments(opts);
4481
+ });
4037
4482
  program.command("pull").description(
4038
- "\u62C9\u53D6\u6C9F\u901A\u7FA4\u6570\u636E\u5230 .apm/sessions/<sessionId>/\uFF08session.yaml\u3001RULE.md\u3001TASK.md\u3001TODO.md\u3001docs\u3001attachments\uFF09"
4483
+ "\u62C9\u53D6\u6C9F\u901A\u7FA4\u6570\u636E\u5230 .apm/sessions/<sessionId>/\uFF0C\u5E76\u540C\u6B65\u90E8\u7F72\u914D\u7F6E\u4E0E\u4ED3\u5E93\u9879\u76EE\u6587\u6863\u5230 .apm/project/"
4039
4484
  ).argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").action(async (sessionId) => {
4040
4485
  await runPull(sessionId);
4041
4486
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "6.0.57",
3
+ "version": "6.0.59",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,
@@ -30,6 +30,11 @@
30
30
  - reply.md:当需要回复消息时需要读取这个文档,记住回复规则
31
31
  - write_doc.md:当需要写文档时需要读取这个文档,记住写文档的规则
32
32
 
33
+ - 仓库项目上下文(`.apm/project/`):
34
+ - 索引: `.apm/project/manifest.json` — 本仓库在平台登记的文档列表
35
+ - 文档文件: `.apm/project/{path}` — 与 manifest 中 path 对应;`apm pull` 自动同步,`apm connect` 消息结束后自动推回平台
36
+ - 任务涉及菜单名、路由、业务术语等时,**先 Read manifest 与相关文档**,禁止猜测路径
37
+
33
38
  - 本轮任务需要关注的文档指引(.apm/sessions/<会话 ID>/\*):
34
39
  - 本轮会话状态: `session.yaml`,从这里可以看到每个成员的信息,找到可以协助你一起解决问题的人,可以结合 `RULE.md`一起看
35
40
  - 群文档: `docs/xxxx.md`,当需要相关上下文可以在这里查找,按需阅读
@@ -1,3 +1,7 @@
1
1
  {
2
- "name": ""
3
- }
2
+ "name": "",
3
+ "deploy": {
4
+ "test": "npm run deploy:test && python .apm/deploy/deploy.py",
5
+ "online": "npm run deploy:online && python .apm/deploy/deploy.py"
6
+ }
7
+ }
File without changes