import type { AnyPredicateHandler, PredicateContext, PredicateHandler } from "../../schema.ts"; /** * Unwrap the boolean payload from a {@link PredicateShape} * argument. Accepts the bare form (`true` / `false`) and the * spread form (`{ value: true, onUnknown? }` / * `{ value: false, onUnknown? }`); the engine's `readLeafOnUnknown` * reads any `onUnknown:` sibling and `projectVerdict` applies the * policy to the handler's `"unknown"` returns. The handler itself * treats `onUnknown:` as an opaque sibling field and only consumes * `value:`. * * Returns `undefined` on malformed input — the caller decides what * to do with that (typically `return false`, mirroring the existing * pattern-unwrap fail-closed contract). * * Used by {@link isClean} and {@link hasStagedChanges}; both ship * with `PredicateShape` in the registry so the bare/spread * shape is identical at the type level too. * * @internal */ declare function unwrapBooleanLeafArg(args: unknown): boolean | undefined; /** * Test-internal export of {@link unwrapBooleanLeafArg}. Module-private * by intent; the `@internal` JSDoc tag (TypeScript ecosystem-standard) * flags "not part of the public surface" and the underscore prefix * mirrors the convention so external consumers can grep-discover it * too. Direct unit tests pin malformed-input branches that are hard * to drive via the engine end-to-end. * * @internal */ export declare const _unwrapBooleanLeafArg: typeof unwrapBooleanLeafArg; /** * Resolved outcome of reading a string tracker value from * `ctx.walkerState[key]`. Callers MUST distinguish the three cases: * * - `value` - the tracker resolved the value statically for this * command ref. Use it directly. * - `unknown` - the tracker observed a write it couldn't resolve * statically (e.g. `git checkout $VAR`). The walker * deliberately surfaces this to signal "a change * happened but I can't name the new value". Falling * through to `exec` would return the PRE-write * value and silently defeat the walker's static * tracking - exactly the case it exists for. * Callers must apply their `onUnknown` policy. * - `missing` - no tracker modifier fired for this dimension in * this ref's scope (the walker threaded the * tracker's initial sentinel, or `walkerState` has * no key for this tracker at all). `exec` fallback * is correct here: the shell's current state is the * value the predicate wants. * * The three-way split requires cooperation from the tracker: its * `initial` value must be distinct from its `unknown` sentinel, so * the predicate can tell "no modifier fired" apart from "modifier * fired and couldn't resolve". `branchTracker` does this via * {@link NO_CHECKOUT_IN_CHAIN}. A tracker that reuses `"unknown"` * for both initial and unknown would collapse these two cases - * preserved here as `missing` for backward compatibility (the * predicate then behaves as it did pre-U1, shelling out on any * unknown). */ export type WalkerStringResult = { kind: "value"; value: string; } | { kind: "unknown"; } | { kind: "missing"; }; /** * Resolve a string tracker value from `ctx.walkerState[key]` into a * three-state discriminated result. See {@link WalkerStringResult} * for why callers must not conflate `unknown` with `missing`. * * `initialSentinel` is the tracker's initial value (distinct from * its `unknown` sentinel). When `walkerState[key]` equals this * sentinel, the result is `missing` - no modifier fired for this * dimension in this ref's scope. */ export declare function walkerString(ctx: PredicateContext, key: string, initialSentinel: string): WalkerStringResult; /** * `when.branch` - match the current git branch. * * Accepted arg shapes: * * ```ts * when: { branch: /^main$/ } // single Pattern * when: { branch: "^feat-" } // single Pattern (string) * when: { branch: [/^main$/, /^master$/, /^trunk$/] } // Pattern[] (any-of) * when: { branch: { pattern: /^main$/, onUnknown: "allow" } } // object form * when: { branch: { pattern: [/^main$/, /^master$/], onUnknown: "allow" } } * ``` * * Array semantics: OR-of-matches (rule fires when the resolved * branch matches ANY of the listed patterns). Empty arrays are * invalid (rule skips); arrays containing non-Pattern values are * invalid (rule skips). * * Resolution order: * 1. `ctx.walkerState.branch` - set by the branch tracker when the * current bash chain contains `git checkout` / `git switch`. * Three outcomes: * - value resolved statically (e.g. `git checkout main`) -> * match the pattern against it. * - `"unknown"` sentinel (dynamic checkout like `git checkout * $VAR`) -> apply `onUnknown` policy. Do NOT fall through * to exec: a `git branch --show-current` call here would * return the PRE-checkout branch (the walker exists to * track exactly this kind of in-chain change statically). * - missing (no checkout in chain) -> fall through to exec. * 2. `git branch --show-current` in `ctx.cwd`. Empty stdout is * treated as "no branch" (detached HEAD) - the predicate falls * back to `onUnknown`. * * `onUnknown` default is `"block"` (fail-closed): if we can't * determine the branch, the predicate reports "match" so * branch-gated rules still fire. */ export declare const branch: PredicateHandler; /** * `when.upstream` - match the current branch's configured upstream. * * Accepted arg shapes: same as {@link branch}. * * Resolves via `git rev-parse --abbrev-ref @{upstream}`. A branch * without an upstream set returns a non-zero exit; the predicate then * applies `onUnknown`. * * No tracker today - upstream configuration isn't changed by in-chain * git commands at a rate that justifies modelling it (and `git push * -u origin main` changes it but only AFTER the push succeeds, which * is past the point where a pre-execution guard would act). The * per-tool_call exec cache ensures multiple upstream-gated rules share * one git call. * * Runtime-cwd guard: `getUpstream` shells out at `ctx.cwd`. When the * walker surfaces `ctx.walkerState.cwd === "unknown"` (dynamic * `cd "$VAR/pkg"` the walker couldn't resolve), the exec would run * against the pi session cwd — the wrong repo — and a user who opted * into `onUnknown: "allow"` would get a silent fail-OPEN. The handler * inlines a {@link cwdIsWalkerUnknown} check at the top and surfaces * trinary `"unknown"`; the engine's leaf-level (outer) or block-level * (inside `not:`) `onUnknown:` policy then projects to the right * boolean (default `"block"` = fail-CLOSED). * * @see walkerUnknownCwdReason — compose the agent-facing reason text * for the walker-unknown-cwd fail-closed branch in your rule's * ReasonFn. */ export declare const upstream: PredicateHandler; /** * Argument shape for {@link commitsAhead}. * * ```ts * when: { commitsAhead: { eq: 1 } } // exactly one ahead * when: { commitsAhead: { gt: 0 } } // at least one * when: { commitsAhead: { lt: 5 } } // fewer than five * when: { commitsAhead: { gt: 0, lt: 5 } } // 1..4 * when: { commitsAhead: { wrt: "origin/main", eq: 1 } } * ``` * * At least one of `eq` / `gt` / `lt` MUST be specified. All provided * comparisons must pass (AND). `wrt` is the git revision expression * to count commits behind (`git rev-list --count WRT..HEAD`); it * defaults to `@{upstream}`. */ export interface CommitsAheadArgs { /** Git revision to count commits ahead of. Defaults to `@{upstream}`. */ wrt?: string; /** Exact equality: `count === eq`. */ eq?: number; /** Strict greater-than: `count > gt`. */ gt?: number; /** Strict less-than: `count < lt`. */ lt?: number; } /** * `when.commitsAhead` - match when commits-ahead-of-WRT satisfy every * supplied comparator. * * Returns `false` (rule doesn't fire) when: * - the arg shape isn't an object with at least one of `eq` / `gt` * / `lt`, * - the `git rev-list` call fails, * - the comparator chain doesn't match. * * No `onUnknown` here: commits-ahead is a numeric comparator, not a * pattern match, and "I couldn't learn the answer" arguably shouldn't * fire a rule that's gated on a specific count. Authors who want the * fail-closed behavior can layer `{ upstream: "..." }` first in the * same `when` (AND semantics via the ADR's plugin predicates) - that * handles the "no upstream" case with explicit `onUnknown`. * * Runtime-cwd guard: `getCommitsAhead` shells out at `ctx.cwd`. When * the walker surfaces `ctx.walkerState.cwd === "unknown"`, the exec * would run against the pi session cwd — wrong repo — and the * `count === null` failure path returns `false`, silently skipping * the rule (fail-OPEN). The handler inlines a * {@link cwdIsWalkerUnknown} check at the top and surfaces trinary * `"unknown"`; the engine's `onUnknown:` policy then projects to the * right boolean (default `"block"` = fail-CLOSED, matching the policy * used by the other runtime-cwd predicates in this plugin). * * @see walkerUnknownCwdReason — compose the agent-facing reason text * for the walker-unknown-cwd fail-closed branch in your rule's * ReasonFn. * @see PiSteeringPredicates.commitsAhead — the registry entry that * declares the bare / spreadBase shape this handler dispatches * on. */ export declare const commitsAhead: PredicateHandler; /** * `when.hasStagedChanges` - match on the presence / absence of staged * changes in the repo at `ctx.cwd`. * * - `when: { hasStagedChanges: true }` - fires when there ARE staged * changes. * - `when: { hasStagedChanges: false }` - fires when there are NOT. * * Uses `git diff --cached --quiet`: exit 0 = no staged changes, exit * 1 = staged changes exist. On any other exit / spawn failure, we * conservatively report `false` - the caller can AND this with an * `upstream` check if fail-closed behavior is needed. * * Runtime-cwd guard: the underlying `git diff --cached` call runs * at `ctx.cwd`. When the walker surfaces `ctx.walkerState.cwd === * "unknown"` (dynamic `cd "$VAR/pkg"` the walker couldn't resolve), * `ctx.cwd` falls back to the pre-cd ambient cwd — the PI session * cwd, not the intended subpackage. The handler inlines a * {@link cwdIsWalkerUnknown} check at the top and surfaces trinary * `"unknown"`; the engine's `onUnknown:` policy then projects to the * right boolean (default `"block"` = fail-CLOSED). * * @see walkerUnknownCwdReason — compose the agent-facing reason text * for the walker-unknown-cwd fail-closed branch in your rule's * ReasonFn. * @see PiSteeringPredicates.hasStagedChanges — the registry entry * that declares the bare / spreadBase shape this handler * dispatches on. */ export declare const hasStagedChanges: PredicateHandler; /** * `when.isClean` - match on the working tree's cleanliness at * `ctx.cwd`. * * - `when: { isClean: true }` - fires when the working tree is * clean (no unstaged, no untracked, no staged changes). * - `when: { isClean: false }` - fires when the working tree is * dirty. * * Uses `git status --porcelain`: empty stdout = clean. Non-zero exit * returns `false` (unknown); pair with an `upstream` check for * fail-closed behavior. * * Runtime-cwd guard: same rationale as {@link hasStagedChanges} — the * handler inlines a {@link cwdIsWalkerUnknown} check at the top and * surfaces trinary `"unknown"` when the walker couldn't statically * resolve the command's effective cwd, rather than silently running * `git status` at the pi session cwd. * * @see walkerUnknownCwdReason — compose the agent-facing reason text * for the walker-unknown-cwd fail-closed branch in your rule's * ReasonFn. * @see PiSteeringPredicates.isClean — the registry entry that * declares the bare / spreadBase shape this handler dispatches * on. */ export declare const isClean: PredicateHandler; /** * `when.remote` - match the repo's `origin` remote URL. * * Accepted arg shapes: same as {@link branch}. Useful for rules that * should only fire in specific repos ("never force-push to * github.com/org/prod"). * * Resolves via `git config --get remote.origin.url`. Non-zero exit * (no origin configured) falls back to `onUnknown`. * * Runtime-cwd guard: the handler inlines a {@link cwdIsWalkerUnknown} * check at the top and surfaces trinary `"unknown"` when the walker * couldn't statically resolve the command's effective cwd — querying * the wrong repo's remote would silently mis-route a repo-gated rule. * Same rationale as {@link hasStagedChanges}. * * @see walkerUnknownCwdReason — compose the agent-facing reason text * for the walker-unknown-cwd fail-closed branch in your rule's * ReasonFn. */ export declare const remote: PredicateHandler; /** * Bundle of predicate handlers the git plugin registers under * `Plugin.predicates`. Keys become the `when.` slots rule authors * see. * * Typed as `Record` to match * {@link Plugin.predicates} at the registry boundary — each handler's * concrete argument shape is preserved in its individual declaration * above, and consumers can import `commitsAhead`, `isClean`, etc. * directly when they want the narrow type. */ export declare const predicates: Record; export {}; //# sourceMappingURL=predicates.d.ts.map