@jwilger/pi-development-system 0.3.5 → 0.3.6
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/package.json
CHANGED
package/src/core/git-intent.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseShell } from "./shell-parse.ts";
|
|
2
2
|
|
|
3
3
|
export type GitIntent =
|
|
4
4
|
| "history-rewrite"
|
|
@@ -58,7 +58,7 @@ const isRedirect = (op: string): boolean => /^[<>]/.test(op) && !op.endsWith("("
|
|
|
58
58
|
/** Tokens of one line grouped into simple commands (operators separate segments). */
|
|
59
59
|
function segments(line: string): string[][] {
|
|
60
60
|
const result: string[][] = [[]];
|
|
61
|
-
for (const token of
|
|
61
|
+
for (const token of parseShell(line, (name) => `$${name}`)) {
|
|
62
62
|
if (typeof token === "string") result[result.length - 1]?.push(token);
|
|
63
63
|
else if ("pattern" in token) result[result.length - 1]?.push(token.pattern);
|
|
64
64
|
else if ("op" in token && isRedirect(token.op)) continue;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ParseEntry, parse } from "shell-quote";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* shell-quote throws on text that is valid shell or valid file content (a template literal inside a
|
|
5
|
+
* heredoc), and a guard must never crash the tool call. Retry with the `${` openers neutralised, then
|
|
6
|
+
* fall back to whitespace-split words so command classification still sees every token.
|
|
7
|
+
*/
|
|
8
|
+
export function parseShell(line: string, env?: (name: string) => string): ParseEntry[] {
|
|
9
|
+
const attempt = (text: string): ParseEntry[] | undefined => {
|
|
10
|
+
try {
|
|
11
|
+
return env === undefined ? parse(text) : parse(text, env);
|
|
12
|
+
} catch {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
return (
|
|
17
|
+
attempt(line) ??
|
|
18
|
+
attempt(line.replaceAll("${", "$")) ??
|
|
19
|
+
line.split(/\s+/).filter((word) => word !== "")
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { posix } from "node:path";
|
|
2
|
-
import {
|
|
2
|
+
import { parseShell } from "./shell-parse.ts";
|
|
3
3
|
|
|
4
4
|
export type WeakeningSignals = { addsSkip: boolean; emptied: boolean; commentedOut: boolean };
|
|
5
5
|
|
|
@@ -151,7 +151,7 @@ function splitLines(command: string): string {
|
|
|
151
151
|
|
|
152
152
|
/** Boundary decode of shell-quote's output into domain tokens; globs count as words. */
|
|
153
153
|
function tokenize(command: string): Token[] {
|
|
154
|
-
return
|
|
154
|
+
return parseShell(splitLines(command)).flatMap((entry): Token[] => {
|
|
155
155
|
if (typeof entry === "string") return [{ kind: "word", value: entry }];
|
|
156
156
|
if ("pattern" in entry) return [{ kind: "word", value: entry.pattern }];
|
|
157
157
|
if ("op" in entry) return [{ kind: "op", op: entry.op }];
|
package/src/gates/test-guard.ts
CHANGED
|
@@ -90,10 +90,13 @@ export function registerTestGuard(deps: TestGuardDeps): void {
|
|
|
90
90
|
|
|
91
91
|
const consumeOpenDeparture = (): void => {
|
|
92
92
|
const { openDepartures, activeSlice } = deps.state.get();
|
|
93
|
-
const
|
|
93
|
+
const applicable = openDepartures.filter(
|
|
94
94
|
(d) => d.gate === gate && (d.scope.kind !== "slice" || d.scope.slice === activeSlice),
|
|
95
95
|
);
|
|
96
|
-
|
|
96
|
+
// A broader departure covers the call without being spent; only a lone once-scoped one is used up.
|
|
97
|
+
if (applicable.some((d) => d.scope.kind !== "once")) return;
|
|
98
|
+
const used = applicable[0];
|
|
99
|
+
if (used === undefined) return;
|
|
97
100
|
deps.state.update((s) => ({
|
|
98
101
|
...s,
|
|
99
102
|
openDepartures: s.openDepartures.filter((d) => d.id !== used.id),
|