@oh-my-pi/pi-coding-agent 3.25.0 → 3.31.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 +90 -0
- package/package.json +5 -5
- package/src/cli/args.ts +4 -0
- package/src/core/agent-session.ts +29 -2
- package/src/core/bash-executor.ts +2 -1
- package/src/core/custom-commands/bundled/review/index.ts +369 -14
- package/src/core/custom-commands/bundled/wt/index.ts +1 -1
- package/src/core/session-manager.ts +158 -246
- package/src/core/session-storage.ts +379 -0
- package/src/core/settings-manager.ts +155 -4
- package/src/core/system-prompt.ts +62 -64
- package/src/core/tools/ask.ts +5 -4
- package/src/core/tools/bash-interceptor.ts +26 -61
- package/src/core/tools/bash.ts +13 -8
- package/src/core/tools/complete.ts +2 -4
- package/src/core/tools/edit-diff.ts +11 -4
- package/src/core/tools/edit.ts +7 -13
- package/src/core/tools/find.ts +111 -50
- package/src/core/tools/gemini-image.ts +128 -147
- package/src/core/tools/grep.ts +397 -415
- package/src/core/tools/index.test.ts +5 -1
- package/src/core/tools/index.ts +6 -8
- package/src/core/tools/jtd-to-json-schema.ts +174 -196
- package/src/core/tools/ls.ts +12 -10
- package/src/core/tools/lsp/client.ts +58 -9
- package/src/core/tools/lsp/config.ts +205 -656
- package/src/core/tools/lsp/defaults.json +465 -0
- package/src/core/tools/lsp/index.ts +55 -32
- package/src/core/tools/lsp/rust-analyzer.ts +49 -10
- package/src/core/tools/lsp/types.ts +1 -0
- package/src/core/tools/lsp/utils.ts +1 -1
- package/src/core/tools/read.ts +152 -76
- package/src/core/tools/render-utils.ts +70 -10
- package/src/core/tools/review.ts +38 -126
- package/src/core/tools/task/artifacts.ts +5 -4
- package/src/core/tools/task/executor.ts +204 -67
- package/src/core/tools/task/index.ts +129 -92
- package/src/core/tools/task/name-generator.ts +1544 -214
- package/src/core/tools/task/parallel.ts +30 -3
- package/src/core/tools/task/render.ts +85 -39
- package/src/core/tools/task/types.ts +34 -11
- package/src/core/tools/task/worker.ts +152 -27
- package/src/core/tools/web-fetch.ts +220 -1657
- package/src/core/tools/web-scrapers/academic.test.ts +239 -0
- package/src/core/tools/web-scrapers/artifacthub.ts +215 -0
- package/src/core/tools/web-scrapers/arxiv.ts +88 -0
- package/src/core/tools/web-scrapers/aur.ts +175 -0
- package/src/core/tools/web-scrapers/biorxiv.ts +141 -0
- package/src/core/tools/web-scrapers/bluesky.ts +284 -0
- package/src/core/tools/web-scrapers/brew.ts +177 -0
- package/src/core/tools/web-scrapers/business.test.ts +82 -0
- package/src/core/tools/web-scrapers/cheatsh.ts +78 -0
- package/src/core/tools/web-scrapers/chocolatey.ts +158 -0
- package/src/core/tools/web-scrapers/choosealicense.ts +110 -0
- package/src/core/tools/web-scrapers/cisa-kev.ts +100 -0
- package/src/core/tools/web-scrapers/clojars.ts +180 -0
- package/src/core/tools/web-scrapers/coingecko.ts +184 -0
- package/src/core/tools/web-scrapers/crates-io.ts +128 -0
- package/src/core/tools/web-scrapers/crossref.ts +149 -0
- package/src/core/tools/web-scrapers/dev-platforms.test.ts +254 -0
- package/src/core/tools/web-scrapers/devto.ts +177 -0
- package/src/core/tools/web-scrapers/discogs.ts +308 -0
- package/src/core/tools/web-scrapers/discourse.ts +221 -0
- package/src/core/tools/web-scrapers/dockerhub.ts +160 -0
- package/src/core/tools/web-scrapers/documentation.test.ts +85 -0
- package/src/core/tools/web-scrapers/fdroid.ts +158 -0
- package/src/core/tools/web-scrapers/finance-media.test.ts +144 -0
- package/src/core/tools/web-scrapers/firefox-addons.ts +214 -0
- package/src/core/tools/web-scrapers/flathub.ts +239 -0
- package/src/core/tools/web-scrapers/git-hosting.test.ts +272 -0
- package/src/core/tools/web-scrapers/github-gist.ts +68 -0
- package/src/core/tools/web-scrapers/github.ts +455 -0
- package/src/core/tools/web-scrapers/gitlab.ts +456 -0
- package/src/core/tools/web-scrapers/go-pkg.ts +275 -0
- package/src/core/tools/web-scrapers/hackage.ts +94 -0
- package/src/core/tools/web-scrapers/hackernews.ts +208 -0
- package/src/core/tools/web-scrapers/hex.ts +121 -0
- package/src/core/tools/web-scrapers/huggingface.ts +385 -0
- package/src/core/tools/web-scrapers/iacr.ts +86 -0
- package/src/core/tools/web-scrapers/index.ts +250 -0
- package/src/core/tools/web-scrapers/jetbrains-marketplace.ts +169 -0
- package/src/core/tools/web-scrapers/lemmy.ts +220 -0
- package/src/core/tools/web-scrapers/lobsters.ts +186 -0
- package/src/core/tools/web-scrapers/mastodon.ts +310 -0
- package/src/core/tools/web-scrapers/maven.ts +152 -0
- package/src/core/tools/web-scrapers/mdn.ts +174 -0
- package/src/core/tools/web-scrapers/media.test.ts +138 -0
- package/src/core/tools/web-scrapers/metacpan.ts +253 -0
- package/src/core/tools/web-scrapers/musicbrainz.ts +273 -0
- package/src/core/tools/web-scrapers/npm.ts +114 -0
- package/src/core/tools/web-scrapers/nuget.ts +205 -0
- package/src/core/tools/web-scrapers/nvd.ts +243 -0
- package/src/core/tools/web-scrapers/ollama.ts +267 -0
- package/src/core/tools/web-scrapers/open-vsx.ts +119 -0
- package/src/core/tools/web-scrapers/opencorporates.ts +275 -0
- package/src/core/tools/web-scrapers/openlibrary.ts +319 -0
- package/src/core/tools/web-scrapers/orcid.ts +299 -0
- package/src/core/tools/web-scrapers/osv.ts +189 -0
- package/src/core/tools/web-scrapers/package-managers-2.test.ts +199 -0
- package/src/core/tools/web-scrapers/package-managers.test.ts +171 -0
- package/src/core/tools/web-scrapers/package-registries.test.ts +259 -0
- package/src/core/tools/web-scrapers/packagist.ts +174 -0
- package/src/core/tools/web-scrapers/pub-dev.ts +185 -0
- package/src/core/tools/web-scrapers/pubmed.ts +178 -0
- package/src/core/tools/web-scrapers/pypi.ts +129 -0
- package/src/core/tools/web-scrapers/rawg.ts +124 -0
- package/src/core/tools/web-scrapers/readthedocs.ts +126 -0
- package/src/core/tools/web-scrapers/reddit.ts +104 -0
- package/src/core/tools/web-scrapers/repology.ts +262 -0
- package/src/core/tools/web-scrapers/research.test.ts +107 -0
- package/src/core/tools/web-scrapers/rfc.ts +209 -0
- package/src/core/tools/web-scrapers/rubygems.ts +117 -0
- package/src/core/tools/web-scrapers/searchcode.ts +217 -0
- package/src/core/tools/web-scrapers/sec-edgar.ts +274 -0
- package/src/core/tools/web-scrapers/security.test.ts +103 -0
- package/src/core/tools/web-scrapers/semantic-scholar.ts +190 -0
- package/src/core/tools/web-scrapers/snapcraft.ts +200 -0
- package/src/core/tools/web-scrapers/social-extended.test.ts +192 -0
- package/src/core/tools/web-scrapers/social.test.ts +259 -0
- package/src/core/tools/web-scrapers/sourcegraph.ts +373 -0
- package/src/core/tools/web-scrapers/spdx.ts +121 -0
- package/src/core/tools/web-scrapers/spotify.ts +218 -0
- package/src/core/tools/web-scrapers/stackexchange.test.ts +120 -0
- package/src/core/tools/web-scrapers/stackoverflow.ts +124 -0
- package/src/core/tools/web-scrapers/standards.test.ts +122 -0
- package/src/core/tools/web-scrapers/terraform.ts +304 -0
- package/src/core/tools/web-scrapers/tldr.ts +51 -0
- package/src/core/tools/web-scrapers/twitter.ts +96 -0
- package/src/core/tools/web-scrapers/types.ts +234 -0
- package/src/core/tools/web-scrapers/utils.ts +162 -0
- package/src/core/tools/web-scrapers/vimeo.ts +152 -0
- package/src/core/tools/web-scrapers/vscode-marketplace.ts +195 -0
- package/src/core/tools/web-scrapers/w3c.ts +163 -0
- package/src/core/tools/web-scrapers/wikidata.ts +357 -0
- package/src/core/tools/web-scrapers/wikipedia.test.ts +73 -0
- package/src/core/tools/web-scrapers/wikipedia.ts +95 -0
- package/src/core/tools/web-scrapers/youtube.test.ts +198 -0
- package/src/core/tools/web-scrapers/youtube.ts +371 -0
- package/src/core/tools/write.ts +21 -18
- package/src/core/voice.ts +3 -2
- package/src/lib/worktree/collapse.ts +2 -1
- package/src/lib/worktree/git.ts +2 -18
- package/src/main.ts +59 -3
- package/src/modes/interactive/components/extensions/extension-dashboard.ts +33 -19
- package/src/modes/interactive/components/extensions/extension-list.ts +15 -8
- package/src/modes/interactive/components/hook-editor.ts +2 -1
- package/src/modes/interactive/components/model-selector.ts +19 -4
- package/src/modes/interactive/interactive-mode.ts +41 -38
- package/src/modes/interactive/theme/theme.ts +58 -58
- package/src/modes/rpc/rpc-mode.ts +10 -9
- package/src/prompts/review-request.md +27 -0
- package/src/prompts/reviewer.md +64 -68
- package/src/prompts/tools/output.md +22 -3
- package/src/prompts/tools/task.md +32 -33
- package/src/utils/clipboard.ts +2 -1
- package/src/utils/tools-manager.ts +110 -8
- package/examples/extensions/subagent/agents/reviewer.md +0 -35
|
@@ -31,7 +31,7 @@ export interface ExtensionListCallbacks {
|
|
|
31
31
|
masterSwitchProvider?: string | null;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const
|
|
34
|
+
const DEFAULT_MAX_VISIBLE = 15;
|
|
35
35
|
|
|
36
36
|
/** Flattened list item for rendering */
|
|
37
37
|
type ListItem =
|
|
@@ -48,14 +48,21 @@ export class ExtensionList implements Component {
|
|
|
48
48
|
private focused = false;
|
|
49
49
|
private callbacks: ExtensionListCallbacks;
|
|
50
50
|
private masterSwitchProvider: string | null = null;
|
|
51
|
+
private maxVisible: number;
|
|
51
52
|
|
|
52
|
-
constructor(extensions: Extension[], callbacks: ExtensionListCallbacks = {}) {
|
|
53
|
+
constructor(extensions: Extension[], callbacks: ExtensionListCallbacks = {}, maxVisible?: number) {
|
|
53
54
|
this.extensions = extensions;
|
|
54
55
|
this.callbacks = callbacks;
|
|
55
56
|
this.masterSwitchProvider = callbacks.masterSwitchProvider ?? null;
|
|
57
|
+
this.maxVisible = maxVisible ?? DEFAULT_MAX_VISIBLE;
|
|
56
58
|
this.rebuildList();
|
|
57
59
|
}
|
|
58
60
|
|
|
61
|
+
setMaxVisible(maxVisible: number): void {
|
|
62
|
+
this.maxVisible = maxVisible;
|
|
63
|
+
this.clampSelection();
|
|
64
|
+
}
|
|
65
|
+
|
|
59
66
|
setExtensions(extensions: Extension[]): void {
|
|
60
67
|
this.extensions = extensions;
|
|
61
68
|
this.rebuildList();
|
|
@@ -126,7 +133,7 @@ export class ExtensionList implements Component {
|
|
|
126
133
|
|
|
127
134
|
// Calculate visible range
|
|
128
135
|
const startIdx = this.scrollOffset;
|
|
129
|
-
const endIdx = Math.min(startIdx +
|
|
136
|
+
const endIdx = Math.min(startIdx + this.maxVisible, this.listItems.length);
|
|
130
137
|
|
|
131
138
|
// Render visible items
|
|
132
139
|
for (let i = startIdx; i < endIdx; i++) {
|
|
@@ -143,7 +150,7 @@ export class ExtensionList implements Component {
|
|
|
143
150
|
}
|
|
144
151
|
|
|
145
152
|
// Scroll indicator
|
|
146
|
-
if (this.listItems.length >
|
|
153
|
+
if (this.listItems.length > this.maxVisible) {
|
|
147
154
|
const indicator = theme.fg("muted", ` (${this.selectedIndex + 1}/${this.listItems.length})`);
|
|
148
155
|
lines.push(indicator);
|
|
149
156
|
}
|
|
@@ -388,8 +395,8 @@ export class ExtensionList implements Component {
|
|
|
388
395
|
// Adjust scroll offset
|
|
389
396
|
if (this.selectedIndex < this.scrollOffset) {
|
|
390
397
|
this.scrollOffset = this.selectedIndex;
|
|
391
|
-
} else if (this.selectedIndex >= this.scrollOffset +
|
|
392
|
-
this.scrollOffset = this.selectedIndex -
|
|
398
|
+
} else if (this.selectedIndex >= this.scrollOffset + this.maxVisible) {
|
|
399
|
+
this.scrollOffset = this.selectedIndex - this.maxVisible + 1;
|
|
393
400
|
}
|
|
394
401
|
}
|
|
395
402
|
|
|
@@ -470,8 +477,8 @@ export class ExtensionList implements Component {
|
|
|
470
477
|
private moveSelectionDown(): void {
|
|
471
478
|
if (this.selectedIndex < this.listItems.length - 1) {
|
|
472
479
|
this.selectedIndex++;
|
|
473
|
-
if (this.selectedIndex >= this.scrollOffset +
|
|
474
|
-
this.scrollOffset = this.selectedIndex -
|
|
480
|
+
if (this.selectedIndex >= this.scrollOffset + this.maxVisible) {
|
|
481
|
+
this.scrollOffset = this.selectedIndex - this.maxVisible + 1;
|
|
475
482
|
}
|
|
476
483
|
this.notifySelectionChange();
|
|
477
484
|
}
|
|
@@ -7,6 +7,7 @@ import * as fs from "node:fs";
|
|
|
7
7
|
import * as os from "node:os";
|
|
8
8
|
import * as path from "node:path";
|
|
9
9
|
import { Container, Editor, isCtrlG, isEscape, Spacer, Text, type TUI } from "@oh-my-pi/pi-tui";
|
|
10
|
+
import { nanoid } from "nanoid";
|
|
10
11
|
import { getEditorTheme, theme } from "../theme/theme";
|
|
11
12
|
import { DynamicBorder } from "./dynamic-border";
|
|
12
13
|
|
|
@@ -90,7 +91,7 @@ export class HookEditorComponent extends Container {
|
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
const currentText = this.editor.getText();
|
|
93
|
-
const tmpFile = path.join(os.tmpdir(), `omp-hook-editor-${
|
|
94
|
+
const tmpFile = path.join(os.tmpdir(), `omp-hook-editor-${nanoid()}.md`);
|
|
94
95
|
|
|
95
96
|
try {
|
|
96
97
|
fs.writeFileSync(tmpFile, currentText, "utf-8");
|
|
@@ -34,7 +34,7 @@ interface ScopedModelItem {
|
|
|
34
34
|
thinkingLevel: string;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
type ModelRole = "default" | "smol" | "slow";
|
|
37
|
+
type ModelRole = "default" | "smol" | "slow" | "temporary";
|
|
38
38
|
|
|
39
39
|
interface MenuAction {
|
|
40
40
|
label: string;
|
|
@@ -75,6 +75,7 @@ export class ModelSelectorComponent extends Container {
|
|
|
75
75
|
private errorMessage?: string;
|
|
76
76
|
private tui: TUI;
|
|
77
77
|
private scopedModels: ReadonlyArray<ScopedModelItem>;
|
|
78
|
+
private temporaryOnly: boolean;
|
|
78
79
|
|
|
79
80
|
// Tab state
|
|
80
81
|
private providers: string[] = [ALL_TAB];
|
|
@@ -92,6 +93,7 @@ export class ModelSelectorComponent extends Container {
|
|
|
92
93
|
scopedModels: ReadonlyArray<ScopedModelItem>,
|
|
93
94
|
onSelect: (model: Model<any>, role: string) => void,
|
|
94
95
|
onCancel: () => void,
|
|
96
|
+
options?: { temporaryOnly?: boolean },
|
|
95
97
|
) {
|
|
96
98
|
super();
|
|
97
99
|
|
|
@@ -102,6 +104,7 @@ export class ModelSelectorComponent extends Container {
|
|
|
102
104
|
this.scopedModels = scopedModels;
|
|
103
105
|
this.onSelectCallback = onSelect;
|
|
104
106
|
this.onCancelCallback = onCancel;
|
|
107
|
+
this.temporaryOnly = options?.temporaryOnly ?? false;
|
|
105
108
|
|
|
106
109
|
// Load current role assignments from settings
|
|
107
110
|
this._loadRoleModels();
|
|
@@ -483,10 +486,16 @@ export class ModelSelectorComponent extends Container {
|
|
|
483
486
|
return;
|
|
484
487
|
}
|
|
485
488
|
|
|
486
|
-
// Enter - open context menu
|
|
489
|
+
// Enter - open context menu or select directly in temporary mode
|
|
487
490
|
if (isEnter(keyData)) {
|
|
488
|
-
|
|
489
|
-
|
|
491
|
+
const selectedModel = this.filteredModels[this.selectedIndex];
|
|
492
|
+
if (selectedModel) {
|
|
493
|
+
if (this.temporaryOnly) {
|
|
494
|
+
// In temporary mode, skip menu and select directly
|
|
495
|
+
this.handleSelect(selectedModel.model, "temporary");
|
|
496
|
+
} else {
|
|
497
|
+
this.openMenu();
|
|
498
|
+
}
|
|
490
499
|
}
|
|
491
500
|
return;
|
|
492
501
|
}
|
|
@@ -536,6 +545,12 @@ export class ModelSelectorComponent extends Container {
|
|
|
536
545
|
}
|
|
537
546
|
|
|
538
547
|
private handleSelect(model: Model<any>, role: ModelRole): void {
|
|
548
|
+
// For temporary role, don't save to settings - just notify caller
|
|
549
|
+
if (role === "temporary") {
|
|
550
|
+
this.onSelectCallback(model, role);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
|
|
539
554
|
// Save to settings
|
|
540
555
|
this.settingsManager.setModelRole(role, `${model.provider}/${model.id}`);
|
|
541
556
|
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
TUI,
|
|
24
24
|
visibleWidth,
|
|
25
25
|
} from "@oh-my-pi/pi-tui";
|
|
26
|
+
import { nanoid } from "nanoid";
|
|
26
27
|
import { getAuthPath, getDebugLogPath } from "../../config";
|
|
27
28
|
import type { AgentSession, AgentSessionEvent } from "../../core/agent-session";
|
|
28
29
|
import type { ExtensionUIContext } from "../../core/extensions/index";
|
|
@@ -941,9 +942,9 @@ export class InteractiveMode {
|
|
|
941
942
|
this.editor.onCtrlD = () => this.handleCtrlD();
|
|
942
943
|
this.editor.onCtrlZ = () => this.handleCtrlZ();
|
|
943
944
|
this.editor.onShiftTab = () => this.cycleThinkingLevel();
|
|
944
|
-
this.editor.onCtrlP = () => this.
|
|
945
|
-
this.editor.onShiftCtrlP = () => this.
|
|
946
|
-
this.editor.onCtrlY = () => this.
|
|
945
|
+
this.editor.onCtrlP = () => this.cycleRoleModel();
|
|
946
|
+
this.editor.onShiftCtrlP = () => this.cycleRoleModel({ temporary: true });
|
|
947
|
+
this.editor.onCtrlY = () => this.showModelSelector({ temporaryOnly: true });
|
|
947
948
|
|
|
948
949
|
// Global debug handler on TUI (works regardless of focus)
|
|
949
950
|
this.ui.onDebug = () => this.handleDebugCommand();
|
|
@@ -951,9 +952,6 @@ export class InteractiveMode {
|
|
|
951
952
|
this.editor.onCtrlO = () => this.toggleToolOutputExpansion();
|
|
952
953
|
this.editor.onCtrlT = () => this.toggleThinkingBlockVisibility();
|
|
953
954
|
this.editor.onCtrlG = () => this.openExternalEditor();
|
|
954
|
-
this.editor.onCtrlY = () => {
|
|
955
|
-
void this.toggleVoiceListening();
|
|
956
|
-
};
|
|
957
955
|
this.editor.onQuestionMark = () => this.handleHotkeysCommand();
|
|
958
956
|
this.editor.onCtrlV = () => this.handleImagePaste();
|
|
959
957
|
|
|
@@ -991,6 +989,14 @@ export class InteractiveMode {
|
|
|
991
989
|
private setupEditorSubmitHandler(): void {
|
|
992
990
|
this.editor.onSubmit = async (text: string) => {
|
|
993
991
|
text = text.trim();
|
|
992
|
+
|
|
993
|
+
// Empty submit while streaming with queued messages: flush queues immediately
|
|
994
|
+
if (!text && this.session.isStreaming && this.session.queuedMessageCount > 0) {
|
|
995
|
+
// Abort current stream and let queued messages be processed
|
|
996
|
+
await this.session.abort();
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
|
|
994
1000
|
if (!text) return;
|
|
995
1001
|
|
|
996
1002
|
// Handle slash commands
|
|
@@ -1994,27 +2000,9 @@ export class InteractiveMode {
|
|
|
1994
2000
|
}
|
|
1995
2001
|
}
|
|
1996
2002
|
|
|
1997
|
-
private async
|
|
1998
|
-
try {
|
|
1999
|
-
const result = await this.session.cycleModel(direction);
|
|
2000
|
-
if (result === undefined) {
|
|
2001
|
-
const msg = this.session.scopedModels.length > 0 ? "Only one model in scope" : "Only one model available";
|
|
2002
|
-
this.showStatus(msg);
|
|
2003
|
-
} else {
|
|
2004
|
-
this.statusLine.invalidate();
|
|
2005
|
-
this.updateEditorBorderColor();
|
|
2006
|
-
const thinkingStr =
|
|
2007
|
-
result.model.reasoning && result.thinkingLevel !== "off" ? ` (thinking: ${result.thinkingLevel})` : "";
|
|
2008
|
-
this.showStatus(`Switched to ${result.model.name || result.model.id}${thinkingStr}`);
|
|
2009
|
-
}
|
|
2010
|
-
} catch (error) {
|
|
2011
|
-
this.showError(error instanceof Error ? error.message : String(error));
|
|
2012
|
-
}
|
|
2013
|
-
}
|
|
2014
|
-
|
|
2015
|
-
private async cycleRoleModel(): Promise<void> {
|
|
2003
|
+
private async cycleRoleModel(options?: { temporary?: boolean }): Promise<void> {
|
|
2016
2004
|
try {
|
|
2017
|
-
const result = await this.session.cycleRoleModels(["slow", "default", "smol"]);
|
|
2005
|
+
const result = await this.session.cycleRoleModels(["slow", "default", "smol"], options);
|
|
2018
2006
|
if (!result) {
|
|
2019
2007
|
this.showStatus("Only one role model available");
|
|
2020
2008
|
return;
|
|
@@ -2025,7 +2013,8 @@ export class InteractiveMode {
|
|
|
2025
2013
|
const roleLabel = result.role === "default" ? "default" : result.role;
|
|
2026
2014
|
const thinkingStr =
|
|
2027
2015
|
result.model.reasoning && result.thinkingLevel !== "off" ? ` (thinking: ${result.thinkingLevel})` : "";
|
|
2028
|
-
|
|
2016
|
+
const tempLabel = options?.temporary ? " (temporary)" : "";
|
|
2017
|
+
this.showStatus(`Switched to ${roleLabel}: ${result.model.name || result.model.id}${thinkingStr}${tempLabel}`);
|
|
2029
2018
|
} catch (error) {
|
|
2030
2019
|
this.showError(error instanceof Error ? error.message : String(error));
|
|
2031
2020
|
}
|
|
@@ -2068,7 +2057,7 @@ export class InteractiveMode {
|
|
|
2068
2057
|
}
|
|
2069
2058
|
|
|
2070
2059
|
const currentText = this.editor.getText();
|
|
2071
|
-
const tmpFile = path.join(os.tmpdir(), `omp-editor-${
|
|
2060
|
+
const tmpFile = path.join(os.tmpdir(), `omp-editor-${nanoid()}.omp.md`);
|
|
2072
2061
|
|
|
2073
2062
|
try {
|
|
2074
2063
|
// Write current content to temp file
|
|
@@ -2255,7 +2244,7 @@ export class InteractiveMode {
|
|
|
2255
2244
|
*/
|
|
2256
2245
|
private showExtensionsDashboard(): void {
|
|
2257
2246
|
this.showSelector((done) => {
|
|
2258
|
-
const dashboard = new ExtensionDashboard(process.cwd(), this.settingsManager);
|
|
2247
|
+
const dashboard = new ExtensionDashboard(process.cwd(), this.settingsManager, this.ui.terminal.rows);
|
|
2259
2248
|
dashboard.onClose = () => {
|
|
2260
2249
|
done();
|
|
2261
2250
|
this.ui.requestRender();
|
|
@@ -2379,7 +2368,7 @@ export class InteractiveMode {
|
|
|
2379
2368
|
}
|
|
2380
2369
|
}
|
|
2381
2370
|
|
|
2382
|
-
private showModelSelector(): void {
|
|
2371
|
+
private showModelSelector(options?: { temporaryOnly?: boolean }): void {
|
|
2383
2372
|
this.showSelector((done) => {
|
|
2384
2373
|
const selector = new ModelSelectorComponent(
|
|
2385
2374
|
this.ui,
|
|
@@ -2389,24 +2378,36 @@ export class InteractiveMode {
|
|
|
2389
2378
|
this.session.scopedModels,
|
|
2390
2379
|
async (model, role) => {
|
|
2391
2380
|
try {
|
|
2392
|
-
|
|
2393
|
-
|
|
2381
|
+
if (role === "temporary") {
|
|
2382
|
+
// Temporary: update agent state but don't persist to settings
|
|
2383
|
+
await this.session.setModelTemporary(model);
|
|
2384
|
+
this.statusLine.invalidate();
|
|
2385
|
+
this.updateEditorBorderColor();
|
|
2386
|
+
this.showStatus(`Temporary model: ${model.id}`);
|
|
2387
|
+
done();
|
|
2388
|
+
this.ui.requestRender();
|
|
2389
|
+
} else if (role === "default") {
|
|
2390
|
+
// Default: update agent state and persist
|
|
2394
2391
|
await this.session.setModel(model, role);
|
|
2395
2392
|
this.statusLine.invalidate();
|
|
2396
2393
|
this.updateEditorBorderColor();
|
|
2394
|
+
this.showStatus(`Default model: ${model.id}`);
|
|
2395
|
+
// Don't call done() - selector stays open for role assignment
|
|
2396
|
+
} else {
|
|
2397
|
+
// Other roles (smol, slow): just update settings, not current model
|
|
2398
|
+
const roleLabel = role === "smol" ? "Smol" : role;
|
|
2399
|
+
this.showStatus(`${roleLabel} model: ${model.id}`);
|
|
2400
|
+
// Don't call done() - selector stays open
|
|
2397
2401
|
}
|
|
2398
|
-
// For other roles (small), just show status - settings already updated by selector
|
|
2399
|
-
const roleLabel = role === "default" ? "Default" : role === "smol" ? "Smol" : role;
|
|
2400
|
-
this.showStatus(`${roleLabel} model: ${model.id}`);
|
|
2401
2402
|
} catch (error) {
|
|
2402
2403
|
this.showError(error instanceof Error ? error.message : String(error));
|
|
2403
2404
|
}
|
|
2404
|
-
// Don't call done() - selector stays open
|
|
2405
2405
|
},
|
|
2406
2406
|
() => {
|
|
2407
2407
|
done();
|
|
2408
2408
|
this.ui.requestRender();
|
|
2409
2409
|
},
|
|
2410
|
+
options,
|
|
2410
2411
|
);
|
|
2411
2412
|
return { component: selector, focus: selector };
|
|
2412
2413
|
});
|
|
@@ -2994,8 +2995,10 @@ export class InteractiveMode {
|
|
|
2994
2995
|
| \`Ctrl+D\` | Exit (when editor is empty) |
|
|
2995
2996
|
| \`Ctrl+Z\` | Suspend to background |
|
|
2996
2997
|
| \`Shift+Tab\` | Cycle thinking level |
|
|
2997
|
-
| \`Ctrl+P\` | Cycle models |
|
|
2998
|
-
| \`Ctrl+
|
|
2998
|
+
| \`Ctrl+P\` | Cycle role models (slow/default/smol) |
|
|
2999
|
+
| \`Shift+Ctrl+P\` | Cycle role models (temporary) |
|
|
3000
|
+
| \`Ctrl+Y\` | Select model (temporary) |
|
|
3001
|
+
| \`Ctrl+L\` | Select model (set roles) |
|
|
2999
3002
|
| \`Ctrl+O\` | Toggle tool output expansion |
|
|
3000
3003
|
| \`Ctrl+T\` | Toggle thinking block visibility |
|
|
3001
3004
|
| \`Ctrl+G\` | Edit message in external editor |
|
|
@@ -315,18 +315,18 @@ const UNICODE_SYMBOLS: SymbolMap = {
|
|
|
315
315
|
"icon.rewind": "↩",
|
|
316
316
|
// pick: ⚡ | alt: ✨ ✦
|
|
317
317
|
"icon.auto": "⚡",
|
|
318
|
-
// pick:
|
|
319
|
-
"icon.extensionSkill": "
|
|
320
|
-
// pick:
|
|
321
|
-
"icon.extensionTool": "
|
|
318
|
+
// pick: ✧ | alt: ⚙ SK 🧠
|
|
319
|
+
"icon.extensionSkill": "✧",
|
|
320
|
+
// pick: ⚒ | alt: ⛭ TL 🛠
|
|
321
|
+
"icon.extensionTool": "⚒",
|
|
322
322
|
// pick: / | alt: ⌘ ⌥
|
|
323
323
|
"icon.extensionSlashCommand": "/",
|
|
324
|
-
// pick:
|
|
325
|
-
"icon.extensionMcp": "
|
|
326
|
-
// pick:
|
|
327
|
-
"icon.extensionRule": "
|
|
328
|
-
// pick:
|
|
329
|
-
"icon.extensionHook": "
|
|
324
|
+
// pick: ◈ | alt: ⧫ MCP 🔌
|
|
325
|
+
"icon.extensionMcp": "◈",
|
|
326
|
+
// pick: § | alt: ⚖ RL 📏
|
|
327
|
+
"icon.extensionRule": "§",
|
|
328
|
+
// pick: ↪ | alt: ⚓ HK 🪝
|
|
329
|
+
"icon.extensionHook": "↪",
|
|
330
330
|
// pick: PR | alt: 💬 ✎
|
|
331
331
|
"icon.extensionPrompt": "PR",
|
|
332
332
|
// pick: CF | alt: 📄 📎
|
|
@@ -356,10 +356,10 @@ const UNICODE_SYMBOLS: SymbolMap = {
|
|
|
356
356
|
"format.bullet": "•",
|
|
357
357
|
// pick: – | alt: — ― -
|
|
358
358
|
"format.dash": "–",
|
|
359
|
-
// pick:
|
|
360
|
-
"format.bracketLeft": "
|
|
361
|
-
// pick:
|
|
362
|
-
"format.bracketRight": "
|
|
359
|
+
// pick: ⟨ | alt: [ ⟦
|
|
360
|
+
"format.bracketLeft": "⟨",
|
|
361
|
+
// pick: ⟩ | alt: ] ⟧
|
|
362
|
+
"format.bracketRight": "⟩",
|
|
363
363
|
// Markdown-specific
|
|
364
364
|
// pick: │ | alt: ┃ ║
|
|
365
365
|
"md.quoteBorder": "│",
|
|
@@ -574,15 +574,15 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
574
574
|
"icon.extensionInstruction": "\uf02d",
|
|
575
575
|
// Thinking Levels - emoji labels
|
|
576
576
|
// pick: 🤨 min | alt: min min
|
|
577
|
-
"thinking.minimal": "
|
|
577
|
+
"thinking.minimal": "\u{F0E7} min",
|
|
578
578
|
// pick: 🤔 low | alt: low low
|
|
579
|
-
"thinking.low": "
|
|
579
|
+
"thinking.low": "\u{F10C} low",
|
|
580
580
|
// pick: 🤓 med | alt: med med
|
|
581
|
-
"thinking.medium": "
|
|
581
|
+
"thinking.medium": "\u{F192} med",
|
|
582
582
|
// pick: 🤯 high | alt: high high
|
|
583
|
-
"thinking.high": "
|
|
583
|
+
"thinking.high": "\u{F111} high",
|
|
584
584
|
// pick: 🧠 xhi | alt: xhi xhi
|
|
585
|
-
"thinking.xhigh": "
|
|
585
|
+
"thinking.xhigh": "\u{F06D} xhi",
|
|
586
586
|
// Checkboxes
|
|
587
587
|
// pick: | alt:
|
|
588
588
|
"checkbox.checked": "\uf14a",
|
|
@@ -595,10 +595,10 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
595
595
|
"format.bullet": "\uf111",
|
|
596
596
|
// pick: – | alt: — ― -
|
|
597
597
|
"format.dash": "\u2013",
|
|
598
|
-
// pick:
|
|
599
|
-
"format.bracketLeft": "
|
|
600
|
-
// pick:
|
|
601
|
-
"format.bracketRight": "
|
|
598
|
+
// pick: ⟨ | alt: [ ⟦
|
|
599
|
+
"format.bracketLeft": "⟨",
|
|
600
|
+
// pick: ⟩ | alt: ] ⟧
|
|
601
|
+
"format.bracketRight": "⟩",
|
|
602
602
|
// Markdown-specific
|
|
603
603
|
// pick: │ | alt: ┃ ║
|
|
604
604
|
"md.quoteBorder": "\u2502",
|
|
@@ -608,41 +608,41 @@ const NERD_SYMBOLS: SymbolMap = {
|
|
|
608
608
|
"md.bullet": "\uf111",
|
|
609
609
|
// Language icons (nerd font devicons)
|
|
610
610
|
"lang.default": "",
|
|
611
|
-
"lang.typescript": "
|
|
612
|
-
"lang.javascript": "
|
|
613
|
-
"lang.python": "
|
|
614
|
-
"lang.rust": "
|
|
615
|
-
"lang.go": "
|
|
616
|
-
"lang.java": "
|
|
617
|
-
"lang.c": "
|
|
618
|
-
"lang.cpp": "
|
|
619
|
-
"lang.csharp": "
|
|
620
|
-
"lang.ruby": "
|
|
621
|
-
"lang.php": "
|
|
622
|
-
"lang.swift": "
|
|
623
|
-
"lang.kotlin": "
|
|
624
|
-
"lang.shell": "
|
|
625
|
-
"lang.html": "
|
|
626
|
-
"lang.css": "
|
|
627
|
-
"lang.json": "
|
|
628
|
-
"lang.yaml": "
|
|
629
|
-
"lang.markdown": "
|
|
630
|
-
"lang.sql": "
|
|
631
|
-
"lang.docker": "
|
|
632
|
-
"lang.lua": "
|
|
633
|
-
"lang.text": "
|
|
634
|
-
"lang.env": "
|
|
635
|
-
"lang.toml": "
|
|
636
|
-
"lang.xml": "
|
|
637
|
-
"lang.ini": "
|
|
638
|
-
"lang.conf": "
|
|
639
|
-
"lang.log": "
|
|
640
|
-
"lang.csv": "
|
|
641
|
-
"lang.tsv": "
|
|
642
|
-
"lang.image": "
|
|
643
|
-
"lang.pdf": "
|
|
644
|
-
"lang.archive": "
|
|
645
|
-
"lang.binary": "
|
|
611
|
+
"lang.typescript": "\u{E628}",
|
|
612
|
+
"lang.javascript": "\u{E60C}",
|
|
613
|
+
"lang.python": "\u{E606}",
|
|
614
|
+
"lang.rust": "\u{E7A8}",
|
|
615
|
+
"lang.go": "\u{E627}",
|
|
616
|
+
"lang.java": "\u{E738}",
|
|
617
|
+
"lang.c": "\u{E61E}",
|
|
618
|
+
"lang.cpp": "\u{E61D}",
|
|
619
|
+
"lang.csharp": "\u{E7BC}",
|
|
620
|
+
"lang.ruby": "\u{E791}",
|
|
621
|
+
"lang.php": "\u{E608}",
|
|
622
|
+
"lang.swift": "\u{E755}",
|
|
623
|
+
"lang.kotlin": "\u{E634}",
|
|
624
|
+
"lang.shell": "\u{E795}",
|
|
625
|
+
"lang.html": "\u{E736}",
|
|
626
|
+
"lang.css": "\u{E749}",
|
|
627
|
+
"lang.json": "\u{E60B}",
|
|
628
|
+
"lang.yaml": "\u{E615}",
|
|
629
|
+
"lang.markdown": "\u{E609}",
|
|
630
|
+
"lang.sql": "\u{E706}",
|
|
631
|
+
"lang.docker": "\u{E7B0}",
|
|
632
|
+
"lang.lua": "\u{E620}",
|
|
633
|
+
"lang.text": "\u{E612}",
|
|
634
|
+
"lang.env": "\u{E615}",
|
|
635
|
+
"lang.toml": "\u{E615}",
|
|
636
|
+
"lang.xml": "\u{F05C0}",
|
|
637
|
+
"lang.ini": "\u{E615}",
|
|
638
|
+
"lang.conf": "\u{E615}",
|
|
639
|
+
"lang.log": "\u{F0331}",
|
|
640
|
+
"lang.csv": "\u{F021B}",
|
|
641
|
+
"lang.tsv": "\u{F021B}",
|
|
642
|
+
"lang.image": "\u{F021F}",
|
|
643
|
+
"lang.pdf": "\u{F0226}",
|
|
644
|
+
"lang.archive": "\u{F187}",
|
|
645
|
+
"lang.binary": "\u{F019A}",
|
|
646
646
|
};
|
|
647
647
|
|
|
648
648
|
const ASCII_SYMBOLS: SymbolMap = {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* - Extension UI: Extension UI requests are emitted, client responds with extension_ui_response
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import { nanoid } from "nanoid";
|
|
14
15
|
import type { AgentSession } from "../../core/agent-session";
|
|
15
16
|
import type { ExtensionUIContext } from "../../core/extensions/index";
|
|
16
17
|
import { theme } from "../interactive/theme/theme";
|
|
@@ -66,7 +67,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
|
|
|
66
67
|
*/
|
|
67
68
|
const createExtensionUIContext = (): ExtensionUIContext => ({
|
|
68
69
|
async select(title: string, options: string[]): Promise<string | undefined> {
|
|
69
|
-
const id =
|
|
70
|
+
const id = nanoid();
|
|
70
71
|
return new Promise((resolve, reject) => {
|
|
71
72
|
pendingExtensionRequests.set(id, {
|
|
72
73
|
resolve: (response: RpcExtensionUIResponse) => {
|
|
@@ -85,7 +86,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
|
|
|
85
86
|
},
|
|
86
87
|
|
|
87
88
|
async confirm(title: string, message: string): Promise<boolean> {
|
|
88
|
-
const id =
|
|
89
|
+
const id = nanoid();
|
|
89
90
|
return new Promise((resolve, reject) => {
|
|
90
91
|
pendingExtensionRequests.set(id, {
|
|
91
92
|
resolve: (response: RpcExtensionUIResponse) => {
|
|
@@ -104,7 +105,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
|
|
|
104
105
|
},
|
|
105
106
|
|
|
106
107
|
async input(title: string, placeholder?: string): Promise<string | undefined> {
|
|
107
|
-
const id =
|
|
108
|
+
const id = nanoid();
|
|
108
109
|
return new Promise((resolve, reject) => {
|
|
109
110
|
pendingExtensionRequests.set(id, {
|
|
110
111
|
resolve: (response: RpcExtensionUIResponse) => {
|
|
@@ -126,7 +127,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
|
|
|
126
127
|
// Fire and forget - no response needed
|
|
127
128
|
output({
|
|
128
129
|
type: "extension_ui_request",
|
|
129
|
-
id:
|
|
130
|
+
id: nanoid(),
|
|
130
131
|
method: "notify",
|
|
131
132
|
message,
|
|
132
133
|
notifyType: type,
|
|
@@ -137,7 +138,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
|
|
|
137
138
|
// Fire and forget - no response needed
|
|
138
139
|
output({
|
|
139
140
|
type: "extension_ui_request",
|
|
140
|
-
id:
|
|
141
|
+
id: nanoid(),
|
|
141
142
|
method: "setStatus",
|
|
142
143
|
statusKey: key,
|
|
143
144
|
statusText: text,
|
|
@@ -149,7 +150,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
|
|
|
149
150
|
if (content === undefined || Array.isArray(content)) {
|
|
150
151
|
output({
|
|
151
152
|
type: "extension_ui_request",
|
|
152
|
-
id:
|
|
153
|
+
id: nanoid(),
|
|
153
154
|
method: "setWidget",
|
|
154
155
|
widgetKey: key,
|
|
155
156
|
widgetLines: content as string[] | undefined,
|
|
@@ -162,7 +163,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
|
|
|
162
163
|
// Fire and forget - host can implement terminal title control
|
|
163
164
|
output({
|
|
164
165
|
type: "extension_ui_request",
|
|
165
|
-
id:
|
|
166
|
+
id: nanoid(),
|
|
166
167
|
method: "setTitle",
|
|
167
168
|
title,
|
|
168
169
|
} as RpcExtensionUIRequest);
|
|
@@ -177,7 +178,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
|
|
|
177
178
|
// Fire and forget - host can implement editor control
|
|
178
179
|
output({
|
|
179
180
|
type: "extension_ui_request",
|
|
180
|
-
id:
|
|
181
|
+
id: nanoid(),
|
|
181
182
|
method: "set_editor_text",
|
|
182
183
|
text,
|
|
183
184
|
} as RpcExtensionUIRequest);
|
|
@@ -190,7 +191,7 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
|
|
|
190
191
|
},
|
|
191
192
|
|
|
192
193
|
async editor(title: string, prefill?: string): Promise<string | undefined> {
|
|
193
|
-
const id =
|
|
194
|
+
const id = nanoid();
|
|
194
195
|
return new Promise((resolve, reject) => {
|
|
195
196
|
pendingExtensionRequests.set(id, {
|
|
196
197
|
resolve: (response: RpcExtensionUIResponse) => {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
## Code Review Request
|
|
2
|
+
|
|
3
|
+
### Mode
|
|
4
|
+
{MODE}
|
|
5
|
+
|
|
6
|
+
### Changed Files ({FILE_COUNT} files, +{LINES_ADDED}/-{LINES_REMOVED} lines)
|
|
7
|
+
|
|
8
|
+
{FILE_TABLE}
|
|
9
|
+
|
|
10
|
+
{EXCLUDED_SECTION}
|
|
11
|
+
|
|
12
|
+
### Distribution Guidelines
|
|
13
|
+
|
|
14
|
+
{DISTRIBUTION_GUIDANCE}
|
|
15
|
+
|
|
16
|
+
{GROUPING_GUIDANCE}
|
|
17
|
+
|
|
18
|
+
### Reviewer Instructions
|
|
19
|
+
|
|
20
|
+
Each reviewer agent should:
|
|
21
|
+
1. Focus ONLY on its assigned files
|
|
22
|
+
2. {DIFF_INSTRUCTION}
|
|
23
|
+
3. Read full file context as needed via the `read` tool
|
|
24
|
+
4. Call `report_finding` for each issue found
|
|
25
|
+
5. Call `complete` with verdict when done
|
|
26
|
+
|
|
27
|
+
{DIFF_SECTION}
|