toolcraft 0.0.383 → 0.0.385

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 CHANGED
@@ -33,7 +33,7 @@ const result = await shell.exec("tools list-users --limit 10 --output json");
33
33
  await shell.dispose();
34
34
  ```
35
35
 
36
- Each root name and alias becomes a virtual command. Nested groups, command aliases,
36
+ Each root name, its kebab-case spelling and aliases become virtual commands. Nested groups, command aliases,
37
37
  default commands, positional arguments, schema defaults, scalar/enum/array/object/
38
38
  record/union inputs and CLI scope filtering reuse Toolcraft's CLI parser. Hidden
39
39
  commands are excluded from this surface. Standalone CLI behavior remains unchanged.
@@ -98,9 +98,15 @@ provider and effective limits as `ctx.regex.executor` and `ctx.regex.limits`.
98
98
  safe-bash exports; missing regex never gains a native JavaScript fallback. Native
99
99
  handlers can use `signal`, byte `stdin`/`stdout`/`stderr`, `cwd`, `inputBudget`,
100
100
  `invoke` and `registerCleanup`. Dry-run readback uses an explicitly supplied mock
101
- or overlay filesystem. Library services combine with invocation services; runtime
102
- capability names are reserved and cannot be replaced through services.
103
- Schema pattern validation currently follows Toolcraft's existing validator.
101
+ or overlay filesystem. Library services combine with invocation services, with
102
+ invocation-owned services taking precedence on name collisions. Runtime capability
103
+ names are reserved and cannot be replaced through services. Explicitly revoking
104
+ `humanInLoop` also disables a library-configured approval provider.
105
+ Schema `pattern` and `patternProperties` validation is unsupported in this native
106
+ surface: registration rejects these keywords in parameter and stream-event schemas,
107
+ including nested JSON Schema definitions, instead of using the host regex engine.
108
+ Tools that need regex work can use the injected bounded provider in their handlers.
109
+ CLI-excluded roots are also inaccessible through the public command executor.
104
110
 
105
111
  ## What the owner decides
106
112
 
package/composition.json CHANGED
@@ -113,7 +113,7 @@
113
113
  },
114
114
  {
115
115
  "name": "toolcraft",
116
- "version": "0.0.383",
116
+ "version": "0.0.385",
117
117
  "license": "MIT"
118
118
  },
119
119
  {
@@ -123,7 +123,7 @@
123
123
  },
124
124
  {
125
125
  "name": "toolcraft-schema",
126
- "version": "0.0.383",
126
+ "version": "0.0.385",
127
127
  "license": "MIT"
128
128
  },
129
129
  {
@@ -113,7 +113,7 @@
113
113
  },
114
114
  {
115
115
  "name": "toolcraft",
116
- "version": "0.0.383",
116
+ "version": "0.0.385",
117
117
  "license": "MIT"
118
118
  },
119
119
  {
@@ -123,7 +123,7 @@
123
123
  },
124
124
  {
125
125
  "name": "toolcraft-schema",
126
- "version": "0.0.383",
126
+ "version": "0.0.385",
127
127
  "license": "MIT"
128
128
  },
129
129
  {
@@ -17,7 +17,7 @@ export interface ToolcraftInvocation<TServices extends object = Record<string, n
17
17
  signal: AbortSignal;
18
18
  services?: TServices;
19
19
  fetch?: typeof globalThis.fetch;
20
- humanInLoop?: HumanInLoopRuntime;
20
+ humanInLoop?: HumanInLoopRuntime | undefined;
21
21
  }
22
22
  declare module "./index.js" {
23
23
  interface HandlerInvocationCapabilities {
package/dist/safe-bash.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import { cloneDefaultValue, validate } from "toolcraft-schema";
2
+ import { cloneDefaultValue, toJsonSchema, validate } from "toolcraft-schema";
3
3
  import { executeCLICommand, formatCLIName } from "./cli.js";
4
4
  import { validateServices } from "./runtime/io.js";
5
5
  /** Checks paths, parameter names and values against a library's inferred definitions. */
@@ -79,11 +79,42 @@ function handlerFileSystem(invocation) {
79
79
  }
80
80
  };
81
81
  }
82
+ function assertNativeSchema(schema, commandPath) {
83
+ if (schema === null || typeof schema !== "object" || Array.isArray(schema))
84
+ return;
85
+ const document = schema;
86
+ if (document.pattern !== undefined || document.patternProperties !== undefined) {
87
+ throw new TypeError(`Schema regex validation is unsupported in native Toolcraft commands: ${commandPath}; use the invocation's bounded regex capability in the handler`);
88
+ }
89
+ // Visit schema positions only: parameter names and literal defaults are not keywords.
90
+ for (const key of ["properties", "$defs", "definitions", "dependentSchemas", "dependencies"]) {
91
+ const map = document[key];
92
+ if (map !== null && typeof map === "object" && !Array.isArray(map)) {
93
+ for (const child of Object.values(map))
94
+ assertNativeSchema(child, commandPath);
95
+ }
96
+ }
97
+ for (const key of ["allOf", "anyOf", "oneOf", "prefixItems", "items", "additionalItems", "additionalProperties", "contains", "if", "then", "else", "not", "propertyNames", "unevaluatedItems", "unevaluatedProperties"]) {
98
+ const child = document[key];
99
+ if (Array.isArray(child)) {
100
+ for (const item of child)
101
+ assertNativeSchema(item, commandPath);
102
+ }
103
+ else
104
+ assertNativeSchema(child, commandPath);
105
+ }
106
+ }
82
107
  /** Executes already-tokenized argv in process, using the same parser and dispatch as runCLI. */
83
108
  export function createToolcraftCommandExecutor(library, options = {}) {
84
- const roots = Array.isArray(library) ? library : [library];
109
+ const roots = (Array.isArray(library) ? library : [library])
110
+ .filter(root => !root.scope || root.scope.includes("cli"));
85
111
  const multiple = Array.isArray(library);
86
112
  const commands = new Map(roots.flatMap(root => [...discoverCommands(root, multiple ? `${root.name}/` : "")]));
113
+ for (const [commandPath, command] of commands) {
114
+ assertNativeSchema(toJsonSchema(command.params), commandPath);
115
+ if (command.stream)
116
+ assertNativeSchema(toJsonSchema(command.stream.event), commandPath);
117
+ }
87
118
  const defaults = cloneDefaultValue(options.defaults ?? {});
88
119
  for (const [commandPath, values] of Object.entries(defaults)) {
89
120
  const command = commands.get(commandPath);
@@ -106,9 +137,9 @@ export function createToolcraftCommandExecutor(library, options = {}) {
106
137
  let root;
107
138
  try {
108
139
  const configuredServices = typeof options.services === "function" ? options.services(invocation) : options.services;
109
- services = { ...invocation.services, ...configuredServices };
140
+ services = { ...configuredServices, ...invocation.services };
110
141
  const rootName = multiple ? argv[0] : roots[0]?.name;
111
- const selectedRoot = multiple ? roots.find(candidate => candidate.name === rootName || candidate.aliases.includes(rootName ?? "")) : roots[0];
142
+ const selectedRoot = multiple ? roots.find(candidate => candidate.name === rootName || formatCLIName(candidate.name, "kebab") === rootName || candidate.aliases.includes(rootName ?? "")) : roots[0];
112
143
  if (!selectedRoot)
113
144
  throw new TypeError(`Unknown toolcraft root: ${rootName}`);
114
145
  root = selectedRoot;
@@ -152,7 +183,7 @@ export function createToolcraftCommandExecutor(library, options = {}) {
152
183
  env: { ...invocation.env },
153
184
  fs: handlerFileSystem(invocation),
154
185
  fetch: invocation.fetch ?? deniedFetch,
155
- humanInLoop: invocation.humanInLoop ?? options.humanInLoop,
186
+ humanInLoop: Object.prototype.hasOwnProperty.call(invocation, "humanInLoop") ? invocation.humanInLoop : options.humanInLoop,
156
187
  controls: { output: true, yes: true, ...options.controls },
157
188
  errorReports: false,
158
189
  outputEmitter: entry => runtime.write(`${entry}\n`)
@@ -173,7 +204,7 @@ export function toolcraftCommands(library, options = {}) {
173
204
  for (const root of roots) {
174
205
  if (root.scope && !root.scope.includes("cli"))
175
206
  continue;
176
- for (const name of [root.name, ...root.aliases]) {
207
+ for (const name of new Set([root.name, formatCLIName(root.name, "kebab"), ...root.aliases])) {
177
208
  host.commands.register({
178
209
  name,
179
210
  description: root.description,
@@ -184,7 +215,7 @@ export function toolcraftCommands(library, options = {}) {
184
215
  services: capabilities?.services,
185
216
  fetch: capabilities?.fetch,
186
217
  regex: capabilities?.regex,
187
- humanInLoop: capabilities?.humanInLoop
218
+ ...(Object.prototype.hasOwnProperty.call(capabilities ?? {}, "humanInLoop") ? { humanInLoop: capabilities?.humanInLoop } : {})
188
219
  });
189
220
  }
190
221
  });
@@ -8,7 +8,7 @@
8
8
  },
9
9
  {
10
10
  "name": "toolcraft-schema",
11
- "version": "0.0.383",
11
+ "version": "0.0.385",
12
12
  "license": "MIT"
13
13
  }
14
14
  ]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft-schema",
3
- "version": "0.0.383",
3
+ "version": "0.0.385",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft",
3
- "version": "0.0.383",
3
+ "version": "0.0.385",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -162,7 +162,7 @@
162
162
  "yaml"
163
163
  ],
164
164
  "optionalDependencies": {
165
- "toolcraft-schema": "0.0.383",
165
+ "toolcraft-schema": "0.0.385",
166
166
  "toolcraft-design": "*",
167
167
  "@poe-code/frontmatter": "*",
168
168
  "@poe-code/agent-mcp-config": "*",