@andares/pi-toolkits 0.11.0 → 0.11.2
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 +8 -5
- package/README.zh-CN.md +8 -5
- package/package.json +4 -4
- package/src/features/ask/index.ts +2 -1
- package/src/features/ask/prompt.ts +2 -1
- package/src/features/ask/reasons.ts +1 -1
- package/src/features/autostart/autostart.ts +1 -1
- package/src/features/autostart/index.ts +1 -1
- package/src/features/compact/compact.test.ts +19 -0
- package/src/features/compact/index.ts +5 -3
- package/src/features/compact/prompt.ts +22 -11
- package/src/features/favorites/constants.ts +13 -8
- package/src/features/favorites/favorites.test.ts +42 -0
- package/src/features/favorites/patch-cycle.ts +5 -4
- package/src/features/favorites/patch-selector.ts +44 -8
- package/src/features/init/detect.test.ts +5 -2
- package/src/features/permission/audit.test.ts +10 -10
- package/src/features/permission/constants.test.ts +28 -2
- package/src/features/permission/constants.ts +28 -3
- package/src/features/permission/index.test.ts +76 -33
- package/src/features/permission/policy.test.ts +48 -11
- package/src/features/permission/policy.ts +16 -1
- package/src/features/permission/units.test.ts +25 -9
- package/src/features/permission/units.ts +49 -24
- package/src/features/stash/stash.ts +1 -1
- package/src/lib/gates.ts +2 -2
- package/src/lib/model-config.ts +2 -2
- package/src/lib/model-picker.ts +6 -5
|
@@ -57,6 +57,23 @@ export interface CommandUnit {
|
|
|
57
57
|
|
|
58
58
|
const SENTINEL = "\u0000";
|
|
59
59
|
|
|
60
|
+
/**
|
|
61
|
+
* Device sinks: writing to them is a no-op, not a boundary decision. They are
|
|
62
|
+
* excluded whenever they appear as a plain operand (`curl -o /dev/null`),
|
|
63
|
+
* not just as a redirect target (`cmd > /dev/null`). Commands that mutate the
|
|
64
|
+
* node itself (`rm /dev/null`, `mv /dev/null x`) still collect the target —
|
|
65
|
+
* the write-path branch below keeps them visible to the policy.
|
|
66
|
+
*/
|
|
67
|
+
const DEVICE_SINKS = new Set(["/dev/null", "/dev/stdout", "/dev/stderr"]);
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Commands whose ordinary arguments are never filesystem paths. `echo` /
|
|
71
|
+
* `printf` can only write through a redirect, which is collected separately,
|
|
72
|
+
* so skipping their operands cannot hide a write and avoids treating a
|
|
73
|
+
* message containing `/` plus an unresolved variable as an opaque path.
|
|
74
|
+
*/
|
|
75
|
+
const NO_PATH_OPERANDS = new Set(["echo", "printf"]);
|
|
76
|
+
|
|
60
77
|
/* -------------------------------------------------------------------------- */
|
|
61
78
|
/* Read/write classification */
|
|
62
79
|
/* -------------------------------------------------------------------------- */
|
|
@@ -768,9 +785,7 @@ function analyzeSegment(
|
|
|
768
785
|
for (const redirect of redirects) {
|
|
769
786
|
if (redirect.target === undefined) continue;
|
|
770
787
|
if (redirect.op === "<<<") continue; // herestring: data, not a path
|
|
771
|
-
if (
|
|
772
|
-
continue;
|
|
773
|
-
}
|
|
788
|
+
if (DEVICE_SINKS.has(redirect.target)) continue;
|
|
774
789
|
const descriptor =
|
|
775
790
|
(redirect.op === ">&" || redirect.op === "<&" || redirect.op === "&>") &&
|
|
776
791
|
/^\d+$/.test(redirect.target);
|
|
@@ -839,9 +854,12 @@ function analyzeSegment(
|
|
|
839
854
|
}
|
|
840
855
|
targets.push({ path: pathValue, effect: "write", source: "operand" });
|
|
841
856
|
}
|
|
842
|
-
} else if (command === "dd") {
|
|
857
|
+
} else if (command === "dd") {
|
|
858
|
+
for (const arg of args) {
|
|
843
859
|
if (arg.startsWith("of=")) {
|
|
844
|
-
const
|
|
860
|
+
const value = arg.slice(3);
|
|
861
|
+
if (DEVICE_SINKS.has(value)) continue;
|
|
862
|
+
const pathValue = resolveTargetPath(base, value);
|
|
845
863
|
if (pathValue === undefined) {
|
|
846
864
|
return {
|
|
847
865
|
units: [opaqueUnit(raw, headWord, command, base, "unresolved dd target")],
|
|
@@ -858,28 +876,35 @@ function analyzeSegment(
|
|
|
858
876
|
}
|
|
859
877
|
}
|
|
860
878
|
|
|
861
|
-
// Path-like arguments are targets for every command
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
879
|
+
// Path-like arguments are targets for every command, except for commands
|
|
880
|
+
// whose arguments are data (`echo`, `printf`) and device sinks.
|
|
881
|
+
if (!NO_PATH_OPERANDS.has(command)) {
|
|
882
|
+
for (const arg of args) {
|
|
883
|
+
if (DEVICE_SINKS.has(arg)) continue;
|
|
884
|
+
if (!looksLikePath(arg)) continue;
|
|
885
|
+
// `sed -i` operands were already classified above; its script is not a path.
|
|
886
|
+
if (command === "sed" && effect === "write") continue;
|
|
887
|
+
const pathValue = resolveTargetPath(commandBase, arg);
|
|
888
|
+
if (pathValue === undefined) {
|
|
889
|
+
return {
|
|
890
|
+
units: [opaqueUnit(raw, headWord, command, base, "unresolved path argument")],
|
|
891
|
+
nextBase: base,
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
targets.push({ path: pathValue, effect, source: "operand" });
|
|
872
895
|
}
|
|
873
|
-
targets.push({ path: pathValue, effect, source: "operand" });
|
|
874
896
|
}
|
|
875
897
|
|
|
876
|
-
// `-o FILE` / `--output FILE` writes whatever the command is.
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
898
|
+
// `-o FILE` / `--output FILE` writes whatever the command is. `echo` and
|
|
899
|
+
// `printf` do not have an output option, so their `-o` is data too.
|
|
900
|
+
if (!NO_PATH_OPERANDS.has(command)) {
|
|
901
|
+
for (let i = 0; i < args.length; i++) {
|
|
902
|
+
if (args[i] !== "-o" && args[i] !== "--output") continue;
|
|
903
|
+
const value = args[i + 1];
|
|
904
|
+
if (value === undefined || DEVICE_SINKS.has(value)) continue;
|
|
905
|
+
const pathValue = resolveTargetPath(commandBase, value);
|
|
906
|
+
if (pathValue) targets.push({ path: pathValue, effect: "write", source: "operand" });
|
|
907
|
+
}
|
|
883
908
|
}
|
|
884
909
|
|
|
885
910
|
units.push({
|
|
@@ -21,7 +21,7 @@ import type { KeyId } from "@earendil-works/pi-tui";
|
|
|
21
21
|
/**
|
|
22
22
|
* Stash hotkey.
|
|
23
23
|
*
|
|
24
|
-
* Compatibility rationale (verified against pi 0.99.0–1.
|
|
24
|
+
* Compatibility rationale (verified against pi 0.99.0–1.5.0 keybindings, live
|
|
25
25
|
* matchesKey probes and terminal/OS behavior):
|
|
26
26
|
* - pi: no default binding for ctrl+alt+<letter> (only ctrl+alt+] is taken);
|
|
27
27
|
* extension shortcuts dispatch first while the editor is focused
|
package/src/lib/gates.ts
CHANGED
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
* promise. The queue serializes our own presentations; the host slot is
|
|
19
19
|
* still shared with other extensions (pi does not arbitrate globally).
|
|
20
20
|
* pi 1.4.0 stacks extension dialogs so a second one no longer strands the
|
|
21
|
-
* first (#11166), but the 1.
|
|
21
|
+
* first (#11166), but the 1.2–1.3 hosts in the supported window still
|
|
22
22
|
* replace it — the queue stays, and it also keeps one confirmation visible
|
|
23
|
-
* at a time instead of a pile of stacked dialogs on
|
|
23
|
+
* at a time instead of a pile of stacked dialogs on later hosts.
|
|
24
24
|
* - A gate that throws blocks the call (fail closed), matching pi's own
|
|
25
25
|
* `tool_call` handler contract.
|
|
26
26
|
*
|
package/src/lib/model-config.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* (review, compact-model — favorites keeps its own store-local key logic).
|
|
5
5
|
*
|
|
6
6
|
* Types are derived from pi-coding-agent's own exports (ModelRegistry)
|
|
7
|
-
* rather than imported from @earendil-works/pi-ai: pi-ai's index.d.ts (1.
|
|
8
|
-
* through 1.
|
|
7
|
+
* rather than imported from @earendil-works/pi-ai: pi-ai's index.d.ts (1.2.0
|
|
8
|
+
* through 1.5.0 alike) re-exports `Model`/`Api` via extensioned
|
|
9
9
|
* `./types.ts` specifiers, which tsc 5.9 bundler resolution substitutes to
|
|
10
10
|
* `.d.ts` but some tsserver builds do not. Deriving from ModelRegistry keeps
|
|
11
11
|
* every resolver in agreement with the exact type
|
package/src/lib/model-picker.ts
CHANGED
|
@@ -9,11 +9,12 @@
|
|
|
9
9
|
*
|
|
10
10
|
* The component is driven through a small ModelRuntime facade backed by
|
|
11
11
|
* ctx.modelRegistry (pi's extension-facing wrapper). SAFETY: verified
|
|
12
|
-
* against pi 1.
|
|
13
|
-
* model-catalog-refresh.js —
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* (
|
|
12
|
+
* against pi 1.2.0–1.5.0 dist (modes/interactive/components/model-selector.js +
|
|
13
|
+
* model-catalog-refresh.js) — the component only calls getAvailableSnapshot,
|
|
14
|
+
* getModel and getError on modelRuntime (plus refresh, which ModelRegistry
|
|
15
|
+
* provides) across that whole span. 1.5.0 changed the selector's rendering
|
|
16
|
+
* and key handling (page keys, disambiguating model names, viewport-input
|
|
17
|
+
* capture) but not the runtime surface this facade implements.
|
|
17
18
|
*
|
|
18
19
|
* `current` is passed as the component's currentModel: the built-in selector
|
|
19
20
|
* preselects, ✓-marks and sorts-first its currentModel, so one Enter accepts
|