@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.
Files changed (111) hide show
  1. package/README.md +1 -18
  2. package/dist/config.d.ts +12 -0
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js +32 -6
  5. package/dist/config.js.map +1 -1
  6. package/dist/execution/action-command.d.ts +131 -0
  7. package/dist/execution/action-command.d.ts.map +1 -0
  8. package/dist/execution/action-command.js +300 -0
  9. package/dist/execution/action-command.js.map +1 -0
  10. package/dist/execution/command.d.ts +8 -8
  11. package/dist/execution/command.js +6 -6
  12. package/dist/execution/index.d.ts +1 -0
  13. package/dist/execution/index.d.ts.map +1 -1
  14. package/dist/execution/index.js +2 -0
  15. package/dist/execution/index.js.map +1 -1
  16. package/dist/execution/types.d.ts +5 -2
  17. package/dist/execution/types.d.ts.map +1 -1
  18. package/dist/execution/types.js.map +1 -1
  19. package/dist/index.d.ts +8 -6
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +11 -4
  22. package/dist/index.js.map +1 -1
  23. package/dist/manifest/actions-loader.d.ts +29 -0
  24. package/dist/manifest/actions-loader.d.ts.map +1 -0
  25. package/dist/manifest/actions-loader.js +34 -0
  26. package/dist/manifest/actions-loader.js.map +1 -0
  27. package/dist/manifest/actions-parser.d.ts +69 -0
  28. package/dist/manifest/actions-parser.d.ts.map +1 -0
  29. package/dist/manifest/actions-parser.js +265 -0
  30. package/dist/manifest/actions-parser.js.map +1 -0
  31. package/dist/manifest/index.d.ts +2 -0
  32. package/dist/manifest/index.d.ts.map +1 -1
  33. package/dist/manifest/index.js +4 -0
  34. package/dist/manifest/index.js.map +1 -1
  35. package/dist/manifest/loader.d.ts +7 -2
  36. package/dist/manifest/loader.d.ts.map +1 -1
  37. package/dist/manifest/loader.js +71 -4
  38. package/dist/manifest/loader.js.map +1 -1
  39. package/dist/manifest/parser.d.ts +1 -0
  40. package/dist/manifest/parser.d.ts.map +1 -1
  41. package/dist/manifest/parser.js +1 -0
  42. package/dist/manifest/parser.js.map +1 -1
  43. package/dist/manifest/scripts.d.ts +19 -0
  44. package/dist/manifest/scripts.d.ts.map +1 -0
  45. package/dist/manifest/scripts.js +102 -0
  46. package/dist/manifest/scripts.js.map +1 -0
  47. package/dist/manifest/validator.d.ts +1 -8
  48. package/dist/manifest/validator.d.ts.map +1 -1
  49. package/dist/manifest/validator.js +14 -13
  50. package/dist/manifest/validator.js.map +1 -1
  51. package/dist/mcp-registry.js +5 -5
  52. package/dist/mcp-registry.js.map +1 -1
  53. package/dist/paths.d.ts +9 -2
  54. package/dist/paths.d.ts.map +1 -1
  55. package/dist/paths.js +12 -3
  56. package/dist/paths.js.map +1 -1
  57. package/dist/registry.d.ts +3 -2
  58. package/dist/registry.d.ts.map +1 -1
  59. package/dist/registry.js +5 -5
  60. package/dist/registry.js.map +1 -1
  61. package/dist/resolver.d.ts +55 -4
  62. package/dist/resolver.d.ts.map +1 -1
  63. package/dist/resolver.js +133 -75
  64. package/dist/resolver.js.map +1 -1
  65. package/dist/types/actions.d.ts +194 -0
  66. package/dist/types/actions.d.ts.map +1 -0
  67. package/dist/types/actions.js +32 -0
  68. package/dist/types/actions.js.map +1 -0
  69. package/dist/types/index.d.ts +3 -1
  70. package/dist/types/index.d.ts.map +1 -1
  71. package/dist/types/index.js +1 -0
  72. package/dist/types/index.js.map +1 -1
  73. package/dist/types/manifest.d.ts +50 -5
  74. package/dist/types/manifest.d.ts.map +1 -1
  75. package/dist/types/manifest.js +10 -2
  76. package/dist/types/manifest.js.map +1 -1
  77. package/package.json +2 -2
  78. package/src/config.ts +48 -6
  79. package/src/execution/action-command.ts +417 -0
  80. package/src/execution/command.ts +8 -8
  81. package/src/execution/index.ts +17 -0
  82. package/src/execution/types.ts +13 -2
  83. package/src/index.ts +37 -0
  84. package/src/manifest/actions-loader.ts +49 -0
  85. package/src/manifest/index.ts +12 -0
  86. package/src/manifest/loader.ts +77 -4
  87. package/src/manifest/parser.ts +1 -0
  88. package/src/manifest/scripts.ts +116 -0
  89. package/src/manifest/validator.ts +15 -14
  90. package/src/mcp-registry.ts +5 -5
  91. package/src/paths.ts +13 -3
  92. package/src/registry.ts +5 -5
  93. package/src/resolver.ts +172 -77
  94. package/src/types/actions.ts +223 -0
  95. package/src/types/index.ts +11 -0
  96. package/src/types/manifest.ts +67 -6
  97. package/tests/action-command.test.ts +249 -0
  98. package/tests/config-normalization.test.ts +279 -0
  99. package/tests/config.test.ts +4 -1
  100. package/tests/effective-input-schema.test.ts +86 -0
  101. package/tests/fixtures/valid-tool.md +5 -12
  102. package/tests/fixtures/valid-tool.yaml +3 -10
  103. package/tests/hooks.test.ts +177 -0
  104. package/tests/manifest/loader.test.ts +34 -20
  105. package/tests/manifest/parser.test.ts +11 -15
  106. package/tests/manifest/validator.test.ts +7 -17
  107. package/tests/manifest-types.test.ts +9 -11
  108. package/tests/paths.test.ts +11 -4
  109. package/tests/registry.test.ts +12 -11
  110. package/tests/resolver.test.ts +11 -7
  111. package/tsconfig.tsbuildinfo +1 -1
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * TypeScript types for Enact tool manifests
3
- * These types define the structure of SKILL.md (and legacy enact.yaml/enact.md) frontmatter
3
+ * These types define the structure of SKILL.md (and skill.yaml/legacy enact.yaml/enact.md) frontmatter
4
4
  */
5
5
  import type { JSONSchema7 } from "json-schema";
6
6
  /**
@@ -11,6 +11,8 @@ export interface EnvVariable {
11
11
  description: string;
12
12
  /** If true, stored in OS keyring; if false, stored in .env files */
13
13
  secret?: boolean;
14
+ /** If true, must be set before execution (no default fallback) */
15
+ required?: boolean;
14
16
  /** Default value if not set (only for non-secrets) */
15
17
  default?: string;
16
18
  }
@@ -44,6 +46,28 @@ export interface ToolAnnotations {
44
46
  /** Tool interacts with external systems (network, APIs) */
45
47
  openWorldHint?: boolean;
46
48
  }
49
+ /**
50
+ * Lifecycle hooks for tool installation and management
51
+ */
52
+ export interface ToolHooks {
53
+ /** Command(s) to run after the tool is installed/extracted (e.g., "npm install", "pip install -r requirements.txt") */
54
+ postinstall?: string | string[];
55
+ /** Build command(s) to run before execution (e.g., "pip install -r requirements.txt") */
56
+ build?: string | string[];
57
+ }
58
+ /**
59
+ * Script definition — either a simple command string or an expanded object
60
+ *
61
+ * Simple form: "python main.py {{url}}"
62
+ * Expanded form: { command: "python main.py {{url}}", description: "Scrape a URL" }
63
+ */
64
+ export type ScriptDefinition = string | {
65
+ command: string;
66
+ description?: string;
67
+ inputSchema?: JSONSchema7;
68
+ outputSchema?: JSONSchema7;
69
+ annotations?: ToolAnnotations;
70
+ };
47
71
  /**
48
72
  * Resource requirements for tool execution
49
73
  */
@@ -68,7 +92,7 @@ export interface ToolExample {
68
92
  }
69
93
  /**
70
94
  * Complete tool manifest structure
71
- * This represents the YAML frontmatter in SKILL.md (or legacy enact.md/enact.yaml)
95
+ * This represents the YAML frontmatter in SKILL.md (or skill.yaml/legacy enact.md/enact.yaml)
72
96
  */
73
97
  export interface ToolManifest {
74
98
  /** Hierarchical tool identifier (e.g., "org/category/tool-name") */
@@ -91,8 +115,6 @@ export interface ToolManifest {
91
115
  license?: string;
92
116
  /** Keywords for tool discovery and categorization */
93
117
  tags?: string[];
94
- /** JSON Schema defining input parameters */
95
- inputSchema?: JSONSchema7;
96
118
  /** JSON Schema defining output structure */
97
119
  outputSchema?: JSONSchema7;
98
120
  /** Environment variables and secrets required by the tool */
@@ -101,6 +123,22 @@ export interface ToolManifest {
101
123
  annotations?: ToolAnnotations;
102
124
  /** Resource limits and requirements */
103
125
  resources?: ResourceRequirements;
126
+ /** Lifecycle hooks (e.g., postinstall build step) */
127
+ hooks?: ToolHooks;
128
+ /**
129
+ * Inline executable scripts (each becomes an MCP tool via colon syntax)
130
+ *
131
+ * Scripts replace the need for a separate ACTIONS.yaml file.
132
+ * Each script maps a name to a command with {{param}} template syntax.
133
+ *
134
+ * @example
135
+ * scripts:
136
+ * scrape: python scripts/scrape.py {{url}}
137
+ * crawl:
138
+ * command: python scripts/crawl.py {{url}} {{depth}}
139
+ * description: Crawl a website to specified depth
140
+ */
141
+ scripts?: Record<string, ScriptDefinition>;
104
142
  /** Environment requirements (intended product, system packages, network access, etc.) */
105
143
  compatibility?: string;
106
144
  /** Arbitrary key-value metadata for additional properties */
@@ -194,13 +232,20 @@ export interface ToolResolution {
194
232
  manifestPath: string;
195
233
  /** Tool version (if available) */
196
234
  version?: string | undefined;
235
+ /** The resolved script (if a script was specified via colon syntax) */
236
+ action?: import("./actions").Action | undefined;
237
+ /** The requested script name (if specified via colon syntax) */
238
+ actionName?: string | undefined;
239
+ /** The scripts manifest (converted from inline scripts) */
240
+ actionsManifest?: import("./actions").ActionsManifest | undefined;
197
241
  }
198
242
  /**
199
243
  * Supported manifest file names
200
244
  * SKILL.md is the primary format (aligned with Anthropic Agent Skills)
245
+ * skill.yaml/yml is the package manifest
201
246
  * enact.md/yaml/yml are supported for backwards compatibility
202
247
  */
203
- export declare const MANIFEST_FILES: readonly ["SKILL.md", "enact.md", "enact.yaml", "enact.yml"];
248
+ export declare const MANIFEST_FILES: readonly ["SKILL.md", "skill.yaml", "skill.yml", "enact.md", "enact.yaml", "enact.yml"];
204
249
  export type ManifestFileName = (typeof MANIFEST_FILES)[number];
205
250
  /**
206
251
  * Package manifest file name
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/types/manifest.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/types/manifest.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,21 @@
1
1
  /**
2
2
  * TypeScript types for Enact tool manifests
3
- * These types define the structure of SKILL.md (and legacy enact.yaml/enact.md) frontmatter
3
+ * These types define the structure of SKILL.md (and skill.yaml/legacy enact.yaml/enact.md) frontmatter
4
4
  */
5
5
  /**
6
6
  * Supported manifest file names
7
7
  * SKILL.md is the primary format (aligned with Anthropic Agent Skills)
8
+ * skill.yaml/yml is the package manifest
8
9
  * enact.md/yaml/yml are supported for backwards compatibility
9
10
  */
10
- export const MANIFEST_FILES = ["SKILL.md", "enact.md", "enact.yaml", "enact.yml"];
11
+ export const MANIFEST_FILES = [
12
+ "SKILL.md",
13
+ "skill.yaml",
14
+ "skill.yml",
15
+ "enact.md",
16
+ "enact.yaml",
17
+ "enact.yml",
18
+ ];
11
19
  /**
12
20
  * Package manifest file name
13
21
  */
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/types/manifest.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA6PH;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,CAAU,CAAC;AAG3F;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,oBAAoB,CAAC"}
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/types/manifest.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAkTH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,UAAU;IACV,YAAY;IACZ,WAAW;IACX,UAAU;IACV,YAAY;IACZ,WAAW;CACH,CAAC;AAGX;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,oBAAoB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enactprotocol/shared",
3
- "version": "2.2.4",
3
+ "version": "2.3.1",
4
4
  "description": "Core business logic and utilities for Enact",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,7 +18,7 @@
18
18
  "typecheck": "tsc --noEmit"
19
19
  },
20
20
  "dependencies": {
21
- "@enactprotocol/trust": "2.2.4",
21
+ "@enactprotocol/trust": "2.3.1",
22
22
  "js-yaml": "^4.1.1",
23
23
  "zod": "^4.1.13"
24
24
  },
package/src/config.ts CHANGED
@@ -4,9 +4,9 @@
4
4
  */
5
5
 
6
6
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
7
- import { dirname, join } from "node:path";
7
+ import { dirname } from "node:path";
8
8
  import yaml from "js-yaml";
9
- import { getConfigPath, getEnactHome } from "./paths";
9
+ import { getConfigPath, getEnactHome, getSkillsDir } from "./paths";
10
10
 
11
11
  /**
12
12
  * Trust configuration for attestation verification
@@ -26,6 +26,10 @@ export interface TrustConfig {
26
26
  policy?: "require_attestation" | "prompt" | "allow";
27
27
  /** Minimum number of trusted attestations required */
28
28
  minimum_attestations?: number;
29
+ /** Alias: when true, sets policy to 'require_attestation'; when false, sets policy to 'allow' */
30
+ require_signatures?: boolean;
31
+ /** Alias: merged into auditors list */
32
+ trusted_publishers?: string[];
29
33
  }
30
34
 
31
35
  /**
@@ -41,11 +45,20 @@ export interface CacheConfig {
41
45
  /**
42
46
  * Execution configuration
43
47
  */
48
+ /** Execution backend type */
49
+ export type ExecutionBackend = "local" | "docker" | "dagger" | "container" | "remote";
50
+
44
51
  export interface ExecutionConfig {
45
52
  /** Default timeout for tool execution (e.g., "30s", "5m") */
46
53
  defaultTimeout?: string;
47
54
  /** Whether to run in verbose mode */
48
55
  verbose?: boolean;
56
+ /** Default execution backend */
57
+ default?: ExecutionBackend;
58
+ /** Fallback backend if default is unavailable */
59
+ fallback?: ExecutionBackend;
60
+ /** Package scopes that bypass container isolation and run locally (e.g., ["@my-org/*"]) */
61
+ trusted_scopes?: string[];
49
62
  }
50
63
 
51
64
  /**
@@ -142,6 +155,33 @@ function deepMerge(target: EnactConfig, source: Partial<EnactConfig>): EnactConf
142
155
  return result;
143
156
  }
144
157
 
158
+ /**
159
+ * Normalize config by resolving alias fields into their canonical counterparts.
160
+ * - trust.require_signatures → trust.policy
161
+ * - trust.trusted_publishers → merged into trust.auditors
162
+ */
163
+ function normalizeConfig(config: EnactConfig): EnactConfig {
164
+ if (config.trust) {
165
+ // require_signatures is an alias for policy
166
+ if (config.trust.require_signatures !== undefined && config.trust.policy === undefined) {
167
+ config.trust.policy = config.trust.require_signatures ? "require_attestation" : "allow";
168
+ }
169
+
170
+ // trusted_publishers merges into auditors
171
+ if (config.trust.trusted_publishers?.length) {
172
+ const auditors = config.trust.auditors ?? [];
173
+ for (const publisher of config.trust.trusted_publishers) {
174
+ if (!auditors.includes(publisher)) {
175
+ auditors.push(publisher);
176
+ }
177
+ }
178
+ config.trust.auditors = auditors;
179
+ }
180
+ }
181
+
182
+ return config;
183
+ }
184
+
145
185
  /**
146
186
  * Load configuration from ~/.enact/config.yaml
147
187
  * Returns default config if file doesn't exist or is invalid
@@ -162,8 +202,10 @@ export function loadConfig(): EnactConfig {
162
202
  return { ...DEFAULT_CONFIG };
163
203
  }
164
204
 
165
- // Merge with defaults to ensure all fields exist
166
- return deepMerge(DEFAULT_CONFIG, parsed);
205
+ // Normalize aliases in user config BEFORE merging with defaults,
206
+ // so require_signatures → policy conversion isn't blocked by default policy
207
+ const normalized = normalizeConfig(parsed as EnactConfig);
208
+ return deepMerge(DEFAULT_CONFIG, normalized);
167
209
  } catch {
168
210
  // Return defaults on any error (parse error, read error, etc.)
169
211
  return { ...DEFAULT_CONFIG };
@@ -275,7 +317,7 @@ export function configExists(): boolean {
275
317
  export function ensureGlobalSetup(): boolean {
276
318
  const enactHome = getEnactHome();
277
319
  const configPath = getConfigPath();
278
- const cacheDir = join(enactHome, "cache");
320
+ const cacheDir = getSkillsDir();
279
321
 
280
322
  let performedSetup = false;
281
323
 
@@ -285,7 +327,7 @@ export function ensureGlobalSetup(): boolean {
285
327
  performedSetup = true;
286
328
  }
287
329
 
288
- // Ensure ~/.enact/cache/ directory exists
330
+ // Ensure ~/.agent/skills/ directory exists
289
331
  if (!existsSync(cacheDir)) {
290
332
  mkdirSync(cacheDir, { recursive: true });
291
333
  performedSetup = true;
@@ -0,0 +1,417 @@
1
+ /**
2
+ * Action command interpolation for Agent Actions
3
+ *
4
+ * Implements the {{param}} template syntax specified in RFC-001-AGENT-ACTIONS.
5
+ *
6
+ * ## Key Differences from ${param} (command.ts)
7
+ *
8
+ * 1. **No shell escaping**: Values are passed directly as arguments to execve()
9
+ * 2. **Array form only**: Templates only work in array-form commands
10
+ * 3. **Argument omission**: Optional params without values are omitted entirely
11
+ * 4. **Single argument substitution**: Each {{var}} becomes exactly one argument
12
+ *
13
+ * ## Security
14
+ *
15
+ * This template system is designed to prevent command injection:
16
+ * - Values are never passed through a shell interpreter
17
+ * - No argument splitting on whitespace or metacharacters
18
+ * - Each template becomes a single argument regardless of content
19
+ *
20
+ * @see RFC-001-AGENT-ACTIONS.md
21
+ */
22
+
23
+ import type { JSONSchema7 } from "json-schema";
24
+
25
+ /**
26
+ * Token types for action command parsing
27
+ */
28
+ export interface ActionCommandLiteralToken {
29
+ type: "literal";
30
+ value: string;
31
+ }
32
+
33
+ export interface ActionCommandParamToken {
34
+ type: "parameter";
35
+ name: string;
36
+ }
37
+
38
+ export type ActionCommandToken = ActionCommandLiteralToken | ActionCommandParamToken;
39
+
40
+ /**
41
+ * Parsed representation of a single command argument
42
+ */
43
+ export interface ParsedArgument {
44
+ /** The tokens that make up this argument */
45
+ tokens: ActionCommandToken[];
46
+ /** Parameter names referenced in this argument */
47
+ parameters: string[];
48
+ }
49
+
50
+ /**
51
+ * Result of parsing an action command
52
+ */
53
+ export interface ParsedActionCommand {
54
+ /** Original command (array form) */
55
+ original: string[];
56
+ /** Parsed arguments with tokens */
57
+ arguments: ParsedArgument[];
58
+ /** All unique parameter names across all arguments */
59
+ allParameters: string[];
60
+ }
61
+
62
+ /**
63
+ * Regex to match {{param}} template syntax
64
+ */
65
+ const ACTION_TEMPLATE_REGEX = /\{\{([^}]+)\}\}/g;
66
+
67
+ /**
68
+ * Check if a string contains {{param}} templates
69
+ */
70
+ export function hasActionTemplates(str: string): boolean {
71
+ // Reset regex state and test
72
+ ACTION_TEMPLATE_REGEX.lastIndex = 0;
73
+ return ACTION_TEMPLATE_REGEX.test(str);
74
+ }
75
+
76
+ /**
77
+ * Parse a single argument string into tokens
78
+ *
79
+ * @param arg - The argument string to parse
80
+ * @returns ParsedArgument with tokens and parameter names
81
+ */
82
+ export function parseActionArgument(arg: string): ParsedArgument {
83
+ const tokens: ActionCommandToken[] = [];
84
+ const parameters: string[] = [];
85
+
86
+ let lastIndex = 0;
87
+ const regex = /\{\{([^}]+)\}\}/g;
88
+ let match: RegExpExecArray | null = regex.exec(arg);
89
+
90
+ while (match !== null) {
91
+ // Add literal text before this match
92
+ if (match.index > lastIndex) {
93
+ tokens.push({
94
+ type: "literal",
95
+ value: arg.slice(lastIndex, match.index),
96
+ });
97
+ }
98
+
99
+ // Add the parameter token
100
+ const paramName = match[1]?.trim();
101
+ if (paramName) {
102
+ tokens.push({
103
+ type: "parameter",
104
+ name: paramName,
105
+ });
106
+
107
+ if (!parameters.includes(paramName)) {
108
+ parameters.push(paramName);
109
+ }
110
+ }
111
+
112
+ lastIndex = match.index + match[0].length;
113
+ match = regex.exec(arg);
114
+ }
115
+
116
+ // Add any remaining literal text
117
+ if (lastIndex < arg.length) {
118
+ tokens.push({
119
+ type: "literal",
120
+ value: arg.slice(lastIndex),
121
+ });
122
+ }
123
+
124
+ // If no tokens were created, the whole arg is a literal
125
+ if (tokens.length === 0) {
126
+ tokens.push({
127
+ type: "literal",
128
+ value: arg,
129
+ });
130
+ }
131
+
132
+ return { tokens, parameters };
133
+ }
134
+
135
+ /**
136
+ * Parse an action command (array form) into tokens
137
+ *
138
+ * @param command - The command array to parse
139
+ * @returns ParsedActionCommand with all arguments parsed
140
+ */
141
+ export function parseActionCommand(command: string[]): ParsedActionCommand {
142
+ const parsedArgs: ParsedArgument[] = [];
143
+ const allParameters: string[] = [];
144
+
145
+ for (const arg of command) {
146
+ const parsed = parseActionArgument(arg);
147
+ parsedArgs.push(parsed);
148
+
149
+ // Collect unique parameters
150
+ for (const param of parsed.parameters) {
151
+ if (!allParameters.includes(param)) {
152
+ allParameters.push(param);
153
+ }
154
+ }
155
+ }
156
+
157
+ return {
158
+ original: command,
159
+ arguments: parsedArgs,
160
+ allParameters,
161
+ };
162
+ }
163
+
164
+ /**
165
+ * Options for action command interpolation
166
+ */
167
+ export interface ActionInterpolationOptions {
168
+ /**
169
+ * JSON Schema for input validation
170
+ * Used to determine which parameters are optional and have defaults
171
+ */
172
+ inputSchema?: JSONSchema7;
173
+
174
+ /**
175
+ * Whether to omit arguments for optional params with no value
176
+ * Default: true (as per RFC-001)
177
+ */
178
+ omitMissingOptional?: boolean;
179
+ }
180
+
181
+ /**
182
+ * Get the default value for a parameter from the input schema
183
+ */
184
+ function getDefaultValue(paramName: string, inputSchema?: JSONSchema7): unknown | undefined {
185
+ if (!inputSchema || inputSchema.type !== "object") {
186
+ return undefined;
187
+ }
188
+
189
+ const properties = inputSchema.properties as Record<string, JSONSchema7> | undefined;
190
+ if (!properties) {
191
+ return undefined;
192
+ }
193
+
194
+ const propSchema = properties[paramName];
195
+ if (!propSchema || typeof propSchema === "boolean") {
196
+ return undefined;
197
+ }
198
+
199
+ return propSchema.default;
200
+ }
201
+
202
+ /**
203
+ * Check if a parameter is required according to the input schema
204
+ */
205
+ function isRequired(paramName: string, inputSchema?: JSONSchema7): boolean {
206
+ if (!inputSchema) {
207
+ return true; // Conservative default
208
+ }
209
+
210
+ const required = inputSchema.required as string[] | undefined;
211
+ return required?.includes(paramName) ?? false;
212
+ }
213
+
214
+ /**
215
+ * Convert a value to string for command argument
216
+ *
217
+ * Unlike command.ts, we don't shell-escape here because values
218
+ * are passed directly to execve(), not through a shell.
219
+ */
220
+ function valueToArgString(value: unknown): string {
221
+ if (value === null || value === undefined) {
222
+ return "";
223
+ }
224
+
225
+ if (typeof value === "string") {
226
+ return value;
227
+ }
228
+
229
+ if (typeof value === "number" || typeof value === "boolean") {
230
+ return String(value);
231
+ }
232
+
233
+ if (typeof value === "object") {
234
+ return JSON.stringify(value);
235
+ }
236
+
237
+ return String(value);
238
+ }
239
+
240
+ /**
241
+ * Interpolate a single argument with parameter values
242
+ *
243
+ * @param parsed - Parsed argument with tokens
244
+ * @param params - Parameter values
245
+ * @param inputSchema - JSON Schema for defaults and required checks
246
+ * @returns Interpolated string, or undefined if argument should be omitted
247
+ */
248
+ function interpolateArgument(
249
+ parsed: ParsedArgument,
250
+ params: Record<string, unknown>,
251
+ inputSchema?: JSONSchema7
252
+ ): string | undefined {
253
+ // Check if any parameter in this argument should cause omission
254
+ for (const paramName of parsed.parameters) {
255
+ const value = params[paramName];
256
+ const defaultValue = getDefaultValue(paramName, inputSchema);
257
+ const required = isRequired(paramName, inputSchema);
258
+
259
+ // If parameter is not provided
260
+ if (value === undefined) {
261
+ // Use default if available
262
+ if (defaultValue !== undefined) {
263
+ // Continue with default value (handled below in token processing)
264
+ continue;
265
+ }
266
+
267
+ // If optional (not required) and no default, omit entire argument
268
+ if (!required) {
269
+ return undefined;
270
+ }
271
+
272
+ // If required and no value, this is an error
273
+ throw new Error(`Missing required parameter: ${paramName}`);
274
+ }
275
+ }
276
+
277
+ // Build the interpolated string
278
+ const parts: string[] = [];
279
+
280
+ for (const token of parsed.tokens) {
281
+ if (token.type === "literal") {
282
+ parts.push(token.value);
283
+ } else {
284
+ const paramName = token.name;
285
+ let value = params[paramName];
286
+
287
+ // Apply default if value not provided
288
+ if (value === undefined) {
289
+ value = getDefaultValue(paramName, inputSchema);
290
+ }
291
+
292
+ parts.push(valueToArgString(value));
293
+ }
294
+ }
295
+
296
+ return parts.join("");
297
+ }
298
+
299
+ /**
300
+ * Interpolate an action command with parameter values
301
+ *
302
+ * Implements RFC-001 template substitution rules:
303
+ * 1. Each {{var}} is replaced with the literal value as a single argument
304
+ * 2. Optional parameters without values cause the argument to be omitted
305
+ * 3. No shell interpolation - values passed directly to execve()
306
+ *
307
+ * @param command - Array-form command with {{param}} templates
308
+ * @param params - Parameter values
309
+ * @param options - Interpolation options
310
+ * @returns Array of command arguments ready for execve()
311
+ */
312
+ export function interpolateActionCommand(
313
+ command: string[],
314
+ params: Record<string, unknown>,
315
+ options: ActionInterpolationOptions = {}
316
+ ): string[] {
317
+ const { inputSchema, omitMissingOptional = true } = options;
318
+ const parsed = parseActionCommand(command);
319
+ const result: string[] = [];
320
+
321
+ for (const arg of parsed.arguments) {
322
+ // If argument has no parameters, include it as-is
323
+ if (arg.parameters.length === 0) {
324
+ // It's just a literal
325
+ const literal = arg.tokens[0];
326
+ if (literal && literal.type === "literal") {
327
+ result.push(literal.value);
328
+ }
329
+ continue;
330
+ }
331
+ const interpolated = interpolateArgument(arg, params, inputSchema);
332
+
333
+ // undefined means omit this argument
334
+ if (interpolated === undefined) {
335
+ if (!omitMissingOptional) {
336
+ // If not omitting, use empty string
337
+ result.push("");
338
+ }
339
+ // Otherwise skip this argument entirely
340
+ continue;
341
+ }
342
+
343
+ result.push(interpolated);
344
+ }
345
+
346
+ return result;
347
+ }
348
+
349
+ /**
350
+ * Validate that all required parameters are provided
351
+ *
352
+ * @param command - Array-form command
353
+ * @param params - Provided parameter values
354
+ * @param inputSchema - JSON Schema for required field info
355
+ * @returns Array of missing required parameter names
356
+ */
357
+ export function getMissingRequiredParams(
358
+ command: string[],
359
+ params: Record<string, unknown>,
360
+ inputSchema?: JSONSchema7
361
+ ): string[] {
362
+ const parsed = parseActionCommand(command);
363
+ const missing: string[] = [];
364
+
365
+ for (const paramName of parsed.allParameters) {
366
+ const value = params[paramName];
367
+ const defaultValue = getDefaultValue(paramName, inputSchema);
368
+ const required = isRequired(paramName, inputSchema);
369
+
370
+ if (required && value === undefined && defaultValue === undefined) {
371
+ missing.push(paramName);
372
+ }
373
+ }
374
+
375
+ return missing;
376
+ }
377
+
378
+ /**
379
+ * Get all parameters referenced in a command
380
+ *
381
+ * @param command - Array-form command
382
+ * @returns Array of parameter names
383
+ */
384
+ export function getActionCommandParams(command: string[]): string[] {
385
+ return parseActionCommand(command).allParameters;
386
+ }
387
+
388
+ /**
389
+ * Prepare an action command for execution
390
+ *
391
+ * This is the main entry point for preparing action commands.
392
+ * It validates required parameters and interpolates the command.
393
+ *
394
+ * @param command - Array-form command with {{param}} templates
395
+ * @param params - Parameter values
396
+ * @param inputSchema - JSON Schema for validation
397
+ * @returns Array of command arguments ready for execve()
398
+ * @throws Error if required parameters are missing
399
+ */
400
+ export function prepareActionCommand(
401
+ command: string[],
402
+ params: Record<string, unknown>,
403
+ inputSchema?: JSONSchema7
404
+ ): string[] {
405
+ // Validate required parameters
406
+ const missing = getMissingRequiredParams(command, params, inputSchema);
407
+ if (missing.length > 0) {
408
+ throw new Error(`Missing required parameters: ${missing.join(", ")}`);
409
+ }
410
+
411
+ // Interpolate and return
412
+ const options: ActionInterpolationOptions = {};
413
+ if (inputSchema !== undefined) {
414
+ options.inputSchema = inputSchema;
415
+ }
416
+ return interpolateActionCommand(command, params, options);
417
+ }