@basou/core 0.36.0 → 0.37.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/dist/index.js +452 -37
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
// src/adapters/command-lookup.ts
|
|
2
2
|
import { spawn } from "child_process";
|
|
3
3
|
async function isOnPath(command) {
|
|
4
|
-
return new Promise((
|
|
4
|
+
return new Promise((resolve4) => {
|
|
5
5
|
const child = spawn("which", [command], { stdio: "ignore" });
|
|
6
|
-
child.on("error", () =>
|
|
7
|
-
child.on("exit", (code) =>
|
|
6
|
+
child.on("error", () => resolve4(false));
|
|
7
|
+
child.on("exit", (code) => resolve4(code === 0));
|
|
8
8
|
});
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -5551,13 +5551,13 @@ async function realpathBestEffort(absPath) {
|
|
|
5551
5551
|
}
|
|
5552
5552
|
return normalize(absPath);
|
|
5553
5553
|
}
|
|
5554
|
-
function expandTilde(p,
|
|
5555
|
-
if (p === "~") return
|
|
5556
|
-
if (p.startsWith("~/")) return join14(
|
|
5554
|
+
function expandTilde(p, homedir5) {
|
|
5555
|
+
if (p === "~") return homedir5;
|
|
5556
|
+
if (p.startsWith("~/")) return join14(homedir5, p.slice(2));
|
|
5557
5557
|
return p;
|
|
5558
5558
|
}
|
|
5559
|
-
function toAbsolute(p, workingDirAbs,
|
|
5560
|
-
const expanded = expandTilde(p,
|
|
5559
|
+
function toAbsolute(p, workingDirAbs, homedir5) {
|
|
5560
|
+
const expanded = expandTilde(p, homedir5);
|
|
5561
5561
|
if (isAbsolute(expanded)) return normalize(expanded);
|
|
5562
5562
|
return normalize(resolve2(workingDirAbs, expanded));
|
|
5563
5563
|
}
|
|
@@ -5570,18 +5570,18 @@ async function classifyFilesBySourceRoot(input) {
|
|
|
5570
5570
|
const inRoot = [];
|
|
5571
5571
|
const outOfRoot = [];
|
|
5572
5572
|
if (input.files.length === 0) return { inRoot, outOfRoot };
|
|
5573
|
-
const
|
|
5574
|
-
const workingDirAbs = toAbsolute(input.workingDirectory,
|
|
5573
|
+
const homedir5 = input.homedir ?? osHomedir();
|
|
5574
|
+
const workingDirAbs = toAbsolute(input.workingDirectory, homedir5, homedir5);
|
|
5575
5575
|
const declared = input.sourceRoots && input.sourceRoots.length > 0 ? [...input.sourceRoots] : ["."];
|
|
5576
5576
|
const rootsAbs = [];
|
|
5577
5577
|
for (const r of declared) {
|
|
5578
|
-
const expanded = expandTilde(r,
|
|
5578
|
+
const expanded = expandTilde(r, homedir5);
|
|
5579
5579
|
const abs = isAbsolute(expanded) ? normalize(expanded) : normalize(resolve2(input.masterRoot, expanded));
|
|
5580
5580
|
rootsAbs.push(await realpathBestEffort(abs));
|
|
5581
5581
|
}
|
|
5582
5582
|
for (const e of input.extraInRoot ?? []) {
|
|
5583
|
-
const expanded = expandTilde(e,
|
|
5584
|
-
const abs = isAbsolute(expanded) ? normalize(expanded) : normalize(resolve2(
|
|
5583
|
+
const expanded = expandTilde(e, homedir5);
|
|
5584
|
+
const abs = isAbsolute(expanded) ? normalize(expanded) : normalize(resolve2(homedir5, expanded));
|
|
5585
5585
|
rootsAbs.push(await realpathBestEffort(abs));
|
|
5586
5586
|
}
|
|
5587
5587
|
if (rootsAbs.length === 0) {
|
|
@@ -5589,7 +5589,7 @@ async function classifyFilesBySourceRoot(input) {
|
|
|
5589
5589
|
}
|
|
5590
5590
|
for (const file of input.files) {
|
|
5591
5591
|
try {
|
|
5592
|
-
const abs = toAbsolute(file, workingDirAbs,
|
|
5592
|
+
const abs = toAbsolute(file, workingDirAbs, homedir5);
|
|
5593
5593
|
const real = await realpathBestEffort(abs);
|
|
5594
5594
|
const within = rootsAbs.some((root) => isUnder(real, root));
|
|
5595
5595
|
(within ? inRoot : outOfRoot).push(file);
|
|
@@ -7496,8 +7496,417 @@ function formatInt(n) {
|
|
|
7496
7496
|
|
|
7497
7497
|
// src/review/review-gaps.ts
|
|
7498
7498
|
import { existsSync, realpathSync } from "fs";
|
|
7499
|
+
import { homedir as homedir3 } from "os";
|
|
7500
|
+
import { basename as basename4, isAbsolute as isAbsolute3, join as join18 } from "path";
|
|
7501
|
+
|
|
7502
|
+
// src/review/command-workdir.ts
|
|
7499
7503
|
import { homedir as homedir2 } from "os";
|
|
7500
|
-
import { basename as basename3, isAbsolute as isAbsolute2,
|
|
7504
|
+
import { basename as basename3, isAbsolute as isAbsolute2, resolve as resolve3 } from "path";
|
|
7505
|
+
function ambiguous(reason) {
|
|
7506
|
+
return { kind: "ambiguous", reason };
|
|
7507
|
+
}
|
|
7508
|
+
var SHELLS = /* @__PURE__ */ new Set(["sh", "bash", "zsh", "dash", "ksh", "mksh"]);
|
|
7509
|
+
var INDIRECT_EXECUTORS = /* @__PURE__ */ new Set([
|
|
7510
|
+
".",
|
|
7511
|
+
"alias",
|
|
7512
|
+
"bash",
|
|
7513
|
+
"builtin",
|
|
7514
|
+
"chroot",
|
|
7515
|
+
"command",
|
|
7516
|
+
"dash",
|
|
7517
|
+
"doas",
|
|
7518
|
+
"env",
|
|
7519
|
+
"eval",
|
|
7520
|
+
"exec",
|
|
7521
|
+
"ksh",
|
|
7522
|
+
"mksh",
|
|
7523
|
+
"nice",
|
|
7524
|
+
"nohup",
|
|
7525
|
+
"sh",
|
|
7526
|
+
"source",
|
|
7527
|
+
"ssh",
|
|
7528
|
+
"stdbuf",
|
|
7529
|
+
"su",
|
|
7530
|
+
"sudo",
|
|
7531
|
+
"timeout",
|
|
7532
|
+
"trap",
|
|
7533
|
+
"xargs",
|
|
7534
|
+
"zsh"
|
|
7535
|
+
]);
|
|
7536
|
+
var DIRECTORY_STACK = /* @__PURE__ */ new Set(["pushd", "popd"]);
|
|
7537
|
+
var SHELL_KEYWORDS = /* @__PURE__ */ new Set([
|
|
7538
|
+
"!",
|
|
7539
|
+
"case",
|
|
7540
|
+
"coproc",
|
|
7541
|
+
"do",
|
|
7542
|
+
"done",
|
|
7543
|
+
"elif",
|
|
7544
|
+
"else",
|
|
7545
|
+
"esac",
|
|
7546
|
+
"fi",
|
|
7547
|
+
"for",
|
|
7548
|
+
"function",
|
|
7549
|
+
"if",
|
|
7550
|
+
"in",
|
|
7551
|
+
"select",
|
|
7552
|
+
"then",
|
|
7553
|
+
"time",
|
|
7554
|
+
"until",
|
|
7555
|
+
"while",
|
|
7556
|
+
"{",
|
|
7557
|
+
"}"
|
|
7558
|
+
]);
|
|
7559
|
+
var RELOCATING_VARIABLES = /* @__PURE__ */ new Set([
|
|
7560
|
+
"CDPATH",
|
|
7561
|
+
"GIT_COMMON_DIR",
|
|
7562
|
+
"GIT_DIR",
|
|
7563
|
+
"GIT_WORK_TREE",
|
|
7564
|
+
"HOME",
|
|
7565
|
+
"OLDPWD",
|
|
7566
|
+
"PWD"
|
|
7567
|
+
]);
|
|
7568
|
+
var CHDIR_OPTIONS = /* @__PURE__ */ new Map([
|
|
7569
|
+
["git", /* @__PURE__ */ new Set(["-C", "--git-dir", "--work-tree"])],
|
|
7570
|
+
["make", /* @__PURE__ */ new Set(["-C", "--directory"])],
|
|
7571
|
+
["gmake", /* @__PURE__ */ new Set(["-C", "--directory"])],
|
|
7572
|
+
["env", /* @__PURE__ */ new Set(["-C", "--chdir"])],
|
|
7573
|
+
["tar", /* @__PURE__ */ new Set(["-C", "--directory"])],
|
|
7574
|
+
["pnpm", /* @__PURE__ */ new Set(["-C", "--dir", "--workspace-root", "-w"])],
|
|
7575
|
+
["npm", /* @__PURE__ */ new Set(["-C", "--prefix"])],
|
|
7576
|
+
["yarn", /* @__PURE__ */ new Set(["--cwd"])],
|
|
7577
|
+
["just", /* @__PURE__ */ new Set(["-d", "--working-directory"])]
|
|
7578
|
+
]);
|
|
7579
|
+
function deriveCommandWorkdir(command, args, cwd) {
|
|
7580
|
+
const program = basename3(command);
|
|
7581
|
+
if (SHELLS.has(program)) {
|
|
7582
|
+
const script = args.length === 2 && args[0] === "-c" ? args[1] : void 0;
|
|
7583
|
+
if (script === void 0) return ambiguous("unsupported_invocation");
|
|
7584
|
+
return readShellProgram(script, cwd);
|
|
7585
|
+
}
|
|
7586
|
+
if (!isPlainCommandHead(command)) return ambiguous("unrecognized_command_word");
|
|
7587
|
+
if (INDIRECT_EXECUTORS.has(program)) return ambiguous("indirect_execution");
|
|
7588
|
+
const words = args.map((text) => ({
|
|
7589
|
+
text,
|
|
7590
|
+
mask: "n".repeat(text.length),
|
|
7591
|
+
quoteOpens: []
|
|
7592
|
+
}));
|
|
7593
|
+
if (hasChdirOption(program, words)) return ambiguous("chdir_option");
|
|
7594
|
+
return { kind: "cwd" };
|
|
7595
|
+
}
|
|
7596
|
+
function readShellProgram(script, cwd) {
|
|
7597
|
+
const scan = scanProgram(script);
|
|
7598
|
+
if (!scan.ok) return ambiguous(scan.reason);
|
|
7599
|
+
const cds = [];
|
|
7600
|
+
let rank = 0;
|
|
7601
|
+
for (const [index, segment] of scan.segments.entries()) {
|
|
7602
|
+
const stripped = stripRedirections(segment);
|
|
7603
|
+
if (!stripped.ok) return ambiguous(stripped.reason);
|
|
7604
|
+
const words = stripped.words;
|
|
7605
|
+
const start = firstNonAssignment(words);
|
|
7606
|
+
for (const word of words.slice(0, start ?? words.length)) {
|
|
7607
|
+
const name2 = assignmentName(word);
|
|
7608
|
+
if (name2 === void 0) continue;
|
|
7609
|
+
if (RELOCATING_VARIABLES.has(name2)) return ambiguous("shell_state_assignment");
|
|
7610
|
+
if (hasLiveDollar(word)) return ambiguous("unreadable_assignment_value");
|
|
7611
|
+
if (assignmentValueHasTildeUser(word, name2.length)) return ambiguous("tilde_user");
|
|
7612
|
+
}
|
|
7613
|
+
if (start === void 0) continue;
|
|
7614
|
+
const head = words[start];
|
|
7615
|
+
if (head === void 0) continue;
|
|
7616
|
+
if (looksLikeAssignmentPrefix(head)) return ambiguous("assignment_or_command");
|
|
7617
|
+
if (head.text === "cd") {
|
|
7618
|
+
cds.push({ index, rank, operands: words.slice(start + 1) });
|
|
7619
|
+
rank++;
|
|
7620
|
+
continue;
|
|
7621
|
+
}
|
|
7622
|
+
rank++;
|
|
7623
|
+
if (hasLiveDollar(head)) return ambiguous("unexpanded_variable");
|
|
7624
|
+
if (SHELL_KEYWORDS.has(head.text)) return ambiguous("compound_command");
|
|
7625
|
+
if (!isPlainCommandHead(head.text)) return ambiguous("unrecognized_command_word");
|
|
7626
|
+
const name = basename3(head.text);
|
|
7627
|
+
if (DIRECTORY_STACK.has(name)) return ambiguous("directory_stack");
|
|
7628
|
+
if (INDIRECT_EXECUTORS.has(name)) return ambiguous("indirect_execution");
|
|
7629
|
+
const operands = words.slice(start + 1);
|
|
7630
|
+
if (hasChdirOption(name, operands)) return ambiguous("chdir_option");
|
|
7631
|
+
if (CHDIR_OPTIONS.has(name) && operands.some((w) => hasUnquoted(w, /\$/))) {
|
|
7632
|
+
return ambiguous("unexpanded_variable");
|
|
7633
|
+
}
|
|
7634
|
+
}
|
|
7635
|
+
if (cds.length === 0) return { kind: "cwd" };
|
|
7636
|
+
if (cds.length > 1) return ambiguous("multiple_cd");
|
|
7637
|
+
const cd = cds[0];
|
|
7638
|
+
if (cd === void 0) return ambiguous("unsupported_cd_form");
|
|
7639
|
+
if (cd.rank !== 0) return ambiguous("cd_not_first");
|
|
7640
|
+
if (scan.separators[cd.index] === "|") return ambiguous("cd_in_pipeline");
|
|
7641
|
+
return readCdTarget(cd.operands, cwd);
|
|
7642
|
+
}
|
|
7643
|
+
function scanProgram(script) {
|
|
7644
|
+
const segments = [];
|
|
7645
|
+
const separators = [];
|
|
7646
|
+
let words = [];
|
|
7647
|
+
let text = "";
|
|
7648
|
+
let mask = "";
|
|
7649
|
+
let quoteOpens = [];
|
|
7650
|
+
let started = false;
|
|
7651
|
+
const endWord = () => {
|
|
7652
|
+
if (started) words.push({ text, mask, quoteOpens });
|
|
7653
|
+
text = "";
|
|
7654
|
+
mask = "";
|
|
7655
|
+
quoteOpens = [];
|
|
7656
|
+
started = false;
|
|
7657
|
+
};
|
|
7658
|
+
const endSegment = (separator) => {
|
|
7659
|
+
endWord();
|
|
7660
|
+
if (words.length === 0) return separator === "newline";
|
|
7661
|
+
segments.push(words);
|
|
7662
|
+
words = [];
|
|
7663
|
+
separators.push(separator);
|
|
7664
|
+
return true;
|
|
7665
|
+
};
|
|
7666
|
+
const runLength = (index, char) => {
|
|
7667
|
+
let n = 0;
|
|
7668
|
+
while (script[index + n] === char) n++;
|
|
7669
|
+
return n;
|
|
7670
|
+
};
|
|
7671
|
+
const pushRedirection = (op) => {
|
|
7672
|
+
if (started && /^\d+$/.test(text) && !/[sd]/.test(mask) && quoteOpens.length === 0) {
|
|
7673
|
+
text = "";
|
|
7674
|
+
mask = "";
|
|
7675
|
+
started = false;
|
|
7676
|
+
}
|
|
7677
|
+
endWord();
|
|
7678
|
+
words.push({ text: op, mask: "n".repeat(op.length), quoteOpens: [], redirection: true });
|
|
7679
|
+
};
|
|
7680
|
+
let i = 0;
|
|
7681
|
+
while (i < script.length) {
|
|
7682
|
+
const c = script[i];
|
|
7683
|
+
if (c === "'") {
|
|
7684
|
+
const close = script.indexOf("'", i + 1);
|
|
7685
|
+
if (close === -1) return { ok: false, reason: "unterminated_quote" };
|
|
7686
|
+
const body = script.slice(i + 1, close);
|
|
7687
|
+
quoteOpens.push(text.length);
|
|
7688
|
+
text += body;
|
|
7689
|
+
mask += "s".repeat(body.length);
|
|
7690
|
+
started = true;
|
|
7691
|
+
i = close + 1;
|
|
7692
|
+
continue;
|
|
7693
|
+
}
|
|
7694
|
+
if (c === '"') {
|
|
7695
|
+
let j = i + 1;
|
|
7696
|
+
let closed = false;
|
|
7697
|
+
quoteOpens.push(text.length);
|
|
7698
|
+
while (j < script.length) {
|
|
7699
|
+
const d = script[j];
|
|
7700
|
+
if (d === '"') {
|
|
7701
|
+
closed = true;
|
|
7702
|
+
break;
|
|
7703
|
+
}
|
|
7704
|
+
if (d === "\\") return { ok: false, reason: "backslash_escape" };
|
|
7705
|
+
if (d === "`") return { ok: false, reason: "backtick" };
|
|
7706
|
+
if (d === "$" && script[j + 1] === "(")
|
|
7707
|
+
return { ok: false, reason: "command_substitution" };
|
|
7708
|
+
text += d;
|
|
7709
|
+
mask += "d";
|
|
7710
|
+
j++;
|
|
7711
|
+
}
|
|
7712
|
+
if (!closed) return { ok: false, reason: "unterminated_quote" };
|
|
7713
|
+
started = true;
|
|
7714
|
+
i = j + 1;
|
|
7715
|
+
continue;
|
|
7716
|
+
}
|
|
7717
|
+
if (c === "\\") return { ok: false, reason: "backslash_escape" };
|
|
7718
|
+
if (c === "`") return { ok: false, reason: "backtick" };
|
|
7719
|
+
if (c === "$" && script[i + 1] === "(") return { ok: false, reason: "command_substitution" };
|
|
7720
|
+
if (c === "$" && (script[i + 1] === "'" || script[i + 1] === '"')) {
|
|
7721
|
+
return { ok: false, reason: "dollar_quote" };
|
|
7722
|
+
}
|
|
7723
|
+
if (c === "(" || c === ")") return { ok: false, reason: "subshell" };
|
|
7724
|
+
if (c === "#" && !started) return { ok: false, reason: "comment" };
|
|
7725
|
+
if (c === "\r") return { ok: false, reason: "carriage_return" };
|
|
7726
|
+
if (c === "<") {
|
|
7727
|
+
if (runLength(i, "<") >= 2) return { ok: false, reason: "heredoc" };
|
|
7728
|
+
const next = script[i + 1];
|
|
7729
|
+
const op = next === "&" || next === ">" ? `<${next}` : "<";
|
|
7730
|
+
pushRedirection(op);
|
|
7731
|
+
i += op.length;
|
|
7732
|
+
continue;
|
|
7733
|
+
}
|
|
7734
|
+
if (c === ">") {
|
|
7735
|
+
const next = script[i + 1];
|
|
7736
|
+
const op = next === ">" || next === "&" || next === "|" ? `>${next}` : ">";
|
|
7737
|
+
pushRedirection(op);
|
|
7738
|
+
i += op.length;
|
|
7739
|
+
continue;
|
|
7740
|
+
}
|
|
7741
|
+
if (c === "&") {
|
|
7742
|
+
const n = runLength(i, "&");
|
|
7743
|
+
if (n === 2) {
|
|
7744
|
+
if (!endSegment("&&")) return { ok: false, reason: "shell_syntax" };
|
|
7745
|
+
i += 2;
|
|
7746
|
+
continue;
|
|
7747
|
+
}
|
|
7748
|
+
return { ok: false, reason: "background" };
|
|
7749
|
+
}
|
|
7750
|
+
if (c === "|") {
|
|
7751
|
+
if (runLength(i, "|") >= 2) return { ok: false, reason: "or_operator" };
|
|
7752
|
+
if (!endSegment("|")) return { ok: false, reason: "shell_syntax" };
|
|
7753
|
+
i++;
|
|
7754
|
+
continue;
|
|
7755
|
+
}
|
|
7756
|
+
if (c === ";") {
|
|
7757
|
+
if (!endSegment(";")) return { ok: false, reason: "shell_syntax" };
|
|
7758
|
+
i++;
|
|
7759
|
+
continue;
|
|
7760
|
+
}
|
|
7761
|
+
if (c === "\n") {
|
|
7762
|
+
if (!endSegment("newline")) return { ok: false, reason: "shell_syntax" };
|
|
7763
|
+
i++;
|
|
7764
|
+
continue;
|
|
7765
|
+
}
|
|
7766
|
+
if (c === " " || c === " ") {
|
|
7767
|
+
endWord();
|
|
7768
|
+
i++;
|
|
7769
|
+
continue;
|
|
7770
|
+
}
|
|
7771
|
+
text += c;
|
|
7772
|
+
mask += "n";
|
|
7773
|
+
started = true;
|
|
7774
|
+
i++;
|
|
7775
|
+
}
|
|
7776
|
+
endWord();
|
|
7777
|
+
const last = separators.at(-1);
|
|
7778
|
+
if (words.length === 0 && (last === "&&" || last === "|")) {
|
|
7779
|
+
return { ok: false, reason: "shell_syntax" };
|
|
7780
|
+
}
|
|
7781
|
+
segments.push(words);
|
|
7782
|
+
return { ok: true, segments, separators };
|
|
7783
|
+
}
|
|
7784
|
+
function stripRedirections(words) {
|
|
7785
|
+
const out = [];
|
|
7786
|
+
for (let i = 0; i < words.length; i++) {
|
|
7787
|
+
const word = words[i];
|
|
7788
|
+
if (word === void 0) continue;
|
|
7789
|
+
if (word.redirection !== true) {
|
|
7790
|
+
out.push(word);
|
|
7791
|
+
continue;
|
|
7792
|
+
}
|
|
7793
|
+
const target = words[i + 1];
|
|
7794
|
+
if (target === void 0 || target.redirection === true) {
|
|
7795
|
+
return { ok: false, reason: "shell_syntax" };
|
|
7796
|
+
}
|
|
7797
|
+
if (!isReadableRedirectionTarget(word.text, target)) {
|
|
7798
|
+
return { ok: false, reason: "unreadable_redirection" };
|
|
7799
|
+
}
|
|
7800
|
+
i++;
|
|
7801
|
+
}
|
|
7802
|
+
return { ok: true, words: out };
|
|
7803
|
+
}
|
|
7804
|
+
var FD_DUP_OPERATORS = /* @__PURE__ */ new Set([">&", "<&"]);
|
|
7805
|
+
var FD_DUP_TARGET = /^\d+$/;
|
|
7806
|
+
function isReadableRedirectionTarget(operator, target) {
|
|
7807
|
+
if (FD_DUP_OPERATORS.has(operator)) return FD_DUP_TARGET.test(target.text);
|
|
7808
|
+
if (target.text.length === 0) return false;
|
|
7809
|
+
return !hasUnquoted(target, /[$*?[{]/);
|
|
7810
|
+
}
|
|
7811
|
+
var ASSIGNMENT_PREFIX = /^([A-Za-z_][A-Za-z0-9_]*)=/;
|
|
7812
|
+
var AMBIGUOUS_ASSIGNMENT_START = /^[A-Za-z_][A-Za-z0-9_]*(\[|\+=)/;
|
|
7813
|
+
var PLAIN_COMMAND_HEAD = /^(?:~\/|\/|\.{1,2}\/)?[A-Za-z0-9_.+-]+(?:\/[A-Za-z0-9_.+-]+)*$/;
|
|
7814
|
+
function isPlainCommandHead(text) {
|
|
7815
|
+
if (!PLAIN_COMMAND_HEAD.test(text)) return false;
|
|
7816
|
+
if (text === ".") return true;
|
|
7817
|
+
const last = text.slice(text.lastIndexOf("/") + 1);
|
|
7818
|
+
return last !== "." && last !== "..";
|
|
7819
|
+
}
|
|
7820
|
+
function assignmentName(word) {
|
|
7821
|
+
if (word.redirection === true) return void 0;
|
|
7822
|
+
const match = ASSIGNMENT_PREFIX.exec(word.text);
|
|
7823
|
+
if (match?.[1] === void 0) return void 0;
|
|
7824
|
+
const upToEquals = match[0].length - 1;
|
|
7825
|
+
if (quotedBefore(word, upToEquals)) return void 0;
|
|
7826
|
+
return match[1];
|
|
7827
|
+
}
|
|
7828
|
+
function looksLikeAssignmentPrefix(word) {
|
|
7829
|
+
if (word.redirection === true) return false;
|
|
7830
|
+
const match = AMBIGUOUS_ASSIGNMENT_START.exec(word.text);
|
|
7831
|
+
if (match === null) return false;
|
|
7832
|
+
return !quotedBefore(word, match[0].length - 1);
|
|
7833
|
+
}
|
|
7834
|
+
function assignmentValueHasTildeUser(word, nameLength) {
|
|
7835
|
+
const at = nameLength + 1;
|
|
7836
|
+
if (word.text[at] !== "~" || word.mask[at] !== "n") return false;
|
|
7837
|
+
const next = word.text[at + 1];
|
|
7838
|
+
return next !== void 0 && next !== "/";
|
|
7839
|
+
}
|
|
7840
|
+
function quotedBefore(word, index) {
|
|
7841
|
+
if (/[sd]/.test(word.mask.slice(0, index))) return true;
|
|
7842
|
+
return word.quoteOpens.some((at) => at <= index);
|
|
7843
|
+
}
|
|
7844
|
+
function firstNonAssignment(words) {
|
|
7845
|
+
for (const [index, word] of words.entries()) {
|
|
7846
|
+
if (assignmentName(word) !== void 0) continue;
|
|
7847
|
+
return index;
|
|
7848
|
+
}
|
|
7849
|
+
return void 0;
|
|
7850
|
+
}
|
|
7851
|
+
function hasChdirOption(program, operands) {
|
|
7852
|
+
const options = CHDIR_OPTIONS.get(program);
|
|
7853
|
+
if (options === void 0) return false;
|
|
7854
|
+
return operands.some((word) => {
|
|
7855
|
+
if (word.redirection === true) return false;
|
|
7856
|
+
if (options.has(word.text)) return true;
|
|
7857
|
+
const eq = word.text.indexOf("=");
|
|
7858
|
+
if (eq > 0 && options.has(word.text.slice(0, eq))) return true;
|
|
7859
|
+
if (word.text.startsWith("--") || !word.text.startsWith("-")) return false;
|
|
7860
|
+
for (const option of options) {
|
|
7861
|
+
if (option.length !== 2 || option.startsWith("--")) continue;
|
|
7862
|
+
if (word.text.includes(option.slice(1), 1)) return true;
|
|
7863
|
+
}
|
|
7864
|
+
return false;
|
|
7865
|
+
});
|
|
7866
|
+
}
|
|
7867
|
+
function hasUnquoted(word, pattern) {
|
|
7868
|
+
for (let i = 0; i < word.text.length; i++) {
|
|
7869
|
+
if (word.mask[i] === "n" && pattern.test(word.text[i])) return true;
|
|
7870
|
+
}
|
|
7871
|
+
return false;
|
|
7872
|
+
}
|
|
7873
|
+
function hasLiveDollar(word) {
|
|
7874
|
+
for (let i = 0; i < word.text.length; i++) {
|
|
7875
|
+
if (word.text[i] === "$" && word.mask[i] !== "s") return true;
|
|
7876
|
+
}
|
|
7877
|
+
return false;
|
|
7878
|
+
}
|
|
7879
|
+
function readCdTarget(operands, cwd) {
|
|
7880
|
+
let operand;
|
|
7881
|
+
let afterEndOfOptions = false;
|
|
7882
|
+
if (operands.length === 1) operand = operands[0];
|
|
7883
|
+
else if (operands.length === 2 && operands[0]?.text === "--") {
|
|
7884
|
+
operand = operands[1];
|
|
7885
|
+
afterEndOfOptions = true;
|
|
7886
|
+
}
|
|
7887
|
+
if (operand === void 0 || operand.text.length === 0) {
|
|
7888
|
+
return ambiguous("unsupported_cd_form");
|
|
7889
|
+
}
|
|
7890
|
+
if (!afterEndOfOptions && operand.text.startsWith("-")) {
|
|
7891
|
+
return ambiguous("unsupported_cd_form");
|
|
7892
|
+
}
|
|
7893
|
+
let target = operand.text;
|
|
7894
|
+
if (hasLiveDollar(operand)) return ambiguous("unexpanded_variable");
|
|
7895
|
+
if (hasUnquoted(operand, /[*?[{]/)) return ambiguous("glob");
|
|
7896
|
+
if (target.startsWith("~")) {
|
|
7897
|
+
if (operand.mask[0] !== "n") return ambiguous("quoted_tilde");
|
|
7898
|
+
if (target === "~") target = homedir2();
|
|
7899
|
+
else if (target.startsWith("~/")) target = homedir2() + target.slice(1);
|
|
7900
|
+
else return ambiguous("tilde_user");
|
|
7901
|
+
}
|
|
7902
|
+
if (!isAbsolute2(target)) {
|
|
7903
|
+
if (!isAbsolute2(cwd)) return ambiguous("unresolvable_relative");
|
|
7904
|
+
target = resolve3(cwd, target);
|
|
7905
|
+
}
|
|
7906
|
+
return { kind: "target", path: target };
|
|
7907
|
+
}
|
|
7908
|
+
|
|
7909
|
+
// src/review/review-gaps.ts
|
|
7501
7910
|
function stripQuotes(s) {
|
|
7502
7911
|
if (s.length >= 2 && (s[0] === '"' && s.at(-1) === '"' || s[0] === "'" && s.at(-1) === "'")) {
|
|
7503
7912
|
return s.slice(1, -1);
|
|
@@ -7529,8 +7938,8 @@ function normalizeRepoPath(p) {
|
|
|
7529
7938
|
if (!p) return null;
|
|
7530
7939
|
let s = stripQuotes(p.trim()).replace(/\/+$/, "");
|
|
7531
7940
|
if (s.length === 0 || s === "~") return null;
|
|
7532
|
-
if (s.startsWith("~/")) s =
|
|
7533
|
-
if (
|
|
7941
|
+
if (s.startsWith("~/")) s = homedir3() + s.slice(1);
|
|
7942
|
+
if (isAbsolute3(s)) {
|
|
7534
7943
|
const real = resolveRealpath(s);
|
|
7535
7944
|
if (real !== null) {
|
|
7536
7945
|
return isRepoRoot(real) ? real : null;
|
|
@@ -7539,7 +7948,7 @@ function normalizeRepoPath(p) {
|
|
|
7539
7948
|
s = s.replace(/\/[^/]*-workspace\/([^/]+)/, "/$1");
|
|
7540
7949
|
const seg = s.split("/").filter((x) => x.length > 0).pop();
|
|
7541
7950
|
if (seg === void 0) return null;
|
|
7542
|
-
if (/-workspace$/.test(seg) ||
|
|
7951
|
+
if (/-workspace$/.test(seg) || s.includes("$")) return null;
|
|
7543
7952
|
return s;
|
|
7544
7953
|
}
|
|
7545
7954
|
function recordRepoKey(p) {
|
|
@@ -7550,8 +7959,8 @@ function resolveRepoRoot(p) {
|
|
|
7550
7959
|
}
|
|
7551
7960
|
function classifyRepoPath(p) {
|
|
7552
7961
|
let s = stripQuotes((p ?? "").trim()).replace(/\/+$/, "");
|
|
7553
|
-
if (s.startsWith("~/")) s =
|
|
7554
|
-
if (s.length === 0 || !
|
|
7962
|
+
if (s.startsWith("~/")) s = homedir3() + s.slice(1);
|
|
7963
|
+
if (s.length === 0 || !isAbsolute3(s)) return { resolved: null, problem: "relative" };
|
|
7555
7964
|
const real = resolveRealpath(s);
|
|
7556
7965
|
if (real === null) return { resolved: null, problem: "absent" };
|
|
7557
7966
|
if (!isRepoRoot(real)) return { resolved: null, problem: "not_a_repo_root" };
|
|
@@ -7567,7 +7976,7 @@ function findUnbindableRepos(repos) {
|
|
|
7567
7976
|
}
|
|
7568
7977
|
function normalizeRepoKey(p) {
|
|
7569
7978
|
const full = normalizeRepoPath(p);
|
|
7570
|
-
return full === null ? null :
|
|
7979
|
+
return full === null ? null : basename4(full);
|
|
7571
7980
|
}
|
|
7572
7981
|
function inspectCommand(args) {
|
|
7573
7982
|
const a = args.join(" ");
|
|
@@ -7581,21 +7990,23 @@ function inspectCommand(args) {
|
|
|
7581
7990
|
let m;
|
|
7582
7991
|
while ((m = re.exec(a)) !== null) {
|
|
7583
7992
|
const f = m[1];
|
|
7584
|
-
if (f !== void 0) files.add(
|
|
7993
|
+
if (f !== void 0) files.add(basename4(f));
|
|
7585
7994
|
}
|
|
7586
7995
|
}
|
|
7587
7996
|
return { files: [...files], examinedDiff };
|
|
7588
7997
|
}
|
|
7589
|
-
function commandRepoWithProvenance(args, cwd) {
|
|
7590
|
-
const raw = commandRepoPath(args, cwd);
|
|
7998
|
+
function commandRepoWithProvenance(command, args, cwd) {
|
|
7999
|
+
const raw = commandRepoPath(command, args, cwd);
|
|
8000
|
+
if (raw === null) return { key: null, resolved: false };
|
|
7591
8001
|
return { key: normalizeRepoPath(raw), resolved: resolveRepoRoot(raw) !== null };
|
|
7592
8002
|
}
|
|
7593
|
-
function commandRepoPath(args, cwd) {
|
|
7594
|
-
const
|
|
7595
|
-
|
|
8003
|
+
function commandRepoPath(command, args, cwd) {
|
|
8004
|
+
const workdir = deriveCommandWorkdir(command, args, cwd);
|
|
8005
|
+
if (workdir.kind === "ambiguous") return null;
|
|
8006
|
+
return workdir.kind === "target" ? workdir.path : cwd;
|
|
7596
8007
|
}
|
|
7597
|
-
function commandRepo(args, cwd) {
|
|
7598
|
-
return normalizeRepoPath(commandRepoPath(args, cwd));
|
|
8008
|
+
function commandRepo(command, args, cwd) {
|
|
8009
|
+
return normalizeRepoPath(commandRepoPath(command, args, cwd));
|
|
7599
8010
|
}
|
|
7600
8011
|
function commandFailed(exitCode) {
|
|
7601
8012
|
return exitCode !== null && exitCode !== 0;
|
|
@@ -7604,7 +8015,7 @@ function commitFiles(args) {
|
|
|
7604
8015
|
const a = args.join(" ");
|
|
7605
8016
|
const add = a.match(/git add\s+([^&|;]+)/);
|
|
7606
8017
|
if (!add?.[1]) return [];
|
|
7607
|
-
return add[1].split(/\s+/).filter((t) => /\.[A-Za-z]/.test(t) && !t.startsWith("-")).map((t) =>
|
|
8018
|
+
return add[1].split(/\s+/).filter((t) => /\.[A-Za-z]/.test(t) && !t.startsWith("-")).map((t) => basename4(t));
|
|
7608
8019
|
}
|
|
7609
8020
|
var REVIEW_SOURCE = "codex-import";
|
|
7610
8021
|
var DEFAULT_WINDOW_HOURS = 24;
|
|
@@ -7657,7 +8068,7 @@ async function findReviewGaps(input) {
|
|
|
7657
8068
|
if (commandFailed(ev.exit_code)) continue;
|
|
7658
8069
|
const at = Date.parse(ev.occurred_at);
|
|
7659
8070
|
if (isReview) {
|
|
7660
|
-
const repo2 = commandRepo(ev.args, ev.cwd);
|
|
8071
|
+
const repo2 = commandRepo(ev.command, ev.args, ev.cwd);
|
|
7661
8072
|
if (repo2 === null) continue;
|
|
7662
8073
|
const ins = inspectCommand(ev.args);
|
|
7663
8074
|
const slot = reviewRepos.get(repo2) ?? { examinedDiff: false, files: /* @__PURE__ */ new Set() };
|
|
@@ -7668,7 +8079,11 @@ async function findReviewGaps(input) {
|
|
|
7668
8079
|
continue;
|
|
7669
8080
|
}
|
|
7670
8081
|
if (!ev.args.join(" ").includes("git commit")) continue;
|
|
7671
|
-
const { key: repo, resolved: keyResolved } = commandRepoWithProvenance(
|
|
8082
|
+
const { key: repo, resolved: keyResolved } = commandRepoWithProvenance(
|
|
8083
|
+
ev.command,
|
|
8084
|
+
ev.args,
|
|
8085
|
+
ev.cwd
|
|
8086
|
+
);
|
|
7672
8087
|
if (repo === null || Number.isNaN(at)) {
|
|
7673
8088
|
const list2 = unknownCommits.get(entry.sessionId) ?? [];
|
|
7674
8089
|
list2.push(Number.isNaN(at) ? null : at);
|
|
@@ -7697,7 +8112,7 @@ async function findReviewGaps(input) {
|
|
|
7697
8112
|
let refusedPairings = 0;
|
|
7698
8113
|
for (const [sessionId, byRepo] of workUnits) {
|
|
7699
8114
|
for (const [repoPath, commits] of byRepo) {
|
|
7700
|
-
const label =
|
|
8115
|
+
const label = basename4(repoPath);
|
|
7701
8116
|
const times = commits.map((c) => c.at).sort((a, b) => a - b);
|
|
7702
8117
|
const first = times[0] ?? null;
|
|
7703
8118
|
const last = times[times.length - 1] ?? null;
|
|
@@ -8069,7 +8484,7 @@ var ChildProcessRunner = class {
|
|
|
8069
8484
|
if (killTimer !== null) clearTimeout(killTimer);
|
|
8070
8485
|
options.signal?.removeEventListener("abort", onAbort);
|
|
8071
8486
|
};
|
|
8072
|
-
return new Promise((
|
|
8487
|
+
return new Promise((resolve4, reject) => {
|
|
8073
8488
|
child.once("error", (error) => {
|
|
8074
8489
|
if (settled) return;
|
|
8075
8490
|
settled = true;
|
|
@@ -8081,7 +8496,7 @@ var ChildProcessRunner = class {
|
|
|
8081
8496
|
settled = true;
|
|
8082
8497
|
cleanup();
|
|
8083
8498
|
const ended_at = /* @__PURE__ */ new Date();
|
|
8084
|
-
|
|
8499
|
+
resolve4({
|
|
8085
8500
|
command: snapshotCommand,
|
|
8086
8501
|
args: snapshotArgs,
|
|
8087
8502
|
cwd: snapshotCwd,
|
|
@@ -8520,7 +8935,7 @@ function hasErrorCode6(error) {
|
|
|
8520
8935
|
|
|
8521
8936
|
// src/storage/session-import.ts
|
|
8522
8937
|
import { mkdir as mkdir5, readFile as readFile10, rm as rm2 } from "fs/promises";
|
|
8523
|
-
import { homedir as
|
|
8938
|
+
import { homedir as homedir4 } from "os";
|
|
8524
8939
|
import { join as join21 } from "path";
|
|
8525
8940
|
async function importSessionFromJson(paths, manifest, payload, options) {
|
|
8526
8941
|
if (options.taskIdOverride !== void 0 && !TaskIdSchema.safeParse(options.taskIdOverride).success) {
|
|
@@ -8629,7 +9044,7 @@ function withIntegrity(record, chainResult) {
|
|
|
8629
9044
|
};
|
|
8630
9045
|
}
|
|
8631
9046
|
function buildSessionRecord(input, manifest, newSessionId, options) {
|
|
8632
|
-
const home =
|
|
9047
|
+
const home = homedir4();
|
|
8633
9048
|
const workingDirectoryRaw = input.working_directory;
|
|
8634
9049
|
const workingDirectorySanitized = sanitizeWorkingDirectory(workingDirectoryRaw, {
|
|
8635
9050
|
homedir: home
|