@dreb/coding-agent 2.50.0 → 2.52.0
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 +7 -5
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +11 -6
- package/dist/cli/args.js.map +1 -1
- package/dist/core/extensions/types.d.ts +31 -11
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/tools/ask-user.d.ts +28 -20
- package/dist/core/tools/ask-user.d.ts.map +1 -1
- package/dist/core/tools/ask-user.js +130 -106
- package/dist/core/tools/ask-user.js.map +1 -1
- package/dist/core/tools/index.d.ts +9 -7
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/modes/interactive/components/ask-wizard.d.ts +83 -0
- package/dist/modes/interactive/components/ask-wizard.d.ts.map +1 -0
- package/dist/modes/interactive/components/ask-wizard.js +464 -0
- package/dist/modes/interactive/components/ask-wizard.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +4 -2
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +7 -5
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +100 -20
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +18 -7
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/dashboard.md +143 -1
- package/docs/extensions.md +35 -18
- package/docs/rpc.md +26 -8
- package/examples/extensions/rpc-demo.ts +29 -0
- package/examples/rpc-extension-ui.ts +162 -6
- package/package.json +1 -1
- package/skills/mach6-implement/SKILL.md +1 -1
- package/skills/mach6-issue/SKILL.md +3 -3
- package/skills/mach6-plan/SKILL.md +3 -3
- package/skills/mach6-publish/SKILL.md +2 -2
- package/skills/mach6-push/SKILL.md +2 -2
- package/skills/mach6-review/SKILL.md +4 -4
- package/dist/modes/interactive/components/ask-user.d.ts +0 -59
- package/dist/modes/interactive/components/ask-user.d.ts.map +0 -1
- package/dist/modes/interactive/components/ask-user.js +0 -240
- package/dist/modes/interactive/components/ask-user.js.map +0 -1
package/docs/extensions.md
CHANGED
|
@@ -162,24 +162,33 @@ export default function (dreb: ExtensionAPI) {
|
|
|
162
162
|
ctx.ui.setStatus("my-ext", "Processing..."); // Footer status
|
|
163
163
|
ctx.ui.setWidget("my-ext", ["Line 1", "Line 2"]); // Widget above editor (default)
|
|
164
164
|
|
|
165
|
-
// ctx.ui.ask —
|
|
166
|
-
// rendered natively in the TUI and Dashboard (and over RPC).
|
|
167
|
-
// { selected: string[], customText?: string
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
|
|
165
|
+
// ctx.ui.ask — one or more rich clarifying questions asked together as a
|
|
166
|
+
// single wizard, rendered natively in the TUI and Dashboard (and over RPC).
|
|
167
|
+
// Resolves to { answers: Array<{ selected: string[], customText?: string,
|
|
168
|
+
// skipped?: boolean }> } with one answer per question, in order. Dismissing
|
|
169
|
+
// or timing out an ask stops the current agent turn and resolves undefined.
|
|
170
|
+
// This is the same primitive that powers the built-in `ask_user` tool.
|
|
171
|
+
const result = await ctx.ui.ask(
|
|
171
172
|
{
|
|
172
173
|
title: "Choose a database",
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
174
|
+
questions: [
|
|
175
|
+
{
|
|
176
|
+
question: "Which persistence strategy should I use?",
|
|
177
|
+
options: ["SQLite", "PostgreSQL", "Keep the JSON file"],
|
|
178
|
+
allowFreeText: true, // default; offers a "type your own answer" field
|
|
179
|
+
multiSelect: false, // true → checkboxes, combined with any free text
|
|
180
|
+
multiline: false, // true → multi-line free-text area
|
|
181
|
+
},
|
|
182
|
+
{ question: "Any migration constraints I should know about?", multiline: true },
|
|
183
|
+
],
|
|
178
184
|
},
|
|
179
185
|
{ signal, timeout: 60000 }, // both optional; absent user never deadlocks
|
|
180
186
|
);
|
|
181
|
-
if (!
|
|
187
|
+
if (!result) {
|
|
182
188
|
// The ask was dismissed or timed out; the current agent turn is stopping.
|
|
189
|
+
} else {
|
|
190
|
+
const [dbAnswer] = result.answers; // one entry per question, in order
|
|
191
|
+
// dbAnswer.selected / dbAnswer.customText / dbAnswer.skipped
|
|
183
192
|
}
|
|
184
193
|
});
|
|
185
194
|
|
|
@@ -1804,14 +1813,21 @@ const ok = await ctx.ui.confirm("Delete?", "This cannot be undone");
|
|
|
1804
1813
|
// Text input
|
|
1805
1814
|
const name = await ctx.ui.input("Name:", "placeholder");
|
|
1806
1815
|
|
|
1807
|
-
// Rich question:
|
|
1808
|
-
//
|
|
1809
|
-
|
|
1816
|
+
// Rich question wizard: one or more questions asked together, question text
|
|
1817
|
+
// supports Markdown and options can be combined with free text. Resolves to
|
|
1818
|
+
// { answers: [...] } (one per question, in order); dismissal stops the current
|
|
1819
|
+
// agent turn and returns undefined.
|
|
1820
|
+
const result = await ctx.ui.ask({
|
|
1810
1821
|
title: "Choose a database",
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1822
|
+
questions: [
|
|
1823
|
+
{
|
|
1824
|
+
question: "Which persistence strategy should I use?",
|
|
1825
|
+
options: ["SQLite", "PostgreSQL"],
|
|
1826
|
+
allowFreeText: true,
|
|
1827
|
+
},
|
|
1828
|
+
],
|
|
1814
1829
|
});
|
|
1830
|
+
const answer = result?.answers[0];
|
|
1815
1831
|
|
|
1816
1832
|
// Multi-line editor
|
|
1817
1833
|
const text = await ctx.ui.editor("Edit:", "prefilled text");
|
|
@@ -2156,6 +2172,7 @@ All examples in [examples/extensions/](../examples/extensions/).
|
|
|
2156
2172
|
| `overlay-qa-tests.ts` | Comprehensive overlay tests | `ui.custom`, all overlay options |
|
|
2157
2173
|
| `notify.ts` | Simple notifications | `ui.notify` |
|
|
2158
2174
|
| `timed-confirm.ts` | Dialogs with timeout | `ui.confirm` with timeout/signal |
|
|
2175
|
+
| `rpc-demo.ts` | Complete RPC extension-UI demo, including the batch question wizard | `ui.select`, `ui.confirm`, `ui.input`, `ui.editor`, `ui.ask`, status/widget/title methods |
|
|
2159
2176
|
| `mac-system-theme.ts` | Auto-switch theme | `setTheme`, `exec` |
|
|
2160
2177
|
| **Complex Extensions** |||
|
|
2161
2178
|
| `plan-mode/` | Full plan mode implementation | All event types, `registerCommand`, `registerShortcut`, `registerFlag`, `setStatus`, `setWidget`, `sendMessage`, `setActiveTools` |
|
package/docs/rpc.md
CHANGED
|
@@ -2015,7 +2015,7 @@ Expected response: `extension_ui_response` with `value` (the edited text) or `ca
|
|
|
2015
2015
|
|
|
2016
2016
|
#### ask
|
|
2017
2017
|
|
|
2018
|
-
Ask the user
|
|
2018
|
+
Ask the user one or more rich clarifying questions together as a single wizard. Each question has Markdown-formatted question text, optional single- or multi-select options, and an optional free-text field. This powers the built-in `ask_user` tool. The request carries a `questions` array (1-10 entries); per question, `options` (2-4 nonblank strings) is optional and `allowFreeText` (default `true`), `multiSelect`, and `multiline` are optional booleans. An overall `title` defaults to `"Question"`.
|
|
2019
2019
|
|
|
2020
2020
|
```json
|
|
2021
2021
|
{
|
|
@@ -2023,11 +2023,20 @@ Ask the user a rich clarifying question with Markdown-formatted question text, o
|
|
|
2023
2023
|
"id": "uuid-5",
|
|
2024
2024
|
"method": "ask",
|
|
2025
2025
|
"title": "Choose a database",
|
|
2026
|
-
"
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2026
|
+
"questions": [
|
|
2027
|
+
{
|
|
2028
|
+
"question": "Which persistence strategy should I use?",
|
|
2029
|
+
"title": "Storage",
|
|
2030
|
+
"options": ["SQLite", "PostgreSQL", "Keep the JSON file"],
|
|
2031
|
+
"allowFreeText": true,
|
|
2032
|
+
"multiSelect": false,
|
|
2033
|
+
"multiline": false
|
|
2034
|
+
},
|
|
2035
|
+
{
|
|
2036
|
+
"question": "Any migration constraints I should know about?",
|
|
2037
|
+
"multiline": true
|
|
2038
|
+
}
|
|
2039
|
+
],
|
|
2031
2040
|
"timeout": 60000,
|
|
2032
2041
|
"expiresAt": 1785434460000
|
|
2033
2042
|
}
|
|
@@ -2035,12 +2044,21 @@ Ask the user a rich clarifying question with Markdown-formatted question text, o
|
|
|
2035
2044
|
|
|
2036
2045
|
`timeout` is the original duration in milliseconds. `expiresAt` is the corresponding absolute Unix timestamp in milliseconds; Dashboard clients should use it for the visible countdown so reload, resync, or drill-in recovery does not restart the full duration.
|
|
2037
2046
|
|
|
2038
|
-
Expected response: `extension_ui_response` with `selected` (an array of strings) and optional string `customText` (the typed answer)
|
|
2047
|
+
Expected response: `extension_ui_response` with `answers` — an array with one entry per question, in the same order. Each answer has `selected` (an array of strings) and optional string `customText` (the typed answer); an answer with an empty `selected` and no nonblank `customText` is treated as skipped (an explicit `skipped: true` is also honored). Answering submits the batch even when some questions are skipped. Sending `cancelled: true` stops the current agent turn rather than continuing; a timeout has the same stop semantics. A missing/non-array `answers`, or a malformed `selected`/`customText`, is rejected as a protocol failure.
|
|
2039
2048
|
|
|
2040
2049
|
```json
|
|
2041
|
-
{
|
|
2050
|
+
{
|
|
2051
|
+
"type": "extension_ui_response",
|
|
2052
|
+
"id": "uuid-5",
|
|
2053
|
+
"answers": [
|
|
2054
|
+
{ "selected": ["SQLite"], "customText": "with WAL enabled" },
|
|
2055
|
+
{ "selected": [], "skipped": true }
|
|
2056
|
+
]
|
|
2057
|
+
}
|
|
2042
2058
|
```
|
|
2043
2059
|
|
|
2060
|
+
`ask` requests are single-flight per RPC runtime. If parallel tool execution starts several calls concurrently, RPC emits only the first request and queues the rest in FIFO order. The next request is emitted only after the active request settles; a queued call that is aborted before it starts settles without emitting. Hosts therefore need to render at most one pending `ask` wizard at a time.
|
|
2061
|
+
|
|
2044
2062
|
#### notify
|
|
2045
2063
|
|
|
2046
2064
|
Display a notification. Fire-and-forget, no response expected.
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* - confirm() - on session_before_switch
|
|
11
11
|
* - input() - via /rpc-input command
|
|
12
12
|
* - editor() - via /rpc-editor command
|
|
13
|
+
* - ask() - via /rpc-ask command
|
|
13
14
|
* - notify() - after each dialog completes
|
|
14
15
|
* - setStatus() - on turn_start/turn_end
|
|
15
16
|
* - setWidget() - on session_start
|
|
@@ -112,6 +113,34 @@ export default function (dreb: ExtensionAPI) {
|
|
|
112
113
|
},
|
|
113
114
|
});
|
|
114
115
|
|
|
116
|
+
// -- batch ask wizard via command --
|
|
117
|
+
|
|
118
|
+
dreb.registerCommand("rpc-ask", {
|
|
119
|
+
description: "Open a batch question wizard (demonstrates ctx.ui.ask in RPC)",
|
|
120
|
+
handler: async (_args, ctx) => {
|
|
121
|
+
const result = await ctx.ui.ask({
|
|
122
|
+
title: "Configure the RPC demo",
|
|
123
|
+
questions: [
|
|
124
|
+
{
|
|
125
|
+
question: "Which **transport checks** should run?",
|
|
126
|
+
title: "Checks",
|
|
127
|
+
options: ["Protocol", "Reconnect", "Timeout"],
|
|
128
|
+
multiSelect: true,
|
|
129
|
+
allowFreeText: true,
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
question: "Anything else the RPC host should display?",
|
|
133
|
+
title: "Notes",
|
|
134
|
+
multiline: true,
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
});
|
|
138
|
+
if (!result) return;
|
|
139
|
+
const answered = result.answers.filter((answer) => !answer.skipped).length;
|
|
140
|
+
ctx.ui.notify(`Submitted ${answered} of ${result.answers.length} answers`, "info");
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
|
|
115
144
|
// -- setEditorText via command --
|
|
116
145
|
|
|
117
146
|
dreb.registerCommand("rpc-prefill", {
|
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A lightweight TUI chat client that spawns the agent in RPC mode.
|
|
5
5
|
* Demonstrates how to build a custom UI on top of the RPC protocol,
|
|
6
|
-
* including handling extension UI requests (select, confirm, input, editor).
|
|
6
|
+
* including handling extension UI requests (select, confirm, input, editor, ask).
|
|
7
7
|
*
|
|
8
8
|
* Usage: npx tsx examples/rpc-extension-ui.ts
|
|
9
9
|
*
|
|
10
10
|
* Slash commands:
|
|
11
|
-
* /select
|
|
12
|
-
* /confirm
|
|
13
|
-
* /input
|
|
14
|
-
* /editor
|
|
11
|
+
* /select - demo select dialog
|
|
12
|
+
* /confirm - demo confirm dialog
|
|
13
|
+
* /input - demo input dialog
|
|
14
|
+
* /editor - demo editor dialog
|
|
15
|
+
* /rpc-ask - demo batch ask wizard from the companion extension
|
|
15
16
|
*/
|
|
16
17
|
|
|
17
18
|
import { spawn } from "node:child_process";
|
|
@@ -39,12 +40,28 @@ const RESET = "\x1b[0m";
|
|
|
39
40
|
// Extension UI request type (subset of rpc-types.ts)
|
|
40
41
|
// ============================================================================
|
|
41
42
|
|
|
43
|
+
interface AskQuestion {
|
|
44
|
+
question: string;
|
|
45
|
+
title?: string;
|
|
46
|
+
options?: string[];
|
|
47
|
+
allowFreeText?: boolean;
|
|
48
|
+
multiSelect?: boolean;
|
|
49
|
+
multiline?: boolean;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface AskAnswer {
|
|
53
|
+
selected: string[];
|
|
54
|
+
customText?: string;
|
|
55
|
+
skipped?: boolean;
|
|
56
|
+
}
|
|
57
|
+
|
|
42
58
|
interface ExtensionUIRequest {
|
|
43
59
|
type: "extension_ui_request";
|
|
44
60
|
id: string;
|
|
45
61
|
method: string;
|
|
46
62
|
title?: string;
|
|
47
63
|
options?: string[];
|
|
64
|
+
questions?: AskQuestion[];
|
|
48
65
|
message?: string;
|
|
49
66
|
placeholder?: string;
|
|
50
67
|
prefill?: string;
|
|
@@ -239,6 +256,98 @@ class InputDialog implements Component {
|
|
|
239
256
|
}
|
|
240
257
|
}
|
|
241
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Compact batch-question wizard for the RPC example. Each page accepts option
|
|
261
|
+
* numbers and optional free text separated by commas, then sends one ordered
|
|
262
|
+
* answers[] response after the final question.
|
|
263
|
+
*/
|
|
264
|
+
class AskDialog implements Component {
|
|
265
|
+
private readonly input = new Input();
|
|
266
|
+
private readonly answers: AskAnswer[] = [];
|
|
267
|
+
private questionIndex = 0;
|
|
268
|
+
onSubmit?: (answers: AskAnswer[]) => void;
|
|
269
|
+
onCancel?: () => void;
|
|
270
|
+
onCtrlD?: () => void;
|
|
271
|
+
|
|
272
|
+
constructor(
|
|
273
|
+
private readonly title: string,
|
|
274
|
+
private readonly questions: AskQuestion[],
|
|
275
|
+
) {
|
|
276
|
+
this.input.onSubmit = (value) => this.acceptAnswer(value);
|
|
277
|
+
this.input.onEscape = () => this.onCancel?.();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
private acceptAnswer(value: string): void {
|
|
281
|
+
const question = this.questions[this.questionIndex];
|
|
282
|
+
if (!question) return;
|
|
283
|
+
const raw = value.trim();
|
|
284
|
+
const options = question.options ?? [];
|
|
285
|
+
const selected: string[] = [];
|
|
286
|
+
const customParts: string[] = [];
|
|
287
|
+
|
|
288
|
+
if (options.length === 0) {
|
|
289
|
+
if (raw) customParts.push(raw);
|
|
290
|
+
} else if (raw) {
|
|
291
|
+
for (const token of raw
|
|
292
|
+
.split(",")
|
|
293
|
+
.map((part) => part.trim())
|
|
294
|
+
.filter(Boolean)) {
|
|
295
|
+
const numericIndex = /^\d+$/.test(token) ? Number(token) - 1 : -1;
|
|
296
|
+
const matched =
|
|
297
|
+
options[numericIndex] ?? options.find((option) => option.toLowerCase() === token.toLowerCase());
|
|
298
|
+
if (matched && (question.multiSelect || selected.length === 0)) {
|
|
299
|
+
if (!selected.includes(matched)) selected.push(matched);
|
|
300
|
+
} else if (!matched && question.allowFreeText !== false) {
|
|
301
|
+
customParts.push(token);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const customText = customParts.join(", ").trim() || undefined;
|
|
307
|
+
this.answers.push(selected.length > 0 || customText ? { selected, customText } : { selected: [], skipped: true });
|
|
308
|
+
this.questionIndex++;
|
|
309
|
+
if (this.questionIndex >= this.questions.length) {
|
|
310
|
+
this.onSubmit?.(this.answers);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
this.input.setValue("");
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
handleInput(data: string): void {
|
|
317
|
+
if (matchesKey(data, "ctrl+d")) {
|
|
318
|
+
this.onCtrlD?.();
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
this.input.handleInput(data);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
invalidate(): void {
|
|
325
|
+
this.input.invalidate();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
render(width: number): string[] {
|
|
329
|
+
const question = this.questions[this.questionIndex];
|
|
330
|
+
if (!question) return [`${RED}Invalid empty ask request${RESET}`];
|
|
331
|
+
const options = question.options ?? [];
|
|
332
|
+
const optionLines = options.map((option, index) => ` ${index + 1}. ${option}`);
|
|
333
|
+
const answerHint =
|
|
334
|
+
options.length === 0
|
|
335
|
+
? "Type an answer"
|
|
336
|
+
: question.multiSelect
|
|
337
|
+
? "Type option numbers separated by commas"
|
|
338
|
+
: "Type one option number";
|
|
339
|
+
const customHint = options.length > 0 && question.allowFreeText !== false ? ", plus optional text" : "";
|
|
340
|
+
return [
|
|
341
|
+
`${MAGENTA}${BOLD}${this.title} — ${this.questionIndex + 1}/${this.questions.length}${RESET}`,
|
|
342
|
+
`${BOLD}${question.title ?? question.question}${RESET}`,
|
|
343
|
+
...(question.title ? [question.question] : []),
|
|
344
|
+
...optionLines,
|
|
345
|
+
...this.input.render(width),
|
|
346
|
+
`${DIM}${answerHint}${customHint}. Enter to continue, Esc to cancel.${RESET}`,
|
|
347
|
+
];
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
242
351
|
// ============================================================================
|
|
243
352
|
// Main
|
|
244
353
|
// ============================================================================
|
|
@@ -300,6 +409,7 @@ async function main() {
|
|
|
300
409
|
// These helpers swap between them.
|
|
301
410
|
|
|
302
411
|
let activeDialog: Component | null = null;
|
|
412
|
+
let activeDialogId: string | null = null;
|
|
303
413
|
|
|
304
414
|
function setBottomComponent(component: Component): void {
|
|
305
415
|
root.clear();
|
|
@@ -312,6 +422,7 @@ async function main() {
|
|
|
312
422
|
|
|
313
423
|
function showPrompt(): void {
|
|
314
424
|
activeDialog = null;
|
|
425
|
+
activeDialogId = null;
|
|
315
426
|
setBottomComponent(promptInput);
|
|
316
427
|
tui.setFocus(promptInput.input);
|
|
317
428
|
}
|
|
@@ -374,8 +485,27 @@ async function main() {
|
|
|
374
485
|
tui.setFocus(dialog.inputComponent);
|
|
375
486
|
}
|
|
376
487
|
|
|
488
|
+
function showAskDialog(
|
|
489
|
+
title: string,
|
|
490
|
+
questions: AskQuestion[],
|
|
491
|
+
onDone: (answers: AskAnswer[] | undefined) => void,
|
|
492
|
+
): void {
|
|
493
|
+
const dialog = new AskDialog(title, questions);
|
|
494
|
+
dialog.onSubmit = (answers) => {
|
|
495
|
+
showPrompt();
|
|
496
|
+
onDone(answers);
|
|
497
|
+
};
|
|
498
|
+
dialog.onCancel = () => {
|
|
499
|
+
showPrompt();
|
|
500
|
+
onDone(undefined);
|
|
501
|
+
};
|
|
502
|
+
dialog.onCtrlD = exit;
|
|
503
|
+
showDialog(dialog);
|
|
504
|
+
}
|
|
505
|
+
|
|
377
506
|
function handleExtensionUI(req: ExtensionUIRequest): void {
|
|
378
507
|
const { id, method } = req;
|
|
508
|
+
if (["select", "confirm", "input", "editor", "ask"].includes(method)) activeDialogId = id;
|
|
379
509
|
|
|
380
510
|
switch (method) {
|
|
381
511
|
// Dialog methods: replace prompt with interactive component
|
|
@@ -422,6 +552,23 @@ async function main() {
|
|
|
422
552
|
break;
|
|
423
553
|
}
|
|
424
554
|
|
|
555
|
+
case "ask": {
|
|
556
|
+
const questions = req.questions ?? [];
|
|
557
|
+
if (questions.length === 0) {
|
|
558
|
+
showPrompt();
|
|
559
|
+
send({ type: "extension_ui_response", id, cancelled: true });
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
showAskDialog(req.title ?? "Question", questions, (answers) => {
|
|
563
|
+
if (answers) {
|
|
564
|
+
send({ type: "extension_ui_response", id, answers });
|
|
565
|
+
} else {
|
|
566
|
+
send({ type: "extension_ui_response", id, cancelled: true });
|
|
567
|
+
}
|
|
568
|
+
});
|
|
569
|
+
break;
|
|
570
|
+
}
|
|
571
|
+
|
|
425
572
|
// Fire-and-forget methods: display as notification
|
|
426
573
|
case "notify": {
|
|
427
574
|
const notifyType = (req.notifyType as string) ?? "info";
|
|
@@ -450,6 +597,10 @@ async function main() {
|
|
|
450
597
|
break;
|
|
451
598
|
}
|
|
452
599
|
|
|
600
|
+
case "setTitle":
|
|
601
|
+
terminal.setTitle(req.title ?? "");
|
|
602
|
+
break;
|
|
603
|
+
|
|
453
604
|
case "set_editor_text":
|
|
454
605
|
promptInput.input.setValue((req.text as string) ?? "");
|
|
455
606
|
tui.requestRender();
|
|
@@ -535,6 +686,11 @@ async function main() {
|
|
|
535
686
|
return;
|
|
536
687
|
}
|
|
537
688
|
|
|
689
|
+
if (data.type === "extension_ui_response_handled") {
|
|
690
|
+
if (data.id === activeDialogId) showPrompt();
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
|
|
538
694
|
if (data.type === "message_update") {
|
|
539
695
|
const evt = data.assistantMessageEvent as Record<string, unknown> | undefined;
|
|
540
696
|
if (evt?.type === "text_delta") {
|
|
@@ -620,7 +776,7 @@ async function main() {
|
|
|
620
776
|
|
|
621
777
|
outputLog.append(`${BOLD}RPC Chat${RESET}`);
|
|
622
778
|
outputLog.append(`${DIM}Type a message and press Enter. Esc to abort or exit. Ctrl+D to quit.${RESET}`);
|
|
623
|
-
outputLog.append(`${DIM}Slash commands: /select /confirm /input /editor${RESET}`);
|
|
779
|
+
outputLog.append(`${DIM}Slash commands: /select /confirm /input /editor /rpc-ask${RESET}`);
|
|
624
780
|
outputLog.append("");
|
|
625
781
|
|
|
626
782
|
tui.start();
|
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@ This skill has two modes:
|
|
|
18
18
|
2. **No `#N` in comment bodies** — Use "finding 3", "item 3" etc. instead.
|
|
19
19
|
3. **Safe git** — Never use `git add -A` or `git add .`. Stage files by name. Never stage secrets.
|
|
20
20
|
4. **Task tracking** — Use the `tasks_update` tool to show progress.
|
|
21
|
-
5. **Non-interactive `gh`** — Set `GH_PAGER=cat` and `GH_EDITOR=cat` before all `gh` commands to prevent interactive prompts from hanging the agent. Use `--body-file` instead of inline `--body` for all `gh pr comment`, `gh pr create`, and `gh issue create` calls to avoid shell interpretation of backticks. Write each body to a **unique per-invocation temp file** via `mktemp` (e.g. `GH_BODY="$(mktemp /tmp/gh-comment
|
|
21
|
+
5. **Non-interactive `gh`** — Set `GH_PAGER=cat` and `GH_EDITOR=cat` before all `gh` commands to prevent interactive prompts from hanging the agent. Use `--body-file` instead of inline `--body` for all `gh pr comment`, `gh pr create`, and `gh issue create` calls to avoid shell interpretation of backticks. Write each body to a **unique per-invocation temp file** via `mktemp` (e.g. `GH_BODY="$(mktemp /tmp/gh-comment.$$.XXXXXXXX)"`) — never a fixed path like `/tmp/gh-comment.md`, which concurrent mach6 sessions on the same machine would clobber, cross-posting one session's body to another's PR/issue.
|
|
22
22
|
|
|
23
23
|
## Parent ownership and the formal-review checkpoint
|
|
24
24
|
|
|
@@ -16,7 +16,7 @@ argument-hint: "[issue-number | description]"
|
|
|
16
16
|
4. **Safe git** — Never use `git add -A` or `git add .`. Stage files by name. Never stage secrets (.env, credentials, tokens, keys).
|
|
17
17
|
5. **Task tracking** — Use the `tasks_update` tool to show progress through multi-step commands.
|
|
18
18
|
6. **Project conventions** — Check for CLAUDE.md, AGENTS.md, .dreb/CONTEXT.md, and CONTRIBUTING.md before planning or implementing.
|
|
19
|
-
7. **Non-interactive `gh`** — Set `GH_PAGER=cat` and `GH_EDITOR=cat` before all `gh` commands to prevent interactive prompts from hanging the agent. Use `--body-file` instead of inline `--body` for all `gh pr comment`, `gh pr create`, and `gh issue create` calls to avoid shell interpretation of backticks. Write each body to a **unique per-invocation temp file** via `mktemp` (e.g. `GH_BODY="$(mktemp /tmp/gh-comment
|
|
19
|
+
7. **Non-interactive `gh`** — Set `GH_PAGER=cat` and `GH_EDITOR=cat` before all `gh` commands to prevent interactive prompts from hanging the agent. Use `--body-file` instead of inline `--body` for all `gh pr comment`, `gh pr create`, and `gh issue create` calls to avoid shell interpretation of backticks. Write each body to a **unique per-invocation temp file** via `mktemp` (e.g. `GH_BODY="$(mktemp /tmp/gh-comment.$$.XXXXXXXX)"`) — never a fixed path like `/tmp/gh-comment.md`, which concurrent mach6 sessions on the same machine would clobber, cross-posting one session's body to another's PR/issue.
|
|
20
20
|
|
|
21
21
|
## Determine Mode
|
|
22
22
|
|
|
@@ -76,7 +76,7 @@ Present to the user:
|
|
|
76
76
|
Post as an issue comment:
|
|
77
77
|
|
|
78
78
|
```bash
|
|
79
|
-
GH_BODY="$(mktemp /tmp/gh-comment
|
|
79
|
+
GH_BODY="$(mktemp /tmp/gh-comment.$$.XXXXXXXX)"
|
|
80
80
|
cat > "$GH_BODY" << 'MACH6_EOF'
|
|
81
81
|
<!-- mach6-assessment -->
|
|
82
82
|
## Issue Assessment
|
|
@@ -128,7 +128,7 @@ Present the draft to the user for approval.
|
|
|
128
128
|
### Step 3: Create the issue
|
|
129
129
|
|
|
130
130
|
```bash
|
|
131
|
-
GH_BODY="$(mktemp /tmp/gh-body
|
|
131
|
+
GH_BODY="$(mktemp /tmp/gh-body.$$.XXXXXXXX)"
|
|
132
132
|
cat > "$GH_BODY" << 'MACH6_EOF'
|
|
133
133
|
<body>
|
|
134
134
|
MACH6_EOF
|
|
@@ -18,7 +18,7 @@ This command is strictly for **planning**. Do NOT implement any code changes —
|
|
|
18
18
|
4. **Safe git** — Never use `git add -A` or `git add .`. Stage files by name. Never stage secrets.
|
|
19
19
|
5. **Task tracking** — Use the `tasks_update` tool to show progress through multi-step commands.
|
|
20
20
|
6. **Project conventions** — Check for CLAUDE.md, AGENTS.md, .dreb/CONTEXT.md, and CONTRIBUTING.md before planning.
|
|
21
|
-
7. **Non-interactive `gh`** — Set `GH_PAGER=cat` and `GH_EDITOR=cat` before all `gh` commands to prevent interactive prompts from hanging the agent. Use `--body-file` instead of inline `--body` for all `gh pr comment`, `gh pr create`, and `gh issue create` calls to avoid shell interpretation of backticks. Write each body to a **unique per-invocation temp file** via `mktemp` (e.g. `GH_BODY="$(mktemp /tmp/gh-comment
|
|
21
|
+
7. **Non-interactive `gh`** — Set `GH_PAGER=cat` and `GH_EDITOR=cat` before all `gh` commands to prevent interactive prompts from hanging the agent. Use `--body-file` instead of inline `--body` for all `gh pr comment`, `gh pr create`, and `gh issue create` calls to avoid shell interpretation of backticks. Write each body to a **unique per-invocation temp file** via `mktemp` (e.g. `GH_BODY="$(mktemp /tmp/gh-comment.$$.XXXXXXXX)"`) — never a fixed path like `/tmp/gh-comment.md`, which concurrent mach6 sessions on the same machine would clobber, cross-posting one session's body to another's PR/issue.
|
|
22
22
|
|
|
23
23
|
## Step 1: Set up task tracking
|
|
24
24
|
|
|
@@ -98,7 +98,7 @@ git commit --allow-empty -m "chore: open PR for issue <N>"
|
|
|
98
98
|
git push -u origin feature/issue-<N>-<slug>
|
|
99
99
|
|
|
100
100
|
# Open draft PR
|
|
101
|
-
GH_BODY="$(mktemp /tmp/gh-body
|
|
101
|
+
GH_BODY="$(mktemp /tmp/gh-body.$$.XXXXXXXX)"
|
|
102
102
|
cat > "$GH_BODY" << 'MACH6_EOF'
|
|
103
103
|
Closes #<N>
|
|
104
104
|
|
|
@@ -114,7 +114,7 @@ Update task: branch → completed, post → in_progress.
|
|
|
114
114
|
## Step 7: Post plan to PR
|
|
115
115
|
|
|
116
116
|
```bash
|
|
117
|
-
GH_BODY="$(mktemp /tmp/gh-comment
|
|
117
|
+
GH_BODY="$(mktemp /tmp/gh-comment.$$.XXXXXXXX)"
|
|
118
118
|
cat > "$GH_BODY" << 'MACH6_EOF'
|
|
119
119
|
<!-- mach6-plan -->
|
|
120
120
|
## Implementation Plan
|
|
@@ -14,7 +14,7 @@ argument-hint: "<pr-number>"
|
|
|
14
14
|
2. **No `#N` in comment bodies** — Use "finding 3", "item 3" etc. instead.
|
|
15
15
|
3. **Safe git** — Never use `git add -A` or `git add .`. Stage files by name. Never stage secrets.
|
|
16
16
|
4. **Task tracking** — Use the `tasks_update` tool to show progress.
|
|
17
|
-
5. **Non-interactive `gh`** — Set `GH_PAGER=cat` and `GH_EDITOR=cat` before all `gh` commands to prevent interactive prompts from hanging the agent. Use `--body-file` instead of inline `--body` for all `gh pr comment`, `gh pr create`, and `gh issue create` calls to avoid shell interpretation of backticks. Write each body to a **unique per-invocation temp file** via `mktemp` (e.g. `GH_BODY="$(mktemp /tmp/gh-comment
|
|
17
|
+
5. **Non-interactive `gh`** — Set `GH_PAGER=cat` and `GH_EDITOR=cat` before all `gh` commands to prevent interactive prompts from hanging the agent. Use `--body-file` instead of inline `--body` for all `gh pr comment`, `gh pr create`, and `gh issue create` calls to avoid shell interpretation of backticks. Write each body to a **unique per-invocation temp file** via `mktemp` (e.g. `GH_BODY="$(mktemp /tmp/gh-comment.$$.XXXXXXXX)"`) — never a fixed path like `/tmp/gh-comment.md`, which concurrent mach6 sessions on the same machine would clobber, cross-posting one session's body to another's PR/issue.
|
|
18
18
|
|
|
19
19
|
## Step 1: Set up task tracking
|
|
20
20
|
|
|
@@ -180,7 +180,7 @@ git push --tags
|
|
|
180
180
|
|
|
181
181
|
3. Present draft to user for approval, then create:
|
|
182
182
|
```bash
|
|
183
|
-
GH_NOTES="$(mktemp /tmp/gh-release-notes
|
|
183
|
+
GH_NOTES="$(mktemp /tmp/gh-release-notes.$$.XXXXXXXX)"
|
|
184
184
|
cat > "$GH_NOTES" << 'MACH6_EOF'
|
|
185
185
|
<release-notes>
|
|
186
186
|
MACH6_EOF
|
|
@@ -15,7 +15,7 @@ argument-hint: "[commit message]"
|
|
|
15
15
|
3. **No `#N` in comment bodies** — Use "finding 3", "item 3", "stage 2" etc. instead.
|
|
16
16
|
4. **Safe git** — Never use `git add -A` or `git add .`. Stage files by name. Never stage secrets (.env, credentials, tokens, keys).
|
|
17
17
|
5. **Task tracking** — Use the `tasks_update` tool to show progress.
|
|
18
|
-
6. **Non-interactive `gh`** — Set `GH_PAGER=cat` and `GH_EDITOR=cat` before all `gh` commands to prevent interactive prompts from hanging the agent. Use `--body-file` instead of inline `--body` for all `gh pr comment`, `gh pr create`, and `gh issue create` calls to avoid shell interpretation of backticks. Write each body to a **unique per-invocation temp file** via `mktemp` (e.g. `GH_BODY="$(mktemp /tmp/gh-comment
|
|
18
|
+
6. **Non-interactive `gh`** — Set `GH_PAGER=cat` and `GH_EDITOR=cat` before all `gh` commands to prevent interactive prompts from hanging the agent. Use `--body-file` instead of inline `--body` for all `gh pr comment`, `gh pr create`, and `gh issue create` calls to avoid shell interpretation of backticks. Write each body to a **unique per-invocation temp file** via `mktemp` (e.g. `GH_BODY="$(mktemp /tmp/gh-comment.$$.XXXXXXXX)"`) — never a fixed path like `/tmp/gh-comment.md`, which concurrent mach6 sessions on the same machine would clobber, cross-posting one session's body to another's PR/issue.
|
|
19
19
|
7. **Stop after durable progress** — The commit, push, and GitHub progress comment are the accountability and recovery boundary. Do not invoke `mach6-review` or continue into a formal review cycle. Only the user may start formal review; offer it with `suggest_next` and stop.
|
|
20
20
|
|
|
21
21
|
## Step 1: Set up task tracking
|
|
@@ -82,7 +82,7 @@ If session context points to an issue but a PR also exists on the current branch
|
|
|
82
82
|
|
|
83
83
|
Post a progress comment:
|
|
84
84
|
```bash
|
|
85
|
-
GH_BODY="$(mktemp /tmp/gh-comment
|
|
85
|
+
GH_BODY="$(mktemp /tmp/gh-comment.$$.XXXXXXXX)"
|
|
86
86
|
cat > "$GH_BODY" << 'MACH6_EOF'
|
|
87
87
|
<!-- mach6-progress -->
|
|
88
88
|
## Progress Update
|
|
@@ -14,7 +14,7 @@ argument-hint: "<pr-number> [code|errors|tests|completeness|simplify]"
|
|
|
14
14
|
2. **HTML markers** — Use `<!-- mach6-review -->` and `<!-- mach6-assessment -->` as the first line of comment bodies.
|
|
15
15
|
3. **No `#N` in comment bodies** — Use "finding 3", "item 3", "stage 2" etc. instead.
|
|
16
16
|
4. **Task tracking** — Use the `tasks_update` tool to show progress.
|
|
17
|
-
5. **Non-interactive `gh`** — Set `GH_PAGER=cat` and `GH_EDITOR=cat` before all `gh` commands to prevent interactive prompts from hanging the agent. Use `--body-file` instead of inline `--body` for all `gh pr comment`, `gh pr create`, and `gh issue create` calls to avoid shell interpretation of backticks. Write each body to a **unique per-invocation temp file** via `mktemp` (e.g. `GH_BODY="$(mktemp /tmp/gh-comment
|
|
17
|
+
5. **Non-interactive `gh`** — Set `GH_PAGER=cat` and `GH_EDITOR=cat` before all `gh` commands to prevent interactive prompts from hanging the agent. Use `--body-file` instead of inline `--body` for all `gh pr comment`, `gh pr create`, and `gh issue create` calls to avoid shell interpretation of backticks. Write each body to a **unique per-invocation temp file** via `mktemp` (e.g. `GH_BODY="$(mktemp /tmp/gh-comment.$$.XXXXXXXX)"`) — never a fixed path like `/tmp/gh-comment.md`, which concurrent mach6 sessions on the same machine would clobber, cross-posting one session's body to another's PR/issue.
|
|
18
18
|
6. **User-controlled checkpoint** — This formal multi-agent review runs only from an explicit user request, either through its slash command or a direct instruction to an agent to invoke it. An agent may invoke it in response to that request; otherwise agents must only offer it with `suggest_next`, never invoke it autonomously or start a review-fix-review loop.
|
|
19
19
|
7. **Review durable work only** — Do not launch formal review agents against uncommitted or unpushed work. The commit, push, and GitHub progress comment are the accountability and recovery boundary.
|
|
20
20
|
|
|
@@ -126,7 +126,7 @@ Update task: review → completed, post-review → in_progress.
|
|
|
126
126
|
Compile all findings from all agents into a single structured comment:
|
|
127
127
|
|
|
128
128
|
```bash
|
|
129
|
-
GH_BODY="$(mktemp /tmp/gh-comment
|
|
129
|
+
GH_BODY="$(mktemp /tmp/gh-comment.$$.XXXXXXXX)"
|
|
130
130
|
cat > "$GH_BODY" << 'MACH6_EOF'
|
|
131
131
|
<!-- mach6-review -->
|
|
132
132
|
## Code Review
|
|
@@ -195,7 +195,7 @@ Update task: assess → completed, post-assess → in_progress.
|
|
|
195
195
|
## Step 7: Post assessment
|
|
196
196
|
|
|
197
197
|
```bash
|
|
198
|
-
GH_BODY="$(mktemp /tmp/gh-comment
|
|
198
|
+
GH_BODY="$(mktemp /tmp/gh-comment.$$.XXXXXXXX)"
|
|
199
199
|
cat > "$GH_BODY" << 'MACH6_EOF'
|
|
200
200
|
<!-- mach6-assessment -->
|
|
201
201
|
## Review Assessment
|
|
@@ -229,7 +229,7 @@ Present to the user:
|
|
|
229
229
|
|
|
230
230
|
If any findings were classified as **deferred**, ask the user if they want to create issues for them:
|
|
231
231
|
```bash
|
|
232
|
-
GH_BODY="$(mktemp /tmp/gh-body
|
|
232
|
+
GH_BODY="$(mktemp /tmp/gh-body.$$.XXXXXXXX)"
|
|
233
233
|
cat > "$GH_BODY" << 'MACH6_EOF'
|
|
234
234
|
<body referencing PR and finding>
|
|
235
235
|
MACH6_EOF
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ask_user question component.
|
|
3
|
-
*
|
|
4
|
-
* Renders a clarifying question with optional single- or multi-select options
|
|
5
|
-
* and an optional free-text field, matching the approved cross-surface design.
|
|
6
|
-
*
|
|
7
|
-
* Keyboard model (settled in issue 396 discussion):
|
|
8
|
-
* ↑/↓ move a single cursor through the options and, last, the free-text field
|
|
9
|
-
* Space toggle the highlighted checkbox (multi-select only)
|
|
10
|
-
* Enter single-select: pick the highlighted option and submit;
|
|
11
|
-
* free-text field: submit the typed answer;
|
|
12
|
-
* multi-select: submit all checked options plus any free text
|
|
13
|
-
* Shift+Enter insert a newline in the multiline free-text field
|
|
14
|
-
* Esc stop the current agent turn
|
|
15
|
-
*/
|
|
16
|
-
import { Container, type Focusable, type TUI } from "@dreb/tui";
|
|
17
|
-
import type { AskRequest, AskResult } from "../../../core/extensions/types.js";
|
|
18
|
-
export interface AskUserComponentOptions {
|
|
19
|
-
tui?: TUI;
|
|
20
|
-
timeout?: number;
|
|
21
|
-
}
|
|
22
|
-
export declare class AskUserComponent extends Container implements Focusable {
|
|
23
|
-
private options;
|
|
24
|
-
private allowFreeText;
|
|
25
|
-
private multiSelect;
|
|
26
|
-
private multiline;
|
|
27
|
-
/** Cursor over [options..., freeTextRow?]. */
|
|
28
|
-
private cursorIndex;
|
|
29
|
-
/** Checkbox state for multi-select, aligned to options. */
|
|
30
|
-
private checked;
|
|
31
|
-
private onSubmitCallback;
|
|
32
|
-
private onStopCallback;
|
|
33
|
-
private titleText;
|
|
34
|
-
private baseTitle;
|
|
35
|
-
private optionsContainer;
|
|
36
|
-
private fieldLabel;
|
|
37
|
-
private input;
|
|
38
|
-
private editor;
|
|
39
|
-
private countdown;
|
|
40
|
-
private submitted;
|
|
41
|
-
private _focused;
|
|
42
|
-
get focused(): boolean;
|
|
43
|
-
set focused(value: boolean);
|
|
44
|
-
private get freeTextRow();
|
|
45
|
-
private get lastRow();
|
|
46
|
-
private cursorOnField;
|
|
47
|
-
constructor(request: AskRequest, onSubmit: (result: AskResult) => void, onStop: () => void, opts?: AskUserComponentOptions);
|
|
48
|
-
private buildHint;
|
|
49
|
-
private renderRows;
|
|
50
|
-
private syncFieldFocus;
|
|
51
|
-
private moveCursor;
|
|
52
|
-
private fieldText;
|
|
53
|
-
private currentAnswer;
|
|
54
|
-
private submit;
|
|
55
|
-
private stop;
|
|
56
|
-
handleInput(keyData: string): void;
|
|
57
|
-
dispose(): void;
|
|
58
|
-
}
|
|
59
|
-
//# sourceMappingURL=ask-user.d.ts.map
|