@thinkingai/ae-cli 6.1.25 → 6.1.27

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 (121) hide show
  1. package/README.md +28 -9
  2. package/README.zh.md +25 -9
  3. package/bin/ae-cli.js +6 -1
  4. package/cli-installation-guide.md +91 -0
  5. package/dist/{auth-LHM7NQPR.js → auth-WA7YGGRT.js} +6 -8
  6. package/dist/automation-VAAQBM7Y.js +459 -0
  7. package/dist/{capability-SJI5KOCK.js → capability-GAAYJ3IQ.js} +6 -6
  8. package/dist/{chunk-KJDOTPYU.js → chunk-3YVEAZUN.js} +59 -26
  9. package/dist/{chunk-KS4DE3MX.js → chunk-5XJYLEVC.js} +4 -4
  10. package/dist/{chunk-BW7JUDEI.js → chunk-6VNIR4AP.js} +514 -213
  11. package/dist/{chunk-5XRQ3CZR.js → chunk-7KU2WNQE.js} +43 -8
  12. package/dist/{chunk-7WZACOAI.js → chunk-AP7YP2NX.js} +2 -2
  13. package/dist/{chunk-XKIW3QMF.js → chunk-AQJPNWZF.js} +3 -3
  14. package/dist/chunk-CI24BDR2.js +43 -0
  15. package/dist/{chunk-SO5LKIWP.js → chunk-CT7QDJS6.js} +2 -2
  16. package/dist/{chunk-DU72X2IO.js → chunk-GHXZK54S.js} +22 -10
  17. package/dist/{chunk-2FJM4HRV.js → chunk-MWIA43UJ.js} +3 -3
  18. package/dist/{chunk-N4BLPJC7.js → chunk-OP44TT4H.js} +1 -1
  19. package/dist/chunk-Q5US7JC5.js +133 -0
  20. package/dist/chunk-RK6GLDGI.js +27 -0
  21. package/dist/chunk-RPBOAQQQ.js +56 -0
  22. package/dist/{chunk-GK7WDY7E.js → chunk-S6M42KND.js} +1 -1
  23. package/dist/{chunk-QYXDJOLA.js → chunk-VZZNLV3I.js} +19 -11
  24. package/dist/{chunk-YVGXGTD6.js → chunk-WNGFSIOB.js} +7 -1
  25. package/dist/{chunk-IG3DYEIR.js → chunk-ZL7SGNIA.js} +5 -3
  26. package/dist/{community-report-client-6VJDDMJJ.js → community-report-client-PEERQCX2.js} +1 -1
  27. package/dist/{config-XQWOPDHN.js → config-JOD5DOPE.js} +4 -4
  28. package/dist/context-NGVFPCBK.js +136 -0
  29. package/dist/{data-integration-XBTCGXQT.js → data-integration-BCLQFTGU.js} +6 -6
  30. package/dist/index.js +123 -46
  31. package/dist/{local-data-upload-client-3P7NIVQ4.js → local-data-upload-client-TWMHFLUA.js} +1 -1
  32. package/dist/memory-CQKH2JAV.js +580 -0
  33. package/dist/{metadata-JRYKNUS5.js → metadata-NR3PHR23.js} +7 -7
  34. package/dist/{model-ZS7HVGKN.js → model-VX5M2CJ6.js} +9 -5
  35. package/dist/{personal-semantic-preference-IDE2GE5T.js → personal-semantic-preference-2RAI36P3.js} +117 -11
  36. package/dist/{project-semantic-36KOCYN7.js → project-semantic-3BUGP3ZM.js} +6 -6
  37. package/dist/{sync-EQLSTUS7.js → sync-RZIAV37C.js} +12 -8
  38. package/dist/{te-agent-Z3HCTKR7.js → te-agent-AORRE2FM.js} +90 -454
  39. package/dist/{te-analysis-5ZZ6YHVH.js → te-analysis-GP7DSQ7Q.js} +297 -20
  40. package/dist/{te-community-Z7JACGF3.js → te-community-42M7ICDO.js} +3 -3
  41. package/dist/{te-dataops-F7GQOIS6.js → te-dataops-BNKNZ4F2.js} +5 -5
  42. package/dist/{te-engage-RNRMYDO3.js → te-engage-2CO6LRCX.js} +6 -6
  43. package/dist/{te-experiment-BTAC2UNK.js → te-experiment-652CLXMU.js} +6 -6
  44. package/dist/{te-kb-QJLJEX5K.js → te-kb-ULATOTQ3.js} +494 -214
  45. package/dist/{te-system-IXC43QHZ.js → te-system-UGHVKBVV.js} +10 -9
  46. package/dist/{te-team-BIZIKIHZ.js → te-team-YA6SNOJ3.js} +6 -6
  47. package/dist/things-DTCOQIQB.js +1091 -0
  48. package/dist/{update-W2SGQNKT.js → update-ESNYKUVR.js} +59 -51
  49. package/dist/workspace-JEOHKFPX.js +554 -0
  50. package/package.json +21 -9
  51. package/skills/ae-agent/SKILL.md +91 -139
  52. package/skills/ae-agent/references/add-model.md +1 -1
  53. package/skills/ae-agent/references/create-agent.md +1 -1
  54. package/skills/ae-agent/references/find-archived-conversations.md +12 -5
  55. package/skills/ae-agent/references/get-agent.md +1 -1
  56. package/skills/ae-agent/references/list-agents.md +2 -2
  57. package/skills/ae-agent/references/list-models.md +2 -2
  58. package/skills/ae-agent/references/restore-conversation.md +3 -1
  59. package/skills/ae-analysis/SKILL.md +15 -6
  60. package/skills/ae-analysis/references/ai_models/funnel.md +5 -3
  61. package/skills/ae-analysis/references/ai_models.md +27 -0
  62. package/skills/ae-analysis/references/command_index.md +12 -4
  63. package/skills/ae-analysis/references/dashboard_move.md +21 -0
  64. package/skills/ae-analysis/references/dashboard_report_add.md +2 -0
  65. package/skills/ae-analysis/references/dashboard_report_get.md +19 -0
  66. package/skills/ae-analysis/references/dashboard_report_update.md +27 -0
  67. package/skills/ae-analysis/references/dashboard_update.md +1 -1
  68. package/skills/ae-analysis/references/event_report_presentation.md +76 -0
  69. package/skills/ae-analysis/references/feedback_submit.md +37 -0
  70. package/skills/ae-analysis/references/metadata_resolution.md +15 -0
  71. package/skills/ae-analysis/references/personal_semantic_preference_review.md +9 -0
  72. package/skills/ae-analysis/references/personal_semantic_preference_review_list.md +7 -0
  73. package/skills/ae-analysis/references/personal_semantic_preference_submit.md +7 -0
  74. package/skills/ae-analysis/references/project_semantic_knowledge_wiki.md +68 -27
  75. package/skills/ae-analysis/references/project_semantic_knowledge_wiki_plan_schema.md +40 -1
  76. package/skills/ae-analysis/references/report_create.md +13 -3
  77. package/skills/ae-analysis/references/report_get.md +5 -1
  78. package/skills/ae-analysis/references/report_update.md +15 -4
  79. package/skills/ae-analysis/references/system_query_monitor_overview.md +2 -0
  80. package/skills/ae-analysis/references/system_query_task_cancel.md +2 -0
  81. package/skills/ae-analysis/references/system_query_task_export.md +6 -2
  82. package/skills/ae-analysis/references/system_query_task_get.md +2 -0
  83. package/skills/ae-analysis/references/system_query_task_list.md +7 -3
  84. package/skills/ae-analysis/references/system_query_task_options.md +2 -2
  85. package/skills/ae-analysis/references/system_query_task_realtime_list.md +15 -0
  86. package/skills/ae-analysis/references/user_tag_models.md +2 -0
  87. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/build-empty-skeleton.mjs +105 -0
  88. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/build-project-semantic-wiki.mjs +337 -22
  89. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/company-kb.mjs +21 -8
  90. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/default-compile-rules.md +24 -11
  91. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/default-compile-schema.md +66 -0
  92. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/generate-build-ir.mjs +1 -0
  93. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/package-wiki-source-zip.mjs +109 -40
  94. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/plan-kb-source-sync.mjs +1 -1
  95. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/prepare-compile-schema.mjs +64 -0
  96. package/skills/ae-analysis/scripts/project-semantic-knowledge-wiki/refresh-state-parts.mjs +91 -0
  97. package/skills/ae-automation/SKILL.md +16 -0
  98. package/skills/ae-automation/references/cli.md +53 -0
  99. package/skills/ae-automation/references/command_index.md +17 -0
  100. package/skills/ae-capability/SKILL.md +15 -6
  101. package/skills/ae-current-context/SKILL.md +3 -2
  102. package/skills/ae-kb/SKILL.md +62 -12
  103. package/skills/ae-kb/references/analysis-workflow.md +73 -36
  104. package/skills/ae-kb/references/query-workflow.md +67 -33
  105. package/skills/ae-kb/references/schema-import.md +5 -3
  106. package/skills/ae-kb/references/source-mutations.md +6 -4
  107. package/skills/ae-kb/references/text-sources.md +53 -0
  108. package/skills/ae-kb-discovery/SKILL.md +1 -1
  109. package/skills/ae-team/SKILL.md +23 -17
  110. package/skills/ae-things/SKILL.md +85 -0
  111. package/skills/ae-things/references/cli.md +103 -0
  112. package/skills/ae-things-progress/SKILL.md +43 -0
  113. package/skills/ae-things-review/SKILL.md +42 -0
  114. package/skills/ae-workspace/SKILL.md +43 -0
  115. package/skills/ae-workspace/references/commands.md +51 -0
  116. package/dist/chunk-E2JKXMVO.js +0 -29
  117. package/dist/context-M3HDGEIR.js +0 -46
  118. package/dist/memory-6APEB2JB.js +0 -892
  119. package/skills/ae-agent/references/create-automation.md +0 -125
  120. package/skills/ae-agent/references/list-automations.md +0 -56
  121. package/skills/ae-agent/references/update-automation.md +0 -107
@@ -1,21 +1,105 @@
1
1
  import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);
2
2
  import {
3
3
  peekCliToken
4
- } from "./chunk-DU72X2IO.js";
4
+ } from "./chunk-GHXZK54S.js";
5
5
  import {
6
6
  getActiveHost,
7
7
  getConfigDir,
8
8
  isAeSandboxRuntime,
9
9
  normalizeUrl
10
- } from "./chunk-IG3DYEIR.js";
10
+ } from "./chunk-ZL7SGNIA.js";
11
11
  import {
12
12
  logger
13
- } from "./chunk-YVGXGTD6.js";
13
+ } from "./chunk-WNGFSIOB.js";
14
14
 
15
- // src/core/version-sync.ts
16
- import { spawnSync } from "child_process";
15
+ // src/core/install-process.ts
16
+ import spawn from "cross-spawn";
17
17
  import fs from "fs";
18
18
  import path from "path";
19
+ var INSTALL_TIMEOUT_MS = 12e4;
20
+ var SKILLS_TOOL = "skills@1.5.12";
21
+ var defaultInstallRunner = (command, args, timeoutMs) => {
22
+ const result = spawn.sync(command, args, {
23
+ encoding: "utf8",
24
+ maxBuffer: 8 * 1024 * 1024,
25
+ stdio: ["ignore", "pipe", "pipe"],
26
+ timeout: timeoutMs,
27
+ windowsHide: true,
28
+ env: {
29
+ ...process.env,
30
+ DISABLE_TELEMETRY: "1",
31
+ NO_COLOR: "1",
32
+ // Select installed clients, not only the AI that launched AE CLI.
33
+ ...args[1] === SKILLS_TOOL && args[2] === "add" ? { AI_AGENT: "ae-cli" } : {}
34
+ }
35
+ });
36
+ return { ...result, stdout: result.stdout || "", stderr: result.stderr || "" };
37
+ };
38
+ function resolveNodeTool(tool, execPath = process.execPath, platform = process.platform) {
39
+ const filename = platform === "win32" ? `${tool}.cmd` : tool;
40
+ const sibling = path.join(path.dirname(execPath), filename);
41
+ return fs.existsSync(sibling) ? sibling : filename;
42
+ }
43
+ function commandSucceeded(result) {
44
+ return !result.error && !result.signal && result.status === 0;
45
+ }
46
+ function exactVersion(raw) {
47
+ const version = raw.trim().replace(/^v/i, "");
48
+ if (!/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.test(version)) {
49
+ throw new Error("Expected an exact version such as 6.0.49 or 6.1.9.");
50
+ }
51
+ return version;
52
+ }
53
+ function installHost(raw) {
54
+ if (!raw) return null;
55
+ const normalized = normalizeUrl(raw.trim());
56
+ try {
57
+ if (raw.includes("://") && !/^https?:\/\//.test(raw.trim())) throw new Error();
58
+ const url = new URL(normalized);
59
+ if (!["https:", "http:"].includes(url.protocol) || !url.hostname || url.username || url.password || url.search || url.hash || /[\s'"`$\\<>]/.test(normalized)) throw new Error();
60
+ return normalized;
61
+ } catch {
62
+ throw new Error("Host must be an HTTP(S) URL without credentials, query parameters, or fragments.");
63
+ }
64
+ }
65
+ function classifyInstallFailure(result) {
66
+ const text = `${result.error?.message || ""}
67
+ ${result.stderr}
68
+ ${result.stdout}`;
69
+ if (result.error?.code === "ETIMEDOUT" || /timed?\s*out|ETIMEDOUT/i.test(text)) return "timeout";
70
+ if (/EACCES|EPERM|permission denied|access is denied/i.test(text)) return "permission";
71
+ if (/EAI_AGAIN|ENOTFOUND|ECONNRESET|ECONNREFUSED|network|unable to access|could not resolve|certificate|SSL/i.test(text)) return "network";
72
+ if (/E404|404 Not Found|no matching version|couldn't find remote ref/i.test(text)) return "package_missing";
73
+ return "unknown";
74
+ }
75
+
76
+ // src/core/package-info.ts
77
+ import fs2 from "fs";
78
+ import path2 from "path";
79
+ import { fileURLToPath } from "url";
80
+ function getLocalCliPackageInfo() {
81
+ let dir = path2.dirname(fileURLToPath(import.meta.url));
82
+ for (let i = 0; i < 8; i += 1) {
83
+ const candidate = path2.join(dir, "package.json");
84
+ try {
85
+ if (fs2.existsSync(candidate)) {
86
+ const pkg = JSON.parse(fs2.readFileSync(candidate, "utf8"));
87
+ if (pkg.name && pkg.version && String(pkg.name).includes("ae-cli")) {
88
+ return { name: pkg.name, version: pkg.version, root: dir };
89
+ }
90
+ }
91
+ } catch {
92
+ }
93
+ const parent = path2.dirname(dir);
94
+ if (parent === dir) break;
95
+ dir = parent;
96
+ }
97
+ return { name: "@tant/ae-cli", version: "0.0.0", root: "" };
98
+ }
99
+
100
+ // src/core/version-sync.ts
101
+ import fs5 from "fs";
102
+ import path5 from "path";
19
103
 
20
104
  // src/core/update-check.ts
21
105
  function shouldSkipUpdateCheck(argv = process.argv) {
@@ -84,8 +168,6 @@ var HISTORICAL_CLUSTER_TO_PUBLIC_PIN = {
84
168
 
85
169
  // src/core/version-compat.ts
86
170
  var OPEN_SOURCE_AE_CLI_PACKAGE = "@thinkingai/ae-cli";
87
- var OPEN_SOURCE_NPM_REGISTRY = "https://registry.npmjs.org";
88
- var AE_CLI_SKILLS_REPO = "ThinkingAIAgenticEngine/ae-cli";
89
171
  var PUBLISHED_EXTERNAL_VERSIONS = /* @__PURE__ */ new Set([
90
172
  "1.0.15",
91
173
  "1.0.18",
@@ -183,16 +265,18 @@ function formatPinCommands(expectedVersion) {
183
265
  const ver = resolvePinTarget(expectedVersion);
184
266
  if (!ver) return [];
185
267
  return [
186
- `npm i -g ${OPEN_SOURCE_AE_CLI_PACKAGE}@${ver} --registry=${OPEN_SOURCE_NPM_REGISTRY}`,
187
- `npx skills add ${AE_CLI_SKILLS_REPO}#v${ver} -g -y`
268
+ `npx ${OPEN_SOURCE_AE_CLI_PACKAGE}@latest update --target ${ver}`
188
269
  ];
189
270
  }
190
271
  function formatUpgradeCommands(expectedVersion) {
191
- const ver = expectedVersion.trim().replace(/^v/i, "");
192
- if (!/^\d+\.\d+\.\d+(?:[-+].*)?$/.test(ver)) return [];
272
+ let ver;
273
+ try {
274
+ ver = exactVersion(expectedVersion);
275
+ } catch {
276
+ return [];
277
+ }
193
278
  return [
194
- `npm i -g ${OPEN_SOURCE_AE_CLI_PACKAGE}@${ver} --registry=${OPEN_SOURCE_NPM_REGISTRY}`,
195
- `npx skills add ${AE_CLI_SKILLS_REPO}#v${ver} -g -y`
279
+ `npx ${OPEN_SOURCE_AE_CLI_PACKAGE}@latest update --target ${ver}`
196
280
  ];
197
281
  }
198
282
  function formatUnifiedUpdateCommand() {
@@ -241,22 +325,335 @@ function formatCompatNotice(verdict) {
241
325
  ].join("\n");
242
326
  }
243
327
 
328
+ // src/core/installation.ts
329
+ import fs4 from "fs";
330
+ import path4 from "path";
331
+
332
+ // src/core/skills-install.ts
333
+ import fs3 from "fs";
334
+ import os from "os";
335
+ import path3 from "path";
336
+ import { parseDocument } from "yaml";
337
+ function globalSkillsDirectory() {
338
+ return path3.join(os.homedir(), ".agents", "skills");
339
+ }
340
+ function skillName(content) {
341
+ const frontmatter = content.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
342
+ if (!frontmatter) throw new Error("Missing YAML frontmatter.");
343
+ const parsed = parseDocument(frontmatter[1], { uniqueKeys: true });
344
+ if (parsed.errors.length) throw new Error("Invalid YAML frontmatter.");
345
+ const data = parsed.toJS({ maxAliasCount: 100 });
346
+ if (!data || typeof data.name !== "string" || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(data.name) || typeof data.description !== "string" || !data.description.trim()) throw new Error("Invalid Skill metadata.");
347
+ return data.name;
348
+ }
349
+ function readFiles(root, prefix = "", files = /* @__PURE__ */ new Map()) {
350
+ for (const entry of fs3.readdirSync(path3.join(root, prefix), { withFileTypes: true })) {
351
+ if (entry.name.startsWith(".") || ["metadata.json", "__pycache__", "__pypackages__"].includes(entry.name)) continue;
352
+ const relative = path3.join(prefix, entry.name);
353
+ if (entry.isSymbolicLink()) throw new Error("Bundled Skills must contain regular files.");
354
+ if (entry.isDirectory()) readFiles(root, relative, files);
355
+ else if (entry.isFile()) files.set(relative, fs3.readFileSync(path3.join(root, relative)));
356
+ }
357
+ return files;
358
+ }
359
+ function readBundledSkills(directory) {
360
+ const skills = fs3.readdirSync(directory, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => {
361
+ try {
362
+ const files = readFiles(path3.join(directory, entry.name));
363
+ return { name: skillName(files.get("SKILL.md").toString("utf8")), files };
364
+ } catch {
365
+ const name = /^[a-z0-9-]+$/.test(entry.name) ? entry.name : "(invalid name)";
366
+ throw new Error(`Invalid bundled Skill ${name}: missing files or YAML/frontmatter error.`);
367
+ }
368
+ });
369
+ if (!skills.length || new Set(skills.map((skill) => skill.name)).size !== skills.length) {
370
+ throw new Error("Bundled Skills are empty or contain duplicate names.");
371
+ }
372
+ return skills;
373
+ }
374
+ function verifyGlobalSkills(skills, directory) {
375
+ return skills.flatMap((skill) => {
376
+ let reason = "";
377
+ try {
378
+ const target = path3.join(directory, skill.name);
379
+ if (skillName(fs3.readFileSync(path3.join(target, "SKILL.md"), "utf8")) !== skill.name) {
380
+ reason = "Installed Skill name does not match the bundled Skill.";
381
+ } else {
382
+ for (const [file, expected] of skill.files) {
383
+ if (!fs3.readFileSync(path3.join(target, file)).equals(expected)) {
384
+ reason = "Installed files do not match the CLI package version.";
385
+ break;
386
+ }
387
+ }
388
+ }
389
+ } catch (error) {
390
+ reason = error.code === "ENOENT" ? "Expected Skill file is missing." : "Installed Skill has invalid YAML/frontmatter or unreadable files.";
391
+ }
392
+ return reason ? [{ skill: skill.name, reason }] : [];
393
+ });
394
+ }
395
+ function skillProcessProblem(result) {
396
+ const output = `${result.stdout}
397
+ ${result.stderr}`.replace(/\x1b\[[0-9;]*m/g, "");
398
+ if (/(?:YAML|frontmatter)[^\n]*(?:parse|invalid|error)|(?:parse|invalid|error)[^\n]*(?:YAML|frontmatter)/i.test(output)) {
399
+ return "Skills reported a YAML/frontmatter parsing error.";
400
+ }
401
+ const lines = output.split(/\r?\n/);
402
+ const ignored = lines.filter((line) => /(?:PromptScript|Eve) does not support global skill installation\s*$/.test(line));
403
+ const failures = [...output.matchAll(/Failed to install (\d+)/g)].reduce((sum, match) => sum + Number(match[1]), 0);
404
+ const remaining = lines.filter((line) => !ignored.includes(line) && !/Failed to install \d+/.test(line)).join("\n");
405
+ if (failures > ignored.length || /✗|(?:^|\n)[^\w\n]*(?:Error\b|Installation failed\b|Failed\b)/i.test(remaining)) {
406
+ return "Skills installation failed for a supported client. Check directory permissions and retry.";
407
+ }
408
+ if (commandSucceeded(result)) return null;
409
+ if (!result.error && !result.signal && ignored.length > 0 && failures === ignored.length && !/EACCES|EPERM|ETIMEDOUT|ENOTFOUND|ECONN|network|permission denied/i.test(remaining)) return null;
410
+ return "Skills process failed. Check network access and directory permissions, then retry.";
411
+ }
412
+
413
+ // src/core/installation.ts
414
+ function distribution(packageName) {
415
+ if (packageName === "@thinkingai/ae-cli") return { registry: "https://registry.npmjs.org", repository: "ThinkingAIAgenticEngine/ae-cli" };
416
+ if (packageName === "@tant/ae-cli") return { registry: "https://npm.thinkingdata.cn:3443", repository: "http://10.27.249.150:8888/te-ai/te-cli.git" };
417
+ throw new Error("Unsupported CLI package. Use an official AE CLI distribution.");
418
+ }
419
+ function newInstallationReport(packageName, version, dryRun = false) {
420
+ return {
421
+ ok: false,
422
+ dry_run: dryRun,
423
+ cli: { package: packageName, version, path: null, verified: false, action: "pending", warnings: [] },
424
+ skills: {
425
+ directory: globalSkillsDirectory(),
426
+ expected_count: 0,
427
+ installed_count: 0,
428
+ missing: [],
429
+ source: null,
430
+ version,
431
+ status: "pending",
432
+ failures: [],
433
+ attempts: []
434
+ },
435
+ auth: { host: null, authenticated: null, warnings: [] },
436
+ next_steps: []
437
+ };
438
+ }
439
+ function failInstallation(report, stage, message, cause = "validation") {
440
+ report.ok = false;
441
+ report.error = { type: "config", code: "AE_CLI_INSTALL_FAILED", message, stage, cause };
442
+ if (!report.next_steps.length) report.next_steps.push("Fix the reported problem and rerun the same install or update command.");
443
+ return report;
444
+ }
445
+ function installPlan(packageName, version, root = "<npm-root>") {
446
+ const config = distribution(packageName);
447
+ const target = exactVersion(version);
448
+ const source = path4.join(root, packageName, "skills");
449
+ return [
450
+ `npm install -g ${packageName}@${target} --registry=${config.registry}`,
451
+ `npx -y ${SKILLS_TOOL} add ${JSON.stringify(source)} -g -y --agent universal`,
452
+ `npx -y ${SKILLS_TOOL} add ${config.repository}#v${target} -g -y --agent universal`,
453
+ `npx -y ${SKILLS_TOOL} add ${JSON.stringify(globalSkillsDirectory())} -g -y --copy`
454
+ ];
455
+ }
456
+ function verifySkillsAttempt(report, skills, result, stage, source) {
457
+ const problem = skillProcessProblem(result);
458
+ report.skills.failures = verifyGlobalSkills(skills, report.skills.directory);
459
+ if (problem) {
460
+ for (const skill of skills) {
461
+ if (!report.skills.failures.some((failure) => failure.skill === skill.name)) {
462
+ report.skills.failures.push({ skill: skill.name, reason: problem });
463
+ }
464
+ }
465
+ }
466
+ report.skills.missing = report.skills.failures.map((failure) => failure.skill);
467
+ report.skills.installed_count = skills.length - report.skills.missing.length;
468
+ const cause = commandSucceeded(result) ? "validation" : classifyInstallFailure(result);
469
+ report.skills.attempts.push({
470
+ source,
471
+ stage,
472
+ cause: report.skills.missing.length ? cause : null,
473
+ failures: [...report.skills.failures]
474
+ });
475
+ report.skills.status = !report.skills.missing.length ? "success" : report.skills.installed_count ? "partial" : "failed";
476
+ return report.skills.status === "success";
477
+ }
478
+ function readPackage(root) {
479
+ try {
480
+ return JSON.parse(fs4.readFileSync(path4.join(root, "package.json"), "utf8"));
481
+ } catch {
482
+ return null;
483
+ }
484
+ }
485
+ function linked(root) {
486
+ try {
487
+ return fs4.lstatSync(root).isSymbolicLink();
488
+ } catch {
489
+ return false;
490
+ }
491
+ }
492
+ function cliBinBelongsToPackage(bin, packageRoot, platform) {
493
+ const entry = fs4.realpathSync(path4.join(packageRoot, "bin", "ae-cli.js"));
494
+ if (platform !== "win32") return fs4.realpathSync(bin) === entry;
495
+ const shim = fs4.readFileSync(bin, "utf8");
496
+ const targets = [...shim.matchAll(/"%(?:dp0%|~dp0)[\\/]([^"\r\n]+)"/gi)];
497
+ return targets.some(([, relative]) => {
498
+ try {
499
+ const target = path4.resolve(path4.dirname(bin), relative.replace(/\\/g, path4.sep));
500
+ return fs4.realpathSync(target).toLowerCase() === entry.toLowerCase();
501
+ } catch {
502
+ return false;
503
+ }
504
+ });
505
+ }
506
+ function runInstallation(options = {}) {
507
+ const local = getLocalCliPackageInfo();
508
+ const packageName = options.packageName ?? local.name;
509
+ const report = newInstallationReport(packageName, "", Boolean(options.dryRun));
510
+ const runner = options.runner ?? defaultInstallRunner;
511
+ const progress = options.progress ?? (() => {
512
+ });
513
+ const platform = options.platform ?? process.platform;
514
+ report.skills.directory = options.skillsDirectory ?? globalSkillsDirectory();
515
+ let stage = "environment";
516
+ try {
517
+ const target = exactVersion(options.target ?? local.version);
518
+ report.cli.version = target;
519
+ report.skills.version = target;
520
+ const config = distribution(packageName);
521
+ report.auth.host = installHost(options.host);
522
+ const major = Number((options.nodeVersion ?? process.versions.node).split(".")[0]);
523
+ if (!Number.isInteger(major) || major < 20) return failInstallation(report, stage, "AE CLI requires Node.js 20 or later. Select an existing supported Node runtime and rerun.");
524
+ if (options.dryRun) {
525
+ report.ok = true;
526
+ report.commands = installPlan(packageName, target);
527
+ report.next_steps.push("Dry run only; CLI, global Skills, and authentication have not been verified.");
528
+ return report;
529
+ }
530
+ const npm = resolveNodeTool("npm", process.execPath, platform);
531
+ progress("[ae-cli] [1/4] Checking Node.js, npm, and npx...");
532
+ for (const tool of [npm, resolveNodeTool("npx", process.execPath, platform)]) {
533
+ const result = runner(tool, ["--version"], 15e3);
534
+ if (!commandSucceeded(result)) return failInstallation(report, stage, "npm and npx must be available alongside the selected Node.js runtime.", classifyInstallFailure(result));
535
+ }
536
+ stage = "package_validation";
537
+ const rootResult = runner(npm, ["root", "-g"], 15e3);
538
+ const prefixResult = runner(npm, ["prefix", "-g"], 15e3);
539
+ const globalRoot = rootResult.stdout.trim();
540
+ const prefix = prefixResult.stdout.trim();
541
+ if (!commandSucceeded(rootResult) || !commandSucceeded(prefixResult) || !path4.isAbsolute(globalRoot) || !path4.isAbsolute(prefix) || /[\r\n]/.test(globalRoot + prefix)) return failInstallation(report, stage, "Cannot resolve npm global root and prefix. Check npm configuration.");
542
+ const packageRoot = path4.join(globalRoot, packageName);
543
+ const bin = platform === "win32" ? path4.join(prefix, "ae-cli.cmd") : path4.join(prefix, "bin", "ae-cli");
544
+ report.cli.path = bin;
545
+ const env = options.env ?? process.env;
546
+ const installedBefore = readPackage(packageRoot);
547
+ if (linked(packageRoot) && !options.skipCliInstall) return failInstallation(report, stage, "The global CLI is npm-linked to source. Preserve the link; use a separate npm prefix for a packaged installation.");
548
+ if (options.skipCliInstall || installedBefore?.name === packageName && installedBefore.version === target && fs4.existsSync(bin)) {
549
+ report.cli.action = "skipped";
550
+ progress("[ae-cli] [2/4] Verifying the existing CLI...");
551
+ } else {
552
+ stage = "npm_install";
553
+ progress(`[ae-cli] [2/4] Installing ${packageName}@${target}...`);
554
+ const result = runner(npm, ["install", "-g", `${packageName}@${target}`, "--registry", config.registry], INSTALL_TIMEOUT_MS);
555
+ if (!commandSucceeded(result)) return failInstallation(report, stage, "npm installation failed. Check network access and a user-writable npm prefix; do not use sudo.", classifyInstallFailure(result));
556
+ report.cli.action = "installed";
557
+ }
558
+ stage = "package_validation";
559
+ const installed = readPackage(packageRoot);
560
+ if (installed?.name !== packageName || installed.version !== target) return failInstallation(report, stage, "Installed CLI package identity or version does not match the requested distribution.");
561
+ if (!fs4.existsSync(bin)) return failInstallation(report, stage, "npm did not create the global ae-cli executable. Enable npm bin-links, reinstall, and add the global executable directory to PATH.");
562
+ if (!cliBinBelongsToPackage(bin, packageRoot, platform)) return failInstallation(report, stage, "The global executable does not belong to the installed CLI package.");
563
+ const versionResult = runner(bin, ["--version"], 15e3);
564
+ if (!commandSucceeded(versionResult) || versionResult.stdout.trim() !== target) return failInstallation(report, stage, "ae-cli --version did not return the requested version. Refresh the shell command cache and check npm bin links.");
565
+ report.cli.verified = true;
566
+ const pathKey = platform === "win32" ? Object.keys(env).find((key) => key.toLowerCase() === "path") : "PATH";
567
+ const installedPathBin = (env[pathKey ?? "PATH"] ?? "").split(platform === "win32" ? ";" : path4.delimiter).filter(Boolean).map((dir) => path4.join(dir.replace(/^"(.*)"$/, "$1"), platform === "win32" ? "ae-cli.cmd" : "ae-cli")).find((file) => fs4.existsSync(file) && !file.includes(`${path4.sep}_npx${path4.sep}`));
568
+ if (!installedPathBin || fs4.realpathSync(installedPathBin) !== fs4.realpathSync(bin)) {
569
+ report.cli.warnings.push(`Add ${path4.dirname(bin)} to the front of PATH and refresh the shell command cache. The absolute executable was verified.`);
570
+ report.next_steps.push(`Use the verified executable at ${bin}, or correct PATH before invoking ae-cli.`);
571
+ }
572
+ stage = "skills_local";
573
+ const skillsPath = path4.join(packageRoot, "skills");
574
+ const skills = readBundledSkills(skillsPath);
575
+ report.skills.expected_count = skills.length;
576
+ for (const source of [skillsPath, `${config.repository}#v${target}`]) {
577
+ stage = source === skillsPath ? "skills_local" : "skills_github";
578
+ report.skills.source = source === skillsPath ? "installed-package" : source;
579
+ progress("[ae-cli] [3/4] Installing and verifying global Skills...");
580
+ const result = runner(resolveNodeTool("npx", process.execPath, platform), [
581
+ "-y",
582
+ SKILLS_TOOL,
583
+ "add",
584
+ source,
585
+ "-g",
586
+ "-y",
587
+ "--agent",
588
+ "universal",
589
+ "--skill",
590
+ ...skills.map((skill) => skill.name)
591
+ ], INSTALL_TIMEOUT_MS);
592
+ if (verifySkillsAttempt(report, skills, result, stage, report.skills.source)) break;
593
+ }
594
+ if (report.skills.status !== "success") {
595
+ report.skills.status = report.skills.installed_count ? "partial" : "failed";
596
+ const first = report.skills.attempts[0];
597
+ return failInstallation(
598
+ report,
599
+ first.stage,
600
+ "Global Skills installation is incomplete. See skills.attempts for the original and fallback failures.",
601
+ first.cause ?? "validation"
602
+ );
603
+ }
604
+ stage = "skills_clients";
605
+ progress("[ae-cli] [3/4] Distributing Skills to detected clients...");
606
+ const clientResult = runner(
607
+ resolveNodeTool("npx", process.execPath, platform),
608
+ ["-y", SKILLS_TOOL, "add", report.skills.directory, "-g", "-y", "--copy", "--skill", ...skills.map((skill) => skill.name)],
609
+ INSTALL_TIMEOUT_MS
610
+ );
611
+ if (!verifySkillsAttempt(report, skills, clientResult, stage, report.skills.directory)) {
612
+ return failInstallation(
613
+ report,
614
+ stage,
615
+ "Global Skills were installed, but client distribution is incomplete. See skills.attempts for details.",
616
+ report.skills.attempts.at(-1).cause ?? "validation"
617
+ );
618
+ }
619
+ progress("[ae-cli] [4/4] Checking authentication guidance...");
620
+ if (report.auth.host) {
621
+ const result = runner(bin, ["--no-update-check", "--format", "json", "auth", "status", "--host", report.auth.host], INSTALL_TIMEOUT_MS);
622
+ try {
623
+ const body = JSON.parse(result.stdout);
624
+ if (!commandSucceeded(result) || !body.ok || typeof body.data?.authenticated !== "boolean") throw new Error();
625
+ report.auth.authenticated = body.data.authenticated;
626
+ } catch {
627
+ report.auth.warnings.push("Authentication status could not be verified. Run auth status for this host.");
628
+ }
629
+ }
630
+ if (report.auth.authenticated !== true) {
631
+ report.next_steps.push(`ae-cli auth login --host ${report.auth.host ?? "<AE_HOST>"} --no-wait`);
632
+ report.next_steps.push("Complete browser authorization, then run ae-cli auth login --host <AE_HOST> --device-code <DEVICE_CODE>.");
633
+ }
634
+ report.ok = true;
635
+ return report;
636
+ } catch (error) {
637
+ const safe = error instanceof Error && /^(Expected an exact|Unsupported CLI package|Host must|Invalid bundled Skill|Bundled Skills are empty)/.test(error.message);
638
+ return failInstallation(report, stage, safe ? error.message : "Installation could not complete at this stage. Check file permissions, package contents, and global Skills directory.");
639
+ }
640
+ }
641
+
244
642
  // src/core/version-sync.ts
245
- var COMMAND_TIMEOUT_MS = 12e4;
246
643
  var AUTO_ATTEMPT_INTERVAL_MS = 60 * 60 * 1e3;
247
644
  var MAX_AUTO_ATTEMPTS_PER_DAY = 3;
248
645
  var LOCK_STALE_MS = 10 * 60 * 1e3;
249
646
  var SYNC_STATE_FILE = "version-sync.json";
250
647
  var SYNC_LOCK_FILE = "version-sync.lock";
251
648
  function syncStatePath() {
252
- return path.join(getConfigDir(), SYNC_STATE_FILE);
649
+ return path5.join(getConfigDir(), SYNC_STATE_FILE);
253
650
  }
254
651
  function syncLockPath() {
255
- return path.join(getConfigDir(), SYNC_LOCK_FILE);
652
+ return path5.join(getConfigDir(), SYNC_LOCK_FILE);
256
653
  }
257
654
  function readSyncStore() {
258
655
  try {
259
- const raw = JSON.parse(fs.readFileSync(syncStatePath(), "utf8"));
656
+ const raw = JSON.parse(fs5.readFileSync(syncStatePath(), "utf8"));
260
657
  if (raw && typeof raw === "object" && raw.hosts && typeof raw.hosts === "object") {
261
658
  return raw;
262
659
  }
@@ -266,8 +663,8 @@ function readSyncStore() {
266
663
  }
267
664
  function writeSyncStore(store) {
268
665
  try {
269
- fs.mkdirSync(getConfigDir(), { recursive: true });
270
- fs.writeFileSync(syncStatePath(), JSON.stringify(store, null, 2));
666
+ fs5.mkdirSync(getConfigDir(), { recursive: true });
667
+ fs5.writeFileSync(syncStatePath(), JSON.stringify(store, null, 2));
271
668
  } catch {
272
669
  }
273
670
  }
@@ -343,61 +740,9 @@ function isAutoSyncTargetEligible(version) {
343
740
  function isPublicAeCliPackage(packageName) {
344
741
  return packageName === OPEN_SOURCE_AE_CLI_PACKAGE;
345
742
  }
346
- function resolveNodeTool(tool, execPath = process.execPath, platform = process.platform) {
347
- const fileName = platform === "win32" ? `${tool}.cmd` : tool;
348
- const sibling = path.join(path.dirname(execPath), fileName);
349
- return fs.existsSync(sibling) ? sibling : fileName;
350
- }
351
- function buildVersionInstallPlan(targetRaw, globalRoot = "<npm-root>") {
743
+ function buildVersionInstallPlan(targetRaw, globalRoot = "<npm-root>", packageName = OPEN_SOURCE_AE_CLI_PACKAGE) {
352
744
  const target = normalizeCliVersion(targetRaw);
353
- const localSkills = path.join(globalRoot, "@thinkingai", "ae-cli", "skills");
354
- return {
355
- target,
356
- commands: [
357
- `npm install -g ${OPEN_SOURCE_AE_CLI_PACKAGE}@${target} --registry=${OPEN_SOURCE_NPM_REGISTRY}`,
358
- `npx -y skills add ${localSkills} -g -y`,
359
- `npx -y skills add ${AE_CLI_SKILLS_REPO}#v${target} -g -y`
360
- ],
361
- skillsSources: ["installed-package", "github-fallback"]
362
- };
363
- }
364
- function defaultRunner(command, args, timeoutMs) {
365
- const result = spawnSync(command, args, {
366
- encoding: "utf8",
367
- maxBuffer: 2 * 1024 * 1024,
368
- stdio: ["ignore", "pipe", "pipe"],
369
- timeout: timeoutMs,
370
- windowsHide: true
371
- });
372
- return {
373
- status: result.status,
374
- stdout: result.stdout || "",
375
- stderr: result.stderr || "",
376
- error: result.error,
377
- signal: result.signal
378
- };
379
- }
380
- function resultMessage(result) {
381
- if (result.error?.message) return result.error.message;
382
- const detail = (result.stderr || result.stdout).trim().split(/\r?\n/).filter(Boolean).slice(-3).join(" ");
383
- if (detail) return detail;
384
- if (result.signal) return `Command terminated by ${result.signal}`;
385
- return `Command exited with status ${result.status ?? "unknown"}`;
386
- }
387
- function classifyFailure(result) {
388
- const text = `${result.error?.message || ""}
389
- ${result.stderr}
390
- ${result.stdout}`;
391
- if (result.error && result.error.code === "ETIMEDOUT") return "timeout";
392
- if (/timed?\s*out|ETIMEDOUT/i.test(text)) return "timeout";
393
- if (/EACCES|EPERM|permission denied|access is denied/i.test(text)) return "permission";
394
- if (/EAI_AGAIN|ENOTFOUND|ECONNRESET|ECONNREFUSED|network|unable to access|could not resolve|certificate|SSL/i.test(text)) {
395
- return "network";
396
- }
397
- if (/E404|404 Not Found|no matching version|couldn't find remote ref|not found/i.test(text)) {
398
- return "package_missing";
399
- }
400
- return "unknown";
745
+ return { target, commands: installPlan(packageName, target, globalRoot), skillsSources: ["installed-package", "github-fallback"] };
401
746
  }
402
747
  function friendlyVersionSyncFailure(result) {
403
748
  if (result.cause === "busy") {
@@ -420,24 +765,21 @@ function friendlyVersionSyncFailure(result) {
420
765
  }
421
766
  return `Version synchronization failed at ${result.stage}.`;
422
767
  }
423
- function commandSucceeded(result) {
424
- return !result.error && result.status === 0;
425
- }
426
768
  function acquireInstallLock(now) {
427
- fs.mkdirSync(getConfigDir(), { recursive: true });
769
+ fs5.mkdirSync(getConfigDir(), { recursive: true });
428
770
  const file = syncLockPath();
429
771
  try {
430
- return fs.openSync(file, "wx");
772
+ return fs5.openSync(file, "wx");
431
773
  } catch {
432
774
  try {
433
- const age = now.getTime() - fs.statSync(file).mtimeMs;
775
+ const age = now.getTime() - fs5.statSync(file).mtimeMs;
434
776
  if (age >= LOCK_STALE_MS) {
435
- fs.unlinkSync(file);
436
- return fs.openSync(file, "wx");
777
+ fs5.unlinkSync(file);
778
+ return fs5.openSync(file, "wx");
437
779
  }
438
780
  } catch {
439
781
  try {
440
- return fs.openSync(file, "wx");
782
+ return fs5.openSync(file, "wx");
441
783
  } catch {
442
784
  return null;
443
785
  }
@@ -447,121 +789,53 @@ function acquireInstallLock(now) {
447
789
  }
448
790
  function releaseInstallLock(fd) {
449
791
  try {
450
- fs.closeSync(fd);
792
+ fs5.closeSync(fd);
451
793
  } catch {
452
794
  }
453
795
  try {
454
- fs.unlinkSync(syncLockPath());
796
+ fs5.unlinkSync(syncLockPath());
455
797
  } catch {
456
798
  }
457
799
  }
458
- function validateInstalledPackage(npm, target, runner) {
459
- const rootResult = runner(npm, ["root", "-g"], COMMAND_TIMEOUT_MS);
460
- if (!commandSucceeded(rootResult)) {
461
- return { ok: false, message: resultMessage(rootResult) };
462
- }
463
- const globalRoot = rootResult.stdout.trim();
464
- const packageRoot = path.join(globalRoot, "@thinkingai", "ae-cli");
465
- const packageJson = path.join(packageRoot, "package.json");
466
- const skillsPath = path.join(packageRoot, "skills");
467
- try {
468
- const installed = JSON.parse(fs.readFileSync(packageJson, "utf8"));
469
- if (normalizeCliVersion(installed.version || "") !== target) {
470
- return {
471
- ok: false,
472
- message: `Installed package version ${installed.version || "unknown"} does not match ${target}`
473
- };
474
- }
475
- if (!fs.statSync(skillsPath).isDirectory()) {
476
- return { ok: false, message: `Installed package has no skills directory: ${skillsPath}` };
477
- }
478
- return { ok: true, skillsPath };
479
- } catch (error) {
480
- return { ok: false, message: error?.message || String(error) };
481
- }
482
- }
483
800
  function installVersion(targetRaw, options = {}) {
484
- const target = normalizeCliVersion(targetRaw);
485
- const runner = options.runner ?? defaultRunner;
486
- const progress = options.progress ?? (() => {
487
- });
488
- const npm = resolveNodeTool("npm");
489
- const npx = resolveNodeTool("npx");
490
- const lock = acquireInstallLock(options.now ?? /* @__PURE__ */ new Date());
491
- if (lock === null) {
801
+ const run = () => {
802
+ const report = runInstallation({ ...options, target: targetRaw });
803
+ const cliInstalled = report.cli.verified || report.cli.action === "installed";
804
+ if (report.ok) return { ok: true, cliInstalled, skillsSource: report.skills.source === "installed-package" ? "local" : "github", report };
805
+ return {
806
+ ok: false,
807
+ stage: report.error?.stage ?? "environment",
808
+ cliInstalled,
809
+ skillsPending: cliInstalled,
810
+ cause: report.error?.cause ?? "validation",
811
+ message: report.error?.message ?? "Installation failed.",
812
+ report
813
+ };
814
+ };
815
+ if (options.dryRun) return run();
816
+ let lock;
817
+ try {
818
+ lock = acquireInstallLock(options.now ?? /* @__PURE__ */ new Date());
819
+ } catch {
492
820
  return {
493
821
  ok: false,
494
822
  stage: "lock",
495
823
  cliInstalled: false,
496
824
  skillsPending: Boolean(options.skipCliInstall),
497
- cause: "busy",
498
- message: "Another ae-cli process is already synchronizing the global installation."
825
+ cause: "permission",
826
+ message: "The installation lock directory is not writable."
499
827
  };
500
828
  }
501
- let cliInstalled = Boolean(options.skipCliInstall);
829
+ if (lock === null) return {
830
+ ok: false,
831
+ stage: "lock",
832
+ cliInstalled: false,
833
+ skillsPending: Boolean(options.skipCliInstall),
834
+ cause: "busy",
835
+ message: "Another ae-cli process is already synchronizing the global installation."
836
+ };
502
837
  try {
503
- if (!options.skipCliInstall) {
504
- progress(`[ae-cli] [1/2] Installing ae-cli ${target}...`);
505
- const npmResult = runner(
506
- npm,
507
- [
508
- "install",
509
- "-g",
510
- `${OPEN_SOURCE_AE_CLI_PACKAGE}@${target}`,
511
- "--registry",
512
- OPEN_SOURCE_NPM_REGISTRY
513
- ],
514
- COMMAND_TIMEOUT_MS
515
- );
516
- if (!commandSucceeded(npmResult)) {
517
- return {
518
- ok: false,
519
- stage: "npm_install",
520
- cliInstalled: false,
521
- skillsPending: false,
522
- cause: classifyFailure(npmResult),
523
- message: resultMessage(npmResult)
524
- };
525
- }
526
- cliInstalled = true;
527
- }
528
- const installed = validateInstalledPackage(npm, target, runner);
529
- if (!installed.ok) {
530
- return {
531
- ok: false,
532
- stage: "package_validation",
533
- cliInstalled,
534
- skillsPending: cliInstalled,
535
- cause: "validation",
536
- message: installed.message
537
- };
538
- }
539
- progress("[ae-cli] [2/2] Synchronizing Skills from the installed npm package...");
540
- const localResult = runner(
541
- npx,
542
- ["-y", "skills", "add", installed.skillsPath, "-g", "-y"],
543
- COMMAND_TIMEOUT_MS
544
- );
545
- if (commandSucceeded(localResult)) {
546
- return { ok: true, cliInstalled, skillsSource: "local" };
547
- }
548
- progress("[ae-cli] Local Skills sync did not complete; trying the GitHub fallback...");
549
- const githubResult = runner(
550
- npx,
551
- ["-y", "skills", "add", `${AE_CLI_SKILLS_REPO}#v${target}`, "-g", "-y"],
552
- COMMAND_TIMEOUT_MS
553
- );
554
- if (commandSucceeded(githubResult)) {
555
- return { ok: true, cliInstalled, skillsSource: "github" };
556
- }
557
- return {
558
- ok: false,
559
- stage: "skills_github",
560
- cliInstalled,
561
- skillsPending: cliInstalled,
562
- cause: classifyFailure(githubResult),
563
- message: resultMessage(githubResult) || resultMessage(localResult)
564
- };
838
+ return run();
565
839
  } finally {
566
840
  releaseInstallLock(lock);
567
841
  }
@@ -581,12 +855,12 @@ function autoSyncDirection(current, expected) {
581
855
 
582
856
  // src/core/feature-config.ts
583
857
  import crypto from "crypto";
584
- import fs2 from "fs";
585
- import path2 from "path";
858
+ import fs6 from "fs";
859
+ import path6 from "path";
586
860
  var FEATURE_TTL_MS = 24 * 60 * 60 * 1e3;
587
861
  var UNAVAILABLE_TTL_MS = 5 * 60 * 1e3;
588
862
  function cachePath() {
589
- return path2.join(getConfigDir(), "feature-config.json");
863
+ return path6.join(getConfigDir(), "feature-config.json");
590
864
  }
591
865
  function cacheKey(host, token) {
592
866
  const fingerprint = crypto.createHash("sha256").update(token).digest("hex");
@@ -594,7 +868,7 @@ function cacheKey(host, token) {
594
868
  }
595
869
  function readStore() {
596
870
  try {
597
- const value = JSON.parse(fs2.readFileSync(cachePath(), "utf8"));
871
+ const value = JSON.parse(fs6.readFileSync(cachePath(), "utf8"));
598
872
  return value?.entries && typeof value.entries === "object" ? value : { entries: {} };
599
873
  } catch {
600
874
  return { entries: {} };
@@ -604,13 +878,13 @@ function writeStore(store) {
604
878
  const target = cachePath();
605
879
  const temp = `${target}.${process.pid}.${crypto.randomBytes(6).toString("hex")}.tmp`;
606
880
  try {
607
- fs2.mkdirSync(path2.dirname(target), { recursive: true });
608
- fs2.writeFileSync(temp, JSON.stringify(store), { encoding: "utf8", mode: 384 });
609
- fs2.renameSync(temp, target);
610
- fs2.chmodSync(target, 384);
881
+ fs6.mkdirSync(path6.dirname(target), { recursive: true });
882
+ fs6.writeFileSync(temp, JSON.stringify(store), { encoding: "utf8", mode: 384 });
883
+ fs6.renameSync(temp, target);
884
+ fs6.chmodSync(target, 384);
611
885
  } catch {
612
886
  try {
613
- fs2.rmSync(temp, { force: true });
887
+ fs6.rmSync(temp, { force: true });
614
888
  } catch {
615
889
  }
616
890
  }
@@ -640,19 +914,20 @@ async function resolveKbAutoDiscoveryFeature(host, token, fetchRemote) {
640
914
  }
641
915
 
642
916
  // src/core/compat-check.ts
643
- import fs3 from "fs";
644
- import path3 from "path";
645
- var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
917
+ import fs7 from "fs";
918
+ import path7 from "path";
919
+ var CHECK_INTERVAL_MS = 60 * 60 * 1e3;
920
+ var NOTIFY_INTERVAL_MS = 24 * 60 * 60 * 1e3;
646
921
  var FETCH_TIMEOUT_MS = 3e3;
647
922
  var CLI_CONFIG_PATH = "/v1/ta/cli/config";
648
923
  function cacheFilePath() {
649
- return path3.join(getConfigDir(), "compat-check.json");
924
+ return path7.join(getConfigDir(), "compat-check.json");
650
925
  }
651
926
  function readStore2() {
652
927
  try {
653
928
  const file = cacheFilePath();
654
- if (!fs3.existsSync(file)) return { hosts: {} };
655
- const raw = JSON.parse(fs3.readFileSync(file, "utf8"));
929
+ if (!fs7.existsSync(file)) return { hosts: {} };
930
+ const raw = JSON.parse(fs7.readFileSync(file, "utf8"));
656
931
  if (!raw || typeof raw !== "object" || !raw.hosts) return { hosts: {} };
657
932
  return raw;
658
933
  } catch {
@@ -662,23 +937,23 @@ function readStore2() {
662
937
  function writeStore2(store) {
663
938
  try {
664
939
  const dir = getConfigDir();
665
- if (!fs3.existsSync(dir)) fs3.mkdirSync(dir, { recursive: true });
666
- fs3.writeFileSync(cacheFilePath(), JSON.stringify(store, null, 2));
940
+ if (!fs7.existsSync(dir)) fs7.mkdirSync(dir, { recursive: true });
941
+ fs7.writeFileSync(cacheFilePath(), JSON.stringify(store, null, 2));
667
942
  } catch {
668
943
  }
669
944
  }
670
945
  function hostKey(host) {
671
946
  return normalizeUrl(host);
672
947
  }
673
- function isStale(entry) {
948
+ function isCompatCacheStale(entry, now = Date.now()) {
674
949
  if (!entry?.lastFetchedAt) return true;
675
- const age = Date.now() - new Date(entry.lastFetchedAt).getTime();
950
+ const age = now - new Date(entry.lastFetchedAt).getTime();
676
951
  return age >= CHECK_INTERVAL_MS;
677
952
  }
678
- function shouldNotify(entry) {
953
+ function shouldNotifyCompat(entry, now = Date.now()) {
679
954
  if (!entry?.lastNotifiedAt) return true;
680
- const age = Date.now() - new Date(entry.lastNotifiedAt).getTime();
681
- return age >= CHECK_INTERVAL_MS;
955
+ const age = now - new Date(entry.lastNotifiedAt).getTime();
956
+ return age >= NOTIFY_INTERVAL_MS;
682
957
  }
683
958
  function shouldSkipCompatCheck(argv = process.argv) {
684
959
  if (process.env.AE_CLI_NO_COMPAT_CHECK === "1") return true;
@@ -702,7 +977,7 @@ function rootCommand(argv) {
702
977
  }
703
978
  function shouldSkipAutoSync(argv = process.argv) {
704
979
  const command = rootCommand(argv);
705
- return command === "update" || command === "auth" || command === "config";
980
+ return command === "update" || command === "install" || command === "auth" || command === "config";
706
981
  }
707
982
  async function fetchCliConfig(host, cliToken) {
708
983
  const url = new URL(CLI_CONFIG_PATH, host.endsWith("/") ? host : `${host}/`);
@@ -792,7 +1067,7 @@ async function refreshAndMaybeNotify(host, pkg, allowAutoSync) {
792
1067
  let expected = existing?.expectedVersion;
793
1068
  let cluster = existing?.clusterVersion;
794
1069
  let fetchedAt = existing?.lastFetchedAt;
795
- if (isStale(existing)) {
1070
+ if (isCompatCacheStale(existing)) {
796
1071
  const remote = await fetchCliConfig(host, token);
797
1072
  if (remote) rememberKbAutoDiscoveryFeature(host, token, remote.projectSemanticKbAutoDiscovery ?? false);
798
1073
  if (!remote?.aeCliVersion) {
@@ -895,7 +1170,7 @@ async function refreshAndMaybeNotify(host, pkg, allowAutoSync) {
895
1170
  const notice = verdict.kind === "ok" && pendingSkills ? `[ae-cli] CLI ${expected} is installed, but its Skills are not synchronized.
896
1171
  Run: ae-cli update` : formatCompatNotice(verdict);
897
1172
  if (!notice) return { status: "continue" };
898
- const dueForTip = shouldNotify(entry);
1173
+ const dueForTip = shouldNotifyCompat(entry);
899
1174
  if (!dueForTip) {
900
1175
  pendingHostCompatNotice = void 0;
901
1176
  pendingUpdateNotice = void 0;
@@ -1070,30 +1345,51 @@ async function formatOutput(data, format, jqExpr) {
1070
1345
  };
1071
1346
  return JSON.stringify(envelope, null, 2);
1072
1347
  }
1073
- function formatError(type, message, hint, code, meta) {
1348
+ function formatError(type, message, hint, code, meta, details) {
1074
1349
  const envelope = {
1075
1350
  ok: false,
1076
- error: { type, message, hint, code },
1351
+ error: { type, message, hint, code, ...details === void 0 ? {} : { details } },
1077
1352
  ...meta && Object.keys(meta).length > 0 ? { meta } : {}
1078
1353
  };
1079
1354
  return JSON.stringify(envelope, null, 2);
1080
1355
  }
1081
1356
  function printError(type, message, hint, code, meta, options = {}) {
1082
- const formatted = formatError(type, message, hint, code, meta);
1357
+ const formatted = formatError(type, message, hint, code, meta, options.details);
1083
1358
  process.stderr.write(formatted + "\n");
1084
1359
  if (options.log !== false) {
1085
1360
  logger.error(`[${type}] ${message}${hint ? " | " + hint : ""}`);
1086
1361
  }
1087
1362
  }
1088
- async function printOutput(data, format, jqExpr) {
1363
+ function sensitiveRecoveryData(data) {
1364
+ const safe = {};
1365
+ const item = {};
1366
+ if (typeof data?.item?.id === "string") item.id = data.item.id;
1367
+ if (Number.isSafeInteger(data?.item?.contentRevision)) item.contentRevision = data.item.contentRevision;
1368
+ if (Object.keys(item).length > 0) safe.item = item;
1369
+ if (["markdown", "text"].includes(data?.contentFormat)) safe.contentFormat = data.contentFormat;
1370
+ if (typeof data?.out === "string") safe.out = data.out;
1371
+ return safe;
1372
+ }
1373
+ async function printOutput(data, format, jqExpr, options = {}) {
1089
1374
  try {
1090
1375
  process.stdout.write(await formatOutput(data, format, jqExpr) + "\n");
1091
1376
  } catch (error) {
1092
1377
  if (error?.code === "OUTPUT_PROJECTION_FAILED") {
1093
- const recovery = JSON.parse(formatError("validation", error.message, error.hint, error.code, readOutputMetadata(data)));
1378
+ if (options.redactErrors) {
1379
+ error.message = "Invalid --jq expression: output projection failed; source content is omitted from diagnostics.";
1380
+ error.hint = "The read completed. Fix --jq and explicitly run read-text again without --out, or inspect the file at data.out if present. This error result excludes source content.";
1381
+ }
1382
+ const recovery = JSON.parse(formatError(
1383
+ "validation",
1384
+ error.message,
1385
+ error.hint,
1386
+ error.code,
1387
+ options.redactErrors ? void 0 : readOutputMetadata(data)
1388
+ ));
1389
+ const recoveryData = options.redactErrors ? sensitiveRecoveryData(unwrapOutputData(data)) : unwrapOutputData(data);
1094
1390
  await new Promise((resolve, reject) => {
1095
1391
  process.stdout.write(
1096
- JSON.stringify({ ...recovery, data: unwrapOutputData(data) }, null, 2) + "\n",
1392
+ JSON.stringify({ ...recovery, data: recoveryData }, null, 2) + "\n",
1097
1393
  (error2) => error2 ? reject(error2) : resolve()
1098
1394
  );
1099
1395
  });
@@ -1103,6 +1399,11 @@ async function printOutput(data, format, jqExpr) {
1103
1399
  }
1104
1400
 
1105
1401
  export {
1402
+ exactVersion,
1403
+ installHost,
1404
+ getLocalCliPackageInfo,
1405
+ newInstallationReport,
1406
+ failInstallation,
1106
1407
  recordVersionSyncResult,
1107
1408
  buildVersionInstallPlan,
1108
1409
  friendlyVersionSyncFailure,