toolcraft 0.0.93 → 0.0.95

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 (76) hide show
  1. package/README.md +74 -4
  2. package/composition.json +1 -1
  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 +24 -65
  13. package/dist/composition.json +1 -1
  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 +2 -0
  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 +2 -0
  50. package/node_modules/@poe-code/agent-mcp-config/README.md +7 -7
  51. package/node_modules/@poe-code/agent-mcp-config/package.json +2 -0
  52. package/node_modules/@poe-code/config-mutations/README.md +8 -8
  53. package/node_modules/@poe-code/config-mutations/package.json +2 -0
  54. package/node_modules/@poe-code/frontmatter/README.md +2 -2
  55. package/node_modules/@poe-code/frontmatter/package.json +2 -0
  56. package/node_modules/@poe-code/process-runner/README.md +1 -1
  57. package/node_modules/@poe-code/process-runner/package.json +2 -0
  58. package/node_modules/@poe-code/task-list/README.md +15 -3
  59. package/node_modules/@poe-code/task-list/package.json +2 -0
  60. package/node_modules/auth-store/README.md +15 -0
  61. package/node_modules/auth-store/package.json +2 -0
  62. package/node_modules/tiny-mcp-client/README.md +36 -36
  63. package/node_modules/tiny-mcp-client/package.json +2 -0
  64. package/node_modules/toolcraft-design/README.md +8 -4
  65. package/node_modules/toolcraft-design/dist/dashboard/terminal.js +48 -6
  66. package/node_modules/toolcraft-design/dist/explorer/actions.d.ts +4 -0
  67. package/node_modules/toolcraft-design/dist/explorer/actions.js +1 -0
  68. package/node_modules/toolcraft-design/dist/explorer/events.d.ts +4 -0
  69. package/node_modules/toolcraft-design/dist/explorer/reducer.js +52 -4
  70. package/node_modules/toolcraft-design/dist/explorer/render/list.js +47 -20
  71. package/node_modules/toolcraft-design/dist/explorer/render/modal.js +13 -1
  72. package/node_modules/toolcraft-design/dist/explorer/render/test-fixtures.js +1 -1
  73. package/node_modules/toolcraft-design/dist/explorer/runtime.js +3 -0
  74. package/node_modules/toolcraft-design/dist/explorer/state.d.ts +9 -0
  75. package/node_modules/toolcraft-design/package.json +5 -0
  76. package/package.json +42 -2
@@ -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,4 +1,6 @@
1
1
  {
2
+ "name": "@poe-code/agent-defs",
3
+ "version": "0.0.1",
2
4
  "private": true,
3
5
  "license": "MIT",
4
6
  "type": "module",
@@ -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,4 +1,6 @@
1
1
  {
2
+ "name": "@poe-code/agent-human-in-loop",
3
+ "version": "0.0.1",
2
4
  "private": true,
3
5
  "license": "MIT",
4
6
  "type": "module",
@@ -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,4 +1,6 @@
1
1
  {
2
+ "name": "@poe-code/agent-mcp-config",
3
+ "version": "0.0.1",
2
4
  "private": true,
3
5
  "license": "MIT",
4
6
  "type": "module",
@@ -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,4 +1,6 @@
1
1
  {
2
+ "name": "@poe-code/config-mutations",
3
+ "version": "0.0.1",
2
4
  "private": true,
3
5
  "license": "MIT",
4
6
  "type": "module",
@@ -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,4 +1,6 @@
1
1
  {
2
+ "name": "@poe-code/frontmatter",
3
+ "version": "0.0.1",
2
4
  "private": true,
3
5
  "license": "MIT",
4
6
  "type": "module",
@@ -32,7 +32,7 @@ Low-level process execution abstraction. Single interface for launching processe
32
32
  - `extraArgs`: additional runtime arguments.
33
33
  - `containerName`: optional container name prefix.
34
34
 
35
- ## Environment variables
35
+ ## Environment Variables
36
36
 
37
37
  This package exposes no environment variables.
38
38
 
@@ -1,4 +1,6 @@
1
1
  {
2
+ "name": "@poe-code/process-runner",
3
+ "version": "0.0.1",
2
4
  "private": true,
3
5
  "license": "MIT",
4
6
  "type": "module",
@@ -46,7 +46,7 @@ interface EventDef<TState extends string = string> {
46
46
 
47
47
  Pass a custom machine with `openTaskList({ stateMachine })`. If omitted, the package uses `defaultStateMachine`, whose event names are `plan`, `start`, `complete`, and `archive`.
48
48
 
49
- ## Options
49
+ ## Configuration Options
50
50
 
51
51
  | Option | Type | Default | Behavior |
52
52
  | ----------------- | ---------------------------------------------- | -------------------------- | ----------------------------------------------------------------------------------------------------------------- |
@@ -59,7 +59,7 @@ Pass a custom machine with `openTaskList({ stateMachine })`. If omitted, the pac
59
59
  | `fs` | `TaskListFs` | `node:fs/promises` adapter | Injectable filesystem, primarily for tests. |
60
60
  | `stateMachine` | `StateMachineDef` | `defaultStateMachine` | Overrides the task lifecycle used by `create`, `fire`, `canFire`, and `events`. |
61
61
 
62
- ## Env vars
62
+ ## Environment Variables
63
63
 
64
64
  | Env var | Behavior |
65
65
  | --------- | --------------------------------------------------------------- |
@@ -231,7 +231,7 @@ interface SyncGhProjectReport extends VerifyGhProjectReport {
231
231
  }
232
232
  ```
233
233
 
234
- The CLI exposes the same verification and provisioning flow:
234
+ The root CLI exposes verification, provisioning, and task mutation commands:
235
235
 
236
236
  ```sh
237
237
  poe-code tasks verify <list> --workflow ./WORKFLOW.md --repo octo-org/octo-repo --project octo-org/7 --states queued,agent-running,human-review,done,failed,archived --json
@@ -249,6 +249,18 @@ poe-code tasks sync <list> --workflow ./WORKFLOW.md --repo octo-org/octo-repo --
249
249
  | `--json` | `verify`, `sync` | Prints the report as JSON. |
250
250
  | `--yes` | `sync` | Confirms non-interactive provisioning. |
251
251
 
252
+ Additional task backend commands:
253
+
254
+ | Command | Purpose |
255
+ | --------------------------------------- | ----------------------------------------------------- |
256
+ | `poe-code tasks move` | Move tasks between workflow-configured backends. |
257
+ | `poe-code tasks import` | Import Markdown task files into a configured backend. |
258
+ | `poe-code tasks get <id>` | Read one task. |
259
+ | `poe-code tasks set <id>` | Update one task. |
260
+ | `poe-code tasks set-state <id> <state>` | Set one task's state. |
261
+ | `poe-code tasks next <id>` | Advance one task to the next workflow state. |
262
+ | `poe-code tasks comment <id>` | Add a task comment when the backend supports it. |
263
+
252
264
  The `Status` field name and option names are matched case-sensitively. The field must be named `Status`, and required option names must match exactly. For example, `status` is treated as a missing field, and `Done` is treated as missing when the required state is `done`.
253
265
 
254
266
  In v1, if sync creates a new GitHub Project v2 board, the CLI prints the new project number and does not rewrite `WORKFLOW.md`. The operator must update `WORKFLOW.md` by hand with the printed `<owner>/<number>`.
@@ -1,4 +1,6 @@
1
1
  {
2
+ "name": "@poe-code/task-list",
3
+ "version": "0.0.1",
2
4
  "private": true,
3
5
  "license": "MIT",
4
6
  "type": "module",