@mrclrchtr/supi-ask-user 1.7.0 → 1.8.1

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/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ ![SuPi](assets/logo.png)
2
+
1
3
  # @mrclrchtr/supi-ask-user
2
4
 
3
5
  Adds a redesigned `ask_user` tool to the [pi coding agent](https://github.com/earendil-works/pi). It lets the model pause and request a small decision form when explicit human input is required.
@@ -14,7 +16,9 @@ For local development:
14
16
  pi install ./packages/supi-ask-user
15
17
  ```
16
18
 
17
- After editing the source, run `/reload`.
19
+ ![ask_user overlay](https://raw.githubusercontent.com/mrclrchtr/supi/main/screenshots/supi-ask-user.png)
20
+
21
+ ![ask_user choice with preview](https://raw.githubusercontent.com/mrclrchtr/supi/main/screenshots/supi-ask-user-2.png)
18
22
 
19
23
  ## What you get
20
24
 
@@ -114,12 +118,10 @@ A completed form returns a result with `details.status` set to one of:
114
118
 
115
119
  The tool registers the following prompt guidance that the model sees:
116
120
 
117
- - Use ask_user only when explicit user input is required to proceed safely; do not use ask_user for open-ended interviews or repo facts.
121
+ - Use ask_user only for blocking user input, not open-ended interviews or repo facts.
118
122
  - Use ask_user with 1-4 related questions; prefer one when possible.
119
- - Use ask_user `choice` for fixed options and ask_user `text` for freeform input; model yes/no as `choice` with `{ value: "yes", label: "Yes" }` and `{ value: "no", label: "No" }`.
120
- - Use ask_user `allowOther` only on single-select `choice` questions.
121
- - Use ask_user `allowDiscuss` or `allowPartialSubmit` only when that outcome is actionable.
122
- - Do not call ask_user while another ask_user form is already in flight.
123
+ - Use ask_user `choice` for fixed options and ask_user `text` for freeform input; yes/no should be a `choice`.
124
+ - Keep one ask_user form active at a time; use `allowOther` only for single-select choice and `allowDiscuss`/`allowPartialSubmit` only when actionable.
123
125
 
124
126
  ## UI controls
125
127
 
@@ -1,29 +1,20 @@
1
+ ![SuPi](assets/logo.png)
2
+
1
3
  # @mrclrchtr/supi-core
2
4
 
3
5
  Shared infrastructure for SuPi extensions.
4
6
 
5
- This package is mainly for extension authors. It gives you a common config system, settings plumbing, context helpers, registries, and a small extension surface that registers `/supi-settings`.
7
+ This is a **pure library** it does not register any pi commands or tools. The `/supi-settings` command is now available through `@mrclrchtr/supi-settings`.
6
8
 
7
9
  ## Install
8
10
 
9
- ### As a dependency for another extension
10
-
11
11
  ```bash
12
12
  pnpm add @mrclrchtr/supi-core
13
13
  ```
14
14
 
15
- ### As a pi package
16
-
17
- ```bash
18
- pi install npm:@mrclrchtr/supi-core
19
- ```
20
-
21
- Installing it as a pi package adds the minimal `/supi-settings` extension surface.
22
-
23
15
  ## Package surfaces
24
16
 
25
17
  - `@mrclrchtr/supi-core/api` — reusable helpers for other packages and extensions
26
- - `@mrclrchtr/supi-core/extension` — minimal pi extension that registers `/supi-settings`
27
18
 
28
19
  ## What you get from the API
29
20
 
@@ -101,7 +92,6 @@ const message = wrapExtensionContext("my-extension", "hello", {
101
92
  ## Source
102
93
 
103
94
  - `src/api.ts` — exported library surface
104
- - `src/extension.ts` — minimal `/supi-settings` entrypoint
105
95
  - `src/config.ts` — shared config loading and writing
106
96
  - `src/config-settings.ts` — config-backed settings registration helper
107
97
  - `src/settings-ui.ts` — shared settings overlay
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-core",
3
- "version": "1.7.0",
3
+ "version": "1.8.1",
4
4
  "description": "SuPi core — shared infrastructure for SuPi extensions (XML context tags, config system)",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -37,12 +37,17 @@
37
37
  "main": "src/api.ts",
38
38
  "exports": {
39
39
  "./api": "./src/api.ts",
40
- "./extension": "./src/extension.ts",
41
- "./package.json": "./package.json"
42
- },
43
- "pi": {
44
- "extensions": [
45
- "./src/extension.ts"
46
- ]
40
+ "./config": "./src/config.ts",
41
+ "./context": "./src/context.ts",
42
+ "./debug": "./src/debug-registry.ts",
43
+ "./package.json": "./package.json",
44
+ "./path": "./src/path.ts",
45
+ "./project": "./src/project.ts",
46
+ "./session": "./src/session.ts",
47
+ "./settings": "./src/settings.ts",
48
+ "./settings-ui": "./src/settings-ui.ts",
49
+ "./terminal": "./src/terminal.ts",
50
+ "./tool-framework": "./src/tool-framework.ts",
51
+ "./types": "./src/types.ts"
47
52
  }
48
53
  }
@@ -1,96 +1,30 @@
1
1
  // supi-core — shared infrastructure for SuPi extensions.
2
2
  // Provides XML context tag wrapping, unified config system, context-message utilities,
3
3
  // settings registry for supi-wide TUI settings, and a shared tool-spec/registration framework.
4
+ //
5
+ // Convenience barrel — re-exports all domain entry points.
6
+ // For lighter imports, use one of the domain subpaths directly
7
+ // (e.g. @mrclrchtr/supi-core/config, @mrclrchtr/supi-core/context).
4
8
 
5
- export type { SupiConfigLocation, SupiConfigOptions } from "./config/config.ts";
6
- export {
7
- loadSupiConfig,
8
- loadSupiConfigForScope,
9
- readJsonFile,
10
- removeSupiConfigKey,
11
- writeSupiConfig,
12
- } from "./config/config.ts";
13
- export type { ConfigSettingsHelpers, ConfigSettingsOptions } from "./config/config-settings.ts";
14
- export { registerConfigSettings } from "./config/config-settings.ts";
15
- export type { ContextMessageLike } from "./context/context-messages.ts";
16
- export {
17
- findLastUserMessageIndex,
18
- getContextToken,
19
- getPromptContent,
20
- pruneAndReorderContextMessages,
21
- restorePromptContent,
22
- } from "./context/context-messages.ts";
23
- export type { ContextProvider } from "./context/context-provider-registry.ts";
24
- export {
25
- clearRegisteredContextProviders,
26
- getRegisteredContextProviders,
27
- registerContextProvider,
28
- } from "./context/context-provider-registry.ts";
29
- export { wrapExtensionContext } from "./context/context-tag.ts";
30
- export type {
31
- DebugAgentAccess,
32
- DebugEvent,
33
- DebugEventInput,
34
- DebugEventQuery,
35
- DebugEventQueryResult,
36
- DebugEventView,
37
- DebugLevel,
38
- DebugNotifyLevel,
39
- DebugRegistryConfig,
40
- DebugSummary,
41
- } from "./debug-registry.ts";
42
- export {
43
- clearDebugEvents,
44
- configureDebugRegistry,
45
- DEBUG_REGISTRY_DEFAULTS,
46
- getDebugEvents,
47
- getDebugRegistryConfig,
48
- getDebugSummary,
49
- recordDebugEvent,
50
- redactDebugData,
51
- resetDebugRegistry,
52
- } from "./debug-registry.ts";
53
- export { fileToUri, resolveToolPath, stripToolPathPrefix, uriToFile } from "./path-utils.ts";
54
- export type { KnownRootEntry } from "./project-roots.ts";
55
- export {
56
- buildKnownRootsMap,
57
- byPathDepth,
58
- dedupeTopmostRoots,
59
- findProjectRoot,
60
- isWithin,
61
- isWithinOrEqual,
62
- mergeKnownRoots,
63
- resolveKnownRoot,
64
- segmentCount,
65
- sortRootsBySpecificity,
66
- walkProject,
67
- } from "./project-roots.ts";
68
- export { createRegistry, createSessionStateRegistry } from "./registry-utils.ts";
69
- export { getActiveBranchEntries } from "./session-utils.ts";
70
- export { registerSettingsCommand } from "./settings/settings-command.ts";
71
- export type { SettingsScope, SettingsSection } from "./settings/settings-registry.ts";
72
- export {
73
- clearRegisteredSettings,
74
- getRegisteredSettings,
75
- registerSettings,
76
- } from "./settings/settings-registry.ts";
77
- export { createInputSubmenu, openSettingsOverlay } from "./settings/settings-ui.ts";
78
- export type { TitleTarget } from "./terminal.ts";
79
- export {
80
- DONE_SYMBOL,
81
- formatTitle,
82
- signalBell,
83
- signalDone,
84
- signalWaiting,
85
- WAITING_SYMBOL,
86
- } from "./terminal.ts";
87
- export type { SuiPiToolPromptSurface, SuiPiToolSpec, ToolExecuteFn } from "./tool-framework.ts";
88
- export {
89
- CharacterParam,
90
- derivePromptSurface,
91
- FileParam,
92
- LineParam,
93
- MaxResultsParam,
94
- registerSuiPiTools,
95
- SymbolParam,
96
- } from "./tool-framework.ts";
9
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
10
+ export * from "./config.ts";
11
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
12
+ export * from "./context.ts";
13
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
14
+ export * from "./debug-registry.ts";
15
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
16
+ export * from "./path.ts";
17
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
18
+ export * from "./project.ts";
19
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
20
+ export * from "./session.ts";
21
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
22
+ export * from "./settings.ts";
23
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
24
+ export * from "./settings-ui.ts";
25
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
26
+ export * from "./terminal.ts";
27
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
28
+ export * from "./tool-framework.ts";
29
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
30
+ export * from "./types.ts";
@@ -0,0 +1,11 @@
1
+ // supi-core config domain — config loading and config-settings helpers.
2
+ export type { SupiConfigLocation, SupiConfigOptions } from "./config/config.ts";
3
+ export {
4
+ loadSupiConfig,
5
+ loadSupiConfigForScope,
6
+ readJsonFile,
7
+ removeSupiConfigKey,
8
+ writeSupiConfig,
9
+ } from "./config/config.ts";
10
+ export type { ConfigSettingsHelpers, ConfigSettingsOptions } from "./config/config-settings.ts";
11
+ export { registerConfigSettings } from "./config/config-settings.ts";
@@ -0,0 +1,16 @@
1
+ // supi-core context domain — context messages, providers, and XML tags.
2
+ export type { ContextMessageLike } from "./context/context-messages.ts";
3
+ export {
4
+ findLastUserMessageIndex,
5
+ getContextToken,
6
+ getPromptContent,
7
+ pruneAndReorderContextMessages,
8
+ restorePromptContent,
9
+ } from "./context/context-messages.ts";
10
+ export type { ContextProvider } from "./context/context-provider-registry.ts";
11
+ export {
12
+ clearRegisteredContextProviders,
13
+ getRegisteredContextProviders,
14
+ registerContextProvider,
15
+ } from "./context/context-provider-registry.ts";
16
+ export { wrapExtensionContext } from "./context/context-tag.ts";
@@ -1,96 +1,30 @@
1
1
  // supi-core — shared infrastructure for SuPi extensions.
2
2
  // Provides XML context tag wrapping, unified config system, context-message utilities,
3
3
  // settings registry for supi-wide TUI settings, and a shared tool-spec/registration framework.
4
+ //
5
+ // Convenience barrel — re-exports all domain entry points.
6
+ // For lighter imports, use one of the domain subpaths directly
7
+ // (e.g. @mrclrchtr/supi-core/config, @mrclrchtr/supi-core/context).
4
8
 
5
- export type { SupiConfigLocation, SupiConfigOptions } from "./config/config.ts";
6
- export {
7
- loadSupiConfig,
8
- loadSupiConfigForScope,
9
- readJsonFile,
10
- removeSupiConfigKey,
11
- writeSupiConfig,
12
- } from "./config/config.ts";
13
- export type { ConfigSettingsHelpers, ConfigSettingsOptions } from "./config/config-settings.ts";
14
- export { registerConfigSettings } from "./config/config-settings.ts";
15
- export type { ContextMessageLike } from "./context/context-messages.ts";
16
- export {
17
- findLastUserMessageIndex,
18
- getContextToken,
19
- getPromptContent,
20
- pruneAndReorderContextMessages,
21
- restorePromptContent,
22
- } from "./context/context-messages.ts";
23
- export type { ContextProvider } from "./context/context-provider-registry.ts";
24
- export {
25
- clearRegisteredContextProviders,
26
- getRegisteredContextProviders,
27
- registerContextProvider,
28
- } from "./context/context-provider-registry.ts";
29
- export { wrapExtensionContext } from "./context/context-tag.ts";
30
- export type {
31
- DebugAgentAccess,
32
- DebugEvent,
33
- DebugEventInput,
34
- DebugEventQuery,
35
- DebugEventQueryResult,
36
- DebugEventView,
37
- DebugLevel,
38
- DebugNotifyLevel,
39
- DebugRegistryConfig,
40
- DebugSummary,
41
- } from "./debug-registry.ts";
42
- export {
43
- clearDebugEvents,
44
- configureDebugRegistry,
45
- DEBUG_REGISTRY_DEFAULTS,
46
- getDebugEvents,
47
- getDebugRegistryConfig,
48
- getDebugSummary,
49
- recordDebugEvent,
50
- redactDebugData,
51
- resetDebugRegistry,
52
- } from "./debug-registry.ts";
53
- export { fileToUri, resolveToolPath, stripToolPathPrefix, uriToFile } from "./path-utils.ts";
54
- export type { KnownRootEntry } from "./project-roots.ts";
55
- export {
56
- buildKnownRootsMap,
57
- byPathDepth,
58
- dedupeTopmostRoots,
59
- findProjectRoot,
60
- isWithin,
61
- isWithinOrEqual,
62
- mergeKnownRoots,
63
- resolveKnownRoot,
64
- segmentCount,
65
- sortRootsBySpecificity,
66
- walkProject,
67
- } from "./project-roots.ts";
68
- export { createRegistry, createSessionStateRegistry } from "./registry-utils.ts";
69
- export { getActiveBranchEntries } from "./session-utils.ts";
70
- export { registerSettingsCommand } from "./settings/settings-command.ts";
71
- export type { SettingsScope, SettingsSection } from "./settings/settings-registry.ts";
72
- export {
73
- clearRegisteredSettings,
74
- getRegisteredSettings,
75
- registerSettings,
76
- } from "./settings/settings-registry.ts";
77
- export { createInputSubmenu, openSettingsOverlay } from "./settings/settings-ui.ts";
78
- export type { TitleTarget } from "./terminal.ts";
79
- export {
80
- DONE_SYMBOL,
81
- formatTitle,
82
- signalBell,
83
- signalDone,
84
- signalWaiting,
85
- WAITING_SYMBOL,
86
- } from "./terminal.ts";
87
- export type { SuiPiToolPromptSurface, SuiPiToolSpec, ToolExecuteFn } from "./tool-framework.ts";
88
- export {
89
- CharacterParam,
90
- derivePromptSurface,
91
- FileParam,
92
- LineParam,
93
- MaxResultsParam,
94
- registerSuiPiTools,
95
- SymbolParam,
96
- } from "./tool-framework.ts";
9
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
10
+ export * from "./config.ts";
11
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
12
+ export * from "./context.ts";
13
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
14
+ export * from "./debug-registry.ts";
15
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
16
+ export * from "./path.ts";
17
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
18
+ export * from "./project.ts";
19
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
20
+ export * from "./session.ts";
21
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
22
+ export * from "./settings.ts";
23
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
24
+ export * from "./settings-ui.ts";
25
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
26
+ export * from "./terminal.ts";
27
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
28
+ export * from "./tool-framework.ts";
29
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
30
+ export * from "./types.ts";
@@ -0,0 +1,2 @@
1
+ // supi-core path domain — file and URI path utilities.
2
+ export { fileToUri, resolveToolPath, stripToolPathPrefix, uriToFile } from "./path-utils.ts";
@@ -0,0 +1,15 @@
1
+ // supi-core project domain — project root discovery and traversal.
2
+ export type { KnownRootEntry } from "./project-roots.ts";
3
+ export {
4
+ buildKnownRootsMap,
5
+ byPathDepth,
6
+ dedupeTopmostRoots,
7
+ findProjectRoot,
8
+ isWithin,
9
+ isWithinOrEqual,
10
+ mergeKnownRoots,
11
+ resolveKnownRoot,
12
+ segmentCount,
13
+ sortRootsBySpecificity,
14
+ walkProject,
15
+ } from "./project-roots.ts";
@@ -0,0 +1,4 @@
1
+ // supi-core session domain — session utilities and registries.
2
+
3
+ export { createRegistry, createSessionStateRegistry } from "./registry-utils.ts";
4
+ export { getActiveBranchEntries } from "./session-utils.ts";
@@ -0,0 +1,2 @@
1
+ // supi-core settings-ui domain — settings TUI components (imports pi-tui at runtime, heavy).
2
+ export { createInputSubmenu, openSettingsOverlay } from "./settings/settings-ui.ts";
@@ -0,0 +1,9 @@
1
+ // supi-core settings domain — settings registry (lightweight, type-only pi-tui import).
2
+
3
+ export { registerSettingsCommand } from "./settings/settings-command.ts";
4
+ export type { SettingsScope, SettingsSection } from "./settings/settings-registry.ts";
5
+ export {
6
+ clearRegisteredSettings,
7
+ getRegisteredSettings,
8
+ registerSettings,
9
+ } from "./settings/settings-registry.ts";
@@ -0,0 +1,11 @@
1
+ /** 0-based position used by LSP and code-intelligence internally. */
2
+ export interface CodePosition {
3
+ line: number;
4
+ character: number;
5
+ }
6
+
7
+ /** Normalized location — flat replacement for LSP's nested Location/range shape. */
8
+ export interface CodeLocation {
9
+ uri: string;
10
+ range: { start: CodePosition; end: CodePosition };
11
+ }
@@ -0,0 +1,2 @@
1
+ // supi-core types domain — shared substrate types (zero dependencies, pure types).
2
+ export type { CodeLocation, CodePosition } from "./substrate-types.ts";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-ask-user",
3
- "version": "1.7.0",
3
+ "version": "1.8.1",
4
4
  "description": "SuPi ask-user extension — rich questionnaire UI for structured agent-user decisions",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -21,7 +21,7 @@
21
21
  ],
22
22
  "main": "src/api.ts",
23
23
  "dependencies": {
24
- "@mrclrchtr/supi-core": "1.7.0"
24
+ "@mrclrchtr/supi-core": "1.8.1"
25
25
  },
26
26
  "bundledDependencies": [
27
27
  "@mrclrchtr/supi-core"
@@ -44,9 +44,9 @@
44
44
  },
45
45
  "pi": {
46
46
  "extensions": [
47
- "./src/extension.ts",
48
- "node_modules/@mrclrchtr/supi-core/src/extension.ts"
49
- ]
47
+ "./src/extension.ts"
48
+ ],
49
+ "image": "https://raw.githubusercontent.com/mrclrchtr/supi/main/packages/supi-ask-user/assets/logo.png"
50
50
  },
51
51
  "exports": {
52
52
  "./api": "./src/api.ts",
package/src/ask-user.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
2
- import { formatTitle, signalWaiting } from "@mrclrchtr/supi-core/api";
2
+ import { formatTitle, signalWaiting } from "@mrclrchtr/supi-core/terminal";
3
3
  import { AskUserValidationError, normalizeQuestionnaire } from "./normalize.ts";
4
4
  import { type AskUserToolResult, buildErrorResult, buildResult } from "./render/result.ts";
5
5
  import { renderAskUserCall, renderAskUserResult } from "./render/transcript.ts";
package/src/schema.ts CHANGED
@@ -3,101 +3,92 @@
3
3
  import { type Static, Type } from "typebox";
4
4
 
5
5
  const OptionSchema = Type.Object({
6
- value: Type.String({ description: "Stable identifier returned when this option is selected" }),
7
- label: Type.String({ description: "Display label shown to the user" }),
6
+ value: Type.String({ description: "Returned id" }),
7
+ label: Type.String({ description: "Displayed label" }),
8
8
  description: Type.Optional(
9
9
  Type.String({
10
- description: "Optional clarification shown under the label in richer UIs",
10
+ description: "Optional note",
11
11
  }),
12
12
  ),
13
13
  preview: Type.Optional(
14
14
  Type.String({
15
- description: "Optional preview content shown for the currently focused option",
15
+ description: "Optional preview",
16
16
  }),
17
17
  ),
18
18
  });
19
19
 
20
20
  const ChoiceQuestionSchema = Type.Object({
21
21
  type: Type.Literal("choice"),
22
- id: Type.String({ description: "Unique question id within this form" }),
23
- header: Type.String({ description: "Short label describing the decision" }),
24
- prompt: Type.String({ description: "Full question text shown to the user" }),
22
+ id: Type.String({ description: "Question id" }),
23
+ header: Type.String({ description: "Short label" }),
24
+ prompt: Type.String({ description: "Question text" }),
25
25
  options: Type.Array(OptionSchema, {
26
- description: "Allowed answers (2-12 distinct options)",
26
+ description: "Allowed options (2-12)",
27
27
  }),
28
28
  required: Type.Optional(
29
29
  Type.Boolean({
30
30
  default: true,
31
- description: "Whether this question must be answered for a full submit",
31
+ description: "Required for full submit",
32
32
  }),
33
33
  ),
34
34
  multi: Type.Optional(
35
35
  Type.Boolean({
36
36
  default: false,
37
- description: "Allow selecting multiple options instead of one",
37
+ description: "Allow multiple selections",
38
38
  }),
39
39
  ),
40
40
  allowOther: Type.Optional(
41
41
  Type.Boolean({
42
- description:
43
- "Allow a custom freeform answer instead of the listed options. Only valid for single-select choice questions.",
42
+ description: "Allow a custom option; single-select only",
44
43
  }),
45
44
  ),
46
45
  recommendation: Type.Optional(
47
46
  Type.Union([Type.String(), Type.Array(Type.String())], {
48
- description:
49
- "Recommended option value or values. Use a string for single-select and an array for multi-select.",
47
+ description: "Recommended value(s)",
50
48
  }),
51
49
  ),
52
50
  initial: Type.Optional(
53
51
  Type.Union([Type.String(), Type.Array(Type.String())], {
54
- description:
55
- "Initial selected option value or values. Use a string for single-select and an array for multi-select.",
52
+ description: "Initial value(s)",
56
53
  }),
57
54
  ),
58
55
  });
59
56
 
60
57
  const TextQuestionSchema = Type.Object({
61
58
  type: Type.Literal("text"),
62
- id: Type.String({ description: "Unique question id within this form" }),
63
- header: Type.String({ description: "Short label describing the prompt" }),
64
- prompt: Type.String({ description: "Full question text shown to the user" }),
59
+ id: Type.String({ description: "Question id" }),
60
+ header: Type.String({ description: "Short label" }),
61
+ prompt: Type.String({ description: "Question text" }),
65
62
  required: Type.Optional(
66
63
  Type.Boolean({
67
64
  default: true,
68
- description: "Whether this question must be answered for a full submit",
65
+ description: "Required for full submit",
69
66
  }),
70
67
  ),
71
- initial: Type.Optional(Type.String({ description: "Initial value shown in the editor" })),
72
- placeholder: Type.Optional(
73
- Type.String({ description: "Placeholder shown before the user types" }),
74
- ),
68
+ initial: Type.Optional(Type.String({ description: "Initial text" })),
69
+ placeholder: Type.Optional(Type.String({ description: "Editor placeholder" })),
75
70
  });
76
71
 
77
72
  const QuestionSchema = Type.Union([ChoiceQuestionSchema, TextQuestionSchema]);
78
73
 
79
74
  export const AskUserParamsSchema = Type.Object({
80
- title: Type.Optional(
81
- Type.String({ description: "Optional short title explaining the overall decision" }),
82
- ),
75
+ title: Type.Optional(Type.String({ description: "Optional title" })),
83
76
  intro: Type.Optional(
84
77
  Type.String({
85
- description: "Optional introductory context explaining why the agent is asking",
78
+ description: "Optional intro for the decision",
86
79
  }),
87
80
  ),
88
81
  questions: Type.Array(QuestionSchema, {
89
- description: "Between 1 and 4 focused questions that belong to the same decision",
82
+ description: "1-4 focused questions for one decision",
90
83
  }),
91
84
  allowPartialSubmit: Type.Optional(
92
85
  Type.Boolean({
93
- description:
94
- "Allow the user to submit a partial form when some required questions remain unanswered",
86
+ description: "Allow partial submission",
95
87
  }),
96
88
  ),
97
89
  allowDiscuss: Type.Optional(
98
90
  Type.Boolean({
99
- description:
100
- "Allow the user to switch back into discussion instead of committing to a final answer",
91
+ description: "Allow discussion handoff",
101
92
  }),
102
93
  ),
103
94
  });
@@ -1,15 +1,13 @@
1
1
  // Prompt guidance and tool description for the redesigned ask_user tool.
2
2
 
3
3
  export const toolDescription =
4
- "Ask the user for a focused blocking decision when explicit input is required to proceed safely. Requires an interactive UI with custom overlay support, and only one ask_user form can be active at a time. Use 1-4 related `choice` or `text` questions. Do not use ask_user for open-ended interviews or repo facts you can get yourself. Forms may allow partial submit or discussion handoff.";
4
+ "Ask the user for a focused blocking decision. Use 1-4 related `choice` or `text` questions, and keep only one ask_user form active at a time.";
5
5
 
6
6
  export const promptSnippet = "ask_user — request a focused blocking user decision";
7
7
 
8
8
  export const promptGuidelines = [
9
- "Use ask_user only when explicit user input is required to proceed safely; do not use ask_user for open-ended interviews or repo facts.",
9
+ "Use ask_user only for blocking user input, not open-ended interviews or repo facts.",
10
10
  "Use ask_user with 1-4 related questions; prefer one when possible.",
11
- 'Use ask_user `choice` for fixed options and ask_user `text` for freeform input; model yes/no as `choice` with `{ value: "yes", label: "Yes" }` and `{ value: "no", label: "No" }`.',
12
- "Use ask_user `allowOther` only on single-select `choice` questions.",
13
- "Use ask_user `allowDiscuss` or `allowPartialSubmit` only when that outcome is actionable.",
14
- "Do not call ask_user while another ask_user form is already in flight.",
11
+ "Use ask_user `choice` for fixed options and ask_user `text` for freeform input; yes/no should be a `choice`.",
12
+ "Keep one ask_user form active at a time; use `allowOther` only for single-select choice and `allowDiscuss`/`allowPartialSubmit` only when actionable.",
15
13
  ];
@@ -5,6 +5,7 @@ import {
5
5
  Markdown,
6
6
  type SelectList,
7
7
  type SelectListTheme,
8
+ wrapTextWithAnsi,
8
9
  } from "@earendil-works/pi-tui";
9
10
  import type { AskUserController } from "../session/controller.ts";
10
11
  import type { NormalizedQuestionnaire } from "../types.ts";
@@ -100,11 +101,15 @@ export function clampIndex(index: number, length: number): number {
100
101
  function renderHeader(args: RenderOverlayFrameArgs): string[] {
101
102
  const lines: string[] = [];
102
103
  const { title, intro } = args.controller.questionnaire;
103
- if (title) lines.push(args.theme.fg("accent", args.theme.bold(title)));
104
+ if (title)
105
+ lines.push(...wrapTextWithAnsi(args.theme.fg("accent", args.theme.bold(title)), args.width));
104
106
  lines.push(
105
- args.theme.fg(
106
- "muted",
107
- `${args.controller.currentIndex + 1}/${args.controller.questions.length} · ${args.controller.currentQuestion.header}`,
107
+ ...wrapTextWithAnsi(
108
+ args.theme.fg(
109
+ "muted",
110
+ `${args.controller.currentIndex + 1}/${args.controller.questions.length} · ${args.controller.currentQuestion.header}`,
111
+ ),
112
+ args.width,
108
113
  ),
109
114
  );
110
115
  if (intro) {
@@ -197,14 +202,17 @@ function renderEditorLines(args: RenderOverlayFrameArgs, width: number): string[
197
202
  : "Option note"
198
203
  : "Your answer";
199
204
 
200
- const lines = [args.theme.fg("accent", label), ...args.editor.render(Math.max(20, width - 1))];
205
+ const labelLines = wrapTextWithAnsi(args.theme.fg("accent", label), width);
206
+ const lines = [...labelLines, ...args.editor.render(Math.max(20, width - 1))];
201
207
  const question = args.controller.currentQuestion;
202
208
  if (question.type !== "text" || args.editor.getText()) return lines;
203
209
 
204
210
  if (question.initial) {
205
- lines.push(args.theme.fg("dim", `Initial: ${question.initial}`));
211
+ lines.push(...wrapTextWithAnsi(args.theme.fg("dim", `Initial: ${question.initial}`), width));
206
212
  } else if (question.placeholder) {
207
- lines.push(args.theme.fg("dim", `Placeholder: ${question.placeholder}`));
213
+ lines.push(
214
+ ...wrapTextWithAnsi(args.theme.fg("dim", `Placeholder: ${question.placeholder}`), width),
215
+ );
208
216
  }
209
217
  return lines;
210
218
  }
@@ -177,7 +177,7 @@ function renderOptionRow(args: {
177
177
  : `${prefix}${labelText}`;
178
178
  const noteSuffix = hasNote ? ` ${theme.fg("accent", "[note]")}` : "";
179
179
 
180
- const lines: string[] = [`${baseText}${noteSuffix}`];
180
+ const lines: string[] = wrapTextWithAnsi(`${baseText}${noteSuffix}`, width);
181
181
 
182
182
  if (option.description) {
183
183
  const descWidth = Math.max(10, width - 2);
@@ -1 +0,0 @@
1
- export { registerSettingsCommand as default } from "./settings/settings-command.ts";