























@@ -13,7 +13,7 @@ import { walkDirectorySync } from "../../infra/fs-safe.js";
1313import { resolveOsHomeDir } from "../../infra/home-dir.js";
1414import { isPathInside } from "../../infra/path-guards.js";
1515import { createSubsystemLogger } from "../../logging/subsystem.js";
16-import { CONFIG_DIR, resolveHomeDir, resolveUserPath } from "../../utils.js";
16+import { CONFIG_DIR, resolveConfigDir, resolveUserPath } from "../../utils.js";
1717import {
1818resolveEffectiveAgentSkillFilter,
1919resolveEffectiveAgentSkillsLimits,
@@ -27,6 +27,7 @@ import type {
2727SkillEntry,
2828SkillSnapshot,
2929} from "../types.js";
30+import { WORKSPACE_SKILLS_PROMPT_FORMAT_VERSION } from "../types.js";
3031import { resolveBundledSkillsDir } from "./bundled-dir.js";
3132import { resolveBundledAllowlist, shouldIncludeSkill } from "./config.js";
3233import { resolveOpenClawMetadata, resolveSkillInvocationPolicy } from "./frontmatter.js";
@@ -42,12 +43,11 @@ const SKILL_SOURCE_ORIGIN_RELATIVE_PATH = path.join(".openclaw", "source-origin.
4243const MAX_SKILL_SOURCE_ORIGIN_BYTES = 16 * 1024;
43444445/**
45- * Replace the user's home directory prefix with `~` in skill file paths
46- * to reduce system prompt token usage. Models understand `~` expansion,
47- * and the read tool resolves `~` to the home directory.
46+ * Replace OS home directory prefixes with `~` in skill file paths to
47+ * reduce system prompt token usage while matching host file-tool expansion.
4848 *
4949 * Example: `/Users/alice/.bun/.../skills/github/SKILL.md`
50- * → `~/.bun/.../skills/github/SKILL.md`
50+ * → `~/.bun/.../skills/github/SKILL.md`
5151 *
5252 * Saves ~5–6 tokens per skill path × N skills ≈ 400–600 tokens total.
5353 */
@@ -64,7 +64,7 @@ function resolveNativeUserHomeDir(): string | undefined {
6464}
65656666function resolveCompactHomePrefixes(): string[] {
67-const homes = [resolveHomeDir(), resolveUserHomeDir(), resolveNativeUserHomeDir()].filter(
67+const homes = [resolveUserHomeDir(), resolveNativeUserHomeDir()].filter(
6868(home): home is string => Boolean(home),
6969);
7070const resolvedHomes = homes.map((home) => path.resolve(home));
@@ -79,12 +79,66 @@ function compactSkillPaths(skills: Skill[]): Skill[] {
7979if (homes.length === 0) {
8080return skills;
8181}
82+const preservedRoots = resolvePreservedPromptSkillPathRoots();
83+const tildeRoots = resolvePromptTildeRoots();
8284return skills.map((s) => ({
8385 ...s,
84-filePath: compactHomePath(s.filePath, homes),
86+filePath: shouldPreservePromptSkillPath(s.filePath, preservedRoots, tildeRoots)
87+ ? s.filePath
88+ : compactHomePath(s.filePath, homes),
8589}));
8690}
879192+function resolvePreservedPromptSkillPathRoots(): string[] {
93+const configDir = resolveConfigDir();
94+const promptSkillDirs = [
95+path.resolve(configDir, "skills"),
96+path.resolve(configDir, "plugin-skills"),
97+];
98+const realPromptSkillDirs = promptSkillDirs
99+.map((dir) => tryRealpath(dir))
100+.filter((dir): dir is string => Boolean(dir));
101+return uniqueStrings([...promptSkillDirs, ...realPromptSkillDirs]);
102+}
103+104+function resolvePromptTildeRoots(): string[] {
105+const nativeHome = resolveNativeUserHomeDir();
106+if (!nativeHome) {
107+return [];
108+}
109+const resolvedNativeHome = path.resolve(nativeHome);
110+if (isContainerStateHomeWherePromptTildeEscapes(resolvedNativeHome)) {
111+return [];
112+}
113+const realNativeHome = tryRealpath(resolvedNativeHome);
114+return uniqueStrings([
115+resolvedNativeHome,
116+ ...(realNativeHome ? [realNativeHome] : []),
117+]);
118+}
119+120+function isContainerStateHomeWherePromptTildeEscapes(home: string): boolean {
121+const configDir = path.resolve(resolveConfigDir());
122+return home === "/data" && (configDir === "/data/.openclaw" || isPathInside("/data/.openclaw", configDir));
123+}
124+125+function shouldPreservePromptSkillPath(
126+filePath: string,
127+roots: readonly string[],
128+tildeRoots: readonly string[],
129+): boolean {
130+const resolvedFilePath = path.resolve(filePath);
131+const isManagedPromptSkillPath = roots.some(
132+(root) => resolvedFilePath === root || isPathInside(root, resolvedFilePath),
133+);
134+if (!isManagedPromptSkillPath) {
135+return false;
136+}
137+return !tildeRoots.some(
138+(root) => resolvedFilePath === root || isPathInside(root, resolvedFilePath),
139+);
140+}
141+88142function compactHomePath(filePath: string, homes: readonly string[]): string {
89143for (const home of homes) {
90144for (const prefix of compactHomePrefixesForHome(home)) {
@@ -1368,6 +1422,7 @@ export function buildWorkspaceSkillSnapshot(
13681422 ...(skillFilter === undefined ? {} : { skillFilter }),
13691423 resolvedSkills,
13701424version: opts?.snapshotVersion,
1425+promptFormatVersion: WORKSPACE_SKILLS_PROMPT_FORMAT_VERSION,
13711426};
13721427}
13731428此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。