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.
Files changed (196) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +24 -0
  3. package/README.md +133 -0
  4. package/dist/agents/contracts.d.ts +49 -0
  5. package/dist/agents/contracts.d.ts.map +1 -0
  6. package/dist/agents/contracts.js +2 -0
  7. package/dist/agents/contracts.js.map +1 -0
  8. package/dist/agents/rollout/shared.d.ts +24 -0
  9. package/dist/agents/rollout/shared.d.ts.map +1 -0
  10. package/dist/agents/rollout/shared.js +105 -0
  11. package/dist/agents/rollout/shared.js.map +1 -0
  12. package/dist/core/agent.d.ts +71 -0
  13. package/dist/core/agent.d.ts.map +1 -0
  14. package/dist/core/agent.js +22 -0
  15. package/dist/core/agent.js.map +1 -0
  16. package/dist/core/loop.d.ts +26 -0
  17. package/dist/core/loop.d.ts.map +1 -0
  18. package/dist/core/loop.js +317 -0
  19. package/dist/core/loop.js.map +1 -0
  20. package/dist/index.d.ts +49 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +53 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/mcp/client.d.ts +79 -0
  25. package/dist/mcp/client.d.ts.map +1 -0
  26. package/dist/mcp/client.js +176 -0
  27. package/dist/mcp/client.js.map +1 -0
  28. package/dist/mcp/pool-cache.d.ts +103 -0
  29. package/dist/mcp/pool-cache.d.ts.map +1 -0
  30. package/dist/mcp/pool-cache.js +249 -0
  31. package/dist/mcp/pool-cache.js.map +1 -0
  32. package/dist/mcp/pool.d.ts +65 -0
  33. package/dist/mcp/pool.d.ts.map +1 -0
  34. package/dist/mcp/pool.js +86 -0
  35. package/dist/mcp/pool.js.map +1 -0
  36. package/dist/media/file-events.d.ts +8 -0
  37. package/dist/media/file-events.d.ts.map +1 -0
  38. package/dist/media/file-events.js +15 -0
  39. package/dist/media/file-events.js.map +1 -0
  40. package/dist/memory/active-task-template.d.ts +34 -0
  41. package/dist/memory/active-task-template.d.ts.map +1 -0
  42. package/dist/memory/active-task-template.js +63 -0
  43. package/dist/memory/active-task-template.js.map +1 -0
  44. package/dist/memory/compressor.d.ts +87 -0
  45. package/dist/memory/compressor.d.ts.map +1 -0
  46. package/dist/memory/compressor.js +164 -0
  47. package/dist/memory/compressor.js.map +1 -0
  48. package/dist/memory/hash.d.ts +17 -0
  49. package/dist/memory/hash.d.ts.map +1 -0
  50. package/dist/memory/hash.js +20 -0
  51. package/dist/memory/hash.js.map +1 -0
  52. package/dist/memory/prune.d.ts +117 -0
  53. package/dist/memory/prune.d.ts.map +1 -0
  54. package/dist/memory/prune.js +416 -0
  55. package/dist/memory/prune.js.map +1 -0
  56. package/dist/memory/reminder.d.ts +57 -0
  57. package/dist/memory/reminder.d.ts.map +1 -0
  58. package/dist/memory/reminder.js +57 -0
  59. package/dist/memory/reminder.js.map +1 -0
  60. package/dist/memory/scrubber.d.ts +28 -0
  61. package/dist/memory/scrubber.d.ts.map +1 -0
  62. package/dist/memory/scrubber.js +147 -0
  63. package/dist/memory/scrubber.js.map +1 -0
  64. package/dist/memory/token-estimate.d.ts +10 -0
  65. package/dist/memory/token-estimate.d.ts.map +1 -0
  66. package/dist/memory/token-estimate.js +69 -0
  67. package/dist/memory/token-estimate.js.map +1 -0
  68. package/dist/platform/config.d.ts +12 -0
  69. package/dist/platform/config.d.ts.map +1 -0
  70. package/dist/platform/config.js +54 -0
  71. package/dist/platform/config.js.map +1 -0
  72. package/dist/platform/jsonl.d.ts +15 -0
  73. package/dist/platform/jsonl.d.ts.map +1 -0
  74. package/dist/platform/jsonl.js +80 -0
  75. package/dist/platform/jsonl.js.map +1 -0
  76. package/dist/platform/lifecycle.d.ts +22 -0
  77. package/dist/platform/lifecycle.d.ts.map +1 -0
  78. package/dist/platform/lifecycle.js +60 -0
  79. package/dist/platform/lifecycle.js.map +1 -0
  80. package/dist/platform/logger.d.ts +26 -0
  81. package/dist/platform/logger.d.ts.map +1 -0
  82. package/dist/platform/logger.js +41 -0
  83. package/dist/platform/logger.js.map +1 -0
  84. package/dist/platform/mcp-config.d.ts +15 -0
  85. package/dist/platform/mcp-config.d.ts.map +1 -0
  86. package/dist/platform/mcp-config.js +8 -0
  87. package/dist/platform/mcp-config.js.map +1 -0
  88. package/dist/provider.d.ts +81 -0
  89. package/dist/provider.d.ts.map +1 -0
  90. package/dist/provider.js +444 -0
  91. package/dist/provider.js.map +1 -0
  92. package/dist/providers/anthropic.d.ts +132 -0
  93. package/dist/providers/anthropic.d.ts.map +1 -0
  94. package/dist/providers/anthropic.js +518 -0
  95. package/dist/providers/anthropic.js.map +1 -0
  96. package/dist/providers/base.d.ts +140 -0
  97. package/dist/providers/base.d.ts.map +1 -0
  98. package/dist/providers/base.js +2 -0
  99. package/dist/providers/base.js.map +1 -0
  100. package/dist/providers/deepseek.d.ts +118 -0
  101. package/dist/providers/deepseek.d.ts.map +1 -0
  102. package/dist/providers/deepseek.js +467 -0
  103. package/dist/providers/deepseek.js.map +1 -0
  104. package/dist/registry.d.ts +3 -0
  105. package/dist/registry.d.ts.map +1 -0
  106. package/dist/registry.js +94 -0
  107. package/dist/registry.js.map +1 -0
  108. package/dist/session-store.d.ts +133 -0
  109. package/dist/session-store.d.ts.map +1 -0
  110. package/dist/session-store.js +277 -0
  111. package/dist/session-store.js.map +1 -0
  112. package/dist/skills/curator.d.ts +104 -0
  113. package/dist/skills/curator.d.ts.map +1 -0
  114. package/dist/skills/curator.js +162 -0
  115. package/dist/skills/curator.js.map +1 -0
  116. package/dist/skills/index-builder.d.ts +42 -0
  117. package/dist/skills/index-builder.d.ts.map +1 -0
  118. package/dist/skills/index-builder.js +94 -0
  119. package/dist/skills/index-builder.js.map +1 -0
  120. package/dist/skills/loader.d.ts +107 -0
  121. package/dist/skills/loader.d.ts.map +1 -0
  122. package/dist/skills/loader.js +286 -0
  123. package/dist/skills/loader.js.map +1 -0
  124. package/dist/skills/preprocess.d.ts +45 -0
  125. package/dist/skills/preprocess.d.ts.map +1 -0
  126. package/dist/skills/preprocess.js +126 -0
  127. package/dist/skills/preprocess.js.map +1 -0
  128. package/dist/skills/usage.d.ts +75 -0
  129. package/dist/skills/usage.d.ts.map +1 -0
  130. package/dist/skills/usage.js +147 -0
  131. package/dist/skills/usage.js.map +1 -0
  132. package/dist/state/todos.d.ts +95 -0
  133. package/dist/state/todos.d.ts.map +1 -0
  134. package/dist/state/todos.js +198 -0
  135. package/dist/state/todos.js.map +1 -0
  136. package/dist/storage/conversations.d.ts +28 -0
  137. package/dist/storage/conversations.d.ts.map +1 -0
  138. package/dist/storage/conversations.js +8 -0
  139. package/dist/storage/conversations.js.map +1 -0
  140. package/dist/sub-agent/runner.d.ts +78 -0
  141. package/dist/sub-agent/runner.d.ts.map +1 -0
  142. package/dist/sub-agent/runner.js +215 -0
  143. package/dist/sub-agent/runner.js.map +1 -0
  144. package/dist/tools/builtin/agent.d.ts +33 -0
  145. package/dist/tools/builtin/agent.d.ts.map +1 -0
  146. package/dist/tools/builtin/agent.js +76 -0
  147. package/dist/tools/builtin/agent.js.map +1 -0
  148. package/dist/tools/builtin/bash.d.ts +11 -0
  149. package/dist/tools/builtin/bash.d.ts.map +1 -0
  150. package/dist/tools/builtin/bash.js +91 -0
  151. package/dist/tools/builtin/bash.js.map +1 -0
  152. package/dist/tools/builtin/edit.d.ts +21 -0
  153. package/dist/tools/builtin/edit.d.ts.map +1 -0
  154. package/dist/tools/builtin/edit.js +238 -0
  155. package/dist/tools/builtin/edit.js.map +1 -0
  156. package/dist/tools/builtin/read.d.ts +17 -0
  157. package/dist/tools/builtin/read.d.ts.map +1 -0
  158. package/dist/tools/builtin/read.js +139 -0
  159. package/dist/tools/builtin/read.js.map +1 -0
  160. package/dist/tools/builtin/sandbox.d.ts +16 -0
  161. package/dist/tools/builtin/sandbox.d.ts.map +1 -0
  162. package/dist/tools/builtin/sandbox.js +58 -0
  163. package/dist/tools/builtin/sandbox.js.map +1 -0
  164. package/dist/tools/builtin/skill_view.d.ts +37 -0
  165. package/dist/tools/builtin/skill_view.d.ts.map +1 -0
  166. package/dist/tools/builtin/skill_view.js +82 -0
  167. package/dist/tools/builtin/skill_view.js.map +1 -0
  168. package/dist/tools/builtin/todo_write.d.ts +29 -0
  169. package/dist/tools/builtin/todo_write.d.ts.map +1 -0
  170. package/dist/tools/builtin/todo_write.js +96 -0
  171. package/dist/tools/builtin/todo_write.js.map +1 -0
  172. package/dist/tools/builtin/web_fetch.d.ts +10 -0
  173. package/dist/tools/builtin/web_fetch.d.ts.map +1 -0
  174. package/dist/tools/builtin/web_fetch.js +150 -0
  175. package/dist/tools/builtin/web_fetch.js.map +1 -0
  176. package/dist/tools/builtin/write.d.ts +35 -0
  177. package/dist/tools/builtin/write.d.ts.map +1 -0
  178. package/dist/tools/builtin/write.js +70 -0
  179. package/dist/tools/builtin/write.js.map +1 -0
  180. package/dist/tools/file-state.d.ts +99 -0
  181. package/dist/tools/file-state.d.ts.map +1 -0
  182. package/dist/tools/file-state.js +133 -0
  183. package/dist/tools/file-state.js.map +1 -0
  184. package/dist/tools/hooks/sandbox-fs.d.ts +25 -0
  185. package/dist/tools/hooks/sandbox-fs.d.ts.map +1 -0
  186. package/dist/tools/hooks/sandbox-fs.js +48 -0
  187. package/dist/tools/hooks/sandbox-fs.js.map +1 -0
  188. package/dist/tools/registry.d.ts +102 -0
  189. package/dist/tools/registry.d.ts.map +1 -0
  190. package/dist/tools/registry.js +93 -0
  191. package/dist/tools/registry.js.map +1 -0
  192. package/dist/types.d.ts +109 -0
  193. package/dist/types.d.ts.map +1 -0
  194. package/dist/types.js +20 -0
  195. package/dist/types.js.map +1 -0
  196. 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"}