@dexto/agent-management 1.6.16 → 1.6.18

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.
@@ -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
  };
@@ -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;AAI9C,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;CAC9B;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;CAmB5F,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"}
@@ -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
  };
@@ -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
  );
@@ -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,EAAE,UAAU,EAAU,KAAK,yBAAyB,EAAE,MAAM,aAAa,CAAC;AACjF,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;CACrD,CAAC;AA0DF,wBAAsB,0BAA0B,CAC5C,OAAO,EAAE,iCAAiC,GAC3C,OAAO,CAAC,UAAU,CAAC,CAsCrB"}
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"}
@@ -6,7 +6,10 @@ import {
6
6
  resolveServicesFromConfig,
7
7
  toDextoAgentOptions
8
8
  } from "@dexto/agent-config";
9
- import { DextoAgent, logger } from "@dexto/core";
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;;;;;;;;;;;;;;;GAeG;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;CAC9B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,eAAe,EAAE,CAkEhF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAW9C"}
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 import_schemas = require("./schemas.js");
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 mcpConfig = loadMcpConfig(pluginPath, pluginName, warnings);
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;AAKH,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAkC,MAAM,YAAY,CAAC;AAEjG;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,gBAAgB,GAAG,YAAY,CAqE3E"}
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 { PluginMCPConfigSchema } from "./schemas.js";
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 mcpConfig = loadMcpConfig(pluginPath, pluginName, warnings);
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, ".agents", "skills"),
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}`, "", options.content.trim());
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. Provide id, description, content, and optional toolkits/allowedTools.",
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
- promptsRefreshed: refreshed
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 { baseDir, scope } = await resolveSkillUpdateDirectory(input, context);
277
- const skillDir = import_node_path.default.join(baseDir, skillId);
278
- ensurePathWithinBase(baseDir, skillDir, "skill_update");
279
- const skillFile = import_node_path.default.join(skillDir, "SKILL.md");
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;AAWvD,OAAO,EAIH,KAAK,kBAAkB,EAC1B,MAAM,cAAc,CAAC;AAkWtB,eAAO,MAAM,mBAAmB,EAAE,WAAW,CAAC,kBAAkB,CA6P/D,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, ".agents", "skills"),
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}`, "", options.content.trim());
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. Provide id, description, content, and optional toolkits/allowedTools.",
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
- promptsRefreshed: refreshed
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 { 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");
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
@@ -26,6 +26,7 @@ var import_zod = require("zod");
26
26
  const CREATOR_TOOL_NAMES = [
27
27
  "skill_create",
28
28
  "skill_update",
29
+ "skill_refresh",
29
30
  "skill_search",
30
31
  "skill_list",
31
32
  "tool_catalog"
@@ -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,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"}
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"}
@@ -2,6 +2,7 @@ import { z } from "zod";
2
2
  const CREATOR_TOOL_NAMES = [
3
3
  "skill_create",
4
4
  "skill_update",
5
+ "skill_refresh",
5
6
  "skill_search",
6
7
  "skill_list",
7
8
  "tool_catalog"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dexto/agent-management",
3
- "version": "1.6.16",
3
+ "version": "1.6.18",
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.16",
20
- "@dexto/core": "1.6.16",
21
- "@dexto/orchestration": "1.6.16",
22
- "@dexto/tools-builtins": "1.6.16"
19
+ "@dexto/agent-config": "1.6.18",
20
+ "@dexto/core": "1.6.18",
21
+ "@dexto/orchestration": "1.6.18",
22
+ "@dexto/tools-builtins": "1.6.18"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/node": "^22.13.5"