@oh-my-pi/pi-coding-agent 11.0.3 → 11.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.
- package/CHANGELOG.md +32 -0
- package/package.json +7 -7
- package/src/cli/config-cli.ts +1 -1
- package/src/commit/agentic/tools/analyze-file.ts +1 -3
- package/src/commit/git/errors.ts +4 -6
- package/src/config/keybindings.ts +1 -3
- package/src/config/settings-schema.ts +10 -0
- package/src/exa/mcp-client.ts +5 -8
- package/src/extensibility/extensions/loader.ts +7 -10
- package/src/extensibility/extensions/runner.ts +5 -15
- package/src/extensibility/extensions/types.ts +1 -1
- package/src/extensibility/hooks/runner.ts +6 -9
- package/src/ipy/kernel.ts +10 -22
- package/src/lsp/clients/biome-client.ts +4 -7
- package/src/lsp/clients/lsp-linter-client.ts +4 -6
- package/src/lsp/index.ts +5 -4
- package/src/lsp/utils.ts +18 -0
- package/src/modes/components/armin.ts +1 -3
- package/src/modes/components/assistant-message.ts +4 -4
- package/src/modes/components/bash-execution.ts +5 -3
- package/src/modes/components/branch-summary-message.ts +1 -3
- package/src/modes/components/compaction-summary-message.ts +1 -3
- package/src/modes/components/custom-message.ts +4 -5
- package/src/modes/components/extensions/extension-dashboard.ts +10 -16
- package/src/modes/components/extensions/extension-list.ts +5 -5
- package/src/modes/components/footer.ts +1 -4
- package/src/modes/components/hook-editor.ts +7 -32
- package/src/modes/components/hook-message.ts +4 -5
- package/src/modes/components/plugin-settings.ts +16 -20
- package/src/modes/components/python-execution.ts +5 -5
- package/src/modes/components/session-selector.ts +6 -7
- package/src/modes/components/settings-defs.ts +49 -40
- package/src/modes/components/settings-selector.ts +8 -17
- package/src/modes/components/skill-message.ts +1 -3
- package/src/modes/components/status-line-segment-editor.ts +1 -3
- package/src/modes/components/status-line.ts +1 -3
- package/src/modes/components/todo-reminder.ts +5 -7
- package/src/modes/components/tree-selector.ts +10 -12
- package/src/modes/components/ttsr-notification.ts +1 -3
- package/src/modes/components/user-message-selector.ts +2 -4
- package/src/modes/components/welcome.ts +6 -18
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +6 -33
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/theme/theme.ts +2 -6
- package/src/patch/index.ts +1 -4
- package/src/prompts/agents/explore.md +1 -0
- package/src/prompts/agents/frontmatter.md +2 -1
- package/src/prompts/agents/init.md +1 -0
- package/src/prompts/agents/plan.md +1 -0
- package/src/prompts/agents/reviewer.md +1 -0
- package/src/prompts/system/subagent-submit-reminder.md +2 -0
- package/src/prompts/system/subagent-system-prompt.md +2 -0
- package/src/prompts/system/subagent-user-prompt.md +8 -0
- package/src/prompts/system/system-prompt.md +5 -3
- package/src/prompts/tools/task.md +216 -163
- package/src/session/agent-session.ts +6 -6
- package/src/session/auth-storage.ts +1 -3
- package/src/session/session-manager.ts +12 -16
- package/src/task/agents.ts +2 -0
- package/src/task/executor.ts +2 -7
- package/src/task/index.ts +43 -23
- package/src/task/render.ts +67 -64
- package/src/task/template.ts +17 -34
- package/src/task/types.ts +49 -22
- package/src/tools/ask.ts +1 -3
- package/src/tools/bash.ts +1 -4
- package/src/tools/browser.ts +1 -3
- package/src/tools/exit-plan-mode.ts +1 -4
- package/src/tools/fetch.ts +1 -3
- package/src/tools/find.ts +4 -3
- package/src/tools/grep.ts +4 -4
- package/src/tools/index.ts +2 -2
- package/src/tools/notebook.ts +1 -5
- package/src/tools/python.ts +4 -3
- package/src/tools/read.ts +1 -3
- package/src/tools/render-utils.ts +23 -0
- package/src/tools/ssh.ts +8 -12
- package/src/tools/todo-write.ts +1 -4
- package/src/tools/tool-errors.ts +1 -4
- package/src/tools/write.ts +1 -3
- package/src/utils/external-editor.ts +59 -0
- package/src/web/search/types.ts +5 -6
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [11.1.0] - 2026-02-05
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added `sortDiagnostics()` utility function to sort diagnostics by severity, location, and message for consistent output ordering
|
|
10
|
+
- Added `task.isolation.enabled` setting to control whether subagents run in isolated git worktrees
|
|
11
|
+
- Added dynamic task schema that conditionally includes `isolated` parameter based on isolation setting
|
|
12
|
+
- Added `openInEditor()` utility function to centralize external editor handling with support for custom file extensions and stdio configuration
|
|
13
|
+
- Added `getEditorCommand()` utility function to retrieve the user's preferred editor from $VISUAL or $EDITOR environment variables
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Changed diagnostic output to sort results by severity (errors first), then by file location and message for improved readability
|
|
18
|
+
- Changed task tool to validate isolation setting and reject `isolated` parameter when isolation is disabled
|
|
19
|
+
- Changed task API to use `assignment` field instead of `args` for per-task instructions, with shared `context` prepended to every task
|
|
20
|
+
- Changed task template rendering to use structured context/assignment separation with `<swarm_context>` wrapper instead of placeholder-based substitution
|
|
21
|
+
- Changed task item schema to require `assignment` string (complete per-task instructions) instead of optional `args` object
|
|
22
|
+
- Changed `TaskItem` to remove `args` field and add `assignment` field for clearer per-task instruction semantics
|
|
23
|
+
- Changed agent frontmatter to use `thinking-level` field name instead of `thinkingLevel` for consistency
|
|
24
|
+
- Refactored task rendering to display full task text instead of args in progress and result views
|
|
25
|
+
- Changed `SubmenuSettingDef.getOptions()` method to `options` getter property for cleaner API access
|
|
26
|
+
- Converted static option providers from functions to direct array definitions for improved performance
|
|
27
|
+
- Added `createSubmenuSettingDef()` helper function to support both static and dynamic option providers
|
|
28
|
+
- Modified `setThinkingLevel()` API to accept optional `persist` parameter (defaults to false) for controlling whether thinking level changes are saved to settings
|
|
29
|
+
- Refactored hook editor and input controller to use shared external editor utilities, reducing code duplication
|
|
30
|
+
|
|
31
|
+
### Removed
|
|
32
|
+
|
|
33
|
+
- Removed `context` parameter from `ExecutorOptions` — context now prepended at template level before task execution
|
|
34
|
+
- Removed `args` field from `AgentProgress` and `SingleResult` interfaces
|
|
35
|
+
- Removed placeholder-based template rendering in favor of structured context/assignment model
|
|
36
|
+
|
|
5
37
|
## [11.0.3] - 2026-02-05
|
|
6
38
|
### Added
|
|
7
39
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
3
|
-
"version": "11.0
|
|
3
|
+
"version": "11.1.0",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"ompConfig": {
|
|
@@ -80,12 +80,12 @@
|
|
|
80
80
|
},
|
|
81
81
|
"dependencies": {
|
|
82
82
|
"@mozilla/readability": "0.6.0",
|
|
83
|
-
"@oh-my-pi/omp-stats": "11.0
|
|
84
|
-
"@oh-my-pi/pi-agent-core": "11.0
|
|
85
|
-
"@oh-my-pi/pi-ai": "11.0
|
|
86
|
-
"@oh-my-pi/pi-natives": "11.0
|
|
87
|
-
"@oh-my-pi/pi-tui": "11.0
|
|
88
|
-
"@oh-my-pi/pi-utils": "11.0
|
|
83
|
+
"@oh-my-pi/omp-stats": "11.1.0",
|
|
84
|
+
"@oh-my-pi/pi-agent-core": "11.1.0",
|
|
85
|
+
"@oh-my-pi/pi-ai": "11.1.0",
|
|
86
|
+
"@oh-my-pi/pi-natives": "11.1.0",
|
|
87
|
+
"@oh-my-pi/pi-tui": "11.1.0",
|
|
88
|
+
"@oh-my-pi/pi-utils": "11.1.0",
|
|
89
89
|
"@openai/agents": "^0.4.5",
|
|
90
90
|
"@sinclair/typebox": "^0.34.48",
|
|
91
91
|
"ajv": "^8.17.1",
|
package/src/cli/config-cli.ts
CHANGED
|
@@ -48,7 +48,7 @@ function getSettingValues(def: SettingDef): readonly string[] | undefined {
|
|
|
48
48
|
return def.values;
|
|
49
49
|
}
|
|
50
50
|
if (def.type === "submenu") {
|
|
51
|
-
const options = def.
|
|
51
|
+
const options = def.options;
|
|
52
52
|
if (options.length > 0) {
|
|
53
53
|
return options.map(o => o.value);
|
|
54
54
|
}
|
|
@@ -62,7 +62,6 @@ export function createAnalyzeFileTool(options: {
|
|
|
62
62
|
async execute(toolCallId, params, onUpdate, ctx, signal) {
|
|
63
63
|
const toolSession = buildToolSession(ctx, options);
|
|
64
64
|
const taskTool = await TaskTool.create(toolSession);
|
|
65
|
-
const context = "{{prompt}}";
|
|
66
65
|
const numstat = options.state.overview?.numstat ?? [];
|
|
67
66
|
const tasks = params.files.map((file, index) => {
|
|
68
67
|
const relatedFiles = formatRelatedFiles(params.files, file, numstat);
|
|
@@ -74,12 +73,11 @@ export function createAnalyzeFileTool(options: {
|
|
|
74
73
|
return {
|
|
75
74
|
id: `AnalyzeFile${index + 1}`,
|
|
76
75
|
description: `Analyze ${file}`,
|
|
77
|
-
|
|
76
|
+
assignment: prompt,
|
|
78
77
|
};
|
|
79
78
|
});
|
|
80
79
|
const taskParams: TaskParams = {
|
|
81
80
|
agent: "quick_task",
|
|
82
|
-
context,
|
|
83
81
|
schema: analyzeFileOutputSchema,
|
|
84
82
|
tasks,
|
|
85
83
|
};
|
package/src/commit/git/errors.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
export class GitError extends Error {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
constructor(
|
|
3
|
+
readonly command: string,
|
|
4
|
+
readonly stderr: string,
|
|
5
|
+
) {
|
|
6
6
|
super(`${command} failed: ${stderr || "unknown error"}`);
|
|
7
|
-
this.command = command;
|
|
8
|
-
this.stderr = stderr;
|
|
9
7
|
this.name = "GitError";
|
|
10
8
|
}
|
|
11
9
|
}
|
|
@@ -151,11 +151,9 @@ export function formatKeyHints(keys: KeyId | KeyId[]): string {
|
|
|
151
151
|
* Manages all keybindings (app + editor).
|
|
152
152
|
*/
|
|
153
153
|
export class KeybindingsManager {
|
|
154
|
-
private config: KeybindingsConfig;
|
|
155
154
|
private appActionToKeys: Map<AppAction, KeyId[]>;
|
|
156
155
|
|
|
157
|
-
private constructor(config: KeybindingsConfig) {
|
|
158
|
-
this.config = config;
|
|
156
|
+
private constructor(private readonly config: KeybindingsConfig) {
|
|
159
157
|
this.appActionToKeys = new Map();
|
|
160
158
|
this.buildMaps();
|
|
161
159
|
}
|
|
@@ -399,6 +399,16 @@ export const SETTINGS_SCHEMA = {
|
|
|
399
399
|
// ─────────────────────────────────────────────────────────────────────────
|
|
400
400
|
// Task tool settings
|
|
401
401
|
// ─────────────────────────────────────────────────────────────────────────
|
|
402
|
+
"task.isolation.enabled": {
|
|
403
|
+
type: "boolean",
|
|
404
|
+
default: false,
|
|
405
|
+
ui: {
|
|
406
|
+
tab: "tools",
|
|
407
|
+
label: "Task isolation",
|
|
408
|
+
description: "Run subagents in isolated git worktrees",
|
|
409
|
+
submenu: true,
|
|
410
|
+
},
|
|
411
|
+
},
|
|
402
412
|
"task.maxConcurrency": {
|
|
403
413
|
type: "number",
|
|
404
414
|
default: 32,
|
package/src/exa/mcp-client.ts
CHANGED
|
@@ -249,17 +249,14 @@ export async function fetchMCPToolSchema(
|
|
|
249
249
|
export class MCPWrappedTool implements CustomTool<TSchema, ExaRenderDetails> {
|
|
250
250
|
public readonly name: string;
|
|
251
251
|
public readonly label: string;
|
|
252
|
-
public readonly description: string;
|
|
253
|
-
public readonly parameters: TSchema;
|
|
254
252
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
253
|
+
constructor(
|
|
254
|
+
private readonly config: MCPToolWrapperConfig,
|
|
255
|
+
public readonly parameters: TSchema,
|
|
256
|
+
public readonly description: string,
|
|
257
|
+
) {
|
|
259
258
|
this.name = config.name;
|
|
260
259
|
this.label = config.label;
|
|
261
|
-
this.description = description;
|
|
262
|
-
this.parameters = schema;
|
|
263
260
|
}
|
|
264
261
|
|
|
265
262
|
async execute(
|
|
@@ -103,17 +103,14 @@ class ConcreteExtensionAPI implements ExtensionAPI, IExtensionRuntime {
|
|
|
103
103
|
readonly logger = logger;
|
|
104
104
|
readonly typebox = TypeBox;
|
|
105
105
|
readonly pi = piCodingAgent;
|
|
106
|
-
readonly events: EventBus;
|
|
107
106
|
readonly flagValues = new Map<string, boolean | string>();
|
|
108
107
|
|
|
109
108
|
constructor(
|
|
110
|
-
private extension: Extension,
|
|
111
|
-
private runtime: IExtensionRuntime,
|
|
112
|
-
private cwd: string,
|
|
113
|
-
|
|
114
|
-
) {
|
|
115
|
-
this.events = eventBus;
|
|
116
|
-
}
|
|
109
|
+
private readonly extension: Extension,
|
|
110
|
+
private readonly runtime: IExtensionRuntime,
|
|
111
|
+
private readonly cwd: string,
|
|
112
|
+
public readonly events: EventBus,
|
|
113
|
+
) {}
|
|
117
114
|
|
|
118
115
|
on<F extends HandlerFn>(event: string, handler: F): void {
|
|
119
116
|
const list = this.extension.handlers.get(event) ?? [];
|
|
@@ -214,8 +211,8 @@ class ConcreteExtensionAPI implements ExtensionAPI, IExtensionRuntime {
|
|
|
214
211
|
return this.runtime.getThinkingLevel();
|
|
215
212
|
}
|
|
216
213
|
|
|
217
|
-
setThinkingLevel(level: ThinkingLevel): void {
|
|
218
|
-
this.runtime.setThinkingLevel(level);
|
|
214
|
+
setThinkingLevel(level: ThinkingLevel, persist?: boolean): void {
|
|
215
|
+
this.runtime.setThinkingLevel(level, persist);
|
|
219
216
|
}
|
|
220
217
|
}
|
|
221
218
|
|
|
@@ -105,12 +105,7 @@ const noOpUIContext: ExtensionUIContext = {
|
|
|
105
105
|
};
|
|
106
106
|
|
|
107
107
|
export class ExtensionRunner {
|
|
108
|
-
private extensions: Extension[];
|
|
109
|
-
private runtime: ExtensionRuntime;
|
|
110
108
|
private uiContext: ExtensionUIContext;
|
|
111
|
-
private cwd: string;
|
|
112
|
-
private sessionManager: SessionManager;
|
|
113
|
-
private modelRegistry: ModelRegistry;
|
|
114
109
|
private errorListeners: Set<ExtensionErrorListener> = new Set();
|
|
115
110
|
private getModel: () => Model | undefined = () => undefined;
|
|
116
111
|
private isIdleFn: () => boolean = () => true;
|
|
@@ -125,18 +120,13 @@ export class ExtensionRunner {
|
|
|
125
120
|
private shutdownHandler: ShutdownHandler = () => {};
|
|
126
121
|
|
|
127
122
|
constructor(
|
|
128
|
-
extensions: Extension[],
|
|
129
|
-
runtime: ExtensionRuntime,
|
|
130
|
-
cwd: string,
|
|
131
|
-
sessionManager: SessionManager,
|
|
132
|
-
modelRegistry: ModelRegistry,
|
|
123
|
+
private readonly extensions: Extension[],
|
|
124
|
+
private readonly runtime: ExtensionRuntime,
|
|
125
|
+
private readonly cwd: string,
|
|
126
|
+
private readonly sessionManager: SessionManager,
|
|
127
|
+
private readonly modelRegistry: ModelRegistry,
|
|
133
128
|
) {
|
|
134
|
-
this.extensions = extensions;
|
|
135
|
-
this.runtime = runtime;
|
|
136
129
|
this.uiContext = noOpUIContext;
|
|
137
|
-
this.cwd = cwd;
|
|
138
|
-
this.sessionManager = sessionManager;
|
|
139
|
-
this.modelRegistry = modelRegistry;
|
|
140
130
|
}
|
|
141
131
|
|
|
142
132
|
initialize(
|
|
@@ -839,7 +839,7 @@ export type SetModelHandler = (model: Model) => Promise<boolean>;
|
|
|
839
839
|
|
|
840
840
|
export type GetThinkingLevelHandler = () => ThinkingLevel;
|
|
841
841
|
|
|
842
|
-
export type SetThinkingLevelHandler = (level: ThinkingLevel) => void;
|
|
842
|
+
export type SetThinkingLevelHandler = (level: ThinkingLevel, persist?: boolean) => void;
|
|
843
843
|
|
|
844
844
|
/** Shared state created by loader, used during registration and runtime. */
|
|
845
845
|
export interface ExtensionRuntimeState {
|
|
@@ -62,12 +62,8 @@ const noOpUIContext: HookUIContext = {
|
|
|
62
62
|
* HookRunner executes hooks and manages event emission.
|
|
63
63
|
*/
|
|
64
64
|
export class HookRunner {
|
|
65
|
-
private hooks: LoadedHook[];
|
|
66
65
|
private uiContext: HookUIContext;
|
|
67
66
|
private hasUI: boolean;
|
|
68
|
-
private cwd: string;
|
|
69
|
-
private sessionManager: SessionManager;
|
|
70
|
-
private modelRegistry: ModelRegistry;
|
|
71
67
|
private errorListeners: Set<HookErrorListener> = new Set();
|
|
72
68
|
private getModel: () => Model | undefined = () => undefined;
|
|
73
69
|
private isIdleFn: () => boolean = () => true;
|
|
@@ -78,13 +74,14 @@ export class HookRunner {
|
|
|
78
74
|
private branchHandler: BranchHandler = async () => ({ cancelled: false });
|
|
79
75
|
private navigateTreeHandler: NavigateTreeHandler = async () => ({ cancelled: false });
|
|
80
76
|
|
|
81
|
-
constructor(
|
|
82
|
-
|
|
77
|
+
constructor(
|
|
78
|
+
private readonly hooks: LoadedHook[],
|
|
79
|
+
private readonly cwd: string,
|
|
80
|
+
private readonly sessionManager: SessionManager,
|
|
81
|
+
private readonly modelRegistry: ModelRegistry,
|
|
82
|
+
) {
|
|
83
83
|
this.uiContext = noOpUIContext;
|
|
84
84
|
this.hasUI = false;
|
|
85
|
-
this.cwd = cwd;
|
|
86
|
-
this.sessionManager = sessionManager;
|
|
87
|
-
this.modelRegistry = modelRegistry;
|
|
88
85
|
}
|
|
89
86
|
|
|
90
87
|
/**
|
package/src/ipy/kernel.ts
CHANGED
|
@@ -16,11 +16,11 @@ const PRELUDE_INTROSPECTION_SNIPPET = "import json\nprint(json.dumps(__omp_prelu
|
|
|
16
16
|
const debugStartup = $env.PI_DEBUG_STARTUP ? (stage: string) => process.stderr.write(`[startup] ${stage}\n`) : () => {};
|
|
17
17
|
|
|
18
18
|
class SharedGatewayCreateError extends Error {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
constructor(
|
|
20
|
+
readonly status: number,
|
|
21
|
+
message: string,
|
|
22
|
+
) {
|
|
22
23
|
super(message);
|
|
23
|
-
this.status = status;
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -265,12 +265,6 @@ export function serializeWebSocketMessage(msg: JupyterMessage): ArrayBuffer {
|
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
export class PythonKernel {
|
|
268
|
-
readonly id: string;
|
|
269
|
-
readonly kernelId: string;
|
|
270
|
-
readonly gatewayUrl: string;
|
|
271
|
-
readonly sessionId: string;
|
|
272
|
-
readonly username: string;
|
|
273
|
-
readonly isSharedGateway: boolean;
|
|
274
268
|
readonly #authToken?: string;
|
|
275
269
|
|
|
276
270
|
#ws: WebSocket | null = null;
|
|
@@ -281,20 +275,14 @@ export class PythonKernel {
|
|
|
281
275
|
#pendingExecutions = new Map<string, (reason: string) => void>();
|
|
282
276
|
|
|
283
277
|
private constructor(
|
|
284
|
-
id: string,
|
|
285
|
-
kernelId: string,
|
|
286
|
-
gatewayUrl: string,
|
|
287
|
-
sessionId: string,
|
|
288
|
-
username: string,
|
|
289
|
-
isSharedGateway: boolean,
|
|
278
|
+
readonly id: string,
|
|
279
|
+
readonly kernelId: string,
|
|
280
|
+
readonly gatewayUrl: string,
|
|
281
|
+
readonly sessionId: string,
|
|
282
|
+
readonly username: string,
|
|
283
|
+
readonly isSharedGateway: boolean,
|
|
290
284
|
authToken?: string,
|
|
291
285
|
) {
|
|
292
|
-
this.id = id;
|
|
293
|
-
this.kernelId = kernelId;
|
|
294
|
-
this.gatewayUrl = gatewayUrl;
|
|
295
|
-
this.sessionId = sessionId;
|
|
296
|
-
this.username = username;
|
|
297
|
-
this.isSharedGateway = isSharedGateway;
|
|
298
286
|
this.#authToken = authToken;
|
|
299
287
|
}
|
|
300
288
|
|
|
@@ -107,18 +107,15 @@ async function runBiome(
|
|
|
107
107
|
* Parses Biome's --reporter=json output into LSP Diagnostic format.
|
|
108
108
|
*/
|
|
109
109
|
export class BiomeClient implements LinterClient {
|
|
110
|
-
private config: ServerConfig;
|
|
111
|
-
private cwd: string;
|
|
112
|
-
|
|
113
110
|
/** Factory method for creating BiomeClient instances */
|
|
114
111
|
static create(config: ServerConfig, cwd: string): LinterClient {
|
|
115
112
|
return new BiomeClient(config, cwd);
|
|
116
113
|
}
|
|
117
114
|
|
|
118
|
-
constructor(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
115
|
+
constructor(
|
|
116
|
+
private readonly config: ServerConfig,
|
|
117
|
+
private readonly cwd: string,
|
|
118
|
+
) {}
|
|
122
119
|
|
|
123
120
|
async format(filePath: string, content: string): Promise<string> {
|
|
124
121
|
// Write content to file first
|
|
@@ -21,8 +21,6 @@ const DEFAULT_FORMAT_OPTIONS = {
|
|
|
21
21
|
* Wraps the existing LSP client infrastructure.
|
|
22
22
|
*/
|
|
23
23
|
export class LspLinterClient implements LinterClient {
|
|
24
|
-
private config: ServerConfig;
|
|
25
|
-
private cwd: string;
|
|
26
24
|
private client: LspClient | null = null;
|
|
27
25
|
|
|
28
26
|
/** Factory method for creating LspLinterClient instances */
|
|
@@ -30,10 +28,10 @@ export class LspLinterClient implements LinterClient {
|
|
|
30
28
|
return new LspLinterClient(config, cwd);
|
|
31
29
|
}
|
|
32
30
|
|
|
33
|
-
constructor(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
31
|
+
constructor(
|
|
32
|
+
private readonly config: ServerConfig,
|
|
33
|
+
private readonly cwd: string,
|
|
34
|
+
) {}
|
|
37
35
|
|
|
38
36
|
private async getClient(): Promise<LspClient> {
|
|
39
37
|
if (!this.client) {
|
package/src/lsp/index.ts
CHANGED
|
@@ -50,6 +50,7 @@ import {
|
|
|
50
50
|
formatLocation,
|
|
51
51
|
formatSymbolInformation,
|
|
52
52
|
formatWorkspaceEdit,
|
|
53
|
+
sortDiagnostics,
|
|
53
54
|
symbolKindToIcon,
|
|
54
55
|
} from "./utils";
|
|
55
56
|
|
|
@@ -458,6 +459,7 @@ async function getDiagnosticsForFile(
|
|
|
458
459
|
}
|
|
459
460
|
}
|
|
460
461
|
|
|
462
|
+
sortDiagnostics(uniqueDiagnostics);
|
|
461
463
|
const formatted = uniqueDiagnostics.map(d => formatDiagnostic(d, relPath));
|
|
462
464
|
const limited = limitDiagnosticMessages(formatted);
|
|
463
465
|
const summary = formatDiagnosticsSummary(uniqueDiagnostics);
|
|
@@ -858,10 +860,7 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
858
860
|
public readonly mergeCallAndResult = true;
|
|
859
861
|
public readonly inline = true;
|
|
860
862
|
|
|
861
|
-
private readonly session: ToolSession
|
|
862
|
-
|
|
863
|
-
constructor(session: ToolSession) {
|
|
864
|
-
this.session = session;
|
|
863
|
+
constructor(private readonly session: ToolSession) {
|
|
865
864
|
this.description = renderPromptTemplate(lspDescription);
|
|
866
865
|
}
|
|
867
866
|
|
|
@@ -966,6 +965,8 @@ export class LspTool implements AgentTool<typeof lspSchema, LspToolDetails, Them
|
|
|
966
965
|
}
|
|
967
966
|
}
|
|
968
967
|
|
|
968
|
+
sortDiagnostics(uniqueDiagnostics);
|
|
969
|
+
|
|
969
970
|
if (!detailed && targets.length === 1) {
|
|
970
971
|
if (uniqueDiagnostics.length === 0) {
|
|
971
972
|
return {
|
package/src/lsp/utils.ts
CHANGED
|
@@ -205,6 +205,24 @@ export function severityToString(severity?: DiagnosticSeverity): string {
|
|
|
205
205
|
return SEVERITY_NAMES[severity ?? 1] ?? "unknown";
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Sort diagnostics by severity, then by location and message.
|
|
210
|
+
*/
|
|
211
|
+
export function sortDiagnostics(diagnostics: Diagnostic[]): Diagnostic[] {
|
|
212
|
+
return diagnostics.sort((a, b) => {
|
|
213
|
+
const aSeverity = a.severity ?? 1;
|
|
214
|
+
const bSeverity = b.severity ?? 1;
|
|
215
|
+
if (aSeverity !== bSeverity) return aSeverity - bSeverity;
|
|
216
|
+
const aLine = a.range.start.line;
|
|
217
|
+
const bLine = b.range.start.line;
|
|
218
|
+
if (aLine !== bLine) return aLine - bLine;
|
|
219
|
+
const aCol = a.range.start.character;
|
|
220
|
+
const bCol = b.range.start.character;
|
|
221
|
+
if (aCol !== bCol) return aCol - bCol;
|
|
222
|
+
return a.message.localeCompare(b.message);
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
208
226
|
/**
|
|
209
227
|
* Get icon for diagnostic severity.
|
|
210
228
|
*/
|
|
@@ -57,7 +57,6 @@ function buildFinalGrid(): string[][] {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
export class ArminComponent implements Component {
|
|
60
|
-
private ui: TUI;
|
|
61
60
|
private interval: ReturnType<typeof setInterval> | null = null;
|
|
62
61
|
private effect: Effect;
|
|
63
62
|
private finalGrid: string[][];
|
|
@@ -68,8 +67,7 @@ export class ArminComponent implements Component {
|
|
|
68
67
|
private gridVersion = 0;
|
|
69
68
|
private cachedVersion = -1;
|
|
70
69
|
|
|
71
|
-
constructor(ui: TUI) {
|
|
72
|
-
this.ui = ui;
|
|
70
|
+
constructor(private readonly ui: TUI) {
|
|
73
71
|
this.effect = EFFECTS[Math.floor(Math.random() * EFFECTS.length)];
|
|
74
72
|
this.finalGrid = buildFinalGrid();
|
|
75
73
|
this.currentGrid = this.createEmptyGrid();
|
|
@@ -8,15 +8,15 @@ import { getMarkdownTheme, theme } from "../../modes/theme/theme";
|
|
|
8
8
|
*/
|
|
9
9
|
export class AssistantMessageComponent extends Container {
|
|
10
10
|
private contentContainer: Container;
|
|
11
|
-
private hideThinkingBlock: boolean;
|
|
12
11
|
private lastMessage?: AssistantMessage;
|
|
13
12
|
private prerenderInFlight = false;
|
|
14
13
|
|
|
15
|
-
constructor(
|
|
14
|
+
constructor(
|
|
15
|
+
message?: AssistantMessage,
|
|
16
|
+
private hideThinkingBlock = false,
|
|
17
|
+
) {
|
|
16
18
|
super();
|
|
17
19
|
|
|
18
|
-
this.hideThinkingBlock = hideThinkingBlock;
|
|
19
|
-
|
|
20
20
|
// Container for text/thinking content
|
|
21
21
|
this.contentContainer = new Container();
|
|
22
22
|
this.addChild(this.contentContainer);
|
|
@@ -12,7 +12,6 @@ import { truncateToVisualLines } from "./visual-truncate";
|
|
|
12
12
|
const PREVIEW_LINES = 20;
|
|
13
13
|
|
|
14
14
|
export class BashExecutionComponent extends Container {
|
|
15
|
-
private command: string;
|
|
16
15
|
private outputLines: string[] = [];
|
|
17
16
|
private status: "running" | "complete" | "cancelled" | "error" = "running";
|
|
18
17
|
private exitCode: number | undefined = undefined;
|
|
@@ -21,9 +20,12 @@ export class BashExecutionComponent extends Container {
|
|
|
21
20
|
private expanded = false;
|
|
22
21
|
private contentContainer: Container;
|
|
23
22
|
|
|
24
|
-
constructor(
|
|
23
|
+
constructor(
|
|
24
|
+
private readonly command: string,
|
|
25
|
+
ui: TUI,
|
|
26
|
+
excludeFromContext = false,
|
|
27
|
+
) {
|
|
25
28
|
super();
|
|
26
|
-
this.command = command;
|
|
27
29
|
|
|
28
30
|
// Use dim border for excluded-from-context commands (!! prefix)
|
|
29
31
|
const colorKey = excludeFromContext ? "dim" : "bashMode";
|
|
@@ -8,11 +8,9 @@ import type { BranchSummaryMessage } from "../../session/messages";
|
|
|
8
8
|
*/
|
|
9
9
|
export class BranchSummaryMessageComponent extends Box {
|
|
10
10
|
private expanded = false;
|
|
11
|
-
private message: BranchSummaryMessage;
|
|
12
11
|
|
|
13
|
-
constructor(message: BranchSummaryMessage) {
|
|
12
|
+
constructor(private readonly message: BranchSummaryMessage) {
|
|
14
13
|
super(1, 1, t => theme.bg("customMessageBg", t));
|
|
15
|
-
this.message = message;
|
|
16
14
|
this.updateDisplay();
|
|
17
15
|
}
|
|
18
16
|
|
|
@@ -8,11 +8,9 @@ import type { CompactionSummaryMessage } from "../../session/messages";
|
|
|
8
8
|
*/
|
|
9
9
|
export class CompactionSummaryMessageComponent extends Box {
|
|
10
10
|
private expanded = false;
|
|
11
|
-
private message: CompactionSummaryMessage;
|
|
12
11
|
|
|
13
|
-
constructor(message: CompactionSummaryMessage) {
|
|
12
|
+
constructor(private readonly message: CompactionSummaryMessage) {
|
|
14
13
|
super(1, 1, t => theme.bg("customMessageBg", t));
|
|
15
|
-
this.message = message;
|
|
16
14
|
this.updateDisplay();
|
|
17
15
|
}
|
|
18
16
|
|
|
@@ -10,16 +10,15 @@ import type { CustomMessage } from "../../session/messages";
|
|
|
10
10
|
* Uses distinct styling to differentiate from user messages.
|
|
11
11
|
*/
|
|
12
12
|
export class CustomMessageComponent extends Container {
|
|
13
|
-
private message: CustomMessage<unknown>;
|
|
14
|
-
private customRenderer?: MessageRenderer;
|
|
15
13
|
private box: Box;
|
|
16
14
|
private customComponent?: Component;
|
|
17
15
|
private _expanded = false;
|
|
18
16
|
|
|
19
|
-
constructor(
|
|
17
|
+
constructor(
|
|
18
|
+
private readonly message: CustomMessage<unknown>,
|
|
19
|
+
private readonly customRenderer?: MessageRenderer,
|
|
20
|
+
) {
|
|
20
21
|
super();
|
|
21
|
-
this.message = message;
|
|
22
|
-
this.customRenderer = customRenderer;
|
|
23
22
|
|
|
24
23
|
this.addChild(new Spacer(1));
|
|
25
24
|
|
|
@@ -33,17 +33,15 @@ export class ExtensionDashboard extends Container {
|
|
|
33
33
|
private state!: DashboardState;
|
|
34
34
|
private mainList!: ExtensionList;
|
|
35
35
|
private inspector!: InspectorPanel;
|
|
36
|
-
private settingsInstance: Settings | null;
|
|
37
|
-
private cwd: string;
|
|
38
|
-
private terminalHeight: number;
|
|
39
36
|
|
|
40
37
|
public onClose?: () => void;
|
|
41
38
|
|
|
42
|
-
private constructor(
|
|
39
|
+
private constructor(
|
|
40
|
+
private readonly cwd: string,
|
|
41
|
+
private readonly settingsInstance: Settings | null,
|
|
42
|
+
private readonly terminalHeight: number,
|
|
43
|
+
) {
|
|
43
44
|
super();
|
|
44
|
-
this.cwd = cwd;
|
|
45
|
-
this.settingsInstance = settingsInstance;
|
|
46
|
-
this.terminalHeight = terminalHeight;
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
static async create(
|
|
@@ -292,15 +290,11 @@ export class ExtensionDashboard extends Container {
|
|
|
292
290
|
* Two-column body component for side-by-side rendering.
|
|
293
291
|
*/
|
|
294
292
|
class TwoColumnBody implements Component {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
this.leftPane = left;
|
|
301
|
-
this.rightPane = right;
|
|
302
|
-
this.maxHeight = maxHeight;
|
|
303
|
-
}
|
|
293
|
+
constructor(
|
|
294
|
+
private readonly leftPane: ExtensionList,
|
|
295
|
+
private readonly rightPane: InspectorPanel,
|
|
296
|
+
private readonly maxHeight: number,
|
|
297
|
+
) {}
|
|
304
298
|
|
|
305
299
|
render(width: number): string[] {
|
|
306
300
|
const leftWidth = Math.floor(width * 0.5);
|
|
@@ -31,19 +31,19 @@ type ListItem =
|
|
|
31
31
|
| { type: "extension"; item: Extension };
|
|
32
32
|
|
|
33
33
|
export class ExtensionList implements Component {
|
|
34
|
-
private extensions: Extension[] = [];
|
|
35
34
|
private listItems: ListItem[] = [];
|
|
36
35
|
private selectedIndex = 0;
|
|
37
36
|
private scrollOffset = 0;
|
|
38
37
|
private searchQuery = "";
|
|
39
38
|
private focused = false;
|
|
40
|
-
private callbacks: ExtensionListCallbacks;
|
|
41
39
|
private masterSwitchProvider: string | null = null;
|
|
42
40
|
private maxVisible: number;
|
|
43
41
|
|
|
44
|
-
constructor(
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
constructor(
|
|
43
|
+
private extensions: Extension[],
|
|
44
|
+
private readonly callbacks: ExtensionListCallbacks = {},
|
|
45
|
+
maxVisible?: number,
|
|
46
|
+
) {
|
|
47
47
|
this.masterSwitchProvider = callbacks.masterSwitchProvider ?? null;
|
|
48
48
|
this.maxVisible = maxVisible ?? DEFAULT_MAX_VISIBLE;
|
|
49
49
|
this.rebuildList();
|
|
@@ -42,16 +42,13 @@ async function findGitHeadPath(): Promise<{ path: string; content: string } | nu
|
|
|
42
42
|
* Footer component that shows pwd, token stats, and context usage
|
|
43
43
|
*/
|
|
44
44
|
export class FooterComponent implements Component {
|
|
45
|
-
private session: AgentSession;
|
|
46
45
|
private cachedBranch: string | null | undefined = undefined; // undefined = not checked yet, null = not in git repo, string = branch name
|
|
47
46
|
private gitWatcher: fs.FSWatcher | null = null;
|
|
48
47
|
private onBranchChange: (() => void) | null = null;
|
|
49
48
|
private autoCompactEnabled: boolean = true;
|
|
50
49
|
private extensionStatuses: Map<string, string> = new Map();
|
|
51
50
|
|
|
52
|
-
constructor(session: AgentSession) {
|
|
53
|
-
this.session = session;
|
|
54
|
-
}
|
|
51
|
+
constructor(private readonly session: AgentSession) {}
|
|
55
52
|
|
|
56
53
|
setAutoCompactEnabled(enabled: boolean): void {
|
|
57
54
|
this.autoCompactEnabled = enabled;
|