@selesai/code 0.3.9 → 0.4.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/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +4 -4
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts +4 -2
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js +1 -1
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +13 -25
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/core/agent-session.d.ts +22 -5
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +130 -49
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +13 -5
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/cache-stats.d.ts +49 -0
- package/dist/core/cache-stats.d.ts.map +1 -0
- package/dist/core/cache-stats.js +101 -0
- package/dist/core/cache-stats.js.map +1 -0
- package/dist/core/compaction/compaction.d.ts +1 -2
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +56 -81
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +10 -0
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +5 -3
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +38 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +41 -12
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +28 -1
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +3 -6
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-registry.d.ts +4 -6
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +34 -8
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +10 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +15 -18
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/package-manager.d.ts +4 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +68 -26
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/provider-attribution.d.ts.map +1 -1
- package/dist/core/provider-attribution.js +0 -10
- package/dist/core/provider-attribution.js.map +1 -1
- package/dist/core/resource-loader.d.ts +2 -2
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +8 -7
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +8 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +21 -2
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +104 -72
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +14 -3
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +29 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts +1 -0
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +3 -3
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/timings.d.ts +4 -2
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js +24 -14
- package/dist/core/timings.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +20 -5
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +2 -2
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +12 -25
- package/dist/core/tools/read.js.map +1 -1
- package/dist/defaults/models.json +2 -3
- package/dist/extensions/caveman/index.js +16 -1
- package/dist/extensions/caveman/test/extension.test.js +7 -4
- package/dist/extensions/caveman/test/helpers.test.js +12 -1
- package/dist/extensions/context-compaction-reminder.test.ts +82 -0
- package/dist/extensions/context-compaction-reminder.ts +28 -0
- package/dist/extensions/handoff-new.test.ts +2 -13
- package/dist/extensions/handoff-new.ts +16 -25
- package/dist/extensions/package.json +1 -0
- package/dist/extensions/pi-subagents/agents/architect.md +25 -173
- package/dist/extensions/pi-subagents/agents/builder.md +16 -105
- package/dist/extensions/pi-subagents/agents/commentator.md +19 -116
- package/dist/extensions/pi-subagents/agents/explorer.md +13 -33
- package/dist/extensions/pi-subagents/agents/recapper.md +19 -11
- package/dist/extensions/pi-subagents/agents/researcher.md +10 -33
- package/dist/extensions/question/constants.ts +0 -7
- package/dist/extensions/question/index.ts +11 -108
- package/dist/extensions/question/schemas.ts +1 -26
- package/dist/extensions/question/shortcuts.ts +5 -14
- package/dist/extensions/question/tests/question-list.test.ts +8 -0
- package/dist/extensions/question/tests/ui-protocol.test.ts +1 -7
- package/dist/extensions/question/tui-adapter.ts +3 -20
- package/dist/extensions/question/types.ts +0 -6
- package/dist/extensions/question/ui-protocol.ts +2 -17
- package/dist/extensions/workflow/adapter.ts +384 -124
- package/dist/extensions/workflow/extension.ts +2 -1
- package/dist/extensions/workflow/modes/prototype.ts +6 -6
- package/dist/extensions/workflow/modes/quick.ts +6 -6
- package/dist/extensions/workflow/modes/task.ts +79 -0
- package/dist/extensions/workflow/run-state.ts +124 -0
- package/dist/extensions/workflow/state-machine.ts +4 -8
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +2 -2
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +17 -4
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +3 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +23 -15
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts +34 -3
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +280 -25
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/custom-entry.d.ts +19 -0
- package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -0
- package/dist/modes/interactive/components/custom-entry.js +52 -0
- package/dist/modes/interactive/components/custom-entry.js.map +1 -0
- package/dist/modes/interactive/components/extension-editor.d.ts +3 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +12 -3
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +4 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +1 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts +3 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +17 -6
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +4 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +25 -2
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/status-indicator.d.ts +28 -0
- package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -0
- package/dist/modes/interactive/components/status-indicator.js +60 -0
- package/dist/modes/interactive/components/status-indicator.js.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +2 -1
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts +6 -2
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +19 -6
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +21 -10
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +417 -196
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/dark.json +1 -0
- package/dist/modes/interactive/theme/light.json +1 -0
- package/dist/modes/interactive/theme/theme-schema.json +6 -2
- package/dist/modes/interactive/theme/theme.d.ts +3 -2
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +10 -3
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +1 -1
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +21 -6
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +17 -3
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +20 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +26 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/package-manager-cli.d.ts +2 -2
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +71 -17
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/rpc-entry.d.ts +3 -0
- package/dist/rpc-entry.d.ts.map +1 -0
- package/dist/rpc-entry.js +10 -0
- package/dist/rpc-entry.js.map +1 -0
- package/dist/skills/workflow-creation/SKILL.md +72 -0
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +1 -1
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/image-convert.d.ts +1 -0
- package/dist/utils/image-convert.d.ts.map +1 -1
- package/dist/utils/image-convert.js +21 -15
- package/dist/utils/image-convert.js.map +1 -1
- package/dist/utils/image-process.d.ts +18 -0
- package/dist/utils/image-process.d.ts.map +1 -0
- package/dist/utils/image-process.js +83 -0
- package/dist/utils/image-process.js.map +1 -0
- package/dist/utils/mime.d.ts.map +1 -1
- package/dist/utils/mime.js +41 -0
- package/dist/utils/mime.js.map +1 -1
- package/docs/workflows.md +40 -6
- package/package.json +8 -5
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: recapper
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
description: Summarize what the current conversation is about and prepare a handoff document.
|
|
6
|
-
tools: read, grep, find, ls, bash, edit, write, intercom
|
|
3
|
+
description: Summarizes the current conversation and prepares a handoff document
|
|
4
|
+
tools: read, grep, find, ls
|
|
7
5
|
systemPromptMode: replace
|
|
8
6
|
inheritProjectContext: true
|
|
9
|
-
inheritSkills:
|
|
10
|
-
|
|
11
|
-
defaultContext: fork
|
|
7
|
+
inheritSkills: false
|
|
8
|
+
defaultContext: fork
|
|
12
9
|
output: handoff.md
|
|
13
10
|
---
|
|
14
11
|
|
|
15
|
-
|
|
12
|
+
Create a concise, self-contained handoff for a fresh agent. Use the inherited conversation, supplied artifacts, and relevant repository evidence. Do not edit project files or launch subagents. The configured output artifact is allowed.
|
|
16
13
|
|
|
17
|
-
Do not duplicate
|
|
14
|
+
Do not duplicate plans, ADRs, issues, commits, diffs, or other artifacts: reference them by exact path or URL. Redact secrets and personal data. If the task names a next focus, tailor the handoff to it.
|
|
18
15
|
|
|
19
|
-
|
|
16
|
+
Output:
|
|
20
17
|
|
|
21
|
-
|
|
18
|
+
# Handoff
|
|
19
|
+
|
|
20
|
+
## Goal and Current State
|
|
21
|
+
|
|
22
|
+
## Decisions and Constraints
|
|
23
|
+
|
|
24
|
+
## Evidence / Artifacts
|
|
25
|
+
- Exact paths and what each contains.
|
|
26
|
+
|
|
27
|
+
## Remaining Work
|
|
28
|
+
|
|
29
|
+
## Validation and Risks
|
|
@@ -1,53 +1,30 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: researcher
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
description: Autonomous web researcher — searches, evaluates, and synthesizes a focused research brief
|
|
6
|
-
tools: read, write, web_explore, intercom
|
|
3
|
+
description: Autonomous web researcher that produces a focused sourced brief
|
|
4
|
+
tools: read, web_explore
|
|
7
5
|
thinking: medium
|
|
8
6
|
systemPromptMode: replace
|
|
9
|
-
inheritProjectContext:
|
|
7
|
+
inheritProjectContext: false
|
|
10
8
|
inheritSkills: false
|
|
11
9
|
output: research.md
|
|
12
|
-
|
|
10
|
+
defaultContext: fresh
|
|
13
11
|
---
|
|
14
12
|
|
|
15
|
-
You are a research subagent.
|
|
13
|
+
You are a web research subagent. Answer the supplied question with a concise, well-sourced brief. Do not edit project files or launch subagents. The configured output artifact is allowed.
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
Use `web_explore` for focused search/fetch/source ranking. Start with 2–4 distinct angles, prioritize primary and official sources, then narrow follow-ups only for material gaps. Do not fetch URLs through shell commands.
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
- Break the problem into 2-4 distinct research angles.
|
|
21
|
-
- Use `web_explore` for each angle; it handles search, fetch, source ranking, and headless escalation internally. One call covers search + fetch + ranking.
|
|
22
|
-
- For follow-ups or gaps after the first pass, call `web_explore` again with a narrower query instead of fetching URLs manually.
|
|
23
|
-
- Prefer primary sources, official docs, specs, benchmarks, and direct evidence over commentary.
|
|
24
|
-
- Drop stale, redundant, or SEO-heavy sources.
|
|
25
|
-
- If the first explore pass leaves important gaps, explore again with tighter follow-up queries.
|
|
26
|
-
|
|
27
|
-
Search strategy:
|
|
28
|
-
- direct answer query
|
|
29
|
-
- authoritative source query
|
|
30
|
-
- practical experience or benchmark query
|
|
31
|
-
- recent developments query when the topic is time-sensitive
|
|
32
|
-
|
|
33
|
-
Output format:
|
|
17
|
+
Output:
|
|
34
18
|
|
|
35
19
|
# Research: [topic]
|
|
36
20
|
|
|
37
21
|
## Summary
|
|
38
|
-
2-3 sentence direct answer.
|
|
39
22
|
|
|
40
23
|
## Findings
|
|
41
|
-
|
|
42
|
-
1. **Finding** — explanation. [Source](url)
|
|
43
|
-
2. **Finding** — explanation. [Source](url)
|
|
24
|
+
1. **Finding** — evidence. [Source](url)
|
|
44
25
|
|
|
45
26
|
## Sources
|
|
46
|
-
- Kept:
|
|
47
|
-
- Dropped: Source Title — why it was excluded
|
|
27
|
+
- Kept: title (url) — relevance.
|
|
48
28
|
|
|
49
29
|
## Gaps
|
|
50
|
-
What
|
|
51
|
-
|
|
52
|
-
## Supervisor coordination
|
|
53
|
-
If runtime bridge instructions identify a safe supervisor target and you are blocked or need a decision, use `contact_supervisor` with `reason: "need_decision"` and wait for the reply. Use `reason: "progress_update"` only for meaningful progress or unexpected discoveries that change the plan. Do not send routine completion handoffs; return the completed research brief normally.
|
|
30
|
+
- What remains uncertain and the smallest next step.
|
|
@@ -3,14 +3,8 @@
|
|
|
3
3
|
import { Key } from "@earendil-works/pi-tui";
|
|
4
4
|
import type { ResolvedShortcut } from "./types.ts";
|
|
5
5
|
|
|
6
|
-
export const QUESTION_STATUS_KEY = "question";
|
|
7
6
|
export const QUESTION_VERSION = "1";
|
|
8
7
|
|
|
9
|
-
export const OVERLAY_MAX_HEIGHT_RATIO = 0.85;
|
|
10
|
-
export const OVERLAY_WIDTH: `${number}%` = "92%";
|
|
11
|
-
export const OVERLAY_OVERLAY_MAX_HEIGHT: `${number}%` = "85%";
|
|
12
|
-
export const OVERLAY_MIN_WIDTH = 40;
|
|
13
|
-
|
|
14
8
|
export const SPLIT_PANE_MIN_WIDTH = 84;
|
|
15
9
|
export const SPLIT_PANE_LEFT_MIN_WIDTH = 32;
|
|
16
10
|
export const SPLIT_PANE_RIGHT_MIN_WIDTH = 28;
|
|
@@ -19,7 +13,6 @@ export const SPLIT_PANE_SEPARATOR = " │ ";
|
|
|
19
13
|
export const FREEFORM_LABEL = "Type custom answer — enter a custom response";
|
|
20
14
|
export const COMMENT_TOGGLE_LABEL = "Add extra context after selection";
|
|
21
15
|
|
|
22
|
-
export const DEFAULT_OVERLAY_TOGGLE_KEY = "alt+o";
|
|
23
16
|
export const DEFAULT_COMMENT_TOGGLE_KEY = "ctrl+g";
|
|
24
17
|
|
|
25
18
|
export const BOX_BORDER_LEFT = "│ ";
|
|
@@ -11,23 +11,21 @@ import {
|
|
|
11
11
|
Key,
|
|
12
12
|
Markdown,
|
|
13
13
|
matchesKey,
|
|
14
|
-
type OverlayHandle,
|
|
15
14
|
Spacer,
|
|
16
15
|
Text,
|
|
17
16
|
type TUI,
|
|
18
17
|
truncateToWidth,
|
|
19
|
-
wrapTextWithAnsi,
|
|
20
18
|
} from "@earendil-works/pi-tui";
|
|
21
19
|
|
|
22
|
-
import { BOX_BORDER_LEFT, BOX_BORDER_OVERHEAD, BOX_BORDER_RIGHT,
|
|
20
|
+
import { BOX_BORDER_LEFT, BOX_BORDER_OVERHEAD, BOX_BORDER_RIGHT, QUESTION_VERSION } from "./constants.ts";
|
|
23
21
|
import { DialogFallback } from "./dialog-adapter.ts";
|
|
24
22
|
import { createFreeformResponse, createSelectionResponse, formatOptionsForMessage, formatResponseSummary, getOptionsFormatError, normalizeOptions, oneLine, safeMarkdownTheme } from "./helpers.ts";
|
|
25
23
|
import { QuestionList } from "./question-list.ts";
|
|
26
24
|
import { MultiSelect, SingleSelect } from "./selection-mode.ts";
|
|
27
25
|
import { resolveShortcuts } from "./shortcuts.ts";
|
|
28
26
|
import { QuestionParamsSchema } from "./schemas.ts";
|
|
29
|
-
import type {
|
|
30
|
-
import {
|
|
27
|
+
import type { QuestionDetails, QuestionMode, QuestionOption, QuestionResponse, RawOption, ResolvedShortcuts } from "./types.ts";
|
|
28
|
+
import { createTUIProtocol } from "./tui-adapter.ts";
|
|
31
29
|
import type { CustomFactory, UIProtocol } from "./ui-protocol.ts";
|
|
32
30
|
|
|
33
31
|
// ---------------------------------------------------------------------------
|
|
@@ -113,7 +111,6 @@ class QuestionComponent extends Container implements Component {
|
|
|
113
111
|
private allowMultiple: boolean,
|
|
114
112
|
private allowFreeform: boolean,
|
|
115
113
|
private allowComment: boolean,
|
|
116
|
-
private displayMode: DisplayMode,
|
|
117
114
|
private tui: TUI,
|
|
118
115
|
private theme: Theme,
|
|
119
116
|
private keybindings: KeybindingsManager,
|
|
@@ -183,14 +180,6 @@ class QuestionComponent extends Container implements Component {
|
|
|
183
180
|
|
|
184
181
|
override render(width: number): string[] {
|
|
185
182
|
const innerWidth = Math.max(1, width - BOX_BORDER_OVERHEAD);
|
|
186
|
-
|
|
187
|
-
if (this.mode === "select" && !this.allowMultiple) {
|
|
188
|
-
const overlayMaxHeight = Math.max(12, Math.floor(this.tui.terminal.rows * OVERLAY_MAX_HEIGHT_RATIO));
|
|
189
|
-
const staticLines = this.countStaticLines(innerWidth);
|
|
190
|
-
const availableOptionRows = Math.max(4, overlayMaxHeight - staticLines);
|
|
191
|
-
this.ensureList().setMaxVisibleRows(availableOptionRows);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
183
|
const rawLines = super.render(innerWidth);
|
|
195
184
|
const borderColor = (s: string) => this.theme.fg("accent", s);
|
|
196
185
|
const titleColor = (s: string) => this.theme.fg("dim", this.theme.bold(s));
|
|
@@ -203,20 +192,6 @@ class QuestionComponent extends Container implements Component {
|
|
|
203
192
|
});
|
|
204
193
|
}
|
|
205
194
|
|
|
206
|
-
private countWrappedLines(text: string, width: number): number {
|
|
207
|
-
return Math.max(1, wrapTextWithAnsi(text, Math.max(10, width - 2)).length);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
private countStaticLines(width: number): number {
|
|
211
|
-
const titleLines = 1;
|
|
212
|
-
const questionLines = this.countWrappedLines(this.question, width);
|
|
213
|
-
const contextLines = this.context ? 1 + this.countWrappedLines(this.context, width) : 0;
|
|
214
|
-
const helpLines = 1;
|
|
215
|
-
const borderLines = 2;
|
|
216
|
-
const spacerLines = this.context ? 6 : 5;
|
|
217
|
-
return borderLines + spacerLines + titleLines + questionLines + contextLines + helpLines;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
195
|
private updateStaticText(): void {
|
|
221
196
|
const theme = this.theme;
|
|
222
197
|
const title = this.mode === "comment" ? "Optional comment" : "Question";
|
|
@@ -235,10 +210,6 @@ class QuestionComponent extends Container implements Component {
|
|
|
235
210
|
private updateHelpText(): void {
|
|
236
211
|
const theme = this.theme;
|
|
237
212
|
const kb = this.keybindings;
|
|
238
|
-
const overlayHint =
|
|
239
|
-
this.displayMode === "overlay" && !this.shortcuts.overlayToggle.disabled
|
|
240
|
-
? `${theme.fg("dim", this.shortcuts.overlayToggle.spec)}${theme.fg("muted", " hide")}`
|
|
241
|
-
: null;
|
|
242
213
|
const commentHint =
|
|
243
214
|
this.allowComment && !this.shortcuts.commentToggle.disabled
|
|
244
215
|
? `${theme.fg("dim", this.shortcuts.commentToggle.spec)}${theme.fg("muted", " toggle context")}`
|
|
@@ -249,7 +220,6 @@ class QuestionComponent extends Container implements Component {
|
|
|
249
220
|
`${theme.fg("dim", kb.getKeys("tui.input.submit").join("/"))}${theme.fg("muted", this.mode === "comment" ? " submit/skip" : " submit")}`,
|
|
250
221
|
`${theme.fg("dim", kb.getKeys("tui.input.newLine").join("/"))}${theme.fg("muted", " newline")}`,
|
|
251
222
|
`${theme.fg("dim", "esc")}${theme.fg("muted", " back")}`,
|
|
252
|
-
overlayHint,
|
|
253
223
|
]
|
|
254
224
|
.filter((h): h is string => !!h)
|
|
255
225
|
.join(" • ");
|
|
@@ -262,7 +232,6 @@ class QuestionComponent extends Container implements Component {
|
|
|
262
232
|
`${theme.fg("dim", "↑↓")}${theme.fg("muted", " navigate")}`,
|
|
263
233
|
`${theme.fg("dim", "space")}${theme.fg("muted", " toggle")}`,
|
|
264
234
|
commentHint,
|
|
265
|
-
overlayHint,
|
|
266
235
|
`${theme.fg("dim", kb.getKeys("tui.select.confirm").join("/"))}${theme.fg("muted", " submit")}`,
|
|
267
236
|
`${theme.fg("dim", kb.getKeys("tui.select.cancel").join("/"))}${theme.fg("muted", " cancel")}`,
|
|
268
237
|
]
|
|
@@ -275,7 +244,6 @@ class QuestionComponent extends Container implements Component {
|
|
|
275
244
|
`${theme.fg("dim", kb.getKeys("tui.editor.deleteCharBackward").join("/"))}${theme.fg("muted", " erase")}`,
|
|
276
245
|
`${theme.fg("dim", "↑↓")}${theme.fg("muted", " navigate")}`,
|
|
277
246
|
commentHint,
|
|
278
|
-
overlayHint,
|
|
279
247
|
`${theme.fg("dim", kb.getKeys("tui.select.confirm").join("/"))}${theme.fg("muted", " select")}`,
|
|
280
248
|
`${theme.fg("dim", "esc")}${theme.fg("muted", " clear/cancel")}`,
|
|
281
249
|
]
|
|
@@ -433,13 +401,7 @@ export default function questionExtension(pi: ExtensionAPI) {
|
|
|
433
401
|
const allowComment = params.allowComment ?? false;
|
|
434
402
|
const timeout = params.timeout as number | undefined;
|
|
435
403
|
|
|
436
|
-
const envMode = process.env.PI_QUESTION_DISPLAY_MODE;
|
|
437
|
-
const envDisplayMode: DisplayMode | undefined = envMode === "overlay" || envMode === "inline" ? envMode : undefined;
|
|
438
|
-
const effectiveDisplayMode: DisplayMode = (params.displayMode as DisplayMode | undefined) ?? envDisplayMode ?? "overlay";
|
|
439
|
-
|
|
440
404
|
const shortcuts: ResolvedShortcuts = resolveShortcuts(
|
|
441
|
-
params.overlayToggleKey as string | null | undefined,
|
|
442
|
-
process.env.PI_QUESTION_OVERLAY_TOGGLE_KEY,
|
|
443
405
|
params.commentToggleKey as string | null | undefined,
|
|
444
406
|
process.env.PI_QUESTION_COMMENT_TOGGLE_KEY,
|
|
445
407
|
);
|
|
@@ -473,42 +435,12 @@ export default function questionExtension(pi: ExtensionAPI) {
|
|
|
473
435
|
};
|
|
474
436
|
}
|
|
475
437
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
if (options.length === 0) {
|
|
483
|
-
if (!allowFreeform) {
|
|
484
|
-
protocol.setStatus(QUESTION_STATUS_KEY, protocol.theme.fg("error", "question failed"));
|
|
485
|
-
return {
|
|
486
|
-
content: [{ type: "text", text: "Question options are empty and allowFreeform is false." }],
|
|
487
|
-
isError: true,
|
|
488
|
-
details: { ...detailsBase, response: null, cancelled: true } satisfies QuestionDetails,
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
try {
|
|
492
|
-
const prompt = normalizedContext ? `${rawQuestion}\n\nContext:\n${normalizedContext}` : rawQuestion;
|
|
493
|
-
const answer = await protocol.input(prompt, "Type your answer...", timeout ? { timeout } : undefined);
|
|
494
|
-
const response = createFreeformResponse(answer);
|
|
495
|
-
if (!response) {
|
|
496
|
-
protocol.setStatus(QUESTION_STATUS_KEY, protocol.theme.fg("warning", "question cancelled"));
|
|
497
|
-
return {
|
|
498
|
-
content: [{ type: "text", text: "User cancelled the question" }],
|
|
499
|
-
details: { ...detailsBase, response: null, cancelled: true } satisfies QuestionDetails,
|
|
500
|
-
};
|
|
501
|
-
}
|
|
502
|
-
pi.events.emit("question:answered", { question: rawQuestion, context: normalizedContext, response });
|
|
503
|
-
protocol.setStatus(QUESTION_STATUS_KEY, protocol.theme.fg("success", "question answered"));
|
|
504
|
-
return {
|
|
505
|
-
content: [{ type: "text", text: `User answered: ${formatResponseSummary(response)}` }],
|
|
506
|
-
details: { ...detailsBase, response, cancelled: false } satisfies QuestionDetails,
|
|
507
|
-
};
|
|
508
|
-
} catch (error) {
|
|
509
|
-
protocol.setStatus(QUESTION_STATUS_KEY, protocol.theme.fg("error", "question failed"));
|
|
510
|
-
throw error;
|
|
511
|
-
}
|
|
438
|
+
if (options.length === 0 && !allowFreeform) {
|
|
439
|
+
return {
|
|
440
|
+
content: [{ type: "text", text: "Question options are empty and allowFreeform is false." }],
|
|
441
|
+
isError: true,
|
|
442
|
+
details: { ...detailsBase, response: null, cancelled: true } satisfies QuestionDetails,
|
|
443
|
+
};
|
|
512
444
|
}
|
|
513
445
|
|
|
514
446
|
onUpdate?.({
|
|
@@ -517,9 +449,6 @@ export default function questionExtension(pi: ExtensionAPI) {
|
|
|
517
449
|
});
|
|
518
450
|
|
|
519
451
|
let result: QuestionResponse | null;
|
|
520
|
-
let overlayHandle: OverlayHandle | undefined;
|
|
521
|
-
let removeOverlayInputListener: (() => void) | undefined;
|
|
522
|
-
let hasAnnouncedHide = false;
|
|
523
452
|
|
|
524
453
|
try {
|
|
525
454
|
const customFactory: CustomFactory = (
|
|
@@ -541,7 +470,6 @@ export default function questionExtension(pi: ExtensionAPI) {
|
|
|
541
470
|
allowMultiple,
|
|
542
471
|
allowFreeform,
|
|
543
472
|
allowComment,
|
|
544
|
-
effectiveDisplayMode,
|
|
545
473
|
tui,
|
|
546
474
|
theme,
|
|
547
475
|
keybindings,
|
|
@@ -550,51 +478,27 @@ export default function questionExtension(pi: ExtensionAPI) {
|
|
|
550
478
|
);
|
|
551
479
|
};
|
|
552
480
|
|
|
553
|
-
const
|
|
554
|
-
if (
|
|
555
|
-
effectiveDisplayMode === "overlay" &&
|
|
556
|
-
!overlayToggle.disabled &&
|
|
557
|
-
typeof protocol.onTerminalInput === "function"
|
|
558
|
-
) {
|
|
559
|
-
removeOverlayInputListener = protocol.onTerminalInput((data) => {
|
|
560
|
-
if (!overlayToggle.matches(data) || !overlayHandle) return undefined;
|
|
561
|
-
const nextHidden = !overlayHandle.isHidden();
|
|
562
|
-
overlayHandle.setHidden(nextHidden);
|
|
563
|
-
if (nextHidden && !hasAnnouncedHide) {
|
|
564
|
-
hasAnnouncedHide = true;
|
|
565
|
-
protocol.notify?.(`question hidden — press ${overlayToggle.spec} to reopen`, "info");
|
|
566
|
-
}
|
|
567
|
-
return { consume: true };
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
const customResult = await protocol.custom(customFactory, buildCustomUIOptions(effectiveDisplayMode, (handle) => {
|
|
572
|
-
overlayHandle = handle;
|
|
573
|
-
}));
|
|
481
|
+
const customResult = await protocol.custom(customFactory);
|
|
574
482
|
|
|
575
483
|
if (customResult !== undefined) {
|
|
576
484
|
result = customResult;
|
|
577
485
|
} else {
|
|
578
486
|
// RPC/headless: custom() returns undefined — degrade to dialog protocol.
|
|
579
487
|
result = await DialogFallback.ask(
|
|
580
|
-
{ question: rawQuestion, context: normalizedContext, options, allowMultiple, allowFreeform, allowComment,
|
|
488
|
+
{ question: rawQuestion, context: normalizedContext, options, allowMultiple, allowFreeform, allowComment, shortcuts, timeout },
|
|
581
489
|
protocol,
|
|
582
490
|
);
|
|
583
491
|
}
|
|
584
492
|
} catch (error) {
|
|
585
493
|
const message = error instanceof Error ? `${error.message}\n${error.stack ?? ""}` : String(error);
|
|
586
|
-
protocol.setStatus(QUESTION_STATUS_KEY, protocol.theme.fg("error", "question failed"));
|
|
587
494
|
return {
|
|
588
495
|
content: [{ type: "text", text: `Question tool failed: ${message}` }],
|
|
589
496
|
isError: true,
|
|
590
497
|
details: { error: message } as unknown as QuestionDetails,
|
|
591
498
|
};
|
|
592
|
-
} finally {
|
|
593
|
-
removeOverlayInputListener?.();
|
|
594
499
|
}
|
|
595
500
|
|
|
596
501
|
if (result === null) {
|
|
597
|
-
protocol.setStatus(QUESTION_STATUS_KEY, protocol.theme.fg("warning", "question cancelled"));
|
|
598
502
|
pi.events.emit("question:cancelled", { question: rawQuestion, context: normalizedContext, options });
|
|
599
503
|
return {
|
|
600
504
|
content: [{ type: "text", text: "User cancelled the question" }],
|
|
@@ -602,7 +506,6 @@ export default function questionExtension(pi: ExtensionAPI) {
|
|
|
602
506
|
};
|
|
603
507
|
}
|
|
604
508
|
|
|
605
|
-
protocol.setStatus(QUESTION_STATUS_KEY, protocol.theme.fg("success", "question answered"));
|
|
606
509
|
pi.events.emit("question:answered", { question: rawQuestion, context: normalizedContext, response: result });
|
|
607
510
|
return {
|
|
608
511
|
content: [{ type: "text", text: `User answered: ${formatResponseSummary(result)}` }],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Typebox schemas for the question tool parameters.
|
|
2
2
|
|
|
3
|
-
import { Type
|
|
3
|
+
import { Type } from "typebox";
|
|
4
4
|
|
|
5
5
|
const OptionSchema = Type.Union([
|
|
6
6
|
Type.String({ description: "Option label" }),
|
|
@@ -10,19 +10,6 @@ const OptionSchema = Type.Union([
|
|
|
10
10
|
}),
|
|
11
11
|
]);
|
|
12
12
|
|
|
13
|
-
/** Flat { type: "string", enum: [...] } instead of anyOf — Google function-calling rejects unions. */
|
|
14
|
-
export function StringEnum<const T extends readonly string[]>(
|
|
15
|
-
values: T,
|
|
16
|
-
options?: { description?: string; default?: T[number] },
|
|
17
|
-
): TUnsafe<T[number]> {
|
|
18
|
-
return Type.Unsafe<T[number]>({
|
|
19
|
-
type: "string",
|
|
20
|
-
enum: [...values],
|
|
21
|
-
...(options?.description ? { description: options.description } : {}),
|
|
22
|
-
...(options?.default !== undefined ? { default: options.default } : {}),
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
|
|
26
13
|
export const QuestionParamsSchema = Type.Object({
|
|
27
14
|
question: Type.String({ description: "Question to ask the user before continuing" }),
|
|
28
15
|
title: Type.Optional(Type.String({ description: "Short title for the question UI (deprecated alias of context summary)" })),
|
|
@@ -45,18 +32,6 @@ export const QuestionParamsSchema = Type.Object({
|
|
|
45
32
|
allowComment: Type.Optional(
|
|
46
33
|
Type.Boolean({ description: "Collect an optional comment after selecting one or more options. Default: false" }),
|
|
47
34
|
),
|
|
48
|
-
displayMode: Type.Optional(
|
|
49
|
-
StringEnum(["overlay", "inline"] as const, {
|
|
50
|
-
description:
|
|
51
|
-
"UI rendering mode. 'overlay' shows a centered modal, 'inline' renders in-place. Default: PI_QUESTION_DISPLAY_MODE env var if set, otherwise 'overlay'.",
|
|
52
|
-
}),
|
|
53
|
-
),
|
|
54
|
-
overlayToggleKey: Type.Optional(
|
|
55
|
-
Type.String({
|
|
56
|
-
description:
|
|
57
|
-
"Shortcut for hiding/showing the overlay popup (overlay mode only), e.g. 'alt+o'. Pass 'off' to disable. Default: PI_QUESTION_OVERLAY_TOGGLE_KEY env var if set, otherwise 'alt+o'.",
|
|
58
|
-
}),
|
|
59
|
-
),
|
|
60
35
|
commentToggleKey: Type.Optional(
|
|
61
36
|
Type.String({
|
|
62
37
|
description:
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
// Shortcut resolution for
|
|
1
|
+
// Shortcut resolution for the optional-comment toggle key.
|
|
2
2
|
|
|
3
3
|
import { matchesKey } from "@earendil-works/pi-tui";
|
|
4
4
|
|
|
5
|
-
import { DISABLED_SHORTCUT, SHORTCUT_DISABLE_VALUES } from "./constants.ts";
|
|
5
|
+
import { DEFAULT_COMMENT_TOGGLE_KEY, DISABLED_SHORTCUT, SHORTCUT_DISABLE_VALUES } from "./constants.ts";
|
|
6
6
|
import type { ResolvedShortcut, ResolvedShortcuts } from "./types.ts";
|
|
7
|
-
import { DEFAULT_COMMENT_TOGGLE_KEY, DEFAULT_OVERLAY_TOGGLE_KEY } from "./constants.ts";
|
|
8
7
|
|
|
9
8
|
export function normalizeShortcutSpec(value: string | null | undefined): string | null | undefined {
|
|
10
9
|
if (value === undefined) return undefined;
|
|
@@ -40,14 +39,6 @@ export function resolveShortcut(
|
|
|
40
39
|
return DISABLED_SHORTCUT;
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
export function resolveShortcuts(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
commentToggleKey: string | null | undefined,
|
|
47
|
-
commentEnv: string | undefined,
|
|
48
|
-
): ResolvedShortcuts {
|
|
49
|
-
return {
|
|
50
|
-
overlayToggle: resolveShortcut(overlayToggleKey, overlayEnv, DEFAULT_OVERLAY_TOGGLE_KEY),
|
|
51
|
-
commentToggle: resolveShortcut(commentToggleKey, commentEnv, DEFAULT_COMMENT_TOGGLE_KEY),
|
|
52
|
-
};
|
|
53
|
-
}
|
|
42
|
+
export function resolveShortcuts(commentToggleKey: string | null | undefined, commentEnv: string | undefined): ResolvedShortcuts {
|
|
43
|
+
return { commentToggle: resolveShortcut(commentToggleKey, commentEnv, DEFAULT_COMMENT_TOGGLE_KEY) };
|
|
44
|
+
}
|
|
@@ -86,6 +86,14 @@ test("SingleSelect: confirm on freeform row → onEnterFreeform", () => {
|
|
|
86
86
|
assert.equal(called, true);
|
|
87
87
|
});
|
|
88
88
|
|
|
89
|
+
test("SingleSelect: no options with freeform → opens custom response", () => {
|
|
90
|
+
const list = new QuestionList([], SingleSelect, true, false, fakeTheme, fakeKeybindings(["tui.select.confirm"]), disabledShortcut);
|
|
91
|
+
let called = false;
|
|
92
|
+
list.onEnterFreeform = () => (called = true);
|
|
93
|
+
list.handleInput(ENTER);
|
|
94
|
+
assert.equal(called, true);
|
|
95
|
+
});
|
|
96
|
+
|
|
89
97
|
test("SingleSelect: confirm on comment-toggle → toggles comment, no submit", () => {
|
|
90
98
|
const list = new QuestionList(opts(["a"]), SingleSelect, false, true, fakeTheme, fakeKeybindings(["tui.select.confirm"]), disabledShortcut);
|
|
91
99
|
let submitted = false;
|
|
@@ -23,8 +23,6 @@ function fakeProtocol(overrides: Partial<UIProtocol> = {}): UIProtocol {
|
|
|
23
23
|
calls.push({ method: "input", args: [prompt, placeholder, opts] });
|
|
24
24
|
return overrides.input?.(prompt, placeholder, opts);
|
|
25
25
|
},
|
|
26
|
-
setStatus: () => {},
|
|
27
|
-
notify: () => {},
|
|
28
26
|
...overrides,
|
|
29
27
|
} as UIProtocol;
|
|
30
28
|
}
|
|
@@ -37,11 +35,7 @@ function makeParams(overrides: Partial<ResolvedQuestionParams> = {}): ResolvedQu
|
|
|
37
35
|
allowMultiple: false,
|
|
38
36
|
allowFreeform: false,
|
|
39
37
|
allowComment: false,
|
|
40
|
-
|
|
41
|
-
shortcuts: {
|
|
42
|
-
overlayToggle: { disabled: true, spec: null, matches: () => false },
|
|
43
|
-
commentToggle: { disabled: true, spec: null, matches: () => false },
|
|
44
|
-
},
|
|
38
|
+
shortcuts: { commentToggle: { disabled: true, spec: null, matches: () => false } },
|
|
45
39
|
timeout: undefined,
|
|
46
40
|
...overrides,
|
|
47
41
|
};
|
|
@@ -1,33 +1,16 @@
|
|
|
1
1
|
// TUI protocol adapter — wraps ctx.ui into UIProtocol.
|
|
2
2
|
|
|
3
3
|
import type { ExtensionContext } from "@selesai/code";
|
|
4
|
-
import type { OverlayHandle } from "@earendil-works/pi-tui";
|
|
5
4
|
|
|
6
|
-
import {
|
|
7
|
-
import type {
|
|
8
|
-
import type { CustomUIOptions, UIProtocol } from "./ui-protocol.ts";
|
|
9
|
-
|
|
10
|
-
export function buildCustomUIOptions(
|
|
11
|
-
displayMode: DisplayMode,
|
|
12
|
-
onHandle?: (handle: OverlayHandle) => void,
|
|
13
|
-
): CustomUIOptions | undefined {
|
|
14
|
-
if (displayMode === "inline") return undefined;
|
|
15
|
-
return {
|
|
16
|
-
overlay: true,
|
|
17
|
-
overlayOptions: { anchor: "center", width: OVERLAY_WIDTH, minWidth: OVERLAY_MIN_WIDTH, maxHeight: OVERLAY_OVERLAY_MAX_HEIGHT, margin: 1 },
|
|
18
|
-
...(onHandle ? { onHandle } : {}),
|
|
19
|
-
};
|
|
20
|
-
}
|
|
5
|
+
import type { QuestionResponse } from "./types.ts";
|
|
6
|
+
import type { UIProtocol } from "./ui-protocol.ts";
|
|
21
7
|
|
|
22
8
|
export function createTUIProtocol(ctx: ExtensionContext): UIProtocol {
|
|
23
9
|
return {
|
|
24
10
|
hasUI: ctx.hasUI,
|
|
25
11
|
theme: ctx.ui.theme,
|
|
26
|
-
custom: (factory
|
|
12
|
+
custom: (factory) => ctx.ui.custom(factory as any) as Promise<QuestionResponse | null | undefined>,
|
|
27
13
|
select: (prompt, options, opts) => ctx.ui.select(prompt, options, opts),
|
|
28
14
|
input: (prompt, placeholder, opts) => ctx.ui.input(prompt, placeholder, opts),
|
|
29
|
-
onTerminalInput: typeof ctx.ui.onTerminalInput === "function" ? ctx.ui.onTerminalInput.bind(ctx.ui) : undefined,
|
|
30
|
-
setStatus: (key, text) => ctx.ui.setStatus(key, text),
|
|
31
|
-
notify: (msg, level) => ctx.ui.notify(msg, level),
|
|
32
15
|
};
|
|
33
16
|
}
|
|
@@ -21,8 +21,6 @@ export interface QuestionDetails {
|
|
|
21
21
|
cancelled: boolean;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export type DisplayMode = "overlay" | "inline";
|
|
25
|
-
|
|
26
24
|
export interface QuestionParams {
|
|
27
25
|
question: string;
|
|
28
26
|
context?: string;
|
|
@@ -30,8 +28,6 @@ export interface QuestionParams {
|
|
|
30
28
|
allowMultiple?: boolean;
|
|
31
29
|
allowFreeform?: boolean;
|
|
32
30
|
allowComment?: boolean;
|
|
33
|
-
displayMode?: DisplayMode;
|
|
34
|
-
overlayToggleKey?: string | null;
|
|
35
31
|
commentToggleKey?: string | null;
|
|
36
32
|
timeout?: number;
|
|
37
33
|
}
|
|
@@ -45,7 +41,6 @@ export type ResolvedShortcut =
|
|
|
45
41
|
| { disabled: true; spec: null; matches: (data: string) => false };
|
|
46
42
|
|
|
47
43
|
export interface ResolvedShortcuts {
|
|
48
|
-
overlayToggle: ResolvedShortcut;
|
|
49
44
|
commentToggle: ResolvedShortcut;
|
|
50
45
|
}
|
|
51
46
|
|
|
@@ -96,7 +91,6 @@ export interface ResolvedQuestionParams {
|
|
|
96
91
|
allowMultiple: boolean;
|
|
97
92
|
allowFreeform: boolean;
|
|
98
93
|
allowComment: boolean;
|
|
99
|
-
displayMode: DisplayMode;
|
|
100
94
|
shortcuts: ResolvedShortcuts;
|
|
101
95
|
timeout: number | undefined;
|
|
102
96
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// UIProtocol port — abstracts TUI vs dialog/RPC transport.
|
|
2
2
|
|
|
3
3
|
import type { KeybindingsManager } from "@selesai/code";
|
|
4
|
-
import type {
|
|
4
|
+
import type { Theme, TUI } from "@earendil-works/pi-tui";
|
|
5
5
|
|
|
6
6
|
import type { QuestionResponse, ResolvedQuestionParams } from "./types.ts";
|
|
7
7
|
|
|
@@ -18,27 +18,12 @@ export type CustomFactory = (
|
|
|
18
18
|
done: (result: QuestionResponse | null) => void,
|
|
19
19
|
) => CustomFactoryResult;
|
|
20
20
|
|
|
21
|
-
export interface CustomUIOptions {
|
|
22
|
-
overlay?: boolean;
|
|
23
|
-
overlayOptions?: {
|
|
24
|
-
anchor: string;
|
|
25
|
-
width: string;
|
|
26
|
-
minWidth: number;
|
|
27
|
-
maxHeight: string;
|
|
28
|
-
margin: number;
|
|
29
|
-
};
|
|
30
|
-
onHandle?: (handle: OverlayHandle) => void;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
21
|
export interface UIProtocol {
|
|
34
22
|
readonly hasUI: boolean;
|
|
35
23
|
readonly theme: Theme;
|
|
36
|
-
custom(factory: CustomFactory
|
|
24
|
+
custom(factory: CustomFactory): Promise<QuestionResponse | null | undefined>;
|
|
37
25
|
select(prompt: string, options: string[], opts?: { timeout?: number }): Promise<string | undefined>;
|
|
38
26
|
input(prompt: string, placeholder?: string, opts?: { timeout?: number }): Promise<string | undefined>;
|
|
39
|
-
onTerminalInput?(handler: (data: string) => { consume: boolean } | void): () => void;
|
|
40
|
-
setStatus(key: string, text: string): void;
|
|
41
|
-
notify(message: string, level: "info" | "warning" | "error"): void;
|
|
42
27
|
}
|
|
43
28
|
|
|
44
29
|
export interface FallbackProtocol {
|