@alexleekt/pi-ask-user-glimpse 0.3.2 → 0.5.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/CHANGELOG.md +85 -0
- package/CONTRIBUTING.md +15 -6
- package/README.md +75 -46
- package/constants/abbreviations.ts +6 -0
- package/constants/stopwords.ts +42 -0
- package/dist/index.html +950 -391
- package/index.ts +374 -211
- package/package.json +60 -9
- package/shared/ask-user.ts +11 -0
- package/tool/ask-user.ts +76 -306
- package/tool/response-formatter.ts +3 -2
- package/fallback/terminal-prompt.ts +0 -191
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,91 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `@alexleekt/pi-ask-user-glimpse` are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.5.0] — 2026-05-24
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Kitchen-sink debug mode** — `/ask-debug` now includes a `kitchen-sink` option that opens a comprehensive questionnaire demonstrating every major feature in one dialog: HTML context panel, recommended badges, multi-select, single-select, freeform, comments, and skip. Replaces the previous 9-mode menu (which had 5 redundant single-select variations) with a clean 5-mode list.
|
|
9
|
+
- **HTML context iframe sizing fix** — Removed `loading="lazy"` from the sandboxed iframe. Glimpse's native WebView host (WKWebView on macOS / WebView2 on Windows) applies lazy-loading heuristics even to `srcDoc` iframes with no network request, causing the HTML content to never render in some versions. Changed the iframe container from `overflow-y-auto` to `overflow-hidden flex flex-col` so the iframe's `flex-1` sizing works reliably within the flex layout. The iframe now uses `flex-1 min-h-0` instead of `h-full` to avoid the 150px default-height fallback when percentage heights fail in nested flex contexts.
|
|
10
|
+
- **Recommendation badges** — Options can now include `recommended: true` to show a "Recommended" badge in the dialog. The badge renders next to the option title using the primary color token. Works in single-select, multi-select, and questionnaire modes. The LLM tool schema documents the field so agents can mark their top recommendation.
|
|
11
|
+
- **Option numbering** — Each option now displays a circular number badge (① ② ③…) between the checkbox/radio and the title text. Provides a quick visual reference for keyboard navigation and makes it easier to refer to options when discussing choices.
|
|
12
|
+
- **Markdown rendering in option text** — Option titles and descriptions are now rendered through inline markdown. Bold `**`, italic `*`, code `` ` ``, and links display correctly. During search, markdown rendering is temporarily disabled to avoid broken markup from injected `<mark>` highlight tags.
|
|
13
|
+
- ~~Auto-catch free-form questions~~ — **Removed.** The "Always Dialog" mode and its auto-catch behavior have been eliminated.
|
|
14
|
+
- **HTML context format** — New `contextFormat: "html"` option for the `ask_user` tool. When set, the `context` field renders inside a sandboxed iframe (`sandbox="allow-scripts"`) in the left panel instead of markdown. The iframe inherits the wrapper's CSS variables for automatic light/dark theme consistency. Theme changes are propagated via `postMessage`.
|
|
15
|
+
- **YOLO style** — New `/ask-style` state that tells the agent to proceed with its best recommendation without asking. Injects a mandate: "Do NOT ask the user for input or confirmation. Go with your best recommendation and proceed immediately. Only use `ask_user` if the action would cause irreversible harm, data loss, security compromise, or violate explicit hard constraints."
|
|
16
|
+
|
|
17
|
+
### Removed
|
|
18
|
+
- **"Always Dialog" mode** — Removed the `ASK_USER_MANDATE` system-prompt injection and the auto-catch behavior that converted free-form agent questions into dialogs. These features caused excessive dialog interruptions and were too aggressive for general use. The `/ask` command and manual `ask_user` tool calls still work normally.
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- **Plain Text is now the default** — The extension no longer injects any system-prompt mandate by default. The agent writes questions as free-form text unless the user manually triggers `/ask` or the agent calls `ask_user`. The `/ask-style` toggle now cycles through **Plain Text** → **YOLO** → **Plain Text**.
|
|
22
|
+
- **Two-state `/ask-style` cycle** — `Plain Text → YOLO → Plain Text`.
|
|
23
|
+
- **`deliverAs: "steer"` for all user message delivery** — Both auto-catch and `/ask` now use `steer` instead of `followUp` when sending answers back to the conversation. `steer` sends immediately and is processed as an active user intervention, avoiding the "Follow-up: queued messages" UI state where the answer was stuck waiting.
|
|
24
|
+
- **Removed terminal fallback** — Deleted `fallback/terminal-prompt.ts`. When the Glimpse native host is unavailable, the extension always returns an explicit error telling the agent to ask in free-form text. The previous TUI fallback (`ctx.ui.select()` / `ctx.ui.input()`) has been removed to simplify the codebase and provide consistent behavior.
|
|
25
|
+
- **Freeform empty submissions allowed** — Removed the guard that blocked empty freeform text submissions. This is consistent with questionnaire behavior where unanswered questions are allowed when `allowSkip: true`.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
- **Agent preamble capture** — When the agent writes an introductory message before calling `ask_user`, that text is now automatically captured and prepended to the context panel. The extension finds the most recent assistant journal entry, extracts its text content, and appends it to the dialog's left panel (separated by a horizontal rule from any explicit `context` provided by the agent). This ensures the user sees the full reasoning that led to the question, not just the question itself.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
- **Markdown in question header** — The `question` field is now rendered through `marked` so inline markdown (bold `**`, italic `*`, code `` ` ``, links) displays correctly instead of showing raw escape characters. The HTML is sanitized with the same defense-in-depth sanitizer used by the context panel. Extracted shared `sanitizeHtml()`, `renderMarkdown()`, and `renderMarkdownInline()` to `webview/src/util/markdown.ts`.
|
|
32
|
+
- **`sendUserMessage` error handling** — Wrapped `pi.sendUserMessage()` in `try/catch` with `await` in the `/ask` handler. Previously, the promise was fire-and-forget; when the framework threw "Agent is already processing" (or any other error), the rejection was unhandled and the dialog answer was silently lost. Errors are now logged to console with `[pi-ask-user-glimpse]` prefix and surfaced via UI notification.
|
|
33
|
+
- **`additionalComments` in questionnaire responses** — The `questionnaire` response kind was missing the `additionalComments` field that existed for `selection` and `freeform` kinds. Now all three kinds consistently include `additionalComments` when the user provides them.
|
|
34
|
+
- **Fast-escape when no UI available** — `askUserHandler` now returns an explicit error message ("No UI available for ask_user dialog...") instead of falling back to a terminal prompt when `!ctx.hasUI`. The `before_agent_start` mandate injection is also skipped in headless environments, preventing the agent from being forced to use a tool that cannot work.
|
|
35
|
+
|
|
36
|
+
## [0.4.1] — 2026-05-20
|
|
37
|
+
|
|
38
|
+
### Security
|
|
39
|
+
- **Comprehensive HTML sanitization** — `ContextPanel.sanitizeHtml()` now blocks `img`, `iframe`, `object`, `embed`, `form`, `input`, `style`, `link`, `svg`, `math`, `meta`, `base`, `noscript`, `template`, `portal`, `frame`, `frameset` tags, plus `javascript:` and `data:` URLs in `href`/`src`/`action` attributes.
|
|
40
|
+
- **XSS-safe search highlighting** — `highlightMatch()` in new `webview/src/util/html.ts` escapes both display text and query strings before wrapping matches in `<mark>`. Replaces raw `.replace()` in `SingleSelect` and `MultiSelect` that was vulnerable to search query injection.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
- **Prominent question header** — Removed sparkle icon and "Ask User" branding. The header now shows the full non-truncated question text in `text-base` font, wrapping naturally.
|
|
44
|
+
- **50/50 panel split** — Default context/options panel width changed from 40/60 to 50/50.
|
|
45
|
+
- **Invisible splitter track** — Removed grey divider bar. Only a centered grip handle is visible (`w-1` by default, `w-1.5` on drag). Handle sits exactly at the panel boundary.
|
|
46
|
+
- **Instant drag feedback** — Removed CSS transition from panel width so resize is immediate, not animated.
|
|
47
|
+
- **Double-click to collapse** — Double-clicking the splitter toggles the context panel between 50% width and fully collapsed.
|
|
48
|
+
- **Click-when-collapsed to expand** — If the context panel is collapsed, clicking the splitter expands it back to 50% without starting a drag.
|
|
49
|
+
- **Hover-only scrollbars** — Scrollbars are hidden by default and appear as thin 6px tracks on hover (macOS overlay style). Applied to the context panel.
|
|
50
|
+
- **Theme persistence across all entry points** — Extracted shared helpers `enrichWithThemeSettings()`, `createThemeSaver()`, and `runAskUserWithTheme()` in `index.ts`. All three entry points (`ask_user` tool, `/ask`, `/ask-debug`) now share identical theme read/save behavior.
|
|
51
|
+
- **Type-safe theme settings** — `getThemeSettings()` now validates stored strings against `ThemeMode`/`AnimationLevel` union types before returning.
|
|
52
|
+
- **Refactored constants** — Extracted `STOPWORDS` (~200 words) and `PROTECTED_ABBREVIATIONS` to `constants/stopwords.ts` and `constants/abbreviations.ts`.
|
|
53
|
+
- **Fully controlled AdditionalComments** — Removed half-controlled anti-pattern. Component now requires both `value` and `onChange` props.
|
|
54
|
+
- **Command rename** — `/ask-last` → `/ask` (shorter, more intuitive).
|
|
55
|
+
|
|
56
|
+
### Fixed
|
|
57
|
+
- **Mermaid rendering errors** — Added explicit `mermaid.initialize()` with `startOnLoad: false`. Defers `mermaid.run()` to `requestAnimationFrame` so the DOM is fully committed first. Errors now log via `console.warn` instead of being silently swallowed.
|
|
58
|
+
- **Stale closure in keydown handlers** — SingleSelect and MultiSelect now use a `stateRef` pattern: all mutable state is snapshotted into a ref, and the global keydown listener has stable dependencies (`useCallback` for handlers).
|
|
59
|
+
- **Short questions dropped** — `extractQuestions()` length threshold lowered from 10 to 3 characters so legitimate questions like "Why?" are not silently discarded.
|
|
60
|
+
- **ARIA on splitter** — Added `role="separator"`, `aria-orientation="vertical"`, `aria-valuenow/min/max`.
|
|
61
|
+
- **Option ref mutation** — Removed direct `optionRefs.current = []` mutation; uses `requestAnimationFrame` for focus timing instead.
|
|
62
|
+
- **Consistent sendCancelled references** — All cancel buttons now pass `sendCancelled` directly instead of arrow wrappers.
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
- **`npm run test:with-context`** — New script that opens a WebView with the context panel, splitter, and Mermaid diagrams for visual testing.
|
|
66
|
+
- **`webview/src/util/html.ts`** — Shared HTML utilities: `escapeHtml()` and `highlightMatch()`.
|
|
67
|
+
|
|
68
|
+
## [0.4.0] — 2026-05-20
|
|
69
|
+
|
|
70
|
+
### Added
|
|
71
|
+
- **Branded header bar** — A thin branded bar at the top of every dialog with a sparkle icon + "Ask User" label on the left, and a settings cog + keyboard-shortcuts help on the right.
|
|
72
|
+
- **Theme toggle (dark / light / system)** — Settings dropdown lets users switch between dark mode, light mode, or following the OS preference. Choice is persisted in the webview's localStorage.
|
|
73
|
+
- **Animation level toggle (none / minimal / all)** — Settings dropdown also controls animation intensity. "None" disables all transitions; "minimal" keeps only essential ones; "all" enables full polish.
|
|
74
|
+
- **Consistent Cmd+Enter submit** — All four dialog types (single-select, multi-select, questionnaire, freeform) now support Cmd+Enter (macOS) / Ctrl+Enter (other) to submit the answer. Footer hints updated accordingly.
|
|
75
|
+
- **Window titles with session name** — Titles now read "Pi · {sessionName} · {question}" when a session name is set, making it easier to identify which conversation a dialog belongs to.
|
|
76
|
+
- **Character counter** — Freeform textareas and questionnaire freeform fields show a live `0/2000` or `0/1000` counter. Turns red when approaching the limit.
|
|
77
|
+
- **Required field badge** — In questionnaire mode, when `allowSkip: false`, unanswered questions show a red "Required" badge and a subtle red border until answered.
|
|
78
|
+
- **Search highlight** — When filtering options via the search box, matching text in option titles and descriptions is highlighted with a yellow background.
|
|
79
|
+
- **Quick-select all/none** — Multi-select dialogs show "Select all" and "Select none" links above the option list (when not actively searching).
|
|
80
|
+
- **Keyboard shortcuts legend** — A `?` button in the header bar opens a modal showing all available keyboard shortcuts.
|
|
81
|
+
|
|
82
|
+
### Changed
|
|
83
|
+
- **CSS dark mode strategy** — Switched from `prefers-color-scheme` media query to Tailwind's `darkMode: 'class'` strategy, enabling explicit theme toggling independent of OS setting.
|
|
84
|
+
- **Theme metadata in results** — The webview sends back the active theme and animation level with every result, so the extension can persist preferences across sessions.
|
|
85
|
+
|
|
86
|
+
### Fixed
|
|
87
|
+
- **White screen on /ask-debug** — The Glimpse native webview (WKWebView) blocks `localStorage` access with `SecurityError: The operation is insecure.` because `loadHTMLString(baseURL: nil)` gives the page no origin. Removed all `localStorage` usage from the webview entirely. Theme and animation state now flows through the payload: the extension reads stored settings from Pi journal entries, passes them into the webview via `AskUserPayload`, and the webview sends back the user's choices via the result's `__theme`/`__animationLevel` fields. The extension then persists them back into journal entries.
|
|
88
|
+
- **Top-level ErrorBoundary** — Wrapped the entire React app in an `ErrorBoundary` so future render crashes show a readable error message instead of an empty white screen.
|
|
89
|
+
|
|
5
90
|
## [0.3.2] — 2026-05-20
|
|
6
91
|
|
|
7
92
|
### Fixed
|
package/CONTRIBUTING.md
CHANGED
|
@@ -20,12 +20,13 @@ npm run check # dry-run npm pack
|
|
|
20
20
|
## Testing
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
npm run validate
|
|
24
|
-
npm run validate:gui
|
|
25
|
-
|
|
26
|
-
npx tsx scripts/visual-qa.ts # cycles through all 5 scenarios
|
|
23
|
+
npm run validate # checks dist exists, placeholder present, binary found
|
|
24
|
+
npm run validate:gui # same + opens actual WebView for visual check
|
|
25
|
+
npm run test:with-context # opens WebView with context panel + resizable splitter
|
|
27
26
|
```
|
|
28
27
|
|
|
28
|
+
For manual visual testing, use `/ask-debug` inside a Pi session. It offers five scenarios: `single-select`, `multi-select`, `freeform`, `questionnaire`, and `kitchen-sink` (comprehensive questionnaire with HTML context panel).
|
|
29
|
+
|
|
29
30
|
## Code Style
|
|
30
31
|
|
|
31
32
|
- **Indentation:** 2 spaces (TypeScript)
|
|
@@ -33,8 +34,9 @@ npx tsx scripts/visual-qa.ts # cycles through all 5 scenarios
|
|
|
33
34
|
- **Console output:** Use `[pi-ask-user-glimpse]` prefix for all `console.warn`/`console.error`
|
|
34
35
|
- **Peer deps:** Only list `@earendil-works/pi-coding-agent` and `@earendil-works/pi-ai` in `peerDependencies`
|
|
35
36
|
|
|
36
|
-
## Security
|
|
37
|
+
## Security Notes
|
|
37
38
|
|
|
39
|
+
### HTML escaping in payload injection
|
|
38
40
|
If you modify payload injection or test scripts, ensure HTML escaping matches production:
|
|
39
41
|
```ts
|
|
40
42
|
JSON.stringify(payload)
|
|
@@ -43,9 +45,16 @@ JSON.stringify(payload)
|
|
|
43
45
|
.replace(/&/g, "\\u0026")
|
|
44
46
|
```
|
|
45
47
|
|
|
48
|
+
### XSS prevention in search highlighting
|
|
49
|
+
`highlightMatch()` in `webview/src/util/html.ts` must escape both display text and search query before producing HTML. Never pass raw user input into `dangerouslySetInnerHTML`.
|
|
50
|
+
|
|
51
|
+
### ContextPanel sanitization
|
|
52
|
+
`sanitizeHtml()` blocks dangerous tags (`script`, `img`, `iframe`, `object`, `embed`, `form`, `svg`, etc.) and strips `javascript:` / `data:` URLs. Audit the sanitizer when adding new rich content support.
|
|
53
|
+
|
|
46
54
|
## Before Submitting
|
|
47
55
|
|
|
48
56
|
- [ ] `npm run build` passes
|
|
49
57
|
- [ ] `npx tsc --noEmit` passes
|
|
50
|
-
- [ ] `npm run check` shows the expected files
|
|
58
|
+
- [ ] `npm run check` shows the expected files (including `constants/`)
|
|
51
59
|
- [ ] `npm run validate` passes
|
|
60
|
+
- [ ] `npm run test:with-context` passes
|
package/README.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# @alexleekt/pi-ask-user-glimpse
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/@alexleekt/pi-ask-user-glimpse)
|
|
4
|
+
[](LICENSE)
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
> Ask better questions. Get better answers.
|
|
7
|
+
|
|
8
|
+
Rich native WebView dialogs powered by [glimpseui](https://npmjs.com/package/glimpseui) and styled with shadcn/ui design tokens.
|
|
7
9
|
|
|
8
10
|
> **Stop reading terminal walls of text.** This extension turns every `ask_user` call into a beautiful, searchable, keyboard-navigable dialog — complete with markdown context panels, inline descriptions, and dark mode. Your agent asks better questions. You answer faster.
|
|
9
11
|
|
|
@@ -30,12 +32,16 @@ The agent gets a clean selection back. You get a decision made in seconds, not m
|
|
|
30
32
|
|
|
31
33
|
## Features
|
|
32
34
|
|
|
33
|
-
- **Single-select** — searchable option list with inline descriptions
|
|
34
|
-
- **Multi-select** — checkbox-style selection with
|
|
35
|
-
- **Freeform** — textarea input
|
|
36
|
-
- **Questionnaire** — cards in a vertical list for structured questions,
|
|
35
|
+
- **Single-select** — searchable option list with inline descriptions, numbered option badges (① ② ③), inline markdown rendering, and search highlight
|
|
36
|
+
- **Multi-select** — checkbox-style selection with quick-select all/none links, numbered badges, and inline markdown
|
|
37
|
+
- **Freeform** — textarea input with live character counter and platform-aware keyboard shortcuts. Empty submissions are allowed (useful when the agent asks an open question but you have nothing to add)
|
|
38
|
+
- **Questionnaire** — cards in a vertical list for structured questions, with required-field badges, per-question character counters, numbered option badges, and inline markdown in option text
|
|
39
|
+
- **Theme toggle** — dark / light / system mode switcher in the dialog header
|
|
40
|
+
- **Animation levels** — none / minimal / all, controlling transition intensity across the UI
|
|
41
|
+
- **Keyboard shortcuts legend** — press `?` in the header bar to see all available shortcuts
|
|
42
|
+
- **Prominent question header** — full non-truncated question text in the header bar, with settings cog and keyboard-shortcuts help
|
|
37
43
|
- **Native WebView** — renders in a real window (macOS WKWebView / Linux GTK4 / Windows WebView2)
|
|
38
|
-
- **
|
|
44
|
+
- **Graceful degradation** — returns clear error when no UI is available, prompting the agent to ask in free-form text
|
|
39
45
|
|
|
40
46
|
## Install
|
|
41
47
|
|
|
@@ -67,7 +73,7 @@ Ask the user to pick exactly one option:
|
|
|
67
73
|
}
|
|
68
74
|
```
|
|
69
75
|
|
|
70
|
-
The dialog shows
|
|
76
|
+
The dialog shows the full question in the header bar, and a two-panel layout when `context` is provided. The left panel renders context as markdown (with Mermaid diagram support) while the right panel shows the searchable option list. The panels are resizable via a drag handle on the boundary — double-click the handle to collapse the context panel. Option descriptions appear inline below each title, and matching text is highlighted when searching. The "Custom" button lets the user submit a freeform answer. Use ⌘+Enter (macOS) or Ctrl+Enter to submit.
|
|
71
77
|
|
|
72
78
|
### Multi Select
|
|
73
79
|
|
|
@@ -88,7 +94,29 @@ Ask the user to pick multiple options:
|
|
|
88
94
|
}
|
|
89
95
|
```
|
|
90
96
|
|
|
91
|
-
Each option has a checkbox. A "Clear all" link resets selections. Submit is disabled until at least one item is selected.
|
|
97
|
+
Each option has a checkbox. "Select all" and "Select none" links appear above the list (when not searching). A "Clear all" link resets selections. Submit is disabled until at least one item is selected. Use ⌘+Enter (macOS) or Ctrl+Enter to submit.
|
|
98
|
+
|
|
99
|
+
### HTML Context (Visualizations)
|
|
100
|
+
|
|
101
|
+
When text and Mermaid diagrams aren't enough, render rich HTML in the context panel:
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"question": "Which layout performs better?",
|
|
106
|
+
"context": "<div style='display:flex;gap:1rem;justify-content:center'>...</div>",
|
|
107
|
+
"contextFormat": "html",
|
|
108
|
+
"options": [
|
|
109
|
+
{ "title": "Layout A", "description": "Dense grid with sidebar" },
|
|
110
|
+
{ "title": "Layout B", "description": "Spacious single column" }
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The `context` HTML renders inside a **sandboxed iframe** (`sandbox="allow-scripts"`) in the left panel. It inherits the wrapper's CSS variables (`--background`, `--foreground`, `--primary`, etc.) for automatic light/dark theme consistency. The iframe auto-updates its theme class when the user toggles settings. This is ideal for throwaway bar charts, tables, decision trees, or any visualization that helps the user decide.
|
|
116
|
+
|
|
117
|
+
**Security:** The iframe is isolated from the wrapper app. It cannot access `localStorage`, cookies, or the parent DOM. Only inline scripts are permitted (for animations/interactivity). The agent should not include `<script src="...">` tags that load external resources — inline JS and CSS only.
|
|
118
|
+
|
|
119
|
+
**Glimpse behavior:** The HTML context panel runs inside Glimpse's native WebView host (WKWebView on macOS, WebView2 on Windows). The iframe receives an opaque ("null") origin because Glimpse loads the page via `loadHTMLString` without a base URL. This means `window.location.origin` is `null`, and `localStorage` access throws a `SecurityError`. Theme changes are propagated via `postMessage` instead of DOM sharing.
|
|
92
120
|
|
|
93
121
|
### Freeform
|
|
94
122
|
|
|
@@ -102,7 +130,7 @@ Ask an open-ended question with no predefined options:
|
|
|
102
130
|
}
|
|
103
131
|
```
|
|
104
132
|
|
|
105
|
-
Shows a full-height textarea with platform-aware keyboard hints (⌘+Enter on macOS, Ctrl+Enter elsewhere). Submit is
|
|
133
|
+
Shows a full-height textarea with a live character counter and platform-aware keyboard hints (⌘+Enter on macOS, Ctrl+Enter elsewhere). Submit is always enabled — you can send an empty answer if you have nothing to add.
|
|
106
134
|
|
|
107
135
|
### Questionnaire
|
|
108
136
|
|
|
@@ -140,15 +168,16 @@ Ask multiple structured questions in one dialog:
|
|
|
140
168
|
}
|
|
141
169
|
```
|
|
142
170
|
|
|
143
|
-
Each question is shown as a card with a progress bar at the top. Questions with `options` render as single-select (radio) or multi-select (checkbox) depending on `allowMultiple`. Questions without `options` render as a textarea. The dialog auto-scrolls to the first unanswered question on open. The comment button shows "Edit comment" when text exists. Submit is disabled until all questions have a non-empty answer, unless `allowSkip: true` is set.
|
|
171
|
+
Each question is shown as a card with a progress bar at the top. Questions with `options` render as single-select (radio) or multi-select (checkbox) depending on `allowMultiple`. Questions without `options` render as a textarea with a character counter. The dialog auto-scrolls to the first unanswered question on open. When `allowSkip: false`, unanswered questions show a red "Required" badge. The comment button shows "Edit comment" when text exists. Submit is disabled until all questions have a non-empty answer, unless `allowSkip: true` is set. Use ⌘+Enter (macOS) or Ctrl+Enter to submit.
|
|
144
172
|
|
|
145
173
|
### Parameters
|
|
146
174
|
|
|
147
175
|
| Parameter | Type | Default | Description |
|
|
148
176
|
|-----------|------|---------|-------------|
|
|
149
177
|
| `question` | `string` | *(required)* | The question to ask |
|
|
150
|
-
| `context` | `string` | — | Additional context shown in a left-side
|
|
151
|
-
| `
|
|
178
|
+
| `context` | `string` | — | Additional context shown in a left-side panel |
|
|
179
|
+
| `contextFormat` | `"markdown" | "html"` | `"markdown"` | Format of the `context` field. `markdown` renders formatted text with Mermaid diagram support. `html` renders in a sandboxed iframe — useful for throwaway charts, tables, or interactive visualizations. The iframe inherits the wrapper's CSS variables for theme consistency. |
|
|
180
|
+
| `options` | `Array<string | {title, description?, recommended?}>` | — | Options for flat single/multi-select mode. Set `recommended: true` to show a "Recommended" badge. |
|
|
152
181
|
| `questions` | `Array<{title, description?, options?, allowMultiple?}>` | — | Questions for questionnaire mode. When present, `options` is ignored. Each question can have its own `options` (same shape as top-level `options`) and `allowMultiple`. Questions without `options` render as freeform textareas. |
|
|
153
182
|
| `allowMultiple` | `boolean` | `false` | Allow selecting multiple options |
|
|
154
183
|
| `allowFreeform` | `boolean` | `true` | Show a freeform "Custom" option |
|
|
@@ -185,50 +214,44 @@ npm run validate:gui # same + opens actual WebView for visual check
|
|
|
185
214
|
npm run check # dry-run npm pack
|
|
186
215
|
```
|
|
187
216
|
|
|
188
|
-
##
|
|
217
|
+
## Ask-Style Toggle: `/ask-style`
|
|
189
218
|
|
|
190
|
-
|
|
219
|
+
By default, the agent asks questions as free-form text. You can change this per-session:
|
|
191
220
|
|
|
192
|
-
|
|
221
|
+
```
|
|
222
|
+
/ask-style
|
|
223
|
+
```
|
|
193
224
|
|
|
194
|
-
|
|
225
|
+
Cycles through two states:
|
|
195
226
|
|
|
196
|
-
|
|
227
|
+
| State | Behavior |
|
|
228
|
+
|-------|----------|
|
|
229
|
+
| **Plain Text** *(default)* | Agent writes questions as free-form text |
|
|
230
|
+
| **YOLO** | Agent proceeds with its best recommendation without asking |
|
|
197
231
|
|
|
198
|
-
|
|
199
|
-
2. **Language patterns** in the system prompt — "ask the questions one at a time", "interview me", "grilling session", "wait for feedback"
|
|
200
|
-
3. **Manual toggle** — `/ask-style` overrides the behavior for the current session
|
|
232
|
+
The setting is persisted in the session and survives restarts.
|
|
201
233
|
|
|
202
|
-
|
|
234
|
+
### YOLO style
|
|
203
235
|
|
|
204
|
-
|
|
236
|
+
When YOLO is active, the extension injects a mandate telling the agent **not** to ask for input or confirmation. Instead, the agent goes with its best recommendation and keeps moving. It will only use `ask_user` if the action would cause irreversible harm, data loss, or security compromise.
|
|
205
237
|
|
|
206
|
-
|
|
238
|
+
Use this when you trust the agent's judgment and want maximum speed:
|
|
207
239
|
|
|
208
240
|
```
|
|
209
241
|
/ask-style
|
|
242
|
+
→ ask_user style: YOLO — go with your recommendation
|
|
210
243
|
```
|
|
211
244
|
|
|
212
|
-
Cycles through three states:
|
|
213
|
-
|
|
214
|
-
| State | Behavior |
|
|
215
|
-
|-------|----------|
|
|
216
|
-
| **AUTO** *(default)* | Auto-detect question sessions by skill name + language patterns |
|
|
217
|
-
| **Always Dialog** | Always use `ask_user` for every question, regardless of detection |
|
|
218
|
-
| **Plain Text** | Disable everything — let the agent write questions as plain text |
|
|
219
|
-
|
|
220
|
-
The setting is persisted in the session and survives restarts.
|
|
221
|
-
|
|
222
245
|
### Token cost
|
|
223
246
|
|
|
224
|
-
The injected mandate is ~100 tokens. It is
|
|
247
|
+
The injected mandate is ~100 tokens. It is appended on every turn when `ask_user` is available in the tool set.
|
|
225
248
|
|
|
226
|
-
## Slash Command: `/ask
|
|
249
|
+
## Slash Command: `/ask`
|
|
227
250
|
|
|
228
251
|
When the assistant writes a question as free-form text (bypassing `ask_user`), use this command to retroactively open the rich dialog:
|
|
229
252
|
|
|
230
253
|
```
|
|
231
|
-
/ask
|
|
254
|
+
/ask
|
|
232
255
|
```
|
|
233
256
|
|
|
234
257
|
### How it works
|
|
@@ -252,26 +275,32 @@ Open a debug prompt that lets you manually test each dialog type:
|
|
|
252
275
|
/ask-debug
|
|
253
276
|
```
|
|
254
277
|
|
|
255
|
-
Options: `single-select`, `multi-select`, `freeform`, `questionnaire`. The result is shown as a Pi notification.
|
|
278
|
+
Options: `single-select`, `multi-select`, `freeform`, `questionnaire`, `kitchen-sink`. The result is shown as a Pi notification.
|
|
279
|
+
|
|
280
|
+
The **kitchen-sink** option opens a comprehensive questionnaire with an HTML context panel, recommended badges, multi-select, freeform, comments, and skip — every major feature in one dialog.
|
|
256
281
|
|
|
257
282
|
## Window Behavior
|
|
258
283
|
|
|
259
|
-
- **Title bar** —
|
|
284
|
+
- **Title bar** — reads "Pi · {sessionName} · {question}" (session name is included when set)
|
|
260
285
|
- **Centered dialog** — normal stacking, not floating
|
|
261
286
|
- **Size** — 1200×900 by default
|
|
287
|
+
- **Context panel** — 50/50 split by default; drag the handle to resize, double-click to collapse
|
|
288
|
+
- **Scrollbars** — hidden by default, appear on hover (macOS-style overlay)
|
|
262
289
|
- **Cursor follow** — off by default; enable with `followCursor: true`
|
|
263
|
-
- **Dark mode** —
|
|
290
|
+
- **Dark mode** — togglable via the settings cog: dark, light, or system (follows OS preference)
|
|
291
|
+
- **Theme persistence** — theme and animation choices survive across dialogs and session restarts
|
|
264
292
|
|
|
265
293
|
## Architecture
|
|
266
294
|
|
|
267
295
|
```
|
|
268
296
|
index.ts → Pi extension entrypoint (tool + command registration)
|
|
297
|
+
constants/ → STOPWORDS, PROTECTED_ABBREVIATIONS
|
|
269
298
|
tool/ask-user.ts → constructs payload, injects into HTML, calls glimpseui.prompt()
|
|
270
299
|
tool/response-formatter.ts → normalizes WebView response for Pi
|
|
271
|
-
|
|
272
|
-
fallback/terminal-prompt.ts → readline fallback when WebView unavailable
|
|
300
|
+
(no terminal fallback — fast-escape with error when UI unavailable)
|
|
273
301
|
webview/ → Vite + React + Tailwind app
|
|
274
|
-
src/components/ → SingleSelect, MultiSelect, Questionnaire, Freeform
|
|
302
|
+
src/components/ → SingleSelect, MultiSelect, Questionnaire, Freeform, ContextPanel, ErrorBoundary, HeaderBar, ShortcutsModal, AdditionalComments
|
|
303
|
+
src/util/ → settings.tsx (theme/animation context), glimpse.ts (host bridge), platform.ts (modKey), html.ts (escapeHtml + highlightMatch)
|
|
275
304
|
dist/index.html → single-file bundle (inlined JS + CSS)
|
|
276
305
|
```
|
|
277
306
|
|
|
@@ -281,9 +310,9 @@ webview/ → Vite + React + Tailwind app
|
|
|
281
310
|
|
|
282
311
|
Run `npm run build` to generate `dist/index.html`. The extension cannot work without the webview bundle.
|
|
283
312
|
|
|
284
|
-
### WebView does not open
|
|
313
|
+
### WebView does not open
|
|
285
314
|
|
|
286
|
-
|
|
315
|
+
If the glimpseui native host is unavailable, the extension returns an error to the agent, which will ask the question in free-form text instead. This is normal on headless systems or if the native binary is missing. Check `npm run validate` to see if the binary is detected.
|
|
287
316
|
|
|
288
317
|
### Dialog shows "Missing or invalid ask_user payload"
|
|
289
318
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Protected abbreviations for sentence splitting. */
|
|
2
|
+
export const PROTECTED_ABBREVIATIONS = new Set([
|
|
3
|
+
"etc", "vs", "fig", "dr", "mr", "mrs", "ms", "prof", "jr", "sr",
|
|
4
|
+
"inc", "ltd", "corp", "co", "llc", "al", "et", "vol", "vols",
|
|
5
|
+
"pg", "pp", "ch", "chap", "sec", "secs",
|
|
6
|
+
]);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** ~200 common English stopwords for title extraction. */
|
|
2
|
+
export const STOPWORDS = new Set([
|
|
3
|
+
"a", "an", "the", "is", "are", "was", "were", "be", "been", "being",
|
|
4
|
+
"have", "has", "had", "do", "does", "did", "will", "would", "could",
|
|
5
|
+
"should", "may", "might", "must", "shall", "can", "need", "ought",
|
|
6
|
+
"used", "to", "of", "in", "for", "on", "with", "at", "by", "from",
|
|
7
|
+
"as", "into", "through", "during", "before", "after", "above",
|
|
8
|
+
"below", "between", "under", "again", "further", "then", "once",
|
|
9
|
+
"here", "there", "when", "where", "why", "how", "all", "each",
|
|
10
|
+
"few", "more", "most", "other", "some", "such", "no", "nor", "not",
|
|
11
|
+
"only", "own", "same", "so", "than", "too", "very", "just", "and",
|
|
12
|
+
"but", "if", "or", "because", "until", "while", "which", "what",
|
|
13
|
+
"who", "whom", "this", "that", "these", "those", "am", "it", "its",
|
|
14
|
+
"we", "our", "you", "your", "they", "their", "them", "he", "him",
|
|
15
|
+
"his", "she", "her", "i", "me", "my", "mine", "us", "any", "both",
|
|
16
|
+
"either", "neither", "one", "two", "first", "last", "another",
|
|
17
|
+
"every", "many", "much", "several", "let", "new", "use", "using",
|
|
18
|
+
"make", "made", "get", "got", "go", "going", "want", "wanted", "like",
|
|
19
|
+
"liked", "know", "knew", "known", "think", "thought", "see", "saw",
|
|
20
|
+
"seen", "come", "came", "give", "gave", "given", "take", "took",
|
|
21
|
+
"taken", "find", "found", "say", "said", "tell", "told", "ask",
|
|
22
|
+
"asked", "work", "worked", "seem", "seemed", "feel", "felt", "try",
|
|
23
|
+
"tried", "leave", "left", "call", "called", "good", "well", "better",
|
|
24
|
+
"best", "bad", "worse", "worst", "old", "long", "great", "little",
|
|
25
|
+
"right", "left", "big", "high", "different", "important", "same",
|
|
26
|
+
"able", "next", "early", "young", "public", "free", "real", "easy",
|
|
27
|
+
"clear", "recent", "local", "social", "full", "small", "large",
|
|
28
|
+
"possible", "particular", "available", "special", "certain", "personal",
|
|
29
|
+
"open", "general", "enough", "probably", "actually", "especially",
|
|
30
|
+
"finally", "usually", "perhaps", "almost", "simply", "quickly",
|
|
31
|
+
"recently", "already", "eventually", "suddenly", "certainly",
|
|
32
|
+
"definitely", "absolutely", "completely", "totally", "entirely",
|
|
33
|
+
"exactly", "specifically", "particularly", "especially", "mainly",
|
|
34
|
+
"mostly", "partly", "fully", "nearly", "quite", "rather", "pretty",
|
|
35
|
+
"fairly", "really", "even", "still", "yet", "ever", "never", "always",
|
|
36
|
+
"sometimes", "often", "usually", "frequently", "rarely", "generally",
|
|
37
|
+
"typically", "normally", "largely", "potentially", "theoretically",
|
|
38
|
+
"practically", "basically", "essentially", "fundamentally",
|
|
39
|
+
"primarily", "chiefly", "principally", "partially", "half", "quarter",
|
|
40
|
+
"double", "single", "multiple", "various", "hundred", "thousand",
|
|
41
|
+
"million", "billion",
|
|
42
|
+
]);
|