@llblab/pi-actors 0.19.11 → 0.20.1
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/AGENTS.md +1 -1
- package/CHANGELOG.md +14 -0
- package/dist/lib/actor-inspector-tui.d.ts +55 -0
- package/dist/lib/actor-inspector-tui.js +559 -0
- package/dist/lib/actor-messages.d.ts +25 -0
- package/dist/lib/actor-messages.js +122 -0
- package/dist/lib/actor-recipe-context.d.ts +14 -0
- package/dist/lib/actor-recipe-context.js +79 -0
- package/dist/lib/actor-rooms.d.ts +81 -0
- package/dist/lib/actor-rooms.js +468 -0
- package/dist/lib/async-runs.d.ts +101 -0
- package/dist/lib/async-runs.js +612 -0
- package/dist/lib/command-templates.d.ts +70 -0
- package/dist/lib/command-templates.js +592 -0
- package/dist/lib/config.d.ts +34 -0
- package/dist/lib/config.js +226 -0
- package/dist/lib/execution.d.ts +63 -0
- package/dist/lib/execution.js +450 -0
- package/dist/lib/file-state.d.ts +6 -0
- package/dist/lib/file-state.js +25 -0
- package/dist/lib/identity.d.ts +9 -0
- package/dist/lib/identity.js +27 -0
- package/dist/lib/observability.d.ts +86 -0
- package/dist/lib/observability.js +534 -0
- package/dist/lib/output.d.ts +25 -0
- package/dist/lib/output.js +89 -0
- package/dist/lib/paths.d.ts +11 -0
- package/dist/lib/paths.js +33 -0
- package/dist/lib/prompts.d.ts +23 -0
- package/dist/lib/prompts.js +50 -0
- package/dist/lib/recipe-discovery.d.ts +50 -0
- package/dist/lib/recipe-discovery.js +317 -0
- package/dist/lib/recipe-migration.d.ts +21 -0
- package/dist/lib/recipe-migration.js +90 -0
- package/dist/lib/recipe-references.d.ts +67 -0
- package/dist/lib/recipe-references.js +542 -0
- package/dist/lib/recipe-usage.d.ts +6 -0
- package/dist/lib/recipe-usage.js +57 -0
- package/dist/lib/registry.d.ts +47 -0
- package/dist/lib/registry.js +222 -0
- package/dist/lib/runtime.d.ts +36 -0
- package/dist/lib/runtime.js +126 -0
- package/dist/lib/schema.d.ts +48 -0
- package/dist/lib/schema.js +355 -0
- package/dist/lib/temp.d.ts +10 -0
- package/dist/lib/temp.js +90 -0
- package/dist/lib/tools.d.ts +39 -0
- package/dist/lib/tools.js +982 -0
- package/lib/async-runs.ts +20 -4
- package/lib/paths.ts +5 -1
- package/package.json +5 -2
- package/scripts/async-runner.mjs +8 -12
- package/scripts/validate-recipe.mjs +9 -13
- package/skills/actors/SKILL.md +1 -1
- package/skills/swarm/SKILL.md +1 -1
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry mutation use-cases
|
|
3
|
+
* Zones: registry mutations, persistence, runtime activation
|
|
4
|
+
* Owns register/update/delete validation, persistence, runtime side effects, and result payloads
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, mkdirSync, unlinkSync } from "node:fs";
|
|
7
|
+
import { dirname, join } from "node:path";
|
|
8
|
+
import * as Identity from "./identity.js";
|
|
9
|
+
import * as Output from "./output.js";
|
|
10
|
+
import * as CommandTemplates from "./command-templates.js";
|
|
11
|
+
import { writeJsonAtomic } from "./file-state.js";
|
|
12
|
+
import * as Paths from "./paths.js";
|
|
13
|
+
import * as RecipeReferences from "./recipe-references.js";
|
|
14
|
+
import * as Schema from "./schema.js";
|
|
15
|
+
function textContent(text) {
|
|
16
|
+
return { type: "text", text };
|
|
17
|
+
}
|
|
18
|
+
function listTools(deps) {
|
|
19
|
+
const names = [...deps.getTools().keys()].sort();
|
|
20
|
+
return {
|
|
21
|
+
content: [
|
|
22
|
+
textContent(Output.formatToolText(names.length > 0
|
|
23
|
+
? `Registered tools:\n${names.map((name) => `- ${name}`).join("\n")}`
|
|
24
|
+
: "No registered tools.")),
|
|
25
|
+
],
|
|
26
|
+
details: { tool: "register_tool" },
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function getRecipeRoot(deps) {
|
|
30
|
+
return deps.recipeRoot ?? Paths.getRecipeRoot(dirname(deps.configPath));
|
|
31
|
+
}
|
|
32
|
+
function getToolRecipePath(deps, name) {
|
|
33
|
+
return join(getRecipeRoot(deps), `${name}.json`);
|
|
34
|
+
}
|
|
35
|
+
function persistToolRecipe(deps, cfg) {
|
|
36
|
+
const path = getToolRecipePath(deps, cfg.name);
|
|
37
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
38
|
+
writeJsonAtomic(path, {
|
|
39
|
+
description: cfg.description,
|
|
40
|
+
tool: true,
|
|
41
|
+
...(cfg.recipe?.async !== undefined ? { async: cfg.recipe.async } : {}),
|
|
42
|
+
...(cfg.recipe?.state_dir ? { state_dir: cfg.recipe.state_dir } : {}),
|
|
43
|
+
...(cfg.storedArgs ? { args: cfg.storedArgs } : {}),
|
|
44
|
+
...(cfg.storedDefaults ? { defaults: cfg.storedDefaults } : {}),
|
|
45
|
+
...(cfg.recipe?.values ? { values: cfg.recipe.values } : {}),
|
|
46
|
+
template: cfg.template,
|
|
47
|
+
});
|
|
48
|
+
return path;
|
|
49
|
+
}
|
|
50
|
+
function deleteTool(name, ctx, deps) {
|
|
51
|
+
const tools = deps.getTools();
|
|
52
|
+
if (!tools.has(name)) {
|
|
53
|
+
return {
|
|
54
|
+
content: [
|
|
55
|
+
textContent(Output.formatToolText(`Tool "${name}" not found.`)),
|
|
56
|
+
],
|
|
57
|
+
details: { tool: name },
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const recipePath = getToolRecipePath(deps, name);
|
|
61
|
+
if (existsSync(recipePath))
|
|
62
|
+
unlinkSync(recipePath);
|
|
63
|
+
tools.delete(name);
|
|
64
|
+
deps.setActiveTools(deps.getActiveTools().filter((toolName) => toolName !== name));
|
|
65
|
+
deps.notify(ctx, `Deleted tool: ${name}`, "info");
|
|
66
|
+
return {
|
|
67
|
+
content: [
|
|
68
|
+
textContent(Output.formatToolText(`Deleted tool "${name}". Reload to remove it from the complete registry.`)),
|
|
69
|
+
],
|
|
70
|
+
details: { config: recipePath, tool: name },
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function getInputTemplate(value) {
|
|
74
|
+
if (typeof value === "string")
|
|
75
|
+
return value.trim();
|
|
76
|
+
if (value === null || value === undefined)
|
|
77
|
+
return value;
|
|
78
|
+
if (Array.isArray(value)) {
|
|
79
|
+
const steps = CommandTemplates.expandCommandTemplateConfigs({
|
|
80
|
+
template: value,
|
|
81
|
+
});
|
|
82
|
+
if (steps.length === 0)
|
|
83
|
+
throw new Error(Output.formatToolText("Tool template sequence is empty."));
|
|
84
|
+
return value;
|
|
85
|
+
}
|
|
86
|
+
throw new Error(Output.formatToolText("Tool template must be a string or sequence."));
|
|
87
|
+
}
|
|
88
|
+
function buildConfig(name, input, existing) {
|
|
89
|
+
const explicitArgs = input.args === undefined
|
|
90
|
+
? undefined
|
|
91
|
+
: Schema.parseToolArgDeclarations(input.args);
|
|
92
|
+
if (explicitArgs?.error)
|
|
93
|
+
throw new Error(Output.formatToolText(explicitArgs.error));
|
|
94
|
+
const description = (input.description ?? existing?.description ?? "").trim();
|
|
95
|
+
if (!description) {
|
|
96
|
+
throw new Error(Output.formatToolText("Tool description is required unless deleting."));
|
|
97
|
+
}
|
|
98
|
+
const template = getInputTemplate(input.template);
|
|
99
|
+
if (template === null) {
|
|
100
|
+
throw new Error(Output.formatToolText("Tool template cannot be null here."));
|
|
101
|
+
}
|
|
102
|
+
const finalTemplate = template === undefined || template === "" ? existing?.template : template;
|
|
103
|
+
if (!finalTemplate) {
|
|
104
|
+
throw new Error(Output.formatToolText("Tool template is required."));
|
|
105
|
+
}
|
|
106
|
+
const inputRecipe = typeof input.async === "boolean" ? name : undefined;
|
|
107
|
+
const recipe = inputRecipe
|
|
108
|
+
? {
|
|
109
|
+
name: inputRecipe,
|
|
110
|
+
...(typeof input.async === "boolean" ? { async: input.async } : {}),
|
|
111
|
+
...(typeof input.state_dir === "string" && input.state_dir.trim()
|
|
112
|
+
? { state_dir: input.state_dir.trim() }
|
|
113
|
+
: {}),
|
|
114
|
+
template: finalTemplate,
|
|
115
|
+
...(input.values && typeof input.values === "object"
|
|
116
|
+
? { values: input.values }
|
|
117
|
+
: {}),
|
|
118
|
+
}
|
|
119
|
+
: template === undefined
|
|
120
|
+
? existing?.recipe
|
|
121
|
+
: undefined;
|
|
122
|
+
const defaults = explicitArgs?.defaults ?? existing?.storedDefaults ?? {};
|
|
123
|
+
const storedArgs = explicitArgs
|
|
124
|
+
? explicitArgs.declarations
|
|
125
|
+
: existing?.storedArgs;
|
|
126
|
+
const storedDefaults = Object.keys(defaults).length > 0 ? defaults : undefined;
|
|
127
|
+
const recipeTemplate = RecipeReferences.getRecipeTemplate(finalTemplate);
|
|
128
|
+
const argTemplate = recipeTemplate ?? finalTemplate;
|
|
129
|
+
const argTemplateConfig = typeof argTemplate === "object" && !Array.isArray(argTemplate)
|
|
130
|
+
? {
|
|
131
|
+
...argTemplate,
|
|
132
|
+
...(storedArgs !== undefined ? { args: storedArgs } : {}),
|
|
133
|
+
defaults: { ...(argTemplate.defaults ?? {}), ...defaults },
|
|
134
|
+
}
|
|
135
|
+
: {
|
|
136
|
+
args: storedArgs,
|
|
137
|
+
defaults,
|
|
138
|
+
template: argTemplate,
|
|
139
|
+
};
|
|
140
|
+
const inferredArgTypes = Schema.getTemplateArgTypes(argTemplateConfig);
|
|
141
|
+
const argTypes = {
|
|
142
|
+
...inferredArgTypes,
|
|
143
|
+
...(existing?.argTypes ?? {}),
|
|
144
|
+
...(explicitArgs?.argTypes ?? {}),
|
|
145
|
+
};
|
|
146
|
+
return {
|
|
147
|
+
name,
|
|
148
|
+
description,
|
|
149
|
+
template: finalTemplate,
|
|
150
|
+
...(recipe ? { recipe } : {}),
|
|
151
|
+
args: RecipeReferences.isRecipeTool(finalTemplate, recipe) &&
|
|
152
|
+
storedArgs !== undefined
|
|
153
|
+
? Schema.getExplicitToolArgNames(storedArgs)
|
|
154
|
+
: RecipeReferences.isRecipeReference(finalTemplate) && !recipeTemplate
|
|
155
|
+
? Schema.getExplicitToolArgNames(storedArgs)
|
|
156
|
+
: Schema.getToolArgNames(argTemplateConfig),
|
|
157
|
+
defaults,
|
|
158
|
+
...(Object.keys(argTypes).length > 0 ? { argTypes } : {}),
|
|
159
|
+
...(storedArgs !== undefined ? { storedArgs } : {}),
|
|
160
|
+
...(storedDefaults !== undefined ? { storedDefaults } : {}),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
export async function executeRegisterTool(params, ctx, deps) {
|
|
164
|
+
const input = params;
|
|
165
|
+
if (!input.name)
|
|
166
|
+
return listTools(deps);
|
|
167
|
+
const name = Identity.normalizeToolName(input.name);
|
|
168
|
+
if (!name)
|
|
169
|
+
throw new Error(Output.formatToolText("Invalid tool name."));
|
|
170
|
+
if (deps.reservedToolNames.has(name)) {
|
|
171
|
+
throw new Error(Output.formatToolText(`Reserved tool name: ${name}`));
|
|
172
|
+
}
|
|
173
|
+
const templateProvided = Object.hasOwn(input, "template");
|
|
174
|
+
const template = getInputTemplate(input.template);
|
|
175
|
+
if (templateProvided && (template === null || template === ""))
|
|
176
|
+
return deleteTool(name, ctx, deps);
|
|
177
|
+
const tools = deps.getTools();
|
|
178
|
+
const existing = tools.get(name);
|
|
179
|
+
const conflict = deps.getExternalToolConflict(name);
|
|
180
|
+
if (conflict)
|
|
181
|
+
throw new Error(Output.formatToolText(conflict));
|
|
182
|
+
if (existing && !input.update) {
|
|
183
|
+
throw new Error(Output.formatToolText(`Tool "${name}" already registered. Use update=true to overwrite.`));
|
|
184
|
+
}
|
|
185
|
+
if (template === undefined && !existing) {
|
|
186
|
+
throw new Error(Output.formatToolText("Tool template is required for new registrations."));
|
|
187
|
+
}
|
|
188
|
+
const cfg = buildConfig(name, input, existing);
|
|
189
|
+
let recipePath;
|
|
190
|
+
try {
|
|
191
|
+
recipePath = persistToolRecipe(deps, cfg);
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
throw new Error(Output.formatToolText(`Failed to persist tool recipe: ${error instanceof Error ? error.message : String(error)}`));
|
|
195
|
+
}
|
|
196
|
+
cfg.sourcePath = recipePath;
|
|
197
|
+
tools.set(name, cfg);
|
|
198
|
+
deps.registerRuntimeTool(cfg);
|
|
199
|
+
deps.notify(ctx, `Tool persisted: ${name}`, "info");
|
|
200
|
+
const templateWarnings = CommandTemplates.getCommandTemplateWarnings(typeof cfg.template === "object" && !Array.isArray(cfg.template)
|
|
201
|
+
? cfg.template
|
|
202
|
+
: { template: cfg.template });
|
|
203
|
+
const warningText = templateWarnings.length > 0
|
|
204
|
+
? `\nWarnings:\n${templateWarnings.map((warning) => `- ${warning}`).join("\n")}`
|
|
205
|
+
: "";
|
|
206
|
+
return {
|
|
207
|
+
content: [
|
|
208
|
+
textContent(Output.formatToolText(`${existing ? "Updated" : "Registered"} tool "${name}" (args: ${Schema.formatToolArgs(cfg.args)}).${warningText}`)),
|
|
209
|
+
],
|
|
210
|
+
details: {
|
|
211
|
+
args: cfg.args,
|
|
212
|
+
config: recipePath,
|
|
213
|
+
defaults: cfg.defaults,
|
|
214
|
+
...(cfg.recipe?.async !== undefined ? { async: cfg.recipe.async } : {}),
|
|
215
|
+
...(cfg.recipe?.name ? { recipeName: cfg.recipe.name } : {}),
|
|
216
|
+
...(cfg.recipe?.state_dir ? { state_dir: cfg.recipe.state_dir } : {}),
|
|
217
|
+
...(cfg.template ? { template: cfg.template } : {}),
|
|
218
|
+
...(templateWarnings.length > 0 ? { templateWarnings } : {}),
|
|
219
|
+
tool: name,
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool registry runtime coordinator
|
|
3
|
+
* Zones: runtime coordination, registry loading, pi tools
|
|
4
|
+
* Owns persisted tool loading, conflict detection, runtime registration, and warning notification
|
|
5
|
+
*/
|
|
6
|
+
import * as Config from "./config.ts";
|
|
7
|
+
import type { RegisteredToolExec } from "./execution.ts";
|
|
8
|
+
import * as Tools from "./tools.ts";
|
|
9
|
+
export interface RuntimeContext {
|
|
10
|
+
hasUI: boolean;
|
|
11
|
+
ui: {
|
|
12
|
+
notify(message: string, type?: "info" | "warning" | "error"): void;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface ToolInfoLike {
|
|
16
|
+
name: string;
|
|
17
|
+
}
|
|
18
|
+
export interface ToolRegistryRuntimeDeps {
|
|
19
|
+
configPath: string;
|
|
20
|
+
exec: RegisteredToolExec;
|
|
21
|
+
packagedRecipeRoot?: string;
|
|
22
|
+
recipeRoot?: string;
|
|
23
|
+
getActiveTools?: () => string[];
|
|
24
|
+
getAllTools: () => ToolInfoLike[];
|
|
25
|
+
registerTool: (definition: ReturnType<typeof Tools.createRuntimeToolDefinition>) => void;
|
|
26
|
+
reservedToolNames: Set<string>;
|
|
27
|
+
setActiveTools?: (toolNames: string[]) => void;
|
|
28
|
+
}
|
|
29
|
+
export interface ToolRegistryRuntime {
|
|
30
|
+
getExternalToolConflict(name: string): string | undefined;
|
|
31
|
+
getTools(): Map<string, Config.RegisteredTool>;
|
|
32
|
+
loadTools(ctx: RuntimeContext): void;
|
|
33
|
+
notify(ctx: RuntimeContext, message: string, type: "info" | "warning" | "error"): void;
|
|
34
|
+
registerRuntimeTool(cfg: Config.RegisteredTool): void;
|
|
35
|
+
}
|
|
36
|
+
export declare function createAutoToolsRuntime(deps: ToolRegistryRuntimeDeps): ToolRegistryRuntime;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool registry runtime coordinator
|
|
3
|
+
* Zones: runtime coordination, registry loading, pi tools
|
|
4
|
+
* Owns persisted tool loading, conflict detection, runtime registration, and warning notification
|
|
5
|
+
*/
|
|
6
|
+
import * as Paths from "./paths.js";
|
|
7
|
+
import * as RecipeDiscovery from "./recipe-discovery.js";
|
|
8
|
+
import * as RecipeMigration from "./recipe-migration.js";
|
|
9
|
+
import * as Tools from "./tools.js";
|
|
10
|
+
export function createAutoToolsRuntime(deps) {
|
|
11
|
+
const tools = new Map();
|
|
12
|
+
const runtimeToolFingerprints = new Map();
|
|
13
|
+
const runtimeTools = new Set();
|
|
14
|
+
function notify(ctx, message, type) {
|
|
15
|
+
if (ctx.hasUI)
|
|
16
|
+
ctx.ui.notify(message, type);
|
|
17
|
+
}
|
|
18
|
+
function getExternalToolConflict(name) {
|
|
19
|
+
if (runtimeTools.has(name))
|
|
20
|
+
return undefined;
|
|
21
|
+
const existing = deps.getAllTools().find((tool) => tool.name === name);
|
|
22
|
+
return existing
|
|
23
|
+
? `Tool "${name}" is already registered outside pi-actors.`
|
|
24
|
+
: undefined;
|
|
25
|
+
}
|
|
26
|
+
function getToolFingerprint(cfg) {
|
|
27
|
+
return JSON.stringify({
|
|
28
|
+
args: cfg.args,
|
|
29
|
+
argTypes: cfg.argTypes,
|
|
30
|
+
defaults: cfg.defaults,
|
|
31
|
+
description: cfg.description,
|
|
32
|
+
recipe: cfg.recipe,
|
|
33
|
+
template: cfg.template,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function deactivateMissingRuntimeTools(activeNames) {
|
|
37
|
+
const stale = [...runtimeTools].filter((name) => !activeNames.has(name));
|
|
38
|
+
if (stale.length === 0)
|
|
39
|
+
return;
|
|
40
|
+
for (const name of stale) {
|
|
41
|
+
runtimeTools.delete(name);
|
|
42
|
+
runtimeToolFingerprints.delete(name);
|
|
43
|
+
}
|
|
44
|
+
if (!deps.getActiveTools || !deps.setActiveTools)
|
|
45
|
+
return;
|
|
46
|
+
const staleSet = new Set(stale);
|
|
47
|
+
deps.setActiveTools(deps.getActiveTools().filter((name) => !staleSet.has(name)));
|
|
48
|
+
}
|
|
49
|
+
function registerRuntimeTool(cfg) {
|
|
50
|
+
const fingerprint = getToolFingerprint(cfg);
|
|
51
|
+
if (runtimeToolFingerprints.get(cfg.name) === fingerprint)
|
|
52
|
+
return;
|
|
53
|
+
deps.registerTool(Tools.createRuntimeToolDefinition(cfg, deps.exec));
|
|
54
|
+
runtimeTools.add(cfg.name);
|
|
55
|
+
runtimeToolFingerprints.set(cfg.name, fingerprint);
|
|
56
|
+
}
|
|
57
|
+
function formatRecipeToolWarnings(warnings) {
|
|
58
|
+
const shadowed = warnings.filter((warning) => warning.includes(" shadows "));
|
|
59
|
+
const skipped = warnings.filter((warning) => warning.includes(" could not be exposed as a tool:"));
|
|
60
|
+
const other = warnings.filter((warning) => !shadowed.includes(warning) && !skipped.includes(warning));
|
|
61
|
+
const lines = ["pi-actors recipe registry warning"];
|
|
62
|
+
if (shadowed.length > 0) {
|
|
63
|
+
lines.push("User recipes override packaged recipes:");
|
|
64
|
+
lines.push(...shadowed.map((warning) => `• ${warning}`));
|
|
65
|
+
}
|
|
66
|
+
if (skipped.length > 0) {
|
|
67
|
+
lines.push("Recipes skipped from tool exposure:");
|
|
68
|
+
lines.push(...skipped.map((warning) => `• ${warning}`));
|
|
69
|
+
}
|
|
70
|
+
if (other.length > 0) {
|
|
71
|
+
lines.push("Other registry diagnostics:");
|
|
72
|
+
lines.push(...other.map((warning) => `• ${warning}`));
|
|
73
|
+
}
|
|
74
|
+
return `${lines.join("\n")}\n`;
|
|
75
|
+
}
|
|
76
|
+
function loadTools(ctx) {
|
|
77
|
+
const warnings = [];
|
|
78
|
+
const recipeRoot = deps.recipeRoot ?? Paths.getRecipeRoot();
|
|
79
|
+
const packagedRecipeRoot = deps.packagedRecipeRoot ?? Paths.getPackagedRecipeRoot();
|
|
80
|
+
const migration = RecipeMigration.migrateLegacyToolRegistry({
|
|
81
|
+
configPath: deps.configPath,
|
|
82
|
+
recipeRoot,
|
|
83
|
+
reservedToolNames: deps.reservedToolNames,
|
|
84
|
+
});
|
|
85
|
+
warnings.push(...migration.warnings);
|
|
86
|
+
if (migration.conflicts.length > 0)
|
|
87
|
+
warnings.push(`Recipe migration conflicts: ${migration.conflicts.join(", ")}`);
|
|
88
|
+
if (migration.invalid.length > 0)
|
|
89
|
+
warnings.push(`Recipe migration invalid entries: ${migration.invalid.join(", ")}`);
|
|
90
|
+
const discovered = RecipeDiscovery.discoverRecipeSources([
|
|
91
|
+
{ root: recipeRoot, defaultTool: true, mutableUsage: true },
|
|
92
|
+
{ root: packagedRecipeRoot },
|
|
93
|
+
]);
|
|
94
|
+
warnings.push(...discovered.diagnostics);
|
|
95
|
+
tools.clear();
|
|
96
|
+
for (const entry of discovered.active.values()) {
|
|
97
|
+
try {
|
|
98
|
+
const cfg = RecipeDiscovery.toRegisteredTool(entry);
|
|
99
|
+
if (cfg)
|
|
100
|
+
tools.set(cfg.name, cfg);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
warnings.push(`Recipe ${entry.id} could not be exposed as a tool: ${error instanceof Error ? error.message : String(error)}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
deactivateMissingRuntimeTools(new Set(tools.keys()));
|
|
107
|
+
for (const cfg of tools.values()) {
|
|
108
|
+
const conflict = getExternalToolConflict(cfg.name);
|
|
109
|
+
if (conflict) {
|
|
110
|
+
warnings.push(conflict);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
registerRuntimeTool(cfg);
|
|
114
|
+
}
|
|
115
|
+
if (warnings.length > 0) {
|
|
116
|
+
notify(ctx, formatRecipeToolWarnings(warnings), "warning");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
getExternalToolConflict,
|
|
121
|
+
getTools: () => tools,
|
|
122
|
+
loadTools,
|
|
123
|
+
notify,
|
|
124
|
+
registerRuntimeTool,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-tools schema helpers
|
|
3
|
+
* Zones: tool schema, registry args, command-template placeholders
|
|
4
|
+
* Owns tool argument declarations, placeholder-derived tool schemas, and persisted registry normalization
|
|
5
|
+
*/
|
|
6
|
+
import * as CommandTemplates from "./command-templates.ts";
|
|
7
|
+
export type ToolArgType = {
|
|
8
|
+
kind: "string";
|
|
9
|
+
} | {
|
|
10
|
+
kind: "path";
|
|
11
|
+
} | {
|
|
12
|
+
kind: "int";
|
|
13
|
+
} | {
|
|
14
|
+
kind: "number";
|
|
15
|
+
} | {
|
|
16
|
+
kind: "bool";
|
|
17
|
+
} | {
|
|
18
|
+
kind: "array";
|
|
19
|
+
} | {
|
|
20
|
+
kind: "enum";
|
|
21
|
+
values: string[];
|
|
22
|
+
};
|
|
23
|
+
export interface ParsedToolArgToken {
|
|
24
|
+
arg: string;
|
|
25
|
+
defaultValue?: string;
|
|
26
|
+
declaration: string;
|
|
27
|
+
type: ToolArgType;
|
|
28
|
+
}
|
|
29
|
+
export interface ToolArgSpec {
|
|
30
|
+
args: string[];
|
|
31
|
+
argTypes: Record<string, ToolArgType>;
|
|
32
|
+
declarations: string[];
|
|
33
|
+
defaults: Record<string, string>;
|
|
34
|
+
error?: string;
|
|
35
|
+
}
|
|
36
|
+
export declare function parseToolArgToken(value: string): ParsedToolArgToken;
|
|
37
|
+
export declare function parseToolArgDeclarations(value: string): ToolArgSpec;
|
|
38
|
+
export declare function normalizeStoredToolArgDeclarations(argsValue: unknown, defaultsValue: unknown): ToolArgSpec & {
|
|
39
|
+
changed: boolean;
|
|
40
|
+
provided: boolean;
|
|
41
|
+
};
|
|
42
|
+
export declare function formatToolArgs(args: string[]): string;
|
|
43
|
+
export declare function getTemplatePlaceholderNames(config: CommandTemplates.CommandTemplateConfig): string[];
|
|
44
|
+
export declare function getTemplateArgTypes(config: CommandTemplates.CommandTemplateConfig): Record<string, ToolArgType>;
|
|
45
|
+
export declare function getExplicitToolArgNames(args: string[] | undefined): string[];
|
|
46
|
+
export declare function getToolArgNames(config: CommandTemplates.CommandTemplateConfig): string[];
|
|
47
|
+
export declare function getRequiredToolArgNames(config: CommandTemplates.CommandTemplateConfig): Set<string>;
|
|
48
|
+
export declare function normalizeRuntimeValues(values: Record<string, unknown>, argTypes: Record<string, ToolArgType> | undefined): Record<string, unknown>;
|