port: sync from Mike/pygienium@f01d4c22
This commit is contained in:
@@ -19,6 +19,7 @@ Pygienium is a `pi` extension: code-hygiene checks that scan a target with isola
|
|||||||
|
|
||||||
- Checks: `gate` (skip reason), `buildScanTask`, optional `buildFixTask`, optional `verify`. Scan tasks embed `!write`/`!echo` lines as deterministic fallback for the fake runner.
|
- Checks: `gate` (skip reason), `buildScanTask`, optional `buildFixTask`, optional `verify`. Scan tasks embed `!write`/`!echo` lines as deterministic fallback for the fake runner.
|
||||||
- Sub-agent sessions must be pinned to the invoking session's model: thread `ctx.model` through `PygieniumCtx` → run options → `AgentTaskOptions.model` → `createAgentSession({ model })`.
|
- Sub-agent sessions must be pinned to the invoking session's model: thread `ctx.model` through `PygieniumCtx` → run options → `AgentTaskOptions.model` → `createAgentSession({ model })`.
|
||||||
|
- A pinned model needs its provider too: a provider an extension registered (`pi.registerProvider`) exists only in the invoking `ModelRegistry`, and the sub-session runs `noExtensions`, so thread `ctx.modelRegistry` (narrowed by `asProviderRegistry`) through the same chain to `AgentTaskOptions.modelRegistry` → `providerForwardingExtension`, which re-registers that one provider through an inline factory. Drop that link and every check dies with `No API key found for <provider>`.
|
||||||
- Keep files modular; no compatibility shims.
|
- Keep files modular; no compatibility shims.
|
||||||
|
|
||||||
## Commit process (IMPORTANT)
|
## Commit process (IMPORTANT)
|
||||||
@@ -29,7 +30,7 @@ Commits are gated by the pre-commit hook (enable: `git config core.hooksPath .gi
|
|||||||
|
|
||||||
- `src/index.ts` — removes `mode: ctx.mode` from the `pygieniumCtx` literal
|
- `src/index.ts` — removes `mode: ctx.mode` from the `pygieniumCtx` literal
|
||||||
- `src/commands.ts` — removes `mode` from the `PygieniumCtx` Pick; swaps `ctx.mode` for `ctx.hasUI` in `print`
|
- `src/commands.ts` — removes `mode` from the `PygieniumCtx` Pick; swaps `ctx.mode` for `ctx.hasUI` in `print`
|
||||||
- `src/agent-runner.ts` — swaps the SDK import + loader for the omp SDK's `createAgentSession` options (`toolNames`, `systemPrompt`, `agentRegistry`, …)
|
- `src/agent-runner.ts` — swaps the SDK import + loader for the omp SDK's `createAgentSession` options (`toolNames`, `systemPrompt`, `agentRegistry`, …); reduces `ProviderRegistry` to an SDK-free shape and stubs `providerForwardingExtension` (the port disables extension discovery, so it has nothing to forward)
|
||||||
- `src/agents.ts` — `find` → `glob`
|
- `src/agents.ts` — `find` → `glob`
|
||||||
|
|
||||||
After touching any of those, run `bun port-to-omp.mjs --out <tmp>` and `(cd <tmp> && bun run typecheck)` before committing; the hook does the same.
|
After touching any of those, run `bun port-to-omp.mjs --out <tmp>` and `(cd <tmp> && bun run typecheck)` before committing; the hook does the same.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@mikefreno/omp-pygienium",
|
"name": "@mikefreno/omp-pygienium",
|
||||||
"version": "0.1.2",
|
"version": "0.1.3",
|
||||||
"description": "Code hygiene extension for omp (port of the pi extension) — isolated sub-agent checks that scan a target, apply fixes, and emit a findings+changes report.",
|
"description": "Code hygiene extension for omp (port of the pi extension) — isolated sub-agent checks that scan a target, apply fixes, and emit a findings+changes report.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"omp",
|
"omp",
|
||||||
|
|||||||
@@ -20,10 +20,53 @@ import { dirname, isAbsolute, join } from "node:path";
|
|||||||
import type {
|
import type {
|
||||||
AgentSession,
|
AgentSession,
|
||||||
AgentSessionEvent,
|
AgentSessionEvent,
|
||||||
|
ExtensionAPI,
|
||||||
ExtensionCommandContext,
|
ExtensionCommandContext,
|
||||||
} from "@oh-my-pi/pi-coding-agent";
|
} from "@oh-my-pi/pi-coding-agent";
|
||||||
import { loadAgents, extensionRoot, type AgentDef } from "./agents.js";
|
import { loadAgents, extensionRoot, type AgentDef } from "./agents.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider registrations as the omp port passes them around. The port does not
|
||||||
|
* forward any: omp sessions disable extension discovery, and its ModelRegistry
|
||||||
|
* keeps no extension provider registrations to forward.
|
||||||
|
*/
|
||||||
|
export interface ProviderRegistry {
|
||||||
|
getRegisteredNativeProvider(providerId: string): object | undefined;
|
||||||
|
getRegisteredProviderConfig(providerId: string): object | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The inline extension that re-registers a provider in a sub-session. Spelled out
|
||||||
|
* structurally — an `InlineExtension` in pi — so the generated omp port, which
|
||||||
|
* names the type differently, still compiles.
|
||||||
|
*/
|
||||||
|
export interface InlineProviderExtension {
|
||||||
|
name: string;
|
||||||
|
hidden: boolean;
|
||||||
|
factory: (pi: ExtensionAPI) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Narrow a host context's `modelRegistry` to a {@link ProviderRegistry},
|
||||||
|
* returning `undefined` when it cannot answer the two questions this module
|
||||||
|
* asks. This is the decode boundary: everything downstream works with the
|
||||||
|
* narrow interface, and a host whose registry does not know extension
|
||||||
|
* registrations simply gets no provider forwarding.
|
||||||
|
*/
|
||||||
|
export function asProviderRegistry(
|
||||||
|
candidate: unknown,
|
||||||
|
): ProviderRegistry | undefined {
|
||||||
|
if (!candidate || typeof candidate !== "object") return undefined;
|
||||||
|
const registry = candidate as Partial<ProviderRegistry>;
|
||||||
|
if (
|
||||||
|
typeof registry.getRegisteredNativeProvider !== "function" ||
|
||||||
|
typeof registry.getRegisteredProviderConfig !== "function"
|
||||||
|
) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return registry as ProviderRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Env var overriding the per-agent settle timeout (ms). Guards against a
|
* Env var overriding the per-agent settle timeout (ms). Guards against a
|
||||||
* sub-agent session that never settles (stalled provider stream, hung retry /
|
* sub-agent session that never settles (stalled provider stream, hung retry /
|
||||||
@@ -58,6 +101,12 @@ export interface AgentTaskOptions {
|
|||||||
* `createAgentSession` falls back to the settings default model.
|
* `createAgentSession` falls back to the settings default model.
|
||||||
*/
|
*/
|
||||||
model?: ExtensionCommandContext["model"];
|
model?: ExtensionCommandContext["model"];
|
||||||
|
/**
|
||||||
|
* The invoking session's provider registry, used to make the pinned
|
||||||
|
* `model`'s provider resolvable inside the sub-session (see
|
||||||
|
* {@link providerForwardingExtension}).
|
||||||
|
*/
|
||||||
|
modelRegistry?: ProviderRegistry;
|
||||||
/** Optional explicit agent definition (skips `loadAgents`). */
|
/** Optional explicit agent definition (skips `loadAgents`). */
|
||||||
agent?: AgentDef;
|
agent?: AgentDef;
|
||||||
/**
|
/**
|
||||||
@@ -166,6 +215,14 @@ export function resetAgentRunner(): void {
|
|||||||
currentRunner = defaultAgentRunner;
|
currentRunner = defaultAgentRunner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The omp port forwards no provider registrations (see `ProviderRegistry`), so
|
||||||
|
* sub-agent sessions get no inline extension.
|
||||||
|
*/
|
||||||
|
export function providerForwardingExtension(): undefined {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Real sub-agent runner: spins up an in-memory `AgentSession` scoped to `cwd`,
|
* Real sub-agent runner: spins up an in-memory `AgentSession` scoped to `cwd`,
|
||||||
* overrides the system prompt with the agent definition's body, restricts tools
|
* overrides the system prompt with the agent definition's body, restricts tools
|
||||||
|
|||||||
@@ -44,12 +44,20 @@ import {
|
|||||||
type ExportFilters,
|
type ExportFilters,
|
||||||
} from "./export.js";
|
} from "./export.js";
|
||||||
import type { SendChatMessage } from "./phases.js";
|
import type { SendChatMessage } from "./phases.js";
|
||||||
|
import type { ProviderRegistry } from "./agent-runner.js";
|
||||||
|
|
||||||
/** Narrow context slice handlers need (a subset of `ExtensionCommandContext`). */
|
/** Narrow context slice handlers need (a subset of `ExtensionCommandContext`). */
|
||||||
export type PygieniumCtx = Pick<
|
export type PygieniumCtx = Pick<
|
||||||
ExtensionCommandContext,
|
ExtensionCommandContext,
|
||||||
"cwd" | "hasUI" | "ui" | "model"
|
"cwd" | "hasUI" | "ui" | "model"
|
||||||
> & {
|
> & {
|
||||||
|
/**
|
||||||
|
* The invoking session's extension provider registrations, already narrowed
|
||||||
|
* at the extension entry point (see `asProviderRegistry`). Carried into every
|
||||||
|
* sub-agent so a pinned model whose provider only an extension registered
|
||||||
|
* stays resolvable there.
|
||||||
|
*/
|
||||||
|
modelRegistry?: ProviderRegistry;
|
||||||
/** Optional callback to post messages to the chat window. */
|
/** Optional callback to post messages to the chat window. */
|
||||||
sendChatMessage?: SendChatMessage;
|
sendChatMessage?: SendChatMessage;
|
||||||
/** Optional callback forwarding raw sub-agent events to the chat stream. */
|
/** Optional callback forwarding raw sub-agent events to the chat stream. */
|
||||||
@@ -177,6 +185,7 @@ export async function handleCheckCommand(
|
|||||||
ui: ctx.ui,
|
ui: ctx.ui,
|
||||||
hasUI: ctx.hasUI,
|
hasUI: ctx.hasUI,
|
||||||
model: ctx.model,
|
model: ctx.model,
|
||||||
|
modelRegistry: ctx.modelRegistry,
|
||||||
sendChatMessage: ctx.sendChatMessage,
|
sendChatMessage: ctx.sendChatMessage,
|
||||||
onAgentEvent: ctx.onAgentEvent,
|
onAgentEvent: ctx.onAgentEvent,
|
||||||
sendPhaseLine: ctx.sendPhaseLine,
|
sendPhaseLine: ctx.sendPhaseLine,
|
||||||
@@ -217,6 +226,7 @@ export async function handleAllCommand(
|
|||||||
ui: ctx.ui,
|
ui: ctx.ui,
|
||||||
hasUI: ctx.hasUI,
|
hasUI: ctx.hasUI,
|
||||||
model: ctx.model,
|
model: ctx.model,
|
||||||
|
modelRegistry: ctx.modelRegistry,
|
||||||
sendChatMessage: ctx.sendChatMessage,
|
sendChatMessage: ctx.sendChatMessage,
|
||||||
onAgentEvent: ctx.onAgentEvent,
|
onAgentEvent: ctx.onAgentEvent,
|
||||||
sendPhaseLine: ctx.sendPhaseLine,
|
sendPhaseLine: ctx.sendPhaseLine,
|
||||||
@@ -306,6 +316,7 @@ export async function handleResumeCommand(
|
|||||||
ui: ctx.ui,
|
ui: ctx.ui,
|
||||||
hasUI: ctx.hasUI,
|
hasUI: ctx.hasUI,
|
||||||
model: ctx.model,
|
model: ctx.model,
|
||||||
|
modelRegistry: ctx.modelRegistry,
|
||||||
existingState: state,
|
existingState: state,
|
||||||
sendChatMessage: ctx.sendChatMessage,
|
sendChatMessage: ctx.sendChatMessage,
|
||||||
onAgentEvent: ctx.onAgentEvent,
|
onAgentEvent: ctx.onAgentEvent,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import type {
|
|||||||
import { Box, Text } from "@oh-my-pi/pi-tui";
|
import { Box, Text } from "@oh-my-pi/pi-tui";
|
||||||
import "./checks/all.js";
|
import "./checks/all.js";
|
||||||
import { registerPygieniumCommands, type PygieniumCtx } from "./commands.js";
|
import { registerPygieniumCommands, type PygieniumCtx } from "./commands.js";
|
||||||
|
import { asProviderRegistry } from "./agent-runner.js";
|
||||||
import {
|
import {
|
||||||
type SendChatMessage,
|
type SendChatMessage,
|
||||||
type CheckCompletionDetails,
|
type CheckCompletionDetails,
|
||||||
@@ -404,6 +405,7 @@ export default async function pygieniumExtension(
|
|||||||
hasUI: ctx.hasUI,
|
hasUI: ctx.hasUI,
|
||||||
ui: ctx.ui,
|
ui: ctx.ui,
|
||||||
model: ctx.model,
|
model: ctx.model,
|
||||||
|
modelRegistry: asProviderRegistry(ctx.modelRegistry),
|
||||||
sendChatMessage,
|
sendChatMessage,
|
||||||
onAgentEvent,
|
onAgentEvent,
|
||||||
sendPhaseLine,
|
sendPhaseLine,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import type {
|
|||||||
} from "@oh-my-pi/pi-coding-agent";
|
} from "@oh-my-pi/pi-coding-agent";
|
||||||
import { getAllChecks, type CheckDefinition } from "../checks/registry.js";
|
import { getAllChecks, type CheckDefinition } from "../checks/registry.js";
|
||||||
import { runCheck } from "./check-runner.js";
|
import { runCheck } from "./check-runner.js";
|
||||||
|
import type { ProviderRegistry } from "../agent-runner.js";
|
||||||
import { createPhaseStrip, type SendChatMessage } from "../phases.js";
|
import { createPhaseStrip, type SendChatMessage } from "../phases.js";
|
||||||
import { createPipelineFooter, type ItemStatus } from "../footer.js";
|
import { createPipelineFooter, type ItemStatus } from "../footer.js";
|
||||||
import { runRecon } from "../recon.js";
|
import { runRecon } from "../recon.js";
|
||||||
@@ -83,6 +84,12 @@ export interface AllRunOptions {
|
|||||||
* the settings default.
|
* the settings default.
|
||||||
*/
|
*/
|
||||||
model?: ExtensionCommandContext["model"];
|
model?: ExtensionCommandContext["model"];
|
||||||
|
/**
|
||||||
|
* The invoking session's provider registry; carried into every check's
|
||||||
|
* sub-agents so a provider only an extension registered stays resolvable
|
||||||
|
* (see `providerForwardingExtension` in `agent-runner.ts`).
|
||||||
|
*/
|
||||||
|
modelRegistry?: ProviderRegistry;
|
||||||
/** Optional callback to post completion messages into the chat. */
|
/** Optional callback to post completion messages into the chat. */
|
||||||
sendChatMessage?: SendChatMessage;
|
sendChatMessage?: SendChatMessage;
|
||||||
/** Optional callback forwarding raw sub-agent events to the chat stream. */
|
/** Optional callback forwarding raw sub-agent events to the chat stream. */
|
||||||
@@ -379,6 +386,7 @@ export async function runAllChecks(
|
|||||||
ui: opts.ui,
|
ui: opts.ui,
|
||||||
hasUI,
|
hasUI,
|
||||||
model: opts.model,
|
model: opts.model,
|
||||||
|
modelRegistry: opts.modelRegistry,
|
||||||
existingState: state,
|
existingState: state,
|
||||||
sendChatMessage: opts.sendChatMessage,
|
sendChatMessage: opts.sendChatMessage,
|
||||||
onAgentEvent: opts.onAgentEvent,
|
onAgentEvent: opts.onAgentEvent,
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import type {
|
|||||||
ExtensionUIContext,
|
ExtensionUIContext,
|
||||||
} from "@oh-my-pi/pi-coding-agent";
|
} from "@oh-my-pi/pi-coding-agent";
|
||||||
import type { CheckDefinition, CheckScope } from "../checks/registry.js";
|
import type { CheckDefinition, CheckScope } from "../checks/registry.js";
|
||||||
import { runAgentTask } from "../agent-runner.js";
|
import { runAgentTask, type ProviderRegistry } from "../agent-runner.js";
|
||||||
import { runRecon } from "../recon.js";
|
import { runRecon } from "../recon.js";
|
||||||
import {
|
import {
|
||||||
createPhaseStrip,
|
createPhaseStrip,
|
||||||
@@ -93,6 +93,12 @@ export interface RunCheckOptions {
|
|||||||
* settings default.
|
* settings default.
|
||||||
*/
|
*/
|
||||||
model?: ExtensionCommandContext["model"];
|
model?: ExtensionCommandContext["model"];
|
||||||
|
/**
|
||||||
|
* The invoking session's provider registry; carried into every sub-agent so a
|
||||||
|
* provider only an extension registered stays resolvable (see
|
||||||
|
* `providerForwardingExtension` in `agent-runner.ts`).
|
||||||
|
*/
|
||||||
|
modelRegistry?: ProviderRegistry;
|
||||||
/** Pre-existing run state to update (for `/pygienium-all` and resume). */
|
/** Pre-existing run state to update (for `/pygienium-all` and resume). */
|
||||||
existingState?: RunState;
|
existingState?: RunState;
|
||||||
/** Optional callback to post completion messages into the chat. */
|
/** Optional callback to post completion messages into the chat. */
|
||||||
@@ -288,6 +294,7 @@ async function runCheckImplInner(
|
|||||||
agentName: check.agentName,
|
agentName: check.agentName,
|
||||||
task: scanTask,
|
task: scanTask,
|
||||||
model: opts.model,
|
model: opts.model,
|
||||||
|
modelRegistry: opts.modelRegistry,
|
||||||
onEvent: forward(PHASE_ANALYSIS),
|
onEvent: forward(PHASE_ANALYSIS),
|
||||||
});
|
});
|
||||||
findings = scanResult.text;
|
findings = scanResult.text;
|
||||||
@@ -321,6 +328,7 @@ async function runCheckImplInner(
|
|||||||
agentName: check.fixAgentName ?? "fixer",
|
agentName: check.fixAgentName ?? "fixer",
|
||||||
task: fixTask,
|
task: fixTask,
|
||||||
model: opts.model,
|
model: opts.model,
|
||||||
|
modelRegistry: opts.modelRegistry,
|
||||||
onEvent: forward(PHASE_FIX),
|
onEvent: forward(PHASE_FIX),
|
||||||
});
|
});
|
||||||
changes = fixResult.text;
|
changes = fixResult.text;
|
||||||
|
|||||||
Reference in New Issue
Block a user