@oh-my-pi/pi-coding-agent 15.10.3 → 15.10.5
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 +72 -0
- package/dist/types/capability/rule-buckets.d.ts +1 -1
- package/dist/types/capability/rule.d.ts +6 -1
- package/dist/types/cli/update-cli.d.ts +11 -1
- package/dist/types/config/model-registry.d.ts +18 -1
- package/dist/types/discovery/at-imports.d.ts +15 -0
- package/dist/types/edit/diff.d.ts +3 -2
- package/dist/types/eval/__tests__/helpers-local-roots.test.d.ts +1 -0
- package/dist/types/eval/__tests__/js-context-manager.test.d.ts +1 -0
- package/dist/types/eval/backend.d.ts +7 -0
- package/dist/types/eval/bridge-timeout.d.ts +1 -1
- package/dist/types/eval/{llm-bridge.d.ts → completion-bridge.d.ts} +8 -8
- package/dist/types/eval/idle-timeout.d.ts +1 -1
- package/dist/types/eval/js/context-manager.d.ts +1 -0
- package/dist/types/eval/js/executor.d.ts +2 -0
- package/dist/types/eval/js/index.d.ts +1 -1
- package/dist/types/eval/js/shared/helpers.d.ts +6 -0
- package/dist/types/eval/js/shared/runtime.d.ts +5 -0
- package/dist/types/eval/js/worker-protocol.d.ts +6 -0
- package/dist/types/eval/py/executor.d.ts +7 -0
- package/dist/types/eval/py/index.d.ts +1 -1
- package/dist/types/export/ttsr.d.ts +14 -0
- package/dist/types/extensibility/extensions/types.d.ts +8 -1
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +1 -1
- package/dist/types/internal-urls/local-protocol.d.ts +10 -0
- package/dist/types/mcp/oauth-flow.d.ts +2 -2
- package/dist/types/modes/components/custom-editor.d.ts +3 -0
- package/dist/types/modes/components/{status-line.d.ts → status-line/component.d.ts} +2 -32
- package/dist/types/modes/components/status-line/index.d.ts +1 -0
- package/dist/types/modes/components/status-line/types.d.ts +31 -2
- package/dist/types/modes/image-references.d.ts +8 -3
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/modes/types.d.ts +2 -1
- package/dist/types/modes/utils/ui-helpers.d.ts +2 -2
- package/dist/types/session/agent-session.d.ts +0 -2
- package/dist/types/tools/ask.d.ts +1 -0
- package/dist/types/tools/browser/tab-worker.d.ts +15 -0
- package/dist/types/tools/index.d.ts +17 -0
- package/dist/types/tools/render-utils.d.ts +1 -1
- package/dist/types/tools/tool-timeouts.d.ts +1 -1
- package/dist/types/utils/block-context.d.ts +35 -0
- package/dist/types/utils/image-loading.d.ts +12 -0
- package/package.json +29 -9
- package/src/capability/rule-buckets.ts +4 -2
- package/src/capability/rule.ts +10 -1
- package/src/cli/auth-broker-cli.ts +6 -7
- package/src/cli/auth-gateway-cli.ts +1 -1
- package/src/cli/list-models.ts +5 -0
- package/src/cli/update-cli.ts +138 -16
- package/src/config/model-registry.ts +81 -2
- package/src/debug/index.ts +4 -8
- package/src/discovery/at-imports.ts +273 -0
- package/src/discovery/builtin-rules/index.ts +4 -0
- package/src/discovery/builtin-rules/ts-no-test-timers.md +55 -0
- package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +75 -0
- package/src/discovery/helpers.ts +2 -1
- package/src/edit/diff.ts +114 -4
- package/src/edit/hashline/diff.ts +1 -1
- package/src/edit/hashline/execute.ts +1 -1
- package/src/edit/modes/patch.ts +6 -2
- package/src/edit/modes/replace.ts +1 -1
- package/src/edit/renderer.ts +12 -2
- package/src/eval/__tests__/agent-bridge.test.ts +13 -0
- package/src/eval/__tests__/{llm-bridge.test.ts → completion-bridge.test.ts} +60 -54
- package/src/eval/__tests__/helpers-local-roots.test.ts +58 -0
- package/src/eval/__tests__/js-context-manager.test.ts +241 -0
- package/src/eval/agent-bridge.ts +6 -1
- package/src/eval/backend.ts +15 -0
- package/src/eval/bridge-timeout.ts +1 -1
- package/src/eval/{llm-bridge.ts → completion-bridge.ts} +30 -27
- package/src/eval/idle-timeout.ts +1 -1
- package/src/eval/js/context-manager.ts +70 -8
- package/src/eval/js/executor.ts +3 -0
- package/src/eval/js/index.ts +7 -1
- package/src/eval/js/shared/helpers.ts +53 -6
- package/src/eval/js/shared/prelude.txt +4 -4
- package/src/eval/js/shared/runtime.ts +8 -0
- package/src/eval/js/tool-bridge.ts +3 -3
- package/src/eval/js/worker-core.ts +1 -0
- package/src/eval/js/worker-entry.ts +6 -0
- package/src/eval/js/worker-protocol.ts +6 -0
- package/src/eval/py/executor.ts +12 -0
- package/src/eval/py/index.ts +7 -1
- package/src/eval/py/prelude.py +46 -7
- package/src/eval/py/runner.py +1 -0
- package/src/exa/render.ts +1 -1
- package/src/export/ttsr.ts +122 -1
- package/src/extensibility/extensions/types.ts +8 -1
- package/src/extensibility/legacy-pi-ai-shim.ts +1 -1
- package/src/extensibility/plugins/doctor.ts +1 -1
- package/src/extensibility/plugins/legacy-pi-compat.ts +6 -5
- package/src/goals/tools/goal-tool.ts +1 -1
- package/src/internal-urls/docs-index.generated.ts +8 -6
- package/src/internal-urls/local-protocol.ts +13 -0
- package/src/lsp/render.ts +8 -6
- package/src/mcp/oauth-flow.ts +3 -3
- package/src/mcp/render.ts +7 -1
- package/src/modes/components/custom-editor.ts +12 -6
- package/src/modes/components/login-dialog.ts +1 -1
- package/src/modes/components/oauth-selector.ts +4 -4
- package/src/modes/components/read-tool-group.ts +10 -3
- package/src/modes/components/{status-line.ts → status-line/component.ts} +18 -40
- package/src/modes/components/status-line/index.ts +1 -0
- package/src/modes/components/status-line/types.ts +23 -8
- package/src/modes/components/tips.txt +1 -1
- package/src/modes/components/tool-execution.ts +1 -1
- package/src/modes/components/transcript-container.ts +17 -10
- package/src/modes/components/user-message.ts +6 -3
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/extension-ui-controller.ts +143 -127
- package/src/modes/controllers/input-controller.ts +36 -10
- package/src/modes/controllers/mcp-command-controller.ts +28 -12
- package/src/modes/controllers/selector-controller.ts +4 -11
- package/src/modes/controllers/ssh-command-controller.ts +2 -2
- package/src/modes/image-references.ts +13 -7
- package/src/modes/interactive-mode.ts +2 -2
- package/src/modes/rpc/rpc-mode.ts +1 -1
- package/src/modes/setup-wizard/scenes/sign-in.ts +3 -11
- package/src/modes/theme/theme.ts +95 -1
- package/src/modes/types.ts +2 -1
- package/src/modes/utils/ui-helpers.ts +14 -5
- package/src/prompts/system/tiny-title-system.md +1 -1
- package/src/prompts/system/title-system.md +16 -3
- package/src/prompts/system/workflow-notice.md +1 -1
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/eval.md +6 -6
- package/src/sdk.ts +31 -14
- package/src/session/agent-session.ts +213 -155
- package/src/session/session-manager.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +1 -1
- package/src/system-prompt.ts +15 -9
- package/src/task/render.ts +20 -8
- package/src/tools/ask.ts +14 -5
- package/src/tools/bash-interactive.ts +1 -1
- package/src/tools/bash.ts +14 -2
- package/src/tools/browser/render.ts +5 -2
- package/src/tools/browser/tab-worker.ts +211 -91
- package/src/tools/debug.ts +5 -2
- package/src/tools/eval-render.ts +8 -5
- package/src/tools/eval.ts +2 -2
- package/src/tools/gh-renderer.ts +29 -15
- package/src/tools/index.ts +32 -0
- package/src/tools/inspect-image-renderer.ts +12 -5
- package/src/tools/job.ts +9 -6
- package/src/tools/memory-render.ts +19 -5
- package/src/tools/read.ts +165 -18
- package/src/tools/render-utils.ts +3 -1
- package/src/tools/resolve.ts +1 -1
- package/src/tools/review.ts +1 -1
- package/src/tools/ssh.ts +4 -1
- package/src/tools/todo.ts +8 -1
- package/src/tools/tool-timeouts.ts +1 -1
- package/src/tools/write.ts +1 -1
- package/src/tui/code-cell.ts +1 -1
- package/src/utils/block-context.ts +312 -0
- package/src/utils/image-loading.ts +31 -1
- package/src/utils/title-generator.ts +2 -2
- package/src/web/search/providers/codex.ts +1 -1
- package/src/web/search/render.ts +14 -6
- /package/dist/types/eval/__tests__/{llm-bridge.test.d.ts → completion-bridge.test.d.ts} +0 -0
|
@@ -184,7 +184,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
|
|
|
184
184
|
showNewVersionNotification(newVersion: string): void;
|
|
185
185
|
clearEditor(): void;
|
|
186
186
|
updatePendingMessagesDisplay(): void;
|
|
187
|
-
queueCompactionMessage(text: string, mode: "steer" | "followUp"): void;
|
|
187
|
+
queueCompactionMessage(text: string, mode: "steer" | "followUp", images?: ImageContent[]): void;
|
|
188
188
|
flushCompactionQueue(options?: {
|
|
189
189
|
willRetry?: boolean;
|
|
190
190
|
}): Promise<void>;
|
|
@@ -6,7 +6,7 @@ export type SymbolPreset = "unicode" | "nerd" | "ascii";
|
|
|
6
6
|
/**
|
|
7
7
|
* All available symbol keys organized by category.
|
|
8
8
|
*/
|
|
9
|
-
export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.goal" | "icon.pause" | "icon.loop" | "icon.folder" | "icon.search" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.time" | "icon.pi" | "icon.agents" | "icon.cache" | "icon.input" | "icon.output" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.mic" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.autoPending" | "checkbox.checked" | "checkbox.unchecked" | "radio.selected" | "radio.unselected" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "md.colorSwatch" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.context" | "tab.editing" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers";
|
|
9
|
+
export type SymbolKey = "status.success" | "status.error" | "status.warning" | "status.info" | "status.pending" | "status.disabled" | "status.enabled" | "status.running" | "status.shadowed" | "status.aborted" | "status.done" | "nav.cursor" | "nav.selected" | "nav.expand" | "nav.collapse" | "nav.back" | "tree.branch" | "tree.last" | "tree.vertical" | "tree.horizontal" | "tree.hook" | "boxRound.topLeft" | "boxRound.topRight" | "boxRound.bottomLeft" | "boxRound.bottomRight" | "boxRound.horizontal" | "boxRound.vertical" | "boxSharp.topLeft" | "boxSharp.topRight" | "boxSharp.bottomLeft" | "boxSharp.bottomRight" | "boxSharp.horizontal" | "boxSharp.vertical" | "boxSharp.cross" | "boxSharp.teeDown" | "boxSharp.teeUp" | "boxSharp.teeRight" | "boxSharp.teeLeft" | "sep.powerline" | "sep.powerlineThin" | "sep.powerlineLeft" | "sep.powerlineRight" | "sep.powerlineThinLeft" | "sep.powerlineThinRight" | "sep.block" | "sep.space" | "sep.asciiLeft" | "sep.asciiRight" | "sep.dot" | "sep.slash" | "sep.pipe" | "icon.model" | "icon.plan" | "icon.goal" | "icon.pause" | "icon.loop" | "icon.folder" | "icon.search" | "icon.scratchFolder" | "icon.file" | "icon.git" | "icon.branch" | "icon.pr" | "icon.tokens" | "icon.context" | "icon.cost" | "icon.time" | "icon.pi" | "icon.agents" | "icon.cache" | "icon.input" | "icon.output" | "icon.host" | "icon.session" | "icon.package" | "icon.warning" | "icon.rewind" | "icon.auto" | "icon.fast" | "icon.extensionSkill" | "icon.extensionTool" | "icon.extensionSlashCommand" | "icon.extensionMcp" | "icon.extensionRule" | "icon.extensionHook" | "icon.extensionPrompt" | "icon.extensionContextFile" | "icon.extensionInstruction" | "icon.mic" | "thinking.minimal" | "thinking.low" | "thinking.medium" | "thinking.high" | "thinking.xhigh" | "thinking.autoPending" | "checkbox.checked" | "checkbox.unchecked" | "radio.selected" | "radio.unselected" | "format.bullet" | "format.dash" | "format.bracketLeft" | "format.bracketRight" | "md.quoteBorder" | "md.hrChar" | "md.bullet" | "md.colorSwatch" | "lang.default" | "lang.typescript" | "lang.javascript" | "lang.python" | "lang.rust" | "lang.go" | "lang.java" | "lang.c" | "lang.cpp" | "lang.csharp" | "lang.ruby" | "lang.php" | "lang.swift" | "lang.kotlin" | "lang.shell" | "lang.html" | "lang.css" | "lang.json" | "lang.yaml" | "lang.markdown" | "lang.sql" | "lang.docker" | "lang.lua" | "lang.text" | "lang.env" | "lang.toml" | "lang.xml" | "lang.ini" | "lang.conf" | "lang.log" | "lang.csv" | "lang.tsv" | "lang.image" | "lang.pdf" | "lang.archive" | "lang.binary" | "tab.appearance" | "tab.model" | "tab.interaction" | "tab.context" | "tab.editing" | "tab.tools" | "tab.memory" | "tab.tasks" | "tab.providers" | "tool.write" | "tool.edit" | "tool.bash" | "tool.ssh" | "tool.lsp" | "tool.gh" | "tool.webSearch" | "tool.exa" | "tool.browser" | "tool.eval" | "tool.debug" | "tool.mcp" | "tool.job" | "tool.task" | "tool.todo" | "tool.memory" | "tool.ask" | "tool.resolve" | "tool.review" | "tool.inspectImage" | "tool.goal";
|
|
10
10
|
export type SpinnerType = "status" | "activity";
|
|
11
11
|
export type ThemeColor = "accent" | "border" | "borderAccent" | "borderMuted" | "success" | "error" | "warning" | "muted" | "dim" | "text" | "thinkingText" | "userMessageText" | "customMessageText" | "customMessageLabel" | "toolTitle" | "toolOutput" | "mdHeading" | "mdLink" | "mdLinkUrl" | "mdCode" | "mdCodeBlock" | "mdCodeBlockBorder" | "mdQuote" | "mdQuoteBorder" | "mdHr" | "mdListBullet" | "toolDiffAdded" | "toolDiffRemoved" | "toolDiffContext" | "syntaxComment" | "syntaxKeyword" | "syntaxFunction" | "syntaxVariable" | "syntaxString" | "syntaxNumber" | "syntaxType" | "syntaxOperator" | "syntaxPunctuation" | "thinkingOff" | "thinkingMinimal" | "thinkingLow" | "thinkingMedium" | "thinkingHigh" | "thinkingXhigh" | "bashMode" | "pythonMode" | "statusLineSep" | "statusLineModel" | "statusLinePath" | "statusLineGitClean" | "statusLineGitDirty" | "statusLineContext" | "statusLineSpend" | "statusLineStaged" | "statusLineDirty" | "statusLineUntracked" | "statusLineOutput" | "statusLineCost" | "statusLineSubagents";
|
|
12
12
|
/** Check if a string is a valid ThemeColor value */
|
|
@@ -80,6 +80,7 @@ export declare class Theme {
|
|
|
80
80
|
running: string;
|
|
81
81
|
shadowed: string;
|
|
82
82
|
aborted: string;
|
|
83
|
+
done: string;
|
|
83
84
|
};
|
|
84
85
|
get nav(): {
|
|
85
86
|
cursor: string;
|
|
@@ -28,6 +28,7 @@ import type { Theme } from "./theme/theme";
|
|
|
28
28
|
export type CompactionQueuedMessage = {
|
|
29
29
|
text: string;
|
|
30
30
|
mode: "steer" | "followUp";
|
|
31
|
+
images?: ImageContent[];
|
|
31
32
|
};
|
|
32
33
|
export type SubmittedUserInput = {
|
|
33
34
|
text: string;
|
|
@@ -155,7 +156,7 @@ export interface InteractiveModeContext {
|
|
|
155
156
|
showNewVersionNotification(newVersion: string): void;
|
|
156
157
|
clearEditor(): void;
|
|
157
158
|
updatePendingMessagesDisplay(): void;
|
|
158
|
-
queueCompactionMessage(text: string, mode: "steer" | "followUp"): void;
|
|
159
|
+
queueCompactionMessage(text: string, mode: "steer" | "followUp", images?: ImageContent[]): void;
|
|
159
160
|
flushCompactionQueue(options?: {
|
|
160
161
|
willRetry?: boolean;
|
|
161
162
|
}): Promise<void>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
2
|
-
import type { AssistantMessage, Message } from "@oh-my-pi/pi-ai";
|
|
2
|
+
import type { AssistantMessage, ImageContent, Message } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import { type Component } from "@oh-my-pi/pi-tui";
|
|
4
4
|
import type { InteractiveModeContext } from "../../modes/types";
|
|
5
5
|
import type { SessionContext } from "../../session/session-manager";
|
|
@@ -42,7 +42,7 @@ export declare class UiHelpers {
|
|
|
42
42
|
showWarning(warningMessage: string): void;
|
|
43
43
|
showNewVersionNotification(newVersion: string): void;
|
|
44
44
|
updatePendingMessagesDisplay(): void;
|
|
45
|
-
queueCompactionMessage(text: string, mode: "steer" | "followUp"): void;
|
|
45
|
+
queueCompactionMessage(text: string, mode: "steer" | "followUp", images?: ImageContent[]): void;
|
|
46
46
|
isKnownSlashCommand(text: string): boolean;
|
|
47
47
|
flushCompactionQueue(options?: {
|
|
48
48
|
willRetry?: boolean;
|
|
@@ -299,8 +299,6 @@ export interface FreshSessionResult {
|
|
|
299
299
|
sessionId: string;
|
|
300
300
|
closedProviderSessions: number;
|
|
301
301
|
}
|
|
302
|
-
export declare const ANTHROPIC_TOOL_CALL_BATCH_CAP = 4;
|
|
303
|
-
export declare function resolveToolCallBatchCapForModel(model: Model | undefined): number | undefined;
|
|
304
302
|
export declare class AgentSession {
|
|
305
303
|
#private;
|
|
306
304
|
readonly agent: Agent;
|
|
@@ -79,6 +79,7 @@ export declare class AskTool implements AgentTool<typeof askSchema, AskToolDetai
|
|
|
79
79
|
}, z.core.$strip>>;
|
|
80
80
|
}, z.core.$strip>;
|
|
81
81
|
readonly strict = true;
|
|
82
|
+
readonly concurrency = "exclusive";
|
|
82
83
|
readonly loadMode = "discoverable";
|
|
83
84
|
constructor(session: ToolSession);
|
|
84
85
|
static createIf(session: ToolSession): AskTool | null;
|
|
@@ -11,9 +11,24 @@ declare global {
|
|
|
11
11
|
elementFromPoint(x: number, y: number): Element | null;
|
|
12
12
|
};
|
|
13
13
|
}
|
|
14
|
+
interface ScreenshotOptions {
|
|
15
|
+
selector?: string;
|
|
16
|
+
fullPage?: boolean;
|
|
17
|
+
save?: string;
|
|
18
|
+
silent?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface InflightOp {
|
|
21
|
+
label: string;
|
|
22
|
+
startedAt: number;
|
|
23
|
+
}
|
|
24
|
+
/** Human-readable label for a screenshot op, used in op tracking + timeout errors. */
|
|
25
|
+
export declare function describeScreenshot(opts?: ScreenshotOptions): string;
|
|
26
|
+
/** Summarize still-running helpers (oldest first) so a cell timeout names what stalled. */
|
|
27
|
+
export declare function describeInflight(inflight: Map<number, InflightOp>): string;
|
|
14
28
|
export declare class WorkerCore {
|
|
15
29
|
#private;
|
|
16
30
|
constructor(transport: Transport);
|
|
17
31
|
nextElementId(): number;
|
|
18
32
|
cacheElement(id: number, handle: ElementHandle): void;
|
|
19
33
|
}
|
|
34
|
+
export {};
|
|
@@ -287,6 +287,23 @@ export declare const DEFAULT_ESSENTIAL_TOOL_NAMES: readonly string[];
|
|
|
287
287
|
* otherwise `DEFAULT_ESSENTIAL_TOOL_NAMES`.
|
|
288
288
|
*/
|
|
289
289
|
export declare function computeEssentialBuiltinNames(settings: Settings): string[];
|
|
290
|
+
/**
|
|
291
|
+
* Filter the initial active tool set when `tools.discoveryMode === "all"`.
|
|
292
|
+
*
|
|
293
|
+
* Non-essential discoverable built-ins are hidden — the model rediscovers them
|
|
294
|
+
* via `search_tool_bm25` and activates them on demand. A tool survives hiding
|
|
295
|
+
* when it is essential, explicitly requested, restored from a prior selection,
|
|
296
|
+
* or required by a forced tool_choice feature (`forceActive`). The last case is
|
|
297
|
+
* load-bearing: a named tool_choice (e.g. the eager `todo` prelude) must
|
|
298
|
+
* reference a tool present in the request, or the provider rejects it with 400.
|
|
299
|
+
*/
|
|
300
|
+
export declare function filterInitialToolsForDiscoveryAll(initialToolNames: string[], opts: {
|
|
301
|
+
loadModeOf: (name: string) => BuiltinToolLoadMode | undefined;
|
|
302
|
+
essentialNames: ReadonlySet<string>;
|
|
303
|
+
explicitlyRequested: ReadonlySet<string>;
|
|
304
|
+
restored: ReadonlySet<string>;
|
|
305
|
+
forceActive: ReadonlySet<string>;
|
|
306
|
+
}): string[];
|
|
290
307
|
/**
|
|
291
308
|
* Public callable factory map. External callers may invoke `BUILTIN_TOOLS.read(session)` or
|
|
292
309
|
* `BUILTIN_TOOLS[name](session)` to construct a tool directly.
|
|
@@ -114,7 +114,7 @@ export declare function formatErrorDetail(message: string | undefined, theme: Th
|
|
|
114
114
|
export declare function formatEmptyMessage(message: string, theme: Theme): string;
|
|
115
115
|
export type CodeFrameMarker = "" | " " | "*" | "+" | "-" | ">";
|
|
116
116
|
export declare function formatCodeFrameLine(marker: CodeFrameMarker, lineNumber: string | number, content: string, lineNumberWidth: number): string;
|
|
117
|
-
export type ToolUIStatus = "success" | "error" | "warning" | "info" | "pending" | "running" | "aborted";
|
|
117
|
+
export type ToolUIStatus = "success" | "done" | "error" | "warning" | "info" | "pending" | "running" | "aborted";
|
|
118
118
|
export type ToolUIColor = "success" | "error" | "warning" | "accent" | "muted";
|
|
119
119
|
export interface ToolUITitleOptions {
|
|
120
120
|
bold?: boolean;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export interface LineSpan {
|
|
2
|
+
startLine: number;
|
|
3
|
+
endLine: number;
|
|
4
|
+
}
|
|
5
|
+
/** Where the source came from, so tree-sitter can pick a grammar. */
|
|
6
|
+
export interface BlockContextSource {
|
|
7
|
+
path?: string;
|
|
8
|
+
lang?: string;
|
|
9
|
+
}
|
|
10
|
+
export type LineEntry = {
|
|
11
|
+
kind: "line";
|
|
12
|
+
lineNumber: number;
|
|
13
|
+
text: string;
|
|
14
|
+
context: boolean;
|
|
15
|
+
} | {
|
|
16
|
+
kind: "ellipsis";
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Resolve the off-window boundary lines for a visible window: tree-sitter
|
|
20
|
+
* syntactic spans first (covers brace and indentation languages), falling back
|
|
21
|
+
* to a lexical bracket scan when the grammar is unavailable. Returns a map of
|
|
22
|
+
* `lineNumber → source text` for the lines to surface, never including a line
|
|
23
|
+
* already visible.
|
|
24
|
+
*/
|
|
25
|
+
export declare function findBlockContextLines(fullLines: readonly string[], visibleInput: ReadonlySet<number> | readonly number[], source?: BlockContextSource): Map<number, string>;
|
|
26
|
+
/**
|
|
27
|
+
* Build display entries for `visibleSpans` plus any off-window block-boundary
|
|
28
|
+
* lines, in source order, with `{ kind: "ellipsis" }` markers inserted across
|
|
29
|
+
* non-contiguous gaps. `options.lineText` lets callers substitute display text
|
|
30
|
+
* (e.g. column-truncated lines) for a given line number.
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildLineEntriesWithBlockContext(fullLines: readonly string[], visibleSpans: readonly LineSpan[], source?: BlockContextSource, options?: {
|
|
33
|
+
lineText?: (lineNumber: number, sourceText: string, context: boolean) => string;
|
|
34
|
+
}): LineEntry[];
|
|
35
|
+
export declare function lineEntriesToPlainText(entries: readonly LineEntry[], ellipsis?: string): string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
2
|
+
import { type ImageResizeOptions } from "./image-resize";
|
|
2
3
|
export declare const MAX_IMAGE_INPUT_BYTES: number;
|
|
3
4
|
export declare const SUPPORTED_INPUT_IMAGE_MIME_TYPES: Set<string>;
|
|
4
5
|
export interface LoadImageInputOptions {
|
|
@@ -23,4 +24,15 @@ export declare class ImageInputTooLargeError extends Error {
|
|
|
23
24
|
constructor(bytes: number, maxBytes: number);
|
|
24
25
|
}
|
|
25
26
|
export declare function ensureSupportedImageInput(image: ImageContent): Promise<ImageContent | null>;
|
|
27
|
+
export interface NormalizeModelContextImagesOptions {
|
|
28
|
+
resize?: ImageResizeOptions;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Normalize image blocks before they enter agent/model context. This keeps
|
|
32
|
+
* provider request construction from having to resize an unbounded batch of
|
|
33
|
+
* large images on the streaming hot path. Images are processed sequentially on
|
|
34
|
+
* purpose: `resizeImage` may fan out multiple encoders for one image, so the
|
|
35
|
+
* outer image batch must stay bounded.
|
|
36
|
+
*/
|
|
37
|
+
export declare function normalizeModelContextImages(images: ImageContent[] | undefined, options?: NormalizeModelContextImagesOptions): Promise<ImageContent[] | undefined>;
|
|
26
38
|
export declare function loadImageInput(options: LoadImageInputOptions): Promise<LoadedImageInput | null>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
4
|
-
"version": "15.10.
|
|
4
|
+
"version": "15.10.5",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -47,14 +47,14 @@
|
|
|
47
47
|
"@agentclientprotocol/sdk": "0.22.1",
|
|
48
48
|
"@babel/parser": "^7.29.7",
|
|
49
49
|
"@mozilla/readability": "^0.6.0",
|
|
50
|
-
"@oh-my-pi/hashline": "15.10.
|
|
51
|
-
"@oh-my-pi/omp-stats": "15.10.
|
|
52
|
-
"@oh-my-pi/pi-agent-core": "15.10.
|
|
53
|
-
"@oh-my-pi/pi-ai": "15.10.
|
|
54
|
-
"@oh-my-pi/pi-mnemopi": "15.10.
|
|
55
|
-
"@oh-my-pi/pi-natives": "15.10.
|
|
56
|
-
"@oh-my-pi/pi-tui": "15.10.
|
|
57
|
-
"@oh-my-pi/pi-utils": "15.10.
|
|
50
|
+
"@oh-my-pi/hashline": "15.10.5",
|
|
51
|
+
"@oh-my-pi/omp-stats": "15.10.5",
|
|
52
|
+
"@oh-my-pi/pi-agent-core": "15.10.5",
|
|
53
|
+
"@oh-my-pi/pi-ai": "15.10.5",
|
|
54
|
+
"@oh-my-pi/pi-mnemopi": "15.10.5",
|
|
55
|
+
"@oh-my-pi/pi-natives": "15.10.5",
|
|
56
|
+
"@oh-my-pi/pi-tui": "15.10.5",
|
|
57
|
+
"@oh-my-pi/pi-utils": "15.10.5",
|
|
58
58
|
"@opentelemetry/api": "^1.9.1",
|
|
59
59
|
"@opentelemetry/context-async-hooks": "^2.7.1",
|
|
60
60
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
|
|
@@ -134,6 +134,14 @@
|
|
|
134
134
|
"types": "./dist/types/cli/*.d.ts",
|
|
135
135
|
"import": "./src/cli/*.ts"
|
|
136
136
|
},
|
|
137
|
+
"./cli/gallery-fixtures": {
|
|
138
|
+
"types": "./dist/types/cli/gallery-fixtures/index.d.ts",
|
|
139
|
+
"import": "./src/cli/gallery-fixtures/index.ts"
|
|
140
|
+
},
|
|
141
|
+
"./cli/gallery-fixtures/*": {
|
|
142
|
+
"types": "./dist/types/cli/gallery-fixtures/*.d.ts",
|
|
143
|
+
"import": "./src/cli/gallery-fixtures/*.ts"
|
|
144
|
+
},
|
|
137
145
|
"./cli/commands/*": {
|
|
138
146
|
"types": "./dist/types/cli/commands/*.d.ts",
|
|
139
147
|
"import": "./src/cli/commands/*.ts"
|
|
@@ -442,6 +450,14 @@
|
|
|
442
450
|
"types": "./dist/types/modes/controllers/*.d.ts",
|
|
443
451
|
"import": "./src/modes/controllers/*.ts"
|
|
444
452
|
},
|
|
453
|
+
"./modes/setup-wizard": {
|
|
454
|
+
"types": "./dist/types/modes/setup-wizard/index.d.ts",
|
|
455
|
+
"import": "./src/modes/setup-wizard/index.ts"
|
|
456
|
+
},
|
|
457
|
+
"./modes/setup-wizard/*": {
|
|
458
|
+
"types": "./dist/types/modes/setup-wizard/*.d.ts",
|
|
459
|
+
"import": "./src/modes/setup-wizard/*.ts"
|
|
460
|
+
},
|
|
445
461
|
"./modes/rpc/*": {
|
|
446
462
|
"types": "./dist/types/modes/rpc/*.d.ts",
|
|
447
463
|
"import": "./src/modes/rpc/*.ts"
|
|
@@ -499,6 +515,10 @@
|
|
|
499
515
|
"types": "./dist/types/task/*.d.ts",
|
|
500
516
|
"import": "./src/task/*.ts"
|
|
501
517
|
},
|
|
518
|
+
"./tool-discovery/*": {
|
|
519
|
+
"types": "./dist/types/tool-discovery/*.d.ts",
|
|
520
|
+
"import": "./src/tool-discovery/*.ts"
|
|
521
|
+
},
|
|
502
522
|
"./tools": {
|
|
503
523
|
"types": "./dist/types/tools/index.d.ts",
|
|
504
524
|
"import": "./src/tools/index.ts"
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* manager, and splits the rest into the always-apply and rulebook buckets.
|
|
7
7
|
*
|
|
8
8
|
* Bucket precedence (matches docs/rulebook-matching-pipeline.md §5):
|
|
9
|
-
* 1. TTSR — non-empty `condition` that `TtsrManager.addRule` accepts
|
|
9
|
+
* 1. TTSR — non-empty `condition`/`astCondition` that `TtsrManager.addRule` accepts
|
|
10
10
|
* 2. always — `alwaysApply === true`
|
|
11
11
|
* 3. rulebook — has a `description`
|
|
12
12
|
*/
|
|
@@ -49,7 +49,9 @@ export function bucketRules(
|
|
|
49
49
|
if (disabled.has(rule.name)) continue;
|
|
50
50
|
if (!includeBuiltin && rule._source?.provider === BUILTIN_DEFAULTS_PROVIDER_ID) continue;
|
|
51
51
|
|
|
52
|
-
const
|
|
52
|
+
const hasTtsrCondition =
|
|
53
|
+
(rule.condition && rule.condition.length > 0) || (rule.astCondition && rule.astCondition.length > 0);
|
|
54
|
+
const isTtsrRule = hasTtsrCondition ? ttsrManager.addRule(rule) : false;
|
|
53
55
|
if (isTtsrRule) continue;
|
|
54
56
|
if (rule.alwaysApply === true) {
|
|
55
57
|
alwaysApplyRules.push(rule);
|
package/src/capability/rule.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface RuleFrontmatter {
|
|
|
26
26
|
alwaysApply?: boolean;
|
|
27
27
|
/** New key for TTSR match conditions. */
|
|
28
28
|
condition?: string | string[];
|
|
29
|
+
/** TTSR match condition(s) expressed as ast-grep patterns (edit/write streams only). */
|
|
30
|
+
astCondition?: string | string[];
|
|
29
31
|
/** New key for TTSR stream scope. */
|
|
30
32
|
scope?: string | string[];
|
|
31
33
|
/** Per-rule TTSR interrupt mode override. */
|
|
@@ -51,6 +53,8 @@ export interface Rule {
|
|
|
51
53
|
description?: string;
|
|
52
54
|
/** Regex condition(s) that can trigger TTSR interruption. */
|
|
53
55
|
condition?: string[];
|
|
56
|
+
/** ast-grep pattern condition(s) that can trigger TTSR interruption (edit/write streams only). */
|
|
57
|
+
astCondition?: string[];
|
|
54
58
|
/** Optional stream scope tokens (for example: text, thinking, tool:edit(*.ts)). */
|
|
55
59
|
scope?: string[];
|
|
56
60
|
/** Per-rule TTSR interrupt mode override (falls back to global ttsr.interruptMode). */
|
|
@@ -188,10 +192,14 @@ function isLikelyFileGlob(value: string): boolean {
|
|
|
188
192
|
* - legacy `ttsr_trigger` / `ttsrTrigger` are accepted as a `condition` fallback
|
|
189
193
|
* - condition tokens that look like file globs become scope shorthands:
|
|
190
194
|
* `*.rs` => `tool:edit(*.rs)`, `tool:write(*.rs)` and a catch-all condition `.*`
|
|
195
|
+
* - `astCondition` holds ast-grep patterns and is kept verbatim (no glob inference)
|
|
191
196
|
*/
|
|
192
|
-
export function parseRuleConditionAndScope(
|
|
197
|
+
export function parseRuleConditionAndScope(
|
|
198
|
+
frontmatter: RuleFrontmatter,
|
|
199
|
+
): Pick<Rule, "condition" | "astCondition" | "scope"> {
|
|
193
200
|
const rawCondition = frontmatter.condition ?? frontmatter.ttsr_trigger ?? frontmatter.ttsrTrigger;
|
|
194
201
|
const parsedCondition = normalizeRuleField(rawCondition);
|
|
202
|
+
const astCondition = normalizeRuleField(frontmatter.astCondition);
|
|
195
203
|
const parsedScope = normalizeScopeField(frontmatter.scope);
|
|
196
204
|
|
|
197
205
|
const inferredScope: string[] = [];
|
|
@@ -213,6 +221,7 @@ export function parseRuleConditionAndScope(frontmatter: RuleFrontmatter): Pick<R
|
|
|
213
221
|
const scope = [...(parsedScope ?? []), ...inferredScope];
|
|
214
222
|
return {
|
|
215
223
|
condition: condition.length > 0 ? Array.from(new Set(condition)) : undefined,
|
|
224
|
+
astCondition,
|
|
216
225
|
scope: scope.length > 0 ? Array.from(new Set(scope)) : undefined,
|
|
217
226
|
};
|
|
218
227
|
}
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
type OAuthCredential,
|
|
31
31
|
type OAuthProvider,
|
|
32
32
|
type OAuthProviderInfo,
|
|
33
|
+
PROVIDER_REGISTRY,
|
|
33
34
|
SqliteAuthCredentialStore,
|
|
34
35
|
startAuthBroker,
|
|
35
36
|
} from "@oh-my-pi/pi-ai";
|
|
@@ -75,13 +76,11 @@ const ACTIONS: readonly AuthBrokerAction[] = [
|
|
|
75
76
|
];
|
|
76
77
|
|
|
77
78
|
/** Callback ports baked from the per-provider OAuth flow modules. */
|
|
78
|
-
const CALLBACK_PORTS: Record<string, number> =
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"gitlab-duo": 8080,
|
|
84
|
-
};
|
|
79
|
+
const CALLBACK_PORTS: Record<string, number> = Object.fromEntries(
|
|
80
|
+
PROVIDER_REGISTRY.flatMap(provider =>
|
|
81
|
+
provider.callbackPort != null ? [[provider.id, provider.callbackPort] as [string, number]] : [],
|
|
82
|
+
),
|
|
83
|
+
);
|
|
85
84
|
|
|
86
85
|
function getTokenFilePath(): string {
|
|
87
86
|
return path.join(getConfigRootDir(), "auth-broker.token");
|
|
@@ -357,7 +357,7 @@ export async function runAuthGatewayCommand(cmd: AuthGatewayCommandArgs): Promis
|
|
|
357
357
|
/**
|
|
358
358
|
* Providers whose chat endpoint expects a JSON-serialized credential blob
|
|
359
359
|
* (`{ token, projectId, refreshToken, expiresAt, … }`) rather than the raw
|
|
360
|
-
* access token. Mirrors `getOAuthApiKey` in `packages/ai/src/
|
|
360
|
+
* access token. Mirrors `getOAuthApiKey` in `packages/ai/src/registry/oauth`.
|
|
361
361
|
*/
|
|
362
362
|
const STRUCTURED_API_KEY_PROVIDERS: ReadonlySet<string> = new Set([
|
|
363
363
|
"github-copilot",
|
package/src/cli/list-models.ts
CHANGED
|
@@ -189,6 +189,11 @@ export async function runListModelsCommand(options: RunListModelsOptions): Promi
|
|
|
189
189
|
modelRegistry.registerProvider(name, config, sourceId);
|
|
190
190
|
}
|
|
191
191
|
extensionsResult.runtime.pendingProviderRegistrations = [];
|
|
192
|
+
// Discover runtime (extension) provider catalogs now that they are registered.
|
|
193
|
+
// The full refresh in main.ts ran before extensions loaded, so this is the only
|
|
194
|
+
// point where extension-contributed dynamic providers get discovered. Cache-aware
|
|
195
|
+
// so it reuses the shared 24 h model cache instead of refetching every invocation.
|
|
196
|
+
await modelRegistry.refreshRuntimeProviders("online-if-uncached");
|
|
192
197
|
|
|
193
198
|
await listModels(modelRegistry, searchPattern);
|
|
194
199
|
}
|
package/src/cli/update-cli.ts
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* Update CLI command handler.
|
|
3
3
|
*
|
|
4
4
|
* Handles `omp update` to check for and install updates.
|
|
5
|
-
* Uses
|
|
5
|
+
* Uses the installer that owns the active omp executable when it can be detected.
|
|
6
6
|
*/
|
|
7
7
|
import * as fs from "node:fs";
|
|
8
|
+
import * as os from "node:os";
|
|
8
9
|
import * as path from "node:path";
|
|
9
10
|
import { pipeline } from "node:stream/promises";
|
|
10
11
|
import { $which, APP_NAME, isEnoent, VERSION } from "@oh-my-pi/pi-utils";
|
|
@@ -14,6 +15,8 @@ import { theme } from "../modes/theme/theme";
|
|
|
14
15
|
|
|
15
16
|
const REPO = "can1357/oh-my-pi";
|
|
16
17
|
const PACKAGE = "@oh-my-pi/pi-coding-agent";
|
|
18
|
+
const HOMEBREW_FORMULA = "can1357/tap/omp";
|
|
19
|
+
const MISE_TOOL = "github:can1357/oh-my-pi";
|
|
17
20
|
/**
|
|
18
21
|
* Official npm registry origin.
|
|
19
22
|
*
|
|
@@ -102,6 +105,46 @@ async function getBunGlobalBinDir(): Promise<string | undefined> {
|
|
|
102
105
|
}
|
|
103
106
|
}
|
|
104
107
|
|
|
108
|
+
async function getHomebrewFormulaPrefix(): Promise<string | undefined> {
|
|
109
|
+
if (!$which("brew")) return undefined;
|
|
110
|
+
for (const formula of [HOMEBREW_FORMULA, APP_NAME]) {
|
|
111
|
+
try {
|
|
112
|
+
const result = await $`brew --prefix ${formula}`.quiet().nothrow();
|
|
113
|
+
if (result.exitCode !== 0) continue;
|
|
114
|
+
const output = result.text().trim();
|
|
115
|
+
if (output.length > 0) return output;
|
|
116
|
+
} catch {}
|
|
117
|
+
}
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function getMiseBinDirs(): Promise<string[]> {
|
|
122
|
+
if (!$which("mise")) return [];
|
|
123
|
+
try {
|
|
124
|
+
const result = await $`mise bin-paths ${MISE_TOOL}`.quiet().nothrow();
|
|
125
|
+
if (result.exitCode !== 0) return [];
|
|
126
|
+
return result
|
|
127
|
+
.text()
|
|
128
|
+
.split(/\r?\n/)
|
|
129
|
+
.map(line => line.trim())
|
|
130
|
+
.filter(line => line.length > 0);
|
|
131
|
+
} catch {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function getMiseDataDir(): string {
|
|
137
|
+
const override = process.env.MISE_DATA_DIR;
|
|
138
|
+
if (override && override.length > 0) return override;
|
|
139
|
+
if (process.platform === "win32") {
|
|
140
|
+
const localAppData = process.env.LOCALAPPDATA;
|
|
141
|
+
if (localAppData && localAppData.length > 0) return path.join(localAppData, "mise");
|
|
142
|
+
}
|
|
143
|
+
const xdgDataHome = process.env.XDG_DATA_HOME;
|
|
144
|
+
if (xdgDataHome && xdgDataHome.length > 0) return path.join(xdgDataHome, "mise");
|
|
145
|
+
return path.join(os.homedir(), ".local", "share", "mise");
|
|
146
|
+
}
|
|
147
|
+
|
|
105
148
|
function normalizePathForComparison(filePath: string): string {
|
|
106
149
|
const normalized = path.normalize(filePath);
|
|
107
150
|
if (process.platform === "win32") return normalized.toLowerCase();
|
|
@@ -129,34 +172,61 @@ function isPathInDirectory(filePath: string, directoryPath: string): boolean {
|
|
|
129
172
|
// is a junction when Bun is installed via Scoop, so `bun pm bin -g` and the
|
|
130
173
|
// PATH-resolved omp path can refer to the same directory through different
|
|
131
174
|
// strings. path.resolve does not traverse junctions/symlinks; realpath does.
|
|
132
|
-
// Resolve the file
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
const fileDir = tryRealpath(path.dirname(path.resolve(filePath)));
|
|
175
|
+
// Resolve both the file and its parent directory: the file catches manager
|
|
176
|
+
// links like Homebrew's `bin/omp -> Cellar/.../bin/omp`; the parent fallback
|
|
177
|
+
// still tolerates fresh install paths where the file does not exist yet.
|
|
136
178
|
const dirReal = tryRealpath(path.resolve(directoryPath));
|
|
137
|
-
if (!
|
|
179
|
+
if (!dirReal) return false;
|
|
180
|
+
const fileReal = tryRealpath(path.resolve(filePath));
|
|
181
|
+
if (fileReal && isPathInDirectoryLexical(fileReal, dirReal)) return true;
|
|
182
|
+
const fileDir = tryRealpath(path.dirname(path.resolve(filePath)));
|
|
183
|
+
if (!fileDir) return false;
|
|
138
184
|
const resolvedFile = path.join(fileDir, path.basename(filePath));
|
|
139
185
|
return isPathInDirectoryLexical(resolvedFile, dirReal);
|
|
140
186
|
}
|
|
141
187
|
|
|
142
|
-
type
|
|
188
|
+
type UpdateMethod = "brew" | "mise" | "bun" | "binary";
|
|
189
|
+
|
|
190
|
+
interface UpdateMethodResolutionOptions {
|
|
191
|
+
homebrewPrefix?: string;
|
|
192
|
+
miseBinDirs?: readonly string[];
|
|
193
|
+
miseDataDir?: string;
|
|
194
|
+
}
|
|
143
195
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
196
|
+
type UpdateTarget = { method: "brew" } | { method: "mise" } | { method: "bun" } | { method: "binary"; path: string };
|
|
197
|
+
|
|
198
|
+
function resolveUpdateMethod(
|
|
199
|
+
ompPath: string,
|
|
200
|
+
bunBinDir: string | undefined,
|
|
201
|
+
options: UpdateMethodResolutionOptions = {},
|
|
202
|
+
): UpdateMethod {
|
|
203
|
+
const { homebrewPrefix, miseBinDirs = [], miseDataDir } = options;
|
|
204
|
+
if (homebrewPrefix && isPathInDirectory(ompPath, path.join(homebrewPrefix, "bin"))) return "brew";
|
|
205
|
+
if (miseBinDirs.some(dir => isPathInDirectory(ompPath, dir))) return "mise";
|
|
206
|
+
if (miseDataDir && isPathInDirectory(ompPath, path.join(miseDataDir, "shims"))) return "mise";
|
|
207
|
+
if (bunBinDir && isPathInDirectory(ompPath, bunBinDir)) return "bun";
|
|
208
|
+
return "binary";
|
|
147
209
|
}
|
|
148
210
|
|
|
149
|
-
export function resolveUpdateMethodForTest(
|
|
150
|
-
|
|
211
|
+
export function resolveUpdateMethodForTest(
|
|
212
|
+
ompPath: string,
|
|
213
|
+
bunBinDir: string | undefined,
|
|
214
|
+
options: UpdateMethodResolutionOptions = {},
|
|
215
|
+
): UpdateMethod {
|
|
216
|
+
return resolveUpdateMethod(ompPath, bunBinDir, options);
|
|
151
217
|
}
|
|
152
218
|
async function resolveUpdateTarget(): Promise<UpdateTarget> {
|
|
153
219
|
const bunBinDir = await getBunGlobalBinDir();
|
|
220
|
+
const homebrewPrefix = await getHomebrewFormulaPrefix();
|
|
221
|
+
const miseAvailable = $which("mise") !== undefined;
|
|
222
|
+
const miseBinDirs = miseAvailable ? await getMiseBinDirs() : [];
|
|
223
|
+
const miseDataDir = miseAvailable ? getMiseDataDir() : undefined;
|
|
154
224
|
const ompPath = resolveOmpPath();
|
|
155
225
|
|
|
156
226
|
if (ompPath) {
|
|
157
|
-
const method = resolveUpdateMethod(ompPath, bunBinDir);
|
|
158
|
-
if (method === "
|
|
159
|
-
return { method
|
|
227
|
+
const method = resolveUpdateMethod(ompPath, bunBinDir, { homebrewPrefix, miseBinDirs, miseDataDir });
|
|
228
|
+
if (method === "binary") return { method, path: ompPath };
|
|
229
|
+
return { method };
|
|
160
230
|
}
|
|
161
231
|
|
|
162
232
|
if (bunBinDir) return { method: "bun" };
|
|
@@ -376,6 +446,18 @@ export function buildBunInstallArgs(expectedVersion: string, nativeTag: string =
|
|
|
376
446
|
return args;
|
|
377
447
|
}
|
|
378
448
|
|
|
449
|
+
export function buildHomebrewUpdateArgs(force: boolean): string[] {
|
|
450
|
+
return [force ? "reinstall" : "upgrade", HOMEBREW_FORMULA];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
export function buildMiseUpgradeArgs(): string[] {
|
|
454
|
+
return ["upgrade", MISE_TOOL, "--bump"];
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export function buildMiseForceInstallArgs(expectedVersion: string): string[] {
|
|
458
|
+
return ["install", "--force", `${MISE_TOOL}@${expectedVersion}`];
|
|
459
|
+
}
|
|
460
|
+
|
|
379
461
|
/**
|
|
380
462
|
* Update via bun package manager.
|
|
381
463
|
*/
|
|
@@ -390,6 +472,42 @@ async function updateViaBun(expectedVersion: string): Promise<void> {
|
|
|
390
472
|
await printVerification(expectedVersion);
|
|
391
473
|
}
|
|
392
474
|
|
|
475
|
+
async function updateViaHomebrew(expectedVersion: string, force: boolean): Promise<void> {
|
|
476
|
+
console.log(chalk.dim("Updating Homebrew formulae..."));
|
|
477
|
+
const update = await $`brew update`.nothrow();
|
|
478
|
+
if (update.exitCode !== 0) {
|
|
479
|
+
throw new Error(`brew update failed with exit code ${update.exitCode}`);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
console.log(chalk.dim("Updating via Homebrew..."));
|
|
483
|
+
const args = buildHomebrewUpdateArgs(force);
|
|
484
|
+
const result = await $`brew ${args}`.nothrow();
|
|
485
|
+
if (result.exitCode !== 0) {
|
|
486
|
+
throw new Error(`brew ${args[0]} failed with exit code ${result.exitCode}`);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
await printVerification(expectedVersion);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
async function updateViaMise(expectedVersion: string, force: boolean): Promise<void> {
|
|
493
|
+
console.log(chalk.dim("Updating via mise..."));
|
|
494
|
+
const args = buildMiseUpgradeArgs();
|
|
495
|
+
const result = await $`mise ${args}`.nothrow();
|
|
496
|
+
if (result.exitCode !== 0) {
|
|
497
|
+
throw new Error(`mise upgrade failed with exit code ${result.exitCode}`);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (force) {
|
|
501
|
+
const forceArgs = buildMiseForceInstallArgs(expectedVersion);
|
|
502
|
+
const forceResult = await $`mise ${forceArgs}`.nothrow();
|
|
503
|
+
if (forceResult.exitCode !== 0) {
|
|
504
|
+
throw new Error(`mise install --force failed with exit code ${forceResult.exitCode}`);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
await printVerification(expectedVersion);
|
|
509
|
+
}
|
|
510
|
+
|
|
393
511
|
/**
|
|
394
512
|
* Download a release binary to a target path, replacing an existing file.
|
|
395
513
|
*/
|
|
@@ -457,7 +575,11 @@ export async function runUpdateCommand(opts: { force: boolean; check: boolean })
|
|
|
457
575
|
// Choose update method based on the prioritized omp binary in PATH
|
|
458
576
|
try {
|
|
459
577
|
const target = await resolveUpdateTarget();
|
|
460
|
-
if (target.method === "
|
|
578
|
+
if (target.method === "brew") {
|
|
579
|
+
await updateViaHomebrew(release.version, opts.force);
|
|
580
|
+
} else if (target.method === "mise") {
|
|
581
|
+
await updateViaMise(release.version, opts.force);
|
|
582
|
+
} else if (target.method === "bun") {
|
|
461
583
|
await updateViaBun(release.version);
|
|
462
584
|
} else {
|
|
463
585
|
await updateViaBinaryAt(target.path, release.version);
|