@adhdev/daemon-core 0.9.82-rc.294 → 0.9.82-rc.295
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/index.js +40 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +40 -10
- package/dist/index.mjs.map +1 -1
- package/dist/providers/spec/cli-adapter.d.ts +9 -0
- package/dist/providers/types/interactive-prompt.d.ts +17 -0
- package/package.json +2 -2
- package/src/providers/spec/cli-adapter.ts +30 -9
- package/src/providers/types/interactive-prompt.ts +36 -7
|
@@ -144,6 +144,15 @@ export declare class SpecCliAdapter implements CliAdapter {
|
|
|
144
144
|
* question to multi-select and re-emit status. Promotion is one-way
|
|
145
145
|
* (false→true only) — once a question is known multi-select we never demote
|
|
146
146
|
* it, since the glyph column can scroll out of view on later frames.
|
|
147
|
+
*
|
|
148
|
+
* For MULTI-question prompts the per-page Tab capture is the actual source
|
|
149
|
+
* of the bug: pages 2..N are snapshotted ~120ms after the Tab keypress,
|
|
150
|
+
* before their option-row glyph column has redrawn, so those questions
|
|
151
|
+
* freeze as single-select while page 1 (already settled) is correct. We
|
|
152
|
+
* cannot upgrade blindly — the live snapshot shows only ONE focused page —
|
|
153
|
+
* but we CAN read that page's question text/header and upgrade the matching
|
|
154
|
+
* question. As the user navigates the picker (or it settles), each page is
|
|
155
|
+
* eventually re-read and repaired.
|
|
147
156
|
*/
|
|
148
157
|
private maybeUpgradeClaudeTuiMultiSelect;
|
|
149
158
|
private readClaudeTuiHeaders;
|
|
@@ -52,6 +52,23 @@ export interface ClaudeInteractiveTuiPage {
|
|
|
52
52
|
* as a secondary signal for layouts that render markers differently.
|
|
53
53
|
*/
|
|
54
54
|
export declare function detectClaudeTuiMultiSelect(screenText: string): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Read the question the live claude TUI picker is CURRENTLY focused on, plus
|
|
57
|
+
* whether that focused page renders multi-select checkbox markers.
|
|
58
|
+
*
|
|
59
|
+
* Used to repair a multi-question prompt after the fact: when the daemon
|
|
60
|
+
* Tab-captures pages 2..N it snapshots ~120ms after the Tab keypress, before
|
|
61
|
+
* the newly-focused page's option-row checkbox column has redrawn — so those
|
|
62
|
+
* questions get frozen as multiSelect:false even though the picker is
|
|
63
|
+
* multi-select. Re-reading the focused page on a later status tick (once it has
|
|
64
|
+
* settled) lets us attribute the now-visible glyphs to the matching question
|
|
65
|
+
* and upgrade just that one. Returns null when no picker question is on screen.
|
|
66
|
+
*/
|
|
67
|
+
export declare function readFocusedClaudeTuiQuestion(screenText: string): {
|
|
68
|
+
question: string;
|
|
69
|
+
header?: string;
|
|
70
|
+
multiSelect: boolean;
|
|
71
|
+
} | null;
|
|
55
72
|
export declare function detectClaudeAskUserQuestionPromptFromTuiPages(pages: ClaudeInteractiveTuiPage[], options: {
|
|
56
73
|
promptId: string;
|
|
57
74
|
providerType?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhdev/daemon-core",
|
|
3
|
-
"version": "0.9.82-rc.
|
|
3
|
+
"version": "0.9.82-rc.295",
|
|
4
4
|
"description": "ADHDev daemon core — CDP, IDE detection, providers, command execution",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"author": "vilmire",
|
|
47
47
|
"license": "AGPL-3.0-or-later",
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@adhdev/mesh-shared": "0.9.82-rc.
|
|
49
|
+
"@adhdev/mesh-shared": "0.9.82-rc.295",
|
|
50
50
|
"@adhdev/session-host-core": "*",
|
|
51
51
|
"@agentclientprotocol/sdk": "^0.16.1",
|
|
52
52
|
"ajv": "^8.20.0",
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
detectClaudeAskUserQuestionPromptFromJson,
|
|
33
33
|
detectClaudeAskUserQuestionPromptFromTuiPages,
|
|
34
34
|
detectClaudeTuiMultiSelect,
|
|
35
|
+
readFocusedClaudeTuiQuestion,
|
|
35
36
|
type ClaudeInteractiveTuiPage,
|
|
36
37
|
type InteractivePrompt,
|
|
37
38
|
type InteractivePromptResponse,
|
|
@@ -629,19 +630,22 @@ export class SpecCliAdapter implements CliAdapter {
|
|
|
629
630
|
* question to multi-select and re-emit status. Promotion is one-way
|
|
630
631
|
* (false→true only) — once a question is known multi-select we never demote
|
|
631
632
|
* it, since the glyph column can scroll out of view on later frames.
|
|
633
|
+
*
|
|
634
|
+
* For MULTI-question prompts the per-page Tab capture is the actual source
|
|
635
|
+
* of the bug: pages 2..N are snapshotted ~120ms after the Tab keypress,
|
|
636
|
+
* before their option-row glyph column has redrawn, so those questions
|
|
637
|
+
* freeze as single-select while page 1 (already settled) is correct. We
|
|
638
|
+
* cannot upgrade blindly — the live snapshot shows only ONE focused page —
|
|
639
|
+
* but we CAN read that page's question text/header and upgrade the matching
|
|
640
|
+
* question. As the user navigates the picker (or it settles), each page is
|
|
641
|
+
* eventually re-read and repaired.
|
|
632
642
|
*/
|
|
633
643
|
private maybeUpgradeClaudeTuiMultiSelect(): void {
|
|
634
644
|
if (this.cliType !== 'claude-cli'
|
|
635
645
|
|| this.interactivePromptTransport !== 'tui'
|
|
636
646
|
|| !this.activeInteractivePrompt) return;
|
|
637
647
|
const questions = this.activeInteractivePrompt.questions;
|
|
638
|
-
|
|
639
|
-
// we can only attribute the glyphs to a specific question when there is
|
|
640
|
-
// exactly one. Multi-question prompts are tab-captured per page at
|
|
641
|
-
// capture time and aren't re-evaluated here to avoid cross-contaminating
|
|
642
|
-
// a mixed single/multi prompt.
|
|
643
|
-
if (questions.length !== 1) return;
|
|
644
|
-
if (questions[0].multiSelect) return;
|
|
648
|
+
if (questions.every(q => q.multiSelect)) return;
|
|
645
649
|
let screenText = '';
|
|
646
650
|
try {
|
|
647
651
|
screenText = this.driver.snapshot();
|
|
@@ -649,8 +653,25 @@ export class SpecCliAdapter implements CliAdapter {
|
|
|
649
653
|
return;
|
|
650
654
|
}
|
|
651
655
|
if (!screenText.includes('Enter to select')) return;
|
|
652
|
-
|
|
653
|
-
questions
|
|
656
|
+
|
|
657
|
+
if (questions.length === 1) {
|
|
658
|
+
if (questions[0].multiSelect) return;
|
|
659
|
+
if (!detectClaudeTuiMultiSelect(screenText)) return;
|
|
660
|
+
questions[0].multiSelect = true;
|
|
661
|
+
this.statusCallback?.();
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Multi-question: attribute the focused page's glyphs to its question by
|
|
666
|
+
// matching header (preferred) or question text, then upgrade just that
|
|
667
|
+
// one. Never demote — a settled non-multi page is left as captured.
|
|
668
|
+
const focused = readFocusedClaudeTuiQuestion(screenText);
|
|
669
|
+
if (!focused || !focused.multiSelect) return;
|
|
670
|
+
const match = questions.find(q =>
|
|
671
|
+
(focused.header && q.header && q.header === focused.header)
|
|
672
|
+
|| q.question === focused.question);
|
|
673
|
+
if (!match || match.multiSelect) return;
|
|
674
|
+
match.multiSelect = true;
|
|
654
675
|
this.statusCallback?.();
|
|
655
676
|
}
|
|
656
677
|
|
|
@@ -184,15 +184,19 @@ export function detectClaudeTuiMultiSelect(screenText: string): boolean {
|
|
|
184
184
|
if (/Space to (?:select|toggle)|toggle selection|select multiple|select all that apply/i.test(screenText)) {
|
|
185
185
|
return true;
|
|
186
186
|
}
|
|
187
|
-
// A checkbox glyph
|
|
188
|
-
// the
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
//
|
|
192
|
-
|
|
187
|
+
// A checkbox glyph on a NUMBERED option row only appears in the multi-select
|
|
188
|
+
// picker. The glyph sits EITHER before the number ("❯ [ ] 1. TypeScript" /
|
|
189
|
+
// "☐ 2. Python") OR after it ("❯ 1. [ ] 계란말이" — claude-cli >=2.1's layout).
|
|
190
|
+
// We require the numbered "N." option marker either way so we don't
|
|
191
|
+
// false-positive on the `✔ Submit` nav line (per-question answered-state
|
|
192
|
+
// ☐/☒) or on the headerless variant where the QUESTION line itself begins
|
|
193
|
+
// with `☐ ` (single-select).
|
|
194
|
+
const optionCheckbox = `(?:\\[[ xX]\\]|[☐☒◻◼])`;
|
|
195
|
+
const beforeNumber = new RegExp(`^\\s*(?:[❯›>]\\s*)?${optionCheckbox}\\s*\\d+\\.\\s+\\S`);
|
|
196
|
+
const afterNumber = new RegExp(`^\\s*(?:[❯›>]\\s*)?\\d+\\.\\s*${optionCheckbox}\\s+\\S`);
|
|
193
197
|
for (const line of screenText.split(/\r?\n/)) {
|
|
194
198
|
if (line.includes('✔ Submit')) continue; // header/nav line
|
|
195
|
-
if (
|
|
199
|
+
if (beforeNumber.test(line) || afterNumber.test(line)) return true;
|
|
196
200
|
}
|
|
197
201
|
return false;
|
|
198
202
|
}
|
|
@@ -361,6 +365,31 @@ function parseClaudeInteractiveTuiQuestion(page: ClaudeInteractiveTuiPage, index
|
|
|
361
365
|
};
|
|
362
366
|
}
|
|
363
367
|
|
|
368
|
+
/**
|
|
369
|
+
* Read the question the live claude TUI picker is CURRENTLY focused on, plus
|
|
370
|
+
* whether that focused page renders multi-select checkbox markers.
|
|
371
|
+
*
|
|
372
|
+
* Used to repair a multi-question prompt after the fact: when the daemon
|
|
373
|
+
* Tab-captures pages 2..N it snapshots ~120ms after the Tab keypress, before
|
|
374
|
+
* the newly-focused page's option-row checkbox column has redrawn — so those
|
|
375
|
+
* questions get frozen as multiSelect:false even though the picker is
|
|
376
|
+
* multi-select. Re-reading the focused page on a later status tick (once it has
|
|
377
|
+
* settled) lets us attribute the now-visible glyphs to the matching question
|
|
378
|
+
* and upgrade just that one. Returns null when no picker question is on screen.
|
|
379
|
+
*/
|
|
380
|
+
export function readFocusedClaudeTuiQuestion(
|
|
381
|
+
screenText: string,
|
|
382
|
+
): { question: string; header?: string; multiSelect: boolean } | null {
|
|
383
|
+
if (!screenText.includes('Enter to select')) return null;
|
|
384
|
+
const parsed = parseClaudeInteractiveTuiQuestion({ screenText }, 0);
|
|
385
|
+
if (!parsed) return null;
|
|
386
|
+
return {
|
|
387
|
+
question: parsed.question,
|
|
388
|
+
...(parsed.header ? { header: parsed.header } : {}),
|
|
389
|
+
multiSelect: parsed.multiSelect,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
364
393
|
export function detectClaudeAskUserQuestionPromptFromTuiPages(
|
|
365
394
|
pages: ClaudeInteractiveTuiPage[],
|
|
366
395
|
options: { promptId: string; providerType?: string; createdAt?: number },
|