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,238 @@
|
|
|
1
|
+
import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { isAbsolute } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* NOTE: filesystem sandboxing now lives in the `sandbox-fs` PreToolUse hook.
|
|
5
|
+
* Standalone callers (tests, scripts) without the registry get NO sandbox.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Edit builtin — claude SDK `Edit` tool parity for maestro.
|
|
9
|
+
*
|
|
10
|
+
* Mirrors the upstream `Edit` tool's name + input schema so the model's
|
|
11
|
+
* pretrained instinct calls our handler with the right shape. Same as
|
|
12
|
+
* Read/Write, this benefits prompt cache when a topic is bridged across
|
|
13
|
+
* agents — the tool schemas line up byte-for-byte.
|
|
14
|
+
*
|
|
15
|
+
* Contract (matches claude SDK exactly):
|
|
16
|
+
* - file_path must be absolute, the file must exist (Edit never creates).
|
|
17
|
+
* - old_string must appear in the file. Same anchor for both modes.
|
|
18
|
+
* - replace_all=false (default): old_string MUST appear exactly once, else
|
|
19
|
+
* we reject. This is intentional: the model is forced to enlarge its
|
|
20
|
+
* `old_string` until it pins a unique anchor, which is the strongest
|
|
21
|
+
* safety against silent mis-edits. claude SDK's own description ("either
|
|
22
|
+
* provide a larger string with more surrounding context to make it
|
|
23
|
+
* unique or use replace_all") is what trains the model's intuition; we
|
|
24
|
+
* error with the same shape so it self-corrects on the next turn.
|
|
25
|
+
* - replace_all=true: every occurrence is swapped (useful for renames).
|
|
26
|
+
* - old_string === new_string is a no-op and rejected as a model bug.
|
|
27
|
+
*
|
|
28
|
+
* Returns a short summary + line-numbered preview of the changed region
|
|
29
|
+
* (claude SDK does the same — the model uses this preview to confirm
|
|
30
|
+
* its own edit landed before continuing). Errors are JSON-stringified
|
|
31
|
+
* `{error}` for parity with bash/read/write.
|
|
32
|
+
*/
|
|
33
|
+
const MAX_FILE_BYTES = 10 * 1024 * 1024; // 10MB — same cap as Read
|
|
34
|
+
const PREVIEW_LINES_AROUND = 3;
|
|
35
|
+
export function createEditTool(opts = {}) {
|
|
36
|
+
const { tracker } = opts;
|
|
37
|
+
return {
|
|
38
|
+
schema: {
|
|
39
|
+
name: "Edit",
|
|
40
|
+
description: "Find-and-replace within a single file. " +
|
|
41
|
+
"file_path must be absolute. old_string must match exactly. " +
|
|
42
|
+
"By default old_string must occur exactly once — if it appears multiple " +
|
|
43
|
+
"times, either enlarge old_string with surrounding context to make it " +
|
|
44
|
+
"unique, or set replace_all=true. old_string and new_string must differ.",
|
|
45
|
+
input_schema: {
|
|
46
|
+
type: "object",
|
|
47
|
+
properties: {
|
|
48
|
+
file_path: {
|
|
49
|
+
type: "string",
|
|
50
|
+
description: "Absolute path to the file. Edit never creates new files.",
|
|
51
|
+
},
|
|
52
|
+
old_string: {
|
|
53
|
+
type: "string",
|
|
54
|
+
description: "Exact string to find. Must be unique unless replace_all=true.",
|
|
55
|
+
},
|
|
56
|
+
new_string: {
|
|
57
|
+
type: "string",
|
|
58
|
+
description: "Replacement string. Must differ from old_string.",
|
|
59
|
+
},
|
|
60
|
+
replace_all: {
|
|
61
|
+
type: "boolean",
|
|
62
|
+
description: "When true, replace every occurrence. Default false (unique-match required).",
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
required: ["file_path", "old_string", "new_string"],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
async execute(input) {
|
|
69
|
+
const filePath = typeof input.file_path === "string" ? input.file_path : "";
|
|
70
|
+
if (!filePath) {
|
|
71
|
+
return JSON.stringify({ error: "Edit: missing 'file_path' argument" });
|
|
72
|
+
}
|
|
73
|
+
if (!isAbsolute(filePath)) {
|
|
74
|
+
return JSON.stringify({
|
|
75
|
+
error: `Edit: file_path must be absolute, got '${filePath}'`,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
// Read-before-Edit gate. No-op when no tracker is wired (standalone use).
|
|
79
|
+
if (tracker) {
|
|
80
|
+
const gateErr = tracker.checkBeforeMutate(filePath, "Edit");
|
|
81
|
+
if (gateErr) {
|
|
82
|
+
return JSON.stringify({ error: gateErr });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (typeof input.old_string !== "string") {
|
|
86
|
+
return JSON.stringify({
|
|
87
|
+
error: `Edit: 'old_string' must be a string, got ${typeof input.old_string}`,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
if (typeof input.new_string !== "string") {
|
|
91
|
+
return JSON.stringify({
|
|
92
|
+
error: `Edit: 'new_string' must be a string, got ${typeof input.new_string}`,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
const oldStr = input.old_string;
|
|
96
|
+
const newStr = input.new_string;
|
|
97
|
+
const replaceAll = Boolean(input.replace_all);
|
|
98
|
+
if (oldStr === newStr) {
|
|
99
|
+
return JSON.stringify({
|
|
100
|
+
error: "Edit: old_string and new_string must differ (no-op edits are rejected)",
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (oldStr.length === 0) {
|
|
104
|
+
return JSON.stringify({
|
|
105
|
+
error: "Edit: old_string must be non-empty",
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (!existsSync(filePath)) {
|
|
109
|
+
return JSON.stringify({
|
|
110
|
+
error: `Edit: file does not exist: ${filePath}. Use Write to create new files.`,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
let stat;
|
|
114
|
+
try {
|
|
115
|
+
stat = statSync(filePath);
|
|
116
|
+
}
|
|
117
|
+
catch (e) {
|
|
118
|
+
return JSON.stringify({
|
|
119
|
+
error: `Edit: stat failed: ${e instanceof Error ? e.message : String(e)}`,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
if (stat.isDirectory()) {
|
|
123
|
+
return JSON.stringify({
|
|
124
|
+
error: `Edit: '${filePath}' is a directory, not a file.`,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
if (stat.size > MAX_FILE_BYTES) {
|
|
128
|
+
return JSON.stringify({
|
|
129
|
+
error: `Edit: file size ${stat.size} exceeds 10MB cap. Use bash sed/awk for large files.`,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
let raw;
|
|
133
|
+
try {
|
|
134
|
+
raw = readFileSync(filePath, "utf-8");
|
|
135
|
+
}
|
|
136
|
+
catch (e) {
|
|
137
|
+
return JSON.stringify({
|
|
138
|
+
error: `Edit: read failed: ${e instanceof Error ? e.message : String(e)}`,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
const occurrences = countOccurrences(raw, oldStr);
|
|
142
|
+
if (occurrences === 0) {
|
|
143
|
+
return JSON.stringify({
|
|
144
|
+
error: `Edit: old_string not found in ${filePath}. Re-read the file and verify the exact text (whitespace, indentation, etc.).`,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
if (!replaceAll && occurrences > 1) {
|
|
148
|
+
return JSON.stringify({
|
|
149
|
+
error: `Edit: old_string appears ${occurrences} times in ${filePath}. Either enlarge old_string with surrounding context to make it unique, or pass replace_all=true.`,
|
|
150
|
+
occurrences,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
// Perform the replacement.
|
|
154
|
+
let updated;
|
|
155
|
+
if (replaceAll) {
|
|
156
|
+
updated = raw.split(oldStr).join(newStr);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
const idx = raw.indexOf(oldStr);
|
|
160
|
+
updated = raw.slice(0, idx) + newStr + raw.slice(idx + oldStr.length);
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
writeFileSync(filePath, updated, "utf-8");
|
|
164
|
+
}
|
|
165
|
+
catch (e) {
|
|
166
|
+
return JSON.stringify({
|
|
167
|
+
error: `Edit: write failed: ${e instanceof Error ? e.message : String(e)}`,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
// Forget the recorded state — file mtime + size just changed, so the
|
|
171
|
+
// next Edit must Re-read to refresh the line-numbered view.
|
|
172
|
+
tracker?.forget(filePath);
|
|
173
|
+
// Build a small line-numbered preview around the first changed location.
|
|
174
|
+
// The model uses this to confirm the change landed correctly — same shape
|
|
175
|
+
// claude SDK returns post-edit.
|
|
176
|
+
const replacedCount = replaceAll ? occurrences : 1;
|
|
177
|
+
const preview = buildEditPreview(raw, updated, oldStr, newStr);
|
|
178
|
+
return [
|
|
179
|
+
`File edited: ${filePath} (${replacedCount} replacement${replacedCount === 1 ? "" : "s"})`,
|
|
180
|
+
"",
|
|
181
|
+
preview,
|
|
182
|
+
].join("\n");
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/** Backwards-compatible singleton (no tracker — Read-before-Edit gate off). */
|
|
187
|
+
export const editTool = createEditTool();
|
|
188
|
+
/**
|
|
189
|
+
* Count non-overlapping occurrences of `needle` in `haystack`. We use this
|
|
190
|
+
* to drive the unique-match check; `indexOf` in a loop is fast enough for
|
|
191
|
+
* the 10MB cap and avoids the regex escaping the model would have to do.
|
|
192
|
+
*/
|
|
193
|
+
export function countOccurrences(haystack, needle) {
|
|
194
|
+
if (needle.length === 0)
|
|
195
|
+
return 0;
|
|
196
|
+
let count = 0;
|
|
197
|
+
let from = 0;
|
|
198
|
+
for (;;) {
|
|
199
|
+
const idx = haystack.indexOf(needle, from);
|
|
200
|
+
if (idx === -1)
|
|
201
|
+
return count;
|
|
202
|
+
count++;
|
|
203
|
+
from = idx + needle.length;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Build a line-numbered preview of the changed region. Locates the first
|
|
208
|
+
* occurrence of `newStr` in `updated`, shows ±N lines around it. Empty
|
|
209
|
+
* preview when we can't locate the change (defensive — never throws).
|
|
210
|
+
*/
|
|
211
|
+
function buildEditPreview(_original, updated, _oldStr, newStr) {
|
|
212
|
+
if (newStr.length === 0)
|
|
213
|
+
return "[change preview unavailable: replacement is empty]";
|
|
214
|
+
const idx = updated.indexOf(newStr);
|
|
215
|
+
if (idx < 0)
|
|
216
|
+
return "";
|
|
217
|
+
const allLines = updated.split("\n");
|
|
218
|
+
// Find the line containing the start of newStr.
|
|
219
|
+
let acc = 0;
|
|
220
|
+
let changedLine = 0;
|
|
221
|
+
for (let i = 0; i < allLines.length; i++) {
|
|
222
|
+
const next = acc + allLines[i].length + 1; // +1 for the dropped \n
|
|
223
|
+
if (next > idx) {
|
|
224
|
+
changedLine = i;
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
acc = next;
|
|
228
|
+
}
|
|
229
|
+
const start = Math.max(0, changedLine - PREVIEW_LINES_AROUND);
|
|
230
|
+
const end = Math.min(allLines.length, changedLine + PREVIEW_LINES_AROUND + 1);
|
|
231
|
+
return allLines
|
|
232
|
+
.slice(start, end)
|
|
233
|
+
.map((line, i) => `${String(start + i + 1).padStart(6, " ")}\t${line}`)
|
|
234
|
+
.join("\n");
|
|
235
|
+
}
|
|
236
|
+
// Internal exports for tests.
|
|
237
|
+
export const __MAX_FILE_BYTES = MAX_FILE_BYTES;
|
|
238
|
+
//# sourceMappingURL=edit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit.js","sourceRoot":"","sources":["../../../src/tools/builtin/edit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FileStateTracker } from "../../tools/file-state.js";
|
|
2
|
+
import type { ToolHandler } from "../../tools/registry.js";
|
|
3
|
+
export interface ReadToolOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Optional file-state tracker. When provided, a successful Read records
|
|
6
|
+
* the path's mtime + size so a subsequent Edit can verify the file hasn't
|
|
7
|
+
* drifted since (Read-before-Edit). Omit for standalone uses — the tool
|
|
8
|
+
* still works, the Edit gate just won't fire.
|
|
9
|
+
*/
|
|
10
|
+
tracker?: FileStateTracker;
|
|
11
|
+
}
|
|
12
|
+
export declare function createReadTool(opts?: ReadToolOptions): ToolHandler;
|
|
13
|
+
/** Backwards-compatible singleton (no tracker). */
|
|
14
|
+
export declare const readTool: ToolHandler;
|
|
15
|
+
export declare const __MAX_FILE_BYTES: number;
|
|
16
|
+
export declare const __DEFAULT_LINE_LIMIT = 2000;
|
|
17
|
+
//# sourceMappingURL=read.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../src/tools/builtin/read.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAkCpD,MAAM,WAAW,eAAe;IAC9B;;;;;OAKG;IACH,OAAO,CAAC,EAAE,gBAAgB,CAAC;CAC5B;AAED,wBAAgB,cAAc,CAAC,IAAI,GAAE,eAAoB,GAAG,WAAW,CAmGtE;AAED,mDAAmD;AACnD,eAAO,MAAM,QAAQ,EAAE,WAA8B,CAAC;AAUtD,eAAO,MAAM,gBAAgB,QAAiB,CAAC;AAC/C,eAAO,MAAM,oBAAoB,OAAqB,CAAC"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { isAbsolute } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* NOTE: filesystem sandboxing now lives in the `sandbox-fs` PreToolUse hook.
|
|
5
|
+
* Standalone callers (tests, scripts) without the registry get NO sandbox.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Read builtin — claude SDK `Read` tool parity for maestro.
|
|
9
|
+
*
|
|
10
|
+
* Mirrors the upstream claude-agent-sdk Read tool's name + input schema so the
|
|
11
|
+
* model's pretrained instinct about how to call it transfers cleanly. The
|
|
12
|
+
* line-numbered output format (` 1\t<content>`) is the same one claude SDK
|
|
13
|
+
* emits, which means we benefit from prompt caching when the same Read result
|
|
14
|
+
* recurs across turns (e.g. the model re-reads the same file).
|
|
15
|
+
*
|
|
16
|
+
* Bounds:
|
|
17
|
+
* - file_path must be absolute (matches claude SDK contract — relative paths
|
|
18
|
+
* are rejected so the model never gets surprised by an ambiguous cwd).
|
|
19
|
+
* - 10MB hard cap on file size — claude SDK has the same ceiling. We bail
|
|
20
|
+
* BEFORE reading bytes so a 1GB log doesn't blow heap.
|
|
21
|
+
* - 2000-line default cap when `limit` is omitted. Claude SDK uses this
|
|
22
|
+
* same default; without it a 50K-line file would dump the whole thing into
|
|
23
|
+
* the model's context.
|
|
24
|
+
* - `offset` is 1-based line number (claude SDK convention, NOT byte offset).
|
|
25
|
+
*
|
|
26
|
+
* Returns the line-numbered string on success or `JSON.stringify({error})`
|
|
27
|
+
* for every failure mode — matches `bashTool`'s convention so the model sees
|
|
28
|
+
* structured errors it can react to.
|
|
29
|
+
*/
|
|
30
|
+
const MAX_FILE_BYTES = 10 * 1024 * 1024; // 10MB
|
|
31
|
+
const DEFAULT_LINE_LIMIT = 2000;
|
|
32
|
+
export function createReadTool(opts = {}) {
|
|
33
|
+
const { tracker } = opts;
|
|
34
|
+
return {
|
|
35
|
+
parallelSafe: true,
|
|
36
|
+
schema: {
|
|
37
|
+
name: "Read",
|
|
38
|
+
description: "Read a file from the local filesystem. Returns line-numbered content. " +
|
|
39
|
+
"file_path must be absolute. Optional offset (1-based line number) and " +
|
|
40
|
+
"limit narrow the slice; without limit at most 2000 lines are returned. " +
|
|
41
|
+
"Files larger than 10MB are rejected — use the bash tool with head/tail for huge logs.",
|
|
42
|
+
input_schema: {
|
|
43
|
+
type: "object",
|
|
44
|
+
properties: {
|
|
45
|
+
file_path: {
|
|
46
|
+
type: "string",
|
|
47
|
+
description: "Absolute path to the file. Relative paths are rejected.",
|
|
48
|
+
},
|
|
49
|
+
offset: {
|
|
50
|
+
type: "number",
|
|
51
|
+
description: "1-based line number to start reading from. Defaults to 1.",
|
|
52
|
+
},
|
|
53
|
+
limit: {
|
|
54
|
+
type: "number",
|
|
55
|
+
description: "Maximum number of lines to return. Defaults to 2000.",
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
required: ["file_path"],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
async execute(input) {
|
|
62
|
+
const filePath = typeof input.file_path === "string" ? input.file_path : "";
|
|
63
|
+
if (!filePath) {
|
|
64
|
+
return JSON.stringify({ error: "Read: missing 'file_path' argument" });
|
|
65
|
+
}
|
|
66
|
+
if (!isAbsolute(filePath)) {
|
|
67
|
+
return JSON.stringify({
|
|
68
|
+
error: `Read: file_path must be absolute, got '${filePath}'`,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (!existsSync(filePath)) {
|
|
72
|
+
return JSON.stringify({ error: `Read: file does not exist: ${filePath}` });
|
|
73
|
+
}
|
|
74
|
+
let stat;
|
|
75
|
+
try {
|
|
76
|
+
stat = statSync(filePath);
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
return JSON.stringify({
|
|
80
|
+
error: `Read: stat failed: ${e instanceof Error ? e.message : String(e)}`,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
if (stat.isDirectory()) {
|
|
84
|
+
return JSON.stringify({
|
|
85
|
+
error: `Read: '${filePath}' is a directory, not a file. Use bash 'ls' to list directories.`,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (stat.size > MAX_FILE_BYTES) {
|
|
89
|
+
return JSON.stringify({
|
|
90
|
+
error: `Read: file size ${stat.size} exceeds 10MB cap. Use bash head/tail for large files.`,
|
|
91
|
+
size: stat.size,
|
|
92
|
+
cap: MAX_FILE_BYTES,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
let raw;
|
|
96
|
+
try {
|
|
97
|
+
raw = readFileSync(filePath, "utf-8");
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
return JSON.stringify({
|
|
101
|
+
error: `Read: read failed: ${e instanceof Error ? e.message : String(e)}`,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// Record post-stat state for the Read-before-Edit gate. We record even
|
|
105
|
+
// when the model paginated (offset/limit) — the gate cares about whether
|
|
106
|
+
// a Read was performed, not which slice was requested.
|
|
107
|
+
tracker?.recordRead(filePath, stat.mtimeMs, stat.size);
|
|
108
|
+
// Anthropic's Read result encodes lines as ` <n>\t<content>` where
|
|
109
|
+
// `<n>` is right-aligned in a 6-char field. Matching the format exactly
|
|
110
|
+
// keeps prompt-cache + pretrained intuition intact.
|
|
111
|
+
const offset = clampPositive(input.offset, 1);
|
|
112
|
+
const limit = clampPositive(input.limit, DEFAULT_LINE_LIMIT);
|
|
113
|
+
const allLines = raw.split("\n");
|
|
114
|
+
const start = Math.max(0, offset - 1);
|
|
115
|
+
const end = Math.min(allLines.length, start + limit);
|
|
116
|
+
const slice = allLines.slice(start, end);
|
|
117
|
+
const formatted = slice
|
|
118
|
+
.map((line, i) => {
|
|
119
|
+
const lineNum = start + i + 1;
|
|
120
|
+
return `${String(lineNum).padStart(6, " ")}\t${line}`;
|
|
121
|
+
})
|
|
122
|
+
.join("\n");
|
|
123
|
+
return formatted;
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
/** Backwards-compatible singleton (no tracker). */
|
|
128
|
+
export const readTool = createReadTool();
|
|
129
|
+
/** Coerce a value to a positive integer, falling back to `fallback` for
|
|
130
|
+
* missing / non-numeric / non-positive inputs. */
|
|
131
|
+
function clampPositive(v, fallback) {
|
|
132
|
+
if (typeof v !== "number" || !Number.isFinite(v) || v < 1)
|
|
133
|
+
return fallback;
|
|
134
|
+
return Math.floor(v);
|
|
135
|
+
}
|
|
136
|
+
// Internal exports for tests.
|
|
137
|
+
export const __MAX_FILE_BYTES = MAX_FILE_BYTES;
|
|
138
|
+
export const __DEFAULT_LINE_LIMIT = DEFAULT_LINE_LIMIT;
|
|
139
|
+
//# sourceMappingURL=read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../../src/tools/builtin/read.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check whether `filePath` is allowed by the sandbox. Returns null on allow,
|
|
3
|
+
* or an error message string on deny. Callers stringify the error into a
|
|
4
|
+
* `{error}` payload so the model sees a structured rejection.
|
|
5
|
+
*
|
|
6
|
+
* The path is normalized (`..` collapsed) but symlinks are NOT followed.
|
|
7
|
+
* Caller is responsible for ensuring `filePath` is already absolute — the
|
|
8
|
+
* Read/Write/Edit tools enforce that at the top of `execute`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function checkFilesystemAccess(filePath: string): string | null;
|
|
11
|
+
/** Whether the sandbox is currently active. Default is OFF; the operator
|
|
12
|
+
* opts in by exporting `MAESTRO_FS_SANDBOX_ENABLED=1`. Read each call so a
|
|
13
|
+
* test can toggle it without restarting the process. */
|
|
14
|
+
export declare function isSandboxEnabled(): boolean;
|
|
15
|
+
export declare const __ENV_ENABLED = "MAESTRO_FS_SANDBOX_ENABLED";
|
|
16
|
+
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../../src/tools/builtin/sandbox.ts"],"names":[],"mappings":"AA8BA;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYrE;AAED;;yDAEyD;AACzD,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED,eAAO,MAAM,aAAa,+BAAc,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { resolve as resolvePath } from "node:path";
|
|
2
|
+
import { WORKSPACE_DIR } from "../../platform/config.js";
|
|
3
|
+
/**
|
|
4
|
+
* Maestro builtin filesystem sandbox.
|
|
5
|
+
*
|
|
6
|
+
* Optional gate Read/Write/Edit (and any PreToolUse hook that consults it)
|
|
7
|
+
* can use to constrain file access to `${WORKSPACE_DIR}`. Default is
|
|
8
|
+
* **disabled** — claude/codex providers grant unconstrained FS access via
|
|
9
|
+
* `bypassPermissions` / `danger-full-access`, and forcing a stricter posture
|
|
10
|
+
* on maestro alone silently breaks any workflow that legitimately reaches
|
|
11
|
+
* outside the workspace (reading `~/.config`, writing into a sibling repo,
|
|
12
|
+
* etc.). The single-tenant Mac deployments maestro targets trust the model
|
|
13
|
+
* with the whole UID anyway.
|
|
14
|
+
*
|
|
15
|
+
* Opt-in: set `MAESTRO_FS_SANDBOX_ENABLED=1` to enforce. Only paths under
|
|
16
|
+
* `${WORKSPACE_DIR}` (`~/claude-code-workspace`) are allowed when enabled;
|
|
17
|
+
* system paths (`~/.ssh`, `/etc`, `/usr`, sibling clawgram clones, etc.)
|
|
18
|
+
* are rejected. Useful for multi-tenant or hardened setups where the
|
|
19
|
+
* model should not be trusted with arbitrary FS access.
|
|
20
|
+
*
|
|
21
|
+
* Symlink note: we resolve `..` segments via `path.resolve` but do NOT
|
|
22
|
+
* follow symlinks (no `realpathSync`) — a symlink inside the workspace
|
|
23
|
+
* pointing OUT is still considered inside (the link target is what gets
|
|
24
|
+
* written/read, and the user explicitly placed that link there). Tightening
|
|
25
|
+
* this is a follow-up if it ever matters.
|
|
26
|
+
*/
|
|
27
|
+
const ENV_ENABLED = "MAESTRO_FS_SANDBOX_ENABLED";
|
|
28
|
+
/**
|
|
29
|
+
* Check whether `filePath` is allowed by the sandbox. Returns null on allow,
|
|
30
|
+
* or an error message string on deny. Callers stringify the error into a
|
|
31
|
+
* `{error}` payload so the model sees a structured rejection.
|
|
32
|
+
*
|
|
33
|
+
* The path is normalized (`..` collapsed) but symlinks are NOT followed.
|
|
34
|
+
* Caller is responsible for ensuring `filePath` is already absolute — the
|
|
35
|
+
* Read/Write/Edit tools enforce that at the top of `execute`.
|
|
36
|
+
*/
|
|
37
|
+
export function checkFilesystemAccess(filePath) {
|
|
38
|
+
if (!isSandboxEnabled())
|
|
39
|
+
return null;
|
|
40
|
+
const resolved = resolvePath(filePath);
|
|
41
|
+
// Allow path === root and any descendant. The separator suffix avoids
|
|
42
|
+
// `WORKSPACE_DIR-sibling` style false-allows.
|
|
43
|
+
if (resolved === WORKSPACE_DIR)
|
|
44
|
+
return null;
|
|
45
|
+
const prefix = WORKSPACE_DIR.endsWith("/") ? WORKSPACE_DIR : `${WORKSPACE_DIR}/`;
|
|
46
|
+
if (resolved.startsWith(prefix))
|
|
47
|
+
return null;
|
|
48
|
+
return (`Sandbox: path '${filePath}' is outside the workspace root (${WORKSPACE_DIR}). ` +
|
|
49
|
+
`Unset ${ENV_ENABLED} or scope the operation under the workspace.`);
|
|
50
|
+
}
|
|
51
|
+
/** Whether the sandbox is currently active. Default is OFF; the operator
|
|
52
|
+
* opts in by exporting `MAESTRO_FS_SANDBOX_ENABLED=1`. Read each call so a
|
|
53
|
+
* test can toggle it without restarting the process. */
|
|
54
|
+
export function isSandboxEnabled() {
|
|
55
|
+
return process.env[ENV_ENABLED] === "1";
|
|
56
|
+
}
|
|
57
|
+
export const __ENV_ENABLED = ENV_ENABLED;
|
|
58
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../../src/tools/builtin/sandbox.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,MAAM,WAAW,GAAG,4BAA4B,CAAC;AAEjD;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,IAAI,CAAC,gBAAgB,EAAE;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IACvC,sEAAsE;IACtE,8CAA8C;IAC9C,IAAI,QAAQ,KAAK,aAAa;QAAE,OAAO,IAAI,CAAC;IAC5C,MAAM,MAAM,GAAG,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,GAAG,CAAC;IACjF,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7C,OAAO,CACL,kBAAkB,QAAQ,oCAAoC,aAAa,KAAK;QAChF,SAAS,WAAW,8CAA8C,CACnE,CAAC;AACJ,CAAC;AAED;;yDAEyD;AACzD,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,GAAG,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,WAAW,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type SkillEntry } from "../../skills/loader.js";
|
|
2
|
+
import type { ToolHandler } from "../../tools/registry.js";
|
|
3
|
+
/**
|
|
4
|
+
* `skill_view` builtin — progressive-disclosure entry point for skills.
|
|
5
|
+
*
|
|
6
|
+
* The Maestro system prompt advertises the catalog (name + 60-char summary).
|
|
7
|
+
* When the model decides a skill is relevant, it calls `skill_view(name=...)`
|
|
8
|
+
* to load the full SKILL.md body. We:
|
|
9
|
+
*
|
|
10
|
+
* 1. Look up the entry from the loaded skill set (case-insensitive
|
|
11
|
+
* fallback handles model lowercasing).
|
|
12
|
+
* 2. Strip the YAML frontmatter so the model sees only the markdown body.
|
|
13
|
+
* 3. Apply `preprocessSkillContent` so `${MAESTRO_SKILL_DIR}` /
|
|
14
|
+
* `${MAESTRO_SESSION_ID}` resolve to live values before the body is
|
|
15
|
+
* handed to the model.
|
|
16
|
+
* 4. Prepend a `[Skill directory: ...]` hint so the model can resolve
|
|
17
|
+
* relative paths in the body against the right cwd without needing to
|
|
18
|
+
* ask. (Matches upstream `_build_skill_message` behavior.)
|
|
19
|
+
*
|
|
20
|
+
* Why a factory (not a module singleton): the loaded skill set + session
|
|
21
|
+
* id come from `maestroProvider` per turn, and we don't want a stale cache
|
|
22
|
+
* from a previous user's session leaking into this one. The factory closes
|
|
23
|
+
* over the per-turn skill list and session id, so multi-user safety falls
|
|
24
|
+
* out for free.
|
|
25
|
+
*/
|
|
26
|
+
export interface SkillViewToolOptions {
|
|
27
|
+
/** Already-loaded skill set for this turn. Comes from `loadSkills(rootDir)`. */
|
|
28
|
+
skills: SkillEntry[];
|
|
29
|
+
/** Active Maestro session id for `${MAESTRO_SESSION_ID}` substitution. */
|
|
30
|
+
sessionId?: string;
|
|
31
|
+
/** When true, enable `!\`cmd\`` inline-shell expansion during preprocessing.
|
|
32
|
+
* Off by default — matches upstream's `skills.inline_shell: false` default
|
|
33
|
+
* so an untrusted SKILL.md can't run code at load time. */
|
|
34
|
+
inlineShell?: boolean;
|
|
35
|
+
}
|
|
36
|
+
export declare function createSkillViewTool(opts: SkillViewToolOptions): ToolHandler;
|
|
37
|
+
//# sourceMappingURL=skill_view.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill_view.d.ts","sourceRoot":"","sources":["../../../src/tools/builtin/skill_view.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGnE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,oBAAoB;IACnC,gFAAgF;IAChF,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;gEAE4D;IAC5D,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,oBAAoB,GAAG,WAAW,CAuE3E"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { findSkillByName } from "../../skills/loader.js";
|
|
2
|
+
import { preprocessSkillContent } from "../../skills/preprocess.js";
|
|
3
|
+
import { bumpView } from "../../skills/usage.js";
|
|
4
|
+
import { logger } from "../../platform/logger.js";
|
|
5
|
+
export function createSkillViewTool(opts) {
|
|
6
|
+
return {
|
|
7
|
+
// Pure catalog lookup + file read. `bumpView` mutates a usage counter
|
|
8
|
+
// on disk but it's idempotent and independent across skills, so parallel
|
|
9
|
+
// views never corrupt each other.
|
|
10
|
+
parallelSafe: true,
|
|
11
|
+
schema: {
|
|
12
|
+
name: "skill_view",
|
|
13
|
+
description: "Load the full SKILL.md body for a single skill by name. " +
|
|
14
|
+
"Call this whenever the Skills (mandatory) catalog lists a skill " +
|
|
15
|
+
"that matches or is partially relevant to your task — the body " +
|
|
16
|
+
"carries the actual commands, pitfalls, and workflows the index " +
|
|
17
|
+
"only summarizes.",
|
|
18
|
+
input_schema: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
name: {
|
|
22
|
+
type: "string",
|
|
23
|
+
description: "Skill name as shown in the <available_skills> list.",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
required: ["name"],
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
async execute(input) {
|
|
30
|
+
const wanted = typeof input.name === "string" ? input.name : "";
|
|
31
|
+
if (!wanted.trim()) {
|
|
32
|
+
return JSON.stringify({ error: "skill_view: missing 'name' argument" });
|
|
33
|
+
}
|
|
34
|
+
const entry = findSkillByName(opts.skills, wanted);
|
|
35
|
+
if (!entry) {
|
|
36
|
+
return JSON.stringify({
|
|
37
|
+
error: `skill_view: no skill named '${wanted}' in the loaded catalog`,
|
|
38
|
+
available: opts.skills.map((s) => s.name),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// Strip the leading `---\nYAML\n---\n` block — the model never needs
|
|
42
|
+
// to see the raw frontmatter, and including it wastes tokens.
|
|
43
|
+
const body = stripFrontmatter(entry.raw);
|
|
44
|
+
const resolved = preprocessSkillContent(body, {
|
|
45
|
+
skillDir: entry.skillDir,
|
|
46
|
+
sessionId: opts.sessionId ?? null,
|
|
47
|
+
inlineShell: opts.inlineShell ?? false,
|
|
48
|
+
});
|
|
49
|
+
const parts = [];
|
|
50
|
+
parts.push(`# ${entry.name}`);
|
|
51
|
+
if (entry.description)
|
|
52
|
+
parts.push(entry.description);
|
|
53
|
+
parts.push("");
|
|
54
|
+
parts.push(`[Skill directory: ${entry.skillDir}]`);
|
|
55
|
+
parts.push("Resolve any relative paths in this skill (e.g. `scripts/foo.js`, `templates/x.yaml`) " +
|
|
56
|
+
"against that directory, then run them with the bash tool using the absolute path.");
|
|
57
|
+
parts.push("");
|
|
58
|
+
parts.push(resolved.trim());
|
|
59
|
+
// Telemetry: bump the usage sidecar so the Curator can later spot
|
|
60
|
+
// skills the model never pulls. Fire-and-forget — a failed write
|
|
61
|
+
// (disk full / permission) must never break the tool call. We log
|
|
62
|
+
// the rejection at debug so operators can investigate if counters
|
|
63
|
+
// mysteriously stay at zero.
|
|
64
|
+
bumpView(entry.name).catch((err) => {
|
|
65
|
+
logger.debug({ err, skill: entry.name }, "skill_view: usage bump failed (continuing)");
|
|
66
|
+
});
|
|
67
|
+
return parts.join("\n");
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/** Drop the leading `---\n...\n---\n` YAML block. No-op when the file has no
|
|
72
|
+
* frontmatter (defensive — SKILL.md files in the wild are usually well-formed
|
|
73
|
+
* but a malformed entry shouldn't crash the load). */
|
|
74
|
+
function stripFrontmatter(raw) {
|
|
75
|
+
if (!raw.startsWith("---"))
|
|
76
|
+
return raw;
|
|
77
|
+
const closeMatch = /\n---[ \t]*(?:\n|$)/.exec(raw.slice(3));
|
|
78
|
+
if (!closeMatch)
|
|
79
|
+
return raw;
|
|
80
|
+
return raw.slice(closeMatch.index + 3 + closeMatch[0].length);
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=skill_view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill_view.js","sourceRoot":"","sources":["../../../src/tools/builtin/skill_view.ts"],"names":[],"mappings":"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"}
|