@mjasnikovs/pi-task 0.13.8 → 0.13.9
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/dist/remote/bridge.d.ts +22 -0
- package/dist/remote/bridge.js +29 -3
- package/dist/task/auto-orchestrator.js +21 -13
- package/dist/task/phases.js +22 -10
- package/package.json +1 -1
package/dist/remote/bridge.d.ts
CHANGED
|
@@ -28,6 +28,19 @@ export interface AskSpec {
|
|
|
28
28
|
recommended2?: string;
|
|
29
29
|
/** Whether the browser card shows a Skip button (answers with empty string). */
|
|
30
30
|
allowSkip: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* When set, the local TUI shows a select() picker of these entries instead of
|
|
33
|
+
* a bare text input — one option per line, arrow-key navigable. Each entry's
|
|
34
|
+
* `label` is what the picker displays; its `value` is what ask() resolves to
|
|
35
|
+
* when chosen. A built-in "type a different answer" entry is appended that
|
|
36
|
+
* falls back to a text input, preserving the free-text override. Used for the
|
|
37
|
+
* binary A/B grill/clarify fork. Remote browsers ignore this and keep
|
|
38
|
+
* rendering recommended/recommended2 as buttons.
|
|
39
|
+
*/
|
|
40
|
+
options?: {
|
|
41
|
+
label: string;
|
|
42
|
+
value: string;
|
|
43
|
+
}[];
|
|
31
44
|
}
|
|
32
45
|
/** Wraps a live command ctx and fans interactions out to local TUI + browsers. */
|
|
33
46
|
export declare class SessionUI {
|
|
@@ -38,6 +51,15 @@ export declare class SessionUI {
|
|
|
38
51
|
get hasUI(): boolean;
|
|
39
52
|
/** Race the local input against a remote answer; first to settle wins. */
|
|
40
53
|
ask(spec: AskSpec): Promise<string | undefined>;
|
|
54
|
+
/**
|
|
55
|
+
* The local-TUI half of ask(). With `spec.options` it renders a select()
|
|
56
|
+
* picker (each option on its own line) plus a trailing "type a different
|
|
57
|
+
* answer" entry that drops to a text input; the chosen entry's `value` is
|
|
58
|
+
* returned. Without options it falls back to a single text input. Cancelling
|
|
59
|
+
* either dialog (or an abort when the remote wins the race) resolves to
|
|
60
|
+
* undefined.
|
|
61
|
+
*/
|
|
62
|
+
private askLocal;
|
|
41
63
|
}
|
|
42
64
|
export declare function publishNotify(message: string, level: 'info' | 'warning' | 'error'): void;
|
|
43
65
|
export declare function publishViewer(title: string, text: string): void;
|
package/dist/remote/bridge.js
CHANGED
|
@@ -25,6 +25,9 @@ export function answerPrompt(id, value) {
|
|
|
25
25
|
b.pending.delete(id);
|
|
26
26
|
settle(value);
|
|
27
27
|
}
|
|
28
|
+
/** Trailing picker entry that drops to a free-text input — the local mirror of
|
|
29
|
+
* the remote card's "✎ Manual answer" button. */
|
|
30
|
+
const TYPE_OWN_LABEL = 'Type a different answer…';
|
|
28
31
|
/** Wraps a live command ctx and fans interactions out to local TUI + browsers. */
|
|
29
32
|
export class SessionUI {
|
|
30
33
|
ctx;
|
|
@@ -63,9 +66,7 @@ export class SessionUI {
|
|
|
63
66
|
// Local: resolves to a value/undefined, or undefined on abort. Swallow
|
|
64
67
|
// the rejection some implementations throw on abort so it never leaks.
|
|
65
68
|
const local = this.ctx.hasUI ?
|
|
66
|
-
this.
|
|
67
|
-
.input(spec.localTitle, spec.recommended, { signal: ac.signal })
|
|
68
|
-
.catch(() => undefined)
|
|
69
|
+
this.askLocal(spec, ac.signal).catch(() => undefined)
|
|
69
70
|
: new Promise(() => { });
|
|
70
71
|
try {
|
|
71
72
|
const winner = await Promise.race([
|
|
@@ -81,6 +82,31 @@ export class SessionUI {
|
|
|
81
82
|
clearPrompt(id);
|
|
82
83
|
}
|
|
83
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* The local-TUI half of ask(). With `spec.options` it renders a select()
|
|
87
|
+
* picker (each option on its own line) plus a trailing "type a different
|
|
88
|
+
* answer" entry that drops to a text input; the chosen entry's `value` is
|
|
89
|
+
* returned. Without options it falls back to a single text input. Cancelling
|
|
90
|
+
* either dialog (or an abort when the remote wins the race) resolves to
|
|
91
|
+
* undefined.
|
|
92
|
+
*/
|
|
93
|
+
async askLocal(spec, signal) {
|
|
94
|
+
const opts = spec.options;
|
|
95
|
+
if (opts && opts.length > 0) {
|
|
96
|
+
const labels = opts.map(o => o.label);
|
|
97
|
+
const choice = await this.ctx.ui.select(spec.localTitle, [...labels, TYPE_OWN_LABEL], {
|
|
98
|
+
signal
|
|
99
|
+
});
|
|
100
|
+
if (choice === undefined)
|
|
101
|
+
return undefined; // cancelled / aborted
|
|
102
|
+
if (choice === TYPE_OWN_LABEL) {
|
|
103
|
+
return this.ctx.ui.input(spec.localTitle, undefined, { signal });
|
|
104
|
+
}
|
|
105
|
+
const hit = opts.find(o => o.label === choice);
|
|
106
|
+
return hit ? hit.value : choice;
|
|
107
|
+
}
|
|
108
|
+
return this.ctx.ui.input(spec.localTitle, spec.recommended, { signal });
|
|
109
|
+
}
|
|
84
110
|
}
|
|
85
111
|
export function publishNotify(message, level) {
|
|
86
112
|
getBridge().broadcast({ type: 'notify', message, level });
|
|
@@ -80,31 +80,39 @@ export async function planAuto(ctx, cwd, feature, deps) {
|
|
|
80
80
|
const plainQ = stripInlineMarkdown(question);
|
|
81
81
|
const plainSuggested = suggested === undefined ? undefined : stripInlineMarkdown(suggested);
|
|
82
82
|
const plainAlt = alt === undefined ? undefined : stripInlineMarkdown(alt);
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
83
|
+
// Identical to /task's grill dialog: a binary fork becomes a select()
|
|
84
|
+
// picker locally — each option on its own line, labelled A/B; a single
|
|
85
|
+
// recommendation rides under the question as the input default; an open
|
|
86
|
+
// question shows the bare prompt. No verbose "Recommended:" /
|
|
87
|
+
// "press Enter to accept" scaffolding.
|
|
88
|
+
const twoOption = plainSuggested !== undefined && plainAlt !== undefined;
|
|
89
|
+
const title = !twoOption && plainSuggested ?
|
|
90
|
+
`${shownQ}\n${renderInlineMarkdown(suggested, theme)}`
|
|
91
91
|
: shownQ;
|
|
92
92
|
const a = await ui.ask({
|
|
93
93
|
localTitle: title,
|
|
94
94
|
question: plainQ,
|
|
95
95
|
recommended: plainSuggested,
|
|
96
96
|
...(plainAlt !== undefined && { recommended2: plainAlt }),
|
|
97
|
-
allowSkip: plainSuggested === undefined && plainAlt === undefined
|
|
97
|
+
allowSkip: plainSuggested === undefined && plainAlt === undefined,
|
|
98
|
+
...(twoOption && {
|
|
99
|
+
options: [
|
|
100
|
+
{
|
|
101
|
+
label: `A: ${renderInlineMarkdown(suggested, theme)}`,
|
|
102
|
+
value: plainSuggested
|
|
103
|
+
},
|
|
104
|
+
{ label: `B: ${renderInlineMarkdown(alt, theme)}`, value: plainAlt }
|
|
105
|
+
]
|
|
106
|
+
})
|
|
98
107
|
});
|
|
99
108
|
if (a === undefined) {
|
|
100
109
|
ctx.ui.notify('/task-auto cancelled.', 'warning');
|
|
101
110
|
return null;
|
|
102
111
|
}
|
|
103
112
|
const typed = a.trim();
|
|
104
|
-
//
|
|
105
|
-
// remote
|
|
106
|
-
// back to the option's full text. Mirrors phaseGrill
|
|
107
|
-
const twoOption = plainSuggested !== undefined && plainAlt !== undefined;
|
|
113
|
+
// The local picker resolves to the chosen option's full value, but a
|
|
114
|
+
// remote user (or the picker's free-text fallback) may still type a bare
|
|
115
|
+
// "A"/"B" — map those back to the option's full text. Mirrors phaseGrill.
|
|
108
116
|
let answer;
|
|
109
117
|
if (typed.length === 0 && plainSuggested) {
|
|
110
118
|
answer = `${plainSuggested} (accepted recommendation)`;
|
package/dist/task/phases.js
CHANGED
|
@@ -326,10 +326,13 @@ export async function phaseGrill(deps, ctx, widgetState, refined, research) {
|
|
|
326
326
|
else {
|
|
327
327
|
const plainSuggested = auto.suggested === undefined ? undefined : stripInlineMarkdown(auto.suggested);
|
|
328
328
|
const plainAlt = auto.alt === undefined ? undefined : stripInlineMarkdown(auto.alt);
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
329
|
+
// A binary fork (suggested + alt) becomes a select() picker locally —
|
|
330
|
+
// each option on its own line, labelled A/B. A single recommendation
|
|
331
|
+
// rides along under the question as the input default; an open
|
|
332
|
+
// question shows the bare prompt.
|
|
333
|
+
const twoOption = plainSuggested !== undefined && plainAlt !== undefined;
|
|
334
|
+
const localTitle = !twoOption && plainSuggested ?
|
|
335
|
+
`${shownQ}\n${renderInlineMarkdown(auto.suggested, theme)}`
|
|
333
336
|
: shownQ;
|
|
334
337
|
widgetState.lastLine = `awaiting Q${n + 1}`;
|
|
335
338
|
const a = await ui.ask({
|
|
@@ -337,16 +340,25 @@ export async function phaseGrill(deps, ctx, widgetState, refined, research) {
|
|
|
337
340
|
question: plainQ,
|
|
338
341
|
recommended: plainSuggested,
|
|
339
342
|
recommended2: plainAlt,
|
|
340
|
-
allowSkip: plainSuggested === undefined && plainAlt === undefined
|
|
343
|
+
allowSkip: plainSuggested === undefined && plainAlt === undefined,
|
|
344
|
+
...(twoOption && {
|
|
345
|
+
options: [
|
|
346
|
+
{
|
|
347
|
+
label: `A: ${renderInlineMarkdown(auto.suggested, theme)}`,
|
|
348
|
+
value: plainSuggested
|
|
349
|
+
},
|
|
350
|
+
{ label: `B: ${renderInlineMarkdown(auto.alt, theme)}`, value: plainAlt }
|
|
351
|
+
]
|
|
352
|
+
})
|
|
341
353
|
});
|
|
342
354
|
if (a === undefined)
|
|
343
355
|
throw new Error(USER_CANCELLED);
|
|
344
356
|
const typed = a.trim();
|
|
345
|
-
//
|
|
346
|
-
//
|
|
347
|
-
//
|
|
348
|
-
// leaves the next grill-gen call a
|
|
349
|
-
|
|
357
|
+
// The local picker resolves to the chosen option's full value, but a
|
|
358
|
+
// remote user (or the picker's free-text fallback) may still type a
|
|
359
|
+
// bare "A"/"B" — map those back to the option's full text, since
|
|
360
|
+
// storing the literal letter leaves the next grill-gen call a
|
|
361
|
+
// dangling reference it can't decode.
|
|
350
362
|
if (typed.length === 0 && plainSuggested) {
|
|
351
363
|
answer = plainSuggested;
|
|
352
364
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mjasnikovs/pi-task",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.9",
|
|
4
4
|
"description": "Deterministic spec-orchestration for local models, with a bundled real-time remote web view and web/docs/fetch/worker subagent tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|