@oh-my-pi/pi-coding-agent 6.8.1 → 6.8.3
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 +34 -0
- package/package.json +6 -6
- package/src/core/agent-session.ts +14 -9
- package/src/core/auth-storage.ts +5 -1
- package/src/core/bash-executor.ts +3 -2
- package/src/core/custom-commands/loader.ts +0 -8
- package/src/core/keybindings.ts +10 -2
- package/src/core/prompt-templates.ts +1 -1
- package/src/core/tools/lsp/client.ts +1 -1
- package/src/core/tools/output.ts +7 -8
- package/src/core/tools/patch/applicator.ts +38 -24
- package/src/core/tools/patch/diff.ts +7 -3
- package/src/core/tools/patch/fuzzy.ts +19 -1
- package/src/core/tools/patch/index.ts +4 -1
- package/src/core/tools/patch/types.ts +4 -0
- package/src/core/tools/task/executor.ts +28 -21
- package/src/core/tools/task/index.ts +11 -12
- package/src/core/tools/task/types.ts +2 -1
- package/src/core/tools/task/worker.ts +2 -1
- package/src/core/tools/todo-write.ts +2 -18
- package/src/core/tools/web-scrapers/youtube.ts +6 -49
- package/src/lib/worktree/collapse.ts +3 -3
- package/src/lib/worktree/git.ts +6 -40
- package/src/lib/worktree/index.ts +1 -1
- package/src/modes/interactive/components/todo-display.ts +1 -8
- package/src/modes/interactive/components/tree-selector.ts +2 -2
- package/src/modes/interactive/interactive-mode.ts +1 -15
- package/src/utils/clipboard.ts +26 -21
- package/src/core/tools/task/artifacts.ts +0 -112
- package/src/core/tools/task/model-resolver.ts +0 -206
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Model resolution with fuzzy pattern matching.
|
|
3
|
-
*
|
|
4
|
-
* Returns models in "provider/modelId" format for use with --model flag.
|
|
5
|
-
*
|
|
6
|
-
* Supports:
|
|
7
|
-
* - Exact match: "gpt-5.2" → "p-openai/gpt-5.2"
|
|
8
|
-
* - Fuzzy match: "opus" → "p-anthropic/claude-opus-4-5"
|
|
9
|
-
* - Comma fallback: "gpt, opus" → tries gpt first, then opus
|
|
10
|
-
* - "default" → undefined (use system default)
|
|
11
|
-
* - "omp/slow" or "pi/slow" → configured slow model from settings
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { $ } from "bun";
|
|
15
|
-
import { type Settings as SettingsFile, settingsCapability } from "../../../capability/settings";
|
|
16
|
-
import { loadCapability } from "../../../discovery";
|
|
17
|
-
import type { Settings as SettingsData } from "../../settings-manager";
|
|
18
|
-
import { resolveOmpCommand } from "./omp-command";
|
|
19
|
-
|
|
20
|
-
/** Cache for available models (provider/modelId format) */
|
|
21
|
-
let cachedModels: string[] | null = null;
|
|
22
|
-
|
|
23
|
-
/** Cache expiry time (5 minutes) */
|
|
24
|
-
let cacheExpiry = 0;
|
|
25
|
-
|
|
26
|
-
const CACHE_TTL_MS = 5 * 60 * 1000;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Get available models from `omp --list-models`.
|
|
30
|
-
* Returns models in "provider/modelId" format.
|
|
31
|
-
* Caches the result for performance.
|
|
32
|
-
*/
|
|
33
|
-
export async function getAvailableModels(): Promise<string[]> {
|
|
34
|
-
const now = Date.now();
|
|
35
|
-
if (cachedModels !== null && now < cacheExpiry) {
|
|
36
|
-
return cachedModels;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
try {
|
|
40
|
-
const ompCommand = resolveOmpCommand();
|
|
41
|
-
const result = await $`${ompCommand.cmd} ${ompCommand.args} --list-models`.quiet().nothrow();
|
|
42
|
-
const stdout = result.stdout?.toString() ?? "";
|
|
43
|
-
|
|
44
|
-
if (result.exitCode !== 0 || !stdout.trim()) {
|
|
45
|
-
cachedModels = [];
|
|
46
|
-
cacheExpiry = now + CACHE_TTL_MS;
|
|
47
|
-
return cachedModels;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Parse output: skip header line, extract provider/model
|
|
51
|
-
const lines = stdout.trim().split("\n");
|
|
52
|
-
cachedModels = lines
|
|
53
|
-
.slice(1) // Skip header
|
|
54
|
-
.map((line) => {
|
|
55
|
-
const parts = line.trim().split(/\s+/);
|
|
56
|
-
// Format: provider/modelId
|
|
57
|
-
return parts[0] && parts[1] ? `${parts[0]}/${parts[1]}` : "";
|
|
58
|
-
})
|
|
59
|
-
.filter(Boolean);
|
|
60
|
-
|
|
61
|
-
cacheExpiry = now + CACHE_TTL_MS;
|
|
62
|
-
return cachedModels;
|
|
63
|
-
} catch {
|
|
64
|
-
cachedModels = [];
|
|
65
|
-
cacheExpiry = now + CACHE_TTL_MS;
|
|
66
|
-
return cachedModels;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Clear the model cache (for testing).
|
|
72
|
-
*/
|
|
73
|
-
export function clearModelCache(): void {
|
|
74
|
-
cachedModels = null;
|
|
75
|
-
cacheExpiry = 0;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Load model roles from settings files using capability API.
|
|
80
|
-
*/
|
|
81
|
-
async function loadModelRoles(): Promise<Record<string, string>> {
|
|
82
|
-
const result = await loadCapability<SettingsFile>(settingsCapability.id, { cwd: process.cwd() });
|
|
83
|
-
|
|
84
|
-
// Merge all settings, prioritizing first (highest priority)
|
|
85
|
-
let modelRoles: Record<string, string> = {};
|
|
86
|
-
for (const settings of result.items.reverse()) {
|
|
87
|
-
const roles = settings.data.modelRoles as Record<string, string> | undefined;
|
|
88
|
-
if (roles) {
|
|
89
|
-
modelRoles = { ...modelRoles, ...roles };
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return modelRoles;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Resolve an omp/<role> alias to a model string.
|
|
98
|
-
* Looks up the role in settings.modelRoles and returns the configured model.
|
|
99
|
-
* Returns undefined if the role isn't configured.
|
|
100
|
-
*/
|
|
101
|
-
async function resolveOmpAlias(
|
|
102
|
-
role: string,
|
|
103
|
-
availableModels: string[],
|
|
104
|
-
settings?: SettingsData,
|
|
105
|
-
): Promise<string | undefined> {
|
|
106
|
-
const roles = settings?.modelRoles ?? (await loadModelRoles());
|
|
107
|
-
|
|
108
|
-
// Look up role in settings (case-insensitive)
|
|
109
|
-
const configured = roles[role] || roles[role.toLowerCase()];
|
|
110
|
-
if (!configured) return undefined;
|
|
111
|
-
|
|
112
|
-
// configured is in "provider/modelId" format, find in available models
|
|
113
|
-
return availableModels.find((m) => m.toLowerCase() === configured.toLowerCase());
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Extract model ID from "provider/modelId" format.
|
|
118
|
-
*/
|
|
119
|
-
function getModelId(fullModel: string): string {
|
|
120
|
-
const slashIdx = fullModel.indexOf("/");
|
|
121
|
-
return slashIdx > 0 ? fullModel.slice(slashIdx + 1) : fullModel;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Extract provider from "provider/modelId" format.
|
|
126
|
-
* Returns undefined if no provider prefix.
|
|
127
|
-
*/
|
|
128
|
-
function getProvider(fullModel: string): string | undefined {
|
|
129
|
-
const slashIdx = fullModel.indexOf("/");
|
|
130
|
-
return slashIdx > 0 ? fullModel.slice(0, slashIdx) : undefined;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Resolve a fuzzy model pattern to "provider/modelId" format.
|
|
135
|
-
*
|
|
136
|
-
* Supports comma-separated patterns (e.g., "gpt, opus") - tries each in order.
|
|
137
|
-
* Returns undefined if pattern is "default", undefined, or no match found.
|
|
138
|
-
*
|
|
139
|
-
* @param pattern - Model pattern to resolve
|
|
140
|
-
* @param availableModels - Optional pre-fetched list of available models (in provider/modelId format)
|
|
141
|
-
* @param settings - Optional settings for role alias resolution (pi/..., omp/...)
|
|
142
|
-
*/
|
|
143
|
-
export async function resolveModelPattern(
|
|
144
|
-
pattern: string | undefined,
|
|
145
|
-
availableModels?: string[],
|
|
146
|
-
settings?: SettingsData,
|
|
147
|
-
): Promise<string | undefined> {
|
|
148
|
-
if (!pattern || pattern === "default") {
|
|
149
|
-
return undefined;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const models = availableModels ?? (await getAvailableModels());
|
|
153
|
-
if (models.length === 0) {
|
|
154
|
-
// Fallback: return pattern as-is if we can't get available models
|
|
155
|
-
return pattern;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Split by comma, try each pattern in order
|
|
159
|
-
const patterns = pattern
|
|
160
|
-
.split(",")
|
|
161
|
-
.map((p) => p.trim())
|
|
162
|
-
.filter(Boolean);
|
|
163
|
-
|
|
164
|
-
for (const p of patterns) {
|
|
165
|
-
// Handle omp/<role> or pi/<role> aliases - looks up role in settings.modelRoles
|
|
166
|
-
const lower = p.toLowerCase();
|
|
167
|
-
if (lower.startsWith("omp/") || lower.startsWith("pi/")) {
|
|
168
|
-
const role = lower.startsWith("omp/") ? p.slice(4) : p.slice(3);
|
|
169
|
-
const resolved = await resolveOmpAlias(role, models, settings);
|
|
170
|
-
if (resolved) return resolved;
|
|
171
|
-
continue; // Role not configured, try next pattern
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Try exact match on full provider/modelId
|
|
175
|
-
const exactFull = models.find((m) => m.toLowerCase() === p.toLowerCase());
|
|
176
|
-
if (exactFull) return exactFull;
|
|
177
|
-
|
|
178
|
-
// Try exact match on model ID only
|
|
179
|
-
const exactId = models.find((m) => getModelId(m).toLowerCase() === p.toLowerCase());
|
|
180
|
-
if (exactId) return exactId;
|
|
181
|
-
|
|
182
|
-
// Check if pattern has provider prefix (e.g., "zai/glm-4.7")
|
|
183
|
-
const patternProvider = getProvider(p);
|
|
184
|
-
const patternModelId = getModelId(p);
|
|
185
|
-
|
|
186
|
-
// If pattern has provider prefix, fuzzy match must stay within that provider
|
|
187
|
-
// (don't cross provider boundaries when user explicitly specifies provider)
|
|
188
|
-
if (patternProvider) {
|
|
189
|
-
const providerFuzzyMatch = models.find(
|
|
190
|
-
(m) =>
|
|
191
|
-
getProvider(m)?.toLowerCase() === patternProvider.toLowerCase() &&
|
|
192
|
-
getModelId(m).toLowerCase().includes(patternModelId.toLowerCase()),
|
|
193
|
-
);
|
|
194
|
-
if (providerFuzzyMatch) return providerFuzzyMatch;
|
|
195
|
-
// No match in specified provider - don't fall through to other providers
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// No provider prefix - fall back to general fuzzy match on model ID (substring)
|
|
200
|
-
const fuzzyMatch = models.find((m) => getModelId(m).toLowerCase().includes(patternModelId.toLowerCase()));
|
|
201
|
-
if (fuzzyMatch) return fuzzyMatch;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// No match found - use default model
|
|
205
|
-
return undefined;
|
|
206
|
-
}
|