@fadhelhaidar/pi-ask-user 0.0.0-stage → 1.0.2

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.
@@ -0,0 +1,31 @@
1
+ {
2
+ "_meta.notes": "Auto-translated draft. Native-speaker review welcome via PR. TUI symbols (↑/↓, ⚠) and key names (Enter, Esc, Tab, Space, n, Ctrl+G, Ctrl+U) are universal CLI conventions and stay untranslated.",
3
+
4
+ "sentinel.other": "Escribe algo.",
5
+ "sentinel.next": "Siguiente",
6
+
7
+ "submit.label": "Enviar respuestas",
8
+ "submit.cancel": "Cancelar",
9
+
10
+ "hint.enter": "Enter para seleccionar",
11
+ "hint.navigate": "↑/↓ para navegar",
12
+ "hint.newline": "Mayús+Enter para nueva línea",
13
+ "hint.clear": "Ctrl+U para borrar",
14
+ "hint.toggle": "Espacio para alternar",
15
+ "hint.notes": "n para añadir notas",
16
+ "hint.tab": "Tab para cambiar de pregunta",
17
+ "hint.cancel": "Esc para cancelar",
18
+
19
+ "review.heading": "Revisa tus respuestas",
20
+ "review.ready": "¿Listo para enviar tus respuestas?",
21
+ "review.incomplete": "⚠ Responde las preguntas restantes antes de enviar:",
22
+
23
+ "preview.no_preview": "Vista previa no disponible",
24
+ "preview.notes_affordance": "Notas: pulsa n para añadir notas",
25
+ "notes.header": "Notas:",
26
+
27
+ "editor.failed": "Error del editor externo",
28
+
29
+ "rpc.multi_instructions": "Introduce los números de todas las opciones que apliquen, separados por comas (p. ej. \"1,3\"), o escribe una respuesta propia como texto.",
30
+ "rpc.custom_answer_title": "Escribe tu respuesta:"
31
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "_meta.notes": "Auto-translated draft. Native-speaker review welcome via PR. TUI symbols (↑/↓, ⚠) and key names (Enter, Esc, Tab, Space, n, Ctrl+G, Ctrl+U) are universal CLI conventions and stay untranslated.",
3
+
4
+ "sentinel.other": "Écrivez quelque chose.",
5
+ "sentinel.next": "Suivant",
6
+
7
+ "submit.label": "Envoyer les réponses",
8
+ "submit.cancel": "Annuler",
9
+
10
+ "hint.enter": "Entrée pour sélectionner",
11
+ "hint.navigate": "↑/↓ pour naviguer",
12
+ "hint.newline": "Maj+Entrée pour une nouvelle ligne",
13
+ "hint.clear": "Ctrl+U pour effacer",
14
+ "hint.toggle": "Espace pour basculer",
15
+ "hint.notes": "n pour ajouter des notes",
16
+ "hint.tab": "Tab pour changer de question",
17
+ "hint.cancel": "Échap pour annuler",
18
+
19
+ "review.heading": "Vérifiez vos réponses",
20
+ "review.ready": "Prêt à envoyer vos réponses ?",
21
+ "review.incomplete": "⚠ Répondez aux questions restantes avant d'envoyer :",
22
+
23
+ "preview.no_preview": "Aucun aperçu disponible",
24
+ "preview.notes_affordance": "Notes : appuyez sur n pour ajouter des notes",
25
+ "notes.header": "Notes :",
26
+
27
+ "editor.failed": "Échec de l’éditeur externe",
28
+
29
+ "rpc.multi_instructions": "Saisissez les numéros de toutes les options applicables, séparés par des virgules (p. ex. « 1,3 »), ou tapez une réponse libre en texte.",
30
+ "rpc.custom_answer_title": "Tapez votre réponse :"
31
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "_meta.notes": "Auto-translated draft. Native-speaker review welcome via PR. TUI symbols (↑/↓, ⚠) and key names (Enter, Esc, Tab, Space, n, Ctrl+G, Ctrl+U) are universal CLI conventions and stay untranslated.",
3
+
4
+ "sentinel.other": "Digite algo.",
5
+ "sentinel.next": "Próximo",
6
+
7
+ "submit.label": "Enviar respostas",
8
+ "submit.cancel": "Cancelar",
9
+
10
+ "hint.enter": "Enter para selecionar",
11
+ "hint.navigate": "↑/↓ para navegar",
12
+ "hint.newline": "Shift+Enter para nova linha",
13
+ "hint.clear": "Ctrl+U para limpar",
14
+ "hint.toggle": "Espaço para alternar",
15
+ "hint.notes": "n para adicionar notas",
16
+ "hint.tab": "Tab para trocar de pergunta",
17
+ "hint.cancel": "Esc para cancelar",
18
+
19
+ "review.heading": "Revise suas respostas",
20
+ "review.ready": "Pronto para enviar suas respostas?",
21
+ "review.incomplete": "⚠ Responda as perguntas restantes antes de enviar:",
22
+
23
+ "preview.no_preview": "Pré-visualização indisponível",
24
+ "preview.notes_affordance": "Notas: pressione n para adicionar notas",
25
+ "notes.header": "Notas:",
26
+
27
+ "editor.failed": "Falha no editor externo",
28
+
29
+ "rpc.multi_instructions": "Digite os números de todas as opções aplicáveis, separados por vírgula (ex.: \"1,3\") ou digite uma resposta própria em texto.",
30
+ "rpc.custom_answer_title": "Digite sua resposta:"
31
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "_meta.notes": "European Portuguese (Portugal). Auto-translated draft. Native-speaker review welcome via PR. Distinct from pt-BR (Brasil): differs in lexicon (utilizador / você), conjugation, and some terms. TUI symbols (↑/↓, ⚠) and key names (Enter, Esc, Tab, Space, n, Ctrl+G, Ctrl+U) are universal CLI conventions and stay untranslated.",
3
+
4
+ "sentinel.other": "Escreva algo.",
5
+ "sentinel.next": "Seguinte",
6
+
7
+ "submit.label": "Enviar respostas",
8
+ "submit.cancel": "Cancelar",
9
+
10
+ "hint.enter": "Enter para selecionar",
11
+ "hint.navigate": "↑/↓ para navegar",
12
+ "hint.newline": "Shift+Enter para nova linha",
13
+ "hint.clear": "Ctrl+U para limpar",
14
+ "hint.toggle": "Espaço para alternar",
15
+ "hint.notes": "n para adicionar notas",
16
+ "hint.tab": "Tab para mudar de pergunta",
17
+ "hint.cancel": "Esc para cancelar",
18
+
19
+ "review.heading": "Reveja as suas respostas",
20
+ "review.ready": "Pronto para enviar as suas respostas?",
21
+ "review.incomplete": "⚠ Responda às perguntas restantes antes de enviar:",
22
+
23
+ "preview.no_preview": "Pré-visualização indisponível",
24
+ "preview.notes_affordance": "Notas: prima n para adicionar notas",
25
+ "notes.header": "Notas:",
26
+
27
+ "editor.failed": "Falha no editor externo",
28
+
29
+ "rpc.multi_instructions": "Introduza os números de todas as opções aplicáveis, separados por vírgulas (ex.: \"1,3\") ou escreva uma resposta própria em texto.",
30
+ "rpc.custom_answer_title": "Escreva a sua resposta:"
31
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "_meta.notes": "Auto-translated draft. Native-speaker review welcome via PR. TUI symbols (↑/↓, ⚠) and key names (Enter, Esc, Tab, Space, n, Ctrl+G, Ctrl+U) are universal CLI conventions and stay untranslated.",
3
+
4
+ "sentinel.other": "Введите что-нибудь.",
5
+ "sentinel.next": "Далее",
6
+
7
+ "submit.label": "Отправить ответы",
8
+ "submit.cancel": "Отмена",
9
+
10
+ "hint.enter": "Enter — выбрать",
11
+ "hint.navigate": "↑/↓ — навигация",
12
+ "hint.newline": "Shift+Enter — новая строка",
13
+ "hint.clear": "Ctrl+U — очистить",
14
+ "hint.toggle": "Space — переключить",
15
+ "hint.notes": "n — добавить заметки",
16
+ "hint.tab": "Tab — следующий вопрос",
17
+ "hint.cancel": "Esc — отменить",
18
+
19
+ "review.heading": "Проверьте свои ответы",
20
+ "review.ready": "Готовы отправить ответы?",
21
+ "review.incomplete": "⚠ Ответьте на оставшиеся вопросы перед отправкой:",
22
+
23
+ "preview.no_preview": "Предпросмотр недоступен",
24
+ "preview.notes_affordance": "Заметки: нажмите n, чтобы добавить заметки",
25
+ "notes.header": "Заметки:",
26
+
27
+ "editor.failed": "Ошибка внешнего редактора",
28
+
29
+ "rpc.multi_instructions": "Введите номера всех подходящих вариантов через запятую (например, \"1,3\") или введите свой ответ текстом.",
30
+ "rpc.custom_answer_title": "Введите свой ответ:"
31
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "_meta.notes": "Auto-translated draft. Native-speaker review welcome via PR. TUI symbols (↑/↓, ⚠) and key names (Enter, Esc, Tab, Space, n, Ctrl+G, Ctrl+U) are universal CLI conventions and stay untranslated.",
3
+
4
+ "sentinel.other": "Введіть щось.",
5
+ "sentinel.next": "Далі",
6
+
7
+ "submit.label": "Надіслати відповіді",
8
+ "submit.cancel": "Скасувати",
9
+
10
+ "hint.enter": "Enter — обрати",
11
+ "hint.navigate": "↑/↓ — навігація",
12
+ "hint.newline": "Shift+Enter — новий рядок",
13
+ "hint.clear": "Ctrl+U — очистити",
14
+ "hint.toggle": "Space — перемкнути",
15
+ "hint.notes": "n — додати нотатки",
16
+ "hint.tab": "Tab — наступне питання",
17
+ "hint.cancel": "Esc — скасувати",
18
+
19
+ "review.heading": "Перегляньте свої відповіді",
20
+ "review.ready": "Готові надіслати відповіді?",
21
+ "review.incomplete": "⚠ Дайте відповіді на решту питань перед надсиланням:",
22
+
23
+ "preview.no_preview": "Попередній перегляд недоступний",
24
+ "preview.notes_affordance": "Нотатки: натисніть n, щоб додати нотатки",
25
+ "notes.header": "Нотатки:",
26
+
27
+ "editor.failed": "Помилка зовнішнього редактора",
28
+
29
+ "rpc.multi_instructions": "Введіть номери всіх відповідних варіантів через кому (наприклад, \"1,3\") або введіть власну відповідь текстом.",
30
+ "rpc.custom_answer_title": "Введіть свою відповідь:"
31
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "_meta.notes": "中文翻译。欢迎中文母语者通过 PR 审校。TUI 符号(↑/↓、⚠)为通用 CLI 约定,保持原文不译。Enter、Space 采用中文惯用说法(回车、空格);Esc、Tab、Ctrl、n 等保留原文。",
3
+
4
+ "sentinel.other": "输入内容",
5
+ "sentinel.next": "下一个",
6
+
7
+ "submit.label": "提交答案",
8
+ "submit.cancel": "取消",
9
+
10
+ "hint.enter": "回车选择",
11
+ "hint.navigate": "↑/↓ 导航",
12
+ "hint.newline": "Shift+Enter 换行",
13
+ "hint.clear": "Ctrl+U 清空",
14
+ "hint.toggle": "空格切换",
15
+ "hint.notes": "按 n 添加备注",
16
+ "hint.tab": "Tab 切换问题",
17
+ "hint.collapse": "{key} 折叠",
18
+ "hint.expand_line": "{key} 展开 · Esc 取消",
19
+ "hint.cancel": "Esc 取消",
20
+
21
+ "review.heading": "检查你的答案",
22
+ "review.ready": "准备提交答案了吗?",
23
+ "review.incomplete": "⚠ 提交前请先回答以下问题:",
24
+
25
+ "preview.no_preview": "暂无预览",
26
+ "preview.notes_affordance": "备注:按 n 添加",
27
+ "notes.header": "备注:",
28
+
29
+ "editor.failed": "外部编辑器启动失败",
30
+
31
+ "rpc.multi_instructions": "输入所有适用选项的编号,用逗号分隔(例如 \"1,3\"),或直接输入自定义回答。",
32
+ "rpc.custom_answer_title": "输入你的回答:"
33
+ }
@@ -0,0 +1,121 @@
1
+ # Configuration
2
+
3
+ Every setting the package reads, where the file lives, and what happens when a value is
4
+ wrong.
5
+
6
+ ## The config file
7
+
8
+ ```
9
+ ~/.config/rpiv-ask-user-question/config.json
10
+ ```
11
+
12
+ The file is optional — with no config at all, every setting takes its default. This
13
+ package only ever *reads* the file; it never creates, writes or chmods it, so its
14
+ permissions are whatever you give it.
15
+
16
+ A complete example:
17
+
18
+ ```json
19
+ {
20
+ "collapseKey": "alt+o",
21
+ "guidance": {
22
+ "description": "Ask the user structured questions whenever requirements are ambiguous.",
23
+ "promptSnippet": "Ask me before guessing on anything ambiguous",
24
+ "promptGuidelines": [
25
+ "Batch every clarifying question into one ask_user_question call.",
26
+ "Put your recommended option first and suffix it with (Recommended)."
27
+ ]
28
+ }
29
+ }
30
+ ```
31
+
32
+ ### Where the file is looked up
33
+
34
+ 1. `$XDG_CONFIG_HOME/rpiv-ask-user-question/config.json`, if `XDG_CONFIG_HOME` is set,
35
+ non-empty and absolute. A leading `~` is expanded first; a relative value is ignored.
36
+ Unset or ignored, the directory falls back to `~/.config`.
37
+ 2. If that file does not exist, the legacy path `~/.config/rpiv-ask-user-question/config.json`
38
+ is read. This path deliberately ignores `XDG_CONFIG_HOME`, so an existing config keeps
39
+ working after you set the variable.
40
+ 3. Neither present: all defaults.
41
+
42
+ If the XDG-path file exists, its result wins even when it is malformed — there is no
43
+ second chance at the legacy path.
44
+
45
+ ### When the file is invalid
46
+
47
+ Malformed JSON is not fatal. The loader warns on stderr and continues with defaults:
48
+
49
+ ```
50
+ rpiv-config: invalid JSON at <path>, using default ({}) — <parser message>
51
+ ```
52
+
53
+ Valid JSON that is not an object (a string, number, `null`, or an array) is rejected too,
54
+ falling back to defaults — but silently, with no warning. Individual keys with the wrong
55
+ type are likewise dropped back to their default without a warning.
56
+
57
+ ## Settings
58
+
59
+ | Setting | What it does | Default |
60
+ | --- | --- | --- |
61
+ | `collapseKey` | Key that collapses and expands the dialog overlay. | `"ctrl+]"` |
62
+ | `guidance.description` | Full text of the tool description the model sees. Replaces the built-in default entirely — no merging. | built-in description |
63
+ | `guidance.promptSnippet` | One-line snippet describing the tool in the system prompt. | built-in snippet |
64
+ | `guidance.promptGuidelines` | List of usage guidelines given to the model. | 4 built-in guidelines |
65
+
66
+ ### `collapseKey`
67
+
68
+ The value uses Pi's keybinding id format: zero or more distinct modifiers from `ctrl`,
69
+ `shift`, `alt`, `super`, joined by `+`, followed by a base key. Values are trimmed and
70
+ lowercased before matching.
71
+
72
+ The base key is either a single printable character from
73
+ `a-z 0-9 _ - ! @ # $ % ^ & * ( ) | ~ \` ' " : ; , . / < > ? [ ] { } = \`, or one of the
74
+ named keys `escape`, `esc`, `enter`, `return`, `tab`, `space`, `backspace`, `delete`,
75
+ `insert`, `clear`, `home`, `end`, `pageup`, `pagedown`, `up`, `down`, `left`, `right`,
76
+ `f1`–`f12`.
77
+
78
+ Examples that work: `"ctrl+]"`, `"alt+o"`, `"ctrl+shift+h"`, `"f9"`, `"ctrl+}"`.
79
+
80
+ Set `"off"` (any casing) to disable the collapse shortcut entirely — no raw terminal
81
+ listener is registered in that case.
82
+
83
+ A spec that does not match the grammar is rejected and the default is used. This is
84
+ strict on purpose: Pi's parser takes the last `+`-separated part as the key and ignores
85
+ unknown parts, so a typo like `"ctr+]"` would otherwise silently capture every bare `]`
86
+ keypress at the terminal level.
87
+
88
+ The footer hint inside the dialog names whatever key you configure (`Alt+O to collapse`
89
+ for `"alt+o"`), as do the collapsed one-line footer and the one-shot notification shown
90
+ when the dialog is first hidden. With `"off"` the collapse hint is dropped from the
91
+ footer entirely, since no shortcut can fire.
92
+
93
+ ### `guidance.description`, `guidance.promptSnippet` and `guidance.promptGuidelines`
94
+
95
+ `guidance.description` replaces the entire built-in description Pi registers for the
96
+ `ask_user_question` tool — the text the model reads when deciding how to use it. There is
97
+ no merging: a valid value wins wholesale. It is used only when it is a non-empty string;
98
+ anything else falls back to the built-in default. Like the other guidance fields it is read
99
+ once, when the extension registers the tool, so changes take effect on the next Pi restart.
100
+
101
+ These replace the text Pi puts in the system prompt about when to reach for
102
+ `ask_user_question`. Use them to make the model ask more or less often, or to enforce a
103
+ house style for options.
104
+
105
+ `promptSnippet` is used only when it is a non-empty string. `promptGuidelines` is used
106
+ only when it is a non-empty array whose entries are all non-empty strings. Anything else
107
+ falls back to the built-in defaults. Both are read once, when the extension registers the
108
+ tool, so changes take effect on the next Pi restart.
109
+
110
+ ## Environment variables
111
+
112
+ | Variable | Effect |
113
+ | --- | --- |
114
+ | `XDG_CONFIG_HOME` | Relocates the config directory, as described above. Must be absolute. |
115
+
116
+ `LANG` and `LC_ALL` influence the dialog language, but they are read by
117
+ [`@juicesharp/rpiv-i18n`](https://www.npmjs.com/package/@juicesharp/rpiv-i18n) rather than
118
+ by this package — see [localization.md](./localization.md).
119
+
120
+ No other environment variables are read. The package makes no model calls, so it needs no
121
+ API keys or model settings of its own.
package/docs/hosts.md ADDED
@@ -0,0 +1,97 @@
1
+ # Hosts and runtime behavior
2
+
3
+ Where the questionnaire renders, what it degrades to, and what happens when it cannot
4
+ render at all.
5
+
6
+ ## Three environments
7
+
8
+ | Environment | What the model sees | What you see |
9
+ | --- | --- | --- |
10
+ | Interactive terminal | `ask_user_question` in its tool list | The full tabbed TUI overlay |
11
+ | RPC / ACP host (VS Code pendant, Zed, Paseo) | `ask_user_question` in its tool list | A sequence of the host's own native select and input dialogs |
12
+ | Non-interactive run (no UI) | Nothing — the tool is removed | Nothing |
13
+
14
+ ### Terminal attention
15
+
16
+ After UI availability and questionnaire validation succeed, the package emits exactly one standard terminal BEL (`\x07`) immediately before the interactive wait begins. The signal is sent to `stdout` only when `process.stdout.isTTY` is true, so redirected output and non-TTY RPC streams stay untouched. A TTY-backed RPC dialog walker receives the same signal as the TUI path.
17
+
18
+ The BEL is best effort: if the synchronous terminal write fails, the questionnaire continues and its existing prompt/blocked lifecycle and result envelope are unchanged. The terminal configuration decides whether the BEL is audible, visual, or ignored. No BEL is emitted for missing UI, invalid questionnaires, or a failed TUI session load.
19
+
20
+ ### Non-interactive runs
21
+
22
+ A `before_agent_start` hook reconciles the active tool set against `ctx.hasUI` before every
23
+ turn. When there is no UI, `ask_user_question` is stripped from the list so the model never
24
+ sees a tool it cannot use — better than offering it and auto-declining every call. When UI
25
+ comes back, the tool is restored. The reconciler is idempotent and leaves sibling tools
26
+ untouched.
27
+
28
+ A second guard lives inside the tool handler as a one-turn backstop: if a call somehow
29
+ arrives without UI, it returns `error: "no_ui"` and the text
30
+ `Error: UI not available (running in non-interactive mode)`.
31
+
32
+ ### RPC and ACP hosts
33
+
34
+ RPC hosts report `hasUI: true` because Pi's dialog sub-protocol works there, but custom
35
+ terminal UI does not render. The package detects this two ways: hosts that advertise
36
+ `ctx.mode === "rpc"` route straight to the dialog walker, skipping the TUI import
37
+ entirely, and older RPC builds are caught by a backstop when custom UI resolves without
38
+ rendering anything. Either path requires the host to expose both `select` and `input`.
39
+
40
+ The walker asks one question per dialog and returns exactly the same result envelope the
41
+ TUI produces. Trade-offs inherent to the native primitives:
42
+
43
+ - No side-by-side preview pane. Previews are folded into the dialog title instead,
44
+ truncated at 600 characters each.
45
+ - No tab bar and no Submit review tab — one dialog per question, in order.
46
+ - No notes. Both note kinds — per-question `n` on a question tab and global `n` on the
47
+ Submit tab — are terminal-only; the host's native `select` and `input` primitives carry
48
+ no note field.
49
+ - Multi-select is a free-text input: type the option numbers, comma-separated
50
+ (`1,3`). Any token that is not a valid option index is treated as a typed custom answer,
51
+ which is how the `Type something.` escape survives. An empty input commits an empty
52
+ selection, matching `Next` with nothing toggled.
53
+ - Dismissing any dialog cancels the whole questionnaire, mirroring `Esc` in the TUI.
54
+
55
+ If the host can render neither custom UI nor dialogs, the call returns
56
+ `error: "no_custom_ui"` with text telling the model the user never saw the questions and
57
+ that it should ask them as plain chat text instead — explicitly not a decline.
58
+
59
+ ## Conditional surfaces
60
+
61
+ Some parts of the dialog exist only under the right conditions:
62
+
63
+ | Surface | Appears when |
64
+ | --- | --- |
65
+ | Tab bar and Submit tab | The call carries more than one question |
66
+ | `Next` row | The question is multi-select |
67
+ | `Type something.` row | Always |
68
+ | Side-by-side preview | An option carries a `preview`, and terminal and pane are both ≥ 100 columns |
69
+ | Preview pane at all | Single-select questions only |
70
+ | Collapse shortcut | `collapseKey` is not `"off"` |
71
+ | Full overlay hide on collapse | The host also exposes raw terminal input (the only path that can reopen a hidden overlay); without it, collapsing shrinks the dialog to a visible one-line row instead |
72
+ | Localized chrome | `@juicesharp/rpiv-i18n` is installed |
73
+
74
+ ## Loading and startup cost
75
+
76
+ The dialog's render graph costs roughly 560 ms to import, so it is loaded lazily — on the
77
+ first tool call, not when the extension registers. To keep that first call fast and safe,
78
+ the graph is also pre-warmed in the background two seconds after startup. The pre-warm
79
+ timer is unref'd, so it never holds a process open, and a failed pre-warm is swallowed:
80
+ the first real call re-imports and reports properly.
81
+
82
+ The pre-warm exists for a specific failure. Pi's module loader registers a module in its
83
+ graph cache *before* evaluating it and does not evict it if evaluation throws. If your
84
+ package manager replaces the dependency store while Pi is running, one failed import can
85
+ poison the cache for the rest of the process. Evaluating the graph early, while the paths
86
+ Pi resolved at boot still exist, keeps it in memory for the process lifetime and makes
87
+ that unreachable.
88
+
89
+ When it does happen, you get a structured envelope rather than a raw `TypeError`:
90
+
91
+ | `error` | Meaning | Fix |
92
+ | --- | --- | --- |
93
+ | `session_load_failed` | The dialog module could not be imported. | Repair the install if needed, then restart Pi. |
94
+ | `stale_module_cache` | The module cache went stale after an earlier failed import. | Restart Pi — this is unrecoverable in the running process. |
95
+
96
+ Both messages tell the model the questions were never shown and to ask them as plain chat
97
+ text instead of treating the failure as a decline.
@@ -0,0 +1,115 @@
1
+ # Keyboard and dialog layout
2
+
3
+ Every key the questionnaire dialog reacts to, the rows it appends for you, and how it
4
+ adapts to the size of your terminal.
5
+
6
+ ## Keys
7
+
8
+ | Key | What it does | Where it applies |
9
+ | --- | --- | --- |
10
+ | `↑` / `↓` | Move between rows. Wraps at both ends. | Option list, Submit picker |
11
+ | `Enter` | Toggle a multi-select checkbox (including custom text), confirm a single-select answer, close notes, or activate Next/Submit. | Everywhere |
12
+ | `Shift+Enter` | Insert a newline. | `Type something.` input, notes editor |
13
+ | `Esc` | Cancel the whole questionnaire. | Everywhere except the notes editor, where it closes notes |
14
+ | `Tab` / `Shift+Tab` | Next / previous tab, wrapping. `→` / `←` do the same. | Multi-question dialogs only |
15
+ | `Space` | Insert a normal space while typing; never toggle a checkbox. | Text inputs only |
16
+ | `n` | Open the notes editor for the focused question — or, on the Submit tab, the global note for the whole questionnaire. | Every question tab; the Submit tab in multi-question dialogs |
17
+ | `Ctrl+G` | Open Pi's configured external editor with the current custom-answer draft. | `Type something.` input |
18
+ | `Ctrl+U` | Clear the current custom-answer draft. | `Type something.` input |
19
+ | `Ctrl+]` | Collapse or expand the dialog. Configurable via `collapseKey`. | Everywhere, including while collapsed |
20
+
21
+ The table names the default keys; the dialog actually follows your Pi keybindings.
22
+ Confirm listens to both `tui.select.confirm` and `tui.input.submit`, and a key bound to
23
+ `tui.input.newLine` always inserts a newline even if it also matches confirm. So a
24
+ Slack-style configuration — `enter` folded into `tui.input.newLine`, submit moved to
25
+ `ctrl+enter` — keeps working: `enter` breaks lines, and your submit key confirms
26
+ everywhere `Enter` does.
27
+
28
+ In a multi-select question, `Enter` is the only checkbox toggle key. It does not
29
+ submit or move focus. Committing the question means focusing `Next`/`Submit` and
30
+ pressing `Enter`. Space does nothing outside text inputs.
31
+
32
+ Focusing the multi-select `Type something.` row allows immediate typing. Enter
33
+ selects or deselects its checkbox while retaining the draft and editor focus.
34
+ Space inserts an ordinary space. Typing or leaving the row does not automatically
35
+ select it; press Enter to include it. Unchecking keeps the text available to edit
36
+ or reselect later, but excludes it from the submitted answer.
37
+
38
+ ## The rows the dialog adds
39
+
40
+ | Row | Label | Appended to |
41
+ | --- | --- | --- |
42
+ | Custom answer | `Type something.` | Every question — single-select and multi-select, with or without previews |
43
+ | Commit | `Next` | Multi-select questions only |
44
+
45
+ Focusing `Type something.` switches the row into an inline multiline editor in
46
+ both single-select and multi-select questions. In preview mode it expands to the full pane width while you type, so a long custom answer is not
47
+ squeezed into the narrow options column. `Shift+Enter` inserts a line break; vertical
48
+ arrows move between lines and return to row navigation at the draft's top and bottom.
49
+ The draft replaces the static row label while you browse other options and is isolated
50
+ per question. `Ctrl+G` round-trips it through Pi's configured external editor; `Ctrl+U`
51
+ clears it, while `Esc` remains the explicit way to cancel the questionnaire. Confirming
52
+ it produces an answer of `kind: "custom"` in single-select mode. In multi-select
53
+ mode, checked custom text is included in the `answer` field of a `kind: "multi"`
54
+ answer alongside predefined `selected` labels; unchecked text remains only a draft
55
+ and is not submitted.
56
+
57
+ Both labels are reserved — the model cannot author an option that collides with them.
58
+ Both localize with the rest of the UI chrome; the reserved-label check always compares
59
+ against the canonical English strings.
60
+
61
+ ## Notes
62
+
63
+ `n` opens a notes editor on any question tab, whether the question is single- or
64
+ multi-select and whether or not its options carry previews. Notes are stored in a
65
+ side-band keyed by tab index, not inside the answer, so writing a note does not mark a
66
+ question as answered — the Submit tab still lists it as outstanding. The note merges into
67
+ the answer when you confirm it, and reaches the model as `user notes: <text>`.
68
+
69
+ On the Submit tab, `n` opens the global note editor instead — one note covering the whole
70
+ questionnaire. It lives outside every answer, so it survives tab switches and never marks
71
+ a question as answered; it reaches the model as `global note: <text>`, and submitting with
72
+ nothing but a global note still returns an answered result rather than a decline.
73
+
74
+ Inside the editor, `Shift+Enter` inserts a newline, while `Esc` and `Enter` close it; other
75
+ keystrokes edit the buffer, so `n` types an `n`. Pasted line breaks are preserved.
76
+
77
+ ## Collapse mode
78
+
79
+ `Ctrl+]` gets the dialog out of the way: the overlay is marked hidden in Pi's overlay
80
+ stack and shrinks to a single dim hint row, so the transcript it was covering becomes
81
+ readable and chat scrolling resumes. Press the same key to bring the questionnaire back
82
+ with your answers intact. The first time you collapse, Pi notifies you with the key to
83
+ press — that message names your configured key.
84
+
85
+ Because Pi routes no input to a hidden overlay, the collapse key is additionally captured
86
+ at the raw terminal level. It only acts when the questionnaire is hidden or focused, so a
87
+ different overlay on top of it (for example `/btw`) keeps its keystrokes.
88
+
89
+ While collapsed, every keystroke other than cancel is ignored, so you cannot mutate
90
+ answers you cannot see.
91
+
92
+ The default `ctrl+]` is free in Terminal.app, iTerm2, Warp, tmux, zellij and screen. On
93
+ keyboard layouts where `]` sits on the shifted layer — Latin American `es-AR` / `es-MX`,
94
+ among others — set a different `collapseKey`, or `"off"` to disable the shortcut.
95
+
96
+ ## Layout
97
+
98
+ Options render in a vertical list. When any option in a single-select question carries a
99
+ `preview`, the dialog splits into a side-by-side layout with the option list on the left
100
+ and a bordered monospace preview box on the right — but only when both the terminal and
101
+ the dialog pane are at least 100 columns wide. Below that, the preview stacks underneath
102
+ the options instead.
103
+
104
+ When the dialog is taller than the terminal, the body scrolls between a sticky heading and
105
+ a sticky footer, and an overflow indicator shows which direction is clipped: `↑` for
106
+ content above, `↓` for content below, `↕` for both.
107
+
108
+ The footer hint line adapts to context — it drops the notes hint and appends the
109
+ `Shift+Enter` newline hint whenever a text editor has the keyboard, with `Ctrl+U` still at
110
+ the far right for custom answers. It adds the tab hint only in multi-question dialogs. The
111
+ Submit tab follows the same idiom: its bottom hint row sits below the picker and carries
112
+ an `n to add a note` part that gives way to the `Shift+Enter` newline hint while the
113
+ global-note editor is open; a committed note shows as a `Note` entry in the review list.
114
+ `Ctrl+G` remains Pi's global external-editor shortcut and is not repeated there. On narrow
115
+ terminals the right edge clips with `…` so the core hints survive.
@@ -0,0 +1,87 @@
1
+ # Localization
2
+
3
+ How the dialog picks its language, which languages ship, and how to add one.
4
+
5
+ ## Enabling it
6
+
7
+ The extension works standalone: install only this package and you get the full dialog in
8
+ English. Localization needs one more install:
9
+
10
+ ```sh
11
+ pi install npm:@juicesharp/rpiv-i18n
12
+ ```
13
+
14
+ Restart your Pi session. If you installed through
15
+ [`@juicesharp/rpiv-pi`](https://www.npmjs.com/package/@juicesharp/rpiv-pi) and ran
16
+ `/rpiv-setup`, you already have it.
17
+
18
+ The dependency is a genuinely optional peer. This package loads the SDK inside a
19
+ `try`/`catch` at startup; when it is absent, every string falls back to its inline English
20
+ literal at the call site. There is no warning and no crash — the dialog simply renders in
21
+ English.
22
+
23
+ ## Picking a language
24
+
25
+ With the SDK installed, the locale resolves in this order, first hit wins:
26
+
27
+ 1. `pi --locale <code>` on the command line
28
+ 2. `~/.config/rpiv-i18n/locale.json`
29
+ 3. `LANG` / `LC_ALL` from the environment
30
+ 4. English
31
+
32
+ `/languages` opens an interactive picker and flips the strings live — the dialog reads its
33
+ strings at render time, so an open questionnaire follows the change. Both `/languages` and
34
+ `--locale` are registered by `@juicesharp/rpiv-i18n`, not by this package.
35
+
36
+ ## Shipped languages
37
+
38
+ Nine locale files ship in `locales/`:
39
+
40
+ | Code | Language |
41
+ | --- | --- |
42
+ | `de` | Deutsch |
43
+ | `en` | English |
44
+ | `es` | Español |
45
+ | `fr` | Français |
46
+ | `pt` | Português |
47
+ | `pt-BR` | Português (Brasil) |
48
+ | `ru` | Русский |
49
+ | `uk` | Українська |
50
+ | `zh` | 中文 |
51
+
52
+ ## What is translated, and what is not
53
+
54
+ Translation covers the chrome you read: the `Type something.` and `Next` sentinel rows,
55
+ the footer hint segments (including the collapsed-state line and the Submit tab's
56
+ `n to add a note` hint), the Submit picker labels, the review-tab heading with its
57
+ submit-readiness prompt, incomplete-answers warning, and committed-note `Note` label, the
58
+ preview pane's empty and notes-affordance lines, the notes header and the global-note
59
+ header on the Submit tab, the
60
+ external-editor failure notification, and the two RPC dialog prompts. Twenty-six keys in total,
61
+ all under the namespace `@juicesharp/rpiv-ask-user-question`.
62
+
63
+ Everything the *model* reads stays English by design: the tool description, the parameter
64
+ schema descriptions, error messages, and the reserved-label list. Those are prompt inputs,
65
+ not UI, and translating them would change model behavior rather than your reading
66
+ experience. It also means reserved-label validation compares against fixed English
67
+ strings, so `Type something.` is rejected as an authored option label in every locale.
68
+
69
+ ## Adding a language
70
+
71
+ No code change is required.
72
+
73
+ 1. Copy `locales/en.json` to `locales/<code>.json`, where `<code>` is a locale the SDK
74
+ supports.
75
+ 2. Translate the values. Keep every key, and keep the placeholders and leading symbols
76
+ (`↑/↓`, `⚠`) intact. `hint.collapse` and `hint.expand_line` contain a literal `{key}`
77
+ placeholder that is replaced at render time with the user's configured `collapseKey` —
78
+ keep it verbatim, positioned wherever your language's word order wants the key name.
79
+ 3. Restart Pi and select the language with `/languages`.
80
+
81
+ The loader iterates the SDK's supported-locale list over this directory at startup, so a
82
+ new file is picked up automatically. See the
83
+ [`@juicesharp/rpiv-i18n`](https://www.npmjs.com/package/@juicesharp/rpiv-i18n) README,
84
+ section "Contributing translations", for the full convention.
85
+
86
+ Keys are looked up individually with an English fallback, so a partially translated file
87
+ is safe — untranslated keys render in English rather than blank.