ai-project-manage-cli 6.0.44 → 6.0.46

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.
Files changed (2) hide show
  1. package/dist/index.js +271 -114
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -16,7 +16,7 @@ function resolveClientMachineId(cfg) {
16
16
  function resolveApiKey(cfg) {
17
17
  return (cfg.apiKey ?? cfg.token ?? "").trim();
18
18
  }
19
- async function readApmConfig() {
19
+ async function tryReadApmConfig() {
20
20
  try {
21
21
  const raw = readFileSync(APM_CONFIG_PATH, "utf8");
22
22
  const v = JSON.parse(raw);
@@ -54,7 +54,7 @@ function httpBaseToWsOrigin(httpBase) {
54
54
  throw new Error("baseUrl \u5FC5\u987B\u4EE5 http:// \u6216 https:// \u5F00\u5934");
55
55
  }
56
56
  async function ensureApmConfig() {
57
- const existing = await readApmConfig();
57
+ const existing = await tryReadApmConfig();
58
58
  if (existing) return existing;
59
59
  const defaults = defaultApmConfig();
60
60
  await writeApmConfig(defaults);
@@ -80,8 +80,8 @@ function buildAgentWsUrl(httpBase, apiKey) {
80
80
  }
81
81
 
82
82
  // src/commands/init.ts
83
- import { join as join3 } from "path";
84
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
83
+ import { join as join4 } from "path";
84
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
85
85
 
86
86
  // src/command-utils.ts
87
87
  import { copyFileSync, existsSync, mkdirSync as mkdirSync2, readdirSync, statSync } from "fs";
@@ -287,32 +287,9 @@ async function copyTemplateFiles(targetDir, workdir = resolveWorkdirPath()) {
287
287
  assertTemplateCopiedToApm(resolvedTarget, resolve2(workdir));
288
288
  }
289
289
 
290
- // src/commands/init.ts
291
- async function runInit(name) {
292
- const workdir = resolveWorkdirPath();
293
- await ensureWorkspaceApmDirForInit(workdir);
294
- const apmDir = workspaceApmDir(workdir);
295
- await copyTemplateFiles(apmDir, workdir);
296
- const trimmedName = name?.trim();
297
- if (trimmedName) {
298
- const apmConfigPath = toFsPath(join3(apmDir, "apm.config.json"));
299
- const config = readFileSync2(apmConfigPath, "utf8");
300
- const configJson = JSON.parse(config);
301
- configJson.name = trimmedName;
302
- writeFileSync2(
303
- apmConfigPath,
304
- `${JSON.stringify(configJson, null, 2)}
305
- `,
306
- "utf8"
307
- );
308
- }
309
- console.log(`[apm] \u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`);
310
- console.log(`[apm] \u5DE5\u4F5C\u76EE\u5F55\u8DEF\u5F84\uFF1A${workdir}`);
311
- console.log("[apm] \u8BF7\u5728\u5E73\u53F0\u300C\u5BA2\u6237\u673A\u7BA1\u7406 \u2192 \u5DE5\u4F5C\u7A7A\u95F4\u300D\u767B\u8BB0\u4E0A\u8FF0\u76EE\u5F55\u8DEF\u5F84");
312
- }
313
-
314
- // src/commands/login.ts
315
- import { ApiError } from "listpage-http";
290
+ // src/deployment-config-sync.ts
291
+ import { join as join3 } from "path";
292
+ import { writeFileSync as writeFileSync2 } from "fs";
316
293
 
317
294
  // src/api/client.ts
318
295
  import { createApiClient } from "listpage-http";
@@ -373,6 +350,18 @@ var requestConfig = {
373
350
  method: "GET",
374
351
  path: "/cli/tasks/branch-baseline"
375
352
  }),
353
+ workspaceBaseline: defineEndpoint({
354
+ method: "GET",
355
+ path: "/cli/workspaces/baseline"
356
+ }),
357
+ getDeploymentConfiguration: defineEndpoint({
358
+ method: "GET",
359
+ path: "/cli/deployment-configurations"
360
+ }),
361
+ matchRepository: defineEndpoint({
362
+ method: "GET",
363
+ path: "/cli/repositories/match"
364
+ }),
376
365
  listSkills: defineEndpoint({
377
366
  method: "GET",
378
367
  path: "/cli/skills"
@@ -395,7 +384,131 @@ function createApmApiClient(cfg) {
395
384
  });
396
385
  }
397
386
 
387
+ // src/git-remote.ts
388
+ import { execFile } from "child_process";
389
+ import { promisify } from "util";
390
+ var execFileAsync = promisify(execFile);
391
+ async function tryReadGitOriginUrl(cwd) {
392
+ try {
393
+ const { stdout } = await execFileAsync(
394
+ "git",
395
+ ["config", "--get", "remote.origin.url"],
396
+ { cwd, encoding: "utf8", maxBuffer: 1024 * 1024 }
397
+ );
398
+ const url = stdout.trim();
399
+ return url || null;
400
+ } catch {
401
+ return null;
402
+ }
403
+ }
404
+
405
+ // src/deployment-config-sync.ts
406
+ var TEMPLATE_HINT = "\u4FDD\u7559\u6A21\u677F .apm/apm.config.json \u4E0E .apm/deploy/README.md";
407
+ var SYNC_HINT = "\u767B\u8BB0\u5DE5\u4F5C\u7A7A\u95F4\u8DEF\u5F84\u3001\u7ED1\u5B9A\u4ED3\u5E93\u540E\uFF0C\u53EF\u6267\u884C: apm sync-deploy-config";
408
+ async function resolveRepositoryIdForSync(api, workdirPath) {
409
+ const baseline = await api.cli.workspaceBaseline({ workdirPath });
410
+ if (baseline.repositoryId) {
411
+ return { repositoryId: baseline.repositoryId, diagnostic: null };
412
+ }
413
+ const gitUrl = await tryReadGitOriginUrl(workdirPath);
414
+ if (gitUrl) {
415
+ const matched = await api.cli.matchRepository({ url: gitUrl });
416
+ if (matched.repositoryId) {
417
+ console.log(`[apm] \u5DE5\u4F5C\u7A7A\u95F4\u8DEF\u5F84\u672A\u5339\u914D\uFF0C\u5DF2\u901A\u8FC7 git remote \u5173\u8054\u4ED3\u5E93: ${gitUrl}`);
418
+ return { repositoryId: matched.repositoryId, diagnostic: null };
419
+ }
420
+ }
421
+ const detail = baseline.diagnostic?.message ?? `\u5F53\u524D\u8DEF\u5F84\uFF08\u89C4\u8303\u5316\uFF1A${baseline.workdirPath}\uFF09\u672A\u5339\u914D\u5230\u5DF2\u7ED1\u5B9A\u4ED3\u5E93\u7684\u5DE5\u4F5C\u7A7A\u95F4\u3002`;
422
+ return { repositoryId: null, diagnostic: detail };
423
+ }
424
+ async function syncRemoteDeploymentConfig(workdirPath, apmDir) {
425
+ const cfg = await tryReadApmConfig();
426
+ if (!cfg || !resolveApiKey(cfg)) {
427
+ console.log(
428
+ `[apm] \u672A\u68C0\u6D4B\u5230\u767B\u5F55\u4FE1\u606F\uFF0C\u8DF3\u8FC7\u5E73\u53F0\u90E8\u7F72\u914D\u7F6E\u540C\u6B65\uFF08${TEMPLATE_HINT}\uFF09\u3002
429
+ [apm] \u8BF7\u5148\u6267\u884C apm login\uFF0C\u518D\u6267\u884C apm sync-deploy-config \u62C9\u53D6\u6700\u65B0\u914D\u7F6E\u3002`
430
+ );
431
+ return { synced: false, repositoryId: null };
432
+ }
433
+ const api = createApmApiClient(cfg);
434
+ const { repositoryId, diagnostic } = await resolveRepositoryIdForSync(
435
+ api,
436
+ workdirPath
437
+ );
438
+ if (!repositoryId) {
439
+ console.log(
440
+ `[apm] \u672A\u80FD\u540C\u6B65\u5E73\u53F0\u90E8\u7F72\u914D\u7F6E\uFF08${TEMPLATE_HINT}\uFF09\u3002
441
+ ${diagnostic ?? ""}
442
+ [apm] ${SYNC_HINT}`
443
+ );
444
+ return { synced: false, repositoryId: null };
445
+ }
446
+ const { config } = await api.cli.getDeploymentConfiguration({ repositoryId });
447
+ if (!config) {
448
+ console.log(
449
+ `[apm] \u672A\u627E\u5230\u5173\u8054\u4ED3\u5E93\u7684\u90E8\u7F72\u914D\u7F6E\uFF08${TEMPLATE_HINT}\uFF0CrepositoryId\uFF1A${repositoryId}\uFF09\u3002
450
+ [apm] \u8BF7\u5728\u5E73\u53F0\u300C\u90E8\u7F72\u914D\u7F6E\u300D\u4E2D\u521B\u5EFA\u914D\u7F6E\u5E76\u5173\u8054\u8BE5\u4ED3\u5E93\uFF0C\u7136\u540E\u6267\u884C: apm sync-deploy-config`
451
+ );
452
+ return { synced: false, repositoryId };
453
+ }
454
+ let parsed;
455
+ try {
456
+ parsed = JSON.parse(config.content);
457
+ } catch {
458
+ console.warn(
459
+ `[apm] \u8FDC\u7A0B\u90E8\u7F72\u914D\u7F6E\u300C${config.name}\u300DJSON \u65E0\u6548\uFF08${TEMPLATE_HINT}\uFF09`
460
+ );
461
+ return { synced: false, repositoryId };
462
+ }
463
+ const targetApmDir = apmDir ?? workspaceApmDir(workdirPath);
464
+ const apmConfigPath = toFsPath(join3(targetApmDir, "apm.config.json"));
465
+ writeFileSync2(apmConfigPath, `${JSON.stringify(parsed, null, 2)}
466
+ `, "utf8");
467
+ const deployDir = join3(targetApmDir, "deploy");
468
+ await ensureDirExists(deployDir);
469
+ writeFileSync2(
470
+ toFsPath(join3(deployDir, "README.md")),
471
+ config.deploymentDoc ?? "",
472
+ "utf8"
473
+ );
474
+ console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u90E8\u7F72\u914D\u7F6E: ${config.name}`);
475
+ console.log("[apm] \u5DF2\u5199\u5165 .apm/apm.config.json \u4E0E .apm/deploy/README.md");
476
+ return { synced: true, repositoryId, configName: config.name };
477
+ }
478
+
479
+ // src/commands/init.ts
480
+ async function runInit(name) {
481
+ const workdir = resolveWorkdirPath();
482
+ await ensureWorkspaceApmDirForInit(workdir);
483
+ const apmDir = workspaceApmDir(workdir);
484
+ await copyTemplateFiles(apmDir, workdir);
485
+ const syncResult = await syncRemoteDeploymentConfig(workdir, apmDir);
486
+ const trimmedName = name?.trim();
487
+ if (trimmedName) {
488
+ const apmConfigPath = toFsPath(join4(apmDir, "apm.config.json"));
489
+ const config = readFileSync2(apmConfigPath, "utf8");
490
+ const configJson = JSON.parse(config);
491
+ configJson.name = trimmedName;
492
+ writeFileSync3(
493
+ apmConfigPath,
494
+ `${JSON.stringify(configJson, null, 2)}
495
+ `,
496
+ "utf8"
497
+ );
498
+ }
499
+ console.log(`[apm] \u5DF2\u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`);
500
+ console.log(`[apm] \u5DE5\u4F5C\u76EE\u5F55\u8DEF\u5F84\uFF1A${workdir}`);
501
+ if (!syncResult.synced) {
502
+ console.log(
503
+ "[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"
504
+ );
505
+ }
506
+ console.log("[apm] \u8BF7\u5728\u5E73\u53F0\u300C\u63A5\u5165\u7BA1\u7406 \u2192 \u5DE5\u4F5C\u7A7A\u95F4\u300D\u767B\u8BB0\u4E0A\u8FF0\u76EE\u5F55\u8DEF\u5F84");
507
+ }
508
+
398
509
  // src/commands/login.ts
510
+ import { existsSync as existsSync2 } from "fs";
511
+ import { ApiError } from "listpage-http";
399
512
  async function runLogin(opts) {
400
513
  const baseUrl = (opts.server?.trim() || process.env.AI_PM_SERVER?.trim() || DEFAULT_BASE_URL).replace(/\/+$/, "");
401
514
  const apiKey = (opts.apiKey?.trim() || process.env.APM_API_KEY?.trim() || "").replace(/^Bearer\s+/i, "");
@@ -447,12 +560,17 @@ async function runLogin(opts) {
447
560
  2
448
561
  )
449
562
  );
563
+ const workdir = resolveWorkdirPath();
564
+ const apmDir = workspaceApmDir(workdir);
565
+ if (existsSync2(apmDir)) {
566
+ await syncRemoteDeploymentConfig(workdir, apmDir);
567
+ }
450
568
  }
451
569
 
452
570
  // src/commands/branch.ts
453
- import { execFile } from "child_process";
454
- import { promisify } from "util";
455
- var execFileAsync = promisify(execFile);
571
+ import { execFile as execFile2 } from "child_process";
572
+ import { promisify as promisify2 } from "util";
573
+ var execFileAsync2 = promisify2(execFile2);
456
574
  function branchNameForSession(sessionId) {
457
575
  const id = sessionId.trim();
458
576
  if (!id) {
@@ -467,7 +585,7 @@ function branchNameForSession(sessionId) {
467
585
  }
468
586
  async function execGit(cwd, args, quiet) {
469
587
  try {
470
- const { stdout, stderr } = await execFileAsync("git", args, {
588
+ const { stdout, stderr } = await execFileAsync2("git", args, {
471
589
  cwd,
472
590
  encoding: "utf8",
473
591
  maxBuffer: 10 * 1024 * 1024
@@ -617,8 +735,8 @@ async function runBranch(sessionId, options = {}) {
617
735
  }
618
736
 
619
737
  // src/commands/pull.ts
620
- import { writeFileSync as writeFileSync6 } from "fs";
621
- import { join as join7 } from "path";
738
+ import { writeFileSync as writeFileSync7 } from "fs";
739
+ import { join as join8 } from "path";
622
740
  import { stringify as yamlStringify } from "yaml";
623
741
 
624
742
  // src/session-messages-xml.ts
@@ -651,8 +769,8 @@ function formatSessionMessagesXml(sessionId, messages) {
651
769
  }
652
770
 
653
771
  // src/commands/sync-session-attachments.ts
654
- import { existsSync as existsSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
655
- import { join as join4 } from "path";
772
+ import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "fs";
773
+ import { join as join5 } from "path";
656
774
  var MANIFEST_FILE = ".sync-manifest.json";
657
775
  async function downloadAttachment(cfg, attachmentId) {
658
776
  const base = cfg.baseUrl.trim().replace(/\/+$/, "");
@@ -666,8 +784,8 @@ async function downloadAttachment(cfg, attachmentId) {
666
784
  return Buffer.from(await res.arrayBuffer());
667
785
  }
668
786
  function loadManifest(dir) {
669
- const path10 = join4(dir, MANIFEST_FILE);
670
- if (!existsSync2(path10)) {
787
+ const path10 = join5(dir, MANIFEST_FILE);
788
+ if (!existsSync3(path10)) {
671
789
  return { version: 1, attachments: {} };
672
790
  }
673
791
  try {
@@ -682,21 +800,21 @@ function loadManifest(dir) {
682
800
  return { version: 1, attachments: {} };
683
801
  }
684
802
  function saveManifest(dir, manifest) {
685
- writeFileSync3(
686
- join4(dir, MANIFEST_FILE),
803
+ writeFileSync4(
804
+ join5(dir, MANIFEST_FILE),
687
805
  `${JSON.stringify(manifest, null, 2)}
688
806
  `,
689
807
  "utf8"
690
808
  );
691
809
  }
692
810
  function isAttachmentUpToDate(entry, item, dest) {
693
- if (!entry || !existsSync2(dest)) return false;
811
+ if (!entry || !existsSync3(dest)) return false;
694
812
  if (entry.name !== item.name) return false;
695
813
  const createdAt = item.createdAt ?? "";
696
814
  return entry.createdAt === createdAt;
697
815
  }
698
816
  async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
699
- const dir = join4(sessionDir(sessionId, apmRoot), SESSION_ATTACHMENTS_SUBDIR);
817
+ const dir = join5(sessionDir(sessionId, apmRoot), SESSION_ATTACHMENTS_SUBDIR);
700
818
  await ensureDirExists(dir);
701
819
  if (attachments.length === 0) {
702
820
  saveManifest(dir, { version: 1, attachments: {} });
@@ -705,7 +823,7 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
705
823
  const manifest = loadManifest(dir);
706
824
  const nextManifest = { version: 1, attachments: {} };
707
825
  for (const item of attachments) {
708
- const dest = join4(dir, item.name);
826
+ const dest = join5(dir, item.name);
709
827
  const entry = manifest.attachments[item.id];
710
828
  const createdAt = item.createdAt ?? "";
711
829
  if (isAttachmentUpToDate(entry, item, dest)) {
@@ -716,7 +834,7 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
716
834
  continue;
717
835
  }
718
836
  const buffer = await downloadAttachment(cfg, item.id);
719
- writeFileSync3(dest, buffer);
837
+ writeFileSync4(dest, buffer);
720
838
  nextManifest.attachments[item.id] = {
721
839
  name: item.name,
722
840
  createdAt
@@ -727,46 +845,46 @@ async function syncSessionAttachments(cfg, sessionId, attachments, apmRoot) {
727
845
  }
728
846
 
729
847
  // src/rules-sync.ts
730
- import { basename as basename2, extname as extname2, join as join6 } from "path";
731
- import { existsSync as existsSync4, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
848
+ import { basename as basename2, extname as extname2, join as join7 } from "path";
849
+ import { existsSync as existsSync5, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
732
850
 
733
851
  // src/skills-sync.ts
734
852
  import {
735
853
  copyFileSync as copyFileSync2,
736
854
  cpSync,
737
- existsSync as existsSync3,
855
+ existsSync as existsSync4,
738
856
  mkdirSync as mkdirSync3,
739
857
  readdirSync as readdirSync2,
740
858
  rmSync,
741
859
  statSync as statSync2,
742
- writeFileSync as writeFileSync4
860
+ writeFileSync as writeFileSync5
743
861
  } from "fs";
744
- import { join as join5 } from "path";
745
- var AGENTS_TEMPLATE_PATH = join5(CLI_TEMPLATE_DIR, "AGENTS.md");
746
- var BASE_SKILLS_TEMPLATE_DIR = join5(CLI_TEMPLATE_DIR, "skills");
747
- var BASE_RULES_TEMPLATE_DIR = join5(CLI_TEMPLATE_DIR, "rules");
862
+ import { join as join6 } from "path";
863
+ var AGENTS_TEMPLATE_PATH = join6(CLI_TEMPLATE_DIR, "AGENTS.md");
864
+ var BASE_SKILLS_TEMPLATE_DIR = join6(CLI_TEMPLATE_DIR, "skills");
865
+ var BASE_RULES_TEMPLATE_DIR = join6(CLI_TEMPLATE_DIR, "rules");
748
866
  function sanitizeSkillDirName(name) {
749
867
  const trimmed = name.trim();
750
868
  if (!trimmed) return "skill";
751
869
  return trimmed.replace(/[/\\:*?"<>|]/g, "_");
752
870
  }
753
871
  function listBaseSkillDirNames() {
754
- if (!existsSync3(BASE_SKILLS_TEMPLATE_DIR)) return [];
872
+ if (!existsSync4(BASE_SKILLS_TEMPLATE_DIR)) return [];
755
873
  return readdirSync2(BASE_SKILLS_TEMPLATE_DIR).filter((name) => {
756
- const path10 = join5(BASE_SKILLS_TEMPLATE_DIR, name);
874
+ const path10 = join6(BASE_SKILLS_TEMPLATE_DIR, name);
757
875
  return statSync2(path10).isDirectory();
758
876
  });
759
877
  }
760
878
  function syncAgentsGuide(apmDir) {
761
- if (!existsSync3(AGENTS_TEMPLATE_PATH)) return false;
879
+ if (!existsSync4(AGENTS_TEMPLATE_PATH)) return false;
762
880
  mkdirSync3(apmDir, { recursive: true });
763
- copyFileSync2(AGENTS_TEMPLATE_PATH, join5(apmDir, "AGENTS.md"));
881
+ copyFileSync2(AGENTS_TEMPLATE_PATH, join6(apmDir, "AGENTS.md"));
764
882
  return true;
765
883
  }
766
884
  function listBaseRuleFileNames() {
767
- if (!existsSync3(BASE_RULES_TEMPLATE_DIR)) return [];
885
+ if (!existsSync4(BASE_RULES_TEMPLATE_DIR)) return [];
768
886
  return readdirSync2(BASE_RULES_TEMPLATE_DIR).filter((name) => {
769
- const path10 = join5(BASE_RULES_TEMPLATE_DIR, name);
887
+ const path10 = join6(BASE_RULES_TEMPLATE_DIR, name);
770
888
  return statSync2(path10).isFile();
771
889
  });
772
890
  }
@@ -774,8 +892,8 @@ function syncBaseRules(rulesDir) {
774
892
  mkdirSync3(rulesDir, { recursive: true });
775
893
  const names = listBaseRuleFileNames();
776
894
  for (const name of names) {
777
- const src = join5(BASE_RULES_TEMPLATE_DIR, name);
778
- const dest = join5(rulesDir, name);
895
+ const src = join6(BASE_RULES_TEMPLATE_DIR, name);
896
+ const dest = join6(rulesDir, name);
779
897
  copyFileSync2(src, dest);
780
898
  }
781
899
  return names;
@@ -784,8 +902,8 @@ function syncBaseSkills(skillsDir) {
784
902
  mkdirSync3(skillsDir, { recursive: true });
785
903
  const names = listBaseSkillDirNames();
786
904
  for (const name of names) {
787
- const src = join5(BASE_SKILLS_TEMPLATE_DIR, name);
788
- const dest = join5(skillsDir, name);
905
+ const src = join6(BASE_SKILLS_TEMPLATE_DIR, name);
906
+ const dest = join6(skillsDir, name);
789
907
  cpSync(src, dest, { recursive: true, force: true });
790
908
  }
791
909
  return names;
@@ -802,15 +920,15 @@ function syncSupplementarySkills(skillsDir, list) {
802
920
  skipped.push(dirName);
803
921
  continue;
804
922
  }
805
- const skillDir = join5(skillsDir, dirName);
923
+ const skillDir = join6(skillsDir, dirName);
806
924
  mkdirSync3(skillDir, { recursive: true });
807
- writeFileSync4(join5(skillDir, "SKILL.md"), skill.content ?? "", "utf8");
925
+ writeFileSync5(join6(skillDir, "SKILL.md"), skill.content ?? "", "utf8");
808
926
  written.push(dirName);
809
927
  }
810
928
  const removed = [];
811
- if (!existsSync3(skillsDir)) return { written, skipped, removed };
929
+ if (!existsSync4(skillsDir)) return { written, skipped, removed };
812
930
  for (const entry of readdirSync2(skillsDir)) {
813
- const full = join5(skillsDir, entry);
931
+ const full = join6(skillsDir, entry);
814
932
  if (!statSync2(full).isDirectory()) continue;
815
933
  if (baseNames.has(entry)) continue;
816
934
  if (apiDirNames.has(entry)) continue;
@@ -830,8 +948,8 @@ function ruleLocalFileName(ruleName) {
830
948
  return `${sanitized}.md`;
831
949
  }
832
950
  function loadManifest2(rulesDir) {
833
- const path10 = join6(rulesDir, MANIFEST_FILE2);
834
- if (!existsSync4(toFsPath(path10))) {
951
+ const path10 = join7(rulesDir, MANIFEST_FILE2);
952
+ if (!existsSync5(toFsPath(path10))) {
835
953
  return { version: 1, rules: {} };
836
954
  }
837
955
  try {
@@ -846,8 +964,8 @@ function loadManifest2(rulesDir) {
846
964
  return { version: 1, rules: {} };
847
965
  }
848
966
  function saveManifest2(rulesDir, manifest) {
849
- writeFileSync5(
850
- toFsPath(join6(rulesDir, MANIFEST_FILE2)),
967
+ writeFileSync6(
968
+ toFsPath(join7(rulesDir, MANIFEST_FILE2)),
851
969
  `${JSON.stringify(manifest, null, 2)}
852
970
  `,
853
971
  "utf8"
@@ -857,7 +975,7 @@ function isBaseRuleFileName(fileName) {
857
975
  return listBaseRuleFileNames().includes(basename2(fileName));
858
976
  }
859
977
  function isRuleUpToDate(entry, rule, dest) {
860
- if (!entry || !existsSync4(toFsPath(dest))) return false;
978
+ if (!entry || !existsSync5(toFsPath(dest))) return false;
861
979
  if (entry.fileName !== ruleLocalFileName(rule.name)) return false;
862
980
  const updatedAt = rule.updatedAt ?? "";
863
981
  if (entry.updatedAt !== updatedAt) return false;
@@ -868,7 +986,7 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
868
986
  const api = createApmApiClient(cfg);
869
987
  const baseline = await api.cli.branchBaseline({ sessionId, workdirPath });
870
988
  const repositoryId = baseline.repositoryId;
871
- const rulesDir = join6(apmRoot ?? workspaceApmDir(workdirPath), "rules");
989
+ const rulesDir = join7(apmRoot ?? workspaceApmDir(workdirPath), "rules");
872
990
  await ensureDirExists(rulesDir);
873
991
  if (!repositoryId) {
874
992
  console.log(
@@ -885,7 +1003,7 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
885
1003
  for (const rule of list) {
886
1004
  remoteIds.add(rule.id);
887
1005
  const fileName = ruleLocalFileName(rule.name);
888
- const dest = join6(rulesDir, fileName);
1006
+ const dest = join7(rulesDir, fileName);
889
1007
  const entry = manifest.rules[rule.id];
890
1008
  const updatedAt = rule.updatedAt ?? "";
891
1009
  if (isRuleUpToDate(entry, rule, dest)) {
@@ -894,7 +1012,7 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
894
1012
  console.log(`[apm] \u89C4\u5219\u65E0\u53D8\u5316\uFF0C\u5DF2\u8DF3\u8FC7: rules/${fileName}`);
895
1013
  continue;
896
1014
  }
897
- writeFileSync5(toFsPath(dest), rule.content ?? "", "utf8");
1015
+ writeFileSync6(toFsPath(dest), rule.content ?? "", "utf8");
898
1016
  nextManifest.rules[rule.id] = { fileName, updatedAt };
899
1017
  written.push(fileName);
900
1018
  console.log(`[apm] \u5DF2\u540C\u6B65\u5E73\u53F0\u89C4\u5219: rules/${fileName}`);
@@ -903,8 +1021,8 @@ async function syncPlatformRules(cfg, sessionId, workdirPath, apmRoot) {
903
1021
  for (const [ruleId, entry] of Object.entries(manifest.rules)) {
904
1022
  if (remoteIds.has(ruleId)) continue;
905
1023
  if (isBaseRuleFileName(entry.fileName)) continue;
906
- const dest = join6(rulesDir, entry.fileName);
907
- if (existsSync4(toFsPath(dest))) {
1024
+ const dest = join7(rulesDir, entry.fileName);
1025
+ if (existsSync5(toFsPath(dest))) {
908
1026
  rmSync2(toFsPath(dest), { force: true });
909
1027
  }
910
1028
  removed.push(entry.fileName);
@@ -937,20 +1055,20 @@ async function runPull(sessionId, remoteWorkdir) {
937
1055
  const dir = sessionDir(trimmedId, apmRoot);
938
1056
  const docsDir = sessionDocsDir(trimmedId, apmRoot);
939
1057
  await ensureDirExists(docsDir);
940
- writeFileSync6(
1058
+ writeFileSync7(
941
1059
  sessionRulePath(trimmedId, apmRoot),
942
1060
  detail.description ?? "",
943
1061
  "utf8"
944
1062
  );
945
- writeFileSync6(
1063
+ writeFileSync7(
946
1064
  sessionTaskPath(trimmedId, apmRoot),
947
1065
  detail.task.description ?? "",
948
1066
  "utf8"
949
1067
  );
950
- writeFileSync6(sessionTodoPath(trimmedId, apmRoot), detail.todo ?? "", "utf8");
1068
+ writeFileSync7(sessionTodoPath(trimmedId, apmRoot), detail.todo ?? "", "utf8");
951
1069
  for (const doc of documents) {
952
1070
  const fileName = documentLocalFileName(doc.name);
953
- writeFileSync6(join7(docsDir, fileName), doc.content ?? "", "utf8");
1071
+ writeFileSync7(join8(docsDir, fileName), doc.content ?? "", "utf8");
954
1072
  }
955
1073
  const sessionYaml = yamlStringify(
956
1074
  {
@@ -967,13 +1085,13 @@ async function runPull(sessionId, remoteWorkdir) {
967
1085
  },
968
1086
  { lineWidth: 0 }
969
1087
  );
970
- writeFileSync6(
1088
+ writeFileSync7(
971
1089
  sessionYamlPath(trimmedId, apmRoot),
972
1090
  sessionYaml.endsWith("\n") ? sessionYaml : `${sessionYaml}
973
1091
  `,
974
1092
  "utf8"
975
1093
  );
976
- writeFileSync6(
1094
+ writeFileSync7(
977
1095
  sessionMessagesXmlPath(trimmedId, apmRoot),
978
1096
  formatSessionMessagesXml(trimmedId, messages),
979
1097
  "utf8"
@@ -989,13 +1107,13 @@ import { spawnSync } from "child_process";
989
1107
 
990
1108
  // src/version.ts
991
1109
  import { readFileSync as readFileSync5 } from "fs";
992
- import { dirname as dirname2, join as join8 } from "path";
1110
+ import { dirname as dirname2, join as join9 } from "path";
993
1111
  import { fileURLToPath as fileURLToPath2 } from "url";
994
1112
  var CLI_PACKAGE_NAME = "ai-project-manage-cli";
995
1113
  function readCliVersion() {
996
1114
  try {
997
1115
  const dir = dirname2(fileURLToPath2(import.meta.url));
998
- const pkgPath = join8(dir, "..", "package.json");
1116
+ const pkgPath = join9(dir, "..", "package.json");
999
1117
  const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
1000
1118
  return pkg.version ?? "0.0.0";
1001
1119
  } catch {
@@ -1063,11 +1181,11 @@ async function runUpdate() {
1063
1181
  }
1064
1182
 
1065
1183
  // src/commands/update-skills.ts
1066
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, statSync as statSync3 } from "fs";
1067
- import { join as join9 } from "path";
1184
+ import { existsSync as existsSync6, mkdirSync as mkdirSync4, statSync as statSync3 } from "fs";
1185
+ import { join as join10 } from "path";
1068
1186
  async function runUpdateSkills() {
1069
1187
  const apmDir = workspaceApmDir();
1070
- if (!existsSync5(apmDir)) {
1188
+ if (!existsSync6(apmDir)) {
1071
1189
  console.error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
1072
1190
  process.exit(1);
1073
1191
  }
@@ -1081,12 +1199,12 @@ async function runUpdateSkills() {
1081
1199
  if (syncAgentsGuide(apmDir)) {
1082
1200
  console.log("[apm] \u5DF2\u540C\u6B65 APM \u6307\u5357: .apm/AGENTS.md");
1083
1201
  }
1084
- const rulesDir = join9(apmDir, "rules");
1202
+ const rulesDir = join10(apmDir, "rules");
1085
1203
  const ruleNames = syncBaseRules(rulesDir);
1086
1204
  for (const name of ruleNames) {
1087
1205
  console.log(`[apm] \u5DF2\u540C\u6B65\u57FA\u7840\u89C4\u5219: rules/${name}`);
1088
1206
  }
1089
- const skillsDir = join9(apmDir, "skills");
1207
+ const skillsDir = join10(apmDir, "skills");
1090
1208
  mkdirSync4(skillsDir, { recursive: true });
1091
1209
  const baseNames = syncBaseSkills(skillsDir);
1092
1210
  for (const name of baseNames) {
@@ -1112,15 +1230,35 @@ async function runUpdateSkills() {
1112
1230
  );
1113
1231
  }
1114
1232
 
1233
+ // src/commands/sync-deploy-config.ts
1234
+ import { existsSync as existsSync7, statSync as statSync4 } from "fs";
1235
+ async function runSyncDeployConfig() {
1236
+ const workdir = resolveWorkdirPath();
1237
+ const apmDir = workspaceApmDir(workdir);
1238
+ if (!existsSync7(apmDir)) {
1239
+ console.error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
1240
+ process.exit(1);
1241
+ }
1242
+ const apmStat = statSync4(apmDir);
1243
+ if (!apmStat.isDirectory()) {
1244
+ throw new Error(`[apm] \u8DEF\u5F84\u5DF2\u5B58\u5728\u4F46\u4E0D\u662F\u76EE\u5F55: ${apmDir}`);
1245
+ }
1246
+ await ensureLoggedConfig();
1247
+ const result = await syncRemoteDeploymentConfig(workdir, apmDir);
1248
+ if (!result.synced) {
1249
+ process.exit(1);
1250
+ }
1251
+ }
1252
+
1115
1253
  // src/commands/sync-document.ts
1116
- import { existsSync as existsSync7 } from "fs";
1254
+ import { existsSync as existsSync9 } from "fs";
1117
1255
  import { basename as basename3 } from "path";
1118
1256
 
1119
1257
  // src/commands/sync-session-documents.ts
1120
- import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync6 } from "fs";
1121
- import { join as join10 } from "path";
1258
+ import { existsSync as existsSync8, readdirSync as readdirSync3, readFileSync as readFileSync6 } from "fs";
1259
+ import { join as join11 } from "path";
1122
1260
  function listLocalMarkdownFiles(docsDir) {
1123
- if (!existsSync6(docsDir)) {
1261
+ if (!existsSync8(docsDir)) {
1124
1262
  return [];
1125
1263
  }
1126
1264
  return readdirSync3(docsDir).filter(
@@ -1135,7 +1273,7 @@ function remoteDocumentByLocalName(remoteDocuments, localFileName) {
1135
1273
  });
1136
1274
  }
1137
1275
  async function upsertLocalDocumentFile(api, sessionId, docsDir, fileName) {
1138
- const absPath = join10(docsDir, fileName);
1276
+ const absPath = join11(docsDir, fileName);
1139
1277
  const content = readFileSync6(absPath, "utf8");
1140
1278
  const name = documentPlatformName(absPath);
1141
1279
  return api.cli.upsertDocument({
@@ -1158,7 +1296,7 @@ async function syncSessionDocuments(cfg, sessionId, apmRoot, options) {
1158
1296
  const remoteDocuments = options?.remoteDocuments ?? await api.cli.listDocuments({ sessionId: trimmedSessionId });
1159
1297
  let synced = 0;
1160
1298
  for (const fileName of localFiles) {
1161
- const absPath = join10(docsDir, fileName);
1299
+ const absPath = join11(docsDir, fileName);
1162
1300
  const content = readFileSync6(absPath, "utf8");
1163
1301
  const remote = remoteDocumentByLocalName(remoteDocuments, fileName);
1164
1302
  if (remote && remote.content === content) {
@@ -1192,7 +1330,7 @@ async function runSyncDocument(sessionId, options) {
1192
1330
  process.exit(1);
1193
1331
  }
1194
1332
  const absPath = resolveSessionDocumentPath(trimmedSessionId, fileArg);
1195
- if (!existsSync7(absPath)) {
1333
+ if (!existsSync9(absPath)) {
1196
1334
  const docsDir2 = sessionDocsDir(trimmedSessionId);
1197
1335
  console.error(
1198
1336
  `[apm] \u6587\u6863\u4E0D\u5B58\u5728: ${absPath}
@@ -1592,13 +1730,13 @@ ${JSON.stringify(event, null, 2)}
1592
1730
  }
1593
1731
 
1594
1732
  // src/commands/connect/agent-session-registry.ts
1595
- import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "node:fs";
1733
+ import { existsSync as existsSync10, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync8 } from "node:fs";
1596
1734
  import { dirname as dirname3, resolve as resolve3 } from "node:path";
1597
1735
  function registryPath(workdir, sessionId) {
1598
1736
  return resolve3(workdir, ".apm", "sessions", sessionId, "cursor-agents.json");
1599
1737
  }
1600
1738
  function readRegistry(path10) {
1601
- if (!existsSync8(path10)) {
1739
+ if (!existsSync10(path10)) {
1602
1740
  return {};
1603
1741
  }
1604
1742
  try {
@@ -1620,7 +1758,7 @@ function readRegistry(path10) {
1620
1758
  }
1621
1759
  function writeRegistry(path10, registry) {
1622
1760
  mkdirSync5(dirname3(path10), { recursive: true });
1623
- writeFileSync7(path10, `${JSON.stringify(registry, null, 2)}
1761
+ writeFileSync8(path10, `${JSON.stringify(registry, null, 2)}
1624
1762
  `, "utf8");
1625
1763
  }
1626
1764
  function loadSessionAgentId(workdir, sessionId, user) {
@@ -1756,6 +1894,16 @@ function createAppendMessageCustomTools(cfg, messageId) {
1756
1894
  };
1757
1895
  }
1758
1896
 
1897
+ // src/commands/connect/playwright-mcp.ts
1898
+ function createPlaywrightMcpServers() {
1899
+ return {
1900
+ playwright: {
1901
+ command: "npx",
1902
+ args: ["@playwright/mcp@latest", "--browser", "chrome", "--vision"]
1903
+ }
1904
+ };
1905
+ }
1906
+
1759
1907
  // src/commands/connect/cursor-agent.ts
1760
1908
  setMaxListeners2(50);
1761
1909
  installAbortSignalDebug();
@@ -1783,7 +1931,8 @@ async function obtainAgent(ctx) {
1783
1931
  model: { id: ctx.model || "default" },
1784
1932
  local: {
1785
1933
  cwd: ctx.cwd
1786
- }
1934
+ },
1935
+ mcpServers: createPlaywrightMcpServers()
1787
1936
  };
1788
1937
  const savedAgentId = ctx.user ? loadSessionAgentId(ctx.workdir, ctx.sessionId, ctx.user) : void 0;
1789
1938
  if (savedAgentId) {
@@ -1851,6 +2000,7 @@ async function runCursorAgent(cfg, ctx, options) {
1851
2000
  logAbortSignalStats(signal, "runCursorAgent:after-addListener");
1852
2001
  try {
1853
2002
  const run = await agent.send(prompt, {
2003
+ mcpServers: createPlaywrightMcpServers(),
1854
2004
  local: {
1855
2005
  customTools: createAppendMessageCustomTools(cfg, ctx.messageId)
1856
2006
  }
@@ -2226,14 +2376,14 @@ async function runConnect(options) {
2226
2376
  import path5 from "node:path";
2227
2377
 
2228
2378
  // src/commands/deploy/internal/apm-config.ts
2229
- import { existsSync as existsSync9, readFileSync as readFileSync8 } from "node:fs";
2379
+ import { existsSync as existsSync11, readFileSync as readFileSync8 } from "node:fs";
2230
2380
  import { resolve as resolve4 } from "node:path";
2231
2381
  function loadApmConfig(options) {
2232
2382
  const p = resolve4(
2233
2383
  process.cwd(),
2234
2384
  options?.configPath ?? resolve4(workspaceApmDir(), "apm.config.json")
2235
2385
  );
2236
- if (!existsSync9(p)) {
2386
+ if (!existsSync11(p)) {
2237
2387
  console.error(`\u672A\u627E\u5230\u914D\u7F6E\u6587\u4EF6\uFF1A${p}`);
2238
2388
  process.exit(1);
2239
2389
  }
@@ -2360,7 +2510,7 @@ import path4 from "node:path";
2360
2510
  import Docker from "dockerode";
2361
2511
 
2362
2512
  // src/commands/deploy/internal/backend-deploy/dockerode-client/connection-options.ts
2363
- import { existsSync as existsSync10, readFileSync as readFileSync9 } from "node:fs";
2513
+ import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
2364
2514
  import path from "node:path";
2365
2515
  function asOptionalTlsBuffer(value) {
2366
2516
  if (typeof value !== "string") {
@@ -2372,7 +2522,7 @@ function asOptionalTlsBuffer(value) {
2372
2522
  if (normalized === "") {
2373
2523
  return void 0;
2374
2524
  }
2375
- if (existsSync10(normalized)) {
2525
+ if (existsSync12(normalized)) {
2376
2526
  return readFileSync9(normalized);
2377
2527
  }
2378
2528
  const looksLikePath = /[\\/]/.test(normalized) || normalized.endsWith(".pem");
@@ -2583,7 +2733,7 @@ var DockerodeClient = class {
2583
2733
  var createDockerodeClient = (config) => new DockerodeClient(config);
2584
2734
 
2585
2735
  // src/commands/deploy/internal/backend-deploy/dockerode-client/env.ts
2586
- import { existsSync as existsSync11, readFileSync as readFileSync10, statSync as statSync4 } from "node:fs";
2736
+ import { existsSync as existsSync13, readFileSync as readFileSync10, statSync as statSync5 } from "node:fs";
2587
2737
  import path2 from "node:path";
2588
2738
  function stripSurroundingQuotes(value) {
2589
2739
  const t = value.trim();
@@ -2600,7 +2750,7 @@ function loadEnvFromFile(envFilePath) {
2600
2750
  return {};
2601
2751
  }
2602
2752
  const targetPath = path2.resolve(envFilePath);
2603
- if (!existsSync11(targetPath) || !statSync4(targetPath).isFile()) {
2753
+ if (!existsSync13(targetPath) || !statSync5(targetPath).isFile()) {
2604
2754
  return {};
2605
2755
  }
2606
2756
  const raw = readFileSync10(targetPath, "utf-8");
@@ -2774,12 +2924,12 @@ function dockerPushImage(params, cwd) {
2774
2924
  }
2775
2925
 
2776
2926
  // src/commands/deploy/internal/backend-deploy/resolve-dockerfile.ts
2777
- import { existsSync as existsSync12 } from "node:fs";
2927
+ import { existsSync as existsSync14 } from "node:fs";
2778
2928
  import path3 from "node:path";
2779
2929
  function resolveDockerBuildPaths(cwd) {
2780
2930
  const dockerfilePath = path3.join(cwd, "Dockerfile");
2781
2931
  Logger.info(`\u67E5\u627EDockerfile\u6587\u4EF6\uFF0C\u8DEF\u5F84: ${dockerfilePath}`);
2782
- if (!existsSync12(dockerfilePath)) {
2932
+ if (!existsSync14(dockerfilePath)) {
2783
2933
  throw new Error(`Dockerfile \u4E0D\u5B58\u5728\uFF1A${dockerfilePath}`);
2784
2934
  }
2785
2935
  Logger.info("\u2713 Dockerfile \u5B58\u5728");
@@ -2908,14 +3058,14 @@ import { copyFile, readdir as readdir2, stat } from "node:fs/promises";
2908
3058
  import path7 from "node:path";
2909
3059
 
2910
3060
  // src/commands/deploy/internal/minio.ts
2911
- import { statSync as statSync5 } from "node:fs";
3061
+ import { statSync as statSync6 } from "node:fs";
2912
3062
  import { readdir, readFile } from "node:fs/promises";
2913
3063
  import path6 from "node:path";
2914
3064
  import * as Minio from "minio";
2915
3065
  var DEFAULT_MAX_FILE_SIZE_MB = 50;
2916
3066
  async function isDirectoryPath(dir) {
2917
3067
  try {
2918
- const st = statSync5(dir);
3068
+ const st = statSync6(dir);
2919
3069
  return st.isDirectory();
2920
3070
  } catch {
2921
3071
  return false;
@@ -2945,7 +3095,7 @@ async function collectFiles(root) {
2945
3095
  if (e.isDirectory()) {
2946
3096
  await walk(abs, rel);
2947
3097
  } else if (e.isFile()) {
2948
- const st = statSync5(abs);
3098
+ const st = statSync6(abs);
2949
3099
  out.push({
2950
3100
  absPath: abs,
2951
3101
  relativePath: rel.replace(/\\/g, "/"),
@@ -3388,7 +3538,9 @@ function buildProgram() {
3388
3538
  ).option("--api-key <key>", "\u5BA2\u6237\u673A API Key\uFF08cm_ \u5F00\u5934\uFF09").option("--server <url>", "API \u6839\u5730\u5740\uFF0C\u4F8B\u5982 http://127.0.0.1:3000").action(async (opts) => {
3389
3539
  await runLogin(opts);
3390
3540
  });
3391
- program.command("init").description("\u5728\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u521D\u59CB\u5316 .apm \u6A21\u677F\uFF08\u82E5 .apm \u5DF2\u5B58\u5728\u4E14\u975E\u7A7A\u5219\u62A5\u9519\uFF09").option("--name <name>", "\u5DE5\u4F5C\u76EE\u5F55\u540D\u79F0").action(async (opts) => {
3541
+ program.command("init").description(
3542
+ "\u5728\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u521D\u59CB\u5316 .apm \u6A21\u677F\uFF08\u82E5 .apm \u5DF2\u5B58\u5728\u4E14\u975E\u7A7A\u5219\u62A5\u9519\uFF09\uFF1B\u5DF2\u767B\u5F55\u4E14\u5E73\u53F0\u6709\u5339\u914D\u90E8\u7F72\u914D\u7F6E\u65F6\u4F1A\u540C\u6B65 apm.config.json \u4E0E deploy/README.md"
3543
+ ).option("--name <name>", "\u5DE5\u4F5C\u76EE\u5F55\u540D\u79F0").action(async (opts) => {
3392
3544
  await runInit(opts.name);
3393
3545
  });
3394
3546
  program.command("update").description(
@@ -3401,6 +3553,11 @@ function buildProgram() {
3401
3553
  ).action(async () => {
3402
3554
  await runUpdateSkills();
3403
3555
  });
3556
+ program.command("sync-deploy-config").description(
3557
+ "\u4ECE\u5E73\u53F0\u62C9\u53D6\u90E8\u7F72\u914D\u7F6E\uFF0C\u8986\u76D6 .apm/apm.config.json \u4E0E .apm/deploy/README.md\uFF08\u9700\u5DF2 login \u4E14\u5DE5\u4F5C\u7A7A\u95F4\u5DF2\u767B\u8BB0\u5E76\u7ED1\u5B9A\u4ED3\u5E93\uFF09"
3558
+ ).action(async () => {
3559
+ await runSyncDeployConfig();
3560
+ });
3404
3561
  program.command("pull").description(
3405
3562
  "\u62C9\u53D6\u6C9F\u901A\u7FA4\u6570\u636E\u5230 .apm/sessions/<sessionId>/\uFF08session.yaml\u3001RULE.md\u3001TASK.md\u3001TODO.md\u3001docs\u3001attachments\uFF09"
3406
3563
  ).argument("<sessionId>", "\u6C9F\u901A\u7FA4 ID").action(async (sessionId) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-project-manage-cli",
3
- "version": "6.0.44",
3
+ "version": "6.0.46",
4
4
  "description": "命令行工具:后续用于调用平台后端 API 完成运维与自动化操作",
5
5
  "type": "module",
6
6
  "private": false,