@dexto/agent-management 1.6.0 → 1.6.2
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/dist/agent-creation.cjs +2 -1
- package/dist/agent-creation.d.ts.map +1 -1
- package/dist/agent-creation.js +2 -1
- package/dist/config/config-enrichment.cjs +8 -3
- package/dist/config/config-enrichment.d.ts +6 -0
- package/dist/config/config-enrichment.d.ts.map +1 -1
- package/dist/config/config-enrichment.js +8 -3
- package/dist/index.cjs +3 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/models/custom-models.cjs +4 -3
- package/dist/models/custom-models.d.ts +26 -5
- package/dist/models/custom-models.d.ts.map +1 -1
- package/dist/models/custom-models.js +4 -3
- package/dist/preferences/loader.cjs +23 -8
- package/dist/preferences/loader.d.ts +7 -3
- package/dist/preferences/loader.d.ts.map +1 -1
- package/dist/preferences/loader.js +23 -8
- package/dist/preferences/schemas.cjs +47 -12
- package/dist/preferences/schemas.d.ts +92 -12
- package/dist/preferences/schemas.d.ts.map +1 -1
- package/dist/preferences/schemas.js +48 -8
- package/dist/registry/types.cjs +4 -0
- package/dist/registry/types.d.ts +8 -0
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/registry/types.js +4 -0
- package/dist/runtime/AgentRuntime.cjs +23 -15
- package/dist/runtime/AgentRuntime.d.ts.map +1 -1
- package/dist/runtime/AgentRuntime.js +23 -15
- package/dist/runtime/approval-delegation.cjs +6 -3
- package/dist/runtime/approval-delegation.d.ts +2 -1
- package/dist/runtime/approval-delegation.d.ts.map +1 -1
- package/dist/runtime/approval-delegation.js +6 -3
- package/dist/runtime/schemas.cjs +1 -1
- package/dist/runtime/schemas.js +1 -1
- package/dist/runtime/types.d.ts +1 -1
- package/dist/runtime/types.d.ts.map +1 -1
- package/dist/tool-factories/agent-spawner/factory.cjs +63 -26
- package/dist/tool-factories/agent-spawner/factory.d.ts.map +1 -1
- package/dist/tool-factories/agent-spawner/factory.js +61 -24
- package/dist/tool-factories/agent-spawner/runtime.cjs +166 -15
- package/dist/tool-factories/agent-spawner/runtime.d.ts +8 -1
- package/dist/tool-factories/agent-spawner/runtime.d.ts.map +1 -1
- package/dist/tool-factories/agent-spawner/runtime.js +173 -17
- package/dist/tool-factories/agent-spawner/schemas.cjs +14 -2
- package/dist/tool-factories/agent-spawner/schemas.d.ts +10 -1
- package/dist/tool-factories/agent-spawner/schemas.d.ts.map +1 -1
- package/dist/tool-factories/agent-spawner/schemas.js +12 -2
- package/dist/tool-factories/agent-spawner/spawn-agent-tool.cjs +10 -1
- package/dist/tool-factories/agent-spawner/spawn-agent-tool.d.ts.map +1 -1
- package/dist/tool-factories/agent-spawner/spawn-agent-tool.js +10 -1
- package/dist/tool-factories/creator-tools/factory.cjs +434 -0
- package/dist/tool-factories/creator-tools/factory.d.ts +4 -0
- package/dist/tool-factories/creator-tools/factory.d.ts.map +1 -0
- package/dist/tool-factories/creator-tools/factory.js +407 -0
- package/dist/tool-factories/creator-tools/index.cjs +33 -0
- package/dist/tool-factories/creator-tools/index.d.ts +3 -0
- package/dist/tool-factories/creator-tools/index.d.ts.map +1 -0
- package/dist/tool-factories/creator-tools/index.js +10 -0
- package/dist/tool-factories/creator-tools/schemas.cjs +41 -0
- package/dist/tool-factories/creator-tools/schemas.d.ts +15 -0
- package/dist/tool-factories/creator-tools/schemas.d.ts.map +1 -0
- package/dist/tool-factories/creator-tools/schemas.js +16 -0
- package/dist/utils/path.cjs +10 -1
- package/dist/utils/path.d.ts +5 -2
- package/dist/utils/path.d.ts.map +1 -1
- package/dist/utils/path.js +10 -1
- package/package.json +6 -6
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parse as yamlParse } from "yaml";
|
|
4
|
+
import {
|
|
5
|
+
ToolError,
|
|
6
|
+
defineTool,
|
|
7
|
+
assertValidPromptName
|
|
8
|
+
} from "@dexto/core";
|
|
9
|
+
import { discoverStandaloneSkills, getSkillSearchPaths } from "../../plugins/discover-skills.js";
|
|
10
|
+
import {
|
|
11
|
+
CREATOR_TOOL_NAMES,
|
|
12
|
+
CreatorToolsConfigSchema
|
|
13
|
+
} from "./schemas.js";
|
|
14
|
+
import { getDextoGlobalPath } from "../../utils/path.js";
|
|
15
|
+
import { z } from "zod";
|
|
16
|
+
const SkillCreateInputSchema = z.object({
|
|
17
|
+
id: z.string().min(1).describe("Skill id (kebab-case)."),
|
|
18
|
+
description: z.string().min(1).describe("Short description of what the skill does."),
|
|
19
|
+
content: z.string().min(1).describe("Skill body (markdown) without frontmatter."),
|
|
20
|
+
allowedTools: z.array(z.string().min(1)).optional().describe("Optional allowed-tools list for the skill frontmatter."),
|
|
21
|
+
toolkits: z.array(z.string().min(1)).optional().describe("Optional toolkits list for the skill frontmatter."),
|
|
22
|
+
scope: z.enum(["global", "workspace"]).optional(),
|
|
23
|
+
overwrite: z.boolean().optional()
|
|
24
|
+
}).strict();
|
|
25
|
+
const SkillUpdateInputSchema = z.object({
|
|
26
|
+
id: z.string().min(1),
|
|
27
|
+
content: z.string().min(1).describe("New SKILL.md body (markdown) without frontmatter."),
|
|
28
|
+
description: z.string().min(1).optional(),
|
|
29
|
+
allowedTools: z.array(z.string().min(1)).optional().describe("Optional allowed-tools list for the skill frontmatter."),
|
|
30
|
+
toolkits: z.array(z.string().min(1)).optional().describe("Optional toolkits list for the skill frontmatter."),
|
|
31
|
+
scope: z.enum(["global", "workspace"]).optional()
|
|
32
|
+
}).strict();
|
|
33
|
+
const SkillListInputSchema = z.object({
|
|
34
|
+
projectPath: z.string().optional(),
|
|
35
|
+
query: z.string().optional().describe("Optional search term to filter skills by name or path")
|
|
36
|
+
}).strict();
|
|
37
|
+
const SkillSearchInputSchema = z.object({
|
|
38
|
+
query: z.string().optional().describe("Optional search term to filter skills by name or description"),
|
|
39
|
+
limit: z.number().int().min(1).max(200).optional().describe(
|
|
40
|
+
"Maximum number of skills to return. If omitted, all matches are returned for queries; otherwise defaults to 50."
|
|
41
|
+
)
|
|
42
|
+
}).strict();
|
|
43
|
+
const ToolCatalogInputSchema = z.object({
|
|
44
|
+
query: z.string().optional().describe("Optional search term to filter tools by id or description"),
|
|
45
|
+
limit: z.number().int().min(1).max(500).optional().describe("Maximum number of tools to return (defaults to all)."),
|
|
46
|
+
includeDescriptions: z.boolean().optional().describe("Include tool descriptions (defaults to true).")
|
|
47
|
+
}).strict();
|
|
48
|
+
function normalizeSkillQuery(value) {
|
|
49
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
50
|
+
}
|
|
51
|
+
function matchesSkillQuery(value, query) {
|
|
52
|
+
if (!value) return false;
|
|
53
|
+
const normalizedQuery = normalizeSkillQuery(query);
|
|
54
|
+
if (!normalizedQuery) return false;
|
|
55
|
+
return normalizeSkillQuery(value).includes(normalizedQuery);
|
|
56
|
+
}
|
|
57
|
+
function resolvePromptSkillName(info, id) {
|
|
58
|
+
return info.displayName || info.name || id;
|
|
59
|
+
}
|
|
60
|
+
function resolveWorkspaceBasePath(context) {
|
|
61
|
+
const workspacePath = context.workspace?.path;
|
|
62
|
+
const fallbackWorkingDir = context.services ? context.services.filesystemService?.getConfig().workingDirectory : void 0;
|
|
63
|
+
return workspacePath || fallbackWorkingDir || process.cwd();
|
|
64
|
+
}
|
|
65
|
+
function resolveWorkspaceSkillDirs(context) {
|
|
66
|
+
const base = resolveWorkspaceBasePath(context);
|
|
67
|
+
return {
|
|
68
|
+
primary: path.join(base, ".agents", "skills"),
|
|
69
|
+
legacy: path.join(base, ".dexto", "skills")
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function resolveSkillBaseDirectory(scope, context) {
|
|
73
|
+
if (scope === "global") {
|
|
74
|
+
return { baseDir: getDextoGlobalPath("skills"), scope: "global" };
|
|
75
|
+
}
|
|
76
|
+
return { baseDir: resolveWorkspaceSkillDirs(context).primary, scope: "workspace" };
|
|
77
|
+
}
|
|
78
|
+
function resolveSkillDirectory(input, context) {
|
|
79
|
+
return resolveSkillBaseDirectory(input.scope, context);
|
|
80
|
+
}
|
|
81
|
+
async function pathExists(filePath) {
|
|
82
|
+
return await fs.stat(filePath).then(() => true).catch(() => false);
|
|
83
|
+
}
|
|
84
|
+
async function resolveSkillUpdateDirectory(input, context) {
|
|
85
|
+
if (input.scope === "global") {
|
|
86
|
+
return resolveSkillBaseDirectory("global", context);
|
|
87
|
+
}
|
|
88
|
+
const { primary, legacy } = resolveWorkspaceSkillDirs(context);
|
|
89
|
+
const skillId = input.id.trim();
|
|
90
|
+
const primaryFile = path.join(primary, skillId, "SKILL.md");
|
|
91
|
+
if (await pathExists(primaryFile)) {
|
|
92
|
+
return { baseDir: primary, scope: "workspace" };
|
|
93
|
+
}
|
|
94
|
+
const legacyFile = path.join(legacy, skillId, "SKILL.md");
|
|
95
|
+
if (await pathExists(legacyFile)) {
|
|
96
|
+
return { baseDir: legacy, scope: "workspace" };
|
|
97
|
+
}
|
|
98
|
+
return { baseDir: primary, scope: "workspace" };
|
|
99
|
+
}
|
|
100
|
+
function ensurePathWithinBase(baseDir, targetDir, toolId) {
|
|
101
|
+
const resolvedBase = path.resolve(baseDir);
|
|
102
|
+
const resolvedTarget = path.resolve(targetDir);
|
|
103
|
+
const rel = path.relative(resolvedBase, resolvedTarget);
|
|
104
|
+
if (rel.startsWith("..") || path.isAbsolute(rel)) {
|
|
105
|
+
throw ToolError.validationFailed(toolId, "invalid skill path");
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
function formatFrontmatterLine(key, value) {
|
|
109
|
+
if (typeof value === "string") {
|
|
110
|
+
return `${key}: ${JSON.stringify(value)}`;
|
|
111
|
+
}
|
|
112
|
+
return `${key}: ${value}`;
|
|
113
|
+
}
|
|
114
|
+
function titleizeSkillId(id) {
|
|
115
|
+
return id.split("-").filter(Boolean).map((segment) => segment[0]?.toUpperCase() + segment.slice(1)).join(" ");
|
|
116
|
+
}
|
|
117
|
+
function resolveSkillCreateInput(input) {
|
|
118
|
+
const id = input.id.trim();
|
|
119
|
+
const description = input.description.trim();
|
|
120
|
+
const content = input.content.trim();
|
|
121
|
+
return {
|
|
122
|
+
...input,
|
|
123
|
+
id,
|
|
124
|
+
description,
|
|
125
|
+
content
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function formatFrontmatterList(key, values) {
|
|
129
|
+
const normalized = values.map((value) => JSON.stringify(value.trim()));
|
|
130
|
+
return `${key}: [${normalized.join(", ")}]`;
|
|
131
|
+
}
|
|
132
|
+
function buildSkillMarkdownFromParts(options) {
|
|
133
|
+
const id = options.id.trim();
|
|
134
|
+
const title = titleizeSkillId(id) || id;
|
|
135
|
+
const lines = ["---"];
|
|
136
|
+
lines.push(formatFrontmatterLine("name", id));
|
|
137
|
+
lines.push(formatFrontmatterLine("description", options.description.trim()));
|
|
138
|
+
if (options.toolkits && options.toolkits.length > 0) {
|
|
139
|
+
lines.push(formatFrontmatterList("toolkits", options.toolkits));
|
|
140
|
+
}
|
|
141
|
+
if (options.allowedTools && options.allowedTools.length > 0) {
|
|
142
|
+
lines.push(formatFrontmatterList("allowed-tools", options.allowedTools));
|
|
143
|
+
}
|
|
144
|
+
lines.push("---", "", `# ${title}`, "", options.content.trim());
|
|
145
|
+
return lines.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
146
|
+
}
|
|
147
|
+
function buildSkillMarkdown(input) {
|
|
148
|
+
return buildSkillMarkdownFromParts({
|
|
149
|
+
id: input.id,
|
|
150
|
+
description: input.description,
|
|
151
|
+
content: input.content,
|
|
152
|
+
allowedTools: input.allowedTools,
|
|
153
|
+
toolkits: input.toolkits
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
async function readSkillFrontmatter(skillFile) {
|
|
157
|
+
try {
|
|
158
|
+
const raw = await fs.readFile(skillFile, "utf-8");
|
|
159
|
+
const match = raw.match(/^---\s*\n([\s\S]*?)\n---\s*\n/);
|
|
160
|
+
if (!match) return {};
|
|
161
|
+
const frontmatter = yamlParse(match[1] ?? "");
|
|
162
|
+
if (!frontmatter || typeof frontmatter !== "object") return {};
|
|
163
|
+
const name = typeof frontmatter.name === "string" ? frontmatter.name.trim() : void 0;
|
|
164
|
+
const description = typeof frontmatter.description === "string" ? frontmatter.description.trim() : void 0;
|
|
165
|
+
const allowedToolsRaw = frontmatter["allowed-tools"];
|
|
166
|
+
const toolkitsRaw = frontmatter.toolkits;
|
|
167
|
+
const allowedTools = Array.isArray(allowedToolsRaw) ? allowedToolsRaw.filter((item) => typeof item === "string").map((item) => item.trim()).filter((item) => item.length > 0) : void 0;
|
|
168
|
+
const toolkits = Array.isArray(toolkitsRaw) ? toolkitsRaw.filter((item) => typeof item === "string").map((item) => item.trim()).filter((item) => item.length > 0) : void 0;
|
|
169
|
+
const result = {};
|
|
170
|
+
if (name) result.name = name;
|
|
171
|
+
if (description) result.description = description;
|
|
172
|
+
if (allowedTools && allowedTools.length > 0) result.allowedTools = allowedTools;
|
|
173
|
+
if (toolkits && toolkits.length > 0) result.toolkits = toolkits;
|
|
174
|
+
return result;
|
|
175
|
+
} catch {
|
|
176
|
+
return {};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async function refreshAgentPrompts(context, skillFile) {
|
|
180
|
+
const agent = context.agent;
|
|
181
|
+
if (!agent) return false;
|
|
182
|
+
const effective = agent.getEffectiveConfig();
|
|
183
|
+
const existingPrompts = Array.isArray(effective.prompts) ? [...effective.prompts] : [];
|
|
184
|
+
const alreadyPresent = existingPrompts.some((prompt) => {
|
|
185
|
+
if (!prompt || typeof prompt !== "object") return false;
|
|
186
|
+
const record = prompt;
|
|
187
|
+
return record.type === "file" && record.file === skillFile;
|
|
188
|
+
});
|
|
189
|
+
const nextPrompts = alreadyPresent ? existingPrompts : [...existingPrompts, { type: "file", file: skillFile }];
|
|
190
|
+
await agent.refreshPrompts(nextPrompts);
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
const creatorToolsFactory = {
|
|
194
|
+
configSchema: CreatorToolsConfigSchema,
|
|
195
|
+
metadata: {
|
|
196
|
+
displayName: "Creator Tools",
|
|
197
|
+
description: "Create and manage standalone skills",
|
|
198
|
+
category: "agents"
|
|
199
|
+
},
|
|
200
|
+
create: (config) => {
|
|
201
|
+
const enabledTools = config.enabledTools ?? CREATOR_TOOL_NAMES;
|
|
202
|
+
const skillCreateTool = defineTool({
|
|
203
|
+
id: "skill_create",
|
|
204
|
+
description: "Create a standalone SKILL.md file and register it with the running agent. Provide id, description, content, and optional toolkits/allowedTools.",
|
|
205
|
+
inputSchema: SkillCreateInputSchema,
|
|
206
|
+
execute: async (input, context) => {
|
|
207
|
+
const resolvedInput = resolveSkillCreateInput(input);
|
|
208
|
+
const skillId = resolvedInput.id.trim();
|
|
209
|
+
assertValidPromptName(skillId, {
|
|
210
|
+
context: "skill_create",
|
|
211
|
+
hint: "Use kebab-case skill ids (e.g., release-notes)"
|
|
212
|
+
});
|
|
213
|
+
const { baseDir, scope } = resolveSkillDirectory(resolvedInput, context);
|
|
214
|
+
const skillDir = path.join(baseDir, skillId);
|
|
215
|
+
ensurePathWithinBase(baseDir, skillDir, "skill_create");
|
|
216
|
+
const skillFile = path.join(skillDir, "SKILL.md");
|
|
217
|
+
const exists = await pathExists(skillFile);
|
|
218
|
+
if (exists && !resolvedInput.overwrite) {
|
|
219
|
+
throw ToolError.validationFailed(
|
|
220
|
+
"skill_create",
|
|
221
|
+
`Skill already exists at ${skillFile}`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
const markdown = buildSkillMarkdown(resolvedInput);
|
|
225
|
+
await fs.mkdir(skillDir, { recursive: true });
|
|
226
|
+
await fs.writeFile(skillFile, markdown, "utf-8");
|
|
227
|
+
const refreshed = await refreshAgentPrompts(context, skillFile);
|
|
228
|
+
const displayName = titleizeSkillId(skillId) || skillId;
|
|
229
|
+
return {
|
|
230
|
+
created: true,
|
|
231
|
+
id: skillId,
|
|
232
|
+
name: displayName,
|
|
233
|
+
description: resolvedInput.description.trim(),
|
|
234
|
+
scope,
|
|
235
|
+
path: skillFile,
|
|
236
|
+
promptsRefreshed: refreshed
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
const skillUpdateTool = defineTool({
|
|
241
|
+
id: "skill_update",
|
|
242
|
+
description: "Update an existing standalone SKILL.md file.",
|
|
243
|
+
inputSchema: SkillUpdateInputSchema,
|
|
244
|
+
execute: async (input, context) => {
|
|
245
|
+
const skillId = input.id.trim();
|
|
246
|
+
assertValidPromptName(skillId, {
|
|
247
|
+
context: "skill_update",
|
|
248
|
+
hint: "Use kebab-case skill ids (e.g., release-notes)"
|
|
249
|
+
});
|
|
250
|
+
const { baseDir, scope } = await resolveSkillUpdateDirectory(input, context);
|
|
251
|
+
const skillDir = path.join(baseDir, skillId);
|
|
252
|
+
ensurePathWithinBase(baseDir, skillDir, "skill_update");
|
|
253
|
+
const skillFile = path.join(skillDir, "SKILL.md");
|
|
254
|
+
const exists = await pathExists(skillFile);
|
|
255
|
+
if (!exists) {
|
|
256
|
+
throw ToolError.validationFailed(
|
|
257
|
+
"skill_update",
|
|
258
|
+
`Skill not found at ${skillFile}`
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
const existing = await readSkillFrontmatter(skillFile);
|
|
262
|
+
const description = input.description?.trim() || existing.description;
|
|
263
|
+
if (!description) {
|
|
264
|
+
throw ToolError.validationFailed(
|
|
265
|
+
"skill_update",
|
|
266
|
+
"description is required when the existing skill is missing one"
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
const allowedTools = input.allowedTools !== void 0 ? input.allowedTools : existing.allowedTools;
|
|
270
|
+
const toolkits = input.toolkits !== void 0 ? input.toolkits : existing.toolkits;
|
|
271
|
+
const markdown = buildSkillMarkdownFromParts({
|
|
272
|
+
id: skillId,
|
|
273
|
+
description,
|
|
274
|
+
content: input.content.trim(),
|
|
275
|
+
allowedTools,
|
|
276
|
+
toolkits
|
|
277
|
+
});
|
|
278
|
+
await fs.writeFile(skillFile, markdown, "utf-8");
|
|
279
|
+
const refreshed = await refreshAgentPrompts(context, skillFile);
|
|
280
|
+
return {
|
|
281
|
+
updated: true,
|
|
282
|
+
id: skillId,
|
|
283
|
+
description,
|
|
284
|
+
scope,
|
|
285
|
+
path: skillFile,
|
|
286
|
+
promptsRefreshed: refreshed
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
const skillSearchTool = defineTool({
|
|
291
|
+
id: "skill_search",
|
|
292
|
+
description: "Search loaded skills (supports query).",
|
|
293
|
+
inputSchema: SkillSearchInputSchema,
|
|
294
|
+
execute: async (input, context) => {
|
|
295
|
+
const query = input.query?.trim() ?? "";
|
|
296
|
+
const normalizedQuery = normalizeSkillQuery(query);
|
|
297
|
+
const hasQuery = normalizedQuery.length > 0;
|
|
298
|
+
const limit = input.limit ?? (hasQuery ? void 0 : 50);
|
|
299
|
+
const promptManager = context.services?.prompts;
|
|
300
|
+
if (!promptManager) {
|
|
301
|
+
throw ToolError.configInvalid(
|
|
302
|
+
"skill_search requires ToolExecutionContext.services.prompts"
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
const loaded = await promptManager.list();
|
|
306
|
+
let results = Object.entries(loaded).map(([id, info]) => ({
|
|
307
|
+
id,
|
|
308
|
+
name: resolvePromptSkillName(info, id),
|
|
309
|
+
...info.displayName ? { displayName: info.displayName } : {},
|
|
310
|
+
...info.commandName ? { commandName: info.commandName } : {},
|
|
311
|
+
...info.description ? { description: info.description } : {},
|
|
312
|
+
...info.context ? { context: info.context } : {},
|
|
313
|
+
...info.agent ? { agent: info.agent } : {}
|
|
314
|
+
}));
|
|
315
|
+
if (hasQuery && normalizedQuery) {
|
|
316
|
+
results = results.filter((entry) => {
|
|
317
|
+
if (matchesSkillQuery(entry.id, normalizedQuery)) return true;
|
|
318
|
+
if (matchesSkillQuery(entry.name, normalizedQuery)) return true;
|
|
319
|
+
if (matchesSkillQuery(entry.displayName, normalizedQuery)) return true;
|
|
320
|
+
if (matchesSkillQuery(entry.commandName, normalizedQuery)) return true;
|
|
321
|
+
if (matchesSkillQuery(entry.description, normalizedQuery)) return true;
|
|
322
|
+
return false;
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
results.sort((a, b) => a.name.localeCompare(b.name));
|
|
326
|
+
const limited = typeof limit === "number" ? results.slice(0, limit) : results;
|
|
327
|
+
return {
|
|
328
|
+
query: input.query?.trim(),
|
|
329
|
+
count: limited.length,
|
|
330
|
+
total: results.length,
|
|
331
|
+
skills: limited,
|
|
332
|
+
_hint: limited.length > 0 ? "Use invoke_skill with the skill id or commandName for loaded skills." : "No skills matched the query."
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
const skillListTool = defineTool({
|
|
337
|
+
id: "skill_list",
|
|
338
|
+
description: "List discovered standalone skills and their search paths. Supports optional query filtering.",
|
|
339
|
+
inputSchema: SkillListInputSchema,
|
|
340
|
+
execute: async (input) => {
|
|
341
|
+
const query = input.query?.trim().toLowerCase();
|
|
342
|
+
const skills = discoverStandaloneSkills(input.projectPath);
|
|
343
|
+
const filtered = query ? skills.filter((skill) => {
|
|
344
|
+
if (skill.name.toLowerCase().includes(query)) return true;
|
|
345
|
+
if (skill.path.toLowerCase().includes(query)) return true;
|
|
346
|
+
if (skill.skillFile.toLowerCase().includes(query)) return true;
|
|
347
|
+
return false;
|
|
348
|
+
}) : skills;
|
|
349
|
+
return {
|
|
350
|
+
searchPaths: getSkillSearchPaths(),
|
|
351
|
+
skills: filtered
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
const toolCatalogTool = defineTool({
|
|
356
|
+
id: "tool_catalog",
|
|
357
|
+
description: "List available tools and configured toolkits for the current agent (from the loaded image/config).",
|
|
358
|
+
inputSchema: ToolCatalogInputSchema,
|
|
359
|
+
execute: async (input, context) => {
|
|
360
|
+
const agent = context.agent;
|
|
361
|
+
if (!agent) {
|
|
362
|
+
throw ToolError.configInvalid(
|
|
363
|
+
"tool_catalog requires ToolExecutionContext.agent"
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
const toolSet = await agent.getAllTools();
|
|
367
|
+
let tools = Object.entries(toolSet).map(([id, tool]) => ({
|
|
368
|
+
id,
|
|
369
|
+
description: tool.description || "No description provided",
|
|
370
|
+
source: id.startsWith("mcp--") ? "mcp" : "local"
|
|
371
|
+
}));
|
|
372
|
+
const query = input.query?.trim().toLowerCase();
|
|
373
|
+
if (query) {
|
|
374
|
+
tools = tools.filter((tool) => {
|
|
375
|
+
if (tool.id.toLowerCase().includes(query)) return true;
|
|
376
|
+
if ((tool.description ?? "").toLowerCase().includes(query)) return true;
|
|
377
|
+
return false;
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
tools.sort((a, b) => a.id.localeCompare(b.id));
|
|
381
|
+
const includeDescriptions = input.includeDescriptions !== false;
|
|
382
|
+
if (!includeDescriptions) {
|
|
383
|
+
tools = tools.map((tool) => ({ id: tool.id, source: tool.source }));
|
|
384
|
+
}
|
|
385
|
+
const limited = typeof input.limit === "number" ? tools.slice(0, input.limit) : tools;
|
|
386
|
+
return {
|
|
387
|
+
query: input.query?.trim(),
|
|
388
|
+
count: limited.length,
|
|
389
|
+
total: tools.length,
|
|
390
|
+
tools: limited,
|
|
391
|
+
_hint: limited.length > 0 ? "Use tool ids in allowed-tools. Use toolkits from the agent config or image defaults." : "No tools matched the query."
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
const toolCreators = {
|
|
396
|
+
skill_create: () => skillCreateTool,
|
|
397
|
+
skill_update: () => skillUpdateTool,
|
|
398
|
+
skill_search: () => skillSearchTool,
|
|
399
|
+
skill_list: () => skillListTool,
|
|
400
|
+
tool_catalog: () => toolCatalogTool
|
|
401
|
+
};
|
|
402
|
+
return enabledTools.map((toolName) => toolCreators[toolName]());
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
export {
|
|
406
|
+
creatorToolsFactory
|
|
407
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var creator_tools_exports = {};
|
|
20
|
+
__export(creator_tools_exports, {
|
|
21
|
+
CREATOR_TOOL_NAMES: () => import_schemas.CREATOR_TOOL_NAMES,
|
|
22
|
+
CreatorToolsConfigSchema: () => import_schemas.CreatorToolsConfigSchema,
|
|
23
|
+
creatorToolsFactory: () => import_factory.creatorToolsFactory
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(creator_tools_exports);
|
|
26
|
+
var import_factory = require("./factory.js");
|
|
27
|
+
var import_schemas = require("./schemas.js");
|
|
28
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
29
|
+
0 && (module.exports = {
|
|
30
|
+
CREATOR_TOOL_NAMES,
|
|
31
|
+
CreatorToolsConfigSchema,
|
|
32
|
+
creatorToolsFactory
|
|
33
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tool-factories/creator-tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EACH,wBAAwB,EACxB,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,KAAK,eAAe,GACvB,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var schemas_exports = {};
|
|
20
|
+
__export(schemas_exports, {
|
|
21
|
+
CREATOR_TOOL_NAMES: () => CREATOR_TOOL_NAMES,
|
|
22
|
+
CreatorToolsConfigSchema: () => CreatorToolsConfigSchema
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(schemas_exports);
|
|
25
|
+
var import_zod = require("zod");
|
|
26
|
+
const CREATOR_TOOL_NAMES = [
|
|
27
|
+
"skill_create",
|
|
28
|
+
"skill_update",
|
|
29
|
+
"skill_search",
|
|
30
|
+
"skill_list",
|
|
31
|
+
"tool_catalog"
|
|
32
|
+
];
|
|
33
|
+
const CreatorToolsConfigSchema = import_zod.z.object({
|
|
34
|
+
type: import_zod.z.literal("creator-tools"),
|
|
35
|
+
enabledTools: import_zod.z.array(import_zod.z.enum(CREATOR_TOOL_NAMES)).optional().describe("Subset of creator tools to enable")
|
|
36
|
+
}).strict();
|
|
37
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
38
|
+
0 && (module.exports = {
|
|
39
|
+
CREATOR_TOOL_NAMES,
|
|
40
|
+
CreatorToolsConfigSchema
|
|
41
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const CREATOR_TOOL_NAMES: readonly ["skill_create", "skill_update", "skill_search", "skill_list", "tool_catalog"];
|
|
3
|
+
export type CreatorToolName = (typeof CREATOR_TOOL_NAMES)[number];
|
|
4
|
+
export declare const CreatorToolsConfigSchema: z.ZodObject<{
|
|
5
|
+
type: z.ZodLiteral<"creator-tools">;
|
|
6
|
+
enabledTools: z.ZodOptional<z.ZodArray<z.ZodEnum<["skill_create", "skill_update", "skill_search", "skill_list", "tool_catalog"]>, "many">>;
|
|
7
|
+
}, "strict", z.ZodTypeAny, {
|
|
8
|
+
type: "creator-tools";
|
|
9
|
+
enabledTools?: ("skill_create" | "skill_update" | "skill_search" | "skill_list" | "tool_catalog")[] | undefined;
|
|
10
|
+
}, {
|
|
11
|
+
type: "creator-tools";
|
|
12
|
+
enabledTools?: ("skill_create" | "skill_update" | "skill_search" | "skill_list" | "tool_catalog")[] | undefined;
|
|
13
|
+
}>;
|
|
14
|
+
export type CreatorToolsConfig = z.output<typeof CreatorToolsConfigSchema>;
|
|
15
|
+
//# sourceMappingURL=schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../../src/tool-factories/creator-tools/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,kBAAkB,yFAMrB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAElE,eAAO,MAAM,wBAAwB;;;;;;;;;EAQxB,CAAC;AAEd,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,wBAAwB,CAAC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const CREATOR_TOOL_NAMES = [
|
|
3
|
+
"skill_create",
|
|
4
|
+
"skill_update",
|
|
5
|
+
"skill_search",
|
|
6
|
+
"skill_list",
|
|
7
|
+
"tool_catalog"
|
|
8
|
+
];
|
|
9
|
+
const CreatorToolsConfigSchema = z.object({
|
|
10
|
+
type: z.literal("creator-tools"),
|
|
11
|
+
enabledTools: z.array(z.enum(CREATOR_TOOL_NAMES)).optional().describe("Subset of creator tools to enable")
|
|
12
|
+
}).strict();
|
|
13
|
+
export {
|
|
14
|
+
CREATOR_TOOL_NAMES,
|
|
15
|
+
CreatorToolsConfigSchema
|
|
16
|
+
};
|
package/dist/utils/path.cjs
CHANGED
|
@@ -83,6 +83,15 @@ function getDextoPath(type, filename, startPath) {
|
|
|
83
83
|
return filename ? path.join(basePath, filename) : basePath;
|
|
84
84
|
}
|
|
85
85
|
function getDextoGlobalPath(type, filename) {
|
|
86
|
+
const isDevMode = process.env.DEXTO_DEV_MODE === "true";
|
|
87
|
+
if (isDevMode && (0, import_execution_context.getExecutionContext)() === "dexto-source") {
|
|
88
|
+
const sourceRoot = (0, import_execution_context.findDextoSourceRoot)();
|
|
89
|
+
if (!sourceRoot) {
|
|
90
|
+
throw new Error("Not in dexto source context");
|
|
91
|
+
}
|
|
92
|
+
const devBasePath = path.join(sourceRoot, ".dexto", type);
|
|
93
|
+
return filename ? path.join(devBasePath, filename) : devBasePath;
|
|
94
|
+
}
|
|
86
95
|
const basePath = path.join((0, import_os.homedir)(), ".dexto", type);
|
|
87
96
|
return filename ? path.join(basePath, filename) : basePath;
|
|
88
97
|
}
|
|
@@ -151,7 +160,7 @@ function resolveBundledScript(scriptPath) {
|
|
|
151
160
|
);
|
|
152
161
|
}
|
|
153
162
|
async function ensureDextoGlobalDirectory() {
|
|
154
|
-
const dextoDir =
|
|
163
|
+
const dextoDir = getDextoGlobalPath("");
|
|
155
164
|
try {
|
|
156
165
|
await import_fs2.promises.mkdir(dextoDir, { recursive: true });
|
|
157
166
|
} catch (error) {
|
package/dist/utils/path.d.ts
CHANGED
|
@@ -12,8 +12,11 @@
|
|
|
12
12
|
*/
|
|
13
13
|
export declare function getDextoPath(type: string, filename?: string, startPath?: string): string;
|
|
14
14
|
/**
|
|
15
|
-
* Global path resolver
|
|
16
|
-
*
|
|
15
|
+
* Global path resolver for user-global resources that should not be project-relative.
|
|
16
|
+
*
|
|
17
|
+
* Dev mode support:
|
|
18
|
+
* - dexto-source + DEXTO_DEV_MODE=true: Use repo-local `.dexto` (isolated testing)
|
|
19
|
+
* - otherwise: Use global `~/.dexto` (user experience)
|
|
17
20
|
* @param type Path type (agents, cache, etc.)
|
|
18
21
|
* @param filename Optional filename to append
|
|
19
22
|
* @returns Absolute path to the global location (~/.dexto/...)
|
package/dist/utils/path.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/utils/path.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAuCxF;AAED
|
|
1
|
+
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/utils/path.ts"],"names":[],"mappings":"AAgBA;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAuCxF;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAc1E;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAe5E;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAW3C;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CAKhF;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAwD/D;AAED;;GAEG;AACH,wBAAsB,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC,CAUhE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,SAAS,GAAE,MAAsB,GAAG,MAAM,CAkCzE"}
|
package/dist/utils/path.js
CHANGED
|
@@ -46,6 +46,15 @@ function getDextoPath(type, filename, startPath) {
|
|
|
46
46
|
return filename ? path.join(basePath, filename) : basePath;
|
|
47
47
|
}
|
|
48
48
|
function getDextoGlobalPath(type, filename) {
|
|
49
|
+
const isDevMode = process.env.DEXTO_DEV_MODE === "true";
|
|
50
|
+
if (isDevMode && getExecutionContext() === "dexto-source") {
|
|
51
|
+
const sourceRoot = findDextoSourceRoot();
|
|
52
|
+
if (!sourceRoot) {
|
|
53
|
+
throw new Error("Not in dexto source context");
|
|
54
|
+
}
|
|
55
|
+
const devBasePath = path.join(sourceRoot, ".dexto", type);
|
|
56
|
+
return filename ? path.join(devBasePath, filename) : devBasePath;
|
|
57
|
+
}
|
|
49
58
|
const basePath = path.join(homedir(), ".dexto", type);
|
|
50
59
|
return filename ? path.join(basePath, filename) : basePath;
|
|
51
60
|
}
|
|
@@ -114,7 +123,7 @@ function resolveBundledScript(scriptPath) {
|
|
|
114
123
|
);
|
|
115
124
|
}
|
|
116
125
|
async function ensureDextoGlobalDirectory() {
|
|
117
|
-
const dextoDir =
|
|
126
|
+
const dextoDir = getDextoGlobalPath("");
|
|
118
127
|
try {
|
|
119
128
|
await fs.mkdir(dextoDir, { recursive: true });
|
|
120
129
|
} catch (error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dexto/agent-management",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"yaml": "^2.7.1",
|
|
18
18
|
"zod": "^3.25.0",
|
|
19
|
-
"@dexto/agent-config": "1.6.
|
|
20
|
-
"@dexto/core": "1.6.
|
|
21
|
-
"@dexto/orchestration": "1.6.
|
|
22
|
-
"@dexto/tools-builtins": "1.6.
|
|
19
|
+
"@dexto/agent-config": "1.6.2",
|
|
20
|
+
"@dexto/core": "1.6.2",
|
|
21
|
+
"@dexto/orchestration": "1.6.2",
|
|
22
|
+
"@dexto/tools-builtins": "1.6.2"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/node": "^22.13.5"
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"sideEffects": false,
|
|
38
38
|
"scripts": {
|
|
39
|
-
"build": "cross-env NODE_OPTIONS='--max-old-space-size=4096' tsup && cross-env NODE_OPTIONS='--max-old-space-size=4096' tsc -b tsconfig.json --
|
|
39
|
+
"build": "cross-env NODE_OPTIONS='--max-old-space-size=4096' tsup && node ../../scripts/clean-tsbuildinfo.mjs && cross-env NODE_OPTIONS='--max-old-space-size=4096' tsc -b tsconfig.json --emitDeclarationOnly && node scripts/fix-dist-aliases.mjs",
|
|
40
40
|
"dev": "tsup --watch",
|
|
41
41
|
"typecheck": "tsc -p tsconfig.typecheck.json --noEmit",
|
|
42
42
|
"lint": "eslint . --ext .ts"
|