@brainervirus/workit-cursor 8.12.2 → 8.12.3

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.
@@ -17960,7 +17960,63 @@ var init_session_marker = __esm(() => {
17960
17960
  });
17961
17961
 
17962
17962
  // packages/workit-core/src/hooks/shell-words.ts
17963
- var closingQuote = (command, start, dialect) => {
17963
+ var ShellAnalysisError, MAX_NESTING = 64, OPEN = "", COMMA = "", CLOSE = "", MARKERS, literal2 = (word) => word.replaceAll(OPEN, "{").replaceAll(COMMA, ",").replaceAll(CLOSE, "}"), BRACE_LIMIT = 64, BRACE_PENDING = 512, BRACE_DEPTH = 16, braceGroup = (word) => {
17964
+ for (let start = 0;start < word.length; start++) {
17965
+ if (word[start] !== OPEN)
17966
+ continue;
17967
+ let depth = 0;
17968
+ let end = start;
17969
+ const commas = [];
17970
+ for (;end < word.length; end++) {
17971
+ if (word[end] === OPEN)
17972
+ depth++;
17973
+ else if (word[end] === CLOSE && --depth === 0)
17974
+ break;
17975
+ else if (word[end] === COMMA && depth === 1)
17976
+ commas.push(end);
17977
+ }
17978
+ if (end < word.length && commas.length)
17979
+ return { start, end, commas };
17980
+ }
17981
+ return null;
17982
+ }, expandBraces = (word) => {
17983
+ let depth = 0;
17984
+ for (const char of word) {
17985
+ if (char === OPEN && ++depth > BRACE_DEPTH)
17986
+ return [literal2(word)];
17987
+ if (char === CLOSE && depth > 0)
17988
+ depth--;
17989
+ }
17990
+ const done = new Set;
17991
+ const queue = [word];
17992
+ const queued = new Set(queue);
17993
+ for (let at = 0;at < queue.length; at++) {
17994
+ const current = queue[at];
17995
+ const group = braceGroup(current);
17996
+ if (!group) {
17997
+ const text = literal2(current);
17998
+ if (text)
17999
+ done.add(text);
18000
+ if (done.size > BRACE_LIMIT)
18001
+ return [literal2(word)];
18002
+ continue;
18003
+ }
18004
+ const pre = current.slice(0, group.start);
18005
+ const post = current.slice(group.end + 1);
18006
+ let from = group.start + 1;
18007
+ for (const comma of [...group.commas, group.end]) {
18008
+ const next = pre + current.slice(from, comma) + post;
18009
+ from = comma + 1;
18010
+ if (queued.has(next))
18011
+ continue;
18012
+ queued.add(next);
18013
+ queue.push(next);
18014
+ }
18015
+ if (queue.length - at > BRACE_PENDING)
18016
+ return [literal2(word)];
18017
+ }
18018
+ return [...done];
18019
+ }, closingQuote = (command, start, dialect, nesting = 0) => {
17964
18020
  const quote = command[start];
17965
18021
  for (let index = start + 1;index < command.length; index++) {
17966
18022
  const char = command[index];
@@ -17977,49 +18033,241 @@ var closingQuote = (command, start, dialect) => {
17977
18033
  index++;
17978
18034
  continue;
17979
18035
  }
18036
+ if (dialect === "posix" && char === "$" && command[index + 1] === "(") {
18037
+ index = substitutionEnd(command, index + 1, nesting + 1);
18038
+ continue;
18039
+ }
18040
+ if (dialect === "posix" && char === "`") {
18041
+ index = backtickEnd(command, index);
18042
+ continue;
18043
+ }
17980
18044
  if (char === '"')
17981
18045
  return index;
17982
18046
  }
17983
18047
  return -1;
18048
+ }, backtickEnd = (command, start) => {
18049
+ for (let index = start + 1;index < command.length; index++) {
18050
+ if (command[index] === "\\")
18051
+ index++;
18052
+ else if (command[index] === "`")
18053
+ return index;
18054
+ }
18055
+ return command.length;
18056
+ }, heredocDelimiter = (command, start) => {
18057
+ let index = start;
18058
+ if (command[index] === "-")
18059
+ index++;
18060
+ while (command[index] === " " || command[index] === "\t")
18061
+ index++;
18062
+ let delimiter = "";
18063
+ for (;index < command.length && !/[\s;&|<>()]/.test(command[index]); index++) {
18064
+ const char = command[index];
18065
+ if (char === "'" || char === '"') {
18066
+ const end = command.indexOf(char, index + 1);
18067
+ if (end < 0)
18068
+ break;
18069
+ delimiter += command.slice(index + 1, end);
18070
+ index = end;
18071
+ } else if (char === "\\")
18072
+ delimiter += command[++index] ?? "";
18073
+ else
18074
+ delimiter += char;
18075
+ }
18076
+ return { delimiter, end: index - 1 };
18077
+ }, skipHeredocs = (command, newline, delimiters) => {
18078
+ let index = newline;
18079
+ while (delimiters.length) {
18080
+ const delimiter = delimiters.shift();
18081
+ for (;; ) {
18082
+ const lineEnd = command.indexOf(`
18083
+ `, index + 1);
18084
+ const line = command.slice(index + 1, lineEnd < 0 ? undefined : lineEnd);
18085
+ index = lineEnd < 0 ? command.length : lineEnd;
18086
+ if (line.trim() === delimiter || lineEnd < 0)
18087
+ break;
18088
+ }
18089
+ }
18090
+ return index;
18091
+ }, substitutionEnd = (command, start, nesting = 0) => {
18092
+ if (nesting > MAX_NESTING)
18093
+ throw new ShellAnalysisError("substitutions nest too deep");
18094
+ let depth = 0;
18095
+ let wordStart = true;
18096
+ const heredocs = [];
18097
+ for (let index = start;index < command.length; index++) {
18098
+ const char = command[index];
18099
+ const atWordStart = wordStart;
18100
+ wordStart = /[\s;&|()]/.test(char);
18101
+ if (char === "\\")
18102
+ index++;
18103
+ else if (char === `
18104
+ ` && heredocs.length) {
18105
+ index = skipHeredocs(command, index, heredocs);
18106
+ wordStart = true;
18107
+ } else if (char === "'" || char === '"') {
18108
+ const end = closingQuote(command, index, "posix", nesting);
18109
+ if (end < 0)
18110
+ return command.length;
18111
+ index = end;
18112
+ } else if (char === "`")
18113
+ index = backtickEnd(command, index);
18114
+ else if (char === "#" && atWordStart) {
18115
+ const newline = command.indexOf(`
18116
+ `, index);
18117
+ index = newline < 0 ? command.length : newline - 1;
18118
+ } else if (char === "<" && command[index + 1] === "<" && command[index + 2] !== "<") {
18119
+ const { delimiter, end } = heredocDelimiter(command, index + 2);
18120
+ if (delimiter)
18121
+ heredocs.push(delimiter);
18122
+ index = end;
18123
+ } else if (char === "<" && command[index + 1] === "<")
18124
+ index += 2;
18125
+ else if (char === "$" && command[index + 1] === "(")
18126
+ index = substitutionEnd(command, index + 1, nesting + 1);
18127
+ else if (char === "(")
18128
+ depth++;
18129
+ else if (char === ")" && --depth === 0)
18130
+ return index;
18131
+ }
18132
+ return command.length;
17984
18133
  }, unquote = (body, quote, dialect) => {
17985
18134
  if (quote === "'")
17986
18135
  return dialect === "powershell" ? body.replaceAll("''", "'") : body;
17987
- return dialect === "posix" ? body.replace(/\\(["\\$`])/g, "$1") : body.replace(/`(.)/g, "$1");
18136
+ return dialect === "posix" ? body.replace(/\\\n/g, "").replace(/\\(["\\$`])/g, "$1") : body.replace(/`(.)/g, "$1");
18137
+ }, C_ESCAPES, ansiC = (command, start) => {
18138
+ let text = "";
18139
+ let index = start + 1;
18140
+ for (;index < command.length && command[index] !== "'"; index++) {
18141
+ const char = command[index];
18142
+ if (char !== "\\" || index + 1 >= command.length) {
18143
+ text += char;
18144
+ continue;
18145
+ }
18146
+ const rest = command.slice(++index);
18147
+ const hex = /^x([0-9a-fA-F]{1,2})/.exec(rest);
18148
+ const octal = /^[0-7]{1,3}/.exec(rest);
18149
+ if (hex) {
18150
+ text += String.fromCharCode(Number.parseInt(hex[1], 16));
18151
+ index += hex[0].length - 1;
18152
+ } else if (octal) {
18153
+ text += String.fromCharCode(Number.parseInt(octal[0], 8));
18154
+ index += octal[0].length - 1;
18155
+ } else
18156
+ text += C_ESCAPES[rest[0]] ?? rest[0];
18157
+ }
18158
+ return { text, end: index };
18159
+ }, substitutions = (body) => {
18160
+ const found = [];
18161
+ for (let index = 0;index < body.length; index++) {
18162
+ const char = body[index];
18163
+ if (char === "\\")
18164
+ index++;
18165
+ else if (char === "`") {
18166
+ const end = backtickEnd(body, index);
18167
+ found.push(body.slice(index + 1, end).replace(/\\(.)/g, "$1"));
18168
+ index = end;
18169
+ } else if (char === "$" && body[index + 1] === "(") {
18170
+ const end = substitutionEnd(body, index + 1);
18171
+ found.push(body.slice(index + 2, end));
18172
+ index = end;
18173
+ }
18174
+ }
18175
+ return found;
18176
+ }, PLAIN_POSIX, PLAIN_POWERSHELL, arithmeticEnd = (command, start) => {
18177
+ const end = substitutionEnd(command, start);
18178
+ return command[end - 1] === ")" && end > start + 2 ? end : 0;
17988
18179
  }, segmentsOf = (command, dialect = "posix") => {
18180
+ try {
18181
+ return tokenize(command, dialect);
18182
+ } catch (error) {
18183
+ const why = error instanceof ShellAnalysisError ? error.message : String(error);
18184
+ return [{ words: [], redirects: [], unanalyzable: why }];
18185
+ }
18186
+ }, tokenize = (command, dialect) => {
18187
+ const posix = dialect === "posix";
17989
18188
  const segments = [];
17990
18189
  let current = { words: [], redirects: [] };
17991
18190
  let word = "";
17992
18191
  let inWord = false;
17993
18192
  let pending = null;
17994
18193
  const heredocs = [];
17995
- const escape = dialect === "posix" ? "\\" : "`";
18194
+ const escape = posix ? "\\" : "`";
17996
18195
  const flushWord = () => {
17997
18196
  if (!inWord)
17998
18197
  return;
18198
+ const text = MARKERS.test(word) ? literal2(word) : word;
17999
18199
  if (pending === "redirect") {
18000
- if (!word.startsWith("&"))
18001
- current.redirects.push(word);
18200
+ if (!text.startsWith("&"))
18201
+ current.redirects.push(text);
18002
18202
  } else if (pending === "heredoc")
18003
- heredocs.push(word.replace(/^-/, ""));
18203
+ heredocs.push({ delimiter: text.replace(/^-/, ""), owner: current });
18204
+ else if (pending === "herestring")
18205
+ (current.stdin ??= []).push(text);
18004
18206
  else if (pending !== "input")
18005
- current.words.push(word);
18207
+ current.words.push(...MARKERS.test(word) ? expandBraces(word) : [word]);
18006
18208
  pending = null;
18007
18209
  word = "";
18008
18210
  inWord = false;
18009
18211
  };
18010
18212
  const flushSegment = () => {
18011
18213
  flushWord();
18012
- if (current.words.length || current.redirects.length)
18214
+ if (current.words.length || current.redirects.length || current.stdin || current.subs)
18013
18215
  segments.push(current);
18014
18216
  current = { words: [], redirects: [] };
18015
18217
  };
18218
+ const group = (kind) => {
18219
+ flushSegment();
18220
+ pending = null;
18221
+ segments.push({ words: [], redirects: [], group: kind });
18222
+ };
18223
+ const substitution = (inner, text) => {
18224
+ (current.subs ??= []).push(inner);
18225
+ word += text;
18226
+ inWord = true;
18227
+ };
18228
+ const plain = posix ? PLAIN_POSIX : PLAIN_POWERSHELL;
18016
18229
  for (let index = 0;index < command.length; index++) {
18230
+ plain.lastIndex = index;
18231
+ const run = plain.exec(command);
18232
+ if (run) {
18233
+ word += run[0];
18234
+ inWord = true;
18235
+ index += run[0].length - 1;
18236
+ continue;
18237
+ }
18017
18238
  const char = command[index];
18018
- if (char === "'" || char === '"') {
18239
+ const next = command[index + 1];
18240
+ if (char === escape && (next === `
18241
+ ` || next === "\r" && command[index + 2] === `
18242
+ `))
18243
+ index += next === "\r" ? 2 : 1;
18244
+ else if (char === "#" && !inWord) {
18245
+ const newline = command.indexOf(`
18246
+ `, index);
18247
+ index = (newline < 0 ? command.length : newline) - 1;
18248
+ } else if (!posix && char === "<" && next === "#") {
18249
+ const end = command.indexOf("#>", index + 2);
18250
+ index = end < 0 ? command.length : end + 1;
18251
+ } else if (posix && char === "$" && next === "'") {
18252
+ const { text, end } = ansiC(command, index + 1);
18253
+ word += text;
18254
+ inWord = true;
18255
+ index = end;
18256
+ } else if (posix && char === "$" && next === '"')
18257
+ continue;
18258
+ else if (char === "'" || char === '"') {
18019
18259
  const end = closingQuote(command, index, dialect);
18020
- word += unquote(command.slice(index + 1, end < 0 ? undefined : end), char, dialect);
18260
+ const body = command.slice(index + 1, end < 0 ? undefined : end);
18261
+ if (posix && char === '"' && /\$\(|`/.test(body))
18262
+ (current.subs ??= []).push(...substitutions(body));
18263
+ word += unquote(body, char, dialect);
18021
18264
  inWord = true;
18022
18265
  index = end < 0 ? command.length : end;
18266
+ } else if (posix && char === "`") {
18267
+ const end = backtickEnd(command, index);
18268
+ const inner = command.slice(index + 1, end);
18269
+ substitution(inner.replace(/\\(.)/g, "$1"), `\`${inner}\``);
18270
+ index = end;
18023
18271
  } else if (char === escape && index + 1 < command.length) {
18024
18272
  word += command[++index];
18025
18273
  inWord = true;
@@ -18027,26 +18275,56 @@ var closingQuote = (command, start, dialect) => {
18027
18275
  `) {
18028
18276
  flushSegment();
18029
18277
  while (heredocs.length) {
18030
- const delimiter = heredocs.shift();
18278
+ const { delimiter, owner } = heredocs.shift();
18031
18279
  const lines = command.slice(index + 1).split(`
18032
18280
  `);
18033
18281
  const at = lines.findIndex((line) => line.trim() === delimiter);
18034
- const skipped = (at < 0 ? lines : lines.slice(0, at + 1)).join(`
18282
+ (owner.stdin ??= []).push((at < 0 ? lines : lines.slice(0, at)).join(`
18283
+ `));
18284
+ index += (at < 0 ? lines : lines.slice(0, at + 1)).join(`
18035
18285
  `).length + 1;
18036
- index += skipped;
18037
18286
  }
18038
18287
  } else if (/\s/.test(char))
18039
18288
  flushWord();
18040
- else if (char === "(" || char === ")") {
18289
+ else if (posix && char === "(" && inWord && word.endsWith("$")) {
18290
+ const end = substitutionEnd(command, index);
18291
+ const inner = command.slice(index + 1, end);
18292
+ substitution(inner, `(${inner})`);
18293
+ index = end;
18294
+ } else if (posix && char === "(" && (pending === "input" || pending === "redirect")) {
18295
+ const sign = pending === "input" ? "<" : ">";
18296
+ pending = null;
18297
+ const end = substitutionEnd(command, index);
18298
+ const inner = command.slice(index + 1, end);
18299
+ substitution(inner, `${sign}(${inner})`);
18300
+ index = end;
18301
+ } else if (posix && char === "(" && next === "(" && !inWord && arithmeticEnd(command, index)) {
18302
+ flushSegment();
18303
+ index = arithmeticEnd(command, index);
18304
+ } else if (char === "(" || char === ")") {
18041
18305
  if (char === "(" && inWord && word.endsWith("$")) {
18042
18306
  word = word.slice(0, -1);
18043
18307
  inWord = word !== "";
18044
18308
  }
18309
+ group(char === "(" ? "open" : "close");
18310
+ } else if (!posix && (char === "{" || char === "}")) {
18311
+ if (char === "{" && inWord && word.endsWith("@")) {
18312
+ word += char;
18313
+ continue;
18314
+ }
18045
18315
  flushSegment();
18046
- segments.push({ words: [], redirects: [], group: char === "(" ? "open" : "close" });
18047
18316
  } else if (";&|".includes(char)) {
18048
- if (char === "&" && command[index + 1] === ">")
18317
+ if (char === "&" && next === ">")
18049
18318
  continue;
18319
+ if (char === "|" && next === "|")
18320
+ index++;
18321
+ else if (char === "|") {
18322
+ flushWord();
18323
+ if (current.words.length)
18324
+ current.pipe = true;
18325
+ if (next === "&")
18326
+ index++;
18327
+ }
18050
18328
  flushSegment();
18051
18329
  } else if (char === ">") {
18052
18330
  if (inWord && /^\d$/.test(word)) {
@@ -18062,16 +18340,24 @@ var closingQuote = (command, start, dialect) => {
18062
18340
  inWord = true;
18063
18341
  }
18064
18342
  pending = "redirect";
18065
- } else if (char === "<" && dialect === "posix") {
18343
+ } else if (char === "<" && posix) {
18066
18344
  flushWord();
18067
- if (command[index + 1] === "<") {
18345
+ if (next === "<" && command[index + 2] === "<") {
18346
+ index += 2;
18347
+ pending = "herestring";
18348
+ } else if (next === "<") {
18068
18349
  index++;
18069
- if (command[index + 1] === "<")
18070
- index++;
18071
18350
  pending = "heredoc";
18072
18351
  } else
18073
18352
  pending = "input";
18074
- } else {
18353
+ } else if (posix && char === "{" && !word.endsWith("$")) {
18354
+ word += OPEN;
18355
+ inWord = true;
18356
+ } else if (posix && char === "," && word.includes(OPEN))
18357
+ word += COMMA;
18358
+ else if (posix && char === "}" && word.includes(OPEN))
18359
+ word += CLOSE;
18360
+ else {
18075
18361
  word += char;
18076
18362
  inWord = true;
18077
18363
  }
@@ -18079,6 +18365,25 @@ var closingQuote = (command, start, dialect) => {
18079
18365
  flushSegment();
18080
18366
  return segments;
18081
18367
  };
18368
+ var init_shell_words = __esm(() => {
18369
+ ShellAnalysisError = class ShellAnalysisError extends Error {
18370
+ };
18371
+ MARKERS = /[\uE000-\uE002]/u;
18372
+ C_ESCAPES = {
18373
+ n: `
18374
+ `,
18375
+ t: "\t",
18376
+ r: "\r",
18377
+ a: "\x07",
18378
+ b: "\b",
18379
+ e: "\x1B",
18380
+ E: "\x1B",
18381
+ f: "\f",
18382
+ v: "\v"
18383
+ };
18384
+ PLAIN_POSIX = /[^\s'"\\`$(){},<>|;&#]+/uy;
18385
+ PLAIN_POWERSHELL = /[^\s'"`(){}<>|;&#]+/uy;
18386
+ });
18082
18387
 
18083
18388
  // packages/workit-core/src/hooks/raw-git.ts
18084
18389
  import { spawnSync as spawnSync11 } from "node:child_process";
@@ -18087,67 +18392,124 @@ import fs16 from "node:fs";
18087
18392
  import os5 from "node:os";
18088
18393
  import path20 from "node:path";
18089
18394
  function rawInvocations(command, dialect = "posix", depth = 0) {
18395
+ if (depth > 0)
18396
+ return walk2(command, dialect, depth);
18397
+ const key = `${dialect}\x00${command}`;
18398
+ const cached = RECENT.get(key);
18399
+ if (cached)
18400
+ return cached;
18401
+ let found;
18402
+ try {
18403
+ found = walk2(command, dialect, depth);
18404
+ } catch (error) {
18405
+ throw error instanceof ShellAnalysisError ? error : new ShellAnalysisError(error instanceof Error ? error.message : String(error));
18406
+ }
18407
+ RECENT.set(key, found);
18408
+ if (RECENT.size > 8)
18409
+ RECENT.delete(RECENT.keys().next().value);
18410
+ return found;
18411
+ }
18412
+ function walk2(command, dialect, depth) {
18090
18413
  const found = [];
18091
18414
  let dir = null;
18092
18415
  const groups = [];
18093
18416
  let stack = [];
18094
- for (const segment of segmentsOf(command, dialect)) {
18095
- if (segment.group === "open") {
18096
- groups.push({ dir, stack: [...stack] });
18097
- continue;
18098
- }
18099
- if (segment.group === "close") {
18100
- const saved = groups.pop();
18101
- if (saved)
18102
- ({ dir, stack } = saved);
18103
- continue;
18104
- }
18105
- const words = stripPrefixes(segment.words);
18417
+ const vars = new Map;
18418
+ const exported = {};
18419
+ const sessionConfig = [];
18420
+ const nested = (script, inner) => {
18421
+ if (!script || depth >= MAX_DEPTH)
18422
+ return;
18423
+ for (const invocation of rawInvocations(script, inner, depth + 1))
18424
+ found.push({ ...invocation, dir: rebase(dir, invocation.dir) });
18425
+ };
18426
+ const visit = (input, stdin, hops) => {
18427
+ const line = /^\$[\w:]+$/.test(input[0] ?? "") && input[1] === "=" ? input.slice(2) : input;
18428
+ const split = splitString(line);
18429
+ if (split !== null)
18430
+ return nested(split, "posix");
18431
+ const { words, env } = stripPrefixes(line);
18106
18432
  const name = commandName(words[0]);
18433
+ if (!name)
18434
+ return;
18435
+ const runs = (lines) => {
18436
+ if (hops < MAX_DEPTH)
18437
+ for (const next of lines)
18438
+ visit(next, undefined, hops + 1);
18439
+ };
18107
18440
  if (CD.has(name)) {
18108
18441
  const target = cdTarget(words);
18109
18442
  dir = target === null ? os5.homedir() : joinDir(dir, target);
18110
- continue;
18111
- }
18112
- if (PUSHD.has(name)) {
18443
+ } else if (PUSHD.has(name)) {
18113
18444
  const target = cdTarget(words);
18114
18445
  stack.push(dir);
18115
18446
  dir = target === null || target === "-" ? undefined : joinDir(dir, target);
18116
- continue;
18117
- }
18118
- if (POPD.has(name)) {
18447
+ } else if (POPD.has(name))
18119
18448
  dir = stack.length ? stack.pop() : undefined;
18120
- continue;
18449
+ else if (EVALS.has(name)) {
18450
+ const args = /^-c(?:o(?:m(?:m(?:a(?:n(?:d)?)?)?)?)?)?$/i.test(words[1] ?? "") ? words.slice(2) : words.slice(1);
18451
+ nested(args.join(" "), dialect);
18452
+ } else if (SHELLS.has(name))
18453
+ nested(shellScript(words, stdin), "posix");
18454
+ else if ((name === "source" || name === ".") && dialect === "posix")
18455
+ nested(processSubstitutionText(words[1]), "posix");
18456
+ else if (name === "trap" && dialect === "posix" && !/^-[lp]$/.test(words[1] ?? ""))
18457
+ nested(words[1], "posix");
18458
+ else if (name === "script")
18459
+ nested(scriptCommand(words), "posix");
18460
+ else if (name === "parallel")
18461
+ runs(parallelCommands(words));
18462
+ else if (dialect === "powershell" && /^(?:start-process|saps|start)$/.test(name)) {
18463
+ const started = startProcessCommand(words);
18464
+ if (started)
18465
+ runs([started]);
18466
+ } else if (POWERSHELLS.has(name))
18467
+ nested(powershellScript(words, stdin), "powershell");
18468
+ else if (name === "cmd")
18469
+ nested(cmdScript(words), "powershell");
18470
+ else if (name === "xargs" || name === "find")
18471
+ runs(name === "xargs" ? xargsCommands(words, stdin) : findCommands(words));
18472
+ else if (name === "gh" || name === "glab")
18473
+ found.push({
18474
+ tool: name,
18475
+ dir,
18476
+ args: words.slice(1),
18477
+ ...stdin === undefined ? {} : { stdin }
18478
+ });
18479
+ else if (name === "git") {
18480
+ const invocation = gitInvocation(words.slice(1), dir, { ...exported, ...env }, stdin, sessionConfig);
18481
+ found.push(invocation);
18482
+ sessionConfig.push(...configSetBy(invocation.args));
18121
18483
  }
18122
- if (SHELLS.has(name) && depth < 3) {
18123
- const flag = words.findIndex((word, index) => index > 0 && SCRIPT_FLAG.test(word));
18124
- const script = flag > 0 ? words[flag + 1] : undefined;
18125
- if (script)
18126
- for (const inner of rawInvocations(script, "posix", depth + 1))
18127
- found.push({
18128
- ...inner,
18129
- dir: inner.dir === null ? dir : inner.dir === undefined ? undefined : joinDir(dir, inner.dir)
18130
- });
18484
+ };
18485
+ const segments = segmentsOf(command, dialect);
18486
+ for (const [index, segment] of segments.entries()) {
18487
+ if (segment.group === "open") {
18488
+ groups.push({ dir, stack: [...stack] });
18131
18489
  continue;
18132
18490
  }
18133
- if (name === "gh" || name === "glab") {
18134
- found.push({ tool: name, dir, args: words.slice(1) });
18491
+ if (segment.group === "close") {
18492
+ const saved = groups.pop();
18493
+ if (saved)
18494
+ ({ dir, stack } = saved);
18135
18495
  continue;
18136
18496
  }
18137
- if (name !== "git")
18497
+ if (segment.unanalyzable)
18498
+ throw new ShellAnalysisError(segment.unanalyzable);
18499
+ for (const sub of segment.subs ?? [])
18500
+ nested(sub, "posix");
18501
+ const set = assignments(segment.words, vars);
18502
+ if (set) {
18503
+ for (const [name, values] of set) {
18504
+ vars.set(name, values);
18505
+ if (segment.words[0] === "export" && GIT_CONFIG_ENV.test(name) && values[0] !== undefined)
18506
+ exported[name] = values[0];
18507
+ }
18138
18508
  continue;
18139
- let here = dir;
18140
- let index = 1;
18141
- for (;index < words.length; index++) {
18142
- const word = words[index];
18143
- if (word === "-C")
18144
- here = words[index + 1] ? joinDir(here, words[++index]) : here;
18145
- else if (GIT_VALUE_OPTIONS.has(word))
18146
- index++;
18147
- else if (!word.startsWith("-"))
18148
- break;
18149
18509
  }
18150
- found.push({ tool: "git", dir: here, args: words.slice(index) });
18510
+ const stdin = stdinOf(segment, segments[index - 1]);
18511
+ for (const line of substitute(segment.words, vars))
18512
+ visit(line, stdin, 0);
18151
18513
  }
18152
18514
  return found;
18153
18515
  }
@@ -18170,20 +18532,40 @@ function workitInvocations(command, dialect = "posix", depth = 0) {
18170
18532
  }
18171
18533
  function rawAction(invocation) {
18172
18534
  const args = invocation.args;
18535
+ if (invocation.tool !== "git" && args[0] === "api")
18536
+ return apiMerges(invocation.tool, args, invocation.stdin) ? { kind: "merge", forge: invocation.tool, via: "api" } : null;
18173
18537
  if (asksHelp(invocation))
18174
18538
  return null;
18175
- const [first, second] = args;
18539
+ const [first] = args;
18176
18540
  if (invocation.tool === "git") {
18541
+ const second = args[1];
18177
18542
  if (first === "commit")
18178
18543
  return args.includes("--dry-run") ? null : { kind: "commit", amend: args.includes("--amend") };
18179
18544
  if (first === "push")
18180
18545
  return pushAction(args);
18546
+ if (first === "send-pack")
18547
+ return pushAction([
18548
+ "push",
18549
+ ...args.slice(1).filter((arg) => arg !== "--stdin"),
18550
+ ...args.includes("--stdin") ? (invocation.stdin ?? "").split(/\s+/).filter(Boolean) : []
18551
+ ]);
18552
+ if (first === "subtree" && second === "push")
18553
+ return subtreePush(args);
18181
18554
  return null;
18182
18555
  }
18183
18556
  const forge = invocation.tool;
18184
18557
  const noun = forge === "gh" ? "pr" : "mr";
18185
18558
  if (first !== noun)
18186
18559
  return null;
18560
+ const values = VALUE_OPTIONS[forge];
18561
+ let second;
18562
+ for (let index = 1;index < args.length && second === undefined; index++) {
18563
+ const arg = args[index];
18564
+ if (values.has(arg))
18565
+ index++;
18566
+ else if (!arg.startsWith("-"))
18567
+ second = arg;
18568
+ }
18187
18569
  if (second === "merge")
18188
18570
  return { kind: "merge", forge };
18189
18571
  if (second === "create")
@@ -18194,31 +18576,18 @@ function rawAction(invocation) {
18194
18576
  }
18195
18577
  function rawGitPre(input, command, options = {}) {
18196
18578
  try {
18197
- if (!mentionsRawTool(command))
18198
- return NONE2;
18199
18579
  const dialect = dialectOf(input);
18200
18580
  const nudges = new Map;
18201
18581
  let store = null;
18202
- for (const { action, repo } of actionsIn(input.cwd, command, dialect)) {
18582
+ const cache = new Map;
18583
+ for (const { action, repo, invocation } of actionsIn(input.cwd, command, dialect, cache)) {
18203
18584
  store ??= repo.location.dir;
18204
18585
  if (action.kind === "merge")
18205
18586
  return deny(refusal(action, null));
18206
- if (action.kind === "push" && toPushRemote(repo, action.remote)) {
18207
- if (action.mirror)
18208
- return deny(`workit: raw ${quote("git push --mirror")} rewrites and deletes every remote ref, protected branches and tags included. Push one branch with ${quote("workit git push")}.`);
18209
- for (const target of action.targets) {
18210
- const branch = target ?? repo.branch;
18211
- if (branch && guardedBranch(repo.dir, branch))
18212
- return deny(refusal(action, branch, repo.dir));
18213
- }
18214
- const remote = action.remote ?? pushRemoteName(repo.dir, repo.branch) ?? "origin";
18215
- const facts = refFacts(repo, remote);
18216
- const tagDeny = tagRefusal(action, facts);
18217
- if (tagDeny)
18218
- return deny(tagDeny);
18219
- const nameDeny = action.deletes ? null : offPolicyPush(action, repo, facts, remote);
18220
- if (nameDeny)
18221
- return deny(nameDeny);
18587
+ if (action.kind === "push") {
18588
+ const refused = pushRefusal(action, repo, gitConfig(repo.dir, invocation, cache));
18589
+ if (refused)
18590
+ return deny(refused);
18222
18591
  }
18223
18592
  const nudge = nudgeFor(action, input.host, input.session.id);
18224
18593
  if (nudge && !nudges.has(nudgeKind(action)))
@@ -18231,8 +18600,8 @@ function rawGitPre(input, command, options = {}) {
18231
18600
  const shown = unseenNudges(input, nudges, store);
18232
18601
  return shown.length ? { kind: "context", text: shown.join(`
18233
18602
  `) } : NONE2;
18234
- } catch {
18235
- return NONE2;
18603
+ } catch (error) {
18604
+ return error instanceof ShellAnalysisError ? analysisDenial(error) : NONE2;
18236
18605
  }
18237
18606
  }
18238
18607
  function noteRawCommit(input, command, key) {
@@ -18271,12 +18640,13 @@ function settlePendingCommit(input) {
18271
18640
  settle2(input, repo, file);
18272
18641
  } catch {}
18273
18642
  }
18274
- var NONE2, SHELLS, PREFIXES2, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, isAbsolute = (value) => path20.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\"), joinDir = (base, next) => {
18643
+ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLARATIONS, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, GIT_CONFIG_OPTIONS, GIT_CONFIG_ENV, ASSIGNMENT, MAX_DEPTH = 4, MAX_EXPANSIONS = 16, isAbsolute = (value) => path20.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\"), HERE, joinDir = (base, raw) => {
18644
+ const next = raw.replace(HERE, ".");
18275
18645
  if (base === undefined || /[$`*?]/.test(next) || next === "-")
18276
18646
  return;
18277
18647
  const expanded = next === "~" || next.startsWith("~/") ? path20.join(os5.homedir(), next.slice(1)) : next;
18278
18648
  return base === null || isAbsolute(expanded) ? expanded : path20.join(base, expanded);
18279
- }, cdTarget = (words) => {
18649
+ }, rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner), cdTarget = (words) => {
18280
18650
  const args = words.slice(1);
18281
18651
  const flag = args.findIndex((word) => /^-(?:literal)?path$/i.test(word));
18282
18652
  if (flag >= 0)
@@ -18284,25 +18654,328 @@ var NONE2, SHELLS, PREFIXES2, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18284
18654
  return args.find((word) => word === "-" || !word.startsWith("-")) ?? null;
18285
18655
  }, stripPrefixes = (input) => {
18286
18656
  let words = input;
18657
+ const env = {};
18287
18658
  for (;; ) {
18288
18659
  const first = words[0];
18289
18660
  if (first === undefined)
18290
- return words;
18291
- if (/^\w+=/.test(first))
18661
+ return { words, env };
18662
+ const assignment = ASSIGNMENT.exec(first);
18663
+ if (assignment) {
18664
+ if (GIT_CONFIG_ENV.test(assignment[1]))
18665
+ env[assignment[1]] = assignment[2];
18292
18666
  words = words.slice(1);
18293
- else if (PREFIXES2.has(first)) {
18667
+ } else if (KEYWORDS.has(first))
18294
18668
  words = words.slice(1);
18295
- while (words[0]?.startsWith("-"))
18296
- words = words.slice(words[0] === "-u" ? 2 : 1);
18297
- } else if (first === "timeout") {
18669
+ else if (first === "function")
18670
+ words = words.slice(2);
18671
+ else if (Object.hasOwn(WRAPPERS, commandName(first))) {
18672
+ const name = commandName(first);
18673
+ const values = WRAPPER_VALUES.get(name);
18298
18674
  words = words.slice(1);
18299
- while (words[0]?.startsWith("-"))
18675
+ const takesValue = (option) => values.has(option) || /^-[a-zA-Z]{2,}$/.test(option) && values.has(`-${option.at(-1)}`);
18676
+ while (words[0]?.startsWith("-") && words[0] !== "--")
18677
+ words = words.slice(takesValue(words[0]) ? 2 : 1);
18678
+ if (words[0] === "--")
18300
18679
  words = words.slice(1);
18301
- words = words.slice(1);
18680
+ words = words.slice(WRAPPERS[name].operands ?? 0);
18302
18681
  } else
18303
- return words;
18682
+ return { words, env };
18683
+ }
18684
+ }, commandName = (word) => (word?.split(/[\\/]/).at(-1) ?? "").toLowerCase().replace(/\.exe$/, ""), VARIABLE, expandWord = (word, vars) => {
18685
+ if (!word.includes("$"))
18686
+ return [word];
18687
+ let results = [""];
18688
+ let last = 0;
18689
+ for (const match of word.matchAll(VARIABLE)) {
18690
+ const values = vars.get(match[1] ?? match[2]) ?? [match[0]];
18691
+ const before = word.slice(last, match.index);
18692
+ results = results.flatMap((result) => values.map((value) => result + before + value)).slice(0, MAX_EXPANSIONS);
18693
+ last = match.index + match[0].length;
18694
+ }
18695
+ return results.map((result) => result + word.slice(last));
18696
+ }, substitute = (words, vars) => {
18697
+ if (vars.size === 0)
18698
+ return [words];
18699
+ let lines = [[]];
18700
+ for (const word of words) {
18701
+ const values = expandWord(word, vars);
18702
+ lines = lines.flatMap((line) => values.map((value) => [...line, value])).slice(0, MAX_EXPANSIONS);
18703
+ }
18704
+ return lines;
18705
+ }, assignments = (words, vars) => {
18706
+ if (words[0] === "for" && words[2] === "in" && /^\w+$/.test(words[1] ?? "")) {
18707
+ const values = words.slice(3).flatMap((word) => expandWord(word, vars));
18708
+ return [[words[1], values.slice(0, MAX_EXPANSIONS)]];
18709
+ }
18710
+ let rest = words;
18711
+ if (DECLARATIONS.has(rest[0] ?? "")) {
18712
+ rest = rest.slice(1);
18713
+ while (rest[0]?.startsWith("-"))
18714
+ rest = rest.slice(1);
18715
+ }
18716
+ if (rest.length === 0 || !rest.every((word) => ASSIGNMENT.test(word)))
18717
+ return null;
18718
+ return rest.map((word) => {
18719
+ const [, name, value] = ASSIGNMENT.exec(word);
18720
+ return [name, expandWord(value, vars)];
18721
+ });
18722
+ }, literalOutput = (segment) => {
18723
+ if (!segment?.pipe)
18724
+ return;
18725
+ const { words } = stripPrefixes(segment.words);
18726
+ const name = commandName(words[0]);
18727
+ if (name === "echo") {
18728
+ let args = words.slice(1);
18729
+ while (/^-[neE]+$/.test(args[0] ?? ""))
18730
+ args = args.slice(1);
18731
+ return args.join(" ");
18732
+ }
18733
+ if (name === "printf") {
18734
+ const [format = "", ...args] = words.slice(1);
18735
+ return (args.length ? args.join(`
18736
+ `) : format).replace(/\\n/g, `
18737
+ `);
18738
+ }
18739
+ if (name === "cat" && words.length === 1 && segment.stdin)
18740
+ return segment.stdin.join(`
18741
+ `);
18742
+ return;
18743
+ }, stdinOf = (segment, previous) => segment.stdin ? segment.stdin.join(`
18744
+ `) : literalOutput(previous), XARGS_VALUES, xargsCommands = (words, input) => {
18745
+ let replace = null;
18746
+ let index = 1;
18747
+ for (;index < words.length; index++) {
18748
+ const word = words[index];
18749
+ if (word === "--") {
18750
+ index++;
18751
+ break;
18752
+ }
18753
+ if (!word.startsWith("-"))
18754
+ break;
18755
+ if (word === "-I")
18756
+ replace = words[++index] ?? "{}";
18757
+ else if (word === "--replace" || word === "-i")
18758
+ replace = "{}";
18759
+ else if (word.startsWith("--replace="))
18760
+ replace = word.slice("--replace=".length);
18761
+ else if (/^-[Ii]./.test(word))
18762
+ replace = word.slice(2);
18763
+ else if (XARGS_VALUES.has(word))
18764
+ index++;
18765
+ }
18766
+ const command = words.slice(index);
18767
+ if (command.length === 0)
18768
+ return [];
18769
+ if (input === undefined)
18770
+ return [command];
18771
+ if (replace === null)
18772
+ return [[...command, ...input.split(/\s+/).filter(Boolean)]];
18773
+ const token = replace;
18774
+ return input.split(`
18775
+ `).map((line) => line.trim()).filter(Boolean).slice(0, MAX_EXPANSIONS).map((line) => command.map((word) => word.replaceAll(token, line)));
18776
+ }, findCommands = (words) => {
18777
+ const found = [];
18778
+ for (let index = 1;index < words.length; index++) {
18779
+ if (!/^-(?:exec|execdir|ok|okdir)$/.test(words[index]))
18780
+ continue;
18781
+ const end = words.findIndex((word, at) => at > index && (word === ";" || word === "+"));
18782
+ found.push(words.slice(index + 1, end < 0 ? undefined : end));
18783
+ if (end < 0)
18784
+ break;
18785
+ index = end;
18786
+ }
18787
+ return found;
18788
+ }, POWERSHELL_VALUES, powershellScript = (words, stdin) => {
18789
+ for (let index = 1;index < words.length; index++) {
18790
+ const word = words[index];
18791
+ if (!/^[-/]/.test(word))
18792
+ return null;
18793
+ const flag = word.replace(/^[-/]+/, "").toLowerCase();
18794
+ if (!flag)
18795
+ return null;
18796
+ if (flag === "e" || flag === "ec" || flag.length >= 2 && "encodedcommand".startsWith(flag)) {
18797
+ try {
18798
+ return Buffer.from(words[index + 1] ?? "", "base64").toString("utf16le");
18799
+ } catch {
18800
+ return null;
18801
+ }
18802
+ }
18803
+ if (flag === "cwa" || flag.length >= 8 && "commandwithargs".startsWith(flag))
18804
+ return words[index + 1] ?? null;
18805
+ if ("command".startsWith(flag))
18806
+ return words[index + 1] === "-" ? stdin ?? null : words.slice(index + 1).join(" ");
18807
+ if (POWERSHELL_VALUES.test(`-${flag}`)) {
18808
+ if (flag === "f" || flag === "file")
18809
+ return null;
18810
+ index++;
18811
+ }
18812
+ }
18813
+ return null;
18814
+ }, cmdScript = (words) => {
18815
+ const at = words.findIndex((word, index) => index > 0 && /^\/[ck]$/i.test(word));
18816
+ if (at > 0)
18817
+ return words.slice(at + 1).join(" ");
18818
+ const attached = words.find((word, index) => index > 0 && /^\/[ck]./i.test(word));
18819
+ return attached ? [attached.slice(2), ...words.slice(words.indexOf(attached) + 1)].join(" ") : null;
18820
+ }, processSubstitutionText = (word) => {
18821
+ const inner = /^<\(([\s\S]*)\)$/.exec(word ?? "")?.[1];
18822
+ if (inner === undefined)
18823
+ return;
18824
+ const segments = segmentsOf(inner);
18825
+ return segments.length === 1 ? literalOutput({ ...segments[0], pipe: true }) : undefined;
18826
+ }, shellScript = (words, stdin) => {
18827
+ const flag = words.findIndex((word, index) => index > 0 && SCRIPT_FLAG.test(word));
18828
+ if (flag > 0)
18829
+ return words[flag + 1] === "--" ? words[flag + 2] : words[flag + 1];
18830
+ const operand = words.slice(1).find((word) => !word.startsWith("-"));
18831
+ if (operand !== undefined)
18832
+ return processSubstitutionText(operand);
18833
+ return stdin;
18834
+ }, scriptCommand = (words) => {
18835
+ for (let index = 1;index < words.length; index++) {
18836
+ const word = words[index];
18837
+ if (word === "--command" || /^-[a-zA-Z]*c$/.test(word))
18838
+ return words[index + 1];
18839
+ if (word.startsWith("--command="))
18840
+ return word.slice("--command=".length);
18841
+ }
18842
+ return;
18843
+ }, PARALLEL_VALUES, parallelCommands = (words) => {
18844
+ let index = 1;
18845
+ while (index < words.length && words[index].startsWith("-"))
18846
+ index += PARALLEL_VALUES.has(words[index]) ? 2 : 1;
18847
+ const separator = words.indexOf(":::", index);
18848
+ const command = words.slice(index, separator < 0 ? undefined : separator);
18849
+ if (separator < 0)
18850
+ return command.length ? [command] : [];
18851
+ return words.slice(separator + 1).filter((arg) => !arg.startsWith(":::")).slice(0, MAX_EXPANSIONS).map((arg) => command.some((word) => word.includes("{}")) ? command.map((word) => word.replaceAll("{}", arg)) : [...command, arg]);
18852
+ }, START_PROCESS_VALUES, startProcessCommand = (words) => {
18853
+ let file;
18854
+ let args;
18855
+ const positionals = [];
18856
+ for (let index = 1;index < words.length; index++) {
18857
+ const word = words[index];
18858
+ const flag = word.startsWith("-") ? word.slice(1).toLowerCase() : null;
18859
+ if (flag === null)
18860
+ positionals.push(word);
18861
+ else if (flag && "filepath".startsWith(flag))
18862
+ file = words[++index];
18863
+ else if (flag && (flag === "args" || "argumentlist".startsWith(flag)))
18864
+ args = words[++index];
18865
+ else if (START_PROCESS_VALUES.test(word))
18866
+ index++;
18867
+ }
18868
+ file ??= positionals.shift();
18869
+ args ??= positionals.shift();
18870
+ if (!file)
18871
+ return null;
18872
+ return [file, ...(args ?? "").split(/[\s,]+/).filter(Boolean)];
18873
+ }, splitString = (line) => {
18874
+ if (commandName(line[0]) !== "env")
18875
+ return null;
18876
+ for (let index = 1;index < line.length; index++) {
18877
+ const word = line[index];
18878
+ if (word === "-S" || word === "--split-string")
18879
+ return line.slice(index + 1).join(" ");
18880
+ if (word.startsWith("--split-string="))
18881
+ return line.slice(index).join(" ").slice(15);
18882
+ if (/^-S./.test(word))
18883
+ return [word.slice(2), ...line.slice(index + 1)].join(" ");
18884
+ if (!word.startsWith("-") && !ASSIGNMENT.test(word))
18885
+ return null;
18304
18886
  }
18305
- }, commandName = (word) => (word?.split(/[\\/]/).at(-1) ?? "").toLowerCase().replace(/\.exe$/, ""), LAUNCHERS, WORKIT_BIN, PUSH_VALUE_OPTIONS, pushAction = (args) => {
18887
+ return null;
18888
+ }, CONFIG_READS, configSetBy = (args) => {
18889
+ const positionals = args.filter((arg, index) => index > 0 && !arg.startsWith("-"));
18890
+ if (args[0] === "remote" && (positionals[0] === "add" || positionals[0] === "set-url")) {
18891
+ const [, name, url] = positionals;
18892
+ const key = args.includes("--push") ? "pushurl" : "url";
18893
+ return name && url ? ["-c", `remote.${name}.${key}=${url}`] : [];
18894
+ }
18895
+ if (args[0] !== "config" || args.some((arg) => CONFIG_READS.test(arg)))
18896
+ return [];
18897
+ const [key, value] = positionals[0] === "set" ? positionals.slice(1) : positionals;
18898
+ return key?.includes(".") && value !== undefined && positionals.length <= 3 ? ["-c", `${key}=${value}`] : [];
18899
+ }, gitInvocation = (words, dir, env = {}, stdin, inherited = []) => {
18900
+ let here = dir;
18901
+ const config = [...inherited];
18902
+ let gitDir = env.GIT_DIR;
18903
+ let index = 0;
18904
+ for (;index < words.length; index++) {
18905
+ const word = words[index];
18906
+ if (word === "-C")
18907
+ here = words[index + 1] ? joinDir(here, words[++index]) : here;
18908
+ else if (GIT_CONFIG_OPTIONS.has(word)) {
18909
+ if (word === "--git-dir")
18910
+ gitDir = words[index + 1];
18911
+ if (words[index + 1] !== undefined)
18912
+ config.push(word, words[++index]);
18913
+ } else if (/^--(?:config-env|git-dir)=/.test(word)) {
18914
+ if (word.startsWith("--git-dir="))
18915
+ gitDir = word.slice("--git-dir=".length);
18916
+ config.push(word);
18917
+ } else if (GIT_VALUE_OPTIONS.has(word))
18918
+ index++;
18919
+ else if (!word.startsWith("-"))
18920
+ break;
18921
+ }
18922
+ if (gitDir)
18923
+ here = joinDir(here, gitDir.replace(/(?:^|[\\/]+)\.git[\\/]*$/, "") || ".");
18924
+ return {
18925
+ tool: "git",
18926
+ dir: here,
18927
+ args: words.slice(index),
18928
+ ...config.length ? { config } : {},
18929
+ ...Object.keys(env).length ? { env } : {},
18930
+ ...stdin === undefined ? {} : { stdin }
18931
+ };
18932
+ }, RECENT, LAUNCHERS, WORKIT_BIN, PUSH_VALUE_OPTIONS, CURRENT_BRANCH, pushDestination = (destination) => {
18933
+ if (destination === "HEAD" || destination === "@" || CURRENT_BRANCH.test(destination))
18934
+ return { name: null };
18935
+ if (destination.startsWith("refs/heads/"))
18936
+ return { name: destination.slice(11) };
18937
+ if (destination.startsWith("refs/tags/"))
18938
+ return { name: undefined, tag: destination.slice(10) };
18939
+ if (destination.startsWith("refs/"))
18940
+ return { name: undefined };
18941
+ return { name: destination };
18942
+ }, pushRefspecs = (specs) => {
18943
+ let force = false;
18944
+ let matching = false;
18945
+ const targets = [];
18946
+ const tags = [];
18947
+ const deleted = [];
18948
+ const globs = [];
18949
+ for (const raw of specs) {
18950
+ if (raw.startsWith("+"))
18951
+ force = true;
18952
+ const spec = raw.replace(/^\+/, "");
18953
+ if (spec.startsWith("^"))
18954
+ continue;
18955
+ if (spec === ":") {
18956
+ matching = true;
18957
+ continue;
18958
+ }
18959
+ const colon = spec.indexOf(":");
18960
+ const source = colon >= 0 ? spec.slice(0, colon) : spec;
18961
+ const destination = colon >= 0 ? spec.slice(colon + 1) : spec;
18962
+ if (!destination)
18963
+ continue;
18964
+ if (destination.includes("*")) {
18965
+ globs.push({ source, destination });
18966
+ continue;
18967
+ }
18968
+ const { name, tag } = pushDestination(destination);
18969
+ if (tag !== undefined)
18970
+ tags.push(tag);
18971
+ if (name === undefined)
18972
+ continue;
18973
+ targets.push(name);
18974
+ if (colon === 0 && name !== null)
18975
+ deleted.push(name);
18976
+ }
18977
+ return { force, matching, targets, tags, deleted, globs };
18978
+ }, pushAction = (args) => {
18306
18979
  let force = false;
18307
18980
  let lease = false;
18308
18981
  let wholesale = false;
@@ -18311,6 +18984,7 @@ var NONE2, SHELLS, PREFIXES2, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18311
18984
  let allBranches = false;
18312
18985
  let mirror = false;
18313
18986
  let remote = null;
18987
+ let dryRun = false;
18314
18988
  const positionals = [];
18315
18989
  for (let index = 1;index < args.length; index++) {
18316
18990
  const arg = args[index];
@@ -18318,9 +18992,11 @@ var NONE2, SHELLS, PREFIXES2, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18318
18992
  positionals.push(...args.slice(index + 1));
18319
18993
  break;
18320
18994
  }
18321
- if (arg === "--dry-run" || /^-[a-zA-Z]*n[a-zA-Z]*$/.test(arg))
18322
- return null;
18323
- if (arg === "--repo")
18995
+ if (/^--dr(?:y(?:-(?:r(?:un?)?)?)?)?$/.test(arg) || /^-[a-zA-Z]*n[a-zA-Z]*$/.test(arg))
18996
+ dryRun = true;
18997
+ else if (/^--no-dr(?:y(?:-(?:r(?:un?)?)?)?)?$/.test(arg))
18998
+ dryRun = false;
18999
+ else if (arg === "--repo")
18324
19000
  remote = args[++index] ?? null;
18325
19001
  else if (arg.startsWith("--repo="))
18326
19002
  remote = arg.slice("--repo=".length);
@@ -18341,41 +19017,28 @@ var NONE2, SHELLS, PREFIXES2, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18341
19017
  else if (!arg.startsWith("-"))
18342
19018
  positionals.push(arg);
18343
19019
  }
19020
+ if (dryRun)
19021
+ return null;
18344
19022
  const specs = remote === null ? positionals.slice(1) : positionals;
18345
19023
  remote ??= positionals[0] ?? null;
18346
- const targets = [];
18347
- const tags = [];
18348
- const deleted = [];
18349
- for (const raw of specs) {
18350
- if (raw.startsWith("+"))
18351
- force = true;
18352
- const spec = raw.replace(/^\+/, "");
18353
- const colon = spec.indexOf(":");
18354
- const destination = colon >= 0 ? spec.slice(colon + 1) : spec;
18355
- if (!destination)
18356
- continue;
18357
- const name = destination === "HEAD" ? null : destination.startsWith("refs/heads/") ? destination.slice(11) : destination.startsWith("refs/") ? undefined : destination;
18358
- if (destination.startsWith("refs/tags/"))
18359
- tags.push(destination.slice(10));
18360
- if (name === undefined)
18361
- continue;
18362
- targets.push(name);
18363
- if (colon === 0 && name !== null)
18364
- deleted.push(name);
18365
- }
18366
- if (specs.length === 0 && !wholesale)
18367
- targets.push(null);
19024
+ const parsed = pushRefspecs(specs);
19025
+ const implicit = specs.length === 0 && !wholesale;
19026
+ if (implicit)
19027
+ parsed.targets.push(null);
18368
19028
  return {
18369
19029
  kind: "push",
18370
- targets,
18371
- blindForce: force && !lease,
19030
+ targets: parsed.targets,
19031
+ blindForce: (force || parsed.force) && !lease,
18372
19032
  remote,
18373
- ...tags.length ? { tags } : {},
19033
+ ...parsed.tags.length ? { tags: parsed.tags } : {},
18374
19034
  ...allTags ? { allTags: true } : {},
18375
19035
  ...deletes ? { deletes: true } : {},
18376
- ...deleted.length ? { deleted } : {},
19036
+ ...parsed.deleted.length ? { deleted: parsed.deleted } : {},
18377
19037
  ...allBranches ? { allBranches: true } : {},
18378
- ...mirror ? { mirror: true } : {}
19038
+ ...mirror ? { mirror: true } : {},
19039
+ ...implicit ? { implicit: true } : {},
19040
+ ...parsed.globs.length ? { globs: parsed.globs } : {},
19041
+ ...parsed.matching ? { matching: true } : {}
18379
19042
  };
18380
19043
  }, VALUE_OPTIONS, asksHelp = (invocation) => {
18381
19044
  const { tool, args } = invocation;
@@ -18392,6 +19055,59 @@ var NONE2, SHELLS, PREFIXES2, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18392
19055
  index++;
18393
19056
  }
18394
19057
  return false;
19058
+ }, API_VALUES, API_SHORT_VALUES = "XHfFqtp", API_FIELDS, API_MERGE, GRAPHQL_MERGE, apiMerges = (tool, args, stdin) => {
19059
+ const request = { method: null, body: false };
19060
+ let endpoint = null;
19061
+ const values = [];
19062
+ const take = (name, value) => {
19063
+ if (name === "-X" || name === "--method")
19064
+ request.method = value.toUpperCase();
19065
+ if (API_FIELDS.has(name) || name === "--input")
19066
+ request.body = true;
19067
+ values.push(value);
19068
+ const fromStdin = name === "--input" ? value === "-" : /^(?:[^=]*=)?@-$/.test(value);
19069
+ if (fromStdin && stdin !== undefined)
19070
+ values.push(stdin);
19071
+ };
19072
+ for (let index = 1;index < args.length; index++) {
19073
+ const arg = args[index];
19074
+ if (arg === "--") {
19075
+ endpoint ??= args[index + 1] ?? null;
19076
+ break;
19077
+ }
19078
+ if (arg.startsWith("--")) {
19079
+ const eq = arg.indexOf("=");
19080
+ const name = eq > 0 ? arg.slice(0, eq) : arg;
19081
+ if (API_VALUES.has(name))
19082
+ take(name, eq > 0 ? arg.slice(eq + 1) : args[++index] ?? "");
19083
+ } else if (arg.startsWith("-") && arg.length > 1) {
19084
+ for (let at = 1;at < arg.length; at++) {
19085
+ if (!API_SHORT_VALUES.includes(arg[at]))
19086
+ continue;
19087
+ take(`-${arg[at]}`, at + 1 < arg.length ? arg.slice(at + 1) : args[++index] ?? "");
19088
+ break;
19089
+ }
19090
+ } else
19091
+ endpoint ??= arg;
19092
+ }
19093
+ if (!endpoint)
19094
+ return false;
19095
+ endpoint = endpoint.replace(/(?<!:)\/{2,}/g, "/");
19096
+ if (/(?:^|\/)graphql\/?$/i.test(endpoint))
19097
+ return values.some((value) => GRAPHQL_MERGE.test(value));
19098
+ const verb = request.method ?? (request.body ? "POST" : "GET");
19099
+ return verb !== "GET" && verb !== "HEAD" && API_MERGE[tool].test(endpoint);
19100
+ }, subtreePush = (args) => {
19101
+ const positionals = [];
19102
+ for (let index = 2;index < args.length; index++) {
19103
+ const arg = args[index];
19104
+ if (arg === "-P" || arg === "--prefix" || arg === "-m" || arg === "--message")
19105
+ index++;
19106
+ else if (!arg.startsWith("-"))
19107
+ positionals.push(arg);
19108
+ }
19109
+ const [remote, branch] = positionals;
19110
+ return remote && branch ? pushAction(["push", remote, `HEAD:refs/heads/${branch}`]) : null;
18395
19111
  }, repoAt = (dir) => {
18396
19112
  const location = resolveStore(dir);
18397
19113
  if (location instanceof Error || !location.shared)
@@ -18412,15 +19128,6 @@ var NONE2, SHELLS, PREFIXES2, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18412
19128
  } catch {
18413
19129
  return false;
18414
19130
  }
18415
- }, toPushRemote = (repo, remote) => {
18416
- if (remote === null)
18417
- return true;
18418
- try {
18419
- const pushRemote = pushRemoteName(repo.dir, repo.branch);
18420
- return pushRemote === null || pushRemote === remote;
18421
- } catch {
18422
- return false;
18423
- }
18424
19131
  }, headSha2 = (repo) => {
18425
19132
  const git = repo.location.git;
18426
19133
  if (!git || git.reftable)
@@ -18441,9 +19148,141 @@ var NONE2, SHELLS, PREFIXES2, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18441
19148
  } catch {
18442
19149
  return null;
18443
19150
  }
19151
+ }, gitConfig = (dir, invocation, cache) => {
19152
+ const key = JSON.stringify([dir, invocation.config ?? [], invocation.env ?? {}]);
19153
+ const cached = cache.get(key);
19154
+ if (cached)
19155
+ return cached;
19156
+ const config = new Map;
19157
+ const listed = spawnSync11("git", [...invocation.config ?? [], "config", "-z", "--list"], {
19158
+ cwd: dir,
19159
+ encoding: "utf8",
19160
+ timeout: 3000,
19161
+ windowsHide: true,
19162
+ env: { ...process.env, ...invocation.env, GIT_TERMINAL_PROMPT: "0", GIT_OPTIONAL_LOCKS: "0" }
19163
+ });
19164
+ for (const entry of (listed.status === 0 ? listed.stdout : "").split("\x00")) {
19165
+ if (!entry)
19166
+ continue;
19167
+ const newline = entry.indexOf(`
19168
+ `);
19169
+ const name = newline < 0 ? entry : entry.slice(0, newline);
19170
+ config.set(name, [
19171
+ ...config.get(name) ?? [],
19172
+ newline < 0 ? "true" : entry.slice(newline + 1)
19173
+ ]);
19174
+ }
19175
+ cache.set(key, config);
19176
+ return config;
19177
+ }, configValue = (config, key) => config.get(key)?.at(-1), GIT_COMMANDS, JUDGED_COMMANDS, editDistance2 = (a, b) => {
19178
+ let before = [];
19179
+ let previous = Array.from({ length: b.length + 1 }, (_, index) => index);
19180
+ for (let i = 1;i <= a.length; i++) {
19181
+ const row = [i];
19182
+ for (let j = 1;j <= b.length; j++) {
19183
+ row[j] = Math.min(previous[j] + 1, row[j - 1] + 1, previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
19184
+ if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1])
19185
+ row[j] = Math.min(row[j], before[j - 2] + 1);
19186
+ }
19187
+ [before, previous] = [previous, row];
19188
+ }
19189
+ return previous[b.length];
19190
+ }, autocorrected = (name, config) => {
19191
+ const setting = (configValue(config, "help.autocorrect") ?? "0").toLowerCase();
19192
+ if (setting !== "immediate" && !/^-?[1-9]\d*$/.test(setting))
19193
+ return null;
19194
+ let best = null;
19195
+ let bestDistance = 3;
19196
+ let tie = false;
19197
+ for (const command of JUDGED_COMMANDS) {
19198
+ const distance = editDistance2(name, command);
19199
+ if (distance < bestDistance)
19200
+ [best, bestDistance, tie] = [command, distance, false];
19201
+ else if (distance === bestDistance)
19202
+ tie = true;
19203
+ }
19204
+ return tie ? null : best;
19205
+ }, shellQuote = (word) => `'${word.replaceAll("'", "'\\''")}'`, positional = (script, args) => {
19206
+ const word = (arg) => /^[\w./@+:=-]+$/.test(arg) ? arg : shellQuote(arg);
19207
+ return script.replace(/\$\{?([1-9])\}?/g, (_, n) => word(args[Number(n) - 1] ?? "")).replace(/\$\{?[@*]\}?/g, args.map(word).join(" "));
19208
+ }, expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
19209
+ const sub = invocation.args[0];
19210
+ if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
19211
+ return [invocation];
19212
+ const dir = path20.resolve(root, invocation.dir ?? ".");
19213
+ if (!fs16.existsSync(dir))
19214
+ return [invocation];
19215
+ const config = gitConfig(dir, invocation, cache);
19216
+ const alias = configValue(config, `alias.${sub.toLowerCase()}`);
19217
+ if (alias === undefined) {
19218
+ const corrected = autocorrected(sub, config);
19219
+ return corrected ? [{ ...invocation, args: [corrected, ...invocation.args.slice(1)] }] : [invocation];
19220
+ }
19221
+ const rest = invocation.args.slice(1);
19222
+ const expanded = alias.startsWith("!") ? rawInvocations([positional(alias.slice(1), rest), ...rest.map(shellQuote)].join(" ")).map((inner) => ({
19223
+ ...inner,
19224
+ dir: rebase(invocation.dir, inner.dir),
19225
+ ...inner.tool === "git" && (invocation.config || inner.config) ? { config: [...invocation.config ?? [], ...inner.config ?? []] } : {},
19226
+ ...inner.tool === "git" && (invocation.env || inner.env) ? { env: { ...invocation.env, ...inner.env } } : {}
19227
+ })) : [
19228
+ gitInvocation([...segmentsOf(alias)[0]?.words ?? [], ...rest], invocation.dir, invocation.env, invocation.stdin, invocation.config)
19229
+ ];
19230
+ return expanded.flatMap((inner) => expandGitAliases(root, inner, cache, depth + 1));
19231
+ }, normalizedUrl = (url, dir) => {
19232
+ const trimmed = url.trim();
19233
+ if (/^file:\/\//i.test(trimmed))
19234
+ return path20.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
19235
+ const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
19236
+ const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
19237
+ if (!scheme && !scp)
19238
+ return path20.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
19239
+ const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
19240
+ return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
19241
+ }, rewrittenUrl = (url, config) => {
19242
+ for (const kind of ["pushinsteadof", "insteadof"]) {
19243
+ let best = null;
19244
+ for (const [key, prefixes] of config) {
19245
+ if (!key.startsWith("url.") || !key.endsWith(`.${kind}`))
19246
+ continue;
19247
+ const base = key.slice(4, -(kind.length + 1));
19248
+ for (const prefix of prefixes)
19249
+ if (url.startsWith(prefix) && prefix.length > (best?.prefix.length ?? -1))
19250
+ best = { base, prefix };
19251
+ }
19252
+ if (best)
19253
+ return best.base + url.slice(best.prefix.length);
19254
+ }
19255
+ return url;
19256
+ }, pushUrl = (config, remote, dir) => {
19257
+ const configured = configValue(config, `remote.${remote}.pushurl`) ?? configValue(config, `remote.${remote}.url`);
19258
+ return normalizedUrl(rewrittenUrl(configured ?? remote, config), dir);
19259
+ }, pushRemoteOf = (config, branch) => {
19260
+ const remotes = new Set;
19261
+ for (const key of config.keys()) {
19262
+ const name = /^remote\.(.+)\.url$/.exec(key)?.[1];
19263
+ if (name)
19264
+ remotes.add(name);
19265
+ }
19266
+ for (const candidate of [
19267
+ branch ? configValue(config, `branch.${branch}.pushremote`) : undefined,
19268
+ configValue(config, "remote.pushdefault"),
19269
+ branch ? configValue(config, `branch.${branch}.remote`) : undefined
19270
+ ])
19271
+ if (candidate && candidate !== "." && remotes.has(candidate))
19272
+ return candidate;
19273
+ if (remotes.has("origin"))
19274
+ return "origin";
19275
+ return remotes.size === 1 ? [...remotes][0] : null;
19276
+ }, toPushRemote = (repo, remote, config) => {
19277
+ if (remote === null || /[$`]/.test(remote))
19278
+ return true;
19279
+ const pushRemote = pushRemoteOf(config, repo.branch);
19280
+ if (pushRemote === null || pushRemote === remote)
19281
+ return true;
19282
+ return pushUrl(config, remote, repo.dir) === pushUrl(config, pushRemote, repo.dir);
18444
19283
  }, quote = (text) => `\`${text}\``, refusal = (action, branch, dir) => {
18445
19284
  if (action.kind === "merge")
18446
- return `workit: raw ${quote(action.forge === "gh" ? "gh pr merge" : "glab mr merge")} bypasses the merge grant and the verdict gate. Run ${quote("workit pr merge")} instead (it checks both; ${quote("workit help pr")}).`;
19285
+ return `workit: raw ${quote(action.via === "api" ? `${action.forge} api` : action.forge === "gh" ? "gh pr merge" : "glab mr merge")}${action.via === "api" ? " merge" : ""} bypasses the merge grant and the verdict gate. Run ${quote("workit pr merge")} instead (it checks both; ${quote("workit help pr")}).`;
18447
19286
  const plainPush = action.kind === "push" && !action.blindForce && !action.deletes;
18448
19287
  if (dir && branch && plainPush && releaseBranch(dir, branch))
18449
19288
  return `workit: raw ${quote("git push")} onto ${branch}, a protected release branch, bypasses workit's protected-branch check. To finish a release or hotfix, push it with ${quote(`workit git push --release ${branch} [--release v<version>]`)}: it pushes ${branch} (fast-forward only) and the release tag, verifies the remote and records it, under the ${quote("release")} grant (if refused, ask the user to run ${quote("workit grant set <workspace> release=true")}). Feature work goes through ${quote("workit git push")} and ${quote("workit pr merge")}.`;
@@ -18462,6 +19301,70 @@ var NONE2, SHELLS, PREFIXES2, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18462
19301
  facts.remoteHeads.add(ref.slice(remotePrefix.length));
18463
19302
  }
18464
19303
  return facts;
19304
+ }, globBranches = (repo, glob) => {
19305
+ const source = glob.source.startsWith("refs/") ? glob.source : `refs/heads/${glob.source}`;
19306
+ const [sourcePrefix, sourceSuffix = ""] = source.split("*");
19307
+ const [destinationPrefix, destinationSuffix = ""] = glob.destination.split("*");
19308
+ const listed = spawnSync11("git", [
19309
+ "for-each-ref",
19310
+ "--format=%(refname)",
19311
+ sourcePrefix.slice(0, sourcePrefix.lastIndexOf("/") + 1)
19312
+ ], { cwd: repo.dir, encoding: "utf8", timeout: 5000, windowsHide: true });
19313
+ const names = [];
19314
+ for (const ref of (listed.stdout ?? "").split(`
19315
+ `)) {
19316
+ if (!ref.startsWith(sourcePrefix) || !ref.endsWith(sourceSuffix))
19317
+ continue;
19318
+ const middle = ref.slice(sourcePrefix.length, ref.length - sourceSuffix.length);
19319
+ const { name } = pushDestination(destinationPrefix + middle + destinationSuffix);
19320
+ if (name)
19321
+ names.push(name);
19322
+ }
19323
+ return names;
19324
+ }, pushedBranches = (action, repo, config, facts, remote) => {
19325
+ const targets = action.implicit ? [] : [...action.targets];
19326
+ const globs = [...action.globs ?? []];
19327
+ let matching = action.matching === true;
19328
+ const configured = action.implicit ? config.get(`remote.${remote}.push`) : undefined;
19329
+ if (configured?.length) {
19330
+ const parsed = pushRefspecs(configured);
19331
+ targets.push(...parsed.targets);
19332
+ globs.push(...parsed.globs);
19333
+ matching ||= parsed.matching;
19334
+ } else if (action.implicit) {
19335
+ const mode = (configValue(config, "push.default") ?? "simple").toLowerCase();
19336
+ const merge = repo.branch ? configValue(config, `branch.${repo.branch}.merge`) : undefined;
19337
+ if (mode === "matching")
19338
+ matching = true;
19339
+ else if ((mode === "upstream" || mode === "tracking") && merge?.startsWith("refs/heads/"))
19340
+ targets.push(merge.slice(11));
19341
+ else if (mode !== "nothing")
19342
+ targets.push(null);
19343
+ }
19344
+ if (action.allBranches)
19345
+ targets.push(...facts.heads);
19346
+ if (matching) {
19347
+ for (const head of facts.heads)
19348
+ if (facts.remoteHeads.has(head))
19349
+ targets.push(head);
19350
+ }
19351
+ for (const glob of globs)
19352
+ targets.push(...globBranches(repo, glob));
19353
+ return targets;
19354
+ }, literalReadings = (name) => {
19355
+ if (!/[$`]/.test(name))
19356
+ return [name];
19357
+ const withDefaults = name.replace(/\$\{\w+:?[-=]([^}$`]*)\}/g, "$1").replace(/\$\{\w+\}|\$\w+/g, "");
19358
+ return /[$`]/.test(withDefaults) || !withDefaults ? [] : [withDefaults];
19359
+ }, guardedTarget = (dir, target) => {
19360
+ for (const branch of literalReadings(target)) {
19361
+ if (guardedBranch(dir, branch))
19362
+ return branch;
19363
+ const bare = branch.startsWith("heads/") ? branch.slice(6) : null;
19364
+ if (bare && guardedBranch(dir, bare))
19365
+ return bare;
19366
+ }
19367
+ return null;
18465
19368
  }, isTag = (facts, name) => facts.tags.has(name) && !facts.heads.has(name), tagRefusal = (action, facts) => {
18466
19369
  const tags = [
18467
19370
  ...action.tags ?? [],
@@ -18471,23 +19374,31 @@ var NONE2, SHELLS, PREFIXES2, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18471
19374
  return null;
18472
19375
  const named = tags.length ? tags.join(" --release ") : "v<version>";
18473
19376
  return `workit: raw ${quote(`git push${action.allTags ? " --tags" : ""}`)} of ${tags.length ? `tag ${tags.join(", ")}` : "tags"} bypasses the release grant. Push a release tag with ${quote(`workit git push --release ${named}`)} (v<version> tags whose commit the production branch already has; a tag never moves), under the ${quote("release")} grant (if refused, ask the user to run ${quote("workit grant set <workspace> release=true")}).`;
18474
- }, remoteHas = (repo, remote, branch) => {
18475
- const listed = spawnSync11("git", ["ls-remote", "--heads", remote, `refs/heads/${branch}`], {
19377
+ }, remoteHeads = (repo, remote) => {
19378
+ const listed = spawnSync11("git", ["ls-remote", "--heads", remote], {
18476
19379
  cwd: repo.dir,
18477
19380
  encoding: "utf8",
18478
19381
  timeout: 3000,
19382
+ windowsHide: true,
18479
19383
  env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
18480
19384
  });
18481
- return listed.status !== 0 || (listed.stdout ?? "").trim() !== "";
19385
+ if (listed.status !== 0)
19386
+ return null;
19387
+ return new Set((listed.stdout ?? "").split(`
19388
+ `).map((line) => line.split("\t")[1] ?? "").filter((ref) => ref.startsWith("refs/heads/")).map((ref) => ref.slice(11)));
18482
19389
  }, offPolicyPush = (action, repo, facts, remote) => {
18483
19390
  const names = action.allBranches ? [...facts.heads] : action.targets.map((target) => target ?? repo.branch);
19391
+ let listed;
19392
+ const remoteHas = (branch) => (listed === undefined ? listed = remoteHeads(repo, remote) : listed)?.has(branch) ?? true;
18484
19393
  for (const branch of names) {
18485
- if (!branch || action.deleted?.includes(branch) || isTag(facts, branch))
19394
+ if (!branch || /[$`]/.test(branch))
19395
+ continue;
19396
+ if (action.deleted?.includes(branch) || isTag(facts, branch))
18486
19397
  continue;
18487
19398
  if (facts.remoteHeads.has(branch))
18488
19399
  continue;
18489
19400
  const check = validateBranchNameFor(repo.dir, branch);
18490
- if (check.ok || check.rule !== "allowed_pattern" || remoteHas(repo, remote, branch))
19401
+ if (check.ok || check.rule !== "allowed_pattern" || remoteHas(branch))
18491
19402
  continue;
18492
19403
  return `workit: raw ${quote("git push")} onto ${branch}: ${check.error}`;
18493
19404
  }
@@ -18507,23 +19418,49 @@ var NONE2, SHELLS, PREFIXES2, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18507
19418
  case "merge":
18508
19419
  return null;
18509
19420
  }
18510
- }, actionsIn = (cwd, command, dialect) => {
19421
+ }, pushRefusal = (action, repo, config) => {
19422
+ if (!toPushRemote(repo, action.remote, config))
19423
+ return null;
19424
+ if (action.mirror)
19425
+ return `workit: raw ${quote("git push --mirror")} rewrites and deletes every remote ref, protected branches and tags included. Push one branch with ${quote("workit git push")}.`;
19426
+ const remote = action.remote ?? pushRemoteOf(config, repo.branch) ?? "origin";
19427
+ const facts = refFacts(repo, remote);
19428
+ const targets = pushedBranches(action, repo, config, facts, remote);
19429
+ const pushed = { ...action, targets };
19430
+ const offPolicy = action.allBranches ? offPolicyPush(pushed, repo, facts, remote) : null;
19431
+ if (offPolicy)
19432
+ return offPolicy;
19433
+ for (const target of targets) {
19434
+ const guarded = guardedTarget(repo.dir, target ?? repo.branch ?? "");
19435
+ if (guarded)
19436
+ return refusal(action, guarded, repo.dir);
19437
+ }
19438
+ return tagRefusal(pushed, facts) ?? (action.deletes ? null : offPolicyPush(pushed, repo, facts, remote));
19439
+ }, actionsIn = (cwd, command, dialect, cache = new Map) => {
18511
19440
  const out = [];
18512
19441
  const repos = new Map;
18513
- for (const invocation of rawInvocations(command, dialect)) {
18514
- if (invocation.dir === undefined)
18515
- continue;
18516
- const action = rawAction(invocation);
18517
- if (!action)
18518
- continue;
18519
- const dir = path20.resolve(cwd, invocation.dir ?? ".");
19442
+ const repoFor = (relative) => {
19443
+ const dir = path20.resolve(cwd, relative ?? ".");
18520
19444
  if (!repos.has(dir)) {
18521
19445
  const repo = repoAt(dir);
18522
19446
  repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
18523
19447
  }
18524
- const repo = repos.get(dir);
18525
- if (repo)
18526
- out.push({ action, repo });
19448
+ return repos.get(dir) ?? null;
19449
+ };
19450
+ for (const raw of rawInvocations(command, dialect)) {
19451
+ if (raw.dir === undefined)
19452
+ continue;
19453
+ const known = raw.tool !== "git" || GIT_COMMANDS.has(raw.args[0] ?? "");
19454
+ if (!known && !repoFor(raw.dir))
19455
+ continue;
19456
+ for (const invocation of known ? [raw] : expandGitAliases(cwd, raw, cache)) {
19457
+ if (invocation.dir === undefined)
19458
+ continue;
19459
+ const action = rawAction(invocation);
19460
+ const repo = action ? repoFor(invocation.dir) : null;
19461
+ if (action && repo)
19462
+ out.push({ action, repo, invocation });
19463
+ }
18527
19464
  }
18528
19465
  return out;
18529
19466
  }, inWorkitWorkspace = (dir) => {
@@ -18543,7 +19480,11 @@ var NONE2, SHELLS, PREFIXES2, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18543
19480
  raw: [...marker.raw, ...fresh.map((kind) => agent + kind)]
18544
19481
  });
18545
19482
  return fresh.map((kind) => nudges.get(kind));
18546
- }, deny = (reason) => ({ kind: "deny", reason, unblock: null }), NEXT_COMMAND = "next", MARKER_TTL_SECONDS = 3600, markerFile = (repo, session, key) => path20.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`), callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`, headCommit = (dir) => {
19483
+ }, deny = (reason) => ({ kind: "deny", reason, unblock: null }), SPLIT_IT = "Split it into simpler commands (no deeply nested substitutions or huge brace lists) and run them one at a time.", analysisDenial = (error) => ({
19484
+ kind: "deny",
19485
+ reason: `workit: the shell hook could not analyze this command (${error.message}), so it does not run. ${SPLIT_IT}`,
19486
+ unblock: SPLIT_IT
19487
+ }), NEXT_COMMAND = "next", MARKER_TTL_SECONDS = 3600, markerFile = (repo, session, key) => path20.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`), callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`, headCommit = (dir) => {
18547
19488
  const run = spawnSync11("git", [
18548
19489
  "log",
18549
19490
  "-1",
@@ -18599,13 +19540,40 @@ var NONE2, SHELLS, PREFIXES2, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18599
19540
  var init_raw_git = __esm(() => {
18600
19541
  init_branch();
18601
19542
  init_workspaces();
18602
- init_rev();
18603
19543
  init_ledger();
18604
19544
  init_paths();
18605
19545
  init_session_marker();
19546
+ init_shell_words();
18606
19547
  NONE2 = { kind: "none" };
18607
- SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
18608
- PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "nice", "&"]);
19548
+ SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh", "ash", "mksh", "yash", "fish"]);
19549
+ POWERSHELLS = new Set(["pwsh", "powershell"]);
19550
+ EVALS = new Set(["eval", "iex", "invoke-expression"]);
19551
+ WRAPPERS = {
19552
+ sudo: { values: "-u -g -h -p -C -D -R -T -r -t -U --user --group --host --prompt --chdir" },
19553
+ doas: { values: "-u -C" },
19554
+ command: { values: "" },
19555
+ builtin: { values: "" },
19556
+ env: { values: "-u -C --unset --chdir" },
19557
+ nohup: { values: "" },
19558
+ setsid: { values: "" },
19559
+ time: { values: "-f -o --format --output" },
19560
+ exec: { values: "-a" },
19561
+ nice: { values: "-n --adjustment" },
19562
+ ionice: { values: "-c -n -p --class --classdata" },
19563
+ stdbuf: { values: "-i -o -e --input --output --error" },
19564
+ chronic: { values: "" },
19565
+ unbuffer: { values: "" },
19566
+ wsl: { values: "-d -u --cd --distribution --user" },
19567
+ timeout: { values: "-s -k --signal --kill-after", operands: 1 },
19568
+ flock: { values: "-w -E --timeout --conflict-exit-code", operands: 1 },
19569
+ coproc: { values: "" },
19570
+ taskset: { values: "", operands: 1 },
19571
+ watch: { values: "-n -d --interval --differences -q --equexit" },
19572
+ "&": { values: "" }
19573
+ };
19574
+ WRAPPER_VALUES = new Map(Object.entries(WRAPPERS).map(([name, wrapper]) => [name, new Set(wrapper.values.split(" "))]));
19575
+ KEYWORDS = new Set(["if", "then", "else", "elif", "while", "until", "do", "!", "{"]);
19576
+ DECLARATIONS = new Set(["export", "local", "declare", "readonly", "typeset"]);
18609
19577
  SCRIPT_FLAG = /^-[a-zA-Z]*c[a-zA-Z]*$/;
18610
19578
  CD = new Set(["cd", "chdir", "set-location", "sl"]);
18611
19579
  PUSHD = new Set(["pushd", "push-location"]);
@@ -18619,6 +19587,24 @@ var init_raw_git = __esm(() => {
18619
19587
  "--config-env",
18620
19588
  "--super-prefix"
18621
19589
  ]);
19590
+ GIT_CONFIG_OPTIONS = new Set(["-c", "--config-env", "--git-dir"]);
19591
+ GIT_CONFIG_ENV = /^GIT_(?:CONFIG\w*|DIR|COMMON_DIR|WORK_TREE)$/;
19592
+ ASSIGNMENT = /^([A-Za-z_]\w*)=([\s\S]*)$/;
19593
+ HERE = /^(?:\$PWD|\$\{PWD\}|\$\(\s*pwd\s*\)|`pwd`|\$\(\s*git\s+rev-parse\s+--show-toplevel\s*\))(?=$|[\\/])/;
19594
+ VARIABLE = /\$(?:\{(\w+)\}|(\w+))/g;
19595
+ XARGS_VALUES = new Set(["-n", "-L", "-l", "-P", "-d", "-E", "-e", "-s", "-a", "--arg-file", "--delimiter"].concat([
19596
+ "--max-args",
19597
+ "--max-lines",
19598
+ "--max-procs",
19599
+ "--eof",
19600
+ "--max-chars",
19601
+ "--process-slot-var"
19602
+ ]));
19603
+ POWERSHELL_VALUES = /^-(?:f|file|ex|executionpolicy|wd|workingdirectory|config|configurationname|o|outputformat|if|inputformat|inp|w|windowstyle|settingsfile|custompipename|psconsolefile)$/;
19604
+ PARALLEL_VALUES = new Set(["-j", "--jobs", "-N", "-n", "-S", "--sshlogin", "-P", "--delay"]);
19605
+ START_PROCESS_VALUES = /^-(?:workingdirectory|wd|redirectstandard\w*|verb|windowstyle|credential|environment)$/i;
19606
+ CONFIG_READS = /^(?:--get\S*|-l|--list|--unset\S*|--remove-section|--rename-section|-e|--edit|--show-\S+|--name-only)$/;
19607
+ RECENT = new Map;
18622
19608
  LAUNCHERS = new Set([
18623
19609
  "npx",
18624
19610
  "bunx",
@@ -18634,6 +19620,7 @@ var init_raw_git = __esm(() => {
18634
19620
  ]);
18635
19621
  WORKIT_BIN = /(?:^|[\\/])workit(?:\.exe)?$|^@brainervirus\/workit-cli$/;
18636
19622
  PUSH_VALUE_OPTIONS = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo"]);
19623
+ CURRENT_BRANCH = /^\$\(\s*git\s+(?:branch\s+--show-current|rev-parse\s+--abbrev-ref\s+HEAD|symbolic-ref\s+--short\s+HEAD)\s*\)$/;
18637
19624
  VALUE_OPTIONS = {
18638
19625
  git: new Set([
18639
19626
  "-m",
@@ -18661,24 +19648,56 @@ var init_raw_git = __esm(() => {
18661
19648
  gh: new Set(["-b", "--body", "-F", "--body-file", "-t", "--title", "--subject", "-A", "--author-email"].concat(["-B", "--base", "-H", "--head", "-l", "--label", "-a", "--assignee"]).concat(["-r", "--reviewer", "-m", "--milestone", "-p", "--project", "-T", "--template"]).concat(["--match-head-commit", "-R", "--repo"])),
18662
19649
  glab: new Set(["-m", "--message", "--squash-message", "--sha", "-t", "--title", "-d", "--description"].concat(["-b", "--target-branch", "-s", "--source-branch", "-l", "--label"]).concat(["-a", "--assignee", "--reviewer", "-R", "--repo"]))
18663
19650
  };
19651
+ API_VALUES = new Set(["--method", "--header", "--raw-field", "--field", "--jq", "--template", "--input"].concat([
19652
+ "--hostname",
19653
+ "--preview",
19654
+ "--cache",
19655
+ "--output"
19656
+ ]));
19657
+ API_FIELDS = new Set(["-f", "-F", "--raw-field", "--field"]);
19658
+ API_MERGE = {
19659
+ gh: /(?:^|\/)(?:pulls\/[^/?#]+\/merge|merges)\/?(?:[?#]|$)/i,
19660
+ glab: /(?:^|\/)merge_requests\/[^/?#]+\/merge\/?(?:[?#]|$)/i
19661
+ };
19662
+ GRAPHQL_MERGE = /\b(?:mergePullRequest|enablePullRequestAutoMerge|mergeBranch|mergeRequestAccept)\b/;
19663
+ GIT_COMMANDS = new Set(("add am annotate apply archive bisect blame branch bugreport bundle cat-file check-attr " + "check-ignore check-mailmap check-ref-format checkout checkout-index cherry cherry-pick " + "citool clean clone column commit commit-graph commit-tree config count-objects credential " + "daemon describe diagnose diff diff-files diff-index diff-tree difftool fast-export " + "fast-import fetch fetch-pack filter-branch fmt-merge-msg for-each-ref for-each-repo " + "format-patch fsck gc get-tar-commit-id grep gui hash-object help hook index-pack init " + "instaweb interpret-trailers lfs log ls-files ls-remote ls-tree mailinfo mailsplit " + "maintenance merge merge-base merge-file merge-index merge-tree mergetool mktag mktree " + "multi-pack-index mv name-rev notes pack-objects pack-refs patch-id prune pull push " + "range-diff read-tree rebase reflog remote repack replace request-pull rerere reset " + "restore rev-list rev-parse revert rm send-email shortlog show show-branch show-ref " + "sparse-checkout stage stash status stripspace submodule subtree switch symbolic-ref tag " + "update-index update-ref var verify-commit verify-pack verify-tag version whatchanged " + "worktree write-tree").split(" "));
19664
+ JUDGED_COMMANDS = [
19665
+ "push",
19666
+ "commit",
19667
+ "switch",
19668
+ "checkout",
19669
+ "branch",
19670
+ "worktree",
19671
+ "stash",
19672
+ "update-ref",
19673
+ "symbolic-ref",
19674
+ "fetch",
19675
+ "pull",
19676
+ "subtree"
19677
+ ];
18664
19678
  });
18665
19679
 
18666
19680
  // packages/workit-core/src/core/route-intent.ts
18667
19681
  import { spawnSync as spawnSync12 } from "node:child_process";
18668
19682
  import { existsSync as existsSync6 } from "node:fs";
18669
19683
  import path21 from "node:path";
18670
- var UNKNOWABLE, SWITCH_CREATE, CHECKOUT_CREATE, WORKTREE_CREATE, BRANCH_NO_CREATE, BRANCH_RENAME, flagValue = (args, flags) => {
19684
+ var UNKNOWABLE, SWITCH_CREATE, CHECKOUT_CREATE, WORKTREE_CREATE, SUBTREE_CREATE, BRANCH_NO_CREATE, BRANCH_RENAME, longOption = (name, flags) => flags.has(name) || name.length >= 3 && [...flags].some((flag) => flag.startsWith("--") && flag.startsWith(name)), shortsOf = (flags) => [...flags].filter((flag) => /^-[^-]$/u.test(flag)).join("").replaceAll("-", ""), flagValue = (args, flags) => {
19685
+ const shorts = shortsOf(flags);
18671
19686
  for (let index = 1;index < args.length; index++) {
18672
19687
  const arg = args[index];
18673
19688
  if (arg === "--")
18674
19689
  return null;
18675
- if (flags.has(arg))
18676
- return args[index + 1] ?? null;
18677
- const eq = arg.indexOf("=");
18678
- if (arg.startsWith("--") && eq > 0 && flags.has(arg.slice(0, eq)))
18679
- return arg.slice(eq + 1);
18680
- if (!arg.startsWith("--") && arg.length > 2 && flags.has(arg.slice(0, 2)))
18681
- return arg.slice(2);
19690
+ if (arg.startsWith("--")) {
19691
+ const eq = arg.indexOf("=");
19692
+ if (!longOption(eq > 0 ? arg.slice(0, eq) : arg, flags))
19693
+ continue;
19694
+ return eq > 0 ? arg.slice(eq + 1) : args[index + 1] ?? null;
19695
+ }
19696
+ if (!arg.startsWith("-") || arg.length < 2)
19697
+ continue;
19698
+ for (let at = 1;at < arg.length; at++)
19699
+ if (shorts.includes(arg[at]))
19700
+ return at + 1 < arg.length ? arg.slice(at + 1) : args[index + 1] ?? null;
18682
19701
  }
18683
19702
  return null;
18684
19703
  }, WORKTREE_VALUES, worktreeTarget = (args) => {
@@ -18708,51 +19727,102 @@ var UNKNOWABLE, SWITCH_CREATE, CHECKOUT_CREATE, WORKTREE_CREATE, BRANCH_NO_CREAT
18708
19727
  positionals.push(...args.slice(index + 1));
18709
19728
  break;
18710
19729
  }
18711
- const flag = arg.split("=", 1)[0];
18712
- if (BRANCH_NO_CREATE.has(flag))
18713
- return null;
18714
- if (BRANCH_RENAME.has(flag))
18715
- rename = true;
18716
- else if (!arg.startsWith("-"))
19730
+ if (arg.startsWith("--")) {
19731
+ const flag = arg.split("=", 1)[0];
19732
+ if (longOption(flag, BRANCH_NO_CREATE))
19733
+ return null;
19734
+ if (longOption(flag, BRANCH_RENAME))
19735
+ rename = true;
19736
+ } else if (arg.startsWith("-") && arg.length > 1) {
19737
+ const shorts = arg.slice(1).split("");
19738
+ if (shorts.some((short) => BRANCH_NO_CREATE.has(`-${short}`)))
19739
+ return null;
19740
+ if (shorts.some((short) => BRANCH_RENAME.has(`-${short}`)))
19741
+ rename = true;
19742
+ } else
18717
19743
  positionals.push(arg);
18718
19744
  }
18719
19745
  if (positionals.length < 1 || positionals.length > 2)
18720
19746
  return null;
18721
19747
  return rename ? positionals.at(-1) ?? null : positionals[0];
18722
- }, gitBranchCreation = (args) => {
18723
- const [sub, second] = args;
18724
- const target = sub === "switch" ? flagValue(args, SWITCH_CREATE) : sub === "checkout" ? flagValue(args, CHECKOUT_CREATE) : sub === "branch" ? branchCommandTarget(args) : sub === "worktree" && second === "add" ? worktreeTarget(args.slice(1)) : null;
18725
- return target && !target.startsWith("-") && !UNKNOWABLE.test(target) ? target : null;
18726
- }, shellBranchTargets = (command, dialect = "posix") => {
18727
- if (!/\bgit\b/u.test(command))
18728
- return [];
19748
+ }, positionalsOf = (args, skip, values = new Set) => {
18729
19749
  const found = [];
18730
- for (const invocation of rawInvocations(command, dialect)) {
18731
- if (invocation.tool !== "git")
18732
- continue;
18733
- const branch = gitBranchCreation(invocation.args);
18734
- if (branch)
18735
- found.push({ branch, dir: invocation.dir });
19750
+ for (let index = skip;index < args.length; index++) {
19751
+ const arg = args[index];
19752
+ if (arg === "--")
19753
+ return [...found, ...args.slice(index + 1)];
19754
+ if (values.has(arg))
19755
+ index++;
19756
+ else if (!arg.startsWith("-") || arg === "-")
19757
+ found.push(arg);
18736
19758
  }
18737
19759
  return found;
18738
- }, localBranchExists = (dir, branch) => spawnSync12("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], {
19760
+ }, localHead = (ref) => ref.startsWith("refs/heads/") ? ref.slice("refs/heads/".length) : null, updateRefTargets = (args, stdin) => {
19761
+ if (args.some((arg) => arg === "-d" || arg === "--delete"))
19762
+ return [];
19763
+ if (args.includes("--stdin"))
19764
+ return (stdin ?? "").split(/[\n\0]/u).map((line) => /^\s*(?:create|update)\s+(\S+)/u.exec(line)?.[1] ?? "").map(localHead).filter((name) => name !== null);
19765
+ const [ref] = positionalsOf(args, 1, new Set(["-m"]));
19766
+ const name = ref ? localHead(ref) : null;
19767
+ return name ? [name] : [];
19768
+ }, symbolicRefTarget = (args) => {
19769
+ if (args.some((arg) => arg === "-d" || arg === "--delete"))
19770
+ return null;
19771
+ const [, ref] = positionalsOf(args, 1, new Set(["-m"]));
19772
+ return ref ? localHead(ref) : null;
19773
+ }, FETCH_VALUES, fetchTargets = (args, stdin) => {
19774
+ if (args.includes("--all") || args.includes("--multiple"))
19775
+ return [];
19776
+ const specs = args.includes("--stdin") ? (stdin ?? "").split(/\s+/u).filter(Boolean) : positionalsOf(args, 1, FETCH_VALUES).slice(1);
19777
+ const names = [];
19778
+ for (const raw of specs) {
19779
+ const colon = raw.indexOf(":");
19780
+ const destination = colon >= 0 ? raw.slice(colon + 1) : "";
19781
+ if (!destination || destination.includes("*"))
19782
+ continue;
19783
+ const name = destination.startsWith("refs/") ? localHead(destination) : destination;
19784
+ if (name)
19785
+ names.push(name);
19786
+ }
19787
+ return names;
19788
+ }, localPushTargets = (args) => {
19789
+ const action = pushAction(args);
19790
+ if (action?.kind !== "push" || action.remote !== "." || action.deletes)
19791
+ return [];
19792
+ return action.targets.filter((name) => name !== null);
19793
+ }, gitBranchCreations = (args, stdin) => {
19794
+ const [sub, second] = args;
19795
+ const targets = sub === "switch" ? [flagValue(args, SWITCH_CREATE)] : sub === "checkout" ? [flagValue(args, CHECKOUT_CREATE)] : sub === "branch" ? [branchCommandTarget(args)] : sub === "worktree" && second === "add" ? [worktreeTarget(args.slice(1))] : sub === "stash" && second === "branch" ? positionalsOf(args, 2).slice(0, 1) : sub === "update-ref" ? updateRefTargets(args, stdin) : sub === "symbolic-ref" ? [symbolicRefTarget(args)] : sub === "fetch" || sub === "pull" ? fetchTargets(args, stdin) : sub === "push" ? localPushTargets(args) : sub === "subtree" && second === "split" ? [flagValue(args.slice(1), SUBTREE_CREATE)] : [];
19796
+ return targets.filter((target) => target !== null && target !== "" && !target.startsWith("-") && !UNKNOWABLE.test(target));
19797
+ }, targetsOf = (invocations) => invocations.flatMap((invocation) => invocation.tool === "git" ? gitBranchCreations(invocation.args, invocation.stdin).map((branch) => ({
19798
+ branch,
19799
+ dir: invocation.dir
19800
+ })) : []), localBranchExists = (dir, branch) => spawnSync12("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], {
18739
19801
  cwd: dir,
18740
19802
  stdio: "ignore",
18741
19803
  timeout: 5000
18742
- }).status === 0, shellBranchPolicyViolation = (root, command, dialect = "posix") => {
19804
+ }).status === 0, judgedDir = (root, dir) => {
19805
+ if (dir === undefined)
19806
+ return null;
19807
+ if (dir === null)
19808
+ return root;
19809
+ const resolved = path21.resolve(root, dir);
19810
+ return existsSync6(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
19811
+ }, shellBranchPolicyViolation = (root, command, dialect = "posix") => {
18743
19812
  let judged = false;
18744
- for (const target of shellBranchTargets(command, dialect)) {
18745
- if (target.dir === undefined)
19813
+ const cache = new Map;
19814
+ for (const raw of rawInvocations(command, dialect)) {
19815
+ if (raw.tool !== "git" || judgedDir(root, raw.dir) === null)
18746
19816
  continue;
18747
- const dir = target.dir === null ? root : path21.resolve(root, target.dir);
18748
- if (target.dir !== null && (!existsSync6(dir) || !inWorkitWorkspace(dir)))
18749
- continue;
18750
- if (localBranchExists(dir, target.branch))
18751
- continue;
18752
- judged = true;
18753
- const check = validateBranchNameFor(dir, target.branch);
18754
- if (!check.ok)
18755
- return check;
19817
+ for (const target of targetsOf(expandGitAliases(root, raw, cache))) {
19818
+ const dir = judgedDir(root, target.dir);
19819
+ if (dir === null || localBranchExists(dir, target.branch))
19820
+ continue;
19821
+ judged = true;
19822
+ const check = validateBranchNameFor(dir, target.branch);
19823
+ if (!check.ok)
19824
+ return check;
19825
+ }
18756
19826
  }
18757
19827
  return judged ? { ok: true } : null;
18758
19828
  };
@@ -18763,18 +19833,29 @@ var init_route_intent = __esm(() => {
18763
19833
  SWITCH_CREATE = new Set(["-c", "--create", "-C", "--force-create", "--orphan"]);
18764
19834
  CHECKOUT_CREATE = new Set(["-b", "-B", "--orphan"]);
18765
19835
  WORKTREE_CREATE = new Set(["-b", "-B"]);
19836
+ SUBTREE_CREATE = new Set(["-b", "--branch"]);
18766
19837
  BRANCH_NO_CREATE = new Set(["-d", "-D", "--delete", "-l", "--list", "-a", "--all", "-r", "--remotes", "--show-current"].concat(["-v", "-vv", "--verbose", "--contains", "--no-contains", "--merged", "--no-merged"]).concat(["--points-at", "--sort", "--format", "--column", "--no-column", "-u"]).concat(["--set-upstream-to", "--unset-upstream", "--edit-description", "-i", "--ignore-case"]));
18767
19838
  BRANCH_RENAME = new Set(["-m", "-M", "--move", "-c", "-C", "--copy"]);
18768
19839
  WORKTREE_VALUES = new Set(["--reason"]);
19840
+ FETCH_VALUES = new Set(["--depth", "--deepen", "--shallow-since", "--shallow-exclude", "--refmap", "-j", "--jobs"].concat(["--upload-pack", "-o", "--server-option", "--negotiation-tip", "--filter"]).concat(["--recurse-submodules-default", "--submodule-prefix", "-s", "--strategy", "-X"]).concat(["--strategy-option"]));
18769
19841
  });
18770
19842
 
18771
19843
  // packages/workit-core/src/hooks/policy.ts
18772
19844
  var shellPolicy = (cwd, command, dialect = "posix") => {
18773
- const policy = shellBranchPolicyViolation(cwd, command, dialect);
19845
+ let policy;
19846
+ try {
19847
+ policy = shellBranchPolicyViolation(cwd, command, dialect);
19848
+ } catch (error) {
19849
+ if (error instanceof ShellAnalysisError)
19850
+ return analysisDenial(error);
19851
+ throw error;
19852
+ }
18774
19853
  return policy && !policy.ok ? { kind: "deny", reason: `branch_policy_denied: ${policy.error}`, unblock: null } : { kind: "none" };
18775
19854
  };
18776
19855
  var init_policy = __esm(() => {
18777
19856
  init_route_intent();
19857
+ init_raw_git();
19858
+ init_shell_words();
18778
19859
  });
18779
19860
 
18780
19861
  // packages/workit-core/src/hooks/write-gate.ts
@@ -18816,9 +19897,9 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
18816
19897
  const posix = relative.split(path22.sep).join("/");
18817
19898
  const segments = posix.split("/");
18818
19899
  return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
18819
- }, PREFIXES3, operands = (words) => words.filter((item) => !item.startsWith("-")), editsOf = (input) => {
19900
+ }, PREFIXES2, operands = (words) => words.filter((item) => !item.startsWith("-")), editsOf = (input) => {
18820
19901
  let words = input;
18821
- while (words.length && (PREFIXES3.has(words[0]) || /^\w+=/.test(words[0])))
19902
+ while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
18822
19903
  words = words.slice(1);
18823
19904
  const [verb, ...args] = words;
18824
19905
  const name = (verb?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
@@ -18947,9 +20028,10 @@ var init_write_gate = __esm(() => {
18947
20028
  init_derive();
18948
20029
  init_task_evaluation();
18949
20030
  init_ledger();
20031
+ init_shell_words();
18950
20032
  NONE3 = { kind: "none" };
18951
20033
  CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
18952
- PREFIXES3 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
20034
+ PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
18953
20035
  cache2 = new Map;
18954
20036
  });
18955
20037
 
@@ -19265,6 +20347,7 @@ var init_skill_nudge = __esm(() => {
19265
20347
  init_ledger();
19266
20348
  init_raw_git();
19267
20349
  init_session_marker();
20350
+ init_shell_words();
19268
20351
  USER_INVOKED = new Set(["workit-retro", "workit-architecture"]);
19269
20352
  SKILL_HOST = {
19270
20353
  claude_code: "claude-code",
@@ -20243,7 +21326,7 @@ var init_cursor = __esm(() => {
20243
21326
  "compact.pre": { support: "partial", native: "preCompact" },
20244
21327
  stop: { support: "native", native: "stop" }
20245
21328
  },
20246
- shellPolicy: { deny: "native", channel: "exit2+json", failClosed: true },
21329
+ shellPolicy: { deny: "native", channel: "exit2+json", failClosed: false },
20247
21330
  context: {
20248
21331
  sessionStart: "native",
20249
21332
  perTurn: "none",