@quantiya/codevibe-codex-plugin 2.0.61 → 2.0.63

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,1008 @@ 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
+ let isComposite = !!runner?.opts, isFreeTier = runner?.opts?.tier === "FREE";
68320
+ if (isComposite) {
68321
+ let localCandidate = runner?.opts?.localRunner ?? runner?.localRunner ?? null, isLocalHealthy = runner?.localHealthy ?? !0, hostedCandidate = runner?.opts?.hostedRunner ?? runner?.hostedRunner ?? null;
68322
+ return localCandidate && isLocalHealthy && localCandidate.runtimeLabel?.startsWith("local-gemma-process:") ? null : isFreeTier ? localCandidate && isLocalHealthy && isQualifiedAdvisoryRunner(localCandidate) ? localCandidate : null : localCandidate && isLocalHealthy && isQualifiedAdvisoryRunner(localCandidate) ? runner : hostedCandidate && isQualifiedAdvisoryRunner(hostedCandidate) ? hostedCandidate : null;
68323
+ }
68324
+ return (runner.providerKind === "local" || !runner.providerKind && (runner.runtimeLabel?.startsWith("local-gemma-ollama:") || runner.runtimeLabel?.startsWith("local-gemma-process:"))) && isQualifiedAdvisoryRunner(runner) || runner.providerKind === "hosted" && isQualifiedAdvisoryRunner(runner) ? runner : null;
68325
+ }
68326
+ function renderLocalGemmaGitStatusQueryPrompt(args) {
68327
+ let sanitized = sanitizeForTerminal(redactAbsoluteLocalPaths(args.userPrompt)).trim();
68328
+ return [
68329
+ "You are a semantic Git query analyzer for CodeVibe.",
68330
+ "Classify the user prompt to determine if they are asking about Git repository status, diffs, commits, history, branches, or working tree state.",
68331
+ "Respond with STRICT JSON ONLY. Do not emit markdown formatting or conversational text outside the JSON object.",
68332
+ "",
68333
+ "JSON Schema:",
68334
+ "{",
68335
+ ' "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.',
68336
+ ' "asksStatus": boolean, // true if asking about working tree state, untracked/modified/staged files, or git status',
68337
+ ' "asksDiff": boolean, // true if asking for working tree diffs or uncommitted changes (NOT a commit diff)',
68338
+ ' "asksLog": boolean, // true if asking for commit history, recent commits, or git log',
68339
+ ' "asksBranch": boolean, // true if asking to list branches or current branch',
68340
+ ' "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")',
68341
+ ' "targetRepo": string | null // repository name if an explicit sub-repository or sibling repo was mentioned, otherwise null',
68342
+ "}",
68343
+ "",
68344
+ "Examples:",
68345
+ 'User: "Show me the diff of the most recent commit on this branch"',
68346
+ '{"isGitQuery":true,"asksStatus":false,"asksDiff":false,"asksLog":false,"asksBranch":false,"showTarget":"HEAD","targetRepo":null}',
68347
+ "",
68348
+ 'User: "Are there any untracked or modified files in the working tree right now?"',
68349
+ '{"isGitQuery":true,"asksStatus":true,"asksDiff":false,"asksLog":false,"asksBranch":false,"showTarget":null,"targetRepo":null}',
68350
+ "",
68351
+ `User: "What's changed since my last commit?"`,
68352
+ '{"isGitQuery":true,"asksStatus":true,"asksDiff":true,"asksLog":false,"asksBranch":false,"showTarget":null,"targetRepo":null}',
68353
+ "",
68354
+ 'User: "What is the status of the running task?"',
68355
+ '{"isGitQuery":false,"asksStatus":false,"asksDiff":false,"asksLog":false,"asksBranch":false,"showTarget":null,"targetRepo":null}',
68356
+ "",
68357
+ 'User: "What\'s the difference between `git checkout` and `git switch`?"',
68358
+ '{"isGitQuery":false,"asksStatus":false,"asksDiff":false,"asksLog":false,"asksBranch":false,"showTarget":null,"targetRepo":null}',
68359
+ "",
68360
+ 'User: "How does git status work?"',
68361
+ '{"isGitQuery":false,"asksStatus":false,"asksDiff":false,"asksLog":false,"asksBranch":false,"showTarget":null,"targetRepo":null}',
68362
+ "",
68363
+ 'User: "What changed in React 19?"',
68364
+ '{"isGitQuery":false,"asksStatus":false,"asksDiff":false,"asksLog":false,"asksBranch":false,"showTarget":null,"targetRepo":null}',
68365
+ "",
68366
+ 'User: "How do I get the current working directory in Python?"',
68367
+ '{"isGitQuery":false,"asksStatus":false,"asksDiff":false,"asksLog":false,"asksBranch":false,"showTarget":null,"targetRepo":null}',
68368
+ "",
68369
+ `User prompt: ${JSON.stringify(sanitized)}`
68370
+ ].join(`
68371
+ `);
68372
+ }
68373
+ function parseGemmaGitStatusResponse(raw) {
68374
+ if (!raw || typeof raw != "string") return null;
68375
+ let cleaned = raw.trim();
68376
+ cleaned.startsWith("```") && (cleaned = cleaned.replace(/^```(?:json)?\s*\n?/i, "").replace(/\n?```\s*$/i, "").trim());
68377
+ let parsed;
68378
+ try {
68379
+ parsed = JSON.parse(cleaned);
68380
+ } catch {
68381
+ return null;
68382
+ }
68383
+ 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")
68384
+ return null;
68385
+ let isGitQuery = parsed.isGitQuery, asksStatus = parsed.asksStatus, asksDiff = parsed.asksDiff, asksLog = parsed.asksLog, asksBranch = parsed.asksBranch, showTarget = null;
68386
+ if (typeof parsed.showTarget == "string" && parsed.showTarget.trim()) {
68387
+ let candidateTarget = parsed.showTarget.trim();
68388
+ SAFE_REF_PATTERN.test(candidateTarget) && !candidateTarget.startsWith("-") && !candidateTarget.includes("..") && !candidateTarget.endsWith("^!") && !candidateTarget.endsWith("^@") && (showTarget = candidateTarget);
68389
+ }
68390
+ let targetRepo = null;
68391
+ if (typeof parsed.targetRepo == "string" && parsed.targetRepo.trim()) {
68392
+ let candidateRepo = parsed.targetRepo.trim();
68393
+ SAFE_REPO_NAME_PATTERN.test(candidateRepo) && candidateRepo !== "." && candidateRepo !== ".." && !candidateRepo.includes("/") && !candidateRepo.includes("\\") && (targetRepo = candidateRepo);
68394
+ }
68395
+ return {
68396
+ isGitQuery,
68397
+ asksStatus,
68398
+ asksDiff,
68399
+ asksLog,
68400
+ asksBranch,
68401
+ showTarget,
68402
+ targetRepo
68403
+ };
68404
+ }
68405
+ async function parseGitStatusQueryWithGemma(runner, prompt, timeoutMs = 12e3) {
68406
+ let startTime = Date.now(), boundedPrompt = prompt.slice(0, 2e3).trim(), promptText = renderLocalGemmaGitStatusQueryPrompt({ userPrompt: boundedPrompt }), abortController = new AbortController(), timer = null;
68407
+ try {
68408
+ let timeoutPromise = new Promise((_, reject) => {
68409
+ timer = setTimeout(() => {
68410
+ abortController.abort(), reject(new Error(`Gemma status query classification timed out after ${timeoutMs}ms`));
68411
+ }, timeoutMs), timer?.unref?.();
68412
+ }), advisoryPromise = runner.generateAdvisory(promptText, {
68413
+ responseFormat: "json",
68414
+ numPredict: 100,
68415
+ temperature: 0.1,
68416
+ timeoutMs,
68417
+ signal: abortController.signal
68418
+ }), raw = await Promise.race([advisoryPromise, timeoutPromise]);
68419
+ if (!raw || typeof raw != "string")
68420
+ return logger.warn("[git-status-summary] Gemma returned empty or non-string response", {
68421
+ prompt: boundedPrompt,
68422
+ durationMs: Date.now() - startTime
68423
+ }), null;
68424
+ let parsed = parseGemmaGitStatusResponse(raw);
68425
+ return parsed || (logger.warn("[git-status-summary] Gemma response failed schema validation", {
68426
+ prompt: boundedPrompt,
68427
+ raw,
68428
+ durationMs: Date.now() - startTime
68429
+ }), null);
68430
+ } catch (err) {
68431
+ let elapsed = Date.now() - startTime;
68432
+ return logger.warn("[git-status-summary] Gemma status query classification timed out or failed", {
68433
+ prompt: boundedPrompt,
68434
+ durationMs: elapsed,
68435
+ error: err?.message || String(err)
68436
+ }), null;
68437
+ } finally {
68438
+ timer && clearTimeout(timer);
68439
+ }
68440
+ }
68441
+ function parseGitStatusQuerySemantic(prompt) {
68442
+ let trimmed = prompt.trim();
68443
+ if (!trimmed)
68444
+ return {
68445
+ isGitQuery: !1,
68446
+ asksStatus: !1,
68447
+ asksDiff: !1,
68448
+ asksLog: !1,
68449
+ asksBranch: !1,
68450
+ showTarget: null,
68451
+ targetRepo: null
68452
+ };
68453
+ let isExplicitGit = /\b(?:git\s+(?:status|diff|log|branch|show|checkout|switch|restore)|commit\s+history|recent\s+commits)\b/i.test(trimmed);
68454
+ 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)))
68455
+ return {
68456
+ isGitQuery: !1,
68457
+ asksStatus: !1,
68458
+ asksDiff: !1,
68459
+ asksLog: !1,
68460
+ asksBranch: !1,
68461
+ showTarget: null,
68462
+ targetRepo: null
68463
+ };
68464
+ 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;
68465
+ if (asksCommitDiff) {
68466
+ 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);
68467
+ commitHexMatch && !commitHexMatch[1].includes("..") ? showTarget = stripSentencePunctuation(commitHexMatch[1]) : showTarget = extractBranchOrRefTarget(trimmed) ?? "HEAD";
68468
+ }
68469
+ 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(?:dirty|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+)?(?:dirty|untracked|modified)\s+files\b/i.test(trimmed) || /\bfiles\s+(?:are\s+)?(?:still\s+|currently\s+)?dirty\b/i.test(trimmed) || /\b(?:what|which)\s+files\s+(?:are\s+)?(?:still\s+|currently\s+)?dirty\b/i.test(trimmed) || /\b(?:repo|repository|workspace|working\s+(?:tree|copy))\s+(?:is\s+)?dirty\b/i.test(trimmed) || /\b(?:is\s+(?:my\s+)?)(?:repo|repository|workspace|working\s+(?:tree|copy))\s+dirty\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|are\s+we)\s+(?:currently\s+|presently\s+|right\s+now\s+)?(?:on|working\s+on)\b/i.test(trimmed) || /\b(?:what|which)\s+branch\s+(?:is\s+(?:currently\s+|presently\s+)?checked\s+out|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);
68470
+ return {
68471
+ isGitQuery: !!(isExplicitGit || GIT_QUERY_PATTERN.test(trimmed) || asksStatus || asksDiff || asksLog || asksBranch || asksShow || asksCommitDiff),
68472
+ asksStatus,
68473
+ asksDiff,
68474
+ asksLog,
68475
+ asksBranch,
68476
+ showTarget,
68477
+ targetRepo: null
68478
+ };
68479
+ }
68480
+ async function resolveGitStatusIntent(options) {
68481
+ let { prompt, advisoryRunner } = options, trimmed = prompt.trim();
68482
+ if (!trimmed)
68483
+ return {
68484
+ isGitQuery: !1,
68485
+ asksStatus: !1,
68486
+ asksDiff: !1,
68487
+ asksLog: !1,
68488
+ asksBranch: !1,
68489
+ showTarget: null,
68490
+ targetRepo: null
68491
+ };
68492
+ if (isConceptualGitQuestion(trimmed))
68493
+ return {
68494
+ isGitQuery: !1,
68495
+ asksStatus: !1,
68496
+ asksDiff: !1,
68497
+ asksLog: !1,
68498
+ asksBranch: !1,
68499
+ showTarget: null,
68500
+ targetRepo: null
68501
+ };
68502
+ if (/^(?:[`'"]?(?:\/|!)?git\s+(?:status|diff|log|branch|show|checkout|switch|restore)\b)/i.test(trimmed))
68503
+ return parseGitStatusQuerySemantic(trimmed);
68504
+ let candidateRunner = resolvePreferredAdvisoryRunner(advisoryRunner);
68505
+ if (candidateRunner)
68506
+ try {
68507
+ let gemmaStart = Date.now(), gemmaIntent = await parseGitStatusQueryWithGemma(candidateRunner, trimmed), gemmaDurationMs = Date.now() - gemmaStart;
68508
+ if (gemmaIntent !== null) {
68509
+ if (gemmaIntent.targetRepo) {
68510
+ let repoCandidate = stripSentencePunctuation(gemmaIntent.targetRepo), candLower = repoCandidate.toLowerCase(), genericStopwords = /* @__PURE__ */ new Set([
68511
+ "work",
68512
+ "working",
68513
+ "tree",
68514
+ "directory",
68515
+ "copy",
68516
+ "repo",
68517
+ "repository",
68518
+ "git",
68519
+ "current",
68520
+ "active",
68521
+ "local",
68522
+ "here",
68523
+ "this",
68524
+ "diff",
68525
+ "status",
68526
+ "log",
68527
+ "branch",
68528
+ "show",
68529
+ "commit",
68530
+ "commits"
68531
+ ]), isExplicitRepoSyntax = SAFE_REPO_NAME_PATTERN.test(repoCandidate) && !repoCandidate.includes(".."), designatedTokens = [], afterMatches = trimmed.matchAll(
68532
+ /\b(?:in|for|from|under|at|inside|to)\s+(?:the\s+)?(?:repo|repository|submodule|package|dir|directory|project)\s+["'`]?([A-Za-z0-9_.-]+)["'`]?/gi
68533
+ );
68534
+ for (let m of afterMatches) {
68535
+ let tok = stripSentencePunctuation(m[1]).toLowerCase();
68536
+ tok && designatedTokens.push(tok);
68537
+ }
68538
+ let beforeMatches = trimmed.matchAll(
68539
+ /(?<=^|[\s"'\`(\[{<])([A-Za-z0-9_.-]+)\s+(?:repo|repository|package|project|submodule)\b/gi
68540
+ );
68541
+ for (let m of beforeMatches) {
68542
+ let tok = stripSentencePunctuation(m[1]).toLowerCase();
68543
+ tok && designatedTokens.push(tok);
68544
+ }
68545
+ !designatedTokens.some(
68546
+ (token) => token === candLower && !genericStopwords.has(token)
68547
+ ) || !isExplicitRepoSyntax || genericStopwords.has(candLower) ? gemmaIntent.targetRepo = null : gemmaIntent.targetRepo = repoCandidate;
68548
+ }
68549
+ return logger.info("[git-status-summary] Gemma status intent decision", {
68550
+ prompt: trimmed,
68551
+ runner: candidateRunner.runtimeLabel,
68552
+ providerKind: candidateRunner.providerKind,
68553
+ durationMs: gemmaDurationMs,
68554
+ intent: gemmaIntent
68555
+ }), gemmaIntent;
68556
+ } else
68557
+ logger.info("[git-status-summary] Gemma returned null or timed out, falling back to deterministic semantic parser", {
68558
+ prompt: trimmed
68559
+ });
68560
+ } catch (err) {
68561
+ logger.warn("[git-status-summary] Gemma status intent query failed, falling back to deterministic semantic parser", {
68562
+ prompt: trimmed,
68563
+ error: err?.message
68564
+ });
68565
+ }
68566
+ return parseGitStatusQuerySemantic(trimmed);
68567
+ }
68568
+ var IGNORED_TARGET_NAMES = /* @__PURE__ */ new Set([
67552
68569
  "git",
67553
68570
  "the",
67554
68571
  "this",
@@ -67703,7 +68720,9 @@ var defaultExecFileAsync = (0, import_util3.promisify)(import_child_process5.exe
67703
68720
  "very"
67704
68721
  ], STOPWORDS = new Set(FUNCTION_WORDS), NON_SHOW_TARGET_WORDS = new Set(FUNCTION_WORDS);
67705
68722
  function extractCandidateRepos(prompt) {
67706
- let candidates = [], seen = /* @__PURE__ */ new Set(), addCandidate = (name, isExplicit) => {
68723
+ let candidates = [], seen = /* @__PURE__ */ new Set(), addCandidate = (rawName, isExplicit) => {
68724
+ let name = stripSentencePunctuation(rawName);
68725
+ if (!name) return;
67707
68726
  let lower = name.toLowerCase();
67708
68727
  if (IGNORED_TARGET_NAMES.has(lower) || /^[0-9a-f]{7,40}$/i.test(name))
67709
68728
  return;
@@ -67714,13 +68733,20 @@ function extractCandidateRepos(prompt) {
67714
68733
  let existing = candidates.find((c) => c.name.toLowerCase() === lower);
67715
68734
  existing && (existing.isExplicit = !0);
67716
68735
  }
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;
68736
+ }, trailingRepoRegex = /(?<=^|[\s"'\`(\[{<])([a-zA-Z0-9_.-]+)\s+(?:repo(?:sitory)?|project|package|submodule)\b/gi, match;
68737
+ for (; (match = trailingRepoRegex.exec(prompt)) !== null; ) {
68738
+ let raw = match[1], isStopword = STOPWORDS.has(raw.toLowerCase());
68739
+ addCandidate(raw, !isStopword);
68740
+ }
68741
+ let leadingRepoRegex = /\b(?:repo(?:sitory)?|project|package|submodule)\s+([a-zA-Z0-9_.-]+)/gi;
68742
+ for (; (match = leadingRepoRegex.exec(prompt)) !== null; ) {
68743
+ let raw = match[1];
68744
+ if (match[0].toLowerCase().startsWith("package") && raw.toLowerCase() === "lock")
68745
+ continue;
68746
+ let isStopword = STOPWORDS.has(raw.toLowerCase());
68747
+ addCandidate(raw, !isStopword);
68748
+ }
68749
+ let casualRegex = /\b(?:in|of|for)\s+(?:the\s+)?([a-zA-Z0-9_.-]+)/gi;
67724
68750
  for (; (match = casualRegex.exec(prompt)) !== null; )
67725
68751
  addCandidate(match[1], !1);
67726
68752
  return candidates;
@@ -67736,8 +68762,11 @@ ${content}
67736
68762
  ${fence}`;
67737
68763
  }
67738
68764
  async function executeGitStatusSummary(options) {
67739
- let { cwd, prompt, hasActiveTask, execFileFn = defaultExecFileAsync } = options;
67740
- if (!GIT_QUERY_PATTERN.test(prompt) || hasActiveTask)
68765
+ let { cwd, prompt, hasActiveTask, advisoryRunner, execFileFn = defaultExecFileAsync } = options;
68766
+ if (hasActiveTask)
68767
+ return null;
68768
+ let intent = await resolveGitStatusIntent({ prompt, advisoryRunner });
68769
+ if (!intent.isGitQuery)
67741
68770
  return null;
67742
68771
  let gitEnv = scrubSearchApiKeys({ ...process.env, LC_ALL: "C" }), defaultRepoName = sanitizeForTerminal(redactAbsoluteLocalPaths(path64.basename(cwd))), defaultRepoTopLevel = cwd;
67743
68772
  try {
@@ -67749,8 +68778,18 @@ async function executeGitStatusSummary(options) {
67749
68778
  trimmed && (defaultRepoTopLevel = trimmed, defaultRepoName = sanitizeForTerminal(redactAbsoluteLocalPaths(path64.basename(trimmed))));
67750
68779
  } catch {
67751
68780
  }
67752
- let targetCwd = cwd, targetRepoName = defaultRepoName, notePrefix = "", candidates = extractCandidateRepos(prompt), resolvedRepo = null;
68781
+ let targetCwd = cwd, targetRepoName = defaultRepoName, notePrefix = "", candidates = extractCandidateRepos(prompt);
68782
+ if (intent.targetRepo) {
68783
+ let rawTarget = stripSentencePunctuation(intent.targetRepo);
68784
+ if (SAFE_REPO_NAME_PATTERN.test(rawTarget) && rawTarget !== "." && rawTarget !== ".." && !rawTarget.includes("/") && !rawTarget.includes("\\")) {
68785
+ let existingIndex = candidates.findIndex((c) => c.name.toLowerCase() === rawTarget.toLowerCase());
68786
+ existingIndex >= 0 && candidates.splice(existingIndex, 1), candidates.unshift({ name: rawTarget, isExplicit: !0 });
68787
+ }
68788
+ }
68789
+ let resolvedRepo = null;
67753
68790
  for (let candidate of candidates) {
68791
+ if (!SAFE_REPO_NAME_PATTERN.test(candidate.name) || candidate.name === "." || candidate.name === ".." || candidate.name.includes("/") || candidate.name.includes("\\"))
68792
+ continue;
67754
68793
  if (defaultRepoName.toLowerCase() === candidate.name.toLowerCase()) {
67755
68794
  resolvedRepo = { cwd, name: defaultRepoName };
67756
68795
  break;
@@ -67809,7 +68848,64 @@ async function executeGitStatusSummary(options) {
67809
68848
 
67810
68849
  `), targetCwd = cwd, targetRepoName = defaultRepoName;
67811
68850
  }
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);
68851
+ 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 });
68852
+ async function isValidGitComparisonRef(ref, targetCwd2, gitEnv2, execFileFn2) {
68853
+ if (ref.includes("..")) {
68854
+ let rangeParts = ref.includes("...") ? ref.split("...") : ref.split("..");
68855
+ if (rangeParts.length !== 2 || !rangeParts[0] && !rangeParts[1])
68856
+ return !1;
68857
+ for (let part of rangeParts) {
68858
+ let endpoint = part || "HEAD";
68859
+ if (!SAFE_REF_PATTERN.test(endpoint) || endpoint.startsWith("-"))
68860
+ return !1;
68861
+ try {
68862
+ await execFileFn2(
68863
+ "git",
68864
+ ["--no-optional-locks", "rev-parse", "--verify", "--quiet", `${endpoint}^{commit}`],
68865
+ { cwd: targetCwd2, env: gitEnv2, timeout: 3e3, encoding: "utf8" }
68866
+ );
68867
+ } catch {
68868
+ return !1;
68869
+ }
68870
+ }
68871
+ return !0;
68872
+ }
68873
+ if (!SAFE_REF_PATTERN.test(ref) || ref.startsWith("-"))
68874
+ return !1;
68875
+ try {
68876
+ return await execFileFn2(
68877
+ "git",
68878
+ ["--no-optional-locks", "rev-parse", "--verify", "--quiet", `${ref}^{commit}`],
68879
+ { cwd: targetCwd2, env: gitEnv2, timeout: 3e3, encoding: "utf8" }
68880
+ ), !0;
68881
+ } catch {
68882
+ return !1;
68883
+ }
68884
+ }
68885
+ function occursOutsideComparisonSpan(prompt2, target, span, comparisonRef) {
68886
+ if (!span) {
68887
+ if (comparisonRef) {
68888
+ let lowerComp = comparisonRef.toLowerCase(), lowerTgt = target.toLowerCase();
68889
+ if (lowerComp === lowerTgt || lowerComp.includes(lowerTgt) || lowerTgt.includes(lowerComp))
68890
+ return !1;
68891
+ }
68892
+ return !0;
68893
+ }
68894
+ let lowerPrompt = prompt2.toLowerCase(), lowerTarget = target.toLowerCase(), idx = lowerPrompt.indexOf(lowerTarget);
68895
+ if (idx === -1)
68896
+ return !0;
68897
+ for (; idx !== -1; ) {
68898
+ if (idx + lowerTarget.length <= span.start || idx >= span.end)
68899
+ return !0;
68900
+ idx = lowerPrompt.indexOf(lowerTarget, idx + 1);
68901
+ }
68902
+ return !1;
68903
+ }
68904
+ if (comparisonBaseInfo && !comparisonBaseInfo.isExplicit) {
68905
+ let candidateRef = comparisonBaseInfo.ref;
68906
+ await isValidGitComparisonRef(candidateRef, targetCwd, gitEnv, execFileFn) || (comparisonBaseInfo = void 0);
68907
+ }
68908
+ 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
68909
  if (openerMatch) {
67814
68910
  let opener = openerMatch[1], pairRegex = null;
67815
68911
  switch (opener) {
@@ -67881,9 +68977,34 @@ async function executeGitStatusSummary(options) {
67881
68977
  }
67882
68978
  }
67883
68979
  }
67884
- if (!showTarget) {
67885
- let commitMatch = /\b([0-9a-f]{7,40})\b/i.exec(prompt);
67886
- commitMatch ? showTarget = commitMatch[1] : asksShow && (showTarget = "HEAD");
68980
+ if (!isExplicitCommitShowRequest && hasComparison)
68981
+ showTarget = null;
68982
+ else if (!showTarget) {
68983
+ 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;
68984
+ if (showCommitMatch && targetRef && occursOutsideComparisonSpan(prompt, targetRef, comparisonBaseInfo?.span, comparisonBaseInfo?.ref))
68985
+ showTarget = stripSentencePunctuation(targetRef);
68986
+ else if (asksShow && intent.showTarget && occursOutsideComparisonSpan(prompt, intent.showTarget, comparisonBaseInfo?.span, comparisonBaseInfo?.ref))
68987
+ showTarget = intent.showTarget;
68988
+ else {
68989
+ let nonComparisonHash = null;
68990
+ 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)) {
68991
+ let matchIndex = m.index, matchEnd = matchIndex + m[0].length;
68992
+ if (comparisonBaseInfo?.span) {
68993
+ if (!(matchEnd <= comparisonBaseInfo.span.start || matchIndex >= comparisonBaseInfo.span.end))
68994
+ continue;
68995
+ } else if (comparisonBaseInfo?.ref) {
68996
+ let compRef = comparisonBaseInfo.ref.toLowerCase(), cand = m[1].toLowerCase();
68997
+ if (compRef === cand || compRef.includes(cand) || cand.includes(compRef))
68998
+ continue;
68999
+ }
69000
+ let candidate = stripSentencePunctuation(m[1]);
69001
+ if (!candidate.includes("..")) {
69002
+ nonComparisonHash = candidate;
69003
+ break;
69004
+ }
69005
+ }
69006
+ nonComparisonHash ? showTarget = nonComparisonHash : asksShow && (showTarget = "HEAD");
69007
+ }
67887
69008
  }
67888
69009
  let includeStatus = asksStatus || !asksDiff && !asksBranch && !asksShow, includeLog = asksLog || !asksDiff && !asksBranch && !asksShow;
67889
69010
  try {
@@ -67920,24 +69041,27 @@ ${formatFencedCodeBlock(statusContent, "text")}`);
67920
69041
  ${formatFencedCodeBlock(logContent, "text")}`);
67921
69042
  }
67922
69043
  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(`
69044
+ 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";
69045
+ 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")));
69046
+ 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"];
69047
+ if (!diffContent.startsWith("Commit or reference"))
69048
+ try {
69049
+ let { stdout: gitDiff } = await execFileFn(
69050
+ "git",
69051
+ diffArgs,
69052
+ { cwd: targetCwd, env: gitEnv, timeout: 3e3, encoding: "utf8" }
69053
+ ), diffLines = gitDiff.split(/\r?\n/).map((l) => l.trimEnd()).filter((l) => l.length > 0);
69054
+ if (diffLines.length > 0) {
69055
+ let capped = diffLines.slice(0, 20), rem = diffLines.length - 20;
69056
+ diffContent = rem > 0 ? `${capped.join(`
67933
69057
  `)}
67934
69058
  ...and ${rem} more` : capped.join(`
67935
69059
  `);
69060
+ }
69061
+ } catch {
69062
+ diffContent = isStaged ? "Staged diff is unavailable" : "Diff is unavailable";
67936
69063
  }
67937
- } catch {
67938
- diffContent = "Diff is unavailable";
67939
- }
67940
- diffContent = sanitizeForTerminal(redactAbsoluteLocalPaths(diffContent)), sections.push(`### Git Diff (${targetRepoName})
69064
+ diffContent = sanitizeForTerminal(redactAbsoluteLocalPaths(diffContent)), sections.push(`${diffTitle}
67941
69065
  ${formatFencedCodeBlock(diffContent, "text")}`);
67942
69066
  }
67943
69067
  if (asksBranch) {
@@ -67948,15 +69072,19 @@ ${formatFencedCodeBlock(diffContent, "text")}`);
67948
69072
  ["--no-optional-locks", "-c", "color.ui=never", "branch", "--list"],
67949
69073
  { cwd: targetCwd, env: gitEnv, timeout: 3e3, encoding: "utf8" }
67950
69074
  ), branchLines = gitBranch.split(/\r?\n/).map((l) => l.trimEnd()).filter((l) => l.length > 0);
67951
- branchLines.length > 0 && (branchContent = branchLines.slice(0, 10).join(`
67952
- `));
69075
+ if (branchLines.length > 0) {
69076
+ let currentBranchLine = branchLines.find((l) => l.startsWith("* ")), cappedOthers = branchLines.filter((l) => !l.startsWith("* ")).slice(0, currentBranchLine ? 9 : 10);
69077
+ branchContent = (currentBranchLine ? [currentBranchLine, ...cappedOthers] : cappedOthers).join(`
69078
+ `);
69079
+ }
67953
69080
  } catch {
67954
69081
  branchContent = "Branch listing is unavailable";
67955
69082
  }
67956
69083
  branchContent = sanitizeForTerminal(redactAbsoluteLocalPaths(branchContent)), sections.push(`### Git Branches (${targetRepoName})
67957
69084
  ${formatFencedCodeBlock(branchContent, "text")}`);
67958
69085
  }
67959
- if (showTarget) {
69086
+ if (!isSinceLastCommit && showTarget) {
69087
+ (showTarget.startsWith("-") || showTarget.includes("\0")) && (showTarget = "HEAD");
67960
69088
  let isHexHash = /^[0-9a-f]{7,40}$/i.test(showTarget), safeTarget = sanitizeForTerminal(redactAbsoluteLocalPaths(showTarget)), title = isHexHash ? `Commit ${safeTarget}` : `Git Show ${safeTarget}`;
67961
69089
  try {
67962
69090
  let gitShowRef = showTarget.endsWith("^!") || showTarget.endsWith("^@") ? showTarget : `${showTarget}^{commit}`, { stdout: showOut } = await execFileFn(
@@ -71053,7 +72181,7 @@ async function runOrchestrationShell(args) {
71053
72181
  let isSlash = isShellSlashCommand(text2), browseController = null;
71054
72182
  isSlash || (browseController = new AbortController(), activeBrowseAbortController = browseController);
71055
72183
  try {
71056
- await handleShellUserInput({
72184
+ logger.info("[orchestration-shell] Turn started", { prompt: text2 }), await handleShellUserInput({
71057
72185
  text: text2,
71058
72186
  store,
71059
72187
  args,
@@ -71087,7 +72215,7 @@ async function runOrchestrationShell(args) {
71087
72215
  // concurrent slash bypass can't leak its advisory into this mirror
71088
72216
  // and vice-versa.
71089
72217
  ownEntries
71090
- });
72218
+ }), logger.info("[orchestration-shell] Turn completed", { prompt: text2 });
71091
72219
  });
71092
72220
  }
71093
72221
  ), handleUserInput = (text2, images, options) => {
@@ -75595,6 +76723,10 @@ async function handleShellUserInput(deps) {
75595
76723
  if (decision = await dispatchClassify({
75596
76724
  ...plannerInput,
75597
76725
  prompt: classifyPrompt
76726
+ }), logger.info("[orchestration-shell] Planner classified input", {
76727
+ prompt: plannerInput.prompt,
76728
+ action: decision.action,
76729
+ rationale: decision.rationale
75598
76730
  }), isLocalPlannerRuntime(args) && decision.action === "browse" && hasFileMutationIntent(plannerInput.prompt) && (decision = {
75599
76731
  action: "start_task",
75600
76732
  rationale: decision.rationale ? `${decision.rationale} (promoted to start_task due to file mutation intent)` : "web request with file mutation intent"
@@ -75771,6 +76903,27 @@ async function handleShellUserInput(deps) {
75771
76903
  return;
75772
76904
  }
75773
76905
  if (isTextAdvisory) {
76906
+ let hasRunningSingle = store.getState().runningTasks.size > 0, team = store.getState().team, teamActive = team !== null && !team.groupResolved && team.haltReason === null, hasActiveTask = hasRunningSingle || teamActive;
76907
+ if (!isConceptualGitQuestion(dispatchText)) {
76908
+ let gitResponse = await executeGitStatusSummary({
76909
+ cwd: args.cwd || process.cwd(),
76910
+ prompt: dispatchText,
76911
+ hasActiveTask,
76912
+ advisoryRunner: args.localAdvisoryRunner,
76913
+ execFileFn: execFileAsync
76914
+ });
76915
+ if (gitResponse !== null) {
76916
+ logger.info("[orchestration-shell] Dispatched SHELL_ADVISORY for git status", {
76917
+ prompt: dispatchText,
76918
+ responseLength: gitResponse.length
76919
+ }), store.dispatch({
76920
+ type: "SHELL_ADVISORY",
76921
+ source: "shell",
76922
+ text: gitResponse
76923
+ });
76924
+ return;
76925
+ }
76926
+ }
75774
76927
  await routeAdvisory({
75775
76928
  store,
75776
76929
  args,
@@ -75946,6 +77099,7 @@ async function handleShellUserInput(deps) {
75946
77099
  cwd: args.cwd || process.cwd(),
75947
77100
  prompt: plannerInput.prompt,
75948
77101
  hasActiveTask,
77102
+ advisoryRunner: args.localAdvisoryRunner,
75949
77103
  execFileFn: execFileAsync
75950
77104
  });
75951
77105
  if (gitResponse !== null) {
@@ -76869,7 +78023,7 @@ var import_node_fs28 = require("node:fs"), path69 = __toESM(require("node:path")
76869
78023
  this.signatureCache = /* @__PURE__ */ new Map();
76870
78024
  this.hydrated = /* @__PURE__ */ new Set();
76871
78025
  this.mutex = new AsyncMutex();
76872
- this.cacheRoot = cacheRoot ?? path69.join(os39.homedir(), ".codevibe", "cache");
78026
+ this.cacheRoot = cacheRoot ?? process.env.CODEVIBE_CACHE_ROOT ?? path69.join(os39.homedir(), ".codevibe", "cache");
76873
78027
  }
76874
78028
  async getWorkflowClassification(userId, prompt) {
76875
78029
  await this.hydrateWorkflowIfNeeded(userId);
@@ -77485,15 +78639,40 @@ var HostedGemmaAdvisoryRunner = class {
77485
78639
  }
77486
78640
  encryptedImages = options.images.map((img) => this.deps.crypto.encryptString(img, sessionKey));
77487
78641
  }
78642
+ if (options?.signal?.aborted) {
78643
+ let err = new Error("Hosted Gemma generation aborted by signal");
78644
+ throw err.name = "AbortError", err;
78645
+ }
77488
78646
  let maxTokens = options?.numPredict, temperature = options?.temperature, start = Date.now();
77489
78647
  try {
77490
- let response = await this.deps.transport.generatePlannerAdvisory({
78648
+ let promises2 = [this.deps.transport.generatePlannerAdvisory({
77491
78649
  sessionId,
77492
78650
  prompt: encryptedPrompt,
77493
78651
  ...encryptedImages && encryptedImages.length > 0 ? { images: encryptedImages } : {},
77494
78652
  ...maxTokens !== void 0 ? { maxTokens } : {},
77495
78653
  ...temperature !== void 0 ? { temperature } : {}
77496
- });
78654
+ })], timer, abortHandler;
78655
+ options?.timeoutMs && options.timeoutMs > 0 && promises2.push(
78656
+ new Promise((_, reject) => {
78657
+ timer = setTimeout(() => {
78658
+ let err = new Error(`Hosted Gemma advisory generation timed out after ${options.timeoutMs}ms`);
78659
+ err.name = "TimeoutError", reject(err);
78660
+ }, options.timeoutMs);
78661
+ })
78662
+ ), options?.signal && promises2.push(
78663
+ new Promise((_, reject) => {
78664
+ abortHandler = () => {
78665
+ let err = new Error("Hosted Gemma generation aborted by signal");
78666
+ err.name = "AbortError", reject(err);
78667
+ }, options.signal.addEventListener("abort", abortHandler, { once: !0 });
78668
+ })
78669
+ );
78670
+ let response;
78671
+ try {
78672
+ response = await Promise.race(promises2);
78673
+ } finally {
78674
+ timer && clearTimeout(timer), abortHandler && options?.signal && options.signal.removeEventListener("abort", abortHandler);
78675
+ }
77497
78676
  return this.deps.crypto.decryptString(response.advisoryText, sessionKey);
77498
78677
  } catch (err) {
77499
78678
  let elapsedMs = Date.now() - start;
@@ -81452,6 +82631,7 @@ function enforceStrictJsonStdout(raw) {
81452
82631
  var LocalGemmaProcessRunner = class {
81453
82632
  constructor(config) {
81454
82633
  this.config = config;
82634
+ this.providerKind = "local";
81455
82635
  this.runtimeLabel = `local-gemma-process:${import_path4.default.basename(config.command)}`;
81456
82636
  }
81457
82637
  async classify(promptText) {