toolcraft 0.0.92 → 0.0.94

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 (123) hide show
  1. package/README.md +74 -4
  2. package/composition.json +1 -6
  3. package/dist/agent-defs.d.ts +1 -0
  4. package/dist/agent-defs.js +1 -0
  5. package/dist/agent-human-in-loop.d.ts +1 -0
  6. package/dist/agent-human-in-loop.js +1 -0
  7. package/dist/agent-mcp-config.d.ts +1 -0
  8. package/dist/agent-mcp-config.js +1 -0
  9. package/dist/auth-store.d.ts +1 -0
  10. package/dist/auth-store.js +1 -0
  11. package/dist/cli.d.ts +4 -1
  12. package/dist/cli.js +23 -64
  13. package/dist/composition.json +1 -6
  14. package/dist/config-mutations.d.ts +1 -0
  15. package/dist/config-mutations.js +1 -0
  16. package/dist/frontmatter.d.ts +1 -0
  17. package/dist/frontmatter.js +1 -0
  18. package/dist/human-in-loop/gate.d.ts +5 -1
  19. package/dist/human-in-loop/gate.js +10 -8
  20. package/dist/human-in-loop/runner.js +1 -28
  21. package/dist/mcp.d.ts +4 -1
  22. package/dist/mcp.js +8 -51
  23. package/dist/process-runner.d.ts +1 -0
  24. package/dist/process-runner.js +1 -0
  25. package/dist/runtime/io.d.ts +5 -0
  26. package/dist/runtime/io.js +50 -0
  27. package/dist/sdk.d.ts +12 -8
  28. package/dist/sdk.js +7 -52
  29. package/dist/task-list.d.ts +1 -0
  30. package/dist/task-list.js +1 -0
  31. package/dist/testing/fakes.d.ts +20 -0
  32. package/dist/testing/fakes.js +83 -0
  33. package/dist/testing/fixtures.d.ts +8 -0
  34. package/dist/testing/fixtures.js +248 -0
  35. package/dist/testing/harness.d.ts +76 -0
  36. package/dist/testing/harness.js +391 -0
  37. package/dist/testing/index.d.ts +4 -0
  38. package/dist/testing/index.js +3 -0
  39. package/dist/testing/memory-fs.d.ts +11 -0
  40. package/dist/testing/memory-fs.js +61 -0
  41. package/dist/testing/parity.d.ts +25 -0
  42. package/dist/testing/parity.js +384 -0
  43. package/dist/testing/render-capture.d.ts +6 -0
  44. package/dist/testing/render-capture.js +54 -0
  45. package/dist/tiny-mcp-client.d.ts +1 -0
  46. package/dist/tiny-mcp-client.js +1 -0
  47. package/node_modules/@poe-code/agent-defs/package.json +9 -3
  48. package/node_modules/@poe-code/agent-human-in-loop/README.md +12 -2
  49. package/node_modules/@poe-code/agent-human-in-loop/package.json +8 -2
  50. package/node_modules/@poe-code/agent-mcp-config/README.md +7 -7
  51. package/node_modules/@poe-code/agent-mcp-config/package.json +8 -2
  52. package/node_modules/@poe-code/config-mutations/README.md +8 -8
  53. package/node_modules/@poe-code/config-mutations/package.json +8 -2
  54. package/node_modules/@poe-code/frontmatter/README.md +2 -2
  55. package/node_modules/@poe-code/frontmatter/package.json +8 -2
  56. package/node_modules/@poe-code/process-runner/README.md +1 -1
  57. package/node_modules/@poe-code/process-runner/package.json +8 -2
  58. package/node_modules/@poe-code/task-list/README.md +15 -3
  59. package/node_modules/@poe-code/task-list/package.json +8 -2
  60. package/node_modules/auth-store/README.md +15 -0
  61. package/node_modules/auth-store/package.json +3 -2
  62. package/node_modules/tiny-mcp-client/.turbo/turbo-build.log +1 -1
  63. package/node_modules/tiny-mcp-client/README.md +36 -36
  64. package/node_modules/tiny-mcp-client/dist/index.d.ts +660 -2
  65. package/node_modules/tiny-mcp-client/dist/index.js +3870 -1
  66. package/node_modules/tiny-mcp-client/package.json +8 -2
  67. package/node_modules/tiny-mcp-client/scripts/build.mjs +63 -0
  68. package/node_modules/toolcraft-design/README.md +8 -4
  69. package/node_modules/toolcraft-design/dist/dashboard/terminal.js +48 -6
  70. package/node_modules/toolcraft-design/dist/explorer/actions.d.ts +4 -0
  71. package/node_modules/toolcraft-design/dist/explorer/actions.js +1 -0
  72. package/node_modules/toolcraft-design/dist/explorer/events.d.ts +4 -0
  73. package/node_modules/toolcraft-design/dist/explorer/reducer.js +52 -4
  74. package/node_modules/toolcraft-design/dist/explorer/render/list.js +47 -20
  75. package/node_modules/toolcraft-design/dist/explorer/render/modal.js +13 -1
  76. package/node_modules/toolcraft-design/dist/explorer/render/test-fixtures.js +1 -1
  77. package/node_modules/toolcraft-design/dist/explorer/runtime.js +3 -0
  78. package/node_modules/toolcraft-design/dist/explorer/state.d.ts +9 -0
  79. package/node_modules/toolcraft-design/package.json +3 -2
  80. package/package.json +44 -6
  81. package/node_modules/mcp-oauth/LICENSE +0 -21
  82. package/node_modules/mcp-oauth/README.md +0 -31
  83. package/node_modules/mcp-oauth/dist/client/auth-store-session-store.d.ts +0 -14
  84. package/node_modules/mcp-oauth/dist/client/auth-store-session-store.js +0 -169
  85. package/node_modules/mcp-oauth/dist/client/authorization-state.d.ts +0 -8
  86. package/node_modules/mcp-oauth/dist/client/authorization-state.js +0 -47
  87. package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.d.ts +0 -3
  88. package/node_modules/mcp-oauth/dist/client/default-oauth-client-provider.js +0 -627
  89. package/node_modules/mcp-oauth/dist/client/loopback-authorization.d.ts +0 -20
  90. package/node_modules/mcp-oauth/dist/client/loopback-authorization.js +0 -207
  91. package/node_modules/mcp-oauth/dist/client/pkce.d.ts +0 -2
  92. package/node_modules/mcp-oauth/dist/client/pkce.js +0 -7
  93. package/node_modules/mcp-oauth/dist/client/token-endpoint.d.ts +0 -40
  94. package/node_modules/mcp-oauth/dist/client/token-endpoint.js +0 -164
  95. package/node_modules/mcp-oauth/dist/client/types.d.ts +0 -113
  96. package/node_modules/mcp-oauth/dist/client/types.js +0 -1
  97. package/node_modules/mcp-oauth/dist/index.d.ts +0 -10
  98. package/node_modules/mcp-oauth/dist/index.js +0 -7
  99. package/node_modules/mcp-oauth/dist/resource-indicator.d.ts +0 -1
  100. package/node_modules/mcp-oauth/dist/resource-indicator.js +0 -11
  101. package/node_modules/mcp-oauth/dist/server/jwks-token-verifier.d.ts +0 -28
  102. package/node_modules/mcp-oauth/dist/server/jwks-token-verifier.js +0 -304
  103. package/node_modules/mcp-oauth/dist/types.compile-check.d.ts +0 -1
  104. package/node_modules/mcp-oauth/dist/types.compile-check.js +0 -22
  105. package/node_modules/mcp-oauth/package.json +0 -32
  106. package/node_modules/tiny-mcp-client/dist/internal.d.ts +0 -556
  107. package/node_modules/tiny-mcp-client/dist/internal.js +0 -2688
  108. package/node_modules/tiny-mcp-client/dist/jsonrpc-types.compile-check.d.ts +0 -1
  109. package/node_modules/tiny-mcp-client/dist/jsonrpc-types.compile-check.js +0 -37
  110. package/node_modules/tiny-mcp-client/dist/mcp-lifecycle-types.compile-check.d.ts +0 -1
  111. package/node_modules/tiny-mcp-client/dist/mcp-lifecycle-types.compile-check.js +0 -50
  112. package/node_modules/tiny-mcp-client/dist/mcp-prompt-types.compile-check.d.ts +0 -1
  113. package/node_modules/tiny-mcp-client/dist/mcp-prompt-types.compile-check.js +0 -50
  114. package/node_modules/tiny-mcp-client/dist/mcp-resource-types.compile-check.d.ts +0 -1
  115. package/node_modules/tiny-mcp-client/dist/mcp-resource-types.compile-check.js +0 -51
  116. package/node_modules/tiny-mcp-client/dist/mcp-tool-types.compile-check.d.ts +0 -1
  117. package/node_modules/tiny-mcp-client/dist/mcp-tool-types.compile-check.js +0 -99
  118. package/node_modules/tiny-mcp-client/dist/mcp-transport-types.compile-check.d.ts +0 -1
  119. package/node_modules/tiny-mcp-client/dist/mcp-transport-types.compile-check.js +0 -56
  120. package/node_modules/tiny-mcp-client/dist/mcp-utility-types.compile-check.d.ts +0 -1
  121. package/node_modules/tiny-mcp-client/dist/mcp-utility-types.compile-check.js +0 -145
  122. package/node_modules/tiny-mcp-client/dist/oauth-discovery.d.ts +0 -24
  123. package/node_modules/tiny-mcp-client/dist/oauth-discovery.js +0 -382
@@ -0,0 +1,384 @@
1
+ import { isDeepStrictEqual } from "node:util";
2
+ import { McpClient, McpError, ERROR_INVALID_PARAMS, createSdkTestPair } from "tiny-mcp-client";
3
+ import { runCLI } from "../cli.js";
4
+ import { UserError } from "../index.js";
5
+ import { createMCPServer } from "../mcp.js";
6
+ import { filterSchemaForScope } from "../schema-scope.js";
7
+ import { createSDK } from "../sdk.js";
8
+ class SurfaceScopeError extends Error {
9
+ constructor(surface, path) {
10
+ super(`Command "${path.join(" ")}" is filtered out of the ${surface} surface.`);
11
+ this.name = "SurfaceScopeError";
12
+ }
13
+ }
14
+ function splitWords(value) {
15
+ const words = [];
16
+ let current = "";
17
+ for (let index = 0; index < value.length; index += 1) {
18
+ const char = value[index] ?? "";
19
+ const lower = char.toLowerCase();
20
+ const upper = char.toUpperCase();
21
+ const isSeparator = char === "-" || char === "_" || char === " " || char === ".";
22
+ if (isSeparator) {
23
+ if (current.length > 0) {
24
+ words.push(current.toLowerCase());
25
+ current = "";
26
+ }
27
+ continue;
28
+ }
29
+ const isUppercase = char !== lower && char === upper;
30
+ const previous = value[index - 1];
31
+ const next = value[index + 1];
32
+ const previousIsLowercase = previous !== undefined &&
33
+ previous === previous.toLowerCase() &&
34
+ previous !== previous.toUpperCase();
35
+ const nextIsLowercase = next !== undefined && next === next.toLowerCase() && next !== next.toUpperCase();
36
+ if (isUppercase && current.length > 0 && (previousIsLowercase || nextIsLowercase)) {
37
+ words.push(current.toLowerCase());
38
+ current = char;
39
+ continue;
40
+ }
41
+ current += char;
42
+ }
43
+ if (current.length > 0) {
44
+ words.push(current.toLowerCase());
45
+ }
46
+ return words;
47
+ }
48
+ function formatName(value, casing) {
49
+ const words = splitWords(value);
50
+ if (casing === "camel") {
51
+ return words
52
+ .map((word, index) => index === 0 ? word : `${word.slice(0, 1).toUpperCase()}${word.slice(1)}`)
53
+ .join("");
54
+ }
55
+ return words.join(casing === "kebab" ? "-" : "_");
56
+ }
57
+ function unwrapOptional(schema) {
58
+ return schema.kind === "optional" ? unwrapOptional(schema.inner) : schema;
59
+ }
60
+ function mapUnknownMCPObject(value) {
61
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => [formatName(key, "snake"), child]));
62
+ }
63
+ function filterParams(schema, params, surface) {
64
+ const filtered = filterSchemaForScope(schema, surface);
65
+ if (filtered?.kind !== "object") {
66
+ return {};
67
+ }
68
+ return Object.fromEntries(Object.entries(filtered.shape).flatMap(([key, childSchema]) => {
69
+ const value = params[key];
70
+ if (value === undefined) {
71
+ return [];
72
+ }
73
+ const unwrapped = unwrapOptional(childSchema);
74
+ if (unwrapped.kind === "object" &&
75
+ typeof value === "object" &&
76
+ value !== null &&
77
+ !Array.isArray(value)) {
78
+ return [[key, filterParams(unwrapped, value, surface)]];
79
+ }
80
+ return [[key, value]];
81
+ }));
82
+ }
83
+ function mapMCPValue(schema, value) {
84
+ const unwrapped = unwrapOptional(schema);
85
+ if (value === null || value === undefined) {
86
+ return value;
87
+ }
88
+ switch (unwrapped.kind) {
89
+ case "array":
90
+ return Array.isArray(value) ? value.map((item) => mapMCPValue(unwrapped.item, item)) : value;
91
+ case "object": {
92
+ if (typeof value !== "object" || Array.isArray(value)) {
93
+ return value;
94
+ }
95
+ return Object.fromEntries(Object.entries(value).map(([key, child]) => {
96
+ const childSchema = unwrapped.shape[key];
97
+ return [
98
+ formatName(key, "snake"),
99
+ childSchema === undefined ? child : mapMCPValue(childSchema, child)
100
+ ];
101
+ }));
102
+ }
103
+ case "record":
104
+ return typeof value === "object" && !Array.isArray(value)
105
+ ? Object.fromEntries(Object.entries(value).map(([key, child]) => [key, mapMCPValue(unwrapped.value, child)]))
106
+ : value;
107
+ case "oneOf": {
108
+ if (typeof value !== "object" || Array.isArray(value)) {
109
+ return value;
110
+ }
111
+ const objectValue = value;
112
+ const discriminator = objectValue[unwrapped.discriminator];
113
+ const branch = typeof discriminator === "string" ? unwrapped.branches[discriminator] : undefined;
114
+ return branch === undefined
115
+ ? mapUnknownMCPObject(objectValue)
116
+ : mapMCPValue(branch, objectValue);
117
+ }
118
+ case "union": {
119
+ if (typeof value !== "object" || Array.isArray(value)) {
120
+ return value;
121
+ }
122
+ const objectValue = value;
123
+ const branch = unwrapped.branches.find((candidate) => Object.keys(candidate.shape).every((key) => candidate.shape[key]?.kind === "optional" ||
124
+ Object.prototype.hasOwnProperty.call(objectValue, key)));
125
+ return branch === undefined
126
+ ? mapUnknownMCPObject(objectValue)
127
+ : mapMCPValue(branch, objectValue);
128
+ }
129
+ case "boolean":
130
+ case "enum":
131
+ case "json":
132
+ case "number":
133
+ case "string":
134
+ return value;
135
+ }
136
+ }
137
+ function success(value) {
138
+ return { ok: true, value, error: undefined };
139
+ }
140
+ function failure(error) {
141
+ return { ok: false, value: undefined, error };
142
+ }
143
+ async function runSDK(root, resolved, params, options) {
144
+ if (!resolved.command.scope.includes("sdk")) {
145
+ return failure(new SurfaceScopeError("sdk", resolved.path));
146
+ }
147
+ try {
148
+ let member = createSDK(root, options);
149
+ for (const segment of resolved.path) {
150
+ member = member[formatName(segment, "camel")];
151
+ }
152
+ if (typeof member !== "function") {
153
+ throw new Error(`SDK command "${resolved.path.join(".")}" is not callable.`);
154
+ }
155
+ return success(await member(filterParams(resolved.command.params, params, "sdk")));
156
+ }
157
+ catch (error) {
158
+ return failure(error);
159
+ }
160
+ }
161
+ function parseMcpValue(result, reference) {
162
+ if (result.structuredContent !== undefined) {
163
+ return result.structuredContent;
164
+ }
165
+ const text = result.content
166
+ .filter((item) => item.type === "text" && item.text !== undefined)
167
+ .map((item) => item.text)
168
+ .join("\n");
169
+ if (text.length === 0) {
170
+ return undefined;
171
+ }
172
+ if (reference?.ok === true && isDeepStrictEqual(text, reference.value)) {
173
+ return text;
174
+ }
175
+ try {
176
+ return JSON.parse(text);
177
+ }
178
+ catch {
179
+ return text;
180
+ }
181
+ }
182
+ async function runMCP(root, resolved, params, options, reference) {
183
+ if (!resolved.command.scope.includes("mcp")) {
184
+ return failure(new SurfaceScopeError("mcp", resolved.path));
185
+ }
186
+ const server = createMCPServer(root, {
187
+ ...options,
188
+ name: `${root.name}-parity`,
189
+ version: "0.0.0",
190
+ casing: "snake"
191
+ });
192
+ const client = new McpClient({
193
+ clientInfo: { name: "toolcraft-parity", version: "0.0.0" }
194
+ });
195
+ const pair = await createSdkTestPair(server, () => client);
196
+ try {
197
+ const toolName = [root.name, ...resolved.path]
198
+ .map((segment) => formatName(segment, "snake"))
199
+ .join("__");
200
+ const argumentsValue = mapMCPValue(resolved.command.params, filterParams(resolved.command.params, params, "mcp"));
201
+ const result = await client.callTool({ name: toolName, arguments: argumentsValue });
202
+ return success(parseMcpValue(result, reference));
203
+ }
204
+ catch (error) {
205
+ if (error instanceof McpError && error.code === ERROR_INVALID_PARAMS) {
206
+ return failure(new UserError(error.message));
207
+ }
208
+ return failure(error);
209
+ }
210
+ finally {
211
+ await pair.cleanup();
212
+ }
213
+ }
214
+ function valueAtPath(value, path) {
215
+ return path.reduce((current, segment) => typeof current === "object" && current !== null
216
+ ? current[segment]
217
+ : undefined, value);
218
+ }
219
+ function appendCLIValue(argv, flag, value) {
220
+ if (typeof value === "boolean") {
221
+ argv.push(value ? flag : `--no-${flag.slice(2)}`);
222
+ return;
223
+ }
224
+ if (Array.isArray(value)) {
225
+ argv.push(flag, ...value.map((item) => String(item)));
226
+ return;
227
+ }
228
+ argv.push(flag, typeof value === "object" ? JSON.stringify(value) : String(value));
229
+ }
230
+ function appendCLIFlags(argv, schema, params, positionals, sourcePath = [], flagPath = []) {
231
+ for (const [key, rawSchema] of Object.entries(schema.shape)) {
232
+ const nextSourcePath = [...sourcePath, key];
233
+ const nextFlagPath = [...flagPath, formatName(key, "kebab")];
234
+ const displayPath = nextSourcePath.join(".");
235
+ const value = params[key];
236
+ if (value === undefined || positionals.has(displayPath)) {
237
+ continue;
238
+ }
239
+ const childSchema = unwrapOptional(rawSchema);
240
+ if (childSchema.kind === "object" &&
241
+ typeof value === "object" &&
242
+ value !== null &&
243
+ !Array.isArray(value)) {
244
+ appendCLIFlags(argv, childSchema, value, positionals, nextSourcePath, nextFlagPath);
245
+ continue;
246
+ }
247
+ if (childSchema.kind === "record" &&
248
+ typeof value === "object" &&
249
+ value !== null &&
250
+ !Array.isArray(value)) {
251
+ for (const [recordKey, recordValue] of Object.entries(value)) {
252
+ appendCLIValue(argv, `--${[...nextFlagPath, recordKey].join(".")}`, recordValue);
253
+ }
254
+ continue;
255
+ }
256
+ appendCLIValue(argv, `--${nextFlagPath.join(".")}`, value);
257
+ }
258
+ }
259
+ function buildCLIArgv(root, resolved, params) {
260
+ const visibleParams = filterParams(resolved.command.params, params, "cli");
261
+ const argv = ["node", root.name, ...resolved.path.map((segment) => formatName(segment, "kebab"))];
262
+ for (const positional of resolved.command.positional) {
263
+ const value = valueAtPath(visibleParams, positional.split("."));
264
+ if (Array.isArray(value)) {
265
+ argv.push(...value.map((item) => String(item)));
266
+ }
267
+ else if (value !== undefined) {
268
+ argv.push(String(value));
269
+ }
270
+ }
271
+ appendCLIFlags(argv, filterSchemaForScope(resolved.command.params, "cli"), visibleParams, new Set(resolved.command.positional));
272
+ argv.push("--output", "json", "--debug");
273
+ return argv;
274
+ }
275
+ function parseCLIValue(entries, reference) {
276
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
277
+ const entry = entries[index];
278
+ if (entry === undefined) {
279
+ continue;
280
+ }
281
+ try {
282
+ const parsed = JSON.parse(entry);
283
+ if (reference?.ok !== true || isDeepStrictEqual(parsed, reference.value)) {
284
+ return parsed;
285
+ }
286
+ if (typeof parsed === "object" &&
287
+ parsed !== null &&
288
+ !Array.isArray(parsed) &&
289
+ Object.keys(parsed).length === 1 &&
290
+ Object.prototype.hasOwnProperty.call(parsed, "result") &&
291
+ isDeepStrictEqual(parsed.result, reference.value)) {
292
+ return reference.value;
293
+ }
294
+ if ((reference.value === null || reference.value === undefined) &&
295
+ isDeepStrictEqual(parsed, { ok: true })) {
296
+ return reference.value;
297
+ }
298
+ return parsed;
299
+ }
300
+ catch {
301
+ continue;
302
+ }
303
+ }
304
+ return entries.at(-1);
305
+ }
306
+ function parseCLIError(entries) {
307
+ const message = entries.at(-1) ?? "CLI command failed.";
308
+ const lines = message.split("\n");
309
+ const lastLine = lines.at(-1) ?? "";
310
+ if (lastLine.startsWith("Run ") && lastLine.endsWith(" --help for usage.")) {
311
+ lines.pop();
312
+ }
313
+ return new UserError(lines.join("\n"));
314
+ }
315
+ async function runCLISurface(root, resolved, params, options, reference) {
316
+ if (!resolved.command.scope.includes("cli")) {
317
+ return failure(new SurfaceScopeError("cli", resolved.path));
318
+ }
319
+ const entries = [];
320
+ const previousExitCode = process.exitCode;
321
+ process.exitCode = undefined;
322
+ try {
323
+ await runCLI(root, {
324
+ ...options,
325
+ argv: buildCLIArgv(root, resolved, params),
326
+ controls: { debug: true, output: true },
327
+ outputEmitter: (entry) => entries.push(entry)
328
+ });
329
+ if (process.exitCode !== undefined && process.exitCode !== 0) {
330
+ return failure(parseCLIError(entries));
331
+ }
332
+ return success(parseCLIValue(entries, reference));
333
+ }
334
+ catch (error) {
335
+ return failure(error);
336
+ }
337
+ finally {
338
+ process.exitCode = previousExitCode;
339
+ }
340
+ }
341
+ function errorIdentity(error) {
342
+ if (error instanceof Error) {
343
+ return { name: error.constructor.name, message: error.message };
344
+ }
345
+ return { name: typeof error, message: String(error) };
346
+ }
347
+ function outcomesAgree(left, right) {
348
+ if (left.ok !== right.ok) {
349
+ return false;
350
+ }
351
+ if (left.ok) {
352
+ return isDeepStrictEqual(left.value, right.value);
353
+ }
354
+ return isDeepStrictEqual(errorIdentity(left.error), errorIdentity(right.error));
355
+ }
356
+ function describeOutcome(surface, outcome) {
357
+ if (outcome.ok) {
358
+ return `${surface}: ok ${JSON.stringify(outcome.value)}`;
359
+ }
360
+ const error = errorIdentity(outcome.error);
361
+ return `${surface}: error ${error.name}: ${error.message}`;
362
+ }
363
+ export async function runParity(root, resolved, params, options) {
364
+ const sdk = await runSDK(root, resolved, params, options);
365
+ const mcp = await runMCP(root, resolved, params, options, sdk);
366
+ const cli = await runCLISurface(root, resolved, params, options, sdk);
367
+ const agree = outcomesAgree(sdk, mcp) && outcomesAgree(sdk, cli);
368
+ return {
369
+ sdk,
370
+ mcp,
371
+ cli,
372
+ agree,
373
+ ...(agree
374
+ ? {}
375
+ : {
376
+ diff: [
377
+ `Surface outcomes differ for "${resolved.path.join(" ")}".`,
378
+ describeOutcome("sdk", sdk),
379
+ describeOutcome("mcp", mcp),
380
+ describeOutcome("cli", cli)
381
+ ].join("\n")
382
+ })
383
+ };
384
+ }
@@ -0,0 +1,6 @@
1
+ import type { RenderPrimitives } from "../index.js";
2
+ export interface RenderCapture {
3
+ primitives: RenderPrimitives;
4
+ output(): string;
5
+ }
6
+ export declare function createRenderCapture(): RenderCapture;
@@ -0,0 +1,54 @@
1
+ import { createLogger, light, renderTable, stripAnsi, withOutputFormat } from "toolcraft-design";
2
+ const captureWidth = 80;
3
+ function withoutColors(theme) {
4
+ return {
5
+ header: (text) => stripAnsi(theme.header(text)),
6
+ divider: (text) => stripAnsi(theme.divider(text)),
7
+ prompt: (text) => stripAnsi(theme.prompt(text)),
8
+ number: (text) => stripAnsi(theme.number(text)),
9
+ intro: (text) => stripAnsi(theme.intro(text)),
10
+ resolvedSymbol: stripAnsi(theme.resolvedSymbol),
11
+ errorSymbol: stripAnsi(theme.errorSymbol),
12
+ accent: (text) => stripAnsi(theme.accent(text)),
13
+ muted: (text) => stripAnsi(theme.muted(text)),
14
+ success: (text) => stripAnsi(theme.success(text)),
15
+ warning: (text) => stripAnsi(theme.warning(text)),
16
+ error: (text) => stripAnsi(theme.error(text)),
17
+ info: (text) => stripAnsi(theme.info(text)),
18
+ badge: (text) => stripAnsi(theme.badge(text)),
19
+ styles: {
20
+ accent: { ...theme.styles.accent },
21
+ muted: { ...theme.styles.muted },
22
+ success: { ...theme.styles.success },
23
+ warning: { ...theme.styles.warning },
24
+ error: { ...theme.styles.error },
25
+ info: { ...theme.styles.info }
26
+ }
27
+ };
28
+ }
29
+ function createCaptureTheme() {
30
+ return {
31
+ ...withoutColors(light),
32
+ intro: (text) => ` Poe - ${text} `
33
+ };
34
+ }
35
+ export function createRenderCapture() {
36
+ const output = [];
37
+ const captureTheme = createCaptureTheme();
38
+ const emit = (message) => {
39
+ output.push(stripAnsi(message));
40
+ };
41
+ return {
42
+ primitives: {
43
+ logger: createLogger(emit),
44
+ renderTable: (options) => withOutputFormat("terminal", () => stripAnsi(renderTable({
45
+ ...options,
46
+ theme: withoutColors(options.theme),
47
+ maxWidth: captureWidth
48
+ }))),
49
+ getTheme: () => captureTheme,
50
+ note: (message, title) => emit(title === undefined ? message : `${title}\n${message}`)
51
+ },
52
+ output: () => output.join("\n")
53
+ };
54
+ }
@@ -0,0 +1 @@
1
+ export * from "tiny-mcp-client";
@@ -0,0 +1 @@
1
+ export * from "tiny-mcp-client";
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@poe-code/agent-defs",
3
3
  "version": "0.0.1",
4
- "license": "MIT",
5
4
  "private": true,
5
+ "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "types": "dist/index.d.ts",
@@ -16,6 +16,12 @@
16
16
  "build": "node ../../scripts/guard-package-dist.mjs && tsc"
17
17
  },
18
18
  "files": [
19
- "dist"
20
- ]
19
+ "dist",
20
+ "LICENSE"
21
+ ],
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/poe-platform/poe-code.git",
25
+ "directory": "packages/agent-defs"
26
+ }
21
27
  }
@@ -1,4 +1,4 @@
1
- ## Overview
1
+ # @poe-code/agent-human-in-loop
2
2
 
3
3
  `@poe-code/agent-human-in-loop` is a UI-only package for asking a human "approve this?" before an agent proceeds. The UI is providerized, sync vs async is the caller's choice because the API returns a Promise you can await immediately or hold and resolve later, and the package is approval-only: decline returns a declined outcome and can optionally capture a reason.
4
4
 
@@ -21,7 +21,13 @@ declare function mockProvider(
21
21
  - `osascriptProvider({ title?, binary? })` — macOS native dialog via `display dialog`. Mac only.
22
22
  - `mockProvider(answer | thunk)` — fixed or scripted answers for tests.
23
23
 
24
- ## Env vars
24
+ ## Configuration Options
25
+
26
+ - `osascriptProvider({ title?, binary? })`: optional dialog title and `osascript` binary path.
27
+ - `mockProvider(answer)`: fixed approval result or callback for tests.
28
+ - `requestApproval({ message, approveLabel?, declineLabel?, declineInputPrompt?, provider })`: approval prompt payload and provider.
29
+
30
+ ## Environment Variables
25
31
 
26
32
  None in v1.
27
33
 
@@ -29,6 +35,10 @@ None in v1.
29
35
 
30
36
  Messages and prompts are passed verbatim through the dialog; the provider escapes `"` and `\` for AppleScript string literals. Do not pass user-supplied AppleScript fragments expecting them to execute.
31
37
 
38
+ ## Manual QA
39
+
40
+ Use [QA.md](QA.md) for package-level approval-flow checks after behavior changes.
41
+
32
42
  ## Example
33
43
 
34
44
  ```ts
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@poe-code/agent-human-in-loop",
3
3
  "version": "0.0.1",
4
- "license": "MIT",
5
4
  "private": true,
5
+ "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
@@ -19,7 +19,13 @@
19
19
  "dev": "tsx example.ts"
20
20
  },
21
21
  "files": [
22
- "dist"
22
+ "dist",
23
+ "LICENSE"
23
24
  ],
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/poe-platform/poe-code.git",
28
+ "directory": "packages/agent-human-in-loop"
29
+ },
24
30
  "devDependencies": {}
25
31
  }
@@ -36,13 +36,13 @@ console.log(supportedAgents);
36
36
 
37
37
  `ApplyOptions` controls how mutations are applied:
38
38
 
39
- | Option | Type | Description |
40
- | ------ | ---- | ----------- |
41
- | `fs` | `FileSystem` | Filesystem adapter from `@poe-code/config-mutations`. |
42
- | `homeDir` | `string` | Home directory used to resolve `~` in agent config paths. |
43
- | `platform` | `"darwin" \| "linux" \| "win32"` | Selects platform-specific config paths. |
44
- | `dryRun` | `boolean` | Computes mutations without writing files. |
45
- | `observers` | `MutationObservers` | Receives mutation lifecycle events. |
39
+ | Option | Type | Description |
40
+ | ----------- | -------------------------------- | --------------------------------------------------------- |
41
+ | `fs` | `FileSystem` | Filesystem adapter from `@poe-code/config-mutations`. |
42
+ | `homeDir` | `string` | Home directory used to resolve `~` in agent config paths. |
43
+ | `platform` | `"darwin" \| "linux" \| "win32"` | Selects platform-specific config paths. |
44
+ | `dryRun` | `boolean` | Computes mutations without writing files. |
45
+ | `observers` | `MutationObservers` | Receives mutation lifecycle events. |
46
46
 
47
47
  The package's built-in agent config table declares `configFile`, `configKey`,
48
48
  `format`, `shape`, and optional `mcpOutputFormat` per supported agent.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@poe-code/agent-mcp-config",
3
3
  "version": "0.0.1",
4
- "license": "MIT",
5
4
  "private": true,
5
+ "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "types": "dist/index.d.ts",
@@ -16,8 +16,14 @@
16
16
  "build": "node ../../scripts/guard-package-dist.mjs && tsc"
17
17
  },
18
18
  "files": [
19
- "dist"
19
+ "dist",
20
+ "LICENSE"
20
21
  ],
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/poe-platform/poe-code.git",
25
+ "directory": "packages/agent-mcp-config"
26
+ },
21
27
  "dependencies": {
22
28
  "yaml": "^2.8.3"
23
29
  }
@@ -38,14 +38,14 @@ await runMutations(
38
38
 
39
39
  `MutationContext` controls execution:
40
40
 
41
- | Option | Type | Description |
42
- | ------ | ---- | ----------- |
43
- | `fs` | `FileSystem` | Required filesystem adapter. |
44
- | `homeDir` | `string` | Required home directory for `~` expansion. |
45
- | `dryRun` | `boolean` | Reports changes without writing them. |
46
- | `observers` | `MutationObservers` | Receives mutation lifecycle events. |
47
- | `templates` | `TemplateLoader` | Loads templates referenced by template mutations. |
48
- | `pathMapper` | `PathMapper` | Redirects target directories for isolated config flows. |
41
+ | Option | Type | Description |
42
+ | ------------ | ------------------- | ------------------------------------------------------- |
43
+ | `fs` | `FileSystem` | Required filesystem adapter. |
44
+ | `homeDir` | `string` | Required home directory for `~` expansion. |
45
+ | `dryRun` | `boolean` | Reports changes without writing them. |
46
+ | `observers` | `MutationObservers` | Receives mutation lifecycle events. |
47
+ | `templates` | `TemplateLoader` | Loads templates referenced by template mutations. |
48
+ | `pathMapper` | `PathMapper` | Redirects target directories for isolated config flows. |
49
49
 
50
50
  Mutation builders also expose per-mutation options such as target path, format,
51
51
  label, force removal, backup behavior, template id, and transform callbacks.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@poe-code/config-mutations",
3
3
  "version": "0.0.1",
4
- "license": "MIT",
5
4
  "private": true,
5
+ "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "types": "dist/index.d.ts",
@@ -20,8 +20,14 @@
20
20
  "build": "node ../../scripts/guard-package-dist.mjs && tsc"
21
21
  },
22
22
  "files": [
23
- "dist"
23
+ "dist",
24
+ "LICENSE"
24
25
  ],
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/poe-platform/poe-code.git",
29
+ "directory": "packages/config-mutations"
30
+ },
25
31
  "dependencies": {
26
32
  "jsonc-parser": "^3.3.1",
27
33
  "smol-toml": "^1.3.0",
@@ -26,10 +26,10 @@ Duplicate mapping keys use YAML's last-wins behavior by default, matching the
26
26
  legacy frontmatter readers. Callers that need strict YAML mappings can pass
27
27
  `{ uniqueKeys: true }` to `parseFrontmatter` or `parseFrontmatterDocument`.
28
28
 
29
- ## Environment
29
+ ## Environment Variables
30
30
 
31
31
  This package reads no environment variables.
32
32
 
33
- ## Config
33
+ ## Configuration Options
34
34
 
35
35
  This package has no configuration options.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@poe-code/frontmatter",
3
3
  "version": "0.0.1",
4
- "license": "MIT",
5
4
  "private": true,
5
+ "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "types": "dist/index.d.ts",
@@ -18,8 +18,14 @@
18
18
  "test:unit": "cd ../.. && vitest run packages/frontmatter/src"
19
19
  },
20
20
  "files": [
21
- "dist"
21
+ "dist",
22
+ "LICENSE"
22
23
  ],
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/poe-platform/poe-code.git",
27
+ "directory": "packages/frontmatter"
28
+ },
23
29
  "dependencies": {
24
30
  "yaml": "^2.8.2"
25
31
  }