@quantiya/codevibe-codex-plugin 2.0.61 → 2.0.62

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.
@@ -8529,7 +8529,12 @@ function renderLocalGemmaPlannerPrompt(input, options) {
8529
8529
  'User: "Can you code in Rust?" -> {"action":"advisory_response","rationale":"capability question, no repo context","advisory_summary":"Yes \u2014 I can write and review Rust code across libraries, CLIs, and web services."}',
8530
8530
  'User: "That sounds like a summary, I am looking for your thoughts" (a web page was read earlier in this session) -> {"action":"advisory_response","rationale":"opinion follow-up on the page read earlier","advisory_summary":"<your own analysis of that page>"}',
8531
8531
  'User: "what do you think about that article?" -> {"action":"advisory_response","rationale":"opinion follow-up on earlier content","advisory_summary":"<your view on the article>"}',
8532
- options?.isSubprocessRunner ? 'User: "what did the last task change?" -> {"action":"summarize_current_status","rationale":"status question"}' : 'User: "what did the last task change?" -> {"action":"advisory_response","rationale":"status question; the shell attaches the status record"}',
8532
+ options?.isSubprocessRunner ? 'User: "what did the last task change?" -> {"action":"summarize_current_status","rationale":"status question"}' : [
8533
+ 'User: "what did the last task change?" -> {"action":"advisory_response","rationale":"status question; the shell attaches the status record"}',
8534
+ 'User: "Show me the diff of the most recent commit on this branch" -> {"action":"advisory_response","rationale":"inspect commit diff status"}',
8535
+ 'User: "Are there any untracked or modified files in the working tree right now?" -> {"action":"advisory_response","rationale":"inspect working tree status"}'
8536
+ ].join(`
8537
+ `),
8533
8538
  'User: "What kind of applications can you implement?" -> {"action":"advisory_response","rationale":"capability question","advisory_summary":"I can implement CLIs, web apps, APIs, libraries, scripts, data pipelines, and tests across most popular languages."}',
8534
8539
  'User: "What is this [path]\\n\\n[The user attached 1 image(s).]" -> {"action":"advisory_response","rationale":"describe the attached image"}',
8535
8540
  'User: "describe this\\n\\n[The user attached 1 image(s).]" -> {"action":"advisory_response","rationale":"describe the attached image"}',
@@ -8972,12 +8977,12 @@ function requestOllamaGenerateStream(config, promptText, opts) {
8972
8977
  num_ctx: numCtx
8973
8978
  }
8974
8979
  }), url = new URL("/api/generate", config.host), timeoutMs = opts?.timeoutMs ?? config.timeoutMs;
8975
- return new Promise((resolve24, reject) => {
8976
- let settled = !1, responseBytes = 0, buffer = "", decoder = new import_string_decoder.StringDecoder("utf8"), out = { text: "" }, emptyRun = 0, chunks = 0, sawDone = !1, lastDone, ndjson = !1, idleTimer = null, activeRes = null, clearIdle = () => {
8980
+ return opts?.signal?.aborted ? Promise.reject(new Error("Ollama generation aborted by signal")) : new Promise((resolve24, reject) => {
8981
+ let settled = !1, responseBytes = 0, buffer = "", decoder = new import_string_decoder.StringDecoder("utf8"), out = { text: "" }, emptyRun = 0, chunks = 0, sawDone = !1, lastDone, ndjson = !1, timeout = null, idleTimer = null, activeRes = null, clearIdle = () => {
8977
8982
  idleTimer && (clearTimeout(idleTimer), idleTimer = null);
8978
8983
  }, finish = (fn) => {
8979
8984
  if (!settled) {
8980
- settled = !0, clearTimeout(timeout), clearIdle();
8985
+ settled = !0, timeout && clearTimeout(timeout), clearIdle();
8981
8986
  try {
8982
8987
  fn();
8983
8988
  } catch (err) {
@@ -9078,10 +9083,19 @@ function requestOllamaGenerateStream(config, promptText, opts) {
9078
9083
  }
9079
9084
  }));
9080
9085
  }
9081
- ), timeout = setTimeout(() => {
9086
+ );
9087
+ if (req.on("error", (err) => finish(() => reject(err))), timeout = setTimeout(() => {
9082
9088
  req.destroy(new Error(`Ollama request timed out after ${timeoutMs}ms`));
9083
- }, timeoutMs);
9084
- req.on("error", (err) => finish(() => reject(err))), req.end(body);
9089
+ }, timeoutMs), opts?.signal) {
9090
+ if (opts.signal.aborted) {
9091
+ finish(() => reject(new Error("Ollama generation aborted by signal"))), req.destroy();
9092
+ return;
9093
+ }
9094
+ opts.signal.addEventListener("abort", () => {
9095
+ finish(() => reject(new Error("Ollama generation aborted by signal"))), req.destroy(), activeRes?.destroy();
9096
+ }, { once: !0 });
9097
+ }
9098
+ req.end(body);
9085
9099
  });
9086
9100
  }
9087
9101
  function describeRecordError(value) {
@@ -9184,6 +9198,7 @@ function requestOllamaPull(config, onProgress = () => {
9184
9198
  var OllamaGemmaPlannerRunner = class {
9185
9199
  constructor(config) {
9186
9200
  this.config = config;
9201
+ this.providerKind = "local";
9187
9202
  this.runtimeLabel = `local-gemma-ollama:${config.model}`;
9188
9203
  }
9189
9204
  classify(promptText, options) {
@@ -9195,7 +9210,8 @@ var OllamaGemmaPlannerRunner = class {
9195
9210
  numPredict,
9196
9211
  // #618 — advisory generations (long prompts, big output budgets) get the
9197
9212
  // advisory timeout floor; classify keeps the fast-fail config timeout.
9198
- timeoutMs: advisoryTimeoutMs(this.config.timeoutMs),
9213
+ timeoutMs: options?.timeoutMs ?? advisoryTimeoutMs(this.config.timeoutMs),
9214
+ ...options?.signal ? { signal: options.signal } : {},
9199
9215
  // IMAGE-ATTACHMENT-DESIGN.md §6: forward RAW base64 images (multimodal
9200
9216
  // answerer). Only the shell's `routeAdvisory`/image-brainstorm passes these
9201
9217
  // with `responseFormat:'text'`; the classifier never does (forced-JSON).
@@ -67548,7 +67564,1007 @@ function chronologicalSupervisionEvents(newestFirstEvents) {
67548
67564
  // src/orchestration-shell/git-status-summary.ts
67549
67565
  var path64 = __toESM(require("path")), import_child_process5 = require("child_process"), import_util3 = require("util");
67550
67566
  init_env_scrub();
67551
- var defaultExecFileAsync = (0, import_util3.promisify)(import_child_process5.execFile), GIT_QUERY_PATTERN = /\b(git\s+(?:status|log|diff|show|branch)|commit\s+history|recent\s+commits)\b/i, IGNORED_TARGET_NAMES = /* @__PURE__ */ new Set([
67567
+ init_logger2();
67568
+ var defaultExecFileAsync = (0, import_util3.promisify)(import_child_process5.execFile), GIT_QUERY_PATTERN = /\b(git\s+(?:status|log|diff|show|branch)|commit\s+history|recent\s+commits)\b/i;
67569
+ var COMMIT_DIFF_PATTERN = /\b(?:diff\s+of\s+(?:the\s+)?(?:most\s+recent|last|latest|current|head)\s+commit\b|(?:most\s+recent|last|latest|current|head)\s+commit(?:'s)?\s+diff\b|(?:show|view|see)\s+(?:the\s+)?diff\s+of\s+(?:the\s+)?(?:most\s+recent|last|latest|current)\b|(?:what\s+changed\s+in|changes?\s+in)\s+(?:the\s+)?(?:most\s+recent|last|latest|current|head)\s+commit\b|(?:show|view|see)\s+what\s+changed\s+in\s+(?:the\s+)?(?:latest|last|most\s+recent)\s+commit\b|(?:show|view|see|inspect|print|display|what\s+is)\s+(?:the\s+)?(?:most\s+recent|last|latest|current)\s+commit\b|[0-9a-f]{7,40}(?:[~^][A-Za-z0-9_/@~^-]*)?'s\s+diff\b|diff\s+of\s+(?:commit\s+)?(?![^\s;,?]*\.\.[a-zA-Z0-9_/@~^-])([0-9a-f]{7,40}(?:[~^][A-Za-z0-9_/@~^-]*)?)(?![0-9a-zA-Z_/@~^-]))/i;
67570
+ var SAFE_REF_PATTERN = /^[A-Za-z0-9_./@~^-]+$/, SAFE_REPO_NAME_PATTERN = /^[A-Za-z0-9_.-]+$/;
67571
+ function stripSentencePunctuation(token, options) {
67572
+ let stripped = token.trim().replace(/^['"`“‘«‹「『(\[(《【]+/u, "");
67573
+ return options?.preserveRangeDots && /\.{2,3}[,;:!?'"`”’»›」』)\])》】]*$/u.test(stripped) ? stripped.replace(/[,;:!?'"`”’»›」』)\])》】]+$/u, "") : (stripped = stripped.replace(/[.,;:!?'"`”’»›」』)\])》】]+$/u, ""), stripped);
67574
+ }
67575
+ function extractBranchOrRefTarget(prompt) {
67576
+ let positivePrompt = prompt.replace(
67577
+ /\b(?:that\s+is\s+|which\s+is\s+)?(?:not|except|excluding|without|instead\s+of)\s+(?:on|in|for|from|to|into)\s+(?:the\s+)?(?:branch\s+[A-Za-z0-9_./@~^-]+|[A-Za-z0-9_./@~^-]+\s+branch|current\s+branch|active\s+branch|this\s+branch)\b/gi,
67578
+ ""
67579
+ ).replace(
67580
+ /\b(?:that\s+is\s+|which\s+is\s+)?(?:not|except|excluding|without|instead\s+of)\s+(?:the\s+)?(?:current|active|this)\s+branch\b/gi,
67581
+ ""
67582
+ );
67583
+ if (/(?:^|[\s,;])(?:on|in|for|from)\s+(?:the\s+|this\s+|current\s+|my\s+|our\s+|active\s+)?(?:this|current|active)\s+branch(?:\s+(?:in\s+detail|and\s+|please|now|here|summary|json))?[\s;,?.]*$/i.test(positivePrompt) || /^(?:this|current|active)\s+branch(?:\s+(?:in\s+detail|and\s+|please|now|here|summary|json))?[\s;,?.]*$/i.test(positivePrompt) || /\b(?:what|which)\s+branch\s+(?:am\s+i|is\s+this)\b/i.test(positivePrompt))
67584
+ return;
67585
+ let explicitPrefixMatch = positivePrompt.match(
67586
+ /(?:(?<![\w-])(?:on|in|for|from|to|into)\s+(?:the\s+)?branch(?:\s+(?:named|called)\s+|:\s*|\s+)|(?<![\w-])(?:the\s+)?branch\s+(?:named|called)\s+|(?<![\w-])branch:\s*|(?<![\w-])(?:named|called)\s+branch\s+)([A-Za-z0-9_./@~^-]+)(?=[\s;,?.]|$)/i
67587
+ );
67588
+ if (explicitPrefixMatch) {
67589
+ let candidate = stripSentencePunctuation(explicitPrefixMatch[1]), lower = candidate.toLowerCase();
67590
+ if (!(/* @__PURE__ */ new Set([
67591
+ "this",
67592
+ "that",
67593
+ "the",
67594
+ "my",
67595
+ "our",
67596
+ "a",
67597
+ "an",
67598
+ "current",
67599
+ "active",
67600
+ "and",
67601
+ "or",
67602
+ "but",
67603
+ "then",
67604
+ "so",
67605
+ "in",
67606
+ "to",
67607
+ "for",
67608
+ "with",
67609
+ "from",
67610
+ "of",
67611
+ "by",
67612
+ "about",
67613
+ "at",
67614
+ "as",
67615
+ "into",
67616
+ "onto",
67617
+ "please",
67618
+ "now",
67619
+ "here",
67620
+ "there",
67621
+ "name",
67622
+ "names",
67623
+ "named",
67624
+ "called",
67625
+ "what",
67626
+ "which",
67627
+ "how",
67628
+ "who",
67629
+ "where",
67630
+ "why",
67631
+ "am",
67632
+ "is",
67633
+ "are"
67634
+ ])).has(lower) && SAFE_REF_PATTERN.test(candidate))
67635
+ return candidate;
67636
+ }
67637
+ let suffixBranchMatch = positivePrompt.match(
67638
+ /(?<![\w-])(?:on|in|for|from)\s+(?:the\s+)?([A-Za-z0-9_./@~^-]+)\s+branch\b/i
67639
+ );
67640
+ if (suffixBranchMatch) {
67641
+ let candidate = stripSentencePunctuation(suffixBranchMatch[1]), lower = candidate.toLowerCase();
67642
+ if (!(/* @__PURE__ */ new Set(["this", "that", "the", "my", "our", "a", "an", "current", "active", "which", "what", "each", "every"])).has(lower) && SAFE_REF_PATTERN.test(candidate))
67643
+ return candidate;
67644
+ }
67645
+ let standaloneMatch = positivePrompt.match(
67646
+ /(?:^|[,;(]\s*)branch\s+([A-Za-z0-9_./@~^-]+)(?=[\s;,?.]|$)/i
67647
+ );
67648
+ if (standaloneMatch) {
67649
+ let candidate = stripSentencePunctuation(standaloneMatch[1]), lower = candidate.toLowerCase();
67650
+ if (!(/* @__PURE__ */ new Set([
67651
+ "this",
67652
+ "that",
67653
+ "the",
67654
+ "my",
67655
+ "our",
67656
+ "a",
67657
+ "an",
67658
+ "current",
67659
+ "active",
67660
+ "and",
67661
+ "or",
67662
+ "but",
67663
+ "then",
67664
+ "so",
67665
+ "in",
67666
+ "to",
67667
+ "for",
67668
+ "with",
67669
+ "from",
67670
+ "of",
67671
+ "by",
67672
+ "about",
67673
+ "at",
67674
+ "as",
67675
+ "into",
67676
+ "onto",
67677
+ "please",
67678
+ "now",
67679
+ "here",
67680
+ "there",
67681
+ "name",
67682
+ "names",
67683
+ "named",
67684
+ "called",
67685
+ "what",
67686
+ "which",
67687
+ "how",
67688
+ "who",
67689
+ "where",
67690
+ "why",
67691
+ "am",
67692
+ "is",
67693
+ "are"
67694
+ ])).has(lower) && SAFE_REF_PATTERN.test(candidate))
67695
+ return candidate;
67696
+ }
67697
+ let stopwords = /* @__PURE__ */ new Set([
67698
+ "this",
67699
+ "that",
67700
+ "the",
67701
+ "our",
67702
+ "my",
67703
+ "a",
67704
+ "an",
67705
+ "current",
67706
+ "active",
67707
+ "latest",
67708
+ "recent",
67709
+ "most",
67710
+ "head",
67711
+ "commit",
67712
+ "commits",
67713
+ "working",
67714
+ "tree",
67715
+ "directory",
67716
+ "copy",
67717
+ "repo",
67718
+ "repository",
67719
+ "status",
67720
+ "diff",
67721
+ "log",
67722
+ "show",
67723
+ "please",
67724
+ "now",
67725
+ "thanks",
67726
+ "first",
67727
+ "here",
67728
+ "and",
67729
+ "then",
67730
+ "all",
67731
+ "review",
67732
+ "branches",
67733
+ "branch",
67734
+ "detail",
67735
+ "details",
67736
+ "depth",
67737
+ "brief",
67738
+ "short",
67739
+ "summary",
67740
+ "general",
67741
+ "plain",
67742
+ "text",
67743
+ "markdown",
67744
+ "terminal",
67745
+ "json",
67746
+ "name",
67747
+ "names"
67748
+ // Note: 'named' and 'called' removed from stopwords per Codex R12-F5 so
67749
+ // "Show the latest commit on named" correctly resolves to "named"
67750
+ ]), qualifiedMatch = positivePrompt.match(/\b(?:commit|diff|changes?|log|inspect|show)\s+(?:on|in|for|from)\s+(?:the\s+)?(?:branch\s+)?([A-Za-z0-9_./@~^-]+)(?:\s+branch)?(?=[\s;,?.]|$)/i);
67751
+ if (qualifiedMatch) {
67752
+ let candidate = stripSentencePunctuation(qualifiedMatch[1]);
67753
+ if (!stopwords.has(candidate.toLowerCase()) && SAFE_REF_PATTERN.test(candidate))
67754
+ return candidate;
67755
+ }
67756
+ let matches = [...positivePrompt.matchAll(/\b(?:on|in|for|from)\s+(?:the\s+)?(?:branch\s+)?([A-Za-z0-9_./@~^-]+)(?:\s+branch)?(?=[\s;,?.]|$)/gi)];
67757
+ for (let m of matches) {
67758
+ let candidate = stripSentencePunctuation(m[1]), lower = candidate.toLowerCase();
67759
+ if (!stopwords.has(lower) && SAFE_REF_PATTERN.test(candidate))
67760
+ return candidate;
67761
+ }
67762
+ }
67763
+ function hasRefSyntax(ref) {
67764
+ let lower = ref.toLowerCase();
67765
+ return !!(["head", "main", "master"].includes(lower) || /^[0-9a-f]{7,40}$/i.test(ref) || /[~^@/]|\.{2,3}/.test(ref) || /^v\d+(?:\.\d+)*$/i.test(ref));
67766
+ }
67767
+ function isLastTokenInPrompt(prompt, token) {
67768
+ let escaped = token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), cleaned = prompt.replace(/(?:[\s,;]+(?:please|thanks|thank\s+you|now|for\s+me))+[\s.!?;,:]*$/i, "");
67769
+ return new RegExp(`(?:^|\\s)${escaped}[\\s.!?;,:]*$`, "i").test(cleaned);
67770
+ }
67771
+ var PURPOSE_VERBS = /* @__PURE__ */ new Set([
67772
+ "review",
67773
+ "compare",
67774
+ "inspect",
67775
+ "check",
67776
+ "examine",
67777
+ "see",
67778
+ "view",
67779
+ "verify",
67780
+ "read",
67781
+ "understand",
67782
+ "share",
67783
+ "make",
67784
+ "study",
67785
+ "test",
67786
+ "explain",
67787
+ "commit",
67788
+ "push",
67789
+ "merge",
67790
+ "stage",
67791
+ "apply",
67792
+ "approve"
67793
+ ]), PRESENTATION_WORDS = /* @__PURE__ */ new Set([
67794
+ "stat",
67795
+ "stats",
67796
+ "statistics",
67797
+ "context",
67798
+ "color",
67799
+ "colors",
67800
+ "detail",
67801
+ "details",
67802
+ "line",
67803
+ "lines",
67804
+ "number",
67805
+ "numbers",
67806
+ "comment",
67807
+ "comments",
67808
+ "whitespace",
67809
+ "whitespaces",
67810
+ "explanation",
67811
+ "explanations",
67812
+ "message",
67813
+ "messages",
67814
+ "history",
67815
+ "hash",
67816
+ "hashes",
67817
+ "info",
67818
+ "information"
67819
+ ]), NON_GIT_SOFTWARE_LIBRARIES = /* @__PURE__ */ new Set([
67820
+ "node",
67821
+ "nodejs",
67822
+ "python",
67823
+ "java",
67824
+ "react",
67825
+ "vue",
67826
+ "angular",
67827
+ "rust",
67828
+ "go",
67829
+ "golang",
67830
+ "ruby",
67831
+ "rails",
67832
+ "django",
67833
+ "spring",
67834
+ "postgres",
67835
+ "postgresql",
67836
+ "mysql",
67837
+ "sqlite",
67838
+ "redis",
67839
+ "docker",
67840
+ "kubernetes",
67841
+ "k8s"
67842
+ ]), TEMPORAL_WORDS = /* @__PURE__ */ new Set([
67843
+ "monday",
67844
+ "tuesday",
67845
+ "wednesday",
67846
+ "thursday",
67847
+ "friday",
67848
+ "saturday",
67849
+ "sunday",
67850
+ "yesterday",
67851
+ "today",
67852
+ "tomorrow",
67853
+ "tonight",
67854
+ "morning",
67855
+ "afternoon",
67856
+ "evening",
67857
+ "recent",
67858
+ "week",
67859
+ "month",
67860
+ "year",
67861
+ "day",
67862
+ "hour",
67863
+ "minute",
67864
+ "before",
67865
+ "after",
67866
+ "prior",
67867
+ "earlier",
67868
+ "previous"
67869
+ ]), EXPLICIT_STRUCTURAL_EXCLUDED = /* @__PURE__ */ new Set([
67870
+ "working",
67871
+ "tree",
67872
+ "copy",
67873
+ "index",
67874
+ "staging",
67875
+ "cache",
67876
+ "repo",
67877
+ "repository",
67878
+ "it",
67879
+ "me",
67880
+ "us",
67881
+ "them",
67882
+ "this",
67883
+ "that",
67884
+ "the",
67885
+ "a",
67886
+ "an",
67887
+ "my",
67888
+ "our",
67889
+ "your",
67890
+ "their"
67891
+ ]), COMMAND_FUNCTION_EXCLUDED = /* @__PURE__ */ new Set([
67892
+ "and",
67893
+ "then",
67894
+ "so",
67895
+ "or",
67896
+ "but",
67897
+ "for",
67898
+ "in",
67899
+ "of",
67900
+ "on",
67901
+ "with",
67902
+ "from",
67903
+ "to",
67904
+ "into",
67905
+ "at",
67906
+ "by",
67907
+ "about",
67908
+ "between",
67909
+ "against",
67910
+ "since",
67911
+ "what",
67912
+ "which",
67913
+ "how",
67914
+ "who",
67915
+ "where",
67916
+ "why",
67917
+ "please",
67918
+ "now",
67919
+ "right",
67920
+ "here",
67921
+ "there",
67922
+ "yesterday",
67923
+ "today",
67924
+ "tomorrow",
67925
+ "week",
67926
+ "month",
67927
+ "year",
67928
+ "last",
67929
+ "past",
67930
+ "recent",
67931
+ "working",
67932
+ "tree",
67933
+ "copy",
67934
+ "index",
67935
+ "staging",
67936
+ "cache",
67937
+ "repo",
67938
+ "repository",
67939
+ "it",
67940
+ "me",
67941
+ "us",
67942
+ "them",
67943
+ "this",
67944
+ "that",
67945
+ "the",
67946
+ "a",
67947
+ "an",
67948
+ "my",
67949
+ "our",
67950
+ "your",
67951
+ "their"
67952
+ ]), REQUIRED_VALUE_SHORT_OPTIONS = /* @__PURE__ */ new Set(["-S", "-G", "-O", "-l", "-I"]), REQUIRED_VALUE_LONG_OPTIONS = /* @__PURE__ */ new Set([
67953
+ "--output",
67954
+ "--src-prefix",
67955
+ "--dst-prefix",
67956
+ "--line-prefix",
67957
+ "--diff-filter",
67958
+ "--color-moved-ws",
67959
+ "--word-diff-regex",
67960
+ "--ignore-matching-lines",
67961
+ "--anchored",
67962
+ "--find-object",
67963
+ "--diff-algorithm",
67964
+ "--inter-hunk-context",
67965
+ "--stat-width",
67966
+ "--stat-name-width",
67967
+ "--stat-count",
67968
+ "--stat-graph-width",
67969
+ "--ws-error-highlight",
67970
+ "--rotate-to",
67971
+ "--skip-to",
67972
+ "--output-indicator-new",
67973
+ "--output-indicator-old",
67974
+ "--output-indicator-context"
67975
+ ]);
67976
+ function tokenizeShellArgs(str, baseOffset = 0) {
67977
+ let tokens = [], i = 0, n = str.length;
67978
+ for (; i < n; ) {
67979
+ for (; i < n && str[i] !== `
67980
+ ` && /\s/.test(str[i]); )
67981
+ i++;
67982
+ if (i >= n) break;
67983
+ let tokenStart = i;
67984
+ if (str[i] === ";" || str[i] === "&" || str[i] === "|" || str[i] === `
67985
+ `) {
67986
+ let ctrl = str[i++];
67987
+ if (ctrl === `
67988
+ `)
67989
+ for (; i < n && (str[i] === `
67990
+ ` || str[i] === "\r"); )
67991
+ i++;
67992
+ else i < n && str[i] === ctrl && (ctrl === "&" || ctrl === "|" || ctrl === ";") && (ctrl += str[i++]);
67993
+ tokens.push({
67994
+ token: ctrl,
67995
+ raw: ctrl,
67996
+ isQuoted: !1,
67997
+ isControl: !0,
67998
+ start: tokenStart + baseOffset,
67999
+ end: i + baseOffset
68000
+ });
68001
+ continue;
68002
+ }
68003
+ let token = "", hasQuote = !1;
68004
+ for (; i < n && !/\s/.test(str[i]); ) {
68005
+ let ch = str[i];
68006
+ if (ch === ";" || ch === "&" || ch === "|" || ch === `
68007
+ `)
68008
+ break;
68009
+ if (ch === "\\")
68010
+ i++, i < n && (str[i] === `
68011
+ ` ? i++ : token += str[i++]);
68012
+ else if (ch === "'") {
68013
+ for (hasQuote = !0, i++; i < n && str[i] !== "'"; )
68014
+ token += str[i++];
68015
+ i < n && str[i] === "'" && i++;
68016
+ } else if (ch === '"') {
68017
+ for (hasQuote = !0, i++; i < n && str[i] !== '"'; )
68018
+ if (str[i] === "\\" && i + 1 < n) {
68019
+ let next = str[i + 1];
68020
+ next === `
68021
+ ` ? i += 2 : ((next === '"' || next === "\\" || next === "$" || next === "`") && i++, token += str[i++]);
68022
+ } else
68023
+ token += str[i++];
68024
+ i < n && str[i] === '"' && i++;
68025
+ } else if (ch === "`") {
68026
+ for (hasQuote = !0, i++; i < n && str[i] !== "`"; )
68027
+ str[i] === "\\" && i + 1 < n && i++, token += str[i++];
68028
+ i < n && str[i] === "`" && i++;
68029
+ } else
68030
+ token += str[i++];
68031
+ }
68032
+ if (i === tokenStart) {
68033
+ i++;
68034
+ continue;
68035
+ }
68036
+ if (!hasQuote && token === "")
68037
+ continue;
68038
+ let raw = str.slice(tokenStart, i);
68039
+ tokens.push({
68040
+ token,
68041
+ raw,
68042
+ isQuoted: hasQuote,
68043
+ start: tokenStart + baseOffset,
68044
+ end: i + baseOffset
68045
+ });
68046
+ }
68047
+ return tokens;
68048
+ }
68049
+ function parseGitDiffTokens(tokens) {
68050
+ for (let i = 0; i < tokens.length; i++) {
68051
+ let tok = tokens[i], { token } = tok;
68052
+ if (tok.isControl || token === "--")
68053
+ break;
68054
+ if (token.startsWith("-") && token !== "-") {
68055
+ if (token.includes("="))
68056
+ continue;
68057
+ if (REQUIRED_VALUE_SHORT_OPTIONS.has(token)) {
68058
+ i + 1 < tokens.length && !tokens[i + 1].isControl && tokens[i + 1].token !== "--" && i++;
68059
+ continue;
68060
+ }
68061
+ if (token.length > 2 && REQUIRED_VALUE_SHORT_OPTIONS.has(token.slice(0, 2)))
68062
+ continue;
68063
+ if (REQUIRED_VALUE_LONG_OPTIONS.has(token)) {
68064
+ i + 1 < tokens.length && !tokens[i + 1].isControl && tokens[i + 1].token !== "--" && i++;
68065
+ continue;
68066
+ }
68067
+ continue;
68068
+ }
68069
+ return tok;
68070
+ }
68071
+ }
68072
+ function extractComparisonBaseRefInfo(prompt, options) {
68073
+ let startIdx = 0;
68074
+ for (; startIdx < prompt.length && /\s/.test(prompt[startIdx]); )
68075
+ startIdx++;
68076
+ let endIdx = prompt.length - 1;
68077
+ for (; endIdx >= startIdx && /\s/.test(prompt[endIdx]); )
68078
+ endIdx--;
68079
+ let promptIndices = [], normalized = "";
68080
+ for (let i = startIdx; i <= endIdx; i++) {
68081
+ let ch = prompt[i];
68082
+ ch !== "`" && ch !== "'" && ch !== '"' && (promptIndices.push(i), normalized += ch);
68083
+ }
68084
+ function makeRefInfo(ref, isExplicit, matchIndex, matchLength, isDirectPromptMatch) {
68085
+ let res = { ref, isExplicit };
68086
+ if (options?.includeSpan && matchIndex !== void 0 && matchLength !== void 0 && matchIndex >= 0) {
68087
+ let start, end;
68088
+ if (isDirectPromptMatch)
68089
+ start = matchIndex, end = matchIndex + matchLength;
68090
+ else {
68091
+ start = promptIndices[matchIndex] ?? matchIndex;
68092
+ let endNormIdx = matchIndex + matchLength - 1;
68093
+ end = (promptIndices[endNormIdx] ?? endNormIdx) + 1;
68094
+ }
68095
+ start > 0 && /[`'"]/.test(prompt[start - 1]) && start--, end < prompt.length && /[`'"]/.test(prompt[end]) && end++, res.span = { start, end };
68096
+ }
68097
+ return res;
68098
+ }
68099
+ let quotedDiffOfRangeMatch = prompt.match(/\b(?:show|view|see)?\s*(?:the\s+)?(?:working\s+(?:tree|copy)\s+)?diff\s+of\s+(?:commit\s+)?([`'"])((?:[A-Za-z0-9_/@~^-]+(?:\.[A-Za-z0-9_/@~^-]+)*)?\.{2,3}(?:[A-Za-z0-9_/@~^-]+(?:\.[A-Za-z0-9_/@~^-]+)*)?)\1/i);
68100
+ if (quotedDiffOfRangeMatch) {
68101
+ let raw = stripSentencePunctuation(quotedDiffOfRangeMatch[2], { preserveRangeDots: !0 });
68102
+ if ((raw.includes("..") || raw.includes("...")) && SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-"))
68103
+ return makeRefInfo(raw, !0, quotedDiffOfRangeMatch.index, quotedDiffOfRangeMatch[0].length, !0);
68104
+ }
68105
+ let diffOfRangeMatch = normalized.match(/\b(?:show|view|see)?\s*(?:the\s+)?(?:working\s+(?:tree|copy)\s+)?diff\s+of\s+(?:commit\s+)?((?:[A-Za-z0-9_/@~^-]+(?:\.[A-Za-z0-9_/@~^-]+)*)?\.{2,3}[A-Za-z0-9_/@~^-]+(?:\.[A-Za-z0-9_/@~^-]+)*)(?![0-9A-Za-z_/@~^-]|\.[0-9A-Za-z_/@~^-])/i);
68106
+ if (diffOfRangeMatch) {
68107
+ let raw = stripSentencePunctuation(diffOfRangeMatch[1], { preserveRangeDots: !0 });
68108
+ if ((raw.includes("..") || raw.includes("...")) && SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-"))
68109
+ return makeRefInfo(raw, !0, diffOfRangeMatch.index, diffOfRangeMatch[0].length, !1);
68110
+ }
68111
+ let quotedBetweenMatch = prompt.match(/\b(?:difference|diff)\s+between\s+(?:my\s+|the\s+)?working\s+(?:tree|copy)\s+and\s+(?:(?:the\s+)?commit\s+)?([`'"])([^`'"]+)\1/i) || prompt.match(/\b(?:difference|diff)\s+between\s+(?:(?:the\s+)?commit\s+)?([`'"])([^`'"]+)\1\s+and\s+(?:my\s+|the\s+)?working\s+(?:tree|copy)/i);
68112
+ if (quotedBetweenMatch) {
68113
+ let raw = stripSentencePunctuation(quotedBetweenMatch[2].trim(), { preserveRangeDots: !0 }), lower = raw.toLowerCase();
68114
+ if (!EXPLICIT_STRUCTURAL_EXCLUDED.has(lower) && SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-") && raw !== "--")
68115
+ return makeRefInfo(lower === "head" ? "HEAD" : raw, !0, quotedBetweenMatch.index, quotedBetweenMatch[0].length, !0);
68116
+ }
68117
+ let betweenMatch = normalized.match(/\b(?:difference|diff)\s+between\s+(?:my\s+|the\s+)?working\s+(?:tree|copy)\s+and\s+(?:\b(?:the\s+)?(?<mod>commit)\s+(?!(?:and|or|then|vs|but|so|also|with|to|for|from|please|pls|kindly|in)(?![a-zA-Z0-9_/@~^-]|\.+[a-zA-Z0-9_/@~^-])))?(?<ref>[a-zA-Z0-9_./@~^-]+)(?![0-9A-Za-z_/@~^-]|\.+[0-9A-Za-z_/@~^-])/i) || normalized.match(/\b(?:difference|diff)\s+between\s+(?:\b(?:the\s+)?(?<mod>commit)\s+(?!(?:and|or|then|vs|but|so|also|with|to|for|from)(?![a-zA-Z0-9_/@~^-]|\.+[a-zA-Z0-9_/@~^-])))?(?<ref>[a-zA-Z0-9_./@~^-]+)\s+and\s+(?:my\s+|the\s+)?working\s+(?:tree|copy)\b/i);
68118
+ if (betweenMatch) {
68119
+ let raw = stripSentencePunctuation(betweenMatch.groups?.ref || betweenMatch[1]), lower = raw.toLowerCase();
68120
+ if (!EXPLICIT_STRUCTURAL_EXCLUDED.has(lower) && SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-")) {
68121
+ let isExplicit = !!betweenMatch.groups?.mod ? hasRefSyntax(raw) : !0;
68122
+ return makeRefInfo(lower === "head" ? "HEAD" : raw, isExplicit, betweenMatch.index, betweenMatch[0].length, !1);
68123
+ }
68124
+ }
68125
+ let quotedDiffAgainstMatch = prompt.match(/\b(?:show|view|see)?\s*(?:the\s+)?(?:working\s+(?:tree|copy)\s+)?diff\s+against\s+(?:(?:the\s+)?commit\s+)?([`'"])([^`'"]+)\1/i);
68126
+ if (quotedDiffAgainstMatch) {
68127
+ let raw = stripSentencePunctuation(quotedDiffAgainstMatch[2], { preserveRangeDots: !0 }), lower = raw.toLowerCase();
68128
+ if (!EXPLICIT_STRUCTURAL_EXCLUDED.has(lower) && SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-"))
68129
+ return makeRefInfo(lower === "head" ? "HEAD" : raw, !0, quotedDiffAgainstMatch.index, quotedDiffAgainstMatch[0].length, !0);
68130
+ }
68131
+ let diffAgainstMatch = normalized.match(/\bdiff\s+against\s+(?:\b(?:the\s+)?(?<mod>commit)\s+(?!(?:and|or|then|vs|but|so|also|with|to|for|from|please|pls|kindly|in)(?![a-zA-Z0-9_/@~^-]|\.+[a-zA-Z0-9_/@~^-])))?(?<ref>[a-zA-Z0-9_./@~^-]+)(?![0-9A-Za-z_/@~^-]|\.+[0-9A-Za-z_/@~^-])/i);
68132
+ if (diffAgainstMatch) {
68133
+ let raw = stripSentencePunctuation(diffAgainstMatch.groups?.ref || diffAgainstMatch[1]), lower = raw.toLowerCase();
68134
+ if (!EXPLICIT_STRUCTURAL_EXCLUDED.has(lower) && SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-")) {
68135
+ let isExplicit = !!diffAgainstMatch.groups?.mod ? hasRefSyntax(raw) : !0;
68136
+ return makeRefInfo(lower === "head" ? "HEAD" : raw, isExplicit, diffAgainstMatch.index, diffAgainstMatch[0].length, !1);
68137
+ }
68138
+ }
68139
+ if (/\b(?:git|repo|repository|working\s+(?:tree|copy)|uncommitted|diff|changes?)\b/i.test(normalized)) {
68140
+ let quotedCompareAgainstMatch = prompt.match(/\bcompare\s+(?:my\s+)?working\s+(?:tree|copy)\s+(?:to|with|against)\s+(?:(?:the\s+)?commit\s+)?([`'"])([^`'"]+)\1/i) || prompt.match(/\bcompare\s+against\s+(?:(?:the\s+)?commit\s+)?([`'"])([^`'"]+)\1/i) || prompt.match(/\bcompare\s+(?:(?:the\s+)?commit\s+)?([`'"])([^`'"]+)\1\s+(?:to|with|against)\s+(?:my\s+)?working\s+(?:tree|copy)\b/i);
68141
+ if (quotedCompareAgainstMatch) {
68142
+ let raw = stripSentencePunctuation(quotedCompareAgainstMatch[2].trim(), { preserveRangeDots: !0 }), lower = raw.toLowerCase();
68143
+ if (!EXPLICIT_STRUCTURAL_EXCLUDED.has(lower) && SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-") && raw !== "--")
68144
+ return makeRefInfo(lower === "head" ? "HEAD" : raw, !0, quotedCompareAgainstMatch.index, quotedCompareAgainstMatch[0].length, !0);
68145
+ }
68146
+ let compareAgainstMatch = normalized.match(/\bcompare\s+(?:my\s+)?working\s+(?:tree|copy)\s+(?:to|with|against)\s+(?:\b(?:the\s+)?(?<mod>commit)\s+(?!(?:and|or|then|vs|but|so|also|with|to|for|from|please|pls|kindly|in)(?![a-zA-Z0-9_/@~^-]|\.+[a-zA-Z0-9_/@~^-])))?(?<ref>[a-zA-Z0-9_./@~^-]+)(?![0-9A-Za-z_/@~^-]|\.+[0-9A-Za-z_/@~^-])/i) || normalized.match(/\bcompare\s+against\s+(?:\b(?:the\s+)?(?<mod>commit)\s+(?!(?:and|or|then|vs|but|so|also|with|to|for|from|please|pls|kindly|in)(?![a-zA-Z0-9_/@~^-]|\.+[a-zA-Z0-9_/@~^-])))?(?<ref>[a-zA-Z0-9_./@~^-]+)(?![0-9A-Za-z_/@~^-]|\.+[0-9A-Za-z_/@~^-])/i) || normalized.match(/\bcompare\s+(?:\b(?:the\s+)?(?<mod>commit)\s+(?!(?:and|or|then|vs|but|so|also|with|to|for|from)(?![a-zA-Z0-9_/@~^-]|\.+[a-zA-Z0-9_/@~^-])))?(?<ref>[a-zA-Z0-9_./@~^-]+)\s+(?:to|with|against)\s+(?:my\s+)?working\s+(?:tree|copy)\b/i);
68147
+ if (compareAgainstMatch) {
68148
+ let raw = stripSentencePunctuation(compareAgainstMatch.groups?.ref || compareAgainstMatch[1]), lower = raw.toLowerCase();
68149
+ if (!EXPLICIT_STRUCTURAL_EXCLUDED.has(lower) && SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-")) {
68150
+ let isExplicit = !!compareAgainstMatch.groups?.mod ? hasRefSyntax(raw) : !0;
68151
+ return makeRefInfo(lower === "head" ? "HEAD" : raw, isExplicit, compareAgainstMatch.index, compareAgainstMatch[0].length, !1);
68152
+ }
68153
+ }
68154
+ }
68155
+ let showDiffAgainstMatch = normalized.match(/\b(?:show|view|see)\s+(?:the\s+)?(?:working\s+(?:tree|copy)\s+)?diff\s+against\s+(?:\b(?:the\s+)?(?<mod>commit)\s+(?!(?:and|or|then|vs|but|so|also|with|to|for|from|please|pls|kindly|in)(?![a-zA-Z0-9_/@~^-]|\.+[a-zA-Z0-9_/@~^-])))?(?<ref>[a-zA-Z0-9_./@~^-]+)(?![0-9A-Za-z_/@~^-]|\.+[0-9A-Za-z_/@~^-])/i);
68156
+ if (showDiffAgainstMatch) {
68157
+ let raw = stripSentencePunctuation(showDiffAgainstMatch.groups?.ref || showDiffAgainstMatch[1]), lower = raw.toLowerCase();
68158
+ if (!EXPLICIT_STRUCTURAL_EXCLUDED.has(lower) && SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-")) {
68159
+ let isExplicit = !!showDiffAgainstMatch.groups?.mod ? hasRefSyntax(raw) : !0;
68160
+ return makeRefInfo(lower === "head" ? "HEAD" : raw, isExplicit, showDiffAgainstMatch.index, showDiffAgainstMatch[0].length, !1);
68161
+ }
68162
+ }
68163
+ let quotedDiffToMatch = prompt.match(/\b(?:show|view|see)?\s*(?:the\s+)?(?:working\s+(?:tree|copy)\s+)?diff\s+to\s+(?:(?:the\s+)?commit\s+)?([`'"])([^`'"]+)\1/i);
68164
+ if (quotedDiffToMatch) {
68165
+ let raw = stripSentencePunctuation(quotedDiffToMatch[2], { preserveRangeDots: !0 }), lower = raw.toLowerCase();
68166
+ if (!EXPLICIT_STRUCTURAL_EXCLUDED.has(lower) && SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-"))
68167
+ return makeRefInfo(lower === "head" ? "HEAD" : raw, !0, quotedDiffToMatch.index, quotedDiffToMatch[0].length, !0);
68168
+ }
68169
+ let diffToMatch = normalized.match(/\bdiff\s+to\s+(?:\b(?:the\s+)?(?<mod>commit)\s+(?!(?:and|or|then|vs|but|so|also|with|to|for|from|please|pls|kindly|in)(?![a-zA-Z0-9_/@~^-]|\.+[a-zA-Z0-9_/@~^-])))?(?<ref>[a-zA-Z0-9_./@~^-]+)(?![0-9A-Za-z_/@~^-]|\.+[0-9A-Za-z_/@~^-])/i) || normalized.match(/\b(?:show|view|see)\s+(?:the\s+)?(?:working\s+(?:tree|copy)\s+)?diff\s+to\s+(?:\b(?:the\s+)?(?<mod>commit)\s+(?!(?:and|or|then|vs|but|so|also|with|to|for|from|please|pls|kindly|in)(?![a-zA-Z0-9_/@~^-]|\.+[a-zA-Z0-9_/@~^-])))?(?<ref>[a-zA-Z0-9_./@~^-]+)(?![0-9A-Za-z_/@~^-]|\.+[0-9A-Za-z_/@~^-])/i);
68170
+ if (diffToMatch) {
68171
+ let raw = stripSentencePunctuation(diffToMatch.groups?.ref || diffToMatch[1]), lower = raw.toLowerCase();
68172
+ if (!PURPOSE_VERBS.has(lower) && !EXPLICIT_STRUCTURAL_EXCLUDED.has(lower) && !TEMPORAL_WORDS.has(lower) && SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-")) {
68173
+ let isExplicit = !!diffToMatch.groups?.mod ? hasRefSyntax(raw) : hasRefSyntax(raw) || isLastTokenInPrompt(normalized, raw);
68174
+ return makeRefInfo(lower === "head" ? "HEAD" : raw, isExplicit, diffToMatch.index, diffToMatch[0].length, !1);
68175
+ }
68176
+ }
68177
+ let quotedDiffWithMatch = prompt.match(/\b(?:show|view|see)?\s*(?:the\s+)?(?:working\s+(?:tree|copy)\s+)?diff\s+with\s+(?:(?:the\s+)?commit\s+)?([`'"])([^`'"]+)\1/i);
68178
+ if (quotedDiffWithMatch) {
68179
+ let raw = stripSentencePunctuation(quotedDiffWithMatch[2], { preserveRangeDots: !0 }), lower = raw.toLowerCase();
68180
+ if (!EXPLICIT_STRUCTURAL_EXCLUDED.has(lower) && SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-"))
68181
+ return makeRefInfo(lower === "head" ? "HEAD" : raw, !0, quotedDiffWithMatch.index, quotedDiffWithMatch[0].length, !0);
68182
+ }
68183
+ let diffWithMatch = normalized.match(/\bdiff\s+with\s+(?:\b(?:the\s+)?(?<mod>commit)\s+(?!(?:and|or|then|vs|but|so|also|with|to|for|from|please|pls|kindly|in)(?![a-zA-Z0-9_/@~^-]|\.+[a-zA-Z0-9_/@~^-])))?(?!line\s+numbers?\b|numbers?\b|(?:more|full|extra)\s+(?:detail|details|context|info|information)\b)(?<ref>[a-zA-Z0-9_./@~^-]+)(?![0-9A-Za-z_/@~^-]|\.+[0-9A-Za-z_/@~^-])/i) || normalized.match(/\b(?:show|view|see)\s+(?:the\s+)?(?:working\s+(?:tree|copy)\s+)?diff\s+with\s+(?:\b(?:the\s+)?(?<mod>commit)\s+(?!(?:and|or|then|vs|but|so|also|with|to|for|from|please|pls|kindly|in)(?![a-zA-Z0-9_/@~^-]|\.+[a-zA-Z0-9_/@~^-])))?(?!line\s+numbers?\b|numbers?\b|(?:more|full|extra)\s+(?:detail|details|context|info|information)\b)(?<ref>[a-zA-Z0-9_./@~^-]+)(?![0-9A-Za-z_/@~^-]|\.+[0-9A-Za-z_/@~^-])/i);
68184
+ if (diffWithMatch) {
68185
+ let raw = stripSentencePunctuation(diffWithMatch.groups?.ref || diffWithMatch[1]), lower = raw.toLowerCase();
68186
+ if (!PRESENTATION_WORDS.has(lower) && !EXPLICIT_STRUCTURAL_EXCLUDED.has(lower) && !TEMPORAL_WORDS.has(lower) && SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-")) {
68187
+ let isExplicit = !!diffWithMatch.groups?.mod ? hasRefSyntax(raw) : hasRefSyntax(raw) || isLastTokenInPrompt(normalized, raw);
68188
+ return makeRefInfo(lower === "head" ? "HEAD" : raw, isExplicit, diffWithMatch.index, diffWithMatch[0].length, !1);
68189
+ }
68190
+ }
68191
+ let quotedSinceMatch = prompt.match(/\b(?:diff|changes)\s+since\s+(?:(?:the\s+)?commit\s+)?([`'"])([^`'"]+)\1/i), diffSinceMatch = quotedSinceMatch || normalized.match(/\b(?:show|view|see|give|get|display|print)?\s*(?:me\s+)?(?:the\s+|my\s+)?(diff|changes)\s+since\s+(?:\b(?:the\s+)?(?<mod>commit)\s+(?!(?:and|or|then|vs|but|so|also|with|to|for|from|please|pls|kindly|in)(?![a-zA-Z0-9_/@~^-]|\.+[a-zA-Z0-9_/@~^-]|\.\.)))?(?<ref>[a-zA-Z0-9_./@~^-]+)(?![0-9A-Za-z_/@~^-]|\.+[0-9A-Za-z_/@~^-]|\.\.)/i);
68192
+ if (diffSinceMatch && !/\bbreaking\s+changes\b/i.test(normalized)) {
68193
+ let isQuoted = !!quotedSinceMatch, raw = isQuoted ? stripSentencePunctuation(quotedSinceMatch[2].trim(), { preserveRangeDots: !0 }) : stripSentencePunctuation(diffSinceMatch.groups?.ref || diffSinceMatch[2]), lower = raw.toLowerCase(), hasGitContext = /\b(?:repo|repository|branch|commit|head|working\s+(?:tree|copy)|git|diff)\b/i.test(normalized), namedLibraryMatch = NON_GIT_SOFTWARE_LIBRARIES.has(lower), escapedRaw = raw.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), versionAfterRawMatch = new RegExp(`\\bsince\\s+${escapedRaw}\\s+(?:v?\\d+(?:\\.\\d+)*)\\b(?!\\s+(?:days?|weeks?|months?|years?|hours?|minutes?))`, "i").test(normalized), dottedVersionMatch = /\bsince\s+v?\d+\.\d+(?:\.\d+)*\b/i.test(normalized), isLibraryInquiry = !isQuoted && !hasRefSyntax(raw) && !hasGitContext && (namedLibraryMatch || versionAfterRawMatch || dottedVersionMatch), isCalendarDate = /^\d{4}[-/]\d{1,2}[-/]\d{1,2}$/.test(raw), isFourDigitYear = /^(?:19|20)\d{2}$/.test(raw), isTemporal = !isQuoted && (isCalendarDate || isFourDigitYear || !hasRefSyntax(raw) && (TEMPORAL_WORDS.has(lower) || /\bsince\s+\d+\s+(?:days?|weeks?|months?|years?|hours?|minutes?)\s+ago\b/i.test(normalized) || /\bsince\s+(?:last|this)\s+(?:week|month|year|monday|tuesday|wednesday|thursday|friday|saturday|sunday)\b/i.test(normalized))), isExcluded = !isQuoted && (COMMAND_FUNCTION_EXCLUDED.has(lower) || TEMPORAL_WORDS.has(lower) || PRESENTATION_WORDS.has(lower));
68194
+ if (!isLibraryInquiry && (!isTemporal || isQuoted) && !isExcluded && SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-")) {
68195
+ let isHexHash = /^[0-9a-fA-F]{4,40}$/.test(raw), isAllDigits = /^\d+$/.test(raw), hasMod = !isQuoted && !!diffSinceMatch.groups?.mod, isExplicit = isQuoted ? !0 : hasMod ? hasRefSyntax(raw) : hasRefSyntax(raw) && !isAllDigits || !isHexHash && !isAllDigits && isLastTokenInPrompt(normalized, raw);
68196
+ return makeRefInfo(lower === "head" ? "HEAD" : raw, isExplicit, diffSinceMatch.index, diffSinceMatch[0].length, isQuoted);
68197
+ }
68198
+ }
68199
+ let quotedWholeGitDiffMatch = prompt.match(/([`'"])\s*git\s+diff\b([\s\S]*?)\1/i);
68200
+ if (quotedWholeGitDiffMatch) {
68201
+ let isLiteralStart = /^\s*[`'"]/.test(prompt), innerStr = quotedWholeGitDiffMatch[0].slice(1, -1), innerTokens = tokenizeShellArgs(innerStr);
68202
+ if (innerTokens.length >= 2 && innerTokens[0].token.toLowerCase() === "git" && innerTokens[1].token.toLowerCase() === "diff") {
68203
+ let operand = parseGitDiffTokens(innerTokens.slice(2));
68204
+ if (operand && !/[\s\x00-\x1f\x7f]/.test(operand.token)) {
68205
+ let raw = stripSentencePunctuation(operand.token, { preserveRangeDots: !0 }), lower = raw.toLowerCase();
68206
+ if (!COMMAND_FUNCTION_EXCLUDED.has(lower) && SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-") && raw !== "--") {
68207
+ let isExplicit = isLiteralStart || operand.isQuoted ? !0 : hasRefSyntax(raw);
68208
+ return makeRefInfo(lower === "head" ? "HEAD" : raw, isExplicit, quotedWholeGitDiffMatch.index, quotedWholeGitDiffMatch[0].length, !0);
68209
+ }
68210
+ }
68211
+ }
68212
+ }
68213
+ let literalGitDiffMatch = prompt.match(/^\s*git\s+diff\b([\s\S]*)/i);
68214
+ if (literalGitDiffMatch) {
68215
+ let cmdTokens = tokenizeShellArgs(literalGitDiffMatch[0]);
68216
+ if (cmdTokens.length >= 2 && cmdTokens[0].token.toLowerCase() === "git" && cmdTokens[1].token.toLowerCase() === "diff") {
68217
+ let operand = parseGitDiffTokens(cmdTokens.slice(2));
68218
+ if (operand && !/[\s\x00-\x1f\x7f]/.test(operand.token)) {
68219
+ let raw = stripSentencePunctuation(operand.token, { preserveRangeDots: !0 }), lower = raw.toLowerCase();
68220
+ if (!COMMAND_FUNCTION_EXCLUDED.has(lower) && SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-") && raw !== "--")
68221
+ return makeRefInfo(lower === "head" ? "HEAD" : raw, !0, literalGitDiffMatch.index, operand.end, !0);
68222
+ }
68223
+ }
68224
+ }
68225
+ let openExcluded = /* @__PURE__ */ new Set([
68226
+ ...EXPLICIT_STRUCTURAL_EXCLUDED,
68227
+ ...PURPOSE_VERBS,
68228
+ ...PRESENTATION_WORDS,
68229
+ ...COMMAND_FUNCTION_EXCLUDED,
68230
+ "vs",
68231
+ "changes",
68232
+ "files",
68233
+ "file",
68234
+ "code",
68235
+ "workspace",
68236
+ "directory",
68237
+ "folder",
68238
+ "project",
68239
+ "stat",
68240
+ "stats",
68241
+ "cached",
68242
+ "staged",
68243
+ "summary",
68244
+ "details",
68245
+ "detail",
68246
+ "show",
68247
+ "shows",
68248
+ "display",
68249
+ "displays",
68250
+ "view",
68251
+ "see",
68252
+ "check",
68253
+ "inspect",
68254
+ "all",
68255
+ "only",
68256
+ "just"
68257
+ ]), conversationalGitDiffMatch = prompt.match(/\bgit\s+diff\b([\s\S]*)/i);
68258
+ if (conversationalGitDiffMatch) {
68259
+ let cmdTokens = tokenizeShellArgs(conversationalGitDiffMatch[0]);
68260
+ if (cmdTokens.length >= 2 && cmdTokens[0].token.toLowerCase() === "git" && cmdTokens[1].token.toLowerCase() === "diff") {
68261
+ let operand = parseGitDiffTokens(cmdTokens.slice(2));
68262
+ if (operand && !/[\s\x00-\x1f\x7f]/.test(operand.token)) {
68263
+ let raw = stripSentencePunctuation(operand.token, { preserveRangeDots: !0 }), lower = raw.toLowerCase();
68264
+ if (operand.isQuoted) {
68265
+ if (SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-") && raw !== "--")
68266
+ return makeRefInfo(lower === "head" ? "HEAD" : raw, !0, conversationalGitDiffMatch.index, operand.end, !0);
68267
+ } else if (!openExcluded.has(lower) && SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-") && raw !== "--")
68268
+ return makeRefInfo(lower === "head" ? "HEAD" : raw, !1, conversationalGitDiffMatch.index, operand.end, !0);
68269
+ }
68270
+ }
68271
+ }
68272
+ let openEndedConversationalMatch = normalized.match(/\b(?:git|repo|working\s+(?:tree|copy)|uncommitted)\s+(?:changes?|diff)\s+(?:since|against)\s+([a-zA-Z0-9_./@~^-]+)(?![0-9A-Za-z_/@~^-]|\.+[0-9A-Za-z_/@~^-]|\.\.)/i) || normalized.match(/\b(?:changes?|diff)\s+in\s+(?:the\s+|my\s+)?(?:working\s+(?:tree|copy)|repo|repository)\s+(?:since|against)\s+([a-zA-Z0-9_./@~^-]+)(?![0-9A-Za-z_/@~^-]|\.+[0-9A-Za-z_/@~^-]|\.\.)/i);
68273
+ if (openEndedConversationalMatch) {
68274
+ let raw = stripSentencePunctuation(openEndedConversationalMatch[1]), lower = raw.toLowerCase();
68275
+ if (!openExcluded.has(lower) && !TEMPORAL_WORDS.has(lower) && SAFE_REF_PATTERN.test(raw) && !raw.startsWith("-") && raw !== "--")
68276
+ return makeRefInfo(lower === "head" ? "HEAD" : raw, !1, openEndedConversationalMatch.index, openEndedConversationalMatch[0].length, !1);
68277
+ }
68278
+ }
68279
+ function extractComparisonBaseRef(prompt) {
68280
+ return extractComparisonBaseRefInfo(prompt)?.ref;
68281
+ }
68282
+ function isConceptualGitQuestion(prompt) {
68283
+ let normalized = prompt.replace(/[\u2018\u2019]/g, "'").replace(/[`'"]/g, "").trim();
68284
+ if (/\b[0-9a-f]{6,40}\b/i.test(normalized) || /^(?:git\s+(?:status|diff|show|log|branch)|(?:check|inspect|examine|look\s+at)\s+(?:my|our|the)\s+(?:repo|repository|working\s+(?:tree|copy)|index|staging|workspace)|(?:show|view|see|display|print)\s+(?:(?:me\s+)?(?:my|our)\s+)?(?:staged\s+(?:changes|diff)|working\s+(?:tree|copy)|untracked))\b/i.test(normalized) || /\b(?:what\s+does|how\s+does)\s+git\s+(?:status|diff)\s+(?:look\s+like|show)\s+(?:right\s+now|currently|in\s+(?:my|our|the)\s+working\s+(?:tree|copy)|in\s+(?:my|our|the)\s+(?:repo|repository)|in\s+my\s+workspace)\b/i.test(normalized))
68285
+ return !1;
68286
+ if (/^(?:(?:can|could|would)\s+you\s+(?:please\s+)?(?:tell|show|explain)\s+(?:to\s+)?me\s+)?(?:please\s+)?(?:how\s+(?:do\s+i|can\s+i|to|should\s+i)|(?:explain|describe|tell\s+me)\s+how\s+to)\s+(?:commit|stage|unstage|discard|add|push|pull|revert|reset|restore|checkout|switch|merge|rebase|stash|tag|diff|use)\b/i.test(normalized) || (/^(?:(?:can|could|would)\s+you\s+(?:please\s+)?(?:tell|show|explain)\s+(?:to\s+)?me\s+)?(?:please\s+)?what\s+does\s+git\s+(?:status|diff|checkout|switch|merge|rebase|commit|branch|log|show|reset|restore|stash)\s+(?:do|mean)\b/i.test(normalized) || /^(?:(?:can|could|would)\s+you\s+)?(?:please\s+)?(?:(?:explain|describe|tell\s+me)\s+)?how\s+(?:does\s+)?git\s+(?:status|diff|checkout|switch|merge|rebase|log|commit|branch)\s+(?:works?|compares?|shows?|handles?|displays?)\b/i.test(normalized) || /^(?:(?:can|could|would)\s+you\s+)?(?:please\s+)?(?:explain|describe)\s+how\s+(?:the\s+|git\s+)?working\s+(?:tree|copy)\s+works?\b/i.test(normalized) || /^(?:(?:can|could|would)\s+you\s+)?(?:please\s+)?(?:explain|describe)\s+(?:the\s+)?concept\s+of\s+(?:the\s+)?(?:git\s+)?(?:working\s+(?:tree|copy)|status|diff|index|staging)\b/i.test(normalized)) && !/\b(?:right\s+now|currently|in\s+my\s+working\s+(?:tree|copy))\b/i.test(normalized))
68287
+ return !0;
68288
+ if (/\b(?:my|our)\s+(?:working\s+(?:tree|copy)|index|staging(?:\s+area)?|staged\s+(?:diff|changes)|repo|repository)\b/i.test(normalized) || /\b(?:staged\s+diff|staged\s+changes|changes\s+in\s+(?:the\s+)?(?:index|staging(?:\s+area)?)|what(?:s|\s+is)\s+in\s+(?:my\s+|the\s+)?staging(?:\s+area)?)\b/i.test(normalized) || /\b(?:my\s+working\s+(?:tree|copy)\s+and\s+(?:head|[a-z0-9_./@~^-]+)|(?:head|[a-z0-9_./@~^-]+)\s+and\s+my\s+working\s+(?:tree|copy))\b/i.test(normalized) || /\b(?:how\s+(?:is|does)\s+my\s+working\s+(?:tree|copy)|what\s+does\s+my\s+working\s+(?:tree|copy)\s+look\s+like|is\s+my\s+working\s+(?:tree|copy)\s+clean)\b/i.test(normalized) || /\b(?:working\s+(?:tree|copy)|repo|repository|git\s+status|status)\s+(?:look\s+like\s+)?after\s+(?:the\s+)?(?:rebase|merge|reset|pull|commit|checkout)\b/i.test(normalized) || /\b(?:changes?\s+in\s+(?:the\s+)?working\s+(?:tree|copy)\s+(?:since|against)\s+HEAD|working\s+(?:tree|copy)\s+diff\s+(?:against|since)\s+HEAD)\b/i.test(normalized) || /\blook\s+like\s+right\s+now\b/i.test(normalized) || /\b(?:right\s+now|currently)\b/i.test(normalized))
68289
+ return !1;
68290
+ let compTargets = "(?:the\\s+)?(?:working\\s+(?:tree|copy)|index|staging(?:\\s+area)?|cache|head)";
68291
+ return !!(new RegExp(
68292
+ `^(?:(?:can|could|would)\\s+you\\s+)?(?:please\\s+)?(?:explain|describe|what(?:s|\\s+is))\\s+(?:(?:the\\s+)?(?:difference|distinction)\\s+between\\s+${compTargets}\\s+and\\s+${compTargets}|how\\s+${compTargets}\\s+(?:differs?|is\\s+different)\\s+from\\s+${compTargets}|${compTargets}\\s+(?:versus|vs\\.?)\\s+${compTargets}|(?:how\\s+to\\s+compare|comparing)\\s+${compTargets}\\s+(?:and|with|to|versus|vs\\.?)\\s+${compTargets})\\b`,
68293
+ "i"
68294
+ ).test(normalized) || /^(?:explain|describe|what\s+is\s+(?:the\s+)?difference\s+between|what\s+is)\s+(?:git\s+)?(?:status|diff|log|branch|show|rebase|merge|commit|stash|checkout|switch|reset|restore)\s*[\?.,]?$/i.test(normalized) || /^(?:explain|describe)\s+(?:the\s+)?(?:git\s+)?(?:status|diff|log|branch|show|rebase|merge|commit|stash|checkout|switch|reset|restore)\s+(?:command|concept|operation|workflow)\b/i.test(normalized) || /\b(?:how\s+does\s+git|what\s+does\s+git|what\s+is\s+git|difference\s+between\s+git|explain\s+(?:what\s+|the\s+|how\s+)?git|why\s+does\s+git|how\s+(?:to|do\s+i|can\s+i)\s+use\s+git|tutorial\s+on\s+git|syntax\s+(?:for|of)\s+git)\b/i.test(normalized) || /\b(?:difference\s+between\s+(?:git\s+)?[a-z-]+\s+and\s+(?:git\s+)?[a-z-]+|how\s+does\s+(?:git\s+)?[a-z-]+\s+work)\b/i.test(normalized) || /\b(?:should\s+(?:i|we)\s+use\s+(?:git\s+)?[a-z-]+\s+or\s+(?:git\s+)?[a-z-]+)\b/i.test(normalized) || /\b(?:how\s+(?:do\s+i|to|can\s+i)\s+(?:undo|revert|reset|restore|abort|resolve|cancel)\s+(?:a\s+)?(?:git\s+)?[a-z-]+)\b/i.test(normalized) || /^(?:how|why|should|when|what\s+does)\b.*\b(?:git\s+)?(?:checkout|switch|rebase|merge|commit|stash|reset|restore|cherry-pick)\b/i.test(normalized));
68295
+ }
68296
+ function isStagedDiffIntent(prompt) {
68297
+ if (/^\s*(?:\/|!)?git\s+diff\b/i.test(prompt)) {
68298
+ let [optionsPart2] = prompt.split(/\s+--\s+/);
68299
+ return /(?:^|\s)(?:--cached|--staged)(?:\s|$)/i.test(optionsPart2);
68300
+ }
68301
+ let [optionsPart] = prompt.split(/\s+--\s+/);
68302
+ return !!(/(?:^|\s)(?:--cached|--staged)(?:\s|$)/i.test(optionsPart) || /\b(?:staged\s+diff|cached\s+diff|staged\s+changes|cached\s+changes|changes\s+in\s+the\s+index|in\s+the\s+index|to\s+be\s+committed)\b/i.test(optionsPart) || /\b(?:diff|show|what\s+is)\s+(?:of\s+)?(?:the\s+)?(?:staged|cached)\b/i.test(optionsPart) && !/\b(?:staged|cached)\.[a-z0-9]+\b/i.test(optionsPart));
68303
+ }
68304
+ function isQualifiedAdvisoryRunner(runner) {
68305
+ if (!runner) return !1;
68306
+ if (runner?.opts?.localRunner)
68307
+ return isQualifiedAdvisoryRunner(runner.opts.localRunner);
68308
+ if (runner?.localRunner)
68309
+ return isQualifiedAdvisoryRunner(runner.localRunner);
68310
+ if (runner?.opts?.hostedRunner)
68311
+ return isQualifiedAdvisoryRunner(runner.opts.hostedRunner);
68312
+ if (runner?.hostedRunner)
68313
+ return isQualifiedAdvisoryRunner(runner.hostedRunner);
68314
+ let label = runner.runtimeLabel?.toLowerCase() || "";
68315
+ return label.includes("claude") || label.includes("anthropic") || label.includes("composite") || runner.providerKind === "composite" || label.startsWith("local-gemma-process:") ? !1 : typeof runner.generateAdvisory == "function";
68316
+ }
68317
+ function resolvePreferredAdvisoryRunner(runner) {
68318
+ if (!runner) return null;
68319
+ if (!!runner?.opts) {
68320
+ let localCandidate = runner?.opts?.localRunner ?? runner?.localRunner ?? null;
68321
+ return localCandidate && isQualifiedAdvisoryRunner(localCandidate) ? localCandidate : null;
68322
+ }
68323
+ return (runner.providerKind === "local" || !runner.providerKind && (runner.runtimeLabel?.startsWith("local-gemma-ollama:") || runner.runtimeLabel?.startsWith("local-gemma-process:"))) && isQualifiedAdvisoryRunner(runner) ? runner : null;
68324
+ }
68325
+ function renderLocalGemmaGitStatusQueryPrompt(args) {
68326
+ let sanitized = sanitizeForTerminal(redactAbsoluteLocalPaths(args.userPrompt)).trim();
68327
+ return [
68328
+ "You are a semantic Git query analyzer for CodeVibe.",
68329
+ "Classify the user prompt to determine if they are asking about Git repository status, diffs, commits, history, branches, or working tree state.",
68330
+ "Respond with STRICT JSON ONLY. Do not emit markdown formatting or conversational text outside the JSON object.",
68331
+ "",
68332
+ "JSON Schema:",
68333
+ "{",
68334
+ ' "isGitQuery": boolean, // true if prompt is asking to inspect git status, diffs, log, commit history, branch, or working tree state. false if asking about task progress ("what is the status of the task"), conceptual questions or explanations ("how does git status work", "difference between checkout and switch"), building, coding, or unrelated topics.',
68335
+ ' "asksStatus": boolean, // true if asking about working tree state, untracked/modified/staged files, or git status',
68336
+ ' "asksDiff": boolean, // true if asking for working tree diffs or uncommitted changes (NOT a commit diff)',
68337
+ ' "asksLog": boolean, // true if asking for commit history, recent commits, or git log',
68338
+ ' "asksBranch": boolean, // true if asking to list branches or current branch',
68339
+ ' "showTarget": string | null, // commit hash, ref, or "HEAD" if asking for diff/details of a specific commit (e.g. "diff of most recent commit" -> "HEAD")',
68340
+ ' "targetRepo": string | null // repository name if an explicit sub-repository or sibling repo was mentioned, otherwise null',
68341
+ "}",
68342
+ "",
68343
+ "Examples:",
68344
+ 'User: "Show me the diff of the most recent commit on this branch"',
68345
+ '{"isGitQuery":true,"asksStatus":false,"asksDiff":false,"asksLog":false,"asksBranch":false,"showTarget":"HEAD","targetRepo":null}',
68346
+ "",
68347
+ 'User: "Are there any untracked or modified files in the working tree right now?"',
68348
+ '{"isGitQuery":true,"asksStatus":true,"asksDiff":false,"asksLog":false,"asksBranch":false,"showTarget":null,"targetRepo":null}',
68349
+ "",
68350
+ `User: "What's changed since my last commit?"`,
68351
+ '{"isGitQuery":true,"asksStatus":true,"asksDiff":true,"asksLog":false,"asksBranch":false,"showTarget":null,"targetRepo":null}',
68352
+ "",
68353
+ 'User: "What is the status of the running task?"',
68354
+ '{"isGitQuery":false,"asksStatus":false,"asksDiff":false,"asksLog":false,"asksBranch":false,"showTarget":null,"targetRepo":null}',
68355
+ "",
68356
+ 'User: "What\'s the difference between `git checkout` and `git switch`?"',
68357
+ '{"isGitQuery":false,"asksStatus":false,"asksDiff":false,"asksLog":false,"asksBranch":false,"showTarget":null,"targetRepo":null}',
68358
+ "",
68359
+ 'User: "How does git status work?"',
68360
+ '{"isGitQuery":false,"asksStatus":false,"asksDiff":false,"asksLog":false,"asksBranch":false,"showTarget":null,"targetRepo":null}',
68361
+ "",
68362
+ 'User: "What changed in React 19?"',
68363
+ '{"isGitQuery":false,"asksStatus":false,"asksDiff":false,"asksLog":false,"asksBranch":false,"showTarget":null,"targetRepo":null}',
68364
+ "",
68365
+ 'User: "How do I get the current working directory in Python?"',
68366
+ '{"isGitQuery":false,"asksStatus":false,"asksDiff":false,"asksLog":false,"asksBranch":false,"showTarget":null,"targetRepo":null}',
68367
+ "",
68368
+ `User prompt: ${JSON.stringify(sanitized)}`
68369
+ ].join(`
68370
+ `);
68371
+ }
68372
+ function parseGemmaGitStatusResponse(raw) {
68373
+ if (!raw || typeof raw != "string") return null;
68374
+ let cleaned = raw.trim();
68375
+ cleaned.startsWith("```") && (cleaned = cleaned.replace(/^```(?:json)?\s*\n?/i, "").replace(/\n?```\s*$/i, "").trim());
68376
+ let parsed;
68377
+ try {
68378
+ parsed = JSON.parse(cleaned);
68379
+ } catch {
68380
+ return null;
68381
+ }
68382
+ if (typeof parsed != "object" || parsed === null || Array.isArray(parsed) || typeof parsed.isGitQuery != "boolean" || typeof parsed.asksStatus != "boolean" || typeof parsed.asksDiff != "boolean" || typeof parsed.asksLog != "boolean" || typeof parsed.asksBranch != "boolean")
68383
+ return null;
68384
+ let isGitQuery = parsed.isGitQuery, asksStatus = parsed.asksStatus, asksDiff = parsed.asksDiff, asksLog = parsed.asksLog, asksBranch = parsed.asksBranch, showTarget = null;
68385
+ if (typeof parsed.showTarget == "string" && parsed.showTarget.trim()) {
68386
+ let candidateTarget = parsed.showTarget.trim();
68387
+ SAFE_REF_PATTERN.test(candidateTarget) && !candidateTarget.startsWith("-") && !candidateTarget.includes("..") && !candidateTarget.endsWith("^!") && !candidateTarget.endsWith("^@") && (showTarget = candidateTarget);
68388
+ }
68389
+ let targetRepo = null;
68390
+ if (typeof parsed.targetRepo == "string" && parsed.targetRepo.trim()) {
68391
+ let candidateRepo = parsed.targetRepo.trim();
68392
+ SAFE_REPO_NAME_PATTERN.test(candidateRepo) && candidateRepo !== "." && candidateRepo !== ".." && !candidateRepo.includes("/") && !candidateRepo.includes("\\") && (targetRepo = candidateRepo);
68393
+ }
68394
+ return {
68395
+ isGitQuery,
68396
+ asksStatus,
68397
+ asksDiff,
68398
+ asksLog,
68399
+ asksBranch,
68400
+ showTarget,
68401
+ targetRepo
68402
+ };
68403
+ }
68404
+ async function parseGitStatusQueryWithGemma(runner, prompt, timeoutMs = 12e3) {
68405
+ let startTime = Date.now(), boundedPrompt = prompt.slice(0, 2e3).trim(), promptText = renderLocalGemmaGitStatusQueryPrompt({ userPrompt: boundedPrompt }), abortController = new AbortController(), timer = null;
68406
+ try {
68407
+ let timeoutPromise = new Promise((_, reject) => {
68408
+ timer = setTimeout(() => {
68409
+ abortController.abort(), reject(new Error(`Gemma status query classification timed out after ${timeoutMs}ms`));
68410
+ }, timeoutMs), timer?.unref?.();
68411
+ }), advisoryPromise = runner.generateAdvisory(promptText, {
68412
+ responseFormat: "json",
68413
+ numPredict: 100,
68414
+ temperature: 0.1,
68415
+ timeoutMs,
68416
+ signal: abortController.signal
68417
+ }), raw = await Promise.race([advisoryPromise, timeoutPromise]);
68418
+ if (!raw || typeof raw != "string")
68419
+ return logger.warn("[git-status-summary] Gemma returned empty or non-string response", {
68420
+ prompt: boundedPrompt,
68421
+ durationMs: Date.now() - startTime
68422
+ }), null;
68423
+ let parsed = parseGemmaGitStatusResponse(raw);
68424
+ return parsed || (logger.warn("[git-status-summary] Gemma response failed schema validation", {
68425
+ prompt: boundedPrompt,
68426
+ raw,
68427
+ durationMs: Date.now() - startTime
68428
+ }), null);
68429
+ } catch (err) {
68430
+ let elapsed = Date.now() - startTime;
68431
+ return logger.warn("[git-status-summary] Gemma status query classification timed out or failed", {
68432
+ prompt: boundedPrompt,
68433
+ durationMs: elapsed,
68434
+ error: err?.message || String(err)
68435
+ }), null;
68436
+ } finally {
68437
+ timer && clearTimeout(timer);
68438
+ }
68439
+ }
68440
+ function parseGitStatusQuerySemantic(prompt) {
68441
+ let trimmed = prompt.trim();
68442
+ if (!trimmed)
68443
+ return {
68444
+ isGitQuery: !1,
68445
+ asksStatus: !1,
68446
+ asksDiff: !1,
68447
+ asksLog: !1,
68448
+ asksBranch: !1,
68449
+ showTarget: null,
68450
+ targetRepo: null
68451
+ };
68452
+ let isExplicitGit = /\b(?:git\s+(?:status|diff|log|branch|show|checkout|switch|restore)|commit\s+history|recent\s+commits)\b/i.test(trimmed);
68453
+ if (!(isExplicitGit || /\b(?:diff|status|log|branch|show|commits?|untracked|modified|working\s+tree|working\s+copy|working\s+directory)\b/i.test(trimmed)) && (/\b(?:task|agent|orchestrat(?:or|ion)|track|run|job|process|build|test|deploy|session)\s+status\b/i.test(trimmed) || /\bstatus\s+of\s+(?:the\s+)?(?:running\s+|active\s+)?(?:task|agent|orchestrat(?:or|ion)|track|run|job|process|team|build|suite)\b/i.test(trimmed) || /\b(?:how\s+many|what)\s+tasks?\s+(?:are\s+)?(?:running|active|pending)\b/i.test(trimmed) || /\b(?:what\s+did\s+)?(?:the\s+)?(?:last|previous|prior)\s+task\b/i.test(trimmed) || /\b(?:has\s+the|is\s+the)\s+(?:git\s+)?build\s+(?:finished|running|done|complete)\b/i.test(trimmed)))
68454
+ return {
68455
+ isGitQuery: !1,
68456
+ asksStatus: !1,
68457
+ asksDiff: !1,
68458
+ asksLog: !1,
68459
+ asksBranch: !1,
68460
+ showTarget: null,
68461
+ targetRepo: null
68462
+ };
68463
+ let isSinceLastCommit = /\b(?:what(?:'s|\s+is|\s+has)?\s+changed|changes?|diff)\s+(?:since|after)\s+(?:the\s+)?(?:most\s+recent|last|latest|current|head|my\s+last)\s+commit\b/i.test(trimmed.replace(/[\u2018\u2019]/g, "'")) || /\b(?:since|after)\s+(?:the\s+)?(?:most\s+recent|last|latest|current|head|my\s+last)\s+commit\b/i.test(trimmed.replace(/[\u2018\u2019]/g, "'")), asksCommitDiff = !isSinceLastCommit && COMMIT_DIFF_PATTERN.test(trimmed), showTarget = null;
68464
+ if (asksCommitDiff) {
68465
+ let commitHexMatch = /\b([0-9a-f]{7,40}(?:[~^][A-Za-z0-9_/@~^-]*)?)'s\s+diff\b/i.exec(trimmed) || /\bdiff\s+of\s+(?:commit\s+)?(?![^\s;,?]*\.\.[a-zA-Z0-9_/@~^-])([0-9a-f]{7,40}(?:[~^][A-Za-z0-9_/@~^-]*)?)(?![0-9a-zA-Z_/@~^-])/i.exec(trimmed);
68466
+ commitHexMatch && !commitHexMatch[1].includes("..") ? showTarget = stripSentencePunctuation(commitHexMatch[1]) : showTarget = extractBranchOrRefTarget(trimmed) ?? "HEAD";
68467
+ }
68468
+ let hasGitOrRepoContext = /\b(?:git|repo|repository)\b/i.test(trimmed), asksStatus = isSinceLastCommit || /\b(?:git\s+status)\b/i.test(trimmed) || /\b(?:status)\b/i.test(trimmed) && /\b(?:repo|repository|git|workspace|working\s+tree|working\s+copy)\b/i.test(trimmed) || /\b(?:untracked|modified|uncommitted|staged|unstaged)\s+(?:files|changes)\b/i.test(trimmed) || /\b(?:untracked\s+or\s+modified|modified\s+or\s+untracked)\s+(?:files|changes)?\b/i.test(trimmed) || /\b(?:did\s+i\s+leave\s+anything\s+uncommitted|any\s+uncommitted\s+(?:changes|files|work)|anything\s+uncommitted)\b/i.test(trimmed) || /\b(?:any\s+)?(?:untracked|modified)\s+files\b/i.test(trimmed) || /\b(?:new\s+files\s+)?not\s+tracked(?:\s+by\s+git)?\b/i.test(trimmed) || /\bworking\s+(?:tree|copy)\b/i.test(trimmed) || /\bworking\s+directory\b/i.test(trimmed) && hasGitOrRepoContext || /\bclean\s+working\s+(?:tree|copy)\b/i.test(trimmed) || /\bclean\s+working\s+directory\b/i.test(trimmed) && hasGitOrRepoContext || /\bis\s+(?:my\s+)?working\s+(?:tree|copy)\s+clean\b/i.test(trimmed) || /\bis\s+(?:my\s+)?working\s+directory\s+clean\b/i.test(trimmed) && hasGitOrRepoContext || /\b(?:which|what)\s+files\s+(?:have\s+i|did\s+i|are|were)?\s*(?:edited|modified|changed)\b/i.test(trimmed) || /\bwhat\s+(?:files\s+)?(?:did\s+i|have\s+been|are)\s+(?:changed|modified|edited)\b/i.test(trimmed) || /(?:git.*(?:狀態|status)|(?:工作區|工作副本|版本庫|暫存區).*(?:狀態|變更|修改)|工作目錄.*(?:狀態|變更|修改)|(?:未追蹤|未跟踪|未暫存|未暂存|已修改|有修改).*(?:檔案|文件)|(?:有沒有|有没有).*(?:未追蹤|未跟踪|未暫存|未暂存|已修改).*(?:檔案|文件)?|(?:程式碼|代碼).*(?:差異|變更))/i.test(trimmed) || /(?:mostra(?:mi)?|elenc(?:a|ami))\s+(?:tutti\s+)?(?:i\s+)?file\s+(?:non\s+tracciati|modificati)|(?:file\s+non\s+tracciati|file\s+modificati|stato\s+del\s+repository)/i.test(trimmed) || /(?:archivos?\s+(?:sin\s+seguimiento|modificados?)|unversionierte\s+dateien|fichiers?\s+(?:non\s+suivis?|modifi[eé]s?)|suivi\s+des\s+fichiers)/i.test(trimmed), comparisonBase = extractComparisonBaseRef(trimmed), asksDiff = !asksCommitDiff && (isSinceLastCommit || !!comparisonBase || /\bgit\s+diff\b/i.test(trimmed) || /\b(?:working\s+tree|working\s+copy|uncommitted|staged|unstaged)\s+diff\b/i.test(trimmed) || /\bdiff\s+against\s+(?:working\s+tree|head)\b/i.test(trimmed) || /\b(?:difference|diff)\s+between\s+my\s+working\s+(?:tree|copy)\s+and\s+([a-z0-9_./@~^-]+)\b/i.test(trimmed) || /^(?:please\s+)?(?:show|view|see)\s+(?:me\s+)?(?:the\s+|my\s+)?(?:diff|changes)(?:\s+since\s+(?:yesterday|today|monday|tuesday|wednesday|thursday|friday|saturday|sunday|(?:last|this)\s+\w+|\d+\s+\w+(?:\s+ago)?))?[\s.!?]*$/i.test(trimmed) || /\b(?:give|show|view|see|get|provide|display|print)\s+(?:me\s+)?(?:a\s+|the\s+|my\s+)?diff\b(?!\s+(?:of|between)\b|\s+(?:for\s+)?these\s+two\b)/i.test(trimmed) || /\b(?:show|view|see)\s+(?:the\s+)?(?:diff|changes)\s+(?:in\s+(?:the\s+|my\s+)?(?:working\s+(?:tree|copy)|repo|repository)|locally|uncommitted)\b/i.test(trimmed) || /\b(?:git|repo|working\s+(?:tree|copy)|uncommitted)\s+(?:changes?|diff)\s+(?:since|against)\b/i.test(trimmed)), asksLog = /\b(?:git\s+log|commit\s+history|recent\s+commits)\b/i.test(trimmed) || /\b(?:log\s+of\s+commits|history\s+of\s+commits|past\s+commits|list\s+commits)\b/i.test(trimmed) || /\b(?:most\s+recent|last|latest)\s+commits\b/i.test(trimmed), asksBranch = /\bgit\s+branch\b/i.test(trimmed) || /\b(?:what|which)\s+branch\s+(?:am\s+i\s+on|is\s+this)\b/i.test(trimmed) || /\bcurrent\s+branch\b/i.test(trimmed) || /\blist\s+(?:all\s+)?branches\b/i.test(trimmed), asksShow = !!showTarget || /\bgit\s+show\b/i.test(trimmed) || /\b(?:show|view|see|display|print|inspect|details\s+of)\s+(?:(?:me|us)\s+)?(?:commit\s+)?(?:HEAD|[0-9a-f]{7,40})(?:[~^][A-Za-z0-9_/@~^-]*)?(?![0-9a-zA-Z_/@~^-]|\.[0-9a-zA-Z_/@~^-])/i.test(trimmed);
68469
+ return {
68470
+ isGitQuery: !!(isExplicitGit || GIT_QUERY_PATTERN.test(trimmed) || asksStatus || asksDiff || asksLog || asksBranch || asksShow || asksCommitDiff),
68471
+ asksStatus,
68472
+ asksDiff,
68473
+ asksLog,
68474
+ asksBranch,
68475
+ showTarget,
68476
+ targetRepo: null
68477
+ };
68478
+ }
68479
+ async function resolveGitStatusIntent(options) {
68480
+ let { prompt, advisoryRunner } = options, trimmed = prompt.trim();
68481
+ if (!trimmed)
68482
+ return {
68483
+ isGitQuery: !1,
68484
+ asksStatus: !1,
68485
+ asksDiff: !1,
68486
+ asksLog: !1,
68487
+ asksBranch: !1,
68488
+ showTarget: null,
68489
+ targetRepo: null
68490
+ };
68491
+ if (isConceptualGitQuestion(trimmed))
68492
+ return {
68493
+ isGitQuery: !1,
68494
+ asksStatus: !1,
68495
+ asksDiff: !1,
68496
+ asksLog: !1,
68497
+ asksBranch: !1,
68498
+ showTarget: null,
68499
+ targetRepo: null
68500
+ };
68501
+ if (/^(?:[`'"]?(?:\/|!)?git\s+(?:status|diff|log|branch|show|checkout|switch|restore)\b)/i.test(trimmed))
68502
+ return parseGitStatusQuerySemantic(trimmed);
68503
+ let candidateRunner = resolvePreferredAdvisoryRunner(advisoryRunner);
68504
+ if (candidateRunner)
68505
+ try {
68506
+ let gemmaStart = Date.now(), gemmaIntent = await parseGitStatusQueryWithGemma(candidateRunner, trimmed), gemmaDurationMs = Date.now() - gemmaStart;
68507
+ if (gemmaIntent !== null) {
68508
+ if (gemmaIntent.targetRepo) {
68509
+ let repoCandidate = stripSentencePunctuation(gemmaIntent.targetRepo), candLower = repoCandidate.toLowerCase(), genericStopwords = /* @__PURE__ */ new Set([
68510
+ "work",
68511
+ "working",
68512
+ "tree",
68513
+ "directory",
68514
+ "copy",
68515
+ "repo",
68516
+ "repository",
68517
+ "git",
68518
+ "current",
68519
+ "active",
68520
+ "local",
68521
+ "here",
68522
+ "this",
68523
+ "diff",
68524
+ "status",
68525
+ "log",
68526
+ "branch",
68527
+ "show",
68528
+ "commit",
68529
+ "commits"
68530
+ ]), isExplicitRepoSyntax = SAFE_REPO_NAME_PATTERN.test(repoCandidate) && !repoCandidate.includes(".."), designatedTokens = [], afterMatches = trimmed.matchAll(
68531
+ /\b(?:in|for|from|under|at|inside|to)\s+(?:the\s+)?(?:repo|repository|submodule|package|dir|directory|project)\s+["'`]?([A-Za-z0-9_.-]+)["'`]?/gi
68532
+ );
68533
+ for (let m of afterMatches) {
68534
+ let tok = stripSentencePunctuation(m[1]).toLowerCase();
68535
+ tok && designatedTokens.push(tok);
68536
+ }
68537
+ let beforeMatches = trimmed.matchAll(
68538
+ /(?<=^|[\s"'\`(\[{<])([A-Za-z0-9_.-]+)\s+(?:repo|repository|package|project|submodule)\b/gi
68539
+ );
68540
+ for (let m of beforeMatches) {
68541
+ let tok = stripSentencePunctuation(m[1]).toLowerCase();
68542
+ tok && designatedTokens.push(tok);
68543
+ }
68544
+ !designatedTokens.some(
68545
+ (token) => token === candLower && !genericStopwords.has(token)
68546
+ ) || !isExplicitRepoSyntax || genericStopwords.has(candLower) ? gemmaIntent.targetRepo = null : gemmaIntent.targetRepo = repoCandidate;
68547
+ }
68548
+ return logger.info("[git-status-summary] Gemma status intent decision", {
68549
+ prompt: trimmed,
68550
+ runner: candidateRunner.runtimeLabel,
68551
+ providerKind: candidateRunner.providerKind,
68552
+ durationMs: gemmaDurationMs,
68553
+ intent: gemmaIntent
68554
+ }), gemmaIntent;
68555
+ } else
68556
+ logger.info("[git-status-summary] Gemma returned null or timed out, falling back to deterministic semantic parser", {
68557
+ prompt: trimmed
68558
+ });
68559
+ } catch (err) {
68560
+ logger.warn("[git-status-summary] Gemma status intent query failed, falling back to deterministic semantic parser", {
68561
+ prompt: trimmed,
68562
+ error: err?.message
68563
+ });
68564
+ }
68565
+ return parseGitStatusQuerySemantic(trimmed);
68566
+ }
68567
+ var IGNORED_TARGET_NAMES = /* @__PURE__ */ new Set([
67552
68568
  "git",
67553
68569
  "the",
67554
68570
  "this",
@@ -67703,7 +68719,9 @@ var defaultExecFileAsync = (0, import_util3.promisify)(import_child_process5.exe
67703
68719
  "very"
67704
68720
  ], STOPWORDS = new Set(FUNCTION_WORDS), NON_SHOW_TARGET_WORDS = new Set(FUNCTION_WORDS);
67705
68721
  function extractCandidateRepos(prompt) {
67706
- let candidates = [], seen = /* @__PURE__ */ new Set(), addCandidate = (name, isExplicit) => {
68722
+ let candidates = [], seen = /* @__PURE__ */ new Set(), addCandidate = (rawName, isExplicit) => {
68723
+ let name = stripSentencePunctuation(rawName);
68724
+ if (!name) return;
67707
68725
  let lower = name.toLowerCase();
67708
68726
  if (IGNORED_TARGET_NAMES.has(lower) || /^[0-9a-f]{7,40}$/i.test(name))
67709
68727
  return;
@@ -67714,13 +68732,20 @@ function extractCandidateRepos(prompt) {
67714
68732
  let existing = candidates.find((c) => c.name.toLowerCase() === lower);
67715
68733
  existing && (existing.isExplicit = !0);
67716
68734
  }
67717
- }, trailingRepoRegex = /\b([a-zA-Z0-9_.-]+)\s+repo(?:sitory)?\b/gi, match;
67718
- for (; (match = trailingRepoRegex.exec(prompt)) !== null; )
67719
- addCandidate(match[1], !0);
67720
- let leadingRepoRegex = /\brepo(?:sitory)?\s+([a-zA-Z0-9_.-]+)\b/gi;
67721
- for (; (match = leadingRepoRegex.exec(prompt)) !== null; )
67722
- addCandidate(match[1], !0);
67723
- let casualRegex = /\b(?:in|of|for)\s+(?:the\s+)?([a-zA-Z0-9_.-]+)\b/gi;
68735
+ }, trailingRepoRegex = /(?<=^|[\s"'\`(\[{<])([a-zA-Z0-9_.-]+)\s+(?:repo(?:sitory)?|project|package|submodule)\b/gi, match;
68736
+ for (; (match = trailingRepoRegex.exec(prompt)) !== null; ) {
68737
+ let raw = match[1], isStopword = STOPWORDS.has(raw.toLowerCase());
68738
+ addCandidate(raw, !isStopword);
68739
+ }
68740
+ let leadingRepoRegex = /\b(?:repo(?:sitory)?|project|package|submodule)\s+([a-zA-Z0-9_.-]+)/gi;
68741
+ for (; (match = leadingRepoRegex.exec(prompt)) !== null; ) {
68742
+ let raw = match[1];
68743
+ if (match[0].toLowerCase().startsWith("package") && raw.toLowerCase() === "lock")
68744
+ continue;
68745
+ let isStopword = STOPWORDS.has(raw.toLowerCase());
68746
+ addCandidate(raw, !isStopword);
68747
+ }
68748
+ let casualRegex = /\b(?:in|of|for)\s+(?:the\s+)?([a-zA-Z0-9_.-]+)/gi;
67724
68749
  for (; (match = casualRegex.exec(prompt)) !== null; )
67725
68750
  addCandidate(match[1], !1);
67726
68751
  return candidates;
@@ -67736,8 +68761,11 @@ ${content}
67736
68761
  ${fence}`;
67737
68762
  }
67738
68763
  async function executeGitStatusSummary(options) {
67739
- let { cwd, prompt, hasActiveTask, execFileFn = defaultExecFileAsync } = options;
67740
- if (!GIT_QUERY_PATTERN.test(prompt) || hasActiveTask)
68764
+ let { cwd, prompt, hasActiveTask, advisoryRunner, execFileFn = defaultExecFileAsync } = options;
68765
+ if (hasActiveTask)
68766
+ return null;
68767
+ let intent = await resolveGitStatusIntent({ prompt, advisoryRunner });
68768
+ if (!intent.isGitQuery)
67741
68769
  return null;
67742
68770
  let gitEnv = scrubSearchApiKeys({ ...process.env, LC_ALL: "C" }), defaultRepoName = sanitizeForTerminal(redactAbsoluteLocalPaths(path64.basename(cwd))), defaultRepoTopLevel = cwd;
67743
68771
  try {
@@ -67749,8 +68777,18 @@ async function executeGitStatusSummary(options) {
67749
68777
  trimmed && (defaultRepoTopLevel = trimmed, defaultRepoName = sanitizeForTerminal(redactAbsoluteLocalPaths(path64.basename(trimmed))));
67750
68778
  } catch {
67751
68779
  }
67752
- let targetCwd = cwd, targetRepoName = defaultRepoName, notePrefix = "", candidates = extractCandidateRepos(prompt), resolvedRepo = null;
68780
+ let targetCwd = cwd, targetRepoName = defaultRepoName, notePrefix = "", candidates = extractCandidateRepos(prompt);
68781
+ if (intent.targetRepo) {
68782
+ let rawTarget = stripSentencePunctuation(intent.targetRepo);
68783
+ if (SAFE_REPO_NAME_PATTERN.test(rawTarget) && rawTarget !== "." && rawTarget !== ".." && !rawTarget.includes("/") && !rawTarget.includes("\\")) {
68784
+ let existingIndex = candidates.findIndex((c) => c.name.toLowerCase() === rawTarget.toLowerCase());
68785
+ existingIndex >= 0 && candidates.splice(existingIndex, 1), candidates.unshift({ name: rawTarget, isExplicit: !0 });
68786
+ }
68787
+ }
68788
+ let resolvedRepo = null;
67753
68789
  for (let candidate of candidates) {
68790
+ if (!SAFE_REPO_NAME_PATTERN.test(candidate.name) || candidate.name === "." || candidate.name === ".." || candidate.name.includes("/") || candidate.name.includes("\\"))
68791
+ continue;
67754
68792
  if (defaultRepoName.toLowerCase() === candidate.name.toLowerCase()) {
67755
68793
  resolvedRepo = { cwd, name: defaultRepoName };
67756
68794
  break;
@@ -67809,7 +68847,64 @@ async function executeGitStatusSummary(options) {
67809
68847
 
67810
68848
  `), targetCwd = cwd, targetRepoName = defaultRepoName;
67811
68849
  }
67812
- let sections = [], asksDiff = /\b(?:git\s+diff|diff)\b/i.test(prompt), asksBranch = /\b(?:git\s+branch|branch(?:es)?)\b/i.test(prompt), asksLog = /\b(?:git\s+log|log|commit\s+history|recent\s+commits)\b/i.test(prompt), asksStatus = /\b(?:git\s+status|status)\b/i.test(prompt), asksShow = /\bgit\s+show\b/i.test(prompt), showTarget = null, openerMatch = /\bgit\s+show\b\s*([`'"“‘«‹「『(\[(《【])/iu.exec(prompt);
68850
+ let sections = [], isSinceLastCommit = /\b(?:what(?:'s|\s+is|\s+has)?\s+changed|changes?|diff)\s+(?:since|after)\s+(?:the\s+)?(?:most\s+recent|last|latest|current|head|my\s+last)\s+commit\b/i.test(prompt.replace(/[\u2018\u2019]/g, "'")) || /\b(?:since|after)\s+(?:the\s+)?(?:most\s+recent|last|latest|current|head|my\s+last)\s+commit\b/i.test(prompt.replace(/[\u2018\u2019]/g, "'")), comparisonBaseInfo = isSinceLastCommit ? void 0 : extractComparisonBaseRefInfo(prompt, { includeSpan: !0 });
68851
+ async function isValidGitComparisonRef(ref, targetCwd2, gitEnv2, execFileFn2) {
68852
+ if (ref.includes("..")) {
68853
+ let rangeParts = ref.includes("...") ? ref.split("...") : ref.split("..");
68854
+ if (rangeParts.length !== 2 || !rangeParts[0] && !rangeParts[1])
68855
+ return !1;
68856
+ for (let part of rangeParts) {
68857
+ let endpoint = part || "HEAD";
68858
+ if (!SAFE_REF_PATTERN.test(endpoint) || endpoint.startsWith("-"))
68859
+ return !1;
68860
+ try {
68861
+ await execFileFn2(
68862
+ "git",
68863
+ ["--no-optional-locks", "rev-parse", "--verify", "--quiet", `${endpoint}^{commit}`],
68864
+ { cwd: targetCwd2, env: gitEnv2, timeout: 3e3, encoding: "utf8" }
68865
+ );
68866
+ } catch {
68867
+ return !1;
68868
+ }
68869
+ }
68870
+ return !0;
68871
+ }
68872
+ if (!SAFE_REF_PATTERN.test(ref) || ref.startsWith("-"))
68873
+ return !1;
68874
+ try {
68875
+ return await execFileFn2(
68876
+ "git",
68877
+ ["--no-optional-locks", "rev-parse", "--verify", "--quiet", `${ref}^{commit}`],
68878
+ { cwd: targetCwd2, env: gitEnv2, timeout: 3e3, encoding: "utf8" }
68879
+ ), !0;
68880
+ } catch {
68881
+ return !1;
68882
+ }
68883
+ }
68884
+ function occursOutsideComparisonSpan(prompt2, target, span, comparisonRef) {
68885
+ if (!span) {
68886
+ if (comparisonRef) {
68887
+ let lowerComp = comparisonRef.toLowerCase(), lowerTgt = target.toLowerCase();
68888
+ if (lowerComp === lowerTgt || lowerComp.includes(lowerTgt) || lowerTgt.includes(lowerComp))
68889
+ return !1;
68890
+ }
68891
+ return !0;
68892
+ }
68893
+ let lowerPrompt = prompt2.toLowerCase(), lowerTarget = target.toLowerCase(), idx = lowerPrompt.indexOf(lowerTarget);
68894
+ if (idx === -1)
68895
+ return !0;
68896
+ for (; idx !== -1; ) {
68897
+ if (idx + lowerTarget.length <= span.start || idx >= span.end)
68898
+ return !0;
68899
+ idx = lowerPrompt.indexOf(lowerTarget, idx + 1);
68900
+ }
68901
+ return !1;
68902
+ }
68903
+ if (comparisonBaseInfo && !comparisonBaseInfo.isExplicit) {
68904
+ let candidateRef = comparisonBaseInfo.ref;
68905
+ await isValidGitComparisonRef(candidateRef, targetCwd, gitEnv, execFileFn) || (comparisonBaseInfo = void 0);
68906
+ }
68907
+ let promptForShowDetection = comparisonBaseInfo?.span ? prompt.slice(0, comparisonBaseInfo.span.start) + " ".repeat(comparisonBaseInfo.span.end - comparisonBaseInfo.span.start) + prompt.slice(comparisonBaseInfo.span.end) : prompt, isExplicitCommitShowRequest = /\bgit\s+show\b/i.test(promptForShowDetection) || /\b(?:show|view|display|details\s+of|print|inspect)\s+(?:(?:me|us)\s+)?(?:what\s+changed\s+in\s+)?(?:(?:the|my)\s+)?(?:most\s+recent|last|latest|recent|current)\s+commit(?:'s)?(?:\s+(?:diff|details))?\b/i.test(promptForShowDetection) || /\b(?:(?:show|view|see|display|print|inspect|details\s+of|what\s+changed\s+in)\s+(?:(?:me|us)\s+)?(?:(?:the\s+)?diff\s+of\s+(?:commit\s+)?|(?:details\s+of|commit)\s+)?|(?:the\s+)?diff\s+of\s+(?:commit\s+)?)([`'"])((?:HEAD|[0-9a-f]{7,40})(?:[~^][A-Za-z0-9_/@~^-]*)?)\1/i.test(promptForShowDetection) || /\b(?:(?:show|view|see|display|print|inspect|details\s+of|what\s+changed\s+in)\s+(?:(?:me|us)\s+)?(?:(?:the\s+)?diff\s+of\s+(?:commit\s+)?|(?:details\s+of|commit)\s+)?|(?:the\s+)?diff\s+of\s+(?:commit\s+)?)(?![^\s;,?]*\.{2,3}[a-zA-Z0-9_/@~^-])(?:HEAD|[0-9a-f]{7,40})(?:[~^][A-Za-z0-9_/@~^-]*)?(?![0-9a-zA-Z_/@~^-]|\.[0-9a-zA-Z_/@~^-])/i.test(promptForShowDetection) || /(?:^\s*|\b(?:and|also)\s+)commit\s+((?:HEAD|[0-9a-f]{7,40})(?:[~^][A-Za-z0-9_/@~^-]*)?)(?![0-9a-zA-Z_/@~^-]|\.[0-9a-zA-Z_/@~^-])/i.test(promptForShowDetection) || /\b[0-9a-f]{7,40}(?:[~^][A-Za-z0-9_/@~^-]*)?'s\s+diff\b/i.test(promptForShowDetection), isExplicitGitShow = isExplicitCommitShowRequest, hasStrongComparison = !!(comparisonBaseInfo && (comparisonBaseInfo.isExplicit || hasRefSyntax(comparisonBaseInfo.ref))), hasComparison = !!(isSinceLastCommit || comparisonBaseInfo), isCommitDiff = !isSinceLastCommit && !hasComparison && (COMMIT_DIFF_PATTERN.test(prompt) || !intent.asksDiff && !!intent.showTarget) || !isSinceLastCommit && !hasComparison && !intent.asksDiff && !!intent.showTarget, asksDiff = isSinceLastCommit || hasStrongComparison || !!comparisonBaseInfo && !isCommitDiff || intent.asksDiff && !isCommitDiff, hasExplicitBranchListRequest = /\b(?:list|show|view|see|display|all|other)\s+branches\b/i.test(prompt) || /\b(?:and|also)\s+(?:list\s+)?branches\b/i.test(prompt) || /\bgit\s+branch\b/i.test(prompt) || /\b(?:which|what)\s+branch\s+(?:am\s+i|is\s+this)\b/i.test(prompt), asksBranch = isSinceLastCommit ? !1 : isCommitDiff ? !!(hasExplicitBranchListRequest && intent.asksBranch) : intent.asksBranch, asksLog = isSinceLastCommit ? !1 : intent.asksLog, asksStatus = isSinceLastCommit || intent.asksStatus, asksShow = isExplicitCommitShowRequest || !isSinceLastCommit && !hasComparison && isCommitDiff, showTarget = null, openerMatch = /\bgit\s+show\b\s*([`'"“‘«‹「『(\[(《【])/iu.exec(prompt);
67813
68908
  if (openerMatch) {
67814
68909
  let opener = openerMatch[1], pairRegex = null;
67815
68910
  switch (opener) {
@@ -67881,9 +68976,34 @@ async function executeGitStatusSummary(options) {
67881
68976
  }
67882
68977
  }
67883
68978
  }
67884
- if (!showTarget) {
67885
- let commitMatch = /\b([0-9a-f]{7,40})\b/i.exec(prompt);
67886
- commitMatch ? showTarget = commitMatch[1] : asksShow && (showTarget = "HEAD");
68979
+ if (!isExplicitCommitShowRequest && hasComparison)
68980
+ showTarget = null;
68981
+ else if (!showTarget) {
68982
+ let showCommitMatch = /\b(?:(?:show|view|see|display|print|inspect|details\s+of|what\s+changed\s+in)\s+(?:(?:me|us)\s+)?(?:(?:the\s+)?diff\s+of\s+(?:commit\s+)?|(?:details\s+of|commit)\s+)?|(?:the\s+)?diff\s+of\s+(?:commit\s+)?)([`'"])((?:HEAD|[0-9a-f]{7,40})(?:[~^][A-Za-z0-9_/@~^-]*)?)\1/i.exec(promptForShowDetection) || /\b(?:show|view|display|details\s+of|print)\s+(?:(?:me|us)\s+)?(?:commit\s+)?([`'"])((?:HEAD|[0-9a-f]{7,40})(?:[~^][A-Za-z0-9_/@~^-]*)?)\1/i.exec(promptForShowDetection) || /\b([0-9a-f]{7,40}(?:[~^][A-Za-z0-9_/@~^-]*)?)'s\s+diff\b/i.exec(promptForShowDetection) || /\b(?:(?:show|view|see|display|print|inspect|details\s+of|what\s+changed\s+in)\s+(?:(?:me|us)\s+)?(?:(?:the\s+)?diff\s+of\s+(?:commit\s+)?|(?:details\s+of|commit)\s+)?|(?:the\s+)?diff\s+of\s+(?:commit\s+)?)(?![^\s;,?]*\.{2,3}[a-zA-Z0-9_/@~^-])((?:HEAD|[0-9a-f]{7,40})(?:[~^][A-Za-z0-9_/@~^-]*)?)(?![0-9a-zA-Z_/@~^-]|\.[0-9a-zA-Z_/@~^-])/i.exec(promptForShowDetection) || /(?:^\s*|\b(?:and|also)\s+)commit\s+((?:HEAD|[0-9a-f]{7,40})(?:[~^][A-Za-z0-9_/@~^-]*)?)(?![0-9a-zA-Z_/@~^-]|\.[0-9a-zA-Z_/@~^-])/i.exec(promptForShowDetection) || /\b(?:show|view|display|details\s+of|print)\s+(?:(?:me|us)\s+)?(?:commit\s+)?(?![^\s;,?]*\.\.[a-zA-Z0-9_/@~^-])([0-9a-f]{7,40}(?:[~^][A-Za-z0-9_/@~^-]*)?)(?![0-9a-zA-Z_/@~^-]|\.[0-9a-zA-Z_/@~^-])/i.exec(promptForShowDetection), targetRef = showCommitMatch ? showCommitMatch[2] ?? showCommitMatch[1] : null;
68983
+ if (showCommitMatch && targetRef && occursOutsideComparisonSpan(prompt, targetRef, comparisonBaseInfo?.span, comparisonBaseInfo?.ref))
68984
+ showTarget = stripSentencePunctuation(targetRef);
68985
+ else if (asksShow && intent.showTarget && occursOutsideComparisonSpan(prompt, intent.showTarget, comparisonBaseInfo?.span, comparisonBaseInfo?.ref))
68986
+ showTarget = intent.showTarget;
68987
+ else {
68988
+ let nonComparisonHash = null;
68989
+ for (let m of promptForShowDetection.matchAll(/\b(?![^\s;,?]*\.\.[a-zA-Z0-9_/@~^-])([0-9a-f]{7,40}(?:[~^][A-Za-z0-9_/@~^-]*)?)(?![0-9a-zA-Z_/@~^-]|\.\.[a-zA-Z0-9_/@~^-])/gi)) {
68990
+ let matchIndex = m.index, matchEnd = matchIndex + m[0].length;
68991
+ if (comparisonBaseInfo?.span) {
68992
+ if (!(matchEnd <= comparisonBaseInfo.span.start || matchIndex >= comparisonBaseInfo.span.end))
68993
+ continue;
68994
+ } else if (comparisonBaseInfo?.ref) {
68995
+ let compRef = comparisonBaseInfo.ref.toLowerCase(), cand = m[1].toLowerCase();
68996
+ if (compRef === cand || compRef.includes(cand) || cand.includes(compRef))
68997
+ continue;
68998
+ }
68999
+ let candidate = stripSentencePunctuation(m[1]);
69000
+ if (!candidate.includes("..")) {
69001
+ nonComparisonHash = candidate;
69002
+ break;
69003
+ }
69004
+ }
69005
+ nonComparisonHash ? showTarget = nonComparisonHash : asksShow && (showTarget = "HEAD");
69006
+ }
67887
69007
  }
67888
69008
  let includeStatus = asksStatus || !asksDiff && !asksBranch && !asksShow, includeLog = asksLog || !asksDiff && !asksBranch && !asksShow;
67889
69009
  try {
@@ -67920,24 +69040,27 @@ ${formatFencedCodeBlock(statusContent, "text")}`);
67920
69040
  ${formatFencedCodeBlock(logContent, "text")}`);
67921
69041
  }
67922
69042
  if (asksDiff) {
67923
- let diffContent = "No working tree changes";
67924
- try {
67925
- let { stdout: gitDiff } = await execFileFn(
67926
- "git",
67927
- ["--no-optional-locks", "-c", "color.ui=never", "diff", "--stat"],
67928
- { cwd: targetCwd, env: gitEnv, timeout: 3e3, encoding: "utf8" }
67929
- ), diffLines = gitDiff.split(/\r?\n/).map((l) => l.trimEnd()).filter((l) => l.length > 0);
67930
- if (diffLines.length > 0) {
67931
- let capped = diffLines.slice(0, 20), rem = diffLines.length - 20;
67932
- diffContent = rem > 0 ? `${capped.join(`
69043
+ let comparisonBase = comparisonBaseInfo?.ref, isExplicitComparison = !!comparisonBaseInfo?.isExplicit, isStaged = !isSinceLastCommit && isStagedDiffIntent(prompt), diffTitle = isStaged ? `### Git Staged Diff (${targetRepoName})` : `### Git Diff (${targetRepoName})`, diffContent = isStaged ? "No staged changes" : isSinceLastCommit || comparisonBase === "HEAD" ? "No changes since last commit" : comparisonBase ? `No differences against ${comparisonBase}` : "No working tree changes";
69044
+ comparisonBase && comparisonBase !== "HEAD" && (await isValidGitComparisonRef(comparisonBase, targetCwd, gitEnv, execFileFn) || (isExplicitComparison ? diffContent = `Commit or reference "${comparisonBase}" was not found` : (comparisonBase = void 0, diffContent = isStaged ? "No staged changes" : "No working tree changes")));
69045
+ let diffArgs = isSinceLastCommit || !isStaged && comparisonBase === "HEAD" ? ["--no-optional-locks", "-c", "color.ui=never", "diff", "--stat", "--end-of-options", "HEAD", "--"] : isStaged && comparisonBase ? ["--no-optional-locks", "-c", "color.ui=never", "diff", "--cached", "--stat", "--end-of-options", comparisonBase, "--"] : isStaged ? ["--no-optional-locks", "-c", "color.ui=never", "diff", "--cached", "--stat"] : comparisonBase ? ["--no-optional-locks", "-c", "color.ui=never", "diff", "--stat", "--end-of-options", comparisonBase, "--"] : ["--no-optional-locks", "-c", "color.ui=never", "diff", "--stat"];
69046
+ if (!diffContent.startsWith("Commit or reference"))
69047
+ try {
69048
+ let { stdout: gitDiff } = await execFileFn(
69049
+ "git",
69050
+ diffArgs,
69051
+ { cwd: targetCwd, env: gitEnv, timeout: 3e3, encoding: "utf8" }
69052
+ ), diffLines = gitDiff.split(/\r?\n/).map((l) => l.trimEnd()).filter((l) => l.length > 0);
69053
+ if (diffLines.length > 0) {
69054
+ let capped = diffLines.slice(0, 20), rem = diffLines.length - 20;
69055
+ diffContent = rem > 0 ? `${capped.join(`
67933
69056
  `)}
67934
69057
  ...and ${rem} more` : capped.join(`
67935
69058
  `);
69059
+ }
69060
+ } catch {
69061
+ diffContent = isStaged ? "Staged diff is unavailable" : "Diff is unavailable";
67936
69062
  }
67937
- } catch {
67938
- diffContent = "Diff is unavailable";
67939
- }
67940
- diffContent = sanitizeForTerminal(redactAbsoluteLocalPaths(diffContent)), sections.push(`### Git Diff (${targetRepoName})
69063
+ diffContent = sanitizeForTerminal(redactAbsoluteLocalPaths(diffContent)), sections.push(`${diffTitle}
67941
69064
  ${formatFencedCodeBlock(diffContent, "text")}`);
67942
69065
  }
67943
69066
  if (asksBranch) {
@@ -67956,7 +69079,8 @@ ${formatFencedCodeBlock(diffContent, "text")}`);
67956
69079
  branchContent = sanitizeForTerminal(redactAbsoluteLocalPaths(branchContent)), sections.push(`### Git Branches (${targetRepoName})
67957
69080
  ${formatFencedCodeBlock(branchContent, "text")}`);
67958
69081
  }
67959
- if (showTarget) {
69082
+ if (!isSinceLastCommit && showTarget) {
69083
+ (showTarget.startsWith("-") || showTarget.includes("\0")) && (showTarget = "HEAD");
67960
69084
  let isHexHash = /^[0-9a-f]{7,40}$/i.test(showTarget), safeTarget = sanitizeForTerminal(redactAbsoluteLocalPaths(showTarget)), title = isHexHash ? `Commit ${safeTarget}` : `Git Show ${safeTarget}`;
67961
69085
  try {
67962
69086
  let gitShowRef = showTarget.endsWith("^!") || showTarget.endsWith("^@") ? showTarget : `${showTarget}^{commit}`, { stdout: showOut } = await execFileFn(
@@ -71053,7 +72177,7 @@ async function runOrchestrationShell(args) {
71053
72177
  let isSlash = isShellSlashCommand(text2), browseController = null;
71054
72178
  isSlash || (browseController = new AbortController(), activeBrowseAbortController = browseController);
71055
72179
  try {
71056
- await handleShellUserInput({
72180
+ logger.info("[orchestration-shell] Turn started", { prompt: text2 }), await handleShellUserInput({
71057
72181
  text: text2,
71058
72182
  store,
71059
72183
  args,
@@ -71087,7 +72211,7 @@ async function runOrchestrationShell(args) {
71087
72211
  // concurrent slash bypass can't leak its advisory into this mirror
71088
72212
  // and vice-versa.
71089
72213
  ownEntries
71090
- });
72214
+ }), logger.info("[orchestration-shell] Turn completed", { prompt: text2 });
71091
72215
  });
71092
72216
  }
71093
72217
  ), handleUserInput = (text2, images, options) => {
@@ -75595,6 +76719,10 @@ async function handleShellUserInput(deps) {
75595
76719
  if (decision = await dispatchClassify({
75596
76720
  ...plannerInput,
75597
76721
  prompt: classifyPrompt
76722
+ }), logger.info("[orchestration-shell] Planner classified input", {
76723
+ prompt: plannerInput.prompt,
76724
+ action: decision.action,
76725
+ rationale: decision.rationale
75598
76726
  }), isLocalPlannerRuntime(args) && decision.action === "browse" && hasFileMutationIntent(plannerInput.prompt) && (decision = {
75599
76727
  action: "start_task",
75600
76728
  rationale: decision.rationale ? `${decision.rationale} (promoted to start_task due to file mutation intent)` : "web request with file mutation intent"
@@ -75771,6 +76899,27 @@ async function handleShellUserInput(deps) {
75771
76899
  return;
75772
76900
  }
75773
76901
  if (isTextAdvisory) {
76902
+ let hasRunningSingle = store.getState().runningTasks.size > 0, team = store.getState().team, teamActive = team !== null && !team.groupResolved && team.haltReason === null, hasActiveTask = hasRunningSingle || teamActive;
76903
+ if (!isConceptualGitQuestion(dispatchText)) {
76904
+ let gitResponse = await executeGitStatusSummary({
76905
+ cwd: args.cwd || process.cwd(),
76906
+ prompt: dispatchText,
76907
+ hasActiveTask,
76908
+ advisoryRunner: args.localAdvisoryRunner,
76909
+ execFileFn: execFileAsync
76910
+ });
76911
+ if (gitResponse !== null) {
76912
+ logger.info("[orchestration-shell] Dispatched SHELL_ADVISORY for git status", {
76913
+ prompt: dispatchText,
76914
+ responseLength: gitResponse.length
76915
+ }), store.dispatch({
76916
+ type: "SHELL_ADVISORY",
76917
+ source: "shell",
76918
+ text: gitResponse
76919
+ });
76920
+ return;
76921
+ }
76922
+ }
75774
76923
  await routeAdvisory({
75775
76924
  store,
75776
76925
  args,
@@ -75946,6 +77095,7 @@ async function handleShellUserInput(deps) {
75946
77095
  cwd: args.cwd || process.cwd(),
75947
77096
  prompt: plannerInput.prompt,
75948
77097
  hasActiveTask,
77098
+ advisoryRunner: args.localAdvisoryRunner,
75949
77099
  execFileFn: execFileAsync
75950
77100
  });
75951
77101
  if (gitResponse !== null) {
@@ -77485,15 +78635,40 @@ var HostedGemmaAdvisoryRunner = class {
77485
78635
  }
77486
78636
  encryptedImages = options.images.map((img) => this.deps.crypto.encryptString(img, sessionKey));
77487
78637
  }
78638
+ if (options?.signal?.aborted) {
78639
+ let err = new Error("Hosted Gemma generation aborted by signal");
78640
+ throw err.name = "AbortError", err;
78641
+ }
77488
78642
  let maxTokens = options?.numPredict, temperature = options?.temperature, start = Date.now();
77489
78643
  try {
77490
- let response = await this.deps.transport.generatePlannerAdvisory({
78644
+ let promises2 = [this.deps.transport.generatePlannerAdvisory({
77491
78645
  sessionId,
77492
78646
  prompt: encryptedPrompt,
77493
78647
  ...encryptedImages && encryptedImages.length > 0 ? { images: encryptedImages } : {},
77494
78648
  ...maxTokens !== void 0 ? { maxTokens } : {},
77495
78649
  ...temperature !== void 0 ? { temperature } : {}
77496
- });
78650
+ })], timer, abortHandler;
78651
+ options?.timeoutMs && options.timeoutMs > 0 && promises2.push(
78652
+ new Promise((_, reject) => {
78653
+ timer = setTimeout(() => {
78654
+ let err = new Error(`Hosted Gemma advisory generation timed out after ${options.timeoutMs}ms`);
78655
+ err.name = "TimeoutError", reject(err);
78656
+ }, options.timeoutMs);
78657
+ })
78658
+ ), options?.signal && promises2.push(
78659
+ new Promise((_, reject) => {
78660
+ abortHandler = () => {
78661
+ let err = new Error("Hosted Gemma generation aborted by signal");
78662
+ err.name = "AbortError", reject(err);
78663
+ }, options.signal.addEventListener("abort", abortHandler, { once: !0 });
78664
+ })
78665
+ );
78666
+ let response;
78667
+ try {
78668
+ response = await Promise.race(promises2);
78669
+ } finally {
78670
+ timer && clearTimeout(timer), abortHandler && options?.signal && options.signal.removeEventListener("abort", abortHandler);
78671
+ }
77497
78672
  return this.deps.crypto.decryptString(response.advisoryText, sessionKey);
77498
78673
  } catch (err) {
77499
78674
  let elapsedMs = Date.now() - start;
@@ -81452,6 +82627,7 @@ function enforceStrictJsonStdout(raw) {
81452
82627
  var LocalGemmaProcessRunner = class {
81453
82628
  constructor(config) {
81454
82629
  this.config = config;
82630
+ this.providerKind = "local";
81455
82631
  this.runtimeLabel = `local-gemma-process:${import_path4.default.basename(config.command)}`;
81456
82632
  }
81457
82633
  async classify(promptText) {