@autopilot-agent/cli 0.0.0-stage → 0.1.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.
Files changed (142) hide show
  1. package/README.md +180 -2
  2. package/dist/activity-animation.d.ts +57 -0
  3. package/dist/activity-animation.js +145 -0
  4. package/dist/animation-clock.d.ts +22 -0
  5. package/dist/animation-clock.js +76 -0
  6. package/dist/composition-root.d.ts +105 -0
  7. package/dist/composition-root.js +588 -0
  8. package/dist/context-report.d.ts +20 -0
  9. package/dist/context-report.js +174 -0
  10. package/dist/controller.d.ts +79 -0
  11. package/dist/controller.js +681 -0
  12. package/dist/design-tokens.d.ts +34 -0
  13. package/dist/design-tokens.js +34 -0
  14. package/dist/error-diagnostics.d.ts +8 -0
  15. package/dist/error-diagnostics.js +62 -0
  16. package/dist/event-projector.d.ts +26 -0
  17. package/dist/event-projector.js +333 -0
  18. package/dist/file-mention-context.d.ts +34 -0
  19. package/dist/file-mention-context.js +173 -0
  20. package/dist/index.d.ts +19 -0
  21. package/dist/index.js +14 -0
  22. package/dist/main.d.ts +40 -0
  23. package/dist/main.js +543 -0
  24. package/dist/parser.d.ts +17 -0
  25. package/dist/parser.js +391 -0
  26. package/dist/renderer.d.ts +9 -0
  27. package/dist/renderer.js +48 -0
  28. package/dist/scroll-viewport.d.ts +22 -0
  29. package/dist/scroll-viewport.js +62 -0
  30. package/dist/termination-format.d.ts +4 -0
  31. package/dist/termination-format.js +17 -0
  32. package/dist/timeline-lines.d.ts +38 -0
  33. package/dist/timeline-lines.js +104 -0
  34. package/dist/tui/app.d.ts +30 -0
  35. package/dist/tui/app.js +410 -0
  36. package/dist/tui/approval.d.ts +19 -0
  37. package/dist/tui/approval.js +37 -0
  38. package/dist/tui/brand/brand-header.d.ts +20 -0
  39. package/dist/tui/brand/brand-header.js +22 -0
  40. package/dist/tui/brand/intro.d.ts +12 -0
  41. package/dist/tui/brand/intro.js +88 -0
  42. package/dist/tui/brand/pixel.d.ts +8 -0
  43. package/dist/tui/brand/pixel.js +50 -0
  44. package/dist/tui/constants.d.ts +56 -0
  45. package/dist/tui/constants.js +57 -0
  46. package/dist/tui/context.d.ts +11 -0
  47. package/dist/tui/context.js +12 -0
  48. package/dist/tui/diff-stats.d.ts +8 -0
  49. package/dist/tui/diff-stats.js +7 -0
  50. package/dist/tui/format.d.ts +75 -0
  51. package/dist/tui/format.js +176 -0
  52. package/dist/tui/hooks/use-file-mention-index.d.ts +15 -0
  53. package/dist/tui/hooks/use-file-mention-index.js +59 -0
  54. package/dist/tui/hooks/use-pickers.d.ts +47 -0
  55. package/dist/tui/hooks/use-pickers.js +288 -0
  56. package/dist/tui/hooks/use-prompt-input.d.ts +54 -0
  57. package/dist/tui/hooks/use-prompt-input.js +258 -0
  58. package/dist/tui/hooks/use-status-view.d.ts +8 -0
  59. package/dist/tui/hooks/use-status-view.js +13 -0
  60. package/dist/tui/hooks/use-viewport.d.ts +22 -0
  61. package/dist/tui/hooks/use-viewport.js +117 -0
  62. package/dist/tui/input/file-mention.d.ts +33 -0
  63. package/dist/tui/input/file-mention.js +112 -0
  64. package/dist/tui/input/graphemes.d.ts +5 -0
  65. package/dist/tui/input/graphemes.js +10 -0
  66. package/dist/tui/input/keys.d.ts +16 -0
  67. package/dist/tui/input/keys.js +58 -0
  68. package/dist/tui/input/prompt-layout.d.ts +27 -0
  69. package/dist/tui/input/prompt-layout.js +84 -0
  70. package/dist/tui/input-area.d.ts +53 -0
  71. package/dist/tui/input-area.js +89 -0
  72. package/dist/tui/jobs.d.ts +10 -0
  73. package/dist/tui/jobs.js +24 -0
  74. package/dist/tui/lifecycle.d.ts +23 -0
  75. package/dist/tui/lifecycle.js +114 -0
  76. package/dist/tui/markdown/diff.d.ts +8 -0
  77. package/dist/tui/markdown/diff.js +19 -0
  78. package/dist/tui/markdown/inline.d.ts +4 -0
  79. package/dist/tui/markdown/inline.js +32 -0
  80. package/dist/tui/markdown/markdown.d.ts +17 -0
  81. package/dist/tui/markdown/markdown.js +117 -0
  82. package/dist/tui/markdown/table.d.ts +19 -0
  83. package/dist/tui/markdown/table.js +74 -0
  84. package/dist/tui/performance-diagnostics.d.ts +7 -0
  85. package/dist/tui/performance-diagnostics.js +57 -0
  86. package/dist/tui/pickers/effort.d.ts +10 -0
  87. package/dist/tui/pickers/effort.js +12 -0
  88. package/dist/tui/pickers/file-mention.d.ts +11 -0
  89. package/dist/tui/pickers/file-mention.js +22 -0
  90. package/dist/tui/pickers/model.d.ts +10 -0
  91. package/dist/tui/pickers/model.js +12 -0
  92. package/dist/tui/pickers/permission.d.ts +10 -0
  93. package/dist/tui/pickers/permission.js +13 -0
  94. package/dist/tui/pickers/skill.d.ts +13 -0
  95. package/dist/tui/pickers/skill.js +25 -0
  96. package/dist/tui/pickers/slash.d.ts +10 -0
  97. package/dist/tui/pickers/slash.js +11 -0
  98. package/dist/tui/prompt-input.d.ts +11 -0
  99. package/dist/tui/prompt-input.js +73 -0
  100. package/dist/tui/render-mode.d.ts +20 -0
  101. package/dist/tui/render-mode.js +31 -0
  102. package/dist/tui/run.d.ts +6 -0
  103. package/dist/tui/run.js +237 -0
  104. package/dist/tui/status-line.d.ts +16 -0
  105. package/dist/tui/status-line.js +37 -0
  106. package/dist/tui/store.d.ts +4 -0
  107. package/dist/tui/store.js +344 -0
  108. package/dist/tui/timeline/live.d.ts +16 -0
  109. package/dist/tui/timeline/live.js +151 -0
  110. package/dist/tui/timeline/presentation-timeline.d.ts +14 -0
  111. package/dist/tui/timeline/presentation-timeline.js +109 -0
  112. package/dist/tui/timeline/progress.d.ts +12 -0
  113. package/dist/tui/timeline/progress.js +43 -0
  114. package/dist/tui/timeline/static-timeline.d.ts +13 -0
  115. package/dist/tui/timeline/static-timeline.js +121 -0
  116. package/dist/tui/timeline/tool-tree.d.ts +7 -0
  117. package/dist/tui/timeline/tool-tree.js +25 -0
  118. package/dist/tui/timeline/turn.d.ts +19 -0
  119. package/dist/tui/timeline/turn.js +21 -0
  120. package/dist/tui/todo-list.d.ts +50 -0
  121. package/dist/tui/todo-list.js +120 -0
  122. package/dist/tui/transcript.d.ts +24 -0
  123. package/dist/tui/transcript.js +34 -0
  124. package/dist/tui/trust-dialog.d.ts +51 -0
  125. package/dist/tui/trust-dialog.js +127 -0
  126. package/dist/tui/types.d.ts +175 -0
  127. package/dist/tui/types.js +5 -0
  128. package/dist/tui/viewport/selection.d.ts +27 -0
  129. package/dist/tui/viewport/selection.js +79 -0
  130. package/dist/tui/viewport/sources.d.ts +29 -0
  131. package/dist/tui/viewport/sources.js +585 -0
  132. package/dist/tui/viewport/viewport.d.ts +12 -0
  133. package/dist/tui/viewport/viewport.js +87 -0
  134. package/dist/tui-input-history.d.ts +23 -0
  135. package/dist/tui-input-history.js +42 -0
  136. package/dist/tui-input-router.d.ts +24 -0
  137. package/dist/tui-input-router.js +115 -0
  138. package/dist/tui.d.ts +15 -0
  139. package/dist/tui.js +13 -0
  140. package/dist/types.d.ts +454 -0
  141. package/dist/types.js +7 -0
  142. package/package.json +58 -4
@@ -0,0 +1,59 @@
1
+ /**
2
+ * TUI @文件提及的索引预热:挂载后调用一次 Core `listWorkspaceFiles`,模块级缓存供整个会话复用。
3
+ * 空查询候选由「工作区一层目录(尾随 /)+ 全部文件」合并稳定排序构成;失败静默降级为空候选。
4
+ */
5
+ import { useEffect, useState } from "react";
6
+ import { listWorkspaceFiles } from "@autopilot-agent/core";
7
+ let cachedMentionEntries;
8
+ let cachedMentionLoad;
9
+ /** 清空模块级 @ 索引缓存;仅供测试在用例间隔离。 */
10
+ export function resetFileMentionIndexCache() {
11
+ cachedMentionEntries = undefined;
12
+ cachedMentionLoad = undefined;
13
+ }
14
+ /** 从文件列表推导空查询候选:一层目录(尾随 /)+ 全部文件,localeCompare 稳定排序。 */
15
+ function buildMentionEntries(files) {
16
+ const directories = new Set();
17
+ for (const file of files) {
18
+ const slashIndex = file.indexOf("/");
19
+ if (slashIndex > 0)
20
+ directories.add(`${file.slice(0, slashIndex)}/`);
21
+ }
22
+ return [...directories, ...files].sort(function compareEntries(left, right) {
23
+ return left.localeCompare(right);
24
+ });
25
+ }
26
+ /** 挂载后预热 @ 文件索引;workspaceRoot 缺失或 Core 枚举失败时静默降级为空候选,不影响普通输入。 */
27
+ export function useFileMentionIndex(workspaceRoot) {
28
+ const [entries, setEntries] = useState(function readCachedEntries() {
29
+ return cachedMentionEntries ?? [];
30
+ });
31
+ const [ready, setReady] = useState(function readCachedReady() {
32
+ return cachedMentionEntries !== undefined;
33
+ });
34
+ useEffect(function preloadMentionIndex() {
35
+ if (!workspaceRoot || cachedMentionEntries !== undefined)
36
+ return;
37
+ if (!cachedMentionLoad) {
38
+ cachedMentionLoad = listWorkspaceFiles({ workspaceRoot })
39
+ .then(function collect(result) {
40
+ return buildMentionEntries(result.files);
41
+ })
42
+ .catch(function degradeOnFailure() {
43
+ return [];
44
+ });
45
+ }
46
+ let cancelled = false;
47
+ void cachedMentionLoad.then(function applyLoaded(loaded) {
48
+ if (cancelled)
49
+ return;
50
+ cachedMentionEntries = loaded;
51
+ setEntries(loaded);
52
+ setReady(true);
53
+ });
54
+ return function cancelPreload() {
55
+ cancelled = true;
56
+ };
57
+ }, [workspaceRoot]);
58
+ return { entries, ready };
59
+ }
@@ -0,0 +1,47 @@
1
+ /** 四 picker(skill/permission/model/effort)及审批/slash 菜单焦点的状态与处理函数。 */
2
+ import type { Key } from "ink";
3
+ import type { CliAction, CliEffortOption, CliModelOption, CliPermissionOption, CliSkillSummary } from "../../types.js";
4
+ import { parseTuiControlAction } from "../input/keys.js";
5
+ /** usePickers 的输入依赖;dispatchAction 与 rawInputSequenceRef 由 app.tsx 注入。 */
6
+ export interface UsePickersInput {
7
+ dispatchAction: (action: CliAction, prompt?: string, preserveSkillPickerQuery?: boolean) => Promise<void>;
8
+ rawInputSequenceRef: {
9
+ current: string;
10
+ };
11
+ }
12
+ /** 管理四个 picker 与审批/slash 菜单焦点;对外暴露 state、setter 与统一分发入口。 */
13
+ export declare function usePickers(input: UsePickersInput): {
14
+ skillPickerSkills: CliSkillSummary[] | undefined;
15
+ skillPickerQuery: string;
16
+ skillPickerIndex: number;
17
+ permissionPickerOptions: CliPermissionOption[] | undefined;
18
+ permissionPickerIndex: number;
19
+ modelPickerOptions: CliModelOption[] | undefined;
20
+ modelPickerIndex: number;
21
+ effortPickerOptions: CliEffortOption[] | undefined;
22
+ effortPickerIndex: number;
23
+ approvalPickerIndex: number;
24
+ slashCommandIndex: number;
25
+ fileMentionIndex: number;
26
+ fileMentionDismissedInput: string | undefined;
27
+ setSkillPickerSkills: import("react").Dispatch<import("react").SetStateAction<CliSkillSummary[] | undefined>>;
28
+ setPermissionPickerOptions: import("react").Dispatch<import("react").SetStateAction<CliPermissionOption[] | undefined>>;
29
+ setModelPickerOptions: import("react").Dispatch<import("react").SetStateAction<CliModelOption[] | undefined>>;
30
+ setEffortPickerOptions: import("react").Dispatch<import("react").SetStateAction<CliEffortOption[] | undefined>>;
31
+ updateSkillPickerQuery: (query: string) => void;
32
+ updatePermissionPickerIndex: (index: number) => void;
33
+ updateModelPickerIndex: (index: number) => void;
34
+ updateEffortPickerIndex: (index: number) => void;
35
+ updateApprovalPickerIndex: (index: number) => void;
36
+ updateSlashCommandIndex: (index: number) => void;
37
+ updateFileMentionIndex: (index: number) => void;
38
+ setFileMentionDismissedInput: import("react").Dispatch<import("react").SetStateAction<string | undefined>>;
39
+ isPickerActive: boolean;
40
+ dispatchPickerInput: (value: string, key: Key, controlAction: ReturnType<typeof parseTuiControlAction>) => void;
41
+ approvalPickerIndexRef: import("react").RefObject<number>;
42
+ slashCommandIndexRef: import("react").RefObject<number>;
43
+ fileMentionIndexRef: import("react").RefObject<number>;
44
+ permissionPickerOptionsRef: import("react").RefObject<CliPermissionOption[] | undefined>;
45
+ modelPickerOptionsRef: import("react").RefObject<CliModelOption[] | undefined>;
46
+ effortPickerOptionsRef: import("react").RefObject<CliEffortOption[] | undefined>;
47
+ };
@@ -0,0 +1,288 @@
1
+ /** 四 picker(skill/permission/model/effort)及审批/slash 菜单焦点的状态与处理函数。 */
2
+ import { useRef, useState } from "react";
3
+ import { splitTuiInputGraphemes } from "../input/graphemes.js";
4
+ import { resolveTuiDeleteDirection } from "../input/keys.js";
5
+ import { filterTuiSkills } from "../pickers/skill.js";
6
+ /** 管理四个 picker 与审批/slash 菜单焦点;对外暴露 state、setter 与统一分发入口。 */
7
+ export function usePickers(input) {
8
+ const { dispatchAction, rawInputSequenceRef } = input;
9
+ const [skillPickerSkills, setSkillPickerSkills] = useState(undefined);
10
+ const [skillPickerQuery, setSkillPickerQuery] = useState("");
11
+ const [skillPickerIndex, setSkillPickerIndex] = useState(0);
12
+ const [permissionPickerOptions, setPermissionPickerOptions] = useState(undefined);
13
+ const [permissionPickerIndex, setPermissionPickerIndex] = useState(0);
14
+ const [modelPickerOptions, setModelPickerOptions] = useState(undefined);
15
+ const [modelPickerIndex, setModelPickerIndex] = useState(0);
16
+ const [effortPickerOptions, setEffortPickerOptions] = useState(undefined);
17
+ const [effortPickerIndex, setEffortPickerIndex] = useState(0);
18
+ const [approvalPickerIndex, setApprovalPickerIndex] = useState(0);
19
+ const [slashCommandIndex, setSlashCommandIndex] = useState(0);
20
+ const [fileMentionIndex, setFileMentionIndex] = useState(0);
21
+ /** Esc 关闭 @ 菜单时记录的完整输入;输入变化后菜单可复活(CC dismissedForInput 模式)。 */
22
+ const [fileMentionDismissedInput, setFileMentionDismissedInput] = useState(undefined);
23
+ const skillPickerQueryRef = useRef("");
24
+ const skillPickerIndexRef = useRef(0);
25
+ const permissionPickerIndexRef = useRef(0);
26
+ const permissionPickerOptionsRef = useRef(undefined);
27
+ const modelPickerIndexRef = useRef(0);
28
+ const modelPickerOptionsRef = useRef(undefined);
29
+ const effortPickerIndexRef = useRef(0);
30
+ const effortPickerOptionsRef = useRef(undefined);
31
+ const approvalPickerIndexRef = useRef(0);
32
+ const slashCommandIndexRef = useRef(0);
33
+ const fileMentionIndexRef = useRef(0);
34
+ /** 更新 picker 搜索词并回到筛选结果首项。 */
35
+ function updateSkillPickerQuery(query) {
36
+ skillPickerQueryRef.current = query;
37
+ setSkillPickerQuery(query);
38
+ updateSkillPickerIndex(0);
39
+ }
40
+ /** 同步保存 picker 当前行,保证连续方向键和回车不会读取旧 React 状态。 */
41
+ function updateSkillPickerIndex(index) {
42
+ skillPickerIndexRef.current = index;
43
+ setSkillPickerIndex(index);
44
+ }
45
+ /** 关闭 picker 并恢复普通单行 prompt。 */
46
+ function closeSkillPicker() {
47
+ setSkillPickerSkills(undefined);
48
+ updateSkillPickerQuery("");
49
+ }
50
+ /** 关闭权限 picker 并恢复普通 prompt。 */
51
+ function closePermissionPicker() {
52
+ permissionPickerOptionsRef.current = undefined;
53
+ setPermissionPickerOptions(undefined);
54
+ updatePermissionPickerIndex(0);
55
+ }
56
+ /** 关闭模型 picker 并恢复普通单行 prompt。 */
57
+ function closeModelPicker() {
58
+ modelPickerOptionsRef.current = undefined;
59
+ setModelPickerOptions(undefined);
60
+ updateModelPickerIndex(0);
61
+ }
62
+ /** 同步保存模型 picker 当前焦点,避免连续方向键读取旧 React 状态。 */
63
+ function updateModelPickerIndex(index) {
64
+ modelPickerIndexRef.current = index;
65
+ setModelPickerIndex(index);
66
+ }
67
+ /** 关闭 effort picker 并恢复普通单行 prompt。 */
68
+ function closeEffortPicker() {
69
+ effortPickerOptionsRef.current = undefined;
70
+ setEffortPickerOptions(undefined);
71
+ updateEffortPickerIndex(0);
72
+ }
73
+ /** 同步保存 effort picker 当前焦点,避免连续方向键读取旧 React 状态。 */
74
+ function updateEffortPickerIndex(index) {
75
+ effortPickerIndexRef.current = index;
76
+ setEffortPickerIndex(index);
77
+ }
78
+ /** 同步保存权限 picker 当前焦点,避免连续方向键读取旧 React 状态。 */
79
+ function updatePermissionPickerIndex(index) {
80
+ permissionPickerIndexRef.current = index;
81
+ setPermissionPickerIndex(index);
82
+ }
83
+ /** 同步保存审批菜单当前焦点,保证连续方向键和回车读取同一选择。 */
84
+ function updateApprovalPickerIndex(index) {
85
+ approvalPickerIndexRef.current = index;
86
+ setApprovalPickerIndex(index);
87
+ }
88
+ /** 同步保存 slash 菜单当前焦点,避免连续方向键读取旧 React 状态。 */
89
+ function updateSlashCommandIndex(index) {
90
+ slashCommandIndexRef.current = index;
91
+ setSlashCommandIndex(index);
92
+ }
93
+ /** 同步保存 @ 菜单当前焦点,避免连续方向键读取旧 React 状态。 */
94
+ function updateFileMentionIndex(index) {
95
+ fileMentionIndexRef.current = index;
96
+ setFileMentionIndex(index);
97
+ }
98
+ /** 在 Skill picker 内处理搜索、方向键、空格开关、确认和取消。 */
99
+ function handleSkillPickerInput(value, key, controlAction) {
100
+ if (controlAction === "cancel") {
101
+ closeSkillPicker();
102
+ return;
103
+ }
104
+ const skills = skillPickerSkills ?? [];
105
+ const filteredSkills = filterTuiSkills(skills, skillPickerQueryRef.current);
106
+ if (key.upArrow) {
107
+ updateSkillPickerIndex(Math.max(0, skillPickerIndexRef.current - 1));
108
+ return;
109
+ }
110
+ if (key.downArrow) {
111
+ updateSkillPickerIndex(Math.min(Math.max(0, filteredSkills.length - 1), skillPickerIndexRef.current + 1));
112
+ return;
113
+ }
114
+ if (key.return) {
115
+ closeSkillPicker();
116
+ return;
117
+ }
118
+ if (value === " ") {
119
+ const selectedSkill = filteredSkills[skillPickerIndexRef.current];
120
+ if (selectedSkill)
121
+ void dispatchAction({ type: "toggle_skill", skill: selectedSkill.qualifiedName }, undefined, true);
122
+ return;
123
+ }
124
+ const deleteDirection = resolveTuiDeleteDirection(value, key, rawInputSequenceRef.current);
125
+ if (deleteDirection) {
126
+ updateSkillPickerQuery(splitTuiInputGraphemes(skillPickerQueryRef.current).slice(0, -1).join(""));
127
+ return;
128
+ }
129
+ if (value === "/" && !skillPickerQueryRef.current)
130
+ return;
131
+ if (value && !key.ctrl && !key.meta)
132
+ updateSkillPickerQuery(skillPickerQueryRef.current + value);
133
+ }
134
+ /** 在权限 picker 内移动焦点,回车提交 Session 默认权限,Esc 只关闭界面。 */
135
+ function handlePermissionPickerInput(value, key, controlAction) {
136
+ if (controlAction === "cancel") {
137
+ closePermissionPicker();
138
+ return;
139
+ }
140
+ const options = permissionPickerOptionsRef.current ?? permissionPickerOptions ?? [];
141
+ if (key.upArrow) {
142
+ updatePermissionPickerIndex(Math.max(0, permissionPickerIndexRef.current - 1));
143
+ return;
144
+ }
145
+ if (key.downArrow) {
146
+ updatePermissionPickerIndex(Math.min(Math.max(0, options.length - 1), permissionPickerIndexRef.current + 1));
147
+ return;
148
+ }
149
+ if (value === " ") {
150
+ const focused = options[permissionPickerIndexRef.current];
151
+ if (!focused || focused.selected)
152
+ return;
153
+ const nextOptions = options.map(function selectPermission(option) {
154
+ return { ...option, selected: option.mode === focused.mode };
155
+ });
156
+ permissionPickerOptionsRef.current = nextOptions;
157
+ setPermissionPickerOptions(nextOptions);
158
+ return;
159
+ }
160
+ if (key.return) {
161
+ const selected = options.find(function findEnabledPermission(option) { return option.selected; });
162
+ closePermissionPicker();
163
+ if (selected)
164
+ void dispatchAction({ type: "set_permission_mode", mode: selected.mode });
165
+ }
166
+ }
167
+ /** 在模型 picker 内移动焦点,空格选择模型,回车只更新下一 Turn 的 Session 默认模型。 */
168
+ function handleModelPickerInput(value, key, controlAction) {
169
+ if (controlAction === "cancel") {
170
+ closeModelPicker();
171
+ return;
172
+ }
173
+ const options = modelPickerOptionsRef.current ?? modelPickerOptions ?? [];
174
+ if (key.upArrow) {
175
+ updateModelPickerIndex(Math.max(0, modelPickerIndexRef.current - 1));
176
+ return;
177
+ }
178
+ if (key.downArrow) {
179
+ updateModelPickerIndex(Math.min(Math.max(0, options.length - 1), modelPickerIndexRef.current + 1));
180
+ return;
181
+ }
182
+ if (value === " ") {
183
+ const focused = options[modelPickerIndexRef.current];
184
+ if (!focused || focused.selected)
185
+ return;
186
+ const nextOptions = options.map(function selectModel(option) {
187
+ return { ...option, selected: option.provider === focused.provider && option.modelId === focused.modelId };
188
+ });
189
+ modelPickerOptionsRef.current = nextOptions;
190
+ setModelPickerOptions(nextOptions);
191
+ return;
192
+ }
193
+ if (key.return) {
194
+ const selected = options.find(function findEnabledModel(option) { return option.selected; });
195
+ closeModelPicker();
196
+ if (selected)
197
+ void dispatchAction({ type: "set_model", provider: selected.provider, modelId: selected.modelId });
198
+ }
199
+ }
200
+ /** 在 effort picker 内移动焦点,空格选择推理强度,回车只更新下一 Turn 的 Session 默认推理强度。 */
201
+ function handleEffortPickerInput(value, key, controlAction) {
202
+ if (controlAction === "cancel") {
203
+ closeEffortPicker();
204
+ return;
205
+ }
206
+ const options = effortPickerOptionsRef.current ?? effortPickerOptions ?? [];
207
+ if (key.upArrow) {
208
+ updateEffortPickerIndex(Math.max(0, effortPickerIndexRef.current - 1));
209
+ return;
210
+ }
211
+ if (key.downArrow) {
212
+ updateEffortPickerIndex(Math.min(Math.max(0, options.length - 1), effortPickerIndexRef.current + 1));
213
+ return;
214
+ }
215
+ if (value === " ") {
216
+ const focused = options[effortPickerIndexRef.current];
217
+ if (!focused || focused.selected)
218
+ return;
219
+ const nextOptions = options.map(function selectEffort(option) {
220
+ return { ...option, selected: option.effort === focused.effort };
221
+ });
222
+ effortPickerOptionsRef.current = nextOptions;
223
+ setEffortPickerOptions(nextOptions);
224
+ return;
225
+ }
226
+ if (key.return) {
227
+ const selected = options.find(function findEnabledEffort(option) { return option.selected; });
228
+ closeEffortPicker();
229
+ if (selected)
230
+ void dispatchAction({ type: "set_effort", effort: selected.effort });
231
+ }
232
+ }
233
+ const isPickerActive = permissionPickerOptions !== undefined || modelPickerOptions !== undefined || effortPickerOptions !== undefined || skillPickerSkills !== undefined;
234
+ /** 按激活的 picker 分发按键;无激活 picker 时由 handleInput 继续处理普通输入。 */
235
+ function dispatchPickerInput(value, key, controlAction) {
236
+ if (permissionPickerOptions) {
237
+ handlePermissionPickerInput(value, key, controlAction);
238
+ return;
239
+ }
240
+ if (modelPickerOptions) {
241
+ handleModelPickerInput(value, key, controlAction);
242
+ return;
243
+ }
244
+ if (effortPickerOptions) {
245
+ handleEffortPickerInput(value, key, controlAction);
246
+ return;
247
+ }
248
+ if (skillPickerSkills) {
249
+ handleSkillPickerInput(value, key, controlAction);
250
+ return;
251
+ }
252
+ }
253
+ return {
254
+ skillPickerSkills,
255
+ skillPickerQuery,
256
+ skillPickerIndex,
257
+ permissionPickerOptions,
258
+ permissionPickerIndex,
259
+ modelPickerOptions,
260
+ modelPickerIndex,
261
+ effortPickerOptions,
262
+ effortPickerIndex,
263
+ approvalPickerIndex,
264
+ slashCommandIndex,
265
+ fileMentionIndex,
266
+ fileMentionDismissedInput,
267
+ setSkillPickerSkills,
268
+ setPermissionPickerOptions,
269
+ setModelPickerOptions,
270
+ setEffortPickerOptions,
271
+ updateSkillPickerQuery,
272
+ updatePermissionPickerIndex,
273
+ updateModelPickerIndex,
274
+ updateEffortPickerIndex,
275
+ updateApprovalPickerIndex,
276
+ updateSlashCommandIndex,
277
+ updateFileMentionIndex,
278
+ setFileMentionDismissedInput,
279
+ isPickerActive,
280
+ dispatchPickerInput,
281
+ approvalPickerIndexRef,
282
+ slashCommandIndexRef,
283
+ fileMentionIndexRef,
284
+ permissionPickerOptionsRef,
285
+ modelPickerOptionsRef,
286
+ effortPickerOptionsRef,
287
+ };
288
+ }
@@ -0,0 +1,54 @@
1
+ /** prompt 输入状态与 handleInput:字符串/光标/历史维护与按键分发。 */
2
+ import type { Key } from "ink";
3
+ import type { CliAction, CliSlashCommand } from "../../types.js";
4
+ import { parseTuiControlAction } from "../input/keys.js";
5
+ import type { CliTuiStore } from "../types.js";
6
+ /** usePromptInput 的运行依赖;派生值(slash 建议、视口按键)由 app.tsx 通过 ref 注入。 */
7
+ export interface UsePromptInputInput {
8
+ store: CliTuiStore;
9
+ columns: number;
10
+ busy: {
11
+ current: boolean;
12
+ };
13
+ keepOpenAfterCancel: {
14
+ current: boolean;
15
+ };
16
+ lastSubmittedPromptRef: {
17
+ current: string | undefined;
18
+ };
19
+ rawInputSequenceRef: {
20
+ current: string;
21
+ };
22
+ dispatchAction: (action: CliAction, prompt?: string, preserveSkillPickerQuery?: boolean) => Promise<void>;
23
+ onEof: () => void;
24
+ onRequestInterrupt: () => void;
25
+ onRequestCancel: () => void;
26
+ isPickerActive: boolean;
27
+ dispatchPickerInput: (value: string, key: Key, controlAction: ReturnType<typeof parseTuiControlAction>) => void;
28
+ approvalPickerIndexRef: {
29
+ current: number;
30
+ };
31
+ updateApprovalPickerIndex: (index: number) => void;
32
+ slashCommandIndexRef: {
33
+ current: number;
34
+ };
35
+ updateSlashCommandIndex: (index: number) => void;
36
+ fileMentionIndexRef: {
37
+ current: number;
38
+ };
39
+ updateFileMentionIndex: (index: number) => void;
40
+ setFileMentionDismissedInput: (input: string | undefined) => void;
41
+ derivedRef: {
42
+ current: {
43
+ slashCommandSuggestions: CliSlashCommand[];
44
+ fileMentionSuggestions: string[];
45
+ handleTuiViewportKey: (key: Key) => boolean;
46
+ };
47
+ };
48
+ }
49
+ /** 管理 prompt 输入状态、历史与按键分发,并暴露 handleInput 供 Ink 的 useInput 使用。 */
50
+ export declare function usePromptInput(input: UsePromptInputInput): {
51
+ input: string;
52
+ cursorIndex: number;
53
+ handleInput: (value: string, key: Key) => void;
54
+ };