feat: back up omp config (agent settings, commands, extensions, themes, loops, plugins)
This commit is contained in:
commit
31c9602d0c
22 files changed
+1154
No files matched your search
@@ -0,0 +1,84 @@
|
||||
---
|
||||
description: Convert a LeetCode solution file into a Fumadocs MDX docs page
|
||||
---
|
||||
|
||||
Convert the LeetCode solution file at `$1` into a Fumadocs-formatted MDX page.
|
||||
|
||||
Extra instructions (optional, may include output dir override): $@
|
||||
|
||||
## Input format
|
||||
|
||||
The source file follows the standard on-disk format:
|
||||
- A `/* ... */` header block: problem number, title, difficulty, LeetCode URL, problem description, examples, constraints
|
||||
- A JSDoc `@param`/`@return` block
|
||||
- One or more solution functions (possibly with line-by-line beginner comments and `// ====` banners)
|
||||
|
||||
## Output location
|
||||
|
||||
Default: write next to the content root at `content/docs/leetcode/<difficulty>/<category>/<number>-<slug>.mdx`, mirroring the source tree (e.g. `Easy/Array/1365.how-many-numbers.js` → `content/docs/leetcode/easy/array/1365-how-many-numbers.mdx`). Lowercase all path segments. If the extra instructions specify a different output path or content root, use that instead. Create directories as needed.
|
||||
|
||||
## Page structure
|
||||
|
||||
Produce exactly this shape:
|
||||
|
||||
```mdx
|
||||
---
|
||||
title: 1365. How Many Numbers Are Smaller Than the Current Number
|
||||
description: <one-sentence summary of the technique, e.g. "Frequency map + prefix count — the counting hammer.">
|
||||
---
|
||||
|
||||
<Callout title="Easy · Array · Hash Table">
|
||||
[View on LeetCode](https://leetcode.com/problems/how-many-numbers-are-smaller-than-the-current-number/)
|
||||
</Callout>
|
||||
|
||||
## Problem
|
||||
|
||||
<problem description from the header block, reflowed into clean Markdown prose — no hard-wrapped 70-char lines>
|
||||
|
||||
## Examples
|
||||
|
||||
<each example as a fenced code block:>
|
||||
|
||||
```txt title="Example 1"
|
||||
Input: nums = [8,1,2,2,3]
|
||||
Output: [4,0,1,1,3]
|
||||
```
|
||||
|
||||
<if an example has an Explanation, put it as plain prose under its code block>
|
||||
|
||||
## Constraints
|
||||
|
||||
<bulleted list from the header block, with values in inline code: `2 <= nums.length <= 500`>
|
||||
|
||||
## Solution
|
||||
|
||||
<one-paragraph intuition: name the pattern (frequency map, prefix sum, two pointers, etc.) and the key insight in 2–3 sentences. Derive this from the code itself.>
|
||||
|
||||
```js title="solution.js" lineNumbers
|
||||
<the solution function, verbatim — including any existing beginner comments>
|
||||
```
|
||||
|
||||
<Callout type="idea" title="Complexity">
|
||||
**Time:** O(n log n) — **Space:** O(n)
|
||||
</Callout>
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
1. **Frontmatter `title` carries the h1** — Fumadocs renders it as the page title, so never emit a `# heading` in the body.
|
||||
2. **Escape MDX hazards.** The header text may contain `<`, `>`, `{`, `}` (e.g. `j != i and nums[j] < nums[i]`, `2 <= nums.length <= 500`). Anything with these characters must be wrapped in inline code or fenced blocks, or the MDX compile will fail. When in doubt, inline-code it.
|
||||
3. **Code is verbatim.** Copy the solution function(s) exactly — do not rewrite, reformat, or strip existing comments. The docs page documents the code as it exists.
|
||||
4. **Multiple approaches → tab group:**
|
||||
````mdx
|
||||
```js tab="Frequency Map" tab-group="approach"
|
||||
...
|
||||
```
|
||||
|
||||
```js tab="Sort + Map"
|
||||
...
|
||||
```
|
||||
````
|
||||
5. **Complexity callout is derived, not guessed blindly.** Read the code: nested loop → O(n²), sort → O(n log n), single pass + map → O(n). If genuinely ambiguous, state the dominant term and why in one clause.
|
||||
6. **Callout header line** = `<Difficulty> · <topic tags>`. Difficulty comes from the header block; infer 1–2 topic tags from the directory path and the technique used.
|
||||
7. **No invented content.** Problem statement, examples, and constraints come only from the header block. The intuition paragraph and complexity are the only sections you author.
|
||||
8. **After writing**, print one line: the output path. Nothing else.
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
description: Add super-simple line-by-line comments to a LeetCode solution file
|
||||
---
|
||||
|
||||
Add beginner-level line-by-line comments to the LeetCode solution file at: $1
|
||||
|
||||
## Rules
|
||||
|
||||
1. **Read the file first.** It follows the standard on-disk format:
|
||||
- A `/* ... */` header block containing problem number, title, difficulty, URL, description, examples, and constraints
|
||||
- A JSDoc `@param`/`@return` block
|
||||
- The solution function(s)
|
||||
|
||||
2. **Never touch the header block or JSDoc block.** Preserve them byte-for-byte.
|
||||
|
||||
3. **Never change the code.** Do not rename variables, reformat logic, alter whitespace inside expressions, or "improve" anything. Comments only. The file must produce an identical AST after your edit.
|
||||
|
||||
4. **Comment style — explain as if to a complete beginner who has never programmed:**
|
||||
- One comment line (or short block) ABOVE each meaningful line/statement
|
||||
- Use plain everyday language and physical metaphors:
|
||||
- object/map → "box" ("Create an empty box to count how many times each number appears")
|
||||
- increment → "add 1 to its count"
|
||||
- `(freq[x] || 0) + 1` → "If we already saw this number, add 1 to its count. If we never saw it before, start counting from 1"
|
||||
- sort comparator → spell out the rule in words ("put the letter that appears MORE times first; if tied, alphabetical order")
|
||||
- return → say WHAT is being returned and WHY in plain words
|
||||
- Explain the WHY, not just the WHAT ("so the next bigger numbers know there are more smaller ones")
|
||||
- Inline comments (`// ...` at end of line) are fine for short trailing clarifications like `.join('')` → "glue the sorted letters back into a string"
|
||||
- No jargon without immediate translation: if you must say "index", follow with "(its position)"
|
||||
|
||||
5. **Add a section banner** above the solution function in this exact style:
|
||||
```js
|
||||
// ============================================
|
||||
// <problem number>. <problem title>
|
||||
// ============================================
|
||||
```
|
||||
Pull the number and title from the header block.
|
||||
|
||||
6. **Multiple solutions in one file:** comment every one of them, each with its own banner (append "– Approach 2" etc. to the banner title).
|
||||
|
||||
7. **Extra instructions** (if provided after the file path): $@
|
||||
|
||||
8. **After editing**, print a one-line confirmation: file path + number of comment lines added. Nothing else.
|
||||
|
||||
## Example transformation
|
||||
|
||||
Input body:
|
||||
```js
|
||||
var smallerNumbersThanCurrent = function (nums) {
|
||||
const freq = {};
|
||||
for (let x of nums) freq[x] = (freq[x] || 0) + 1;
|
||||
return nums.map((x) => smaller[x]);
|
||||
};
|
||||
```
|
||||
|
||||
Output body:
|
||||
```js
|
||||
// ============================================
|
||||
// 1365. How Many Numbers Are Smaller Than the Current Number
|
||||
// ============================================
|
||||
|
||||
var smallerNumbersThanCurrent = function (nums) {
|
||||
// Create an empty box to store how many times each number appears
|
||||
const freq = {};
|
||||
|
||||
// Go through every number in the list one by one
|
||||
// If we already saw this number, add 1 to its count
|
||||
// If we never saw it before, start counting from 1
|
||||
for (let x of nums) freq[x] = (freq[x] || 0) + 1;
|
||||
|
||||
// Go through the original list and replace each number
|
||||
// with "how many numbers are smaller than it"
|
||||
return nums.map((x) => smaller[x]);
|
||||
};
|
||||
```
|
||||
@@ -0,0 +1,31 @@
|
||||
theme:
|
||||
dark: titanium
|
||||
light: light
|
||||
symbolPreset: nerd
|
||||
setupVersion: 2
|
||||
hideThinkingBlock: false
|
||||
modelRoles:
|
||||
plan: anthropic/claude-fable-5-1:high
|
||||
default: anthropic/claude-opus-5-5:high
|
||||
smol: anthropic/claude-haiku-5-5
|
||||
task: anthropic/claude-sonnet-5-5
|
||||
web: web/exa
|
||||
dictation: local/parakeet-tdt-0.6b-v3
|
||||
speech: local/kokoro
|
||||
image: openai/gpt-image-2
|
||||
advisor: anthropic/claude-mythos-5-1:xhigh
|
||||
slow: anthropic/claude-fable-5-1:high
|
||||
vision: openai/gpt-6-astra:high
|
||||
commit: cerebras/qwen-3-coder-480b
|
||||
tiny: opencode-go/step-5-preview:auto
|
||||
memory: opencode-go/gemini-4-argon-contributor:medium
|
||||
judge: anthropic/claude-fable-5-1:max
|
||||
composer:
|
||||
shape: rule
|
||||
modelPresets:
|
||||
Primary:
|
||||
modelRoles:
|
||||
plan: anthropic/claude-fable-5-1:high
|
||||
default: anthropic/claude-opus-5-5:high
|
||||
smol: anthropic/claude-haiku-5-5
|
||||
defaultThinkingLevel: high
|
||||
@@ -0,0 +1,471 @@
|
||||
// installed by herdr
|
||||
// managed by herdr; reinstalling or updating the integration overwrites this file.
|
||||
// add custom hooks/plugins beside this file instead of editing it.
|
||||
// HERDR_INTEGRATION_ID=omp
|
||||
// HERDR_INTEGRATION_VERSION=10
|
||||
// @ts-nocheck
|
||||
|
||||
import net from "node:net";
|
||||
import path from "node:path";
|
||||
|
||||
const HERDR_ENV = process.env.HERDR_ENV;
|
||||
const socketPath = process.env.HERDR_SOCKET_PATH;
|
||||
const socketEndpoint =
|
||||
process.platform === "win32" && socketPath ? `\\\\.\\pipe\\${socketPath}` : socketPath;
|
||||
const paneId = process.env.HERDR_PANE_ID;
|
||||
const source = "herdr:omp";
|
||||
// OMP marks every shell it spawns with OMPCODE=1. A nested `omp` launched from
|
||||
// a parent session's shell inherits it, so that process is not the pane's root
|
||||
// agent and must not report its short-lived session over the parent's.
|
||||
const nestedOmpSession = process.env.OMPCODE === "1";
|
||||
|
||||
function enabled() {
|
||||
return HERDR_ENV === "1" && !!socketPath && !!paneId && !nestedOmpSession;
|
||||
}
|
||||
|
||||
let requestQueue = Promise.resolve();
|
||||
|
||||
function sendRequestAttempt(request: unknown, timeoutMs: number): Promise<boolean> {
|
||||
if (!enabled()) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
let done = false;
|
||||
let timeout: ReturnType<typeof setTimeout> | undefined;
|
||||
const finish = (delivered: boolean) => {
|
||||
if (done) return;
|
||||
done = true;
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
socket.destroy();
|
||||
resolve(delivered);
|
||||
};
|
||||
|
||||
const socket = net.createConnection(socketEndpoint!);
|
||||
socket.on("error", () => finish(false));
|
||||
socket.on("connect", () => socket.write(`${JSON.stringify(request)}\n`));
|
||||
socket.on("data", () => finish(true));
|
||||
socket.on("end", () => finish(false));
|
||||
timeout = setTimeout(() => finish(false), timeoutMs);
|
||||
timeout.unref?.();
|
||||
});
|
||||
}
|
||||
|
||||
async function sendRequestNow(request: unknown): Promise<void> {
|
||||
if (await sendRequestAttempt(request, 500)) {
|
||||
return;
|
||||
}
|
||||
await sendRequestAttempt(request, 1500);
|
||||
}
|
||||
|
||||
function sendRequest(request: unknown): Promise<void> {
|
||||
requestQueue = requestQueue.then(
|
||||
() => sendRequestNow(request),
|
||||
() => sendRequestNow(request),
|
||||
);
|
||||
return requestQueue;
|
||||
}
|
||||
|
||||
type AgentState = "working" | "blocked" | "idle";
|
||||
|
||||
type QueuedState = {
|
||||
state: AgentState;
|
||||
message?: string;
|
||||
seq: number;
|
||||
};
|
||||
|
||||
const idleDebounceMs = parseDurationEnv("HERDR_OMP_IDLE_DEBOUNCE_MS", 250);
|
||||
const retryGraceMs = parseDurationEnv("HERDR_OMP_RETRY_GRACE_MS", 2500);
|
||||
const retryableErrorPattern =
|
||||
/overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|ended without|http2 request did not get a response|timed? out|timeout|terminated|retry delay/i;
|
||||
let reportSeq = Date.now() * 1000;
|
||||
let currentAgentSessionId: string | undefined;
|
||||
let currentAgentSessionPath: string | undefined;
|
||||
|
||||
function nextReportSeq(): number {
|
||||
reportSeq += 1;
|
||||
return reportSeq;
|
||||
}
|
||||
|
||||
export function isAbsoluteSessionPath(file: unknown): file is string {
|
||||
return (
|
||||
typeof file === "string" &&
|
||||
(path.posix.isAbsolute(file) || path.win32.isAbsolute(file))
|
||||
);
|
||||
}
|
||||
|
||||
function updateSessionRef(ctx: any): void {
|
||||
try {
|
||||
const file = ctx?.sessionManager?.getSessionFile?.();
|
||||
currentAgentSessionPath = isAbsoluteSessionPath(file) ? file : undefined;
|
||||
} catch {
|
||||
currentAgentSessionPath = undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
const id = ctx?.sessionManager?.getSessionId?.();
|
||||
currentAgentSessionId = typeof id === "string" && id.length > 0 ? id : undefined;
|
||||
} catch {
|
||||
currentAgentSessionId = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function withSessionRef(params: Record<string, unknown>): Record<string, unknown> {
|
||||
if (currentAgentSessionPath) {
|
||||
return { ...params, agent_session_path: currentAgentSessionPath };
|
||||
}
|
||||
if (currentAgentSessionId) {
|
||||
return { ...params, agent_session_id: currentAgentSessionId };
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
function parseDurationEnv(name: string, fallback: number): number {
|
||||
const raw = process.env[name];
|
||||
if (!raw) {
|
||||
return fallback;
|
||||
}
|
||||
const parsed = Number.parseInt(raw, 10);
|
||||
if (!Number.isFinite(parsed) || parsed < 0) {
|
||||
return fallback;
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function currentSessionRef(): Record<string, unknown> | undefined {
|
||||
if (currentAgentSessionPath) {
|
||||
return { agent_session_path: currentAgentSessionPath };
|
||||
}
|
||||
if (currentAgentSessionId) {
|
||||
return { agent_session_id: currentAgentSessionId };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function reportSession(sessionStartSource = "startup"): Promise<void> {
|
||||
const sessionRef = currentSessionRef();
|
||||
if (!sessionRef) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return sendRequest({
|
||||
id: `${source}:session:${Date.now()}:${Math.random().toString(36).slice(2)}`,
|
||||
method: "pane.report_agent_session",
|
||||
params: {
|
||||
pane_id: paneId,
|
||||
source,
|
||||
agent: "omp",
|
||||
seq: nextReportSeq(),
|
||||
session_start_source: sessionStartSource,
|
||||
...sessionRef,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function sendState(state: AgentState, message?: string, seq = nextReportSeq()): Promise<void> {
|
||||
return sendRequest({
|
||||
id: `${source}:${Date.now()}:${Math.random().toString(36).slice(2)}`,
|
||||
method: "pane.report_agent",
|
||||
params: withSessionRef({
|
||||
pane_id: paneId,
|
||||
source,
|
||||
agent: "omp",
|
||||
state,
|
||||
message,
|
||||
seq,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
let sendInFlight = false;
|
||||
let queuedState: QueuedState | undefined;
|
||||
|
||||
function queueState(state: AgentState, message?: string): void {
|
||||
queuedState = { state, message, seq: nextReportSeq() };
|
||||
if (!sendInFlight) {
|
||||
void drainStateQueue();
|
||||
}
|
||||
}
|
||||
|
||||
async function drainStateQueue(): Promise<void> {
|
||||
if (sendInFlight) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendInFlight = true;
|
||||
try {
|
||||
while (queuedState) {
|
||||
const next = queuedState;
|
||||
queuedState = undefined;
|
||||
await sendState(next.state, next.message, next.seq);
|
||||
}
|
||||
} finally {
|
||||
sendInFlight = false;
|
||||
if (queuedState) {
|
||||
void drainStateQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function lastAssistantMessage(messages: unknown[]): any | undefined {
|
||||
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
||||
const message = messages[i] as any;
|
||||
if (message?.role === "assistant") {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function retryableErrorMessage(event: any): string | undefined {
|
||||
const messages = Array.isArray(event?.messages) ? event.messages : [];
|
||||
const assistant = lastAssistantMessage(messages);
|
||||
if (assistant?.stopReason !== "error") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const errorMessage = String(assistant.errorMessage ?? "");
|
||||
if (!retryableErrorPattern.test(errorMessage)) {
|
||||
return undefined;
|
||||
}
|
||||
return errorMessage || "retryable provider error";
|
||||
}
|
||||
|
||||
function askBlockedMessage(args: any): string {
|
||||
const questions = Array.isArray(args?.questions) ? args.questions : [];
|
||||
const firstQuestion = questions.find((question: any) => typeof question?.question === "string");
|
||||
if (firstQuestion?.question) {
|
||||
return firstQuestion.question;
|
||||
}
|
||||
return "waiting for user input";
|
||||
}
|
||||
|
||||
export default function (pi) {
|
||||
if (!enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let agentActive = false;
|
||||
let retryHoldActive = false;
|
||||
let failureBlocked = false;
|
||||
let failureMessage: string | undefined;
|
||||
let blockedCount = 0;
|
||||
let blockedMessage: string | undefined;
|
||||
let lastState: AgentState | undefined;
|
||||
let lastMessage: string | undefined;
|
||||
let idleTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
let retryTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
let rootSession = false;
|
||||
|
||||
function clearTimer(timer: ReturnType<typeof setTimeout> | undefined) {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
function clearPendingTimers() {
|
||||
clearTimer(idleTimer);
|
||||
clearTimer(retryTimer);
|
||||
idleTimer = undefined;
|
||||
retryTimer = undefined;
|
||||
}
|
||||
|
||||
function clearFailureState() {
|
||||
retryHoldActive = false;
|
||||
failureBlocked = false;
|
||||
failureMessage = undefined;
|
||||
}
|
||||
|
||||
function desiredState() {
|
||||
if (blockedCount > 0) {
|
||||
return { state: "blocked" as const, message: blockedMessage };
|
||||
}
|
||||
if (failureBlocked) {
|
||||
return { state: "blocked" as const, message: failureMessage };
|
||||
}
|
||||
if (agentActive || retryHoldActive) {
|
||||
return { state: "working" as const, message: undefined };
|
||||
}
|
||||
return { state: "idle" as const, message: undefined };
|
||||
}
|
||||
|
||||
function publishState(force = false) {
|
||||
const next = desiredState();
|
||||
if (!force && next.state === lastState && next.message === lastMessage) {
|
||||
return;
|
||||
}
|
||||
lastState = next.state;
|
||||
lastMessage = next.message;
|
||||
queueState(next.state, next.message);
|
||||
}
|
||||
|
||||
function scheduleIdle() {
|
||||
clearPendingTimers();
|
||||
clearFailureState();
|
||||
idleTimer = setTimeout(() => {
|
||||
idleTimer = undefined;
|
||||
publishState();
|
||||
}, idleDebounceMs);
|
||||
idleTimer.unref?.();
|
||||
}
|
||||
|
||||
function holdForRetry(message: string) {
|
||||
clearPendingTimers();
|
||||
retryHoldActive = true;
|
||||
failureBlocked = false;
|
||||
failureMessage = message;
|
||||
publishState();
|
||||
|
||||
retryTimer = setTimeout(() => {
|
||||
retryTimer = undefined;
|
||||
retryHoldActive = false;
|
||||
failureBlocked = true;
|
||||
publishState();
|
||||
}, retryGraceMs);
|
||||
retryTimer.unref?.();
|
||||
}
|
||||
|
||||
function activateRootSession(ctx: any, sessionStartSource = "startup"): boolean {
|
||||
if (ctx?.hasUI !== true) {
|
||||
return false;
|
||||
}
|
||||
rootSession = true;
|
||||
updateSessionRef(ctx);
|
||||
void reportSession(sessionStartSource);
|
||||
return true;
|
||||
}
|
||||
|
||||
function resetSessionState() {
|
||||
clearPendingTimers();
|
||||
clearFailureState();
|
||||
agentActive = false;
|
||||
blockedCount = 0;
|
||||
blockedMessage = undefined;
|
||||
}
|
||||
|
||||
function activateBlocked(message: string | undefined) {
|
||||
clearPendingTimers();
|
||||
blockedCount += 1;
|
||||
blockedMessage = message;
|
||||
publishState();
|
||||
}
|
||||
|
||||
function deactivateBlocked() {
|
||||
blockedCount = Math.max(0, blockedCount - 1);
|
||||
if (blockedCount === 0) {
|
||||
blockedMessage = undefined;
|
||||
}
|
||||
publishState();
|
||||
}
|
||||
|
||||
pi.events.on("herdr:blocked", (data) => {
|
||||
if (!rootSession) {
|
||||
return;
|
||||
}
|
||||
if (!data?.active) {
|
||||
deactivateBlocked();
|
||||
return;
|
||||
}
|
||||
|
||||
activateBlocked(data.label);
|
||||
});
|
||||
|
||||
pi.on("session_start", (_event, ctx) => {
|
||||
if (!activateRootSession(ctx)) {
|
||||
return;
|
||||
}
|
||||
// A reload can replace this extension mid-run without emitting another agent_start.
|
||||
agentActive = ctx?.isIdle?.() === false;
|
||||
publishState(true);
|
||||
});
|
||||
|
||||
pi.on("session_switch", (event, ctx) => {
|
||||
if (!activateRootSession(ctx, event?.reason || "resume")) {
|
||||
return;
|
||||
}
|
||||
resetSessionState();
|
||||
publishState(true);
|
||||
});
|
||||
|
||||
pi.on("agent_start", (_event, ctx) => {
|
||||
if (!rootSession && !activateRootSession(ctx)) {
|
||||
return;
|
||||
}
|
||||
updateSessionRef(ctx);
|
||||
void reportSession();
|
||||
clearPendingTimers();
|
||||
clearFailureState();
|
||||
agentActive = true;
|
||||
publishState();
|
||||
});
|
||||
|
||||
pi.on("tool_approval_requested", (event, ctx) => {
|
||||
if (!rootSession && !activateRootSession(ctx)) {
|
||||
return;
|
||||
}
|
||||
const label = event?.reason || `${event?.toolName || "Tool"} approval`;
|
||||
activateBlocked(label);
|
||||
});
|
||||
|
||||
pi.on("tool_approval_resolved", (_event, ctx) => {
|
||||
if (!rootSession && !activateRootSession(ctx)) {
|
||||
return;
|
||||
}
|
||||
deactivateBlocked();
|
||||
});
|
||||
|
||||
pi.on("tool_execution_start", (event, ctx) => {
|
||||
if (event?.toolName !== "ask") {
|
||||
return;
|
||||
}
|
||||
if (!rootSession && !activateRootSession(ctx)) {
|
||||
return;
|
||||
}
|
||||
activateBlocked(askBlockedMessage(event.args));
|
||||
});
|
||||
|
||||
pi.on("tool_execution_end", (event, ctx) => {
|
||||
if (event?.toolName !== "ask") {
|
||||
return;
|
||||
}
|
||||
if (!rootSession && !activateRootSession(ctx)) {
|
||||
return;
|
||||
}
|
||||
deactivateBlocked();
|
||||
});
|
||||
|
||||
pi.on("agent_end", (event) => {
|
||||
if (!rootSession) {
|
||||
return;
|
||||
}
|
||||
if (!agentActive) {
|
||||
// OMP can emit duplicate/late end events while auto-retry is already
|
||||
// holding the pane in Working. Do not let an unqualified duplicate end
|
||||
// cancel the retry hold and publish a false Idle.
|
||||
return;
|
||||
}
|
||||
if (event?.willContinue === true) {
|
||||
// A continuation is already scheduled, so this end is not a settle.
|
||||
// Older builds omit the field and fall through as before.
|
||||
return;
|
||||
}
|
||||
|
||||
agentActive = false;
|
||||
|
||||
const retryableMessage = retryableErrorMessage(event);
|
||||
if (retryableMessage) {
|
||||
holdForRetry(retryableMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
scheduleIdle();
|
||||
});
|
||||
|
||||
pi.on("session_shutdown", () => {
|
||||
if (rootSession) {
|
||||
clearPendingTimers();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
app.editor.external: Ctrl+E
|
||||
app.exit: Ctrl+Q
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json",
|
||||
"mcpServers": {
|
||||
"github": {
|
||||
"type": "http",
|
||||
"url": "https://api.githubcopilot.com/mcp/",
|
||||
"headers": {
|
||||
"Authorization": "!printf 'Bearer %s' \"$(gh auth token)\""
|
||||
}
|
||||
},
|
||||
"obscura": {
|
||||
"type": "stdio",
|
||||
"command": "obscura",
|
||||
"args": [
|
||||
"mcp"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
|
||||
"name": "cyberdream",
|
||||
"vars": {
|
||||
"bg": "#16181a",
|
||||
"bgAlt": "#1e2124",
|
||||
"bgHighlight": "#3c4048",
|
||||
"fg": "#ffffff",
|
||||
"grey": "#7b8496",
|
||||
"blue": "#5ea1ff",
|
||||
"green": "#5eff6c",
|
||||
"cyan": "#5ef1ff",
|
||||
"red": "#ff6e5e",
|
||||
"yellow": "#f1ff5e",
|
||||
"magenta": "#ff5ef1",
|
||||
"pink": "#ff5ea0",
|
||||
"orange": "#ffbd5e",
|
||||
"purple": "#bd5eff"
|
||||
},
|
||||
"colors": {
|
||||
"accent": "blue",
|
||||
"border": "blue",
|
||||
"borderAccent": "cyan",
|
||||
"borderMuted": "grey",
|
||||
"success": "green",
|
||||
"error": "red",
|
||||
"warning": "yellow",
|
||||
"muted": "grey",
|
||||
"dim": "grey",
|
||||
"text": "",
|
||||
"thinkingText": "grey",
|
||||
|
||||
"selectedBg": "bgHighlight",
|
||||
"userMessageBg": "bgAlt",
|
||||
"userMessageText": "fg",
|
||||
"customMessageBg": "bgAlt",
|
||||
"customMessageText": "fg",
|
||||
"customMessageLabel": "cyan",
|
||||
"toolPendingBg": "bgAlt",
|
||||
"toolSuccessBg": "bgAlt",
|
||||
"toolErrorBg": "bgAlt",
|
||||
"toolTitle": "cyan",
|
||||
"toolOutput": "fg",
|
||||
|
||||
"mdHeading": "orange",
|
||||
"mdLink": "cyan",
|
||||
"mdLinkUrl": "blue",
|
||||
"mdCode": "cyan",
|
||||
"mdCodeBlock": "fg",
|
||||
"mdCodeBlockBorder": "grey",
|
||||
"mdQuote": "grey",
|
||||
"mdQuoteBorder": "grey",
|
||||
"mdHr": "grey",
|
||||
"mdListBullet": "cyan",
|
||||
|
||||
"toolDiffAdded": "green",
|
||||
"toolDiffRemoved": "red",
|
||||
"toolDiffContext": "grey",
|
||||
|
||||
"syntaxComment": "grey",
|
||||
"syntaxKeyword": "magenta",
|
||||
"syntaxFunction": "blue",
|
||||
"syntaxVariable": "yellow",
|
||||
"syntaxString": "green",
|
||||
"syntaxNumber": "purple",
|
||||
"syntaxType": "cyan",
|
||||
"syntaxOperator": "pink",
|
||||
"syntaxPunctuation": "grey",
|
||||
|
||||
"thinkingOff": "grey",
|
||||
"thinkingMinimal": "blue",
|
||||
"thinkingLow": "cyan",
|
||||
"thinkingMedium": "green",
|
||||
"thinkingHigh": "yellow",
|
||||
"thinkingXhigh": "orange",
|
||||
"thinkingMax": "red",
|
||||
|
||||
"bashMode": "yellow",
|
||||
"pythonMode": "purple",
|
||||
|
||||
"statusLineBg": "bgAlt",
|
||||
"statusLineSep": "grey",
|
||||
"statusLineModel": "purple",
|
||||
"statusLinePath": "blue",
|
||||
"statusLineGitClean": "green",
|
||||
"statusLineGitDirty": "yellow",
|
||||
"statusLineContext": "cyan",
|
||||
"statusLineSpend": "pink",
|
||||
"statusLineStaged": "green",
|
||||
"statusLineDirty": "yellow",
|
||||
"statusLineUntracked": "red",
|
||||
"statusLineOutput": "fg",
|
||||
"statusLineCost": "orange",
|
||||
"statusLineSubagents": "magenta"
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user