maestro-agent-sdk 0.1.0
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/LICENSE +21 -0
- package/NOTICE +24 -0
- package/README.md +133 -0
- package/dist/agents/contracts.d.ts +49 -0
- package/dist/agents/contracts.d.ts.map +1 -0
- package/dist/agents/contracts.js +2 -0
- package/dist/agents/contracts.js.map +1 -0
- package/dist/agents/rollout/shared.d.ts +24 -0
- package/dist/agents/rollout/shared.d.ts.map +1 -0
- package/dist/agents/rollout/shared.js +105 -0
- package/dist/agents/rollout/shared.js.map +1 -0
- package/dist/core/agent.d.ts +71 -0
- package/dist/core/agent.d.ts.map +1 -0
- package/dist/core/agent.js +22 -0
- package/dist/core/agent.js.map +1 -0
- package/dist/core/loop.d.ts +26 -0
- package/dist/core/loop.d.ts.map +1 -0
- package/dist/core/loop.js +317 -0
- package/dist/core/loop.js.map +1 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/client.d.ts +79 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +176 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/pool-cache.d.ts +103 -0
- package/dist/mcp/pool-cache.d.ts.map +1 -0
- package/dist/mcp/pool-cache.js +249 -0
- package/dist/mcp/pool-cache.js.map +1 -0
- package/dist/mcp/pool.d.ts +65 -0
- package/dist/mcp/pool.d.ts.map +1 -0
- package/dist/mcp/pool.js +86 -0
- package/dist/mcp/pool.js.map +1 -0
- package/dist/media/file-events.d.ts +8 -0
- package/dist/media/file-events.d.ts.map +1 -0
- package/dist/media/file-events.js +15 -0
- package/dist/media/file-events.js.map +1 -0
- package/dist/memory/active-task-template.d.ts +34 -0
- package/dist/memory/active-task-template.d.ts.map +1 -0
- package/dist/memory/active-task-template.js +63 -0
- package/dist/memory/active-task-template.js.map +1 -0
- package/dist/memory/compressor.d.ts +87 -0
- package/dist/memory/compressor.d.ts.map +1 -0
- package/dist/memory/compressor.js +164 -0
- package/dist/memory/compressor.js.map +1 -0
- package/dist/memory/hash.d.ts +17 -0
- package/dist/memory/hash.d.ts.map +1 -0
- package/dist/memory/hash.js +20 -0
- package/dist/memory/hash.js.map +1 -0
- package/dist/memory/prune.d.ts +117 -0
- package/dist/memory/prune.d.ts.map +1 -0
- package/dist/memory/prune.js +416 -0
- package/dist/memory/prune.js.map +1 -0
- package/dist/memory/reminder.d.ts +57 -0
- package/dist/memory/reminder.d.ts.map +1 -0
- package/dist/memory/reminder.js +57 -0
- package/dist/memory/reminder.js.map +1 -0
- package/dist/memory/scrubber.d.ts +28 -0
- package/dist/memory/scrubber.d.ts.map +1 -0
- package/dist/memory/scrubber.js +147 -0
- package/dist/memory/scrubber.js.map +1 -0
- package/dist/memory/token-estimate.d.ts +10 -0
- package/dist/memory/token-estimate.d.ts.map +1 -0
- package/dist/memory/token-estimate.js +69 -0
- package/dist/memory/token-estimate.js.map +1 -0
- package/dist/platform/config.d.ts +12 -0
- package/dist/platform/config.d.ts.map +1 -0
- package/dist/platform/config.js +54 -0
- package/dist/platform/config.js.map +1 -0
- package/dist/platform/jsonl.d.ts +15 -0
- package/dist/platform/jsonl.d.ts.map +1 -0
- package/dist/platform/jsonl.js +80 -0
- package/dist/platform/jsonl.js.map +1 -0
- package/dist/platform/lifecycle.d.ts +22 -0
- package/dist/platform/lifecycle.d.ts.map +1 -0
- package/dist/platform/lifecycle.js +60 -0
- package/dist/platform/lifecycle.js.map +1 -0
- package/dist/platform/logger.d.ts +26 -0
- package/dist/platform/logger.d.ts.map +1 -0
- package/dist/platform/logger.js +41 -0
- package/dist/platform/logger.js.map +1 -0
- package/dist/platform/mcp-config.d.ts +15 -0
- package/dist/platform/mcp-config.d.ts.map +1 -0
- package/dist/platform/mcp-config.js +8 -0
- package/dist/platform/mcp-config.js.map +1 -0
- package/dist/provider.d.ts +81 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +444 -0
- package/dist/provider.js.map +1 -0
- package/dist/providers/anthropic.d.ts +132 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +518 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/base.d.ts +140 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +2 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/deepseek.d.ts +118 -0
- package/dist/providers/deepseek.d.ts.map +1 -0
- package/dist/providers/deepseek.js +467 -0
- package/dist/providers/deepseek.js.map +1 -0
- package/dist/registry.d.ts +3 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +94 -0
- package/dist/registry.js.map +1 -0
- package/dist/session-store.d.ts +133 -0
- package/dist/session-store.d.ts.map +1 -0
- package/dist/session-store.js +277 -0
- package/dist/session-store.js.map +1 -0
- package/dist/skills/curator.d.ts +104 -0
- package/dist/skills/curator.d.ts.map +1 -0
- package/dist/skills/curator.js +162 -0
- package/dist/skills/curator.js.map +1 -0
- package/dist/skills/index-builder.d.ts +42 -0
- package/dist/skills/index-builder.d.ts.map +1 -0
- package/dist/skills/index-builder.js +94 -0
- package/dist/skills/index-builder.js.map +1 -0
- package/dist/skills/loader.d.ts +107 -0
- package/dist/skills/loader.d.ts.map +1 -0
- package/dist/skills/loader.js +286 -0
- package/dist/skills/loader.js.map +1 -0
- package/dist/skills/preprocess.d.ts +45 -0
- package/dist/skills/preprocess.d.ts.map +1 -0
- package/dist/skills/preprocess.js +126 -0
- package/dist/skills/preprocess.js.map +1 -0
- package/dist/skills/usage.d.ts +75 -0
- package/dist/skills/usage.d.ts.map +1 -0
- package/dist/skills/usage.js +147 -0
- package/dist/skills/usage.js.map +1 -0
- package/dist/state/todos.d.ts +95 -0
- package/dist/state/todos.d.ts.map +1 -0
- package/dist/state/todos.js +198 -0
- package/dist/state/todos.js.map +1 -0
- package/dist/storage/conversations.d.ts +28 -0
- package/dist/storage/conversations.d.ts.map +1 -0
- package/dist/storage/conversations.js +8 -0
- package/dist/storage/conversations.js.map +1 -0
- package/dist/sub-agent/runner.d.ts +78 -0
- package/dist/sub-agent/runner.d.ts.map +1 -0
- package/dist/sub-agent/runner.js +215 -0
- package/dist/sub-agent/runner.js.map +1 -0
- package/dist/tools/builtin/agent.d.ts +33 -0
- package/dist/tools/builtin/agent.d.ts.map +1 -0
- package/dist/tools/builtin/agent.js +76 -0
- package/dist/tools/builtin/agent.js.map +1 -0
- package/dist/tools/builtin/bash.d.ts +11 -0
- package/dist/tools/builtin/bash.d.ts.map +1 -0
- package/dist/tools/builtin/bash.js +91 -0
- package/dist/tools/builtin/bash.js.map +1 -0
- package/dist/tools/builtin/edit.d.ts +21 -0
- package/dist/tools/builtin/edit.d.ts.map +1 -0
- package/dist/tools/builtin/edit.js +238 -0
- package/dist/tools/builtin/edit.js.map +1 -0
- package/dist/tools/builtin/read.d.ts +17 -0
- package/dist/tools/builtin/read.d.ts.map +1 -0
- package/dist/tools/builtin/read.js +139 -0
- package/dist/tools/builtin/read.js.map +1 -0
- package/dist/tools/builtin/sandbox.d.ts +16 -0
- package/dist/tools/builtin/sandbox.d.ts.map +1 -0
- package/dist/tools/builtin/sandbox.js +58 -0
- package/dist/tools/builtin/sandbox.js.map +1 -0
- package/dist/tools/builtin/skill_view.d.ts +37 -0
- package/dist/tools/builtin/skill_view.d.ts.map +1 -0
- package/dist/tools/builtin/skill_view.js +82 -0
- package/dist/tools/builtin/skill_view.js.map +1 -0
- package/dist/tools/builtin/todo_write.d.ts +29 -0
- package/dist/tools/builtin/todo_write.d.ts.map +1 -0
- package/dist/tools/builtin/todo_write.js +96 -0
- package/dist/tools/builtin/todo_write.js.map +1 -0
- package/dist/tools/builtin/web_fetch.d.ts +10 -0
- package/dist/tools/builtin/web_fetch.d.ts.map +1 -0
- package/dist/tools/builtin/web_fetch.js +150 -0
- package/dist/tools/builtin/web_fetch.js.map +1 -0
- package/dist/tools/builtin/write.d.ts +35 -0
- package/dist/tools/builtin/write.d.ts.map +1 -0
- package/dist/tools/builtin/write.js +70 -0
- package/dist/tools/builtin/write.js.map +1 -0
- package/dist/tools/file-state.d.ts +99 -0
- package/dist/tools/file-state.d.ts.map +1 -0
- package/dist/tools/file-state.js +133 -0
- package/dist/tools/file-state.js.map +1 -0
- package/dist/tools/hooks/sandbox-fs.d.ts +25 -0
- package/dist/tools/hooks/sandbox-fs.d.ts.map +1 -0
- package/dist/tools/hooks/sandbox-fs.js +48 -0
- package/dist/tools/hooks/sandbox-fs.js.map +1 -0
- package/dist/tools/registry.d.ts +102 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +93 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/types.d.ts +109 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +20 -0
- package/dist/types.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { basename, join, sep } from "node:path";
|
|
3
|
+
import { logger } from "../platform/logger.js";
|
|
4
|
+
/** Sub-directory names we never descend into. Mirrors upstream
|
|
5
|
+
* `scan_skill_commands` skip set. */
|
|
6
|
+
const SKIP_DIRS = new Set([".git", ".github", ".hub", ".archive", "node_modules"]);
|
|
7
|
+
/** Description length cap for the rendered index. Upstream uses 80, but the
|
|
8
|
+
* Clawgram routing review settled on 60 to keep the system-prompt block
|
|
9
|
+
* tight (~6K tokens hard ceiling once the catalog has 60+ skills). The
|
|
10
|
+
* full description survives in `frontmatter.description` for skill_view. */
|
|
11
|
+
export const SKILL_INDEX_DESCRIPTION_CAP = 60;
|
|
12
|
+
/**
|
|
13
|
+
* Walk `rootDir` recursively and return one `SkillEntry` per SKILL.md found,
|
|
14
|
+
* subject to platform compatibility + the skip-dir filter.
|
|
15
|
+
*
|
|
16
|
+
* Errors per-file (unreadable, malformed) are logged at debug and the file
|
|
17
|
+
* is dropped — one bad SKILL.md never aborts the whole scan, same stance as
|
|
18
|
+
* upstream `scan_skill_commands`.
|
|
19
|
+
*/
|
|
20
|
+
export function loadSkills(rootDir) {
|
|
21
|
+
if (!existsSync(rootDir)) {
|
|
22
|
+
logger.debug({ rootDir }, "maestro skills: rootDir missing — returning empty");
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
const out = [];
|
|
26
|
+
const seenNames = new Set();
|
|
27
|
+
walk(rootDir, rootDir, out, seenNames);
|
|
28
|
+
// Deterministic ordering for cache-friendly system-prompt rendering: by
|
|
29
|
+
// category then by name. Without this, readdir's filesystem-dependent
|
|
30
|
+
// ordering would shuffle entries between runs and break prefix caching.
|
|
31
|
+
out.sort((a, b) => {
|
|
32
|
+
if (a.category !== b.category)
|
|
33
|
+
return a.category.localeCompare(b.category);
|
|
34
|
+
return a.name.localeCompare(b.name);
|
|
35
|
+
});
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
function walk(root, dir, out, seenNames) {
|
|
39
|
+
let entries;
|
|
40
|
+
try {
|
|
41
|
+
entries = readdirSync(dir);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
logger.debug({ err, dir }, "maestro skills: readdir failed, skipping subtree");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
for (const name of entries) {
|
|
48
|
+
if (SKIP_DIRS.has(name))
|
|
49
|
+
continue;
|
|
50
|
+
const path = join(dir, name);
|
|
51
|
+
let stat;
|
|
52
|
+
try {
|
|
53
|
+
stat = statSync(path);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (stat.isDirectory()) {
|
|
59
|
+
walk(root, path, out, seenNames);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (!stat.isFile() || basename(path) !== "SKILL.md")
|
|
63
|
+
continue;
|
|
64
|
+
const entry = parseSkillFile(root, path);
|
|
65
|
+
if (!entry)
|
|
66
|
+
continue;
|
|
67
|
+
// De-duplicate by skill name. Upstream scans local dir first, then
|
|
68
|
+
// external dirs; we follow the same precedence by walking in readdir
|
|
69
|
+
// order from a single root — the first occurrence wins.
|
|
70
|
+
if (seenNames.has(entry.name))
|
|
71
|
+
continue;
|
|
72
|
+
if (!matchesPlatform(entry.frontmatter))
|
|
73
|
+
continue;
|
|
74
|
+
seenNames.add(entry.name);
|
|
75
|
+
out.push(entry);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function parseSkillFile(root, mdPath) {
|
|
79
|
+
let raw;
|
|
80
|
+
try {
|
|
81
|
+
raw = readFileSync(mdPath, "utf8");
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
logger.debug({ err, mdPath }, "maestro skills: read failed, skipping");
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
const { frontmatter, body } = parseFrontmatter(raw);
|
|
88
|
+
const skillDir = mdPath.slice(0, -"/SKILL.md".length);
|
|
89
|
+
const dirName = basename(skillDir);
|
|
90
|
+
const name = (frontmatter.name ?? dirName).trim();
|
|
91
|
+
if (!name)
|
|
92
|
+
return null;
|
|
93
|
+
// Category = directory bucket relative to root. e.g. root/apple/foo/SKILL.md → "apple".
|
|
94
|
+
// Root-level skills get "general" to match upstream behavior.
|
|
95
|
+
const rel = skillDir.slice(root.length).replace(/^[/\\]+/, "");
|
|
96
|
+
const parts = rel.split(sep).filter(Boolean);
|
|
97
|
+
const category = parts.length > 1 ? parts.slice(0, -1).join("/") : "general";
|
|
98
|
+
// Description: prefer frontmatter, fall back to the first non-blank,
|
|
99
|
+
// non-heading line of the body (upstream same fallback). Empty string is
|
|
100
|
+
// valid — index-builder renders it without the `: ...` suffix.
|
|
101
|
+
let description = (frontmatter.description ?? "").trim();
|
|
102
|
+
if (!description) {
|
|
103
|
+
for (const line of body.split("\n")) {
|
|
104
|
+
const t = line.trim();
|
|
105
|
+
if (!t || t.startsWith("#"))
|
|
106
|
+
continue;
|
|
107
|
+
description = t;
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Strip surrounding quotes (YAML scalars are often written as quoted strings).
|
|
112
|
+
description = description.replace(/^["']|["']$/g, "");
|
|
113
|
+
return {
|
|
114
|
+
name,
|
|
115
|
+
description,
|
|
116
|
+
category,
|
|
117
|
+
skillDir,
|
|
118
|
+
mdPath,
|
|
119
|
+
raw,
|
|
120
|
+
frontmatter,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Parse a `---\n...\n---\n` YAML frontmatter block into a flat string map.
|
|
125
|
+
*
|
|
126
|
+
* Supports the SKILL.md surface that matters in v0.13.0:
|
|
127
|
+
* - `key: value` scalars (with optional surrounding quotes)
|
|
128
|
+
* - `key: [a, b, c]` flow-list literals (joined with "," for storage)
|
|
129
|
+
* - Nested blocks (`metadata:\n maestro:\n tags: [...]`) are flattened
|
|
130
|
+
* by ignoring indented lines — we only need top-level keys for the
|
|
131
|
+
* index + platform filter.
|
|
132
|
+
*
|
|
133
|
+
* Lines that don't fit the `key: value` shape are dropped silently. This
|
|
134
|
+
* matches the upstream "fallback" parser; the full YAML parser is omitted
|
|
135
|
+
* because every real-world SKILL.md in v0.13.0 round-trips cleanly through
|
|
136
|
+
* this subset.
|
|
137
|
+
*
|
|
138
|
+
* Returns `body` as the post-frontmatter text. If no frontmatter is
|
|
139
|
+
* present, `frontmatter` is empty and `body` is the original input.
|
|
140
|
+
*/
|
|
141
|
+
export function parseFrontmatter(content) {
|
|
142
|
+
const frontmatter = {};
|
|
143
|
+
if (!content.startsWith("---"))
|
|
144
|
+
return { frontmatter, body: content };
|
|
145
|
+
// Find the closing `---` (must be on its own line, after the opening).
|
|
146
|
+
const closeMatch = /\n---[ \t]*(?:\n|$)/.exec(content.slice(3));
|
|
147
|
+
if (!closeMatch)
|
|
148
|
+
return { frontmatter, body: content };
|
|
149
|
+
const yamlBlock = content.slice(3, closeMatch.index + 3);
|
|
150
|
+
const body = content.slice(closeMatch.index + 3 + closeMatch[0].length);
|
|
151
|
+
for (const rawLine of yamlBlock.split("\n")) {
|
|
152
|
+
// Skip blanks, comments, and indented (nested) entries.
|
|
153
|
+
const line = rawLine.replace(/[\r]+$/, "");
|
|
154
|
+
if (!line.trim() || line.trim().startsWith("#"))
|
|
155
|
+
continue;
|
|
156
|
+
if (/^\s/.test(line))
|
|
157
|
+
continue;
|
|
158
|
+
const idx = line.indexOf(":");
|
|
159
|
+
if (idx < 0)
|
|
160
|
+
continue;
|
|
161
|
+
const key = line.slice(0, idx).trim();
|
|
162
|
+
let value = line.slice(idx + 1).trim();
|
|
163
|
+
if (!key)
|
|
164
|
+
continue;
|
|
165
|
+
// Flow-list literal → CSV of unwrapped scalars.
|
|
166
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
167
|
+
value = value
|
|
168
|
+
.slice(1, -1)
|
|
169
|
+
.split(",")
|
|
170
|
+
.map((s) => s.trim().replace(/^["']|["']$/g, ""))
|
|
171
|
+
.filter(Boolean)
|
|
172
|
+
.join(",");
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
// Strip surrounding quotes if present.
|
|
176
|
+
value = value.replace(/^["']|["']$/g, "");
|
|
177
|
+
}
|
|
178
|
+
frontmatter[key] = value;
|
|
179
|
+
}
|
|
180
|
+
return { frontmatter, body };
|
|
181
|
+
}
|
|
182
|
+
/** Map `process.platform` to the upstream platform tokens used in SKILL.md
|
|
183
|
+
* `platforms:` lists. */
|
|
184
|
+
const PLATFORM_ALIASES = {
|
|
185
|
+
macos: "darwin",
|
|
186
|
+
osx: "darwin",
|
|
187
|
+
mac: "darwin",
|
|
188
|
+
linux: "linux",
|
|
189
|
+
win: "win32",
|
|
190
|
+
windows: "win32",
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* Return true if the skill's `platforms:` list contains the current OS, or
|
|
194
|
+
* if the field is missing/empty (skill claims cross-platform).
|
|
195
|
+
*
|
|
196
|
+
* Match logic is upstream-compatible: any listed platform whose mapped
|
|
197
|
+
* runtime prefix is a prefix of `process.platform` counts as a hit (so
|
|
198
|
+
* `darwin` matches `darwin23.6.0`).
|
|
199
|
+
*/
|
|
200
|
+
export function matchesPlatform(frontmatter, platform = process.platform) {
|
|
201
|
+
const raw = frontmatter.platforms;
|
|
202
|
+
if (!raw)
|
|
203
|
+
return true;
|
|
204
|
+
const list = raw
|
|
205
|
+
.split(",")
|
|
206
|
+
.map((s) => s.trim().toLowerCase())
|
|
207
|
+
.filter(Boolean);
|
|
208
|
+
if (list.length === 0)
|
|
209
|
+
return true;
|
|
210
|
+
for (const p of list) {
|
|
211
|
+
const mapped = PLATFORM_ALIASES[p] ?? p;
|
|
212
|
+
if (platform.startsWith(mapped))
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
let cachedSkills = null;
|
|
218
|
+
const DEFAULT_CACHE_TTL_MS = 30_000;
|
|
219
|
+
function cacheTtlMs() {
|
|
220
|
+
const raw = process.env.MAESTRO_SKILL_CACHE_TTL_MS;
|
|
221
|
+
if (raw === undefined)
|
|
222
|
+
return DEFAULT_CACHE_TTL_MS;
|
|
223
|
+
const n = Number(raw);
|
|
224
|
+
if (!Number.isFinite(n) || n < 0)
|
|
225
|
+
return DEFAULT_CACHE_TTL_MS;
|
|
226
|
+
return n;
|
|
227
|
+
}
|
|
228
|
+
function safeRootMtime(rootDir) {
|
|
229
|
+
try {
|
|
230
|
+
return statSync(rootDir).mtimeMs;
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
return 0;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Cached variant of `loadSkills` — same return shape, but memoizes on the
|
|
238
|
+
* (rootDir, rootDir-mtime) pair with a TTL backstop. Use this from any hot
|
|
239
|
+
* path (per-turn callers, per-iteration callers). Tests or operators that
|
|
240
|
+
* need a guaranteed fresh load can call `invalidateSkillsCache()` first.
|
|
241
|
+
*/
|
|
242
|
+
export function loadSkillsCached(rootDir) {
|
|
243
|
+
const ttl = cacheTtlMs();
|
|
244
|
+
if (ttl === 0)
|
|
245
|
+
return loadSkills(rootDir);
|
|
246
|
+
const now = Date.now();
|
|
247
|
+
if (cachedSkills && cachedSkills.rootDir === rootDir) {
|
|
248
|
+
const rootMtimeMs = safeRootMtime(rootDir);
|
|
249
|
+
const fresh = now - cachedSkills.builtAtMs < ttl;
|
|
250
|
+
const sameRoot = rootMtimeMs === cachedSkills.rootMtimeMs;
|
|
251
|
+
if (fresh && sameRoot)
|
|
252
|
+
return cachedSkills.entries;
|
|
253
|
+
}
|
|
254
|
+
const entries = loadSkills(rootDir);
|
|
255
|
+
cachedSkills = {
|
|
256
|
+
rootDir,
|
|
257
|
+
entries,
|
|
258
|
+
builtAtMs: now,
|
|
259
|
+
rootMtimeMs: safeRootMtime(rootDir),
|
|
260
|
+
};
|
|
261
|
+
return entries;
|
|
262
|
+
}
|
|
263
|
+
/** Drop the in-memory cache so the next `loadSkillsCached` call rebuilds.
|
|
264
|
+
* Call after writing a new SKILL.md to disk, or between tests. */
|
|
265
|
+
export function invalidateSkillsCache() {
|
|
266
|
+
cachedSkills = null;
|
|
267
|
+
}
|
|
268
|
+
/** Find a single skill by name. Used by `skill_view`. Returns null if the
|
|
269
|
+
* name doesn't resolve to a loaded entry. */
|
|
270
|
+
export function findSkillByName(skills, name) {
|
|
271
|
+
const wanted = name.trim();
|
|
272
|
+
if (!wanted)
|
|
273
|
+
return null;
|
|
274
|
+
for (const s of skills) {
|
|
275
|
+
if (s.name === wanted)
|
|
276
|
+
return s;
|
|
277
|
+
}
|
|
278
|
+
// Case-insensitive fallback — model occasionally lowercases identifiers.
|
|
279
|
+
const lower = wanted.toLowerCase();
|
|
280
|
+
for (const s of skills) {
|
|
281
|
+
if (s.name.toLowerCase() === lower)
|
|
282
|
+
return s;
|
|
283
|
+
}
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/skills/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface PreprocessOptions {
|
|
2
|
+
/** Absolute path passed to `${MAESTRO_SKILL_DIR}` (`null` keeps the token literal). */
|
|
3
|
+
skillDir?: string | null;
|
|
4
|
+
/** Maestro session UUID for `${MAESTRO_SESSION_ID}` (`null` keeps the token literal). */
|
|
5
|
+
sessionId?: string | null;
|
|
6
|
+
/** Run `!`cmd`` snippets at preprocess time. Default `false` (matches upstream). */
|
|
7
|
+
inlineShell?: boolean;
|
|
8
|
+
/** Per-snippet timeout in seconds when `inlineShell: true`. */
|
|
9
|
+
inlineShellTimeoutS?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Replace `${MAESTRO_SKILL_DIR}` / `${MAESTRO_SESSION_ID}` in `content`.
|
|
13
|
+
*
|
|
14
|
+
* Tokens for which the corresponding option value is missing are left in
|
|
15
|
+
* place — the author can then see "oh, this skill was loaded without a
|
|
16
|
+
* session id" instead of getting silent empty strings.
|
|
17
|
+
*/
|
|
18
|
+
export declare function substituteTemplateVars(content: string, opts: {
|
|
19
|
+
skillDir?: string | null;
|
|
20
|
+
sessionId?: string | null;
|
|
21
|
+
}): string;
|
|
22
|
+
/**
|
|
23
|
+
* Run one inline-shell snippet. Failures return a short `[inline-shell ...]`
|
|
24
|
+
* marker rather than throwing so a single bad snippet can't break the whole
|
|
25
|
+
* skill load.
|
|
26
|
+
*
|
|
27
|
+
* `cwd` is set to the skill directory so relative paths in the snippet
|
|
28
|
+
* resolve where the author expected them to.
|
|
29
|
+
*/
|
|
30
|
+
export declare function runInlineShell(command: string, cwd: string | null | undefined, timeoutS: number): string;
|
|
31
|
+
/** Expand every `` !`cmd` `` snippet in `content` with its stdout. No-op
|
|
32
|
+
* when the content has no `!`` marker (cheap fast path). */
|
|
33
|
+
export declare function expandInlineShell(content: string, cwd: string | null | undefined, timeoutS: number): string;
|
|
34
|
+
/**
|
|
35
|
+
* Single-call entry point used by `skill_view`. Applies template-var
|
|
36
|
+
* substitution unconditionally and inline-shell only when explicitly
|
|
37
|
+
* opted in.
|
|
38
|
+
*
|
|
39
|
+
* Returns the input unchanged when it's empty (defensive — upstream's
|
|
40
|
+
* `preprocess_skill_content` returns `""` for empty inputs).
|
|
41
|
+
*/
|
|
42
|
+
export declare function preprocessSkillContent(content: string, opts: PreprocessOptions): string;
|
|
43
|
+
export declare const __INLINE_SHELL_MAX_OUTPUT = 4000;
|
|
44
|
+
export declare const __INLINE_SHELL_DEFAULT_TIMEOUT_S = 10;
|
|
45
|
+
//# sourceMappingURL=preprocess.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preprocess.d.ts","sourceRoot":"","sources":["../../src/skills/preprocess.ts"],"names":[],"mappings":"AA4CA,MAAM,WAAW,iBAAiB;IAChC,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,yFAAyF;IACzF,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,oFAAoF;IACpF,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+DAA+D;IAC/D,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC5D,MAAM,CAOR;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC9B,QAAQ,EAAE,MAAM,GACf,MAAM,CAyBR;AAED;6DAC6D;AAC7D,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC9B,QAAQ,EAAE,MAAM,GACf,MAAM,CAOR;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAQvF;AAGD,eAAO,MAAM,yBAAyB,OAA0B,CAAC;AACjE,eAAO,MAAM,gCAAgC,KAAiC,CAAC"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
/**
|
|
3
|
+
* SKILL.md content preprocessing for the Maestro TS port.
|
|
4
|
+
*
|
|
5
|
+
* Two transforms, both lifted from upstream `agent/skill_preprocessing.py`:
|
|
6
|
+
*
|
|
7
|
+
* 1. Template variable substitution — `${MAESTRO_SKILL_DIR}` and
|
|
8
|
+
* `${MAESTRO_SESSION_ID}` get replaced with the active skill's absolute
|
|
9
|
+
* directory and the current Maestro session id, respectively. Skill
|
|
10
|
+
* authors rely on this to reference bundled scripts:
|
|
11
|
+
*
|
|
12
|
+
* Run `bash ${MAESTRO_SKILL_DIR}/scripts/setup.sh` before continuing.
|
|
13
|
+
*
|
|
14
|
+
* Tokens whose value isn't supplied stay literal — the author can spot
|
|
15
|
+
* the unresolved placeholder when reading the model's output.
|
|
16
|
+
*
|
|
17
|
+
* 2. Inline shell snippets — `` !`<cmd>` `` runs the command at load time
|
|
18
|
+
* and is replaced with its stdout (capped). Off by default (matches
|
|
19
|
+
* upstream's `skills.inline_shell: false` default) because it runs
|
|
20
|
+
* arbitrary code at preprocessing time; opt-in via the `inlineShell`
|
|
21
|
+
* flag for trusted skills that need dynamic state (e.g. current
|
|
22
|
+
* git branch).
|
|
23
|
+
*
|
|
24
|
+
* Both are applied at `skill_view`-time, **after** the SKILL.md body has been
|
|
25
|
+
* loaded and **before** the body is handed to the model. The model sees a
|
|
26
|
+
* fully-resolved skill, never a raw template token.
|
|
27
|
+
*/
|
|
28
|
+
/** Matches `${MAESTRO_SKILL_DIR}` / `${MAESTRO_SESSION_ID}`. Unknown tokens are
|
|
29
|
+
* left intact for debugability. */
|
|
30
|
+
const TEMPLATE_TOKEN_RE = /\$\{(MAESTRO_SKILL_DIR|MAESTRO_SESSION_ID)\}/g;
|
|
31
|
+
/** Inline shell shape: `` !`single-line-cmd` ``. No newlines inside the
|
|
32
|
+
* backticks (matches upstream — multi-line shell goes in a code fence). */
|
|
33
|
+
const INLINE_SHELL_RE = /!`([^`\n]+)`/g;
|
|
34
|
+
/** Hard cap on stdout from a single inline-shell snippet so a runaway
|
|
35
|
+
* command can't blow out the model's context window. */
|
|
36
|
+
const INLINE_SHELL_MAX_OUTPUT = 4000;
|
|
37
|
+
/** Default per-snippet timeout (seconds). Upstream uses 10s. */
|
|
38
|
+
const INLINE_SHELL_DEFAULT_TIMEOUT_S = 10;
|
|
39
|
+
/**
|
|
40
|
+
* Replace `${MAESTRO_SKILL_DIR}` / `${MAESTRO_SESSION_ID}` in `content`.
|
|
41
|
+
*
|
|
42
|
+
* Tokens for which the corresponding option value is missing are left in
|
|
43
|
+
* place — the author can then see "oh, this skill was loaded without a
|
|
44
|
+
* session id" instead of getting silent empty strings.
|
|
45
|
+
*/
|
|
46
|
+
export function substituteTemplateVars(content, opts) {
|
|
47
|
+
if (!content)
|
|
48
|
+
return content;
|
|
49
|
+
return content.replace(TEMPLATE_TOKEN_RE, (full, token) => {
|
|
50
|
+
if (token === "MAESTRO_SKILL_DIR" && opts.skillDir)
|
|
51
|
+
return opts.skillDir;
|
|
52
|
+
if (token === "MAESTRO_SESSION_ID" && opts.sessionId)
|
|
53
|
+
return opts.sessionId;
|
|
54
|
+
return full;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Run one inline-shell snippet. Failures return a short `[inline-shell ...]`
|
|
59
|
+
* marker rather than throwing so a single bad snippet can't break the whole
|
|
60
|
+
* skill load.
|
|
61
|
+
*
|
|
62
|
+
* `cwd` is set to the skill directory so relative paths in the snippet
|
|
63
|
+
* resolve where the author expected them to.
|
|
64
|
+
*/
|
|
65
|
+
export function runInlineShell(command, cwd, timeoutS) {
|
|
66
|
+
const cleanedTimeout = Number.isFinite(timeoutS) && timeoutS > 0 ? Math.floor(timeoutS) : 1;
|
|
67
|
+
try {
|
|
68
|
+
const result = spawnSync("bash", ["-c", command], {
|
|
69
|
+
cwd: cwd ?? undefined,
|
|
70
|
+
encoding: "utf-8",
|
|
71
|
+
timeout: cleanedTimeout * 1000,
|
|
72
|
+
maxBuffer: INLINE_SHELL_MAX_OUTPUT * 4,
|
|
73
|
+
});
|
|
74
|
+
if (result.error) {
|
|
75
|
+
const err = result.error;
|
|
76
|
+
if (err.code === "ETIMEDOUT") {
|
|
77
|
+
return `[inline-shell timeout after ${cleanedTimeout}s: ${command}]`;
|
|
78
|
+
}
|
|
79
|
+
return `[inline-shell error: ${err.message}]`;
|
|
80
|
+
}
|
|
81
|
+
let output = (result.stdout ?? "").replace(/\n+$/, "");
|
|
82
|
+
if (!output && result.stderr)
|
|
83
|
+
output = result.stderr.replace(/\n+$/, "");
|
|
84
|
+
if (output.length > INLINE_SHELL_MAX_OUTPUT) {
|
|
85
|
+
output = `${output.slice(0, INLINE_SHELL_MAX_OUTPUT)}...[truncated]`;
|
|
86
|
+
}
|
|
87
|
+
return output;
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
return `[inline-shell error: ${err.message}]`;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** Expand every `` !`cmd` `` snippet in `content` with its stdout. No-op
|
|
94
|
+
* when the content has no `!`` marker (cheap fast path). */
|
|
95
|
+
export function expandInlineShell(content, cwd, timeoutS) {
|
|
96
|
+
if (!content.includes("!`"))
|
|
97
|
+
return content;
|
|
98
|
+
return content.replace(INLINE_SHELL_RE, (_full, cmd) => {
|
|
99
|
+
const trimmed = String(cmd).trim();
|
|
100
|
+
if (!trimmed)
|
|
101
|
+
return "";
|
|
102
|
+
return runInlineShell(trimmed, cwd, timeoutS);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Single-call entry point used by `skill_view`. Applies template-var
|
|
107
|
+
* substitution unconditionally and inline-shell only when explicitly
|
|
108
|
+
* opted in.
|
|
109
|
+
*
|
|
110
|
+
* Returns the input unchanged when it's empty (defensive — upstream's
|
|
111
|
+
* `preprocess_skill_content` returns `""` for empty inputs).
|
|
112
|
+
*/
|
|
113
|
+
export function preprocessSkillContent(content, opts) {
|
|
114
|
+
if (!content)
|
|
115
|
+
return content;
|
|
116
|
+
let out = substituteTemplateVars(content, { skillDir: opts.skillDir, sessionId: opts.sessionId });
|
|
117
|
+
if (opts.inlineShell) {
|
|
118
|
+
const timeout = opts.inlineShellTimeoutS ?? INLINE_SHELL_DEFAULT_TIMEOUT_S;
|
|
119
|
+
out = expandInlineShell(out, opts.skillDir, timeout);
|
|
120
|
+
}
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
123
|
+
// Expose internal constants for tests.
|
|
124
|
+
export const __INLINE_SHELL_MAX_OUTPUT = INLINE_SHELL_MAX_OUTPUT;
|
|
125
|
+
export const __INLINE_SHELL_DEFAULT_TIMEOUT_S = INLINE_SHELL_DEFAULT_TIMEOUT_S;
|
|
126
|
+
//# sourceMappingURL=preprocess.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preprocess.js","sourceRoot":"","sources":["../../src/skills/preprocess.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill usage sidecar — process-local persistent counters.
|
|
3
|
+
*
|
|
4
|
+
* Each skill the catalog ever surfaces accumulates three counters:
|
|
5
|
+
*
|
|
6
|
+
* - viewCount: bumped when `skill_view(name)` returns its body. Direct
|
|
7
|
+
* signal that the model thought this skill was worth pulling
|
|
8
|
+
* the full SKILL.md for.
|
|
9
|
+
* - useCount: bumped by callers when one of the skill's recommended
|
|
10
|
+
* commands actually ran. Optional today (no auto-detector
|
|
11
|
+
* for "did the bash call use a skill-suggested invocation");
|
|
12
|
+
* kept in the schema so future instrumentation can land
|
|
13
|
+
* without a migration.
|
|
14
|
+
* - patchCount: bumped on skill edits (future `skill_edit` builtin).
|
|
15
|
+
*
|
|
16
|
+
* Storage: a single JSON file at `${DATA_DIR}/agents/maestro/skills/usage.json`.
|
|
17
|
+
* Process-local — Clawgram is single-process in production, so a per-skill
|
|
18
|
+
* sidecar would over-shard the disk for no gain. Atomic-write via tmp +
|
|
19
|
+
* rename guards against the rare `writeFileSync` interrupted-write race.
|
|
20
|
+
*
|
|
21
|
+
* Concurrency: serialized via an in-process mutex chain (Promises). Two
|
|
22
|
+
* `bumpView` calls during the same tick (e.g. a model that views two skills
|
|
23
|
+
* in one assistant turn) queue cleanly instead of one clobbering the other.
|
|
24
|
+
* Inter-process locking is intentionally NOT implemented — production has
|
|
25
|
+
* one bun process; tests use `__resetForTests` to avoid cross-test leak.
|
|
26
|
+
*
|
|
27
|
+
* Read path is non-blocking (synchronous read of the at-rest JSON). Write
|
|
28
|
+
* path is async (await the mutex, then sync-write the tmp + rename). Both
|
|
29
|
+
* tolerate ENOENT — first-ever bump creates the file fresh.
|
|
30
|
+
*
|
|
31
|
+
* Upstream reference: `/Users/maestrobot/__KEEP_MAESTRO_AGENT__/tools/skill_usage.py`
|
|
32
|
+
* (similar schema; we collapse their per-skill `.usage.json` directory
|
|
33
|
+
* pattern into one central JSON to avoid polluting the upstream snapshot's
|
|
34
|
+
* skills/ directory with mutated files).
|
|
35
|
+
*/
|
|
36
|
+
export interface SkillCounters {
|
|
37
|
+
viewCount: number;
|
|
38
|
+
useCount: number;
|
|
39
|
+
patchCount: number;
|
|
40
|
+
/** ISO timestamp of the most recent bump on any counter. */
|
|
41
|
+
lastTouchedTs: string;
|
|
42
|
+
/** ISO timestamp of the first time this skill appeared in the file.
|
|
43
|
+
* Used by the Curator to weight new vs long-stale skills. */
|
|
44
|
+
firstSeenTs: string;
|
|
45
|
+
}
|
|
46
|
+
export interface SkillUsageFile {
|
|
47
|
+
schemaVersion: 1;
|
|
48
|
+
skills: Record<string, SkillCounters>;
|
|
49
|
+
}
|
|
50
|
+
/** Default sidecar path. Overridable via `MAESTRO_SKILL_USAGE_PATH` for tests
|
|
51
|
+
* and operators that prefer a different on-disk location. */
|
|
52
|
+
export declare function defaultUsagePath(): string;
|
|
53
|
+
/**
|
|
54
|
+
* Read the usage file synchronously. Returns an empty (in-memory) file on
|
|
55
|
+
* ENOENT / parse failure — callers that need to disambiguate can call
|
|
56
|
+
* `existsSync(defaultUsagePath())` themselves. Logged at debug so a missing
|
|
57
|
+
* file is not noise on every read.
|
|
58
|
+
*/
|
|
59
|
+
export declare function loadUsage(path?: string): SkillUsageFile;
|
|
60
|
+
/** Get counters for a single skill. Never returns undefined — synthesizes
|
|
61
|
+
* zeroed counters for unseen skills so callers can compute deltas without
|
|
62
|
+
* null-checks. */
|
|
63
|
+
export declare function getCounters(name: string, path?: string): SkillCounters;
|
|
64
|
+
/** Bump `viewCount` for `name`. Called from `skill_view` after a successful body load. */
|
|
65
|
+
export declare function bumpView(name: string, path?: string): Promise<SkillCounters>;
|
|
66
|
+
/** Bump `useCount` for `name`. Reserved for future call-site instrumentation. */
|
|
67
|
+
export declare function bumpUse(name: string, path?: string): Promise<SkillCounters>;
|
|
68
|
+
/** Bump `patchCount` for `name`. Reserved for the future `skill_edit` builtin. */
|
|
69
|
+
export declare function bumpPatch(name: string, path?: string): Promise<SkillCounters>;
|
|
70
|
+
/** Test-only: drop the in-memory mutex queue + write-through cache. Disk
|
|
71
|
+
* file is NOT erased — tests that need filesystem isolation should point
|
|
72
|
+
* `MAESTRO_SKILL_USAGE_PATH` at a tmp file via `process.env` and remove it
|
|
73
|
+
* themselves. */
|
|
74
|
+
export declare function __resetForTests(): void;
|
|
75
|
+
//# sourceMappingURL=usage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/skills/usage.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,aAAa,EAAE,MAAM,CAAC;IACtB;kEAC8D;IAC9D,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,CAAC,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CACvC;AAID;8DAC8D;AAC9D,wBAAgB,gBAAgB,IAAI,MAAM,CAKzC;AAmCD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,GAAE,MAA2B,GAAG,cAAc,CAM3E;AA2BD;;mBAEmB;AACnB,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAA2B,GAAG,aAAa,CAG1F;AAmDD,0FAA0F;AAC1F,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAE5E;AAED,iFAAiF;AACjF,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAE3E;AAED,kFAAkF;AAClF,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAE7E;AAED;;;kBAGkB;AAClB,wBAAgB,eAAe,IAAI,IAAI,CAGtC"}
|