@oh-my-pi/pi-coding-agent 4.6.0 → 4.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/package.json +5 -5
- package/src/cli/config-cli.ts +344 -0
- package/src/core/agent-session.ts +112 -1
- package/src/core/extensions/types.ts +2 -0
- package/src/core/settings-manager.ts +37 -0
- package/src/core/tools/ask.ts +272 -99
- package/src/core/tools/task/model-resolver.ts +28 -2
- package/src/main.ts +12 -0
- package/src/modes/interactive/components/hook-selector.ts +3 -1
- package/src/modes/interactive/components/index.ts +1 -0
- package/src/modes/interactive/components/read-tool-group.ts +12 -4
- package/src/modes/interactive/components/settings-defs.ts +9 -0
- package/src/modes/interactive/components/todo-display.ts +1 -1
- package/src/modes/interactive/components/todo-reminder.ts +42 -0
- package/src/modes/interactive/controllers/command-controller.ts +1 -0
- package/src/modes/interactive/controllers/event-controller.ts +8 -0
- package/src/modes/interactive/controllers/extension-ui-controller.ts +9 -2
- package/src/modes/interactive/controllers/input-controller.ts +1 -1
- package/src/modes/interactive/controllers/selector-controller.ts +2 -0
- package/src/modes/interactive/interactive-mode.ts +8 -3
- package/src/modes/interactive/types.ts +2 -1
- package/src/prompts/tools/ask.md +14 -0
|
@@ -3,6 +3,7 @@ import type { AgentSessionEvent } from "../../../core/agent-session";
|
|
|
3
3
|
import { detectNotificationProtocol, isNotificationSuppressed, sendNotification } from "../../../core/terminal-notify";
|
|
4
4
|
import { AssistantMessageComponent } from "../components/assistant-message";
|
|
5
5
|
import { ReadToolGroupComponent } from "../components/read-tool-group";
|
|
6
|
+
import { TodoReminderComponent } from "../components/todo-reminder";
|
|
6
7
|
import { ToolExecutionComponent } from "../components/tool-execution";
|
|
7
8
|
import { TtsrNotificationComponent } from "../components/ttsr-notification";
|
|
8
9
|
import { getSymbolTheme, theme } from "../theme/theme";
|
|
@@ -374,6 +375,13 @@ export class EventController {
|
|
|
374
375
|
this.ctx.ui.requestRender();
|
|
375
376
|
break;
|
|
376
377
|
}
|
|
378
|
+
|
|
379
|
+
case "todo_reminder": {
|
|
380
|
+
const component = new TodoReminderComponent(event.todos, event.attempt, event.maxAttempts);
|
|
381
|
+
this.ctx.chatContainer.addChild(component);
|
|
382
|
+
this.ctx.ui.requestRender();
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
377
385
|
}
|
|
378
386
|
}
|
|
379
387
|
|
|
@@ -25,7 +25,7 @@ export class ExtensionUiController {
|
|
|
25
25
|
async initHooksAndCustomTools(): Promise<void> {
|
|
26
26
|
// Create and set hook & tool UI context
|
|
27
27
|
const uiContext: ExtensionUIContext = {
|
|
28
|
-
select: (title, options,
|
|
28
|
+
select: (title, options, dialogOptions) => this.showHookSelector(title, options, dialogOptions?.initialIndex),
|
|
29
29
|
confirm: (title, message, _dialogOptions) => this.showHookConfirm(title, message),
|
|
30
30
|
input: (title, placeholder, _dialogOptions) => this.showHookInput(title, placeholder),
|
|
31
31
|
notify: (message, type) => this.showHookNotify(message, type),
|
|
@@ -141,6 +141,7 @@ export class ExtensionUiController {
|
|
|
141
141
|
this.ctx.chatContainer.addChild(
|
|
142
142
|
new Text(`${theme.fg("accent", `${theme.status.success} New session started`)}`, 1, 1),
|
|
143
143
|
);
|
|
144
|
+
await this.ctx.reloadTodos();
|
|
144
145
|
this.ctx.ui.requestRender();
|
|
145
146
|
|
|
146
147
|
return { cancelled: false };
|
|
@@ -154,6 +155,7 @@ export class ExtensionUiController {
|
|
|
154
155
|
// Update UI
|
|
155
156
|
this.ctx.chatContainer.clear();
|
|
156
157
|
this.ctx.renderInitialMessages();
|
|
158
|
+
await this.ctx.reloadTodos();
|
|
157
159
|
this.ctx.editor.setText(result.selectedText);
|
|
158
160
|
this.ctx.showStatus("Branched to new session");
|
|
159
161
|
|
|
@@ -168,6 +170,7 @@ export class ExtensionUiController {
|
|
|
168
170
|
// Update UI
|
|
169
171
|
this.ctx.chatContainer.clear();
|
|
170
172
|
this.ctx.renderInitialMessages();
|
|
173
|
+
await this.ctx.reloadTodos();
|
|
171
174
|
if (result.editorText) {
|
|
172
175
|
this.ctx.editor.setText(result.editorText);
|
|
173
176
|
}
|
|
@@ -289,6 +292,7 @@ export class ExtensionUiController {
|
|
|
289
292
|
this.ctx.chatContainer.addChild(
|
|
290
293
|
new Text(`${theme.fg("accent", `${theme.status.success} New session started`)}`, 1, 1),
|
|
291
294
|
);
|
|
295
|
+
await this.ctx.reloadTodos();
|
|
292
296
|
this.ctx.ui.requestRender();
|
|
293
297
|
|
|
294
298
|
return { cancelled: false };
|
|
@@ -305,6 +309,7 @@ export class ExtensionUiController {
|
|
|
305
309
|
// Update UI
|
|
306
310
|
this.ctx.chatContainer.clear();
|
|
307
311
|
this.ctx.renderInitialMessages();
|
|
312
|
+
await this.ctx.reloadTodos();
|
|
308
313
|
this.ctx.editor.setText(result.selectedText);
|
|
309
314
|
this.ctx.showStatus("Branched to new session");
|
|
310
315
|
|
|
@@ -322,6 +327,7 @@ export class ExtensionUiController {
|
|
|
322
327
|
// Update UI
|
|
323
328
|
this.ctx.chatContainer.clear();
|
|
324
329
|
this.ctx.renderInitialMessages();
|
|
330
|
+
await this.ctx.reloadTodos();
|
|
325
331
|
if (result.editorText) {
|
|
326
332
|
this.ctx.editor.setText(result.editorText);
|
|
327
333
|
}
|
|
@@ -425,7 +431,7 @@ export class ExtensionUiController {
|
|
|
425
431
|
/**
|
|
426
432
|
* Show a selector for hooks.
|
|
427
433
|
*/
|
|
428
|
-
showHookSelector(title: string, options: string[]): Promise<string | undefined> {
|
|
434
|
+
showHookSelector(title: string, options: string[], initialIndex?: number): Promise<string | undefined> {
|
|
429
435
|
return new Promise((resolve) => {
|
|
430
436
|
this.ctx.hookSelector = new HookSelectorComponent(
|
|
431
437
|
title,
|
|
@@ -438,6 +444,7 @@ export class ExtensionUiController {
|
|
|
438
444
|
this.hideHookSelector();
|
|
439
445
|
resolve(undefined);
|
|
440
446
|
},
|
|
447
|
+
{ initialIndex },
|
|
441
448
|
);
|
|
442
449
|
|
|
443
450
|
this.ctx.editorContainer.clear();
|
|
@@ -428,6 +428,7 @@ export class SelectorController {
|
|
|
428
428
|
// Update UI
|
|
429
429
|
this.ctx.chatContainer.clear();
|
|
430
430
|
this.ctx.renderInitialMessages();
|
|
431
|
+
await this.ctx.reloadTodos();
|
|
431
432
|
if (result.editorText) {
|
|
432
433
|
this.ctx.editor.setText(result.editorText);
|
|
433
434
|
}
|
|
@@ -500,6 +501,7 @@ export class SelectorController {
|
|
|
500
501
|
// Clear and re-render the chat
|
|
501
502
|
this.ctx.chatContainer.clear();
|
|
502
503
|
this.ctx.renderInitialMessages();
|
|
504
|
+
await this.ctx.reloadTodos();
|
|
503
505
|
this.ctx.showStatus("Resumed session");
|
|
504
506
|
}
|
|
505
507
|
|
|
@@ -446,7 +446,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
446
446
|
|
|
447
447
|
if (!this.todoExpanded && visibleTodos.length < this.todoItems.length) {
|
|
448
448
|
const remaining = this.todoItems.length - visibleTodos.length;
|
|
449
|
-
lines.push(theme.fg("muted", `${indent}${hook} +${remaining} more (Ctrl+T to expand)`));
|
|
449
|
+
lines.push(theme.fg("muted", `${indent} ${hook} +${remaining} more (Ctrl+T to expand)`));
|
|
450
450
|
}
|
|
451
451
|
|
|
452
452
|
this.todoContainer.addChild(new Text(lines.join("\n"), 1, 0));
|
|
@@ -757,6 +757,11 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
757
757
|
this.ui.requestRender();
|
|
758
758
|
}
|
|
759
759
|
|
|
760
|
+
async reloadTodos(): Promise<void> {
|
|
761
|
+
await this.loadTodoList();
|
|
762
|
+
this.ui.requestRender();
|
|
763
|
+
}
|
|
764
|
+
|
|
760
765
|
openExternalEditor(): void {
|
|
761
766
|
this.inputController.openExternalEditor();
|
|
762
767
|
}
|
|
@@ -810,8 +815,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
810
815
|
this.extensionUiController.setHookStatus(key, text);
|
|
811
816
|
}
|
|
812
817
|
|
|
813
|
-
showHookSelector(title: string, options: string[]): Promise<string | undefined> {
|
|
814
|
-
return this.extensionUiController.showHookSelector(title, options);
|
|
818
|
+
showHookSelector(title: string, options: string[], initialIndex?: number): Promise<string | undefined> {
|
|
819
|
+
return this.extensionUiController.showHookSelector(title, options, initialIndex);
|
|
815
820
|
}
|
|
816
821
|
|
|
817
822
|
hideHookSelector(): void {
|
|
@@ -124,6 +124,7 @@ export interface InteractiveModeContext {
|
|
|
124
124
|
updateEditorBorderColor(): void;
|
|
125
125
|
rebuildChatFromMessages(): void;
|
|
126
126
|
setTodos(todos: TodoItem[]): void;
|
|
127
|
+
reloadTodos(): Promise<void>;
|
|
127
128
|
toggleTodoExpansion(): void;
|
|
128
129
|
|
|
129
130
|
// Command handling
|
|
@@ -184,7 +185,7 @@ export interface InteractiveModeContext {
|
|
|
184
185
|
): Promise<void>;
|
|
185
186
|
setHookWidget(key: string, content: unknown): void;
|
|
186
187
|
setHookStatus(key: string, text: string | undefined): void;
|
|
187
|
-
showHookSelector(title: string, options: string[]): Promise<string | undefined>;
|
|
188
|
+
showHookSelector(title: string, options: string[], initialIndex?: number): Promise<string | undefined>;
|
|
188
189
|
hideHookSelector(): void;
|
|
189
190
|
showHookInput(title: string, placeholder?: string): Promise<string | undefined>;
|
|
190
191
|
hideHookInput(): void;
|
package/src/prompts/tools/ask.md
CHANGED
|
@@ -13,11 +13,25 @@ Tips:
|
|
|
13
13
|
- 2-5 concise, distinct options
|
|
14
14
|
- Users can always select "Other" for custom input
|
|
15
15
|
|
|
16
|
+
**Do NOT include an "Other" option in your options array.** The UI automatically adds "Other (type your own)" to every question. Adding your own creates duplicate "Other" options.
|
|
17
|
+
|
|
16
18
|
<example>
|
|
17
19
|
question: "Which authentication method should this API use?"
|
|
18
20
|
options: [{"label": "JWT (Recommended)"}, {"label": "OAuth2"}, {"label": "Session cookies"}]
|
|
19
21
|
</example>
|
|
20
22
|
|
|
23
|
+
## Multi-part questions
|
|
24
|
+
|
|
25
|
+
When you have multiple related questions, use the `questions` array instead of asking one at a time. Each question has its own id, options, and optional `multi` flag.
|
|
26
|
+
|
|
27
|
+
<example>
|
|
28
|
+
questions: [
|
|
29
|
+
{"id": "auth", "question": "Which auth method?", "options": [{"label": "JWT"}, {"label": "OAuth2"}]},
|
|
30
|
+
{"id": "cache", "question": "Enable caching?", "options": [{"label": "Yes"}, {"label": "No"}]},
|
|
31
|
+
{"id": "features", "question": "Which features to include?", "options": [{"label": "Logging"}, {"label": "Metrics"}, {"label": "Tracing"}], "multi": true}
|
|
32
|
+
]
|
|
33
|
+
</example>
|
|
34
|
+
|
|
21
35
|
## Critical: Resolve before asking
|
|
22
36
|
|
|
23
37
|
**Exhaust all other options before asking.** Questions interrupt user flow.
|