@hank-warren/pi-auto-permissions 0.5.0 → 0.6.0
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/README.md +7 -1
- package/index.ts +66 -4
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -143,7 +143,7 @@ The guardian must return one of three decisions:
|
|
|
143
143
|
|
|
144
144
|
- `approve`: execute the command
|
|
145
145
|
- `revise`: block it and tell the main agent what to correct
|
|
146
|
-
- `ask_user`: open
|
|
146
|
+
- `ask_user`: open an approval prompt (rendered with [`@hank-warren/pi-permission-selector`](../pi-permission-selector)'s `OptionSelector`): numbered options with `1`–`9` hotkeys, Tab to attach a note that is delivered to the agent as a steering user message, Esc to cancel — which blocks the command
|
|
147
147
|
|
|
148
148
|
## Conversation context and caching
|
|
149
149
|
|
|
@@ -264,6 +264,12 @@ $ git commit --dry-run -m "fix auth"
|
|
|
264
264
|
|
|
265
265
|
Set `ui.enabled` to `false` to hide review state without disabling enforcement.
|
|
266
266
|
|
|
267
|
+
### Herdr pane indicator
|
|
268
|
+
|
|
269
|
+
When the environment variable `HERDR_ENV` is set to exactly `1`, the extension additionally emits a `herdr:blocked` event whenever a command is waiting on your approval, and a matching cleared event once the review resolves. [Herdr](https://herdr.dev), a terminal multiplexer for coding agents, sets this variable for the panes it manages and uses the event to flag the pane that needs attention — useful when a review is blocking in a pane you are not currently looking at. The blocked event carries the gate label so the indicator can name the operation.
|
|
270
|
+
|
|
271
|
+
This is an optional integration and nothing needs to be configured to use it. Outside Herdr the variable is unset, the emit is skipped entirely, and every other feature behaves identically; the extension has no dependency on Herdr being installed.
|
|
272
|
+
|
|
267
273
|
## Trusted groups
|
|
268
274
|
|
|
269
275
|
In a trusted project, create `.pi/trusted-ops` to bypass selected rule groups:
|
package/index.ts
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
type ReviewEvidenceRecord,
|
|
38
38
|
} from "./review.js";
|
|
39
39
|
import { mergeOverrideEvidence, type PermissionOverride } from "./override-evidence.js";
|
|
40
|
+
import { OptionSelector } from "@hank-warren/pi-permission-selector/selector.ts";
|
|
40
41
|
|
|
41
42
|
const WIDGET_KEY = "auto-permissions";
|
|
42
43
|
const PROJECT_CONFIG_DIR_NAME = (PiCodingAgent as { CONFIG_DIR_NAME?: string }).CONFIG_DIR_NAME ?? ".pi";
|
|
@@ -653,6 +654,61 @@ export default function autoPermissionsExtension(pi: ExtensionAPI) {
|
|
|
653
654
|
}
|
|
654
655
|
}
|
|
655
656
|
|
|
657
|
+
/**
|
|
658
|
+
* Render an approval prompt with the shared `OptionSelector` through
|
|
659
|
+
* `ctx.ui.custom()` (non-overlay, so it swaps into the editor area exactly
|
|
660
|
+
* where `ctx.ui.select` used to render).
|
|
661
|
+
*
|
|
662
|
+
* Cancellation parity with `ctx.ui.select` — all of these resolve
|
|
663
|
+
* `undefined`, which every caller must treat as deny/cancel, never allow:
|
|
664
|
+
* - Esc (`OptionSelector.onCancel`)
|
|
665
|
+
* - `signal` abort (listener calls `done(undefined)`; pi's `done` is
|
|
666
|
+
* idempotent and performs its own teardown, so nothing else is needed)
|
|
667
|
+
* - a host that reports `hasUI` but cannot render custom components
|
|
668
|
+
*
|
|
669
|
+
* A Tab-typed note is delivered to the agent as a steering user message,
|
|
670
|
+
* matching what the retired `ExtensionSelectorComponent` monkey patch in
|
|
671
|
+
* pi-permission-selector did.
|
|
672
|
+
*/
|
|
673
|
+
function promptSelect(
|
|
674
|
+
ctx: ExtensionContext,
|
|
675
|
+
title: string,
|
|
676
|
+
values: string[],
|
|
677
|
+
signal: AbortSignal,
|
|
678
|
+
opts: { allowComment: boolean },
|
|
679
|
+
): Promise<string | undefined> {
|
|
680
|
+
return ctx.ui.custom<string | undefined>((tui, theme, _keybindings, done) => {
|
|
681
|
+
const onAbort = () => finish(undefined);
|
|
682
|
+
const finish = (result: string | undefined) => {
|
|
683
|
+
signal.removeEventListener("abort", onAbort);
|
|
684
|
+
done(result);
|
|
685
|
+
};
|
|
686
|
+
const selector = new OptionSelector({
|
|
687
|
+
title,
|
|
688
|
+
options: values.map((value) => ({ value, label: value })),
|
|
689
|
+
allowComment: opts.allowComment,
|
|
690
|
+
theme,
|
|
691
|
+
onSelect: (option, comment) => {
|
|
692
|
+
if (comment) {
|
|
693
|
+
try {
|
|
694
|
+
pi.sendUserMessage(comment, { deliverAs: "steer" });
|
|
695
|
+
} catch {
|
|
696
|
+
// Never let note delivery break the approval flow.
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
finish(option.value);
|
|
700
|
+
},
|
|
701
|
+
onCancel: () => finish(undefined),
|
|
702
|
+
requestRender: () => tui.requestRender(),
|
|
703
|
+
});
|
|
704
|
+
// `done` before the factory resolves is safe: pi marks the dialog closed
|
|
705
|
+
// and never mounts the component.
|
|
706
|
+
if (signal.aborted) onAbort();
|
|
707
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
708
|
+
return selector;
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
|
|
656
712
|
async function askUser(
|
|
657
713
|
gate: Gate,
|
|
658
714
|
command: string,
|
|
@@ -695,10 +751,12 @@ export default function autoPermissionsExtension(pi: ExtensionAPI) {
|
|
|
695
751
|
try {
|
|
696
752
|
let choice: string | undefined;
|
|
697
753
|
try {
|
|
698
|
-
choice = await
|
|
754
|
+
choice = await promptSelect(
|
|
755
|
+
ctx,
|
|
699
756
|
`${gate.label} — Auto Permissions needs approval\n\n${detail}\n\n${command}`,
|
|
700
757
|
permissionPromptOptions(config.evaluationLog.enabled),
|
|
701
|
-
|
|
758
|
+
promptSignal,
|
|
759
|
+
{ allowComment: true },
|
|
702
760
|
);
|
|
703
761
|
} catch (error) {
|
|
704
762
|
if (!lifecycleStale() && !reviewCancelled(signal)) throw error;
|
|
@@ -879,10 +937,14 @@ export default function autoPermissionsExtension(pi: ExtensionAPI) {
|
|
|
879
937
|
try {
|
|
880
938
|
let choice: string | undefined;
|
|
881
939
|
try {
|
|
882
|
-
|
|
940
|
+
// The override prompt never had Tab-to-comment (the monkey patch only
|
|
941
|
+
// enabled it on titles matching /needs approval/i), so keep parity.
|
|
942
|
+
choice = await promptSelect(
|
|
943
|
+
ctx,
|
|
883
944
|
`Convention override: ${gate.label}\n\n${params.reason}\n\n${params.command}`,
|
|
884
945
|
["Allow for this session", "Keep blocked"],
|
|
885
|
-
|
|
946
|
+
promptSignal,
|
|
947
|
+
{ allowComment: false },
|
|
886
948
|
);
|
|
887
949
|
} catch (error) {
|
|
888
950
|
if (!lifecycleStale() && !reviewCancelled(signal)) throw error;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hank-warren/pi-auto-permissions",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Fork of @ogulcancelik/pi-auto-permissions with dialog-answer user evidence: context-aware Bash permissions for Pi with automated guardian review.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -51,6 +51,9 @@
|
|
|
51
51
|
"README.md",
|
|
52
52
|
"LICENSE"
|
|
53
53
|
],
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@hank-warren/pi-permission-selector": "^1.0.0"
|
|
56
|
+
},
|
|
54
57
|
"peerDependencies": {
|
|
55
58
|
"@earendil-works/pi-ai": "*",
|
|
56
59
|
"@earendil-works/pi-coding-agent": "*",
|