@dexto/agent-management 1.6.17 → 1.6.19
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/AgentFactory.cjs +2 -1
- package/dist/AgentFactory.d.ts +3 -1
- package/dist/AgentFactory.d.ts.map +1 -1
- package/dist/AgentFactory.js +2 -1
- package/dist/agent-creation.cjs +2 -1
- package/dist/agent-creation.d.ts +2 -1
- package/dist/agent-creation.d.ts.map +1 -1
- package/dist/agent-creation.js +6 -2
- package/dist/plugins/discover-skills.d.ts +8 -0
- package/dist/plugins/discover-skills.d.ts.map +1 -1
- package/dist/plugins/load-plugin.cjs +4 -58
- package/dist/plugins/load-plugin.d.ts.map +1 -1
- package/dist/plugins/load-plugin.js +4 -58
- package/dist/plugins/mcp-config.cjs +35 -0
- package/dist/plugins/mcp-config.d.ts +10 -0
- package/dist/plugins/mcp-config.d.ts.map +1 -0
- package/dist/plugins/mcp-config.js +11 -0
- package/dist/tool-factories/creator-tools/factory.cjs +149 -10
- package/dist/tool-factories/creator-tools/factory.d.ts.map +1 -1
- package/dist/tool-factories/creator-tools/factory.js +151 -11
- package/dist/tool-factories/creator-tools/schemas.cjs +1 -0
- package/dist/tool-factories/creator-tools/schemas.d.ts +4 -4
- package/dist/tool-factories/creator-tools/schemas.d.ts.map +1 -1
- package/dist/tool-factories/creator-tools/schemas.js +1 -0
- package/package.json +6 -6
package/dist/AgentFactory.cjs
CHANGED
|
@@ -126,7 +126,8 @@ const AgentFactory = {
|
|
|
126
126
|
}
|
|
127
127
|
return await (0, import_agent_creation.createDextoAgentFromConfig)({
|
|
128
128
|
config: configToEnrich,
|
|
129
|
-
enrichOptions: { isInteractiveCli: options?.isInteractiveCli ?? false }
|
|
129
|
+
enrichOptions: { isInteractiveCli: options?.isInteractiveCli ?? false },
|
|
130
|
+
runtimeOverrides: options?.runtimeOverrides
|
|
130
131
|
});
|
|
131
132
|
}
|
|
132
133
|
};
|
package/dist/AgentFactory.d.ts
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
* @see https://docs.dexto.ai/api/sdk/agent-factory for full documentation
|
|
25
25
|
*/
|
|
26
26
|
import type { AgentConfig } from '@dexto/agent-config';
|
|
27
|
-
import type { DextoAgent } from '@dexto/core';
|
|
27
|
+
import type { DextoAgent, DextoAgentConfigInput } from '@dexto/core';
|
|
28
28
|
import { type InstallOptions } from './installation.js';
|
|
29
29
|
import type { AgentMetadata } from './AgentManager.js';
|
|
30
30
|
/**
|
|
@@ -44,6 +44,8 @@ export interface CreateAgentOptions {
|
|
|
44
44
|
agentId?: string;
|
|
45
45
|
/** Whether this is interactive CLI mode (affects logger defaults) */
|
|
46
46
|
isInteractiveCli?: boolean;
|
|
47
|
+
/** Explicit runtime overrides applied outside the validated agent config */
|
|
48
|
+
runtimeOverrides?: Pick<DextoAgentConfigInput, 'usageScopeId'> | undefined;
|
|
47
49
|
}
|
|
48
50
|
/**
|
|
49
51
|
* Static API for agent management operations
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AgentFactory.d.ts","sourceRoot":"","sources":["../src/AgentFactory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"AgentFactory.d.ts","sourceRoot":"","sources":["../src/AgentFactory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAIrE,OAAO,EAIH,KAAK,cAAc,EACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAC9B,6CAA6C;IAC7C,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,6CAA6C;IAC7C,8BAA8B,CAAC,EAAE,MAAM,CAAC;CAC3C;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAC/B,8FAA8F;IAC9F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,cAAc,CAAC,GAAG,SAAS,CAAC;CAC9E;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY;IACrB;;;OAGG;yBACwB,iBAAiB;;;;;;;;;;;;;;;;;;IAyC5C;;OAEG;0BACyB,MAAM,YAAY,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;IAI9E;;OAEG;gCAEU,MAAM,cACH,MAAM,YACR,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,aAAa,GAAG,QAAQ,GAAG,MAAM,CAAC,YAC/D,cAAc,GACzB,OAAO,CAAC,MAAM,CAAC;IAIlB;;;;OAIG;4BAC2B,MAAM,WAAW,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;wBACuB,WAAW,YAAY,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAC;CAoB5F,CAAC"}
|
package/dist/AgentFactory.js
CHANGED
|
@@ -107,7 +107,8 @@ const AgentFactory = {
|
|
|
107
107
|
}
|
|
108
108
|
return await createDextoAgentFromConfig({
|
|
109
109
|
config: configToEnrich,
|
|
110
|
-
enrichOptions: { isInteractiveCli: options?.isInteractiveCli ?? false }
|
|
110
|
+
enrichOptions: { isInteractiveCli: options?.isInteractiveCli ?? false },
|
|
111
|
+
runtimeOverrides: options?.runtimeOverrides
|
|
111
112
|
});
|
|
112
113
|
}
|
|
113
114
|
};
|
package/dist/agent-creation.cjs
CHANGED
|
@@ -61,7 +61,7 @@ function applySubAgentToolConstraints(config) {
|
|
|
61
61
|
return { ...config, tools: constrainedTools };
|
|
62
62
|
}
|
|
63
63
|
async function createDextoAgentFromConfig(options) {
|
|
64
|
-
const { configPath, enrichOptions, agentIdOverride, overrides } = options;
|
|
64
|
+
const { configPath, enrichOptions, agentIdOverride, overrides, runtimeOverrides } = options;
|
|
65
65
|
const cleanedConfig = (0, import_agent_config.cleanNullValues)(options.config);
|
|
66
66
|
const { image } = await loadImageForConfig({
|
|
67
67
|
config: cleanedConfig,
|
|
@@ -86,6 +86,7 @@ async function createDextoAgentFromConfig(options) {
|
|
|
86
86
|
(0, import_agent_config.toDextoAgentOptions)({
|
|
87
87
|
config: validatedConfig,
|
|
88
88
|
services,
|
|
89
|
+
...runtimeOverrides ? { runtimeOverrides } : {},
|
|
89
90
|
overrides: mergedOverrides
|
|
90
91
|
})
|
|
91
92
|
);
|
package/dist/agent-creation.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentConfig } from '@dexto/agent-config';
|
|
2
|
-
import { DextoAgent, type InitializeServicesOptions } from '@dexto/core';
|
|
2
|
+
import { DextoAgent, type DextoAgentConfigInput, type InitializeServicesOptions } from '@dexto/core';
|
|
3
3
|
import { type EnrichAgentConfigOptions } from './config/index.js';
|
|
4
4
|
type CreateDextoAgentFromConfigOptions = {
|
|
5
5
|
config: AgentConfig;
|
|
@@ -9,6 +9,7 @@ type CreateDextoAgentFromConfigOptions = {
|
|
|
9
9
|
imageNameOverride?: string | undefined;
|
|
10
10
|
agentContext?: 'subagent' | undefined;
|
|
11
11
|
overrides?: InitializeServicesOptions | undefined;
|
|
12
|
+
runtimeOverrides?: Pick<DextoAgentConfigInput, 'usageScopeId'> | undefined;
|
|
12
13
|
};
|
|
13
14
|
export declare function createDextoAgentFromConfig(options: CreateDextoAgentFromConfigOptions): Promise<DextoAgent>;
|
|
14
15
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-creation.d.ts","sourceRoot":"","sources":["../src/agent-creation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,qBAAqB,CAAC;AASnE,OAAO,
|
|
1
|
+
{"version":3,"file":"agent-creation.d.ts","sourceRoot":"","sources":["../src/agent-creation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAc,MAAM,qBAAqB,CAAC;AASnE,OAAO,EACH,UAAU,EAEV,KAAK,qBAAqB,EAC1B,KAAK,yBAAyB,EACjC,MAAM,aAAa,CAAC;AACrB,OAAO,EAAqB,KAAK,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAGrF,KAAK,iCAAiC,GAAG;IACrC,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,aAAa,CAAC,EAAE,wBAAwB,GAAG,SAAS,CAAC;IACrD,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,YAAY,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IACtC,SAAS,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;IAClD,gBAAgB,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,cAAc,CAAC,GAAG,SAAS,CAAC;CAC9E,CAAC;AA0DF,wBAAsB,0BAA0B,CAC5C,OAAO,EAAE,iCAAiC,GAC3C,OAAO,CAAC,UAAU,CAAC,CAuCrB"}
|
package/dist/agent-creation.js
CHANGED
|
@@ -6,7 +6,10 @@ import {
|
|
|
6
6
|
resolveServicesFromConfig,
|
|
7
7
|
toDextoAgentOptions
|
|
8
8
|
} from "@dexto/agent-config";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
DextoAgent,
|
|
11
|
+
logger
|
|
12
|
+
} from "@dexto/core";
|
|
10
13
|
import { enrichAgentConfig } from "./config/index.js";
|
|
11
14
|
import { BUILTIN_TOOL_NAMES } from "@dexto/tools-builtins";
|
|
12
15
|
async function loadImageForConfig(options) {
|
|
@@ -45,7 +48,7 @@ function applySubAgentToolConstraints(config) {
|
|
|
45
48
|
return { ...config, tools: constrainedTools };
|
|
46
49
|
}
|
|
47
50
|
async function createDextoAgentFromConfig(options) {
|
|
48
|
-
const { configPath, enrichOptions, agentIdOverride, overrides } = options;
|
|
51
|
+
const { configPath, enrichOptions, agentIdOverride, overrides, runtimeOverrides } = options;
|
|
49
52
|
const cleanedConfig = cleanNullValues(options.config);
|
|
50
53
|
const { image } = await loadImageForConfig({
|
|
51
54
|
config: cleanedConfig,
|
|
@@ -70,6 +73,7 @@ async function createDextoAgentFromConfig(options) {
|
|
|
70
73
|
toDextoAgentOptions({
|
|
71
74
|
config: validatedConfig,
|
|
72
75
|
services,
|
|
76
|
+
...runtimeOverrides ? { runtimeOverrides } : {},
|
|
73
77
|
overrides: mergedOverrides
|
|
74
78
|
})
|
|
75
79
|
);
|
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
* ~/.dexto/skills/
|
|
11
11
|
* └── skill-name/
|
|
12
12
|
* ├── SKILL.md (required - the skill prompt)
|
|
13
|
+
* ├── handlers/ (optional - workflow helper files)
|
|
14
|
+
* ├── scripts/ (optional - executable helpers)
|
|
15
|
+
* ├── mcps/ (optional - MCP server config JSON files)
|
|
13
16
|
* └── references/ (optional - reference files)
|
|
14
17
|
*
|
|
15
18
|
* These skills are loaded as prompts directly, not as part of a plugin package.
|
|
@@ -26,6 +29,11 @@ export interface DiscoveredSkill {
|
|
|
26
29
|
skillFile: string;
|
|
27
30
|
/** Source location */
|
|
28
31
|
source: 'user' | 'project';
|
|
32
|
+
/**
|
|
33
|
+
* Reserved for future discovery metadata.
|
|
34
|
+
* Skill-bundled MCP config is resolved lazily from the skill directory when the prompt is loaded.
|
|
35
|
+
*/
|
|
36
|
+
warnings?: string[] | undefined;
|
|
29
37
|
}
|
|
30
38
|
/**
|
|
31
39
|
* Discovers standalone skills from standard locations.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discover-skills.d.ts","sourceRoot":"","sources":["../../src/plugins/discover-skills.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"discover-skills.d.ts","sourceRoot":"","sources":["../../src/plugins/discover-skills.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC5B,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB;IACtB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CACnC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,eAAe,EAAE,CAkEhF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAW9C"}
|
|
@@ -33,7 +33,7 @@ __export(load_plugin_exports, {
|
|
|
33
33
|
module.exports = __toCommonJS(load_plugin_exports);
|
|
34
34
|
var path = __toESM(require("path"), 1);
|
|
35
35
|
var import_fs = require("fs");
|
|
36
|
-
var
|
|
36
|
+
var import_mcp_config = require("./mcp-config.js");
|
|
37
37
|
function loadClaudeCodePlugin(plugin) {
|
|
38
38
|
const warnings = [];
|
|
39
39
|
const commands = [];
|
|
@@ -82,12 +82,13 @@ function loadClaudeCodePlugin(plugin) {
|
|
|
82
82
|
} catch {
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
|
-
const
|
|
85
|
+
const mcpResult = (0, import_mcp_config.loadMcpConfigFromDirectory)(pluginPath, pluginName);
|
|
86
|
+
warnings.push(...mcpResult.warnings);
|
|
86
87
|
checkUnsupportedFeatures(pluginPath, pluginName, warnings);
|
|
87
88
|
return {
|
|
88
89
|
manifest: plugin.manifest,
|
|
89
90
|
commands,
|
|
90
|
-
mcpConfig,
|
|
91
|
+
mcpConfig: mcpResult.mcpConfig,
|
|
91
92
|
warnings
|
|
92
93
|
};
|
|
93
94
|
}
|
|
@@ -111,61 +112,6 @@ function readFileSafe(filePath) {
|
|
|
111
112
|
return null;
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
|
-
function loadMcpConfig(pluginPath, pluginName, warnings) {
|
|
115
|
-
const mcpPath = path.join(pluginPath, ".mcp.json");
|
|
116
|
-
if (!(0, import_fs.existsSync)(mcpPath)) {
|
|
117
|
-
return void 0;
|
|
118
|
-
}
|
|
119
|
-
try {
|
|
120
|
-
const content = (0, import_fs.readFileSync)(mcpPath, "utf-8");
|
|
121
|
-
const parsed = JSON.parse(content);
|
|
122
|
-
if (!parsed.mcpServers && typeof parsed === "object" && parsed !== null) {
|
|
123
|
-
const hasServerConfig = Object.values(parsed).some(
|
|
124
|
-
(val) => typeof val === "object" && val !== null && ("type" in val || "command" in val || "url" in val)
|
|
125
|
-
);
|
|
126
|
-
if (hasServerConfig) {
|
|
127
|
-
const normalized = {};
|
|
128
|
-
for (const [serverName, serverConfig] of Object.entries(parsed)) {
|
|
129
|
-
if (typeof serverConfig === "object" && serverConfig !== null && !Array.isArray(serverConfig)) {
|
|
130
|
-
const config = serverConfig;
|
|
131
|
-
if ("type" in config) {
|
|
132
|
-
normalized[serverName] = config;
|
|
133
|
-
} else if ("command" in config) {
|
|
134
|
-
normalized[serverName] = {
|
|
135
|
-
type: "stdio",
|
|
136
|
-
...config
|
|
137
|
-
};
|
|
138
|
-
} else if ("url" in config) {
|
|
139
|
-
const url = String(config.url || "");
|
|
140
|
-
const inferredType = url.includes("/sse") ? "sse" : "http";
|
|
141
|
-
normalized[serverName] = {
|
|
142
|
-
type: inferredType,
|
|
143
|
-
...config
|
|
144
|
-
};
|
|
145
|
-
} else {
|
|
146
|
-
normalized[serverName] = config;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return { mcpServers: normalized };
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
const result = import_schemas.PluginMCPConfigSchema.safeParse(parsed);
|
|
154
|
-
if (!result.success) {
|
|
155
|
-
const issues = result.error.issues.map((i) => i.message).join(", ");
|
|
156
|
-
warnings.push(`[${pluginName}] Invalid .mcp.json: ${issues}`);
|
|
157
|
-
return void 0;
|
|
158
|
-
}
|
|
159
|
-
return result.data;
|
|
160
|
-
} catch (error) {
|
|
161
|
-
if (error instanceof SyntaxError) {
|
|
162
|
-
warnings.push(`[${pluginName}] Failed to parse .mcp.json: invalid JSON`);
|
|
163
|
-
} else {
|
|
164
|
-
warnings.push(`[${pluginName}] Failed to load .mcp.json: ${String(error)}`);
|
|
165
|
-
}
|
|
166
|
-
return void 0;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
115
|
function checkUnsupportedFeatures(pluginPath, pluginName, warnings) {
|
|
170
116
|
const hooksPath = path.join(pluginPath, "hooks", "hooks.json");
|
|
171
117
|
if ((0, import_fs.existsSync)(hooksPath)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-plugin.d.ts","sourceRoot":"","sources":["../../src/plugins/load-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"load-plugin.d.ts","sourceRoot":"","sources":["../../src/plugins/load-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAiB,MAAM,YAAY,CAAC;AAGhF;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,gBAAgB,GAAG,YAAY,CAsE3E"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as path from "path";
|
|
2
2
|
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
3
|
-
import {
|
|
3
|
+
import { loadMcpConfigFromDirectory } from "./mcp-config.js";
|
|
4
4
|
function loadClaudeCodePlugin(plugin) {
|
|
5
5
|
const warnings = [];
|
|
6
6
|
const commands = [];
|
|
@@ -49,12 +49,13 @@ function loadClaudeCodePlugin(plugin) {
|
|
|
49
49
|
} catch {
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
const
|
|
52
|
+
const mcpResult = loadMcpConfigFromDirectory(pluginPath, pluginName);
|
|
53
|
+
warnings.push(...mcpResult.warnings);
|
|
53
54
|
checkUnsupportedFeatures(pluginPath, pluginName, warnings);
|
|
54
55
|
return {
|
|
55
56
|
manifest: plugin.manifest,
|
|
56
57
|
commands,
|
|
57
|
-
mcpConfig,
|
|
58
|
+
mcpConfig: mcpResult.mcpConfig,
|
|
58
59
|
warnings
|
|
59
60
|
};
|
|
60
61
|
}
|
|
@@ -78,61 +79,6 @@ function readFileSafe(filePath) {
|
|
|
78
79
|
return null;
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
|
-
function loadMcpConfig(pluginPath, pluginName, warnings) {
|
|
82
|
-
const mcpPath = path.join(pluginPath, ".mcp.json");
|
|
83
|
-
if (!existsSync(mcpPath)) {
|
|
84
|
-
return void 0;
|
|
85
|
-
}
|
|
86
|
-
try {
|
|
87
|
-
const content = readFileSync(mcpPath, "utf-8");
|
|
88
|
-
const parsed = JSON.parse(content);
|
|
89
|
-
if (!parsed.mcpServers && typeof parsed === "object" && parsed !== null) {
|
|
90
|
-
const hasServerConfig = Object.values(parsed).some(
|
|
91
|
-
(val) => typeof val === "object" && val !== null && ("type" in val || "command" in val || "url" in val)
|
|
92
|
-
);
|
|
93
|
-
if (hasServerConfig) {
|
|
94
|
-
const normalized = {};
|
|
95
|
-
for (const [serverName, serverConfig] of Object.entries(parsed)) {
|
|
96
|
-
if (typeof serverConfig === "object" && serverConfig !== null && !Array.isArray(serverConfig)) {
|
|
97
|
-
const config = serverConfig;
|
|
98
|
-
if ("type" in config) {
|
|
99
|
-
normalized[serverName] = config;
|
|
100
|
-
} else if ("command" in config) {
|
|
101
|
-
normalized[serverName] = {
|
|
102
|
-
type: "stdio",
|
|
103
|
-
...config
|
|
104
|
-
};
|
|
105
|
-
} else if ("url" in config) {
|
|
106
|
-
const url = String(config.url || "");
|
|
107
|
-
const inferredType = url.includes("/sse") ? "sse" : "http";
|
|
108
|
-
normalized[serverName] = {
|
|
109
|
-
type: inferredType,
|
|
110
|
-
...config
|
|
111
|
-
};
|
|
112
|
-
} else {
|
|
113
|
-
normalized[serverName] = config;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return { mcpServers: normalized };
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
const result = PluginMCPConfigSchema.safeParse(parsed);
|
|
121
|
-
if (!result.success) {
|
|
122
|
-
const issues = result.error.issues.map((i) => i.message).join(", ");
|
|
123
|
-
warnings.push(`[${pluginName}] Invalid .mcp.json: ${issues}`);
|
|
124
|
-
return void 0;
|
|
125
|
-
}
|
|
126
|
-
return result.data;
|
|
127
|
-
} catch (error) {
|
|
128
|
-
if (error instanceof SyntaxError) {
|
|
129
|
-
warnings.push(`[${pluginName}] Failed to parse .mcp.json: invalid JSON`);
|
|
130
|
-
} else {
|
|
131
|
-
warnings.push(`[${pluginName}] Failed to load .mcp.json: ${String(error)}`);
|
|
132
|
-
}
|
|
133
|
-
return void 0;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
82
|
function checkUnsupportedFeatures(pluginPath, pluginName, warnings) {
|
|
137
83
|
const hooksPath = path.join(pluginPath, "hooks", "hooks.json");
|
|
138
84
|
if (existsSync(hooksPath)) {
|
|
@@ -0,0 +1,35 @@
|
|
|
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 mcp_config_exports = {};
|
|
20
|
+
__export(mcp_config_exports, {
|
|
21
|
+
loadMcpConfigFromDirectory: () => loadMcpConfigFromDirectory
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(mcp_config_exports);
|
|
24
|
+
var import_core = require("@dexto/core");
|
|
25
|
+
function loadMcpConfigFromDirectory(directoryPath, ownerName, options = {}) {
|
|
26
|
+
const result = (0, import_core.loadBundledMcpConfigFromDirectory)(directoryPath, ownerName, options);
|
|
27
|
+
return {
|
|
28
|
+
...result.mcpServers ? { mcpConfig: { mcpServers: result.mcpServers } } : {},
|
|
29
|
+
warnings: result.warnings
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
33
|
+
0 && (module.exports = {
|
|
34
|
+
loadMcpConfigFromDirectory
|
|
35
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PluginMCPConfig } from './types.js';
|
|
2
|
+
export interface LoadMcpConfigOptions {
|
|
3
|
+
scanNestedMcps?: boolean | undefined;
|
|
4
|
+
}
|
|
5
|
+
export interface LoadMcpConfigResult {
|
|
6
|
+
mcpConfig?: PluginMCPConfig | undefined;
|
|
7
|
+
warnings: string[];
|
|
8
|
+
}
|
|
9
|
+
export declare function loadMcpConfigFromDirectory(directoryPath: string, ownerName: string, options?: LoadMcpConfigOptions): LoadMcpConfigResult;
|
|
10
|
+
//# sourceMappingURL=mcp-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-config.d.ts","sourceRoot":"","sources":["../../src/plugins/mcp-config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,oBAAoB;IACjC,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACxC;AAED,MAAM,WAAW,mBAAmB;IAChC,SAAS,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IACxC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,wBAAgB,0BAA0B,CACtC,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,oBAAyB,GACnC,mBAAmB,CASrB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { loadBundledMcpConfigFromDirectory } from "@dexto/core";
|
|
2
|
+
function loadMcpConfigFromDirectory(directoryPath, ownerName, options = {}) {
|
|
3
|
+
const result = loadBundledMcpConfigFromDirectory(directoryPath, ownerName, options);
|
|
4
|
+
return {
|
|
5
|
+
...result.mcpServers ? { mcpConfig: { mcpServers: result.mcpServers } } : {},
|
|
6
|
+
warnings: result.warnings
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export {
|
|
10
|
+
loadMcpConfigFromDirectory
|
|
11
|
+
};
|
|
@@ -60,6 +60,10 @@ const SkillListInputSchema = import_zod.z.object({
|
|
|
60
60
|
projectPath: import_zod.z.string().optional(),
|
|
61
61
|
query: import_zod.z.string().optional().describe("Optional search term to filter skills by name or path")
|
|
62
62
|
}).strict();
|
|
63
|
+
const SkillRefreshInputSchema = import_zod.z.object({
|
|
64
|
+
id: import_zod.z.string().min(1).describe("Skill id to refresh in the running agent session."),
|
|
65
|
+
scope: import_zod.z.enum(["global", "workspace"]).optional()
|
|
66
|
+
}).strict();
|
|
63
67
|
const SkillSearchInputSchema = import_zod.z.object({
|
|
64
68
|
query: import_zod.z.string().optional().describe("Optional search term to filter skills by name or description"),
|
|
65
69
|
limit: import_zod.z.number().int().min(1).max(200).optional().describe(
|
|
@@ -71,6 +75,7 @@ const ToolCatalogInputSchema = import_zod.z.object({
|
|
|
71
75
|
limit: import_zod.z.number().int().min(1).max(500).optional().describe("Maximum number of tools to return (defaults to all)."),
|
|
72
76
|
includeDescriptions: import_zod.z.boolean().optional().describe("Include tool descriptions (defaults to true).")
|
|
73
77
|
}).strict();
|
|
78
|
+
const SKILL_RESOURCE_DIRECTORIES = ["handlers", "scripts", "mcps", "references"];
|
|
74
79
|
function normalizeSkillQuery(value) {
|
|
75
80
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
76
81
|
}
|
|
@@ -91,10 +96,30 @@ function resolveWorkspaceBasePath(context) {
|
|
|
91
96
|
function resolveWorkspaceSkillDirs(context) {
|
|
92
97
|
const base = resolveWorkspaceBasePath(context);
|
|
93
98
|
return {
|
|
94
|
-
primary: import_node_path.default.join(base, "
|
|
99
|
+
primary: import_node_path.default.join(base, "skills"),
|
|
100
|
+
secondary: import_node_path.default.join(base, ".agents", "skills"),
|
|
95
101
|
legacy: import_node_path.default.join(base, ".dexto", "skills")
|
|
96
102
|
};
|
|
97
103
|
}
|
|
104
|
+
function findRegisteredSkillFile(skillId, context) {
|
|
105
|
+
const prompts = context.agent?.getEffectiveConfig().prompts;
|
|
106
|
+
if (!Array.isArray(prompts)) {
|
|
107
|
+
return void 0;
|
|
108
|
+
}
|
|
109
|
+
for (const prompt of prompts) {
|
|
110
|
+
if (!prompt || typeof prompt !== "object" || prompt.type !== "file") {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const filePath = typeof prompt.file === "string" ? prompt.file : void 0;
|
|
114
|
+
if (!filePath || import_node_path.default.basename(filePath) !== "SKILL.md") {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (import_node_path.default.basename(import_node_path.default.dirname(filePath)) === skillId) {
|
|
118
|
+
return filePath;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return void 0;
|
|
122
|
+
}
|
|
98
123
|
function resolveSkillBaseDirectory(scope, context) {
|
|
99
124
|
if (scope === "global") {
|
|
100
125
|
return { baseDir: (0, import_path.getDextoGlobalPath)("skills"), scope: "global" };
|
|
@@ -111,18 +136,48 @@ async function resolveSkillUpdateDirectory(input, context) {
|
|
|
111
136
|
if (input.scope === "global") {
|
|
112
137
|
return resolveSkillBaseDirectory("global", context);
|
|
113
138
|
}
|
|
114
|
-
const { primary, legacy } = resolveWorkspaceSkillDirs(context);
|
|
139
|
+
const { primary, secondary, legacy } = resolveWorkspaceSkillDirs(context);
|
|
115
140
|
const skillId = input.id.trim();
|
|
116
141
|
const primaryFile = import_node_path.default.join(primary, skillId, "SKILL.md");
|
|
117
142
|
if (await pathExists(primaryFile)) {
|
|
118
143
|
return { baseDir: primary, scope: "workspace" };
|
|
119
144
|
}
|
|
145
|
+
const secondaryFile = import_node_path.default.join(secondary, skillId, "SKILL.md");
|
|
146
|
+
if (await pathExists(secondaryFile)) {
|
|
147
|
+
return { baseDir: secondary, scope: "workspace" };
|
|
148
|
+
}
|
|
120
149
|
const legacyFile = import_node_path.default.join(legacy, skillId, "SKILL.md");
|
|
121
150
|
if (await pathExists(legacyFile)) {
|
|
122
151
|
return { baseDir: legacy, scope: "workspace" };
|
|
123
152
|
}
|
|
124
153
|
return { baseDir: primary, scope: "workspace" };
|
|
125
154
|
}
|
|
155
|
+
async function resolveExistingSkillLocation(input, context) {
|
|
156
|
+
if (input.scope !== "global") {
|
|
157
|
+
const registeredSkillFile = findRegisteredSkillFile(input.id.trim(), context);
|
|
158
|
+
if (registeredSkillFile) {
|
|
159
|
+
const skillDir2 = import_node_path.default.dirname(registeredSkillFile);
|
|
160
|
+
const baseDir2 = import_node_path.default.dirname(skillDir2);
|
|
161
|
+
ensurePathWithinBase(baseDir2, skillDir2, "skill_refresh");
|
|
162
|
+
return {
|
|
163
|
+
baseDir: baseDir2,
|
|
164
|
+
scope: "workspace",
|
|
165
|
+
skillDir: skillDir2,
|
|
166
|
+
skillFile: registeredSkillFile
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const { baseDir, scope } = await resolveSkillUpdateDirectory(input, context);
|
|
171
|
+
const skillDir = import_node_path.default.join(baseDir, input.id.trim());
|
|
172
|
+
ensurePathWithinBase(baseDir, skillDir, "skill_refresh");
|
|
173
|
+
const skillFile = import_node_path.default.join(skillDir, "SKILL.md");
|
|
174
|
+
return {
|
|
175
|
+
baseDir,
|
|
176
|
+
scope,
|
|
177
|
+
skillDir,
|
|
178
|
+
skillFile
|
|
179
|
+
};
|
|
180
|
+
}
|
|
126
181
|
function ensurePathWithinBase(baseDir, targetDir, toolId) {
|
|
127
182
|
const resolvedBase = import_node_path.default.resolve(baseDir);
|
|
128
183
|
const resolvedTarget = import_node_path.default.resolve(targetDir);
|
|
@@ -158,6 +213,7 @@ function formatFrontmatterList(key, values) {
|
|
|
158
213
|
function buildSkillMarkdownFromParts(options) {
|
|
159
214
|
const id = options.id.trim();
|
|
160
215
|
const title = titleizeSkillId(id) || id;
|
|
216
|
+
const body = normalizeSkillBody(options.content);
|
|
161
217
|
const lines = ["---"];
|
|
162
218
|
lines.push(formatFrontmatterLine("name", id));
|
|
163
219
|
lines.push(formatFrontmatterLine("description", options.description.trim()));
|
|
@@ -167,9 +223,18 @@ function buildSkillMarkdownFromParts(options) {
|
|
|
167
223
|
if (options.allowedTools && options.allowedTools.length > 0) {
|
|
168
224
|
lines.push(formatFrontmatterList("allowed-tools", options.allowedTools));
|
|
169
225
|
}
|
|
170
|
-
lines.push("---", "", `# ${title}
|
|
226
|
+
lines.push("---", "", `# ${title}`);
|
|
227
|
+
if (body.length > 0) {
|
|
228
|
+
lines.push("", body);
|
|
229
|
+
}
|
|
171
230
|
return lines.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
172
231
|
}
|
|
232
|
+
function normalizeSkillBody(content) {
|
|
233
|
+
const trimmed = content.trim();
|
|
234
|
+
const withoutFrontmatter = trimmed.replace(/^---\s*\n[\s\S]*?\n---\s*\n?/, "").trimStart();
|
|
235
|
+
const withoutLeadingH1 = withoutFrontmatter.replace(/^#\s+[^\n]+(?:\n+|$)/, "").trimStart();
|
|
236
|
+
return withoutLeadingH1.trim();
|
|
237
|
+
}
|
|
173
238
|
function buildSkillMarkdown(input) {
|
|
174
239
|
return buildSkillMarkdownFromParts({
|
|
175
240
|
id: input.id,
|
|
@@ -216,6 +281,29 @@ async function refreshAgentPrompts(context, skillFile) {
|
|
|
216
281
|
await agent.refreshPrompts(nextPrompts);
|
|
217
282
|
return true;
|
|
218
283
|
}
|
|
284
|
+
function buildSkillBundleNotes(bundledMcpServers) {
|
|
285
|
+
const notes = [
|
|
286
|
+
"Creating or editing files under mcps/ only defines bundled MCP config. It does not implement or verify the target MCP server.",
|
|
287
|
+
"After editing SKILL.md or bundled MCP files with non-creator tools, run skill_refresh so the current session sees the latest skill content and MCP metadata."
|
|
288
|
+
];
|
|
289
|
+
if (bundledMcpServers.length > 0) {
|
|
290
|
+
notes.push(
|
|
291
|
+
"Bundled MCP config is present. Only describe the skill as shipping a real MCP when the config points at a bundled runnable server or a verified external command/package."
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
return notes;
|
|
295
|
+
}
|
|
296
|
+
function inspectSkillBundle(skillDir, skillId) {
|
|
297
|
+
const bundledMcpResult = (0, import_core.loadBundledMcpConfigFromDirectory)(skillDir, skillId, {
|
|
298
|
+
scanNestedMcps: true
|
|
299
|
+
});
|
|
300
|
+
const bundledMcpServers = Object.keys(bundledMcpResult.mcpServers ?? {});
|
|
301
|
+
return {
|
|
302
|
+
bundledMcpServers,
|
|
303
|
+
bundledMcpWarnings: bundledMcpResult.warnings,
|
|
304
|
+
notes: buildSkillBundleNotes(bundledMcpServers)
|
|
305
|
+
};
|
|
306
|
+
}
|
|
219
307
|
const creatorToolsFactory = {
|
|
220
308
|
configSchema: import_schemas.CreatorToolsConfigSchema,
|
|
221
309
|
metadata: {
|
|
@@ -227,7 +315,7 @@ const creatorToolsFactory = {
|
|
|
227
315
|
const enabledTools = config.enabledTools ?? import_schemas.CREATOR_TOOL_NAMES;
|
|
228
316
|
const skillCreateTool = (0, import_core.defineTool)({
|
|
229
317
|
id: "skill_create",
|
|
230
|
-
description: "Create a standalone SKILL.md file and register it with the running agent.
|
|
318
|
+
description: "Create a standalone SKILL.md file, scaffold bundled resource directories, and register it with the running agent. This scaffolds mcps/ but does not implement or verify an MCP server.",
|
|
231
319
|
inputSchema: SkillCreateInputSchema,
|
|
232
320
|
execute: async (input, context) => {
|
|
233
321
|
const resolvedInput = resolveSkillCreateInput(input);
|
|
@@ -250,8 +338,14 @@ const creatorToolsFactory = {
|
|
|
250
338
|
const markdown = buildSkillMarkdown(resolvedInput);
|
|
251
339
|
await import_node_fs.promises.mkdir(skillDir, { recursive: true });
|
|
252
340
|
await import_node_fs.promises.writeFile(skillFile, markdown, "utf-8");
|
|
341
|
+
await Promise.all(
|
|
342
|
+
SKILL_RESOURCE_DIRECTORIES.map(
|
|
343
|
+
(directory) => import_node_fs.promises.mkdir(import_node_path.default.join(skillDir, directory), { recursive: true })
|
|
344
|
+
)
|
|
345
|
+
);
|
|
253
346
|
const refreshed = await refreshAgentPrompts(context, skillFile);
|
|
254
347
|
const displayName = titleizeSkillId(skillId) || skillId;
|
|
348
|
+
const bundleDetails = inspectSkillBundle(skillDir, skillId);
|
|
255
349
|
return {
|
|
256
350
|
created: true,
|
|
257
351
|
id: skillId,
|
|
@@ -259,7 +353,11 @@ const creatorToolsFactory = {
|
|
|
259
353
|
description: resolvedInput.description.trim(),
|
|
260
354
|
scope,
|
|
261
355
|
path: skillFile,
|
|
262
|
-
|
|
356
|
+
resourceDirectories: SKILL_RESOURCE_DIRECTORIES.map(
|
|
357
|
+
(directory) => import_node_path.default.join(skillDir, directory)
|
|
358
|
+
),
|
|
359
|
+
promptsRefreshed: refreshed,
|
|
360
|
+
...bundleDetails
|
|
263
361
|
};
|
|
264
362
|
}
|
|
265
363
|
});
|
|
@@ -273,10 +371,10 @@ const creatorToolsFactory = {
|
|
|
273
371
|
context: "skill_update",
|
|
274
372
|
hint: "Use kebab-case skill ids (e.g., release-notes)"
|
|
275
373
|
});
|
|
276
|
-
const {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
374
|
+
const { scope, skillDir, skillFile } = await resolveExistingSkillLocation(
|
|
375
|
+
input,
|
|
376
|
+
context
|
|
377
|
+
);
|
|
280
378
|
const exists = await pathExists(skillFile);
|
|
281
379
|
if (!exists) {
|
|
282
380
|
throw import_core.ToolError.validationFailed(
|
|
@@ -303,13 +401,53 @@ const creatorToolsFactory = {
|
|
|
303
401
|
});
|
|
304
402
|
await import_node_fs.promises.writeFile(skillFile, markdown, "utf-8");
|
|
305
403
|
const refreshed = await refreshAgentPrompts(context, skillFile);
|
|
404
|
+
const bundleDetails = inspectSkillBundle(skillDir, skillId);
|
|
306
405
|
return {
|
|
307
406
|
updated: true,
|
|
308
407
|
id: skillId,
|
|
309
408
|
description,
|
|
310
409
|
scope,
|
|
311
410
|
path: skillFile,
|
|
312
|
-
promptsRefreshed: refreshed
|
|
411
|
+
promptsRefreshed: refreshed,
|
|
412
|
+
...bundleDetails
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
const skillRefreshTool = (0, import_core.defineTool)({
|
|
417
|
+
id: "skill_refresh",
|
|
418
|
+
description: "Refresh one standalone skill bundle in the current session after editing SKILL.md, mcps/, scripts/, or references/. Rebuilds prompt metadata so bundled MCP servers can be discovered without restarting.",
|
|
419
|
+
inputSchema: SkillRefreshInputSchema,
|
|
420
|
+
execute: async (input, context) => {
|
|
421
|
+
const skillId = input.id.trim();
|
|
422
|
+
(0, import_core.assertValidPromptName)(skillId, {
|
|
423
|
+
context: "skill_refresh",
|
|
424
|
+
hint: "Use kebab-case skill ids (e.g., release-notes)"
|
|
425
|
+
});
|
|
426
|
+
if (!context.agent) {
|
|
427
|
+
throw import_core.ToolError.configInvalid(
|
|
428
|
+
"skill_refresh requires ToolExecutionContext.agent"
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
const { scope, skillDir, skillFile } = await resolveExistingSkillLocation(
|
|
432
|
+
input,
|
|
433
|
+
context
|
|
434
|
+
);
|
|
435
|
+
const exists = await pathExists(skillFile);
|
|
436
|
+
if (!exists) {
|
|
437
|
+
throw import_core.ToolError.validationFailed(
|
|
438
|
+
"skill_refresh",
|
|
439
|
+
`Skill not found at ${skillFile}`
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
const refreshed = await refreshAgentPrompts(context, skillFile);
|
|
443
|
+
const bundleDetails = inspectSkillBundle(skillDir, skillId);
|
|
444
|
+
return {
|
|
445
|
+
refreshed: true,
|
|
446
|
+
id: skillId,
|
|
447
|
+
scope,
|
|
448
|
+
path: skillFile,
|
|
449
|
+
promptsRefreshed: refreshed,
|
|
450
|
+
...bundleDetails
|
|
313
451
|
};
|
|
314
452
|
}
|
|
315
453
|
});
|
|
@@ -421,6 +559,7 @@ const creatorToolsFactory = {
|
|
|
421
559
|
const toolCreators = {
|
|
422
560
|
skill_create: () => skillCreateTool,
|
|
423
561
|
skill_update: () => skillUpdateTool,
|
|
562
|
+
skill_refresh: () => skillRefreshTool,
|
|
424
563
|
skill_search: () => skillSearchTool,
|
|
425
564
|
skill_list: () => skillListTool,
|
|
426
565
|
tool_catalog: () => toolCatalogTool
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/tool-factories/creator-tools/factory.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/tool-factories/creator-tools/factory.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAYvD,OAAO,EAIH,KAAK,kBAAkB,EAC1B,MAAM,cAAc,CAAC;AAqetB,eAAO,MAAM,mBAAmB,EAAE,WAAW,CAAC,kBAAkB,CAqT/D,CAAC"}
|
|
@@ -4,7 +4,8 @@ import { parse as yamlParse } from "yaml";
|
|
|
4
4
|
import {
|
|
5
5
|
ToolError,
|
|
6
6
|
defineTool,
|
|
7
|
-
assertValidPromptName
|
|
7
|
+
assertValidPromptName,
|
|
8
|
+
loadBundledMcpConfigFromDirectory
|
|
8
9
|
} from "@dexto/core";
|
|
9
10
|
import { discoverStandaloneSkills, getSkillSearchPaths } from "../../plugins/discover-skills.js";
|
|
10
11
|
import {
|
|
@@ -34,6 +35,10 @@ const SkillListInputSchema = z.object({
|
|
|
34
35
|
projectPath: z.string().optional(),
|
|
35
36
|
query: z.string().optional().describe("Optional search term to filter skills by name or path")
|
|
36
37
|
}).strict();
|
|
38
|
+
const SkillRefreshInputSchema = z.object({
|
|
39
|
+
id: z.string().min(1).describe("Skill id to refresh in the running agent session."),
|
|
40
|
+
scope: z.enum(["global", "workspace"]).optional()
|
|
41
|
+
}).strict();
|
|
37
42
|
const SkillSearchInputSchema = z.object({
|
|
38
43
|
query: z.string().optional().describe("Optional search term to filter skills by name or description"),
|
|
39
44
|
limit: z.number().int().min(1).max(200).optional().describe(
|
|
@@ -45,6 +50,7 @@ const ToolCatalogInputSchema = z.object({
|
|
|
45
50
|
limit: z.number().int().min(1).max(500).optional().describe("Maximum number of tools to return (defaults to all)."),
|
|
46
51
|
includeDescriptions: z.boolean().optional().describe("Include tool descriptions (defaults to true).")
|
|
47
52
|
}).strict();
|
|
53
|
+
const SKILL_RESOURCE_DIRECTORIES = ["handlers", "scripts", "mcps", "references"];
|
|
48
54
|
function normalizeSkillQuery(value) {
|
|
49
55
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
50
56
|
}
|
|
@@ -65,10 +71,30 @@ function resolveWorkspaceBasePath(context) {
|
|
|
65
71
|
function resolveWorkspaceSkillDirs(context) {
|
|
66
72
|
const base = resolveWorkspaceBasePath(context);
|
|
67
73
|
return {
|
|
68
|
-
primary: path.join(base, "
|
|
74
|
+
primary: path.join(base, "skills"),
|
|
75
|
+
secondary: path.join(base, ".agents", "skills"),
|
|
69
76
|
legacy: path.join(base, ".dexto", "skills")
|
|
70
77
|
};
|
|
71
78
|
}
|
|
79
|
+
function findRegisteredSkillFile(skillId, context) {
|
|
80
|
+
const prompts = context.agent?.getEffectiveConfig().prompts;
|
|
81
|
+
if (!Array.isArray(prompts)) {
|
|
82
|
+
return void 0;
|
|
83
|
+
}
|
|
84
|
+
for (const prompt of prompts) {
|
|
85
|
+
if (!prompt || typeof prompt !== "object" || prompt.type !== "file") {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const filePath = typeof prompt.file === "string" ? prompt.file : void 0;
|
|
89
|
+
if (!filePath || path.basename(filePath) !== "SKILL.md") {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (path.basename(path.dirname(filePath)) === skillId) {
|
|
93
|
+
return filePath;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return void 0;
|
|
97
|
+
}
|
|
72
98
|
function resolveSkillBaseDirectory(scope, context) {
|
|
73
99
|
if (scope === "global") {
|
|
74
100
|
return { baseDir: getDextoGlobalPath("skills"), scope: "global" };
|
|
@@ -85,18 +111,48 @@ async function resolveSkillUpdateDirectory(input, context) {
|
|
|
85
111
|
if (input.scope === "global") {
|
|
86
112
|
return resolveSkillBaseDirectory("global", context);
|
|
87
113
|
}
|
|
88
|
-
const { primary, legacy } = resolveWorkspaceSkillDirs(context);
|
|
114
|
+
const { primary, secondary, legacy } = resolveWorkspaceSkillDirs(context);
|
|
89
115
|
const skillId = input.id.trim();
|
|
90
116
|
const primaryFile = path.join(primary, skillId, "SKILL.md");
|
|
91
117
|
if (await pathExists(primaryFile)) {
|
|
92
118
|
return { baseDir: primary, scope: "workspace" };
|
|
93
119
|
}
|
|
120
|
+
const secondaryFile = path.join(secondary, skillId, "SKILL.md");
|
|
121
|
+
if (await pathExists(secondaryFile)) {
|
|
122
|
+
return { baseDir: secondary, scope: "workspace" };
|
|
123
|
+
}
|
|
94
124
|
const legacyFile = path.join(legacy, skillId, "SKILL.md");
|
|
95
125
|
if (await pathExists(legacyFile)) {
|
|
96
126
|
return { baseDir: legacy, scope: "workspace" };
|
|
97
127
|
}
|
|
98
128
|
return { baseDir: primary, scope: "workspace" };
|
|
99
129
|
}
|
|
130
|
+
async function resolveExistingSkillLocation(input, context) {
|
|
131
|
+
if (input.scope !== "global") {
|
|
132
|
+
const registeredSkillFile = findRegisteredSkillFile(input.id.trim(), context);
|
|
133
|
+
if (registeredSkillFile) {
|
|
134
|
+
const skillDir2 = path.dirname(registeredSkillFile);
|
|
135
|
+
const baseDir2 = path.dirname(skillDir2);
|
|
136
|
+
ensurePathWithinBase(baseDir2, skillDir2, "skill_refresh");
|
|
137
|
+
return {
|
|
138
|
+
baseDir: baseDir2,
|
|
139
|
+
scope: "workspace",
|
|
140
|
+
skillDir: skillDir2,
|
|
141
|
+
skillFile: registeredSkillFile
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const { baseDir, scope } = await resolveSkillUpdateDirectory(input, context);
|
|
146
|
+
const skillDir = path.join(baseDir, input.id.trim());
|
|
147
|
+
ensurePathWithinBase(baseDir, skillDir, "skill_refresh");
|
|
148
|
+
const skillFile = path.join(skillDir, "SKILL.md");
|
|
149
|
+
return {
|
|
150
|
+
baseDir,
|
|
151
|
+
scope,
|
|
152
|
+
skillDir,
|
|
153
|
+
skillFile
|
|
154
|
+
};
|
|
155
|
+
}
|
|
100
156
|
function ensurePathWithinBase(baseDir, targetDir, toolId) {
|
|
101
157
|
const resolvedBase = path.resolve(baseDir);
|
|
102
158
|
const resolvedTarget = path.resolve(targetDir);
|
|
@@ -132,6 +188,7 @@ function formatFrontmatterList(key, values) {
|
|
|
132
188
|
function buildSkillMarkdownFromParts(options) {
|
|
133
189
|
const id = options.id.trim();
|
|
134
190
|
const title = titleizeSkillId(id) || id;
|
|
191
|
+
const body = normalizeSkillBody(options.content);
|
|
135
192
|
const lines = ["---"];
|
|
136
193
|
lines.push(formatFrontmatterLine("name", id));
|
|
137
194
|
lines.push(formatFrontmatterLine("description", options.description.trim()));
|
|
@@ -141,9 +198,18 @@ function buildSkillMarkdownFromParts(options) {
|
|
|
141
198
|
if (options.allowedTools && options.allowedTools.length > 0) {
|
|
142
199
|
lines.push(formatFrontmatterList("allowed-tools", options.allowedTools));
|
|
143
200
|
}
|
|
144
|
-
lines.push("---", "", `# ${title}
|
|
201
|
+
lines.push("---", "", `# ${title}`);
|
|
202
|
+
if (body.length > 0) {
|
|
203
|
+
lines.push("", body);
|
|
204
|
+
}
|
|
145
205
|
return lines.join("\n").replace(/\n{3,}/g, "\n\n");
|
|
146
206
|
}
|
|
207
|
+
function normalizeSkillBody(content) {
|
|
208
|
+
const trimmed = content.trim();
|
|
209
|
+
const withoutFrontmatter = trimmed.replace(/^---\s*\n[\s\S]*?\n---\s*\n?/, "").trimStart();
|
|
210
|
+
const withoutLeadingH1 = withoutFrontmatter.replace(/^#\s+[^\n]+(?:\n+|$)/, "").trimStart();
|
|
211
|
+
return withoutLeadingH1.trim();
|
|
212
|
+
}
|
|
147
213
|
function buildSkillMarkdown(input) {
|
|
148
214
|
return buildSkillMarkdownFromParts({
|
|
149
215
|
id: input.id,
|
|
@@ -190,6 +256,29 @@ async function refreshAgentPrompts(context, skillFile) {
|
|
|
190
256
|
await agent.refreshPrompts(nextPrompts);
|
|
191
257
|
return true;
|
|
192
258
|
}
|
|
259
|
+
function buildSkillBundleNotes(bundledMcpServers) {
|
|
260
|
+
const notes = [
|
|
261
|
+
"Creating or editing files under mcps/ only defines bundled MCP config. It does not implement or verify the target MCP server.",
|
|
262
|
+
"After editing SKILL.md or bundled MCP files with non-creator tools, run skill_refresh so the current session sees the latest skill content and MCP metadata."
|
|
263
|
+
];
|
|
264
|
+
if (bundledMcpServers.length > 0) {
|
|
265
|
+
notes.push(
|
|
266
|
+
"Bundled MCP config is present. Only describe the skill as shipping a real MCP when the config points at a bundled runnable server or a verified external command/package."
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
return notes;
|
|
270
|
+
}
|
|
271
|
+
function inspectSkillBundle(skillDir, skillId) {
|
|
272
|
+
const bundledMcpResult = loadBundledMcpConfigFromDirectory(skillDir, skillId, {
|
|
273
|
+
scanNestedMcps: true
|
|
274
|
+
});
|
|
275
|
+
const bundledMcpServers = Object.keys(bundledMcpResult.mcpServers ?? {});
|
|
276
|
+
return {
|
|
277
|
+
bundledMcpServers,
|
|
278
|
+
bundledMcpWarnings: bundledMcpResult.warnings,
|
|
279
|
+
notes: buildSkillBundleNotes(bundledMcpServers)
|
|
280
|
+
};
|
|
281
|
+
}
|
|
193
282
|
const creatorToolsFactory = {
|
|
194
283
|
configSchema: CreatorToolsConfigSchema,
|
|
195
284
|
metadata: {
|
|
@@ -201,7 +290,7 @@ const creatorToolsFactory = {
|
|
|
201
290
|
const enabledTools = config.enabledTools ?? CREATOR_TOOL_NAMES;
|
|
202
291
|
const skillCreateTool = defineTool({
|
|
203
292
|
id: "skill_create",
|
|
204
|
-
description: "Create a standalone SKILL.md file and register it with the running agent.
|
|
293
|
+
description: "Create a standalone SKILL.md file, scaffold bundled resource directories, and register it with the running agent. This scaffolds mcps/ but does not implement or verify an MCP server.",
|
|
205
294
|
inputSchema: SkillCreateInputSchema,
|
|
206
295
|
execute: async (input, context) => {
|
|
207
296
|
const resolvedInput = resolveSkillCreateInput(input);
|
|
@@ -224,8 +313,14 @@ const creatorToolsFactory = {
|
|
|
224
313
|
const markdown = buildSkillMarkdown(resolvedInput);
|
|
225
314
|
await fs.mkdir(skillDir, { recursive: true });
|
|
226
315
|
await fs.writeFile(skillFile, markdown, "utf-8");
|
|
316
|
+
await Promise.all(
|
|
317
|
+
SKILL_RESOURCE_DIRECTORIES.map(
|
|
318
|
+
(directory) => fs.mkdir(path.join(skillDir, directory), { recursive: true })
|
|
319
|
+
)
|
|
320
|
+
);
|
|
227
321
|
const refreshed = await refreshAgentPrompts(context, skillFile);
|
|
228
322
|
const displayName = titleizeSkillId(skillId) || skillId;
|
|
323
|
+
const bundleDetails = inspectSkillBundle(skillDir, skillId);
|
|
229
324
|
return {
|
|
230
325
|
created: true,
|
|
231
326
|
id: skillId,
|
|
@@ -233,7 +328,11 @@ const creatorToolsFactory = {
|
|
|
233
328
|
description: resolvedInput.description.trim(),
|
|
234
329
|
scope,
|
|
235
330
|
path: skillFile,
|
|
236
|
-
|
|
331
|
+
resourceDirectories: SKILL_RESOURCE_DIRECTORIES.map(
|
|
332
|
+
(directory) => path.join(skillDir, directory)
|
|
333
|
+
),
|
|
334
|
+
promptsRefreshed: refreshed,
|
|
335
|
+
...bundleDetails
|
|
237
336
|
};
|
|
238
337
|
}
|
|
239
338
|
});
|
|
@@ -247,10 +346,10 @@ const creatorToolsFactory = {
|
|
|
247
346
|
context: "skill_update",
|
|
248
347
|
hint: "Use kebab-case skill ids (e.g., release-notes)"
|
|
249
348
|
});
|
|
250
|
-
const {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
349
|
+
const { scope, skillDir, skillFile } = await resolveExistingSkillLocation(
|
|
350
|
+
input,
|
|
351
|
+
context
|
|
352
|
+
);
|
|
254
353
|
const exists = await pathExists(skillFile);
|
|
255
354
|
if (!exists) {
|
|
256
355
|
throw ToolError.validationFailed(
|
|
@@ -277,13 +376,53 @@ const creatorToolsFactory = {
|
|
|
277
376
|
});
|
|
278
377
|
await fs.writeFile(skillFile, markdown, "utf-8");
|
|
279
378
|
const refreshed = await refreshAgentPrompts(context, skillFile);
|
|
379
|
+
const bundleDetails = inspectSkillBundle(skillDir, skillId);
|
|
280
380
|
return {
|
|
281
381
|
updated: true,
|
|
282
382
|
id: skillId,
|
|
283
383
|
description,
|
|
284
384
|
scope,
|
|
285
385
|
path: skillFile,
|
|
286
|
-
promptsRefreshed: refreshed
|
|
386
|
+
promptsRefreshed: refreshed,
|
|
387
|
+
...bundleDetails
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
const skillRefreshTool = defineTool({
|
|
392
|
+
id: "skill_refresh",
|
|
393
|
+
description: "Refresh one standalone skill bundle in the current session after editing SKILL.md, mcps/, scripts/, or references/. Rebuilds prompt metadata so bundled MCP servers can be discovered without restarting.",
|
|
394
|
+
inputSchema: SkillRefreshInputSchema,
|
|
395
|
+
execute: async (input, context) => {
|
|
396
|
+
const skillId = input.id.trim();
|
|
397
|
+
assertValidPromptName(skillId, {
|
|
398
|
+
context: "skill_refresh",
|
|
399
|
+
hint: "Use kebab-case skill ids (e.g., release-notes)"
|
|
400
|
+
});
|
|
401
|
+
if (!context.agent) {
|
|
402
|
+
throw ToolError.configInvalid(
|
|
403
|
+
"skill_refresh requires ToolExecutionContext.agent"
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
const { scope, skillDir, skillFile } = await resolveExistingSkillLocation(
|
|
407
|
+
input,
|
|
408
|
+
context
|
|
409
|
+
);
|
|
410
|
+
const exists = await pathExists(skillFile);
|
|
411
|
+
if (!exists) {
|
|
412
|
+
throw ToolError.validationFailed(
|
|
413
|
+
"skill_refresh",
|
|
414
|
+
`Skill not found at ${skillFile}`
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
const refreshed = await refreshAgentPrompts(context, skillFile);
|
|
418
|
+
const bundleDetails = inspectSkillBundle(skillDir, skillId);
|
|
419
|
+
return {
|
|
420
|
+
refreshed: true,
|
|
421
|
+
id: skillId,
|
|
422
|
+
scope,
|
|
423
|
+
path: skillFile,
|
|
424
|
+
promptsRefreshed: refreshed,
|
|
425
|
+
...bundleDetails
|
|
287
426
|
};
|
|
288
427
|
}
|
|
289
428
|
});
|
|
@@ -395,6 +534,7 @@ const creatorToolsFactory = {
|
|
|
395
534
|
const toolCreators = {
|
|
396
535
|
skill_create: () => skillCreateTool,
|
|
397
536
|
skill_update: () => skillUpdateTool,
|
|
537
|
+
skill_refresh: () => skillRefreshTool,
|
|
398
538
|
skill_search: () => skillSearchTool,
|
|
399
539
|
skill_list: () => skillListTool,
|
|
400
540
|
tool_catalog: () => toolCatalogTool
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
export declare const CREATOR_TOOL_NAMES: readonly ["skill_create", "skill_update", "skill_search", "skill_list", "tool_catalog"];
|
|
2
|
+
export declare const CREATOR_TOOL_NAMES: readonly ["skill_create", "skill_update", "skill_refresh", "skill_search", "skill_list", "tool_catalog"];
|
|
3
3
|
export type CreatorToolName = (typeof CREATOR_TOOL_NAMES)[number];
|
|
4
4
|
export declare const CreatorToolsConfigSchema: z.ZodObject<{
|
|
5
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">>;
|
|
6
|
+
enabledTools: z.ZodOptional<z.ZodArray<z.ZodEnum<["skill_create", "skill_update", "skill_refresh", "skill_search", "skill_list", "tool_catalog"]>, "many">>;
|
|
7
7
|
}, "strict", z.ZodTypeAny, {
|
|
8
8
|
type: "creator-tools";
|
|
9
|
-
enabledTools?: ("skill_create" | "skill_update" | "skill_search" | "skill_list" | "tool_catalog")[] | undefined;
|
|
9
|
+
enabledTools?: ("skill_create" | "skill_update" | "skill_refresh" | "skill_search" | "skill_list" | "tool_catalog")[] | undefined;
|
|
10
10
|
}, {
|
|
11
11
|
type: "creator-tools";
|
|
12
|
-
enabledTools?: ("skill_create" | "skill_update" | "skill_search" | "skill_list" | "tool_catalog")[] | undefined;
|
|
12
|
+
enabledTools?: ("skill_create" | "skill_update" | "skill_refresh" | "skill_search" | "skill_list" | "tool_catalog")[] | undefined;
|
|
13
13
|
}>;
|
|
14
14
|
export type CreatorToolsConfig = z.output<typeof CreatorToolsConfigSchema>;
|
|
15
15
|
//# sourceMappingURL=schemas.d.ts.map
|
|
@@ -1 +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,
|
|
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,0GAOrB,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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dexto/agent-management",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.19",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -14,12 +14,12 @@
|
|
|
14
14
|
"./package.json": "./package.json"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"yaml": "^2.
|
|
17
|
+
"yaml": "^2.8.3",
|
|
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.19",
|
|
20
|
+
"@dexto/core": "1.6.19",
|
|
21
|
+
"@dexto/orchestration": "1.6.19",
|
|
22
|
+
"@dexto/tools-builtins": "1.6.19"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/node": "^22.13.5"
|