@enactprotocol/shared 2.2.4 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -18
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +32 -6
- package/dist/config.js.map +1 -1
- package/dist/execution/action-command.d.ts +131 -0
- package/dist/execution/action-command.d.ts.map +1 -0
- package/dist/execution/action-command.js +300 -0
- package/dist/execution/action-command.js.map +1 -0
- package/dist/execution/command.d.ts +8 -8
- package/dist/execution/command.js +6 -6
- package/dist/execution/index.d.ts +1 -0
- package/dist/execution/index.d.ts.map +1 -1
- package/dist/execution/index.js +2 -0
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/types.d.ts +5 -2
- package/dist/execution/types.d.ts.map +1 -1
- package/dist/execution/types.js.map +1 -1
- package/dist/index.d.ts +8 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -4
- package/dist/index.js.map +1 -1
- package/dist/manifest/actions-loader.d.ts +29 -0
- package/dist/manifest/actions-loader.d.ts.map +1 -0
- package/dist/manifest/actions-loader.js +34 -0
- package/dist/manifest/actions-loader.js.map +1 -0
- package/dist/manifest/actions-parser.d.ts +69 -0
- package/dist/manifest/actions-parser.d.ts.map +1 -0
- package/dist/manifest/actions-parser.js +265 -0
- package/dist/manifest/actions-parser.js.map +1 -0
- package/dist/manifest/index.d.ts +2 -0
- package/dist/manifest/index.d.ts.map +1 -1
- package/dist/manifest/index.js +4 -0
- package/dist/manifest/index.js.map +1 -1
- package/dist/manifest/loader.d.ts +7 -2
- package/dist/manifest/loader.d.ts.map +1 -1
- package/dist/manifest/loader.js +71 -4
- package/dist/manifest/loader.js.map +1 -1
- package/dist/manifest/parser.d.ts +1 -0
- package/dist/manifest/parser.d.ts.map +1 -1
- package/dist/manifest/parser.js +1 -0
- package/dist/manifest/parser.js.map +1 -1
- package/dist/manifest/scripts.d.ts +19 -0
- package/dist/manifest/scripts.d.ts.map +1 -0
- package/dist/manifest/scripts.js +102 -0
- package/dist/manifest/scripts.js.map +1 -0
- package/dist/manifest/validator.d.ts +1 -8
- package/dist/manifest/validator.d.ts.map +1 -1
- package/dist/manifest/validator.js +14 -13
- package/dist/manifest/validator.js.map +1 -1
- package/dist/mcp-registry.js +5 -5
- package/dist/mcp-registry.js.map +1 -1
- package/dist/paths.d.ts +9 -2
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +12 -3
- package/dist/paths.js.map +1 -1
- package/dist/registry.d.ts +3 -2
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +5 -5
- package/dist/registry.js.map +1 -1
- package/dist/resolver.d.ts +55 -4
- package/dist/resolver.d.ts.map +1 -1
- package/dist/resolver.js +133 -75
- package/dist/resolver.js.map +1 -1
- package/dist/types/actions.d.ts +194 -0
- package/dist/types/actions.d.ts.map +1 -0
- package/dist/types/actions.js +32 -0
- package/dist/types/actions.js.map +1 -0
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/manifest.d.ts +50 -5
- package/dist/types/manifest.d.ts.map +1 -1
- package/dist/types/manifest.js +10 -2
- package/dist/types/manifest.js.map +1 -1
- package/package.json +2 -2
- package/src/config.ts +48 -6
- package/src/execution/action-command.ts +417 -0
- package/src/execution/command.ts +8 -8
- package/src/execution/index.ts +17 -0
- package/src/execution/types.ts +13 -2
- package/src/index.ts +37 -0
- package/src/manifest/actions-loader.ts +49 -0
- package/src/manifest/index.ts +12 -0
- package/src/manifest/loader.ts +77 -4
- package/src/manifest/parser.ts +1 -0
- package/src/manifest/scripts.ts +116 -0
- package/src/manifest/validator.ts +15 -14
- package/src/mcp-registry.ts +5 -5
- package/src/paths.ts +13 -3
- package/src/registry.ts +5 -5
- package/src/resolver.ts +172 -77
- package/src/types/actions.ts +223 -0
- package/src/types/index.ts +11 -0
- package/src/types/manifest.ts +67 -6
- package/tests/action-command.test.ts +249 -0
- package/tests/config-normalization.test.ts +279 -0
- package/tests/config.test.ts +4 -1
- package/tests/effective-input-schema.test.ts +86 -0
- package/tests/fixtures/valid-tool.md +5 -12
- package/tests/fixtures/valid-tool.yaml +3 -10
- package/tests/hooks.test.ts +177 -0
- package/tests/manifest/loader.test.ts +34 -20
- package/tests/manifest/parser.test.ts +11 -15
- package/tests/manifest/validator.test.ts +7 -17
- package/tests/manifest-types.test.ts +9 -11
- package/tests/paths.test.ts +11 -4
- package/tests/registry.test.ts +12 -11
- package/tests/resolver.test.ts +11 -7
- package/tsconfig.tsbuildinfo +1 -1
package/src/execution/command.ts
CHANGED
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
*
|
|
6
6
|
* ## Parameter Recognition
|
|
7
7
|
*
|
|
8
|
-
* Only `${...}` patterns that match
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Only `${...}` patterns that match known parameter names are substituted.
|
|
9
|
+
* Other `${...}` patterns (like bash variables, arrays, etc.) are passed
|
|
10
|
+
* through unchanged to the shell.
|
|
11
11
|
*
|
|
12
12
|
* This allows natural use of shell syntax:
|
|
13
|
-
* - `${name}` - Substituted if "name" is
|
|
14
|
-
* - `${MY_VAR}` - Passed through to bash (not
|
|
15
|
-
* - `${array[$i]}` - Passed through to bash (not
|
|
13
|
+
* - `${name}` - Substituted if "name" is a known parameter
|
|
14
|
+
* - `${MY_VAR}` - Passed through to bash (not a known parameter)
|
|
15
|
+
* - `${array[$i]}` - Passed through to bash (not a known parameter)
|
|
16
16
|
*
|
|
17
17
|
* ## Quoting Behavior
|
|
18
18
|
*
|
|
@@ -50,9 +50,9 @@ const PARAM_PATTERN = /\$\{([^}:]+)(?::([^}]+))?\}/g;
|
|
|
50
50
|
*/
|
|
51
51
|
export interface ParseCommandOptions {
|
|
52
52
|
/**
|
|
53
|
-
* Set of known parameter names
|
|
53
|
+
* Set of known parameter names.
|
|
54
54
|
* Only ${...} patterns matching these names will be treated as parameters.
|
|
55
|
-
* If not provided, ALL ${...} patterns are treated as parameters
|
|
55
|
+
* If not provided, ALL ${...} patterns are treated as parameters.
|
|
56
56
|
*/
|
|
57
57
|
knownParameters?: Set<string>;
|
|
58
58
|
}
|
package/src/execution/index.ts
CHANGED
|
@@ -73,5 +73,22 @@ export {
|
|
|
73
73
|
getParamInfo,
|
|
74
74
|
} from "./validation.js";
|
|
75
75
|
|
|
76
|
+
// Action command interpolation ({{param}} templates)
|
|
77
|
+
export {
|
|
78
|
+
parseActionCommand,
|
|
79
|
+
parseActionArgument,
|
|
80
|
+
interpolateActionCommand,
|
|
81
|
+
prepareActionCommand,
|
|
82
|
+
getMissingRequiredParams,
|
|
83
|
+
getActionCommandParams,
|
|
84
|
+
hasActionTemplates,
|
|
85
|
+
type ActionCommandToken,
|
|
86
|
+
type ActionCommandLiteralToken,
|
|
87
|
+
type ActionCommandParamToken,
|
|
88
|
+
type ParsedArgument,
|
|
89
|
+
type ParsedActionCommand,
|
|
90
|
+
type ActionInterpolationOptions,
|
|
91
|
+
} from "./action-command.js";
|
|
92
|
+
|
|
76
93
|
// NOTE: Dagger provider moved to @enactprotocol/execution package
|
|
77
94
|
// This keeps @enactprotocol/shared browser-safe (no Dagger SDK dependency)
|
package/src/execution/types.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Provides interfaces for tool execution, container management, and results
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import type { Action, ActionsManifest } from "../types/actions";
|
|
6
7
|
import type { ToolManifest } from "../types/manifest";
|
|
7
8
|
|
|
8
9
|
// ============================================================================
|
|
@@ -302,6 +303,16 @@ export interface ExecutionProvider {
|
|
|
302
303
|
options?: ExecutionOptions
|
|
303
304
|
): Promise<ExecutionResult>;
|
|
304
305
|
|
|
306
|
+
/** Execute an action from ACTIONS.yaml */
|
|
307
|
+
executeAction(
|
|
308
|
+
manifest: ToolManifest,
|
|
309
|
+
actionsManifest: ActionsManifest,
|
|
310
|
+
actionName: string,
|
|
311
|
+
action: Action,
|
|
312
|
+
input: ExecutionInput,
|
|
313
|
+
options?: ExecutionOptions
|
|
314
|
+
): Promise<ExecutionResult>;
|
|
315
|
+
|
|
305
316
|
/** Shutdown the provider */
|
|
306
317
|
shutdown(): Promise<void>;
|
|
307
318
|
}
|
|
@@ -342,9 +353,9 @@ export interface InterpolationOptions {
|
|
|
342
353
|
/** Callback for warnings (e.g., potential double-quoting) */
|
|
343
354
|
onWarning?: (warning: CommandWarning) => void;
|
|
344
355
|
/**
|
|
345
|
-
* Set of known parameter names
|
|
356
|
+
* Set of known parameter names.
|
|
346
357
|
* Only ${...} patterns matching these names will be substituted.
|
|
347
|
-
* If not provided, ALL ${...} patterns are treated as parameters
|
|
358
|
+
* If not provided, ALL ${...} patterns are treated as parameters.
|
|
348
359
|
*/
|
|
349
360
|
knownParameters?: Set<string>;
|
|
350
361
|
}
|
package/src/index.ts
CHANGED
|
@@ -25,6 +25,7 @@ export {
|
|
|
25
25
|
getEnactHome,
|
|
26
26
|
getProjectEnactDir,
|
|
27
27
|
getToolsDir,
|
|
28
|
+
getSkillsDir,
|
|
28
29
|
getCacheDir,
|
|
29
30
|
getConfigPath,
|
|
30
31
|
getGlobalEnvPath,
|
|
@@ -60,6 +61,7 @@ export {
|
|
|
60
61
|
type TrustConfig,
|
|
61
62
|
type CacheConfig,
|
|
62
63
|
type ExecutionConfig,
|
|
64
|
+
type ExecutionBackend,
|
|
63
65
|
type RegistryConfig,
|
|
64
66
|
} from "./config";
|
|
65
67
|
|
|
@@ -80,10 +82,21 @@ export type {
|
|
|
80
82
|
ToolLocation,
|
|
81
83
|
ToolResolution,
|
|
82
84
|
ManifestFileName,
|
|
85
|
+
ScriptDefinition,
|
|
83
86
|
} from "./types/manifest";
|
|
84
87
|
|
|
85
88
|
export { MANIFEST_FILES, PACKAGE_MANIFEST_FILE } from "./types/manifest";
|
|
86
89
|
|
|
90
|
+
// Actions types (internal — used by scripts bridge and execution pipeline)
|
|
91
|
+
export type {
|
|
92
|
+
ActionEnvVar,
|
|
93
|
+
ActionEnvVars,
|
|
94
|
+
Action,
|
|
95
|
+
ActionsManifest,
|
|
96
|
+
} from "./types/actions";
|
|
97
|
+
|
|
98
|
+
export { DEFAULT_INPUT_SCHEMA, getEffectiveInputSchema } from "./types/actions";
|
|
99
|
+
|
|
87
100
|
// Manifest parsing, validation, and loading
|
|
88
101
|
export {
|
|
89
102
|
// Parser
|
|
@@ -110,6 +123,12 @@ export {
|
|
|
110
123
|
tryLoadManifest,
|
|
111
124
|
tryLoadManifestFromDir,
|
|
112
125
|
type LoadedManifest,
|
|
126
|
+
// Manifest with scripts
|
|
127
|
+
loadManifestWithActions,
|
|
128
|
+
type LoadedManifestWithActions,
|
|
129
|
+
// Scripts (inline scripts → Action bridge)
|
|
130
|
+
scriptToAction,
|
|
131
|
+
manifestScriptsToActionsManifest,
|
|
113
132
|
} from "./manifest";
|
|
114
133
|
|
|
115
134
|
// Tool resolver
|
|
@@ -118,6 +137,9 @@ export {
|
|
|
118
137
|
resolveTool,
|
|
119
138
|
resolveToolAuto,
|
|
120
139
|
resolveToolFromPath,
|
|
140
|
+
resolveToolWithAction,
|
|
141
|
+
resolveAction,
|
|
142
|
+
parseActionSpecifier,
|
|
121
143
|
tryResolveTool,
|
|
122
144
|
tryResolveToolDetailed,
|
|
123
145
|
normalizeToolName,
|
|
@@ -126,6 +148,7 @@ export {
|
|
|
126
148
|
getToolSearchPaths,
|
|
127
149
|
type ResolveOptions,
|
|
128
150
|
type TryResolveResult,
|
|
151
|
+
type ParsedActionSpecifier,
|
|
129
152
|
} from "./resolver";
|
|
130
153
|
|
|
131
154
|
// Local tool registry (tools.json management)
|
|
@@ -281,4 +304,18 @@ export {
|
|
|
281
304
|
applyDefaults,
|
|
282
305
|
getRequiredParams,
|
|
283
306
|
getParamInfo,
|
|
307
|
+
// Action command ({{param}} templates)
|
|
308
|
+
parseActionCommand,
|
|
309
|
+
parseActionArgument,
|
|
310
|
+
interpolateActionCommand,
|
|
311
|
+
prepareActionCommand,
|
|
312
|
+
getMissingRequiredParams,
|
|
313
|
+
getActionCommandParams,
|
|
314
|
+
hasActionTemplates,
|
|
315
|
+
type ActionCommandToken,
|
|
316
|
+
type ActionCommandLiteralToken,
|
|
317
|
+
type ActionCommandParamToken,
|
|
318
|
+
type ParsedArgument,
|
|
319
|
+
type ParsedActionCommand,
|
|
320
|
+
type ActionInterpolationOptions,
|
|
284
321
|
} from "./execution";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manifest loader with scripts support
|
|
3
|
+
*
|
|
4
|
+
* Loads SKILL.md manifests and converts inline scripts to ActionsManifest
|
|
5
|
+
* objects for the execution pipeline.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ActionsManifest } from "../types/actions";
|
|
9
|
+
import { type LoadManifestOptions, type LoadedManifest, loadManifestFromDir } from "./loader";
|
|
10
|
+
import { manifestScriptsToActionsManifest } from "./scripts";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Result of loading a manifest with its scripts
|
|
14
|
+
*/
|
|
15
|
+
export interface LoadedManifestWithActions extends LoadedManifest {
|
|
16
|
+
/** The scripts manifest (converted from inline scripts) */
|
|
17
|
+
actionsManifest?: ActionsManifest;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Load a SKILL.md manifest and convert inline scripts to an ActionsManifest
|
|
22
|
+
*
|
|
23
|
+
* This is the primary function for loading a complete skill with scripts.
|
|
24
|
+
* It loads the SKILL.md for documentation and metadata, then converts any
|
|
25
|
+
* inline `scripts` field into an ActionsManifest for the execution pipeline.
|
|
26
|
+
*
|
|
27
|
+
* @param dir - Directory containing SKILL.md
|
|
28
|
+
* @param options - Options for loading and validation
|
|
29
|
+
* @returns LoadedManifestWithActions containing manifest and optional scripts
|
|
30
|
+
* @throws ManifestLoadError if SKILL.md loading fails
|
|
31
|
+
*/
|
|
32
|
+
export function loadManifestWithActions(
|
|
33
|
+
dir: string,
|
|
34
|
+
options: LoadManifestOptions = {}
|
|
35
|
+
): LoadedManifestWithActions {
|
|
36
|
+
// Load the primary manifest (SKILL.md)
|
|
37
|
+
const loaded = loadManifestFromDir(dir, options);
|
|
38
|
+
|
|
39
|
+
// Convert inline scripts to ActionsManifest
|
|
40
|
+
const scriptsManifest = manifestScriptsToActionsManifest(loaded.manifest);
|
|
41
|
+
if (scriptsManifest) {
|
|
42
|
+
return {
|
|
43
|
+
...loaded,
|
|
44
|
+
actionsManifest: scriptsManifest,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return loaded;
|
|
49
|
+
}
|
package/src/manifest/index.ts
CHANGED
|
@@ -37,3 +37,15 @@ export {
|
|
|
37
37
|
type LoadedManifest,
|
|
38
38
|
type LoadManifestOptions,
|
|
39
39
|
} from "./loader";
|
|
40
|
+
|
|
41
|
+
// Manifest with scripts
|
|
42
|
+
export {
|
|
43
|
+
loadManifestWithActions,
|
|
44
|
+
type LoadedManifestWithActions,
|
|
45
|
+
} from "./actions-loader";
|
|
46
|
+
|
|
47
|
+
// Scripts (inline scripts → Action bridge)
|
|
48
|
+
export {
|
|
49
|
+
scriptToAction,
|
|
50
|
+
manifestScriptsToActionsManifest,
|
|
51
|
+
} from "./scripts";
|
package/src/manifest/loader.ts
CHANGED
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
import { existsSync, readFileSync } from "node:fs";
|
|
8
8
|
import { basename, join } from "node:path";
|
|
9
|
+
import yaml from "js-yaml";
|
|
9
10
|
import type { ParsedManifest, ToolManifest, ValidationResult } from "../types/manifest";
|
|
10
11
|
import { MANIFEST_FILES } from "../types/manifest";
|
|
11
|
-
import { ManifestParseError, parseManifestAuto } from "./parser";
|
|
12
|
+
import { ManifestParseError, extractFrontmatter, parseManifestAuto } from "./parser";
|
|
12
13
|
import { type ValidateManifestOptions, validateManifest } from "./validator";
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -51,7 +52,7 @@ export interface LoadManifestOptions extends ValidateManifestOptions {
|
|
|
51
52
|
/**
|
|
52
53
|
* Load a manifest from a file path
|
|
53
54
|
*
|
|
54
|
-
* @param filePath - Path to the manifest file (SKILL.md, enact.md, enact.yaml, or enact.yml)
|
|
55
|
+
* @param filePath - Path to the manifest file (SKILL.md, skill.yaml, skill.yml, enact.md, enact.yaml, or enact.yml)
|
|
55
56
|
* @param options - Options for loading and validation
|
|
56
57
|
* @returns LoadedManifest with validated manifest and metadata
|
|
57
58
|
* @throws ManifestLoadError if file doesn't exist, parse fails, or validation fails
|
|
@@ -116,10 +117,36 @@ export function loadManifest(filePath: string, options: LoadManifestOptions = {}
|
|
|
116
117
|
return result;
|
|
117
118
|
}
|
|
118
119
|
|
|
120
|
+
/**
|
|
121
|
+
* Load SKILL.md for its body content and frontmatter fields.
|
|
122
|
+
* Does NOT validate as a full ToolManifest — used only to extract
|
|
123
|
+
* agent-facing documentation in two-file mode (skill.yaml + SKILL.md).
|
|
124
|
+
*/
|
|
125
|
+
function loadSkillDoc(
|
|
126
|
+
filePath: string
|
|
127
|
+
): { body: string; frontmatter: Record<string, unknown> } | null {
|
|
128
|
+
try {
|
|
129
|
+
const content = readFileSync(filePath, "utf-8");
|
|
130
|
+
const extracted = extractFrontmatter(content);
|
|
131
|
+
if (!extracted) return null;
|
|
132
|
+
const frontmatter = extracted.frontmatter
|
|
133
|
+
? ((yaml.load(extracted.frontmatter) as Record<string, unknown>) ?? {})
|
|
134
|
+
: {};
|
|
135
|
+
return { body: extracted.body, frontmatter };
|
|
136
|
+
} catch {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
119
141
|
/**
|
|
120
142
|
* Find and load a manifest from a directory
|
|
121
143
|
*
|
|
122
|
-
*
|
|
144
|
+
* Supports two modes:
|
|
145
|
+
* 1. **Two-file model**: If both `skill.yaml` and `SKILL.md` exist, `skill.yaml` is
|
|
146
|
+
* the package manifest and `SKILL.md` provides agent-facing documentation (body → `doc`).
|
|
147
|
+
* Also supports legacy `enact.yaml` in place of `skill.yaml`.
|
|
148
|
+
* 2. **Single-file fallback**: Searches for SKILL.md, skill.yaml, skill.yml, enact.md, enact.yaml, or enact.yml
|
|
149
|
+
* and uses the first match as the complete manifest.
|
|
123
150
|
*
|
|
124
151
|
* @param dir - Directory to search for manifest
|
|
125
152
|
* @param options - Options for loading and validation
|
|
@@ -130,7 +157,53 @@ export function loadManifestFromDir(
|
|
|
130
157
|
dir: string,
|
|
131
158
|
options: LoadManifestOptions = {}
|
|
132
159
|
): LoadedManifest {
|
|
133
|
-
|
|
160
|
+
const skillMdPath = join(dir, "SKILL.md");
|
|
161
|
+
const hasSkillMd = existsSync(skillMdPath);
|
|
162
|
+
|
|
163
|
+
// Find config file (skill.yaml, skill.yml, or legacy enact.yaml/enact.yml)
|
|
164
|
+
let enactConfigPath: string | null = null;
|
|
165
|
+
for (const f of ["skill.yaml", "skill.yml", "enact.yaml", "enact.yml"]) {
|
|
166
|
+
const p = join(dir, f);
|
|
167
|
+
if (existsSync(p)) {
|
|
168
|
+
enactConfigPath = p;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Two-file model: skill.yaml + SKILL.md
|
|
174
|
+
if (enactConfigPath && hasSkillMd) {
|
|
175
|
+
const loaded = loadManifest(enactConfigPath, options);
|
|
176
|
+
const skillDoc = loadSkillDoc(skillMdPath);
|
|
177
|
+
|
|
178
|
+
if (skillDoc) {
|
|
179
|
+
// Merge SKILL.md body into manifest.doc and LoadedManifest.body
|
|
180
|
+
loaded.manifest = { ...loaded.manifest };
|
|
181
|
+
if (skillDoc.body) {
|
|
182
|
+
if (!loaded.manifest.doc) {
|
|
183
|
+
loaded.manifest.doc = skillDoc.body;
|
|
184
|
+
}
|
|
185
|
+
loaded.body = skillDoc.body;
|
|
186
|
+
}
|
|
187
|
+
// Use SKILL.md frontmatter as fallbacks for shared fields
|
|
188
|
+
const fm = skillDoc.frontmatter;
|
|
189
|
+
if (!loaded.manifest.description && typeof fm.description === "string") {
|
|
190
|
+
loaded.manifest.description = fm.description;
|
|
191
|
+
}
|
|
192
|
+
if (!loaded.manifest.license && typeof fm.license === "string") {
|
|
193
|
+
loaded.manifest.license = fm.license;
|
|
194
|
+
}
|
|
195
|
+
if (!loaded.manifest.compatibility && typeof fm.compatibility === "string") {
|
|
196
|
+
loaded.manifest.compatibility = fm.compatibility;
|
|
197
|
+
}
|
|
198
|
+
if (!loaded.manifest.metadata && fm.metadata && typeof fm.metadata === "object") {
|
|
199
|
+
loaded.manifest.metadata = fm.metadata as Record<string, string>;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return loaded;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Single-file fallback (existing behavior)
|
|
134
207
|
for (const filename of MANIFEST_FILES) {
|
|
135
208
|
const filePath = join(dir, filename);
|
|
136
209
|
if (existsSync(filePath)) {
|
package/src/manifest/parser.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Handles parsing of:
|
|
5
5
|
* - SKILL.md files (YAML frontmatter + Markdown body) - primary format
|
|
6
|
+
* - skill.yaml/yml files (pure YAML) - package manifest
|
|
6
7
|
* - enact.yaml/yml files (pure YAML) - legacy format
|
|
7
8
|
* - enact.md files (YAML frontmatter + Markdown body) - legacy format
|
|
8
9
|
*/
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Script-to-Action bridge
|
|
3
|
+
*
|
|
4
|
+
* Converts inline `scripts` from ToolManifest into Action/ActionsManifest
|
|
5
|
+
* objects so the existing execution pipeline works unchanged.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { JSONSchema7 } from "json-schema";
|
|
9
|
+
import { getActionCommandParams } from "../execution/action-command";
|
|
10
|
+
import type { Action, ActionEnvVars, ActionsManifest } from "../types/actions";
|
|
11
|
+
import type { ScriptDefinition, ToolManifest } from "../types/manifest";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Convert a script command string to array form
|
|
15
|
+
*
|
|
16
|
+
* Splits on whitespace while preserving {{param}} tokens as single elements.
|
|
17
|
+
*/
|
|
18
|
+
function scriptCommandToArray(command: string): string[] {
|
|
19
|
+
return command.split(/\s+/).filter((s) => s.length > 0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Auto-infer an inputSchema from {{param}} patterns in a command array
|
|
24
|
+
*
|
|
25
|
+
* Every parameter found becomes a required string property.
|
|
26
|
+
*/
|
|
27
|
+
function inferInputSchema(commandArray: string[]): JSONSchema7 {
|
|
28
|
+
const params = getActionCommandParams(commandArray);
|
|
29
|
+
|
|
30
|
+
if (params.length === 0) {
|
|
31
|
+
return { type: "object", properties: {} };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const properties: Record<string, JSONSchema7> = {};
|
|
35
|
+
for (const param of params) {
|
|
36
|
+
properties[param] = { type: "string" };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
type: "object",
|
|
41
|
+
required: params,
|
|
42
|
+
properties,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Convert a single script entry to an Action object
|
|
48
|
+
*/
|
|
49
|
+
export function scriptToAction(name: string, script: ScriptDefinition): Action {
|
|
50
|
+
if (typeof script === "string") {
|
|
51
|
+
const commandArray = scriptCommandToArray(script);
|
|
52
|
+
return {
|
|
53
|
+
description: name,
|
|
54
|
+
command: commandArray,
|
|
55
|
+
inputSchema: inferInputSchema(commandArray),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Expanded form
|
|
60
|
+
const commandArray = scriptCommandToArray(script.command);
|
|
61
|
+
return {
|
|
62
|
+
description: script.description ?? name,
|
|
63
|
+
command: commandArray,
|
|
64
|
+
inputSchema: script.inputSchema ?? inferInputSchema(commandArray),
|
|
65
|
+
...(script.outputSchema && { outputSchema: script.outputSchema }),
|
|
66
|
+
...(script.annotations && { annotations: script.annotations }),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Convert manifest env (EnvVariables) to action env (ActionEnvVars)
|
|
72
|
+
*/
|
|
73
|
+
function convertEnv(env: ToolManifest["env"]): ActionEnvVars | undefined {
|
|
74
|
+
if (!env || Object.keys(env).length === 0) return undefined;
|
|
75
|
+
|
|
76
|
+
const actionEnv: ActionEnvVars = {};
|
|
77
|
+
for (const [key, value] of Object.entries(env)) {
|
|
78
|
+
const envVar: ActionEnvVars[string] = {
|
|
79
|
+
description: value.description,
|
|
80
|
+
};
|
|
81
|
+
if (value.secret !== undefined) {
|
|
82
|
+
envVar.secret = value.secret;
|
|
83
|
+
}
|
|
84
|
+
if (value.default !== undefined) {
|
|
85
|
+
envVar.default = value.default;
|
|
86
|
+
}
|
|
87
|
+
actionEnv[key] = envVar;
|
|
88
|
+
}
|
|
89
|
+
return actionEnv;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Convert all inline scripts from a manifest to an ActionsManifest
|
|
94
|
+
*
|
|
95
|
+
* Returns null if the manifest has no scripts.
|
|
96
|
+
*/
|
|
97
|
+
export function manifestScriptsToActionsManifest(manifest: ToolManifest): ActionsManifest | null {
|
|
98
|
+
if (!manifest.scripts || Object.keys(manifest.scripts).length === 0) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const actions: Record<string, Action> = {};
|
|
103
|
+
for (const [name, script] of Object.entries(manifest.scripts)) {
|
|
104
|
+
actions[name] = scriptToAction(name, script);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const result: ActionsManifest = { actions };
|
|
108
|
+
const env = convertEnv(manifest.env);
|
|
109
|
+
if (env) {
|
|
110
|
+
result.env = env;
|
|
111
|
+
}
|
|
112
|
+
if (manifest.hooks?.build) {
|
|
113
|
+
result.build = manifest.hooks.build;
|
|
114
|
+
}
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
@@ -20,6 +20,7 @@ import type {
|
|
|
20
20
|
const EnvVariableSchema = z.object({
|
|
21
21
|
description: z.string().min(1, "Description is required"),
|
|
22
22
|
secret: z.boolean().optional(),
|
|
23
|
+
required: z.boolean().optional(),
|
|
23
24
|
default: z.string().optional(),
|
|
24
25
|
});
|
|
25
26
|
|
|
@@ -83,15 +84,16 @@ const SEMVER_REGEX =
|
|
|
83
84
|
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
|
|
84
85
|
|
|
85
86
|
/**
|
|
86
|
-
* Tool name regex -
|
|
87
|
+
* Tool name regex - @scope/name format (required for publishing)
|
|
88
|
+
* Exactly 2 segments with optional @ prefix: "org/tool" or "@org/tool"
|
|
87
89
|
*/
|
|
88
|
-
const TOOL_NAME_REGEX =
|
|
90
|
+
const TOOL_NAME_REGEX = /^@?[a-z0-9_-]+\/[a-z0-9_-]+$/;
|
|
89
91
|
|
|
90
92
|
/**
|
|
91
93
|
* Tool name regex - simple format (allowed for local tools)
|
|
92
|
-
* Allows
|
|
94
|
+
* Allows simple names ("my-tool") or scope/name ("org/tool", "@org/tool")
|
|
93
95
|
*/
|
|
94
|
-
const TOOL_NAME_REGEX_LOCAL =
|
|
96
|
+
const TOOL_NAME_REGEX_LOCAL = /^@?[a-z0-9_-]+(?:\/[a-z0-9_-]+)?$/;
|
|
95
97
|
|
|
96
98
|
/**
|
|
97
99
|
* Go duration regex (used for timeout)
|
|
@@ -105,7 +107,7 @@ function createToolManifestSchema(allowSimpleNames: boolean) {
|
|
|
105
107
|
const nameRegex = allowSimpleNames ? TOOL_NAME_REGEX_LOCAL : TOOL_NAME_REGEX;
|
|
106
108
|
const nameMessage = allowSimpleNames
|
|
107
109
|
? "Tool name must contain only lowercase letters, numbers, hyphens, and underscores"
|
|
108
|
-
: "Tool name must be
|
|
110
|
+
: "Tool name must be scope/name format (e.g., 'org/tool' or '@org/tool')";
|
|
109
111
|
|
|
110
112
|
return z
|
|
111
113
|
.object({
|
|
@@ -133,7 +135,6 @@ function createToolManifestSchema(allowSimpleNames: boolean) {
|
|
|
133
135
|
tags: z.array(z.string()).optional(),
|
|
134
136
|
|
|
135
137
|
// Schema fields
|
|
136
|
-
inputSchema: JsonSchemaSchema.optional(),
|
|
137
138
|
outputSchema: JsonSchemaSchema.optional(),
|
|
138
139
|
|
|
139
140
|
// Environment variables
|
|
@@ -223,14 +224,6 @@ function generateWarnings(manifest: ToolManifest): ValidationWarning[] {
|
|
|
223
224
|
});
|
|
224
225
|
}
|
|
225
226
|
|
|
226
|
-
if (!manifest.inputSchema && manifest.command) {
|
|
227
|
-
warnings.push({
|
|
228
|
-
path: "inputSchema",
|
|
229
|
-
message: "Input schema is recommended for tools with parameters",
|
|
230
|
-
code: "MISSING_RECOMMENDED",
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
|
|
234
227
|
if (!manifest.outputSchema) {
|
|
235
228
|
warnings.push({
|
|
236
229
|
path: "outputSchema",
|
|
@@ -249,6 +242,14 @@ function generateWarnings(manifest: ToolManifest): ValidationWarning[] {
|
|
|
249
242
|
code: "SECRET_WITH_DEFAULT",
|
|
250
243
|
});
|
|
251
244
|
}
|
|
245
|
+
if (value.required && value.default !== undefined) {
|
|
246
|
+
warnings.push({
|
|
247
|
+
path: `env.${key}`,
|
|
248
|
+
message:
|
|
249
|
+
"Required variables with a default value are always satisfied — 'required' has no effect",
|
|
250
|
+
code: "REQUIRED_WITH_DEFAULT",
|
|
251
|
+
});
|
|
252
|
+
}
|
|
252
253
|
}
|
|
253
254
|
}
|
|
254
255
|
|
package/src/mcp-registry.ts
CHANGED
|
@@ -132,12 +132,12 @@ export function getMcpToolVersion(toolName: string): string | null {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
/**
|
|
135
|
-
* Get the
|
|
135
|
+
* Get the install path for an MCP tool
|
|
136
|
+
* Skills are stored at ~/.agent/skills/{name}/ (flat, no version subdirectory)
|
|
136
137
|
*/
|
|
137
|
-
function getMcpToolCachePath(toolName: string,
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
return join(cacheDir, toolName, `v${normalizedVersion}`);
|
|
138
|
+
function getMcpToolCachePath(toolName: string, _version: string): string {
|
|
139
|
+
const skillsDir = getCacheDir();
|
|
140
|
+
return join(skillsDir, toolName);
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
/**
|
package/src/paths.ts
CHANGED
|
@@ -74,11 +74,21 @@ export function getToolsDir(scope: ToolScope, startDir?: string): string | null
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
|
-
* Get the
|
|
78
|
-
*
|
|
77
|
+
* Get the skills directory (~/.agent/skills/)
|
|
78
|
+
* This is the standard Agent Skills location for installed skills.
|
|
79
|
+
* @returns Absolute path to ~/.agent/skills/
|
|
80
|
+
*/
|
|
81
|
+
export function getSkillsDir(): string {
|
|
82
|
+
return join(homedir(), ".agent", "skills");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get the cache directory (~/.agent/skills/)
|
|
87
|
+
* @deprecated Use getSkillsDir() instead
|
|
88
|
+
* @returns Absolute path to ~/.agent/skills/
|
|
79
89
|
*/
|
|
80
90
|
export function getCacheDir(): string {
|
|
81
|
-
return
|
|
91
|
+
return getSkillsDir();
|
|
82
92
|
}
|
|
83
93
|
|
|
84
94
|
/**
|
package/src/registry.ts
CHANGED
|
@@ -165,12 +165,12 @@ export function getInstalledVersion(
|
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
/**
|
|
168
|
-
* Get the
|
|
168
|
+
* Get the install path for a skill
|
|
169
|
+
* Skills are stored at ~/.agent/skills/{name}/ (flat, no version subdirectory)
|
|
169
170
|
*/
|
|
170
|
-
export function getToolCachePath(toolName: string,
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
return join(cacheDir, toolName, `v${normalizedVersion}`);
|
|
171
|
+
export function getToolCachePath(toolName: string, _version: string): string {
|
|
172
|
+
const skillsDir = getCacheDir();
|
|
173
|
+
return join(skillsDir, toolName);
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
/**
|