ai-project-manage-cli 8.1.6 → 8.1.8

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
@@ -79,179 +79,9 @@ function buildAgentWsUrl(httpBase, apiKey) {
79
79
  return `${origin}/ws/agent?${q.toString()}`;
80
80
  }
81
81
 
82
- // src/utils/workspace.ts
83
- import { copyFileSync, existsSync, mkdirSync as mkdirSync2, readdirSync, statSync } from "fs";
84
- import { dirname, join as join2, resolve as resolve2 } from "path";
85
- import { fileURLToPath } from "url";
86
-
87
- // src/utils/path.ts
88
- import { realpathSync } from "fs";
89
- import { platform } from "os";
90
- import { resolve } from "path";
91
- function toFsPath(inputPath) {
92
- const absolute = resolve(inputPath);
93
- if (platform() !== "win32") return absolute;
94
- if (absolute.startsWith("\\\\?\\")) return absolute;
95
- const normalized = absolute.replace(/\//g, "\\");
96
- if (normalized.startsWith("\\\\")) {
97
- return `\\\\?\\UNC\\${normalized.slice(2)}`;
98
- }
99
- return `\\\\?\\${normalized}`;
100
- }
101
- function normalizeWorkdirPath(path) {
102
- let normalized = path.trim().replace(/\\/g, "/").normalize("NFC");
103
- if (normalized.startsWith("//?/")) {
104
- normalized = normalized.slice(4);
105
- }
106
- const windowsDrive = /^([A-Za-z]:)\/*(.*)$/.exec(normalized);
107
- if (windowsDrive) {
108
- const drive = windowsDrive[1].toLowerCase();
109
- const rest = windowsDrive[2].replace(/\/+/g, "/").replace(/\/$/, "");
110
- return rest ? `${drive}/${rest}` : drive;
111
- }
112
- normalized = normalized.replace(/\/+/g, "/");
113
- if (normalized.length > 1 && normalized.endsWith("/")) {
114
- normalized = normalized.slice(0, -1);
115
- }
116
- return normalized;
117
- }
118
- function resolveWorkdirPath(cwd = process.cwd()) {
119
- const absolute = resolve(cwd);
120
- try {
121
- return normalizeWorkdirPath(realpathSync.native(absolute));
122
- } catch {
123
- return normalizeWorkdirPath(absolute);
124
- }
125
- }
126
-
127
- // src/utils/workspace.ts
128
- var __dirname = dirname(fileURLToPath(import.meta.url));
129
- var CLI_TEMPLATE_DIR = resolve2(__dirname, "../template");
130
- function workspaceApmDir(cwd = resolveWorkdirPath()) {
131
- return resolve2(resolve2(cwd), ".apm");
132
- }
133
- async function ensureLoggedConfig() {
134
- const cfg = await ensureApmConfig();
135
- if (!resolveApiKey(cfg)) {
136
- console.error(
137
- "[apm] \u672A\u68C0\u6D4B\u5230 API Key\uFF0C\u8BF7\u5148\u6267\u884C: apm login --api-key cm_xxx"
138
- );
139
- process.exit(1);
140
- }
141
- return cfg;
142
- }
143
- async function ensureDirExists(dir) {
144
- mkdirSync2(dir, { recursive: true });
145
- }
146
- async function ensureWorkspaceApmDirForInit(cwd = resolveWorkdirPath()) {
147
- const dir = workspaceApmDir(cwd);
148
- const fsDir = toFsPath(dir);
149
- if (existsSync(fsDir) && !statSync(fsDir).isDirectory()) {
150
- throw new Error(`[apm] \u8DEF\u5F84\u5DF2\u5B58\u5728\u4F46\u4E0D\u662F\u76EE\u5F55: ${dir}`);
151
- }
152
- mkdirSync2(fsDir, { recursive: true });
153
- }
154
- var WEBIDE_AGENTS_TEMPLATE_NAME = "AGENTS.webide.md";
155
- function shouldSkipTemplateEntry(name) {
156
- return name === ".DS_Store" || name === "Thumbs.db";
157
- }
158
- function isWebIdeRuleFileName(name) {
159
- return name.startsWith("webide");
160
- }
161
- function copyTemplateEntry(src, dest) {
162
- const fsSrc = toFsPath(src);
163
- const fsDest = toFsPath(dest);
164
- const st = statSync(fsSrc);
165
- if (st.isDirectory()) {
166
- mkdirSync2(fsDest, { recursive: true });
167
- for (const name of readdirSync(fsSrc)) {
168
- if (shouldSkipTemplateEntry(name)) continue;
169
- copyTemplateEntry(join2(src, name), join2(dest, name));
170
- }
171
- return;
172
- }
173
- if (!st.isFile()) return;
174
- mkdirSync2(toFsPath(dirname(dest)), { recursive: true });
175
- copyFileSync(fsSrc, fsDest);
176
- }
177
- function listWebIdeRuleFileNames(rulesDir) {
178
- const fsRulesDir = toFsPath(rulesDir);
179
- if (!existsSync(fsRulesDir) || !statSync(fsRulesDir).isDirectory()) {
180
- return [];
181
- }
182
- return readdirSync(fsRulesDir).filter((name) => {
183
- if (shouldSkipTemplateEntry(name) || !isWebIdeRuleFileName(name)) {
184
- return false;
185
- }
186
- return statSync(toFsPath(join2(rulesDir, name))).isFile();
187
- });
188
- }
189
- function assertWebIdeTemplateCopiedToApm(apmDir, workdir) {
190
- const required = ["AGENTS.md", "rules"];
191
- for (const item of required) {
192
- const path = join2(apmDir, item);
193
- if (!existsSync(toFsPath(path))) {
194
- throw new Error(`[apm] WebIDE \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11: ${path}`);
195
- }
196
- }
197
- const rulesDir = join2(apmDir, "rules");
198
- const ruleNames = listWebIdeRuleFileNames(rulesDir);
199
- if (ruleNames.length === 0) {
200
- throw new Error(`[apm] WebIDE \u521D\u59CB\u5316\u4E0D\u5B8C\u6574\uFF0C\u7F3A\u5C11 webide \u89C4\u5219: ${rulesDir}`);
201
- }
202
- const leakedRules = join2(workdir, "rules");
203
- const apmRules = join2(apmDir, "rules");
204
- if (existsSync(toFsPath(leakedRules)) && !existsSync(toFsPath(join2(apmRules, "webide_reply.md")))) {
205
- throw new Error(
206
- `[apm] \u6A21\u677F\u88AB\u590D\u5236\u5230\u9519\u8BEF\u4F4D\u7F6E: ${leakedRules}\uFF08\u5E94\u5728 ${apmRules}\uFF09`
207
- );
208
- }
209
- }
210
- async function copyWebIdeTemplateFiles(targetDir, workdir = resolveWorkdirPath()) {
211
- const resolvedTarget = resolve2(targetDir);
212
- const templateDir = resolve2(CLI_TEMPLATE_DIR);
213
- const fsTemplateDir = toFsPath(templateDir);
214
- if (!existsSync(fsTemplateDir) || !statSync(fsTemplateDir).isDirectory()) {
215
- throw new Error(`[apm] \u672A\u627E\u5230 CLI \u6A21\u677F\u76EE\u5F55: ${templateDir}`);
216
- }
217
- const agentsSrc = join2(templateDir, WEBIDE_AGENTS_TEMPLATE_NAME);
218
- if (!existsSync(toFsPath(agentsSrc))) {
219
- throw new Error(`[apm] \u672A\u627E\u5230 WebIDE \u6307\u5357: ${agentsSrc}`);
220
- }
221
- const rulesSrc = join2(templateDir, "rules");
222
- const ruleNames = listWebIdeRuleFileNames(rulesSrc);
223
- if (ruleNames.length === 0) {
224
- throw new Error(`[apm] CLI \u6A21\u677F\u4E2D\u6CA1\u6709 webide \u89C4\u5219: ${rulesSrc}`);
225
- }
226
- mkdirSync2(toFsPath(resolvedTarget), { recursive: true });
227
- copyFileSync(
228
- toFsPath(agentsSrc),
229
- toFsPath(join2(resolvedTarget, "AGENTS.md"))
230
- );
231
- const rulesDest = join2(resolvedTarget, "rules");
232
- mkdirSync2(toFsPath(rulesDest), { recursive: true });
233
- for (const name of ruleNames) {
234
- copyTemplateEntry(join2(rulesSrc, name), join2(rulesDest, name));
235
- }
236
- assertWebIdeTemplateCopiedToApm(resolvedTarget, resolve2(workdir));
237
- }
238
-
239
- // src/commands/init.ts
240
- async function initializeWorkspace(workdir) {
241
- await ensureWorkspaceApmDirForInit(workdir);
242
- const apmDir = workspaceApmDir(workdir);
243
- await copyWebIdeTemplateFiles(apmDir, workdir);
244
- console.log(`[apm] \u5DF2\u6309 WebIDE \u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`);
245
- return { didInit: true };
246
- }
247
- async function runInit() {
248
- const workdir = resolveWorkdirPath();
249
- await initializeWorkspace(workdir);
250
- console.log(`[apm] \u5DE5\u4F5C\u76EE\u5F55\u8DEF\u5F84\uFF1A${workdir}`);
251
- }
252
-
253
- // src/commands/login.ts
254
- import { ApiError } from "listpage-http";
82
+ // src/commands/sync-webide-agent.ts
83
+ import { existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
84
+ import { join as join3 } from "path";
255
85
 
256
86
  // src/api/client.ts
257
87
  import { createApiClient } from "listpage-http";
@@ -266,6 +96,10 @@ var requestConfig = {
266
96
  path: "/cli/me"
267
97
  }
268
98
  ),
99
+ webideAgentConfig: defineEndpoint({
100
+ method: "GET",
101
+ path: "/cli/webide/agent-config"
102
+ }),
269
103
  webideAppendMessageContent: defineEndpoint({
270
104
  method: "PUT",
271
105
  path: "/cli/webide/messages/content"
@@ -324,6 +158,14 @@ var requestConfig = {
324
158
  method: "PUT",
325
159
  path: "/cli/webide/plan"
326
160
  }),
161
+ webideUpsertChecklist: defineEndpoint({
162
+ method: "PUT",
163
+ path: "/cli/webide/checklist"
164
+ }),
165
+ webideGetChecklist: defineEndpoint({
166
+ method: "GET",
167
+ path: "/cli/webide/checklist"
168
+ }),
327
169
  webideReplaceTestCases: defineEndpoint({
328
170
  method: "PUT",
329
171
  path: "/cli/webide/test-cases"
@@ -456,7 +298,157 @@ function createApmApiClient(cfg) {
456
298
  });
457
299
  }
458
300
 
301
+ // src/utils/path.ts
302
+ import { realpathSync } from "fs";
303
+ import { platform } from "os";
304
+ import { resolve } from "path";
305
+ function toFsPath(inputPath) {
306
+ const absolute = resolve(inputPath);
307
+ if (platform() !== "win32") return absolute;
308
+ if (absolute.startsWith("\\\\?\\")) return absolute;
309
+ const normalized = absolute.replace(/\//g, "\\");
310
+ if (normalized.startsWith("\\\\")) {
311
+ return `\\\\?\\UNC\\${normalized.slice(2)}`;
312
+ }
313
+ return `\\\\?\\${normalized}`;
314
+ }
315
+ function normalizeWorkdirPath(path) {
316
+ let normalized = path.trim().replace(/\\/g, "/").normalize("NFC");
317
+ if (normalized.startsWith("//?/")) {
318
+ normalized = normalized.slice(4);
319
+ }
320
+ const windowsDrive = /^([A-Za-z]:)\/*(.*)$/.exec(normalized);
321
+ if (windowsDrive) {
322
+ const drive = windowsDrive[1].toLowerCase();
323
+ const rest = windowsDrive[2].replace(/\/+/g, "/").replace(/\/$/, "");
324
+ return rest ? `${drive}/${rest}` : drive;
325
+ }
326
+ normalized = normalized.replace(/\/+/g, "/");
327
+ if (normalized.length > 1 && normalized.endsWith("/")) {
328
+ normalized = normalized.slice(0, -1);
329
+ }
330
+ return normalized;
331
+ }
332
+ function resolveWorkdirPath(cwd = process.cwd()) {
333
+ const absolute = resolve(cwd);
334
+ try {
335
+ return normalizeWorkdirPath(realpathSync.native(absolute));
336
+ } catch {
337
+ return normalizeWorkdirPath(absolute);
338
+ }
339
+ }
340
+
341
+ // src/utils/workspace.ts
342
+ import { existsSync, mkdirSync as mkdirSync2, readdirSync, statSync } from "fs";
343
+ import { join as join2, resolve as resolve2 } from "path";
344
+ function workspaceApmDir(cwd = resolveWorkdirPath()) {
345
+ return resolve2(resolve2(cwd), ".apm");
346
+ }
347
+ async function ensureLoggedConfig() {
348
+ const cfg = await ensureApmConfig();
349
+ if (!resolveApiKey(cfg)) {
350
+ console.error(
351
+ "[apm] \u672A\u68C0\u6D4B\u5230 API Key\uFF0C\u8BF7\u5148\u6267\u884C: apm login --api-key cm_xxx"
352
+ );
353
+ process.exit(1);
354
+ }
355
+ return cfg;
356
+ }
357
+ async function ensureDirExists(dir) {
358
+ mkdirSync2(dir, { recursive: true });
359
+ }
360
+ async function ensureWorkspaceApmDirForInit(cwd = resolveWorkdirPath()) {
361
+ const dir = workspaceApmDir(cwd);
362
+ const fsDir = toFsPath(dir);
363
+ if (existsSync(fsDir) && !statSync(fsDir).isDirectory()) {
364
+ throw new Error(`[apm] \u8DEF\u5F84\u5DF2\u5B58\u5728\u4F46\u4E0D\u662F\u76EE\u5F55: ${dir}`);
365
+ }
366
+ mkdirSync2(fsDir, { recursive: true });
367
+ }
368
+
369
+ // src/commands/sync-webide-agent.ts
370
+ var REVISION_FILE = ".webide-agent.json";
371
+ var RULE_KEY = /^webide_[a-z0-9_]+$/;
372
+ function withTrailingNewline(text) {
373
+ return text.endsWith("\n") ? text : `${text}
374
+ `;
375
+ }
376
+ function readRevision(apmDir) {
377
+ const path = toFsPath(join3(apmDir, REVISION_FILE));
378
+ if (!existsSync2(path)) return null;
379
+ try {
380
+ const parsed = JSON.parse(readFileSync2(path, "utf8"));
381
+ if (parsed?.version === 1 && parsed.revision?.trim()) {
382
+ return parsed.revision.trim();
383
+ }
384
+ } catch {
385
+ return null;
386
+ }
387
+ return null;
388
+ }
389
+ function writeRevision(apmDir, revision) {
390
+ writeFileSync2(
391
+ toFsPath(join3(apmDir, REVISION_FILE)),
392
+ `${JSON.stringify({ version: 1, revision }, null, 2)}
393
+ `,
394
+ "utf8"
395
+ );
396
+ }
397
+ async function syncWebIdeAgentFiles(workdir, cfg) {
398
+ const api = createApmApiClient(cfg);
399
+ const bundle = await api.cli.webideAgentConfig();
400
+ const revision = bundle.revision?.trim();
401
+ if (!revision) {
402
+ throw new Error("[apm] \u5E73\u53F0\u672A\u8FD4\u56DE WebIDE \u89C4\u5219\u7248\u672C");
403
+ }
404
+ const apmDir = workspaceApmDir(workdir);
405
+ mkdirSync3(toFsPath(apmDir), { recursive: true });
406
+ if (readRevision(apmDir) === revision) {
407
+ return { revision, wrote: false, ruleCount: bundle.rules?.length ?? 0 };
408
+ }
409
+ const rulesDir = join3(apmDir, "rules");
410
+ mkdirSync3(toFsPath(rulesDir), { recursive: true });
411
+ writeFileSync2(
412
+ toFsPath(join3(apmDir, "AGENTS.md")),
413
+ withTrailingNewline(bundle.guide ?? ""),
414
+ "utf8"
415
+ );
416
+ for (const rule of bundle.rules ?? []) {
417
+ const key = rule.key?.trim() ?? "";
418
+ if (!RULE_KEY.test(key)) {
419
+ throw new Error(`[apm] \u975E\u6CD5\u89C4\u5219\u540D: ${key || "(\u7A7A)"}`);
420
+ }
421
+ writeFileSync2(
422
+ toFsPath(join3(rulesDir, `${key}.md`)),
423
+ withTrailingNewline(rule.body ?? ""),
424
+ "utf8"
425
+ );
426
+ }
427
+ writeRevision(apmDir, revision);
428
+ return {
429
+ revision,
430
+ wrote: true,
431
+ ruleCount: bundle.rules?.length ?? 0
432
+ };
433
+ }
434
+
435
+ // src/commands/init.ts
436
+ async function initializeWorkspace(workdir, cfg) {
437
+ await ensureWorkspaceApmDirForInit(workdir);
438
+ await syncWebIdeAgentFiles(workdir, cfg);
439
+ const apmDir = workspaceApmDir(workdir);
440
+ console.log(`[apm] \u5DF2\u6309 WebIDE \u521D\u59CB\u5316\u5DE5\u4F5C\u533A\uFF1A${apmDir}`);
441
+ return { didInit: true };
442
+ }
443
+ async function runInit() {
444
+ const workdir = resolveWorkdirPath();
445
+ const cfg = await ensureLoggedConfig();
446
+ await initializeWorkspace(workdir, cfg);
447
+ console.log(`[apm] \u5DE5\u4F5C\u76EE\u5F55\u8DEF\u5F84\uFF1A${workdir}`);
448
+ }
449
+
459
450
  // src/commands/login.ts
451
+ import { ApiError } from "listpage-http";
460
452
  async function runLogin(opts) {
461
453
  const baseUrl = (opts.server?.trim() || process.env.AI_PM_SERVER?.trim() || DEFAULT_BASE_URL).replace(/\/+$/, "");
462
454
  const apiKey = (opts.apiKey?.trim() || process.env.APM_API_KEY?.trim() || "").replace(/^Bearer\s+/i, "");
@@ -512,17 +504,19 @@ async function runLogin(opts) {
512
504
 
513
505
  // src/commands/update.ts
514
506
  import { spawnSync } from "child_process";
507
+ import { existsSync as existsSync3 } from "fs";
508
+ import { join as join5 } from "path";
515
509
 
516
510
  // src/version.ts
517
- import { readFileSync as readFileSync2 } from "fs";
518
- import { dirname as dirname2, join as join3 } from "path";
519
- import { fileURLToPath as fileURLToPath2 } from "url";
511
+ import { readFileSync as readFileSync3 } from "fs";
512
+ import { dirname, join as join4 } from "path";
513
+ import { fileURLToPath } from "url";
520
514
  var CLI_PACKAGE_NAME = "ai-project-manage-cli";
521
515
  function readCliVersion() {
522
516
  try {
523
- const dir = dirname2(fileURLToPath2(import.meta.url));
524
- const pkgPath = join3(dir, "..", "package.json");
525
- const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
517
+ const dir = dirname(fileURLToPath(import.meta.url));
518
+ const pkgPath = join4(dir, "..", "package.json");
519
+ const pkg = JSON.parse(readFileSync3(pkgPath, "utf8"));
526
520
  return pkg.version ?? "0.0.0";
527
521
  } catch {
528
522
  return "0.0.0";
@@ -530,6 +524,7 @@ function readCliVersion() {
530
524
  }
531
525
 
532
526
  // src/commands/update.ts
527
+ var APM_SKIP_SELF_UPDATE_ENV = "APM_SKIP_SELF_UPDATE";
533
528
  var useNpmShell = process.platform === "win32";
534
529
  function runNpm(args, options = {}) {
535
530
  return spawnSync(useNpmShell ? "npm.cmd" : "npm", args, {
@@ -622,10 +617,17 @@ function npmAvailable() {
622
617
  return !r.error && r.status === 0;
623
618
  }
624
619
  async function runUpdate(options = {}) {
620
+ if (process.env[APM_SKIP_SELF_UPDATE_ENV] === "1") {
621
+ return { didUpdate: false };
622
+ }
625
623
  const current = readCliVersion();
626
624
  const currentMajor = parseMajorVersion(current);
627
625
  const globalLatest = await fetchLatestPublishedVersion();
628
626
  const latestInMajor = options.allowMajorUpgrade ? null : await fetchLatestPublishedVersionInMajor(currentMajor);
627
+ if (options.quietIfCurrent && !options.allowMajorUpgrade && !latestInMajor && !globalLatest) {
628
+ console.warn(`[apm] \u65E0\u6CD5\u68C0\u6D4B\u6700\u65B0\u7248\u672C\uFF0C\u7EE7\u7EED\u4F7F\u7528\u5F53\u524D ${current}`);
629
+ return { didUpdate: false };
630
+ }
629
631
  const resolved = resolveUpdateTarget({
630
632
  current,
631
633
  allowMajorUpgrade: options.allowMajorUpgrade,
@@ -633,8 +635,10 @@ async function runUpdate(options = {}) {
633
635
  globalLatest
634
636
  });
635
637
  if (resolved.action === "skip") {
636
- console.log(`[apm] \u5DF2\u662F\u6700\u65B0\u7248\u672C ${current}`);
637
- return;
638
+ if (!options.quietIfCurrent) {
639
+ console.log(`[apm] \u5DF2\u662F\u6700\u65B0\u7248\u672C ${current}`);
640
+ }
641
+ return { didUpdate: false };
638
642
  }
639
643
  if (resolved.majorUpgradeAvailable) {
640
644
  console.log(
@@ -671,26 +675,72 @@ async function runUpdate(options = {}) {
671
675
  } else {
672
676
  console.log(`[apm] \u5DF2\u662F\u6700\u65B0\u7248\u672C ${current}`);
673
677
  }
678
+ return { didUpdate: expectedBump };
679
+ }
680
+ function resolveApmEntryPath(entryArg = process.argv[1]) {
681
+ const npmResult = runNpm(["root", "-g"], {
682
+ encoding: "utf8",
683
+ stdio: ["ignore", "pipe", "pipe"]
684
+ });
685
+ if (npmResult.status === 0) {
686
+ const globalRoot = npmResult.stdout?.toString().trim();
687
+ if (globalRoot) {
688
+ const candidate = join5(globalRoot, CLI_PACKAGE_NAME, "dist", "index.js");
689
+ if (existsSync3(candidate)) {
690
+ return candidate;
691
+ }
692
+ }
693
+ }
694
+ const fromArgv = entryArg?.trim();
695
+ if (fromArgv && existsSync3(fromArgv)) {
696
+ return fromArgv;
697
+ }
698
+ if (fromArgv) {
699
+ return fromArgv;
700
+ }
701
+ console.error("[apm] \u65E0\u6CD5\u89E3\u6790 apm \u5165\u53E3\u8DEF\u5F84");
702
+ process.exit(1);
703
+ }
704
+ function reexecConnectAfterUpdate(options) {
705
+ console.log("[apm] \u66F4\u65B0\u5B8C\u6210\uFF0C\u6B63\u5728\u4EE5\u65B0\u7248\u672C\u91CD\u65B0\u8FDE\u63A5\u2026");
706
+ const apmScript = resolveApmEntryPath();
707
+ const args = [apmScript, "connect"];
708
+ const server = options.server?.trim();
709
+ if (server) {
710
+ args.push("--server", server);
711
+ }
712
+ const result = spawnSync(process.execPath, args, {
713
+ stdio: "inherit",
714
+ env: { ...process.env, [APM_SKIP_SELF_UPDATE_ENV]: "1" }
715
+ });
716
+ if (result.error) {
717
+ console.error("[apm] \u91CD\u542F connect \u5931\u8D25:", result.error.message);
718
+ process.exit(1);
719
+ }
720
+ process.exit(result.status ?? 0);
674
721
  }
675
722
 
676
723
  // src/commands/update-skills.ts
677
- import { existsSync as existsSync2, statSync as statSync2 } from "fs";
724
+ import { existsSync as existsSync4, statSync as statSync2 } from "fs";
678
725
  async function syncWorkspaceSkills(workdir) {
679
726
  const apmDir = workspaceApmDir(workdir);
680
727
  const fsApmDir = toFsPath(apmDir);
681
- if (!existsSync2(fsApmDir)) {
728
+ if (!existsSync4(fsApmDir)) {
682
729
  throw new Error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
683
730
  }
684
731
  const apmStat = statSync2(fsApmDir);
685
732
  if (!apmStat.isDirectory()) {
686
733
  throw new Error(`[apm] \u8DEF\u5F84\u5DF2\u5B58\u5728\u4F46\u4E0D\u662F\u76EE\u5F55: ${apmDir}`);
687
734
  }
688
- await copyWebIdeTemplateFiles(apmDir, workdir);
689
- console.log("[apm] \u5DF2\u540C\u6B65 WebIDE \u6307\u5357\u4E0E\u89C4\u5219");
735
+ const cfg = await ensureLoggedConfig();
736
+ const result = await syncWebIdeAgentFiles(workdir, cfg);
737
+ console.log(
738
+ result.wrote ? `[apm] \u5DF2\u540C\u6B65 WebIDE \u6307\u5357\u4E0E ${result.ruleCount} \u6761\u89C4\u5219` : "[apm] WebIDE \u6307\u5357\u4E0E\u89C4\u5219\u672A\u53D8\u5316"
739
+ );
690
740
  }
691
741
  async function runUpdateSkills() {
692
742
  const apmDir = workspaceApmDir();
693
- if (!existsSync2(apmDir)) {
743
+ if (!existsSync4(apmDir)) {
694
744
  console.error("[apm] \u672A\u627E\u5230 .apm \u76EE\u5F55\uFF0C\u8BF7\u5148\u6267\u884C apm init");
695
745
  process.exit(1);
696
746
  }
@@ -703,14 +753,14 @@ async function runUpdateSkills() {
703
753
 
704
754
  // src/utils/project-documents.ts
705
755
  import {
706
- existsSync as existsSync3,
756
+ existsSync as existsSync5,
707
757
  readdirSync as readdirSync2,
708
- readFileSync as readFileSync3,
758
+ readFileSync as readFileSync4,
709
759
  rmSync,
710
- writeFileSync as writeFileSync2
760
+ writeFileSync as writeFileSync3
711
761
  } from "fs";
712
762
  import { createHash } from "crypto";
713
- import { dirname as dirname3, join as join4, relative, sep } from "path";
763
+ import { dirname as dirname2, join as join6, relative, sep } from "path";
714
764
 
715
765
  // src/utils/git.ts
716
766
  import { execFile as execFile2 } from "child_process";
@@ -784,11 +834,11 @@ function normalizeProjectIdForPath(projectId) {
784
834
  }
785
835
  function projectDocumentsDir(apmRoot, projectId) {
786
836
  const id = normalizeProjectIdForPath(projectId);
787
- return join4(apmRoot ?? workspaceApmDir(), "project", id);
837
+ return join6(apmRoot ?? workspaceApmDir(), "project", id);
788
838
  }
789
839
  function projectDocumentLocalPath(apmRoot, projectId, documentPath) {
790
840
  const normalized = normalizeLocalDocumentPath(documentPath);
791
- return join4(
841
+ return join6(
792
842
  projectDocumentsDir(apmRoot, projectId),
793
843
  ...normalized.split("/")
794
844
  );
@@ -808,16 +858,16 @@ function hashLocalFileContent(content) {
808
858
  return createHash("sha256").update(content, "utf8").digest("hex");
809
859
  }
810
860
  function readLocalManifest(apmRoot, projectId) {
811
- const manifestPath = join4(
861
+ const manifestPath = join6(
812
862
  projectDocumentsDir(apmRoot, projectId),
813
863
  MANIFEST_FILE
814
864
  );
815
- if (!existsSync3(manifestPath)) {
865
+ if (!existsSync5(manifestPath)) {
816
866
  return null;
817
867
  }
818
868
  try {
819
869
  return JSON.parse(
820
- readFileSync3(manifestPath, "utf8")
870
+ readFileSync4(manifestPath, "utf8")
821
871
  );
822
872
  } catch {
823
873
  return null;
@@ -825,13 +875,13 @@ function readLocalManifest(apmRoot, projectId) {
825
875
  }
826
876
  function listLocalDocumentPaths(apmRoot, projectId) {
827
877
  const root = projectDocumentsDir(apmRoot, projectId);
828
- if (!existsSync3(root)) {
878
+ if (!existsSync5(root)) {
829
879
  return [];
830
880
  }
831
881
  const paths = [];
832
882
  const walk = (dir) => {
833
883
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
834
- const abs = join4(dir, entry.name);
884
+ const abs = join6(dir, entry.name);
835
885
  if (entry.isDirectory()) {
836
886
  walk(abs);
837
887
  continue;
@@ -950,8 +1000,8 @@ ${diagnostic ?? ""}`);
950
1000
  const absPath = toFsPath(
951
1001
  projectDocumentLocalPath(targetApmDir, projectId, doc.path)
952
1002
  );
953
- await ensureDirExists(dirname3(absPath));
954
- writeFileSync2(absPath, doc.content, "utf8");
1003
+ await ensureDirExists(dirname2(absPath));
1004
+ writeFileSync3(absPath, doc.content, "utf8");
955
1005
  downloaded += 1;
956
1006
  }
957
1007
  }
@@ -960,13 +1010,13 @@ ${diagnostic ?? ""}`);
960
1010
  const absPath = toFsPath(
961
1011
  projectDocumentLocalPath(targetApmDir, projectId, path)
962
1012
  );
963
- if (existsSync3(absPath)) {
1013
+ if (existsSync5(absPath)) {
964
1014
  rmSync(absPath, { force: true });
965
1015
  deleted += 1;
966
1016
  }
967
1017
  }
968
- writeFileSync2(
969
- toFsPath(join4(docsDir, MANIFEST_FILE)),
1018
+ writeFileSync3(
1019
+ toFsPath(join6(docsDir, MANIFEST_FILE)),
970
1020
  `${JSON.stringify(remoteManifest, null, 2)}
971
1021
  `,
972
1022
  "utf8"
@@ -1009,7 +1059,7 @@ async function syncProjectDocumentsPush(cfg, workdirPath, apmRoot, options) {
1009
1059
  const absPath = toFsPath(
1010
1060
  projectDocumentLocalPath(targetApmDir, projectId, path)
1011
1061
  );
1012
- const content = readFileSync3(absPath, "utf8");
1062
+ const content = readFileSync4(absPath, "utf8");
1013
1063
  const contentHash = hashLocalFileContent(content);
1014
1064
  if (remoteHashByPath.get(path) === contentHash) {
1015
1065
  continue;
@@ -1284,8 +1334,8 @@ function handleCancelAction(payload, ctx) {
1284
1334
  }
1285
1335
 
1286
1336
  // src/commands/connect/core/worker/worker.ts
1287
- import { dirname as dirname4, join as join5 } from "path";
1288
- import { fileURLToPath as fileURLToPath3 } from "url";
1337
+ import { dirname as dirname3, join as join7 } from "path";
1338
+ import { fileURLToPath as fileURLToPath2 } from "url";
1289
1339
  import { Worker } from "node:worker_threads";
1290
1340
 
1291
1341
  // src/commands/connect/core/worker/config.ts
@@ -1310,8 +1360,8 @@ function resolveWebIdeWorkerHeapMb(env = process.env, totalMemoryBytes = totalme
1310
1360
  }
1311
1361
 
1312
1362
  // src/commands/connect/core/worker/worker.ts
1313
- var workerFile = join5(
1314
- dirname4(fileURLToPath3(import.meta.url)),
1363
+ var workerFile = join7(
1364
+ dirname3(fileURLToPath2(import.meta.url)),
1315
1365
  "worker-script.js"
1316
1366
  );
1317
1367
  var ActionWorker = class {
@@ -1663,14 +1713,14 @@ function dispatchConnectAction(payload, ctx) {
1663
1713
 
1664
1714
  // src/commands/connect-lock.ts
1665
1715
  import {
1666
- existsSync as existsSync4,
1667
- mkdirSync as mkdirSync3,
1668
- readFileSync as readFileSync4,
1716
+ existsSync as existsSync6,
1717
+ mkdirSync as mkdirSync4,
1718
+ readFileSync as readFileSync5,
1669
1719
  unlinkSync,
1670
- writeFileSync as writeFileSync3
1720
+ writeFileSync as writeFileSync4
1671
1721
  } from "fs";
1672
- import { join as join6 } from "path";
1673
- var CONNECT_LOCK_PATH = join6(APM_CONFIG_DIR, "connect.lock");
1722
+ import { join as join8 } from "path";
1723
+ var CONNECT_LOCK_PATH = join8(APM_CONFIG_DIR, "connect.lock");
1674
1724
  function isProcessAlive(pid) {
1675
1725
  if (!Number.isInteger(pid) || pid <= 0) return false;
1676
1726
  try {
@@ -1681,9 +1731,9 @@ function isProcessAlive(pid) {
1681
1731
  }
1682
1732
  }
1683
1733
  function readConnectLock() {
1684
- if (!existsSync4(CONNECT_LOCK_PATH)) return null;
1734
+ if (!existsSync6(CONNECT_LOCK_PATH)) return null;
1685
1735
  try {
1686
- const raw = readFileSync4(CONNECT_LOCK_PATH, "utf8");
1736
+ const raw = readFileSync5(CONNECT_LOCK_PATH, "utf8");
1687
1737
  const parsed = JSON.parse(raw);
1688
1738
  if (typeof parsed.pid !== "number" || typeof parsed.startedAt !== "string") {
1689
1739
  return null;
@@ -1715,12 +1765,12 @@ function acquireConnectLock() {
1715
1765
  console.error(`[apm] \u5DF2\u6709 apm connect \u5728\u8FD0\u884C (pid=${existing.pid})`);
1716
1766
  process.exit(1);
1717
1767
  }
1718
- mkdirSync3(APM_CONFIG_DIR, { recursive: true });
1768
+ mkdirSync4(APM_CONFIG_DIR, { recursive: true });
1719
1769
  const lock = {
1720
1770
  pid: process.pid,
1721
1771
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
1722
1772
  };
1723
- writeFileSync3(
1773
+ writeFileSync4(
1724
1774
  CONNECT_LOCK_PATH,
1725
1775
  JSON.stringify(lock, null, 2) + "\n",
1726
1776
  "utf8"
@@ -1847,6 +1897,10 @@ function resolveConnectServerOption(options) {
1847
1897
  return void 0;
1848
1898
  }
1849
1899
  async function runConnect(options) {
1900
+ const { didUpdate } = await runUpdate({ quietIfCurrent: true });
1901
+ if (didUpdate) {
1902
+ reexecConnectAfterUpdate(options);
1903
+ }
1850
1904
  const cfg = await ensureLoggedConfig();
1851
1905
  const serverOverride = resolveConnectServerOption(options);
1852
1906
  if (serverOverride) {
@@ -1951,7 +2005,7 @@ function buildProgram() {
1951
2005
  ).option("--api-key <key>", "\u8FDC\u7A0B\u4E3B\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) => {
1952
2006
  await runLogin(opts);
1953
2007
  });
1954
- program.command("init").description("\u5728\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u6309 WebIDE \u5DE5\u4F5C\u533A\u521D\u59CB\u5316\uFF1A\u5199\u5165\u6307\u5357\u4E0E webide \u89C4\u5219").action(async () => {
2008
+ program.command("init").description("\u5728\u5F53\u524D\u5DE5\u4F5C\u76EE\u5F55\u6309 WebIDE \u5DE5\u4F5C\u533A\u521D\u59CB\u5316\uFF1A\u4ECE\u5E73\u53F0\u5199\u5165\u6307\u5357\u4E0E\u89C4\u5219").action(async () => {
1955
2009
  await runInit();
1956
2010
  });
1957
2011
  program.command("update").description(
@@ -1964,7 +2018,7 @@ function buildProgram() {
1964
2018
  ).action(async (opts) => {
1965
2019
  await runUpdate({ allowMajorUpgrade: opts.major === true });
1966
2020
  });
1967
- program.command("update-skills").description("\u540C\u6B65 .apm/ \u4E0B\u7684 WebIDE \u6307\u5357\u4E0E\u89C4\u5219").action(async () => {
2021
+ program.command("update-skills").description("\u4ECE\u5E73\u53F0\u540C\u6B65 .apm/ \u4E0B\u7684 WebIDE \u6307\u5357\u4E0E\u89C4\u5219").action(async () => {
1968
2022
  await runUpdateSkills();
1969
2023
  });
1970
2024
  program.command("sync-project-documents").description(
@@ -1973,7 +2027,7 @@ function buildProgram() {
1973
2027
  await runSyncProjectDocuments(opts);
1974
2028
  });
1975
2029
  program.command("connect").description(
1976
- "\u8FDE\u63A5\u5E73\u53F0 WebSocket\uFF08/ws/agent\uFF09\uFF0C\u7EF4\u6301\u5FC3\u8DF3\u5E76\u5904\u7406\u4E0B\u884C webide-message"
2030
+ "\u8FDE\u63A5\u524D\u68C0\u6D4B\u5E76\u66F4\u65B0\u5230\u5F53\u524D\u5927\u7248\u672C\u6700\u65B0 CLI\uFF0C\u518D\u8FDE\u63A5\u5E73\u53F0 WebSocket\uFF08/ws/agent\uFF09"
1977
2031
  ).option("--server <url>", "API \u6839\u5730\u5740\uFF0C\u8986\u76D6 config \u4E2D\u7684 baseUrl").action(async (opts) => {
1978
2032
  await runConnect({ server: opts.server });
1979
2033
  });