@ridit/lens 0.3.4 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -272580,31 +272580,121 @@ import { existsSync as existsSync14 } from "fs";
272580
272580
  import path19 from "path";
272581
272581
 
272582
272582
  // src/components/chat/ChatRunner.tsx
272583
- var import_react48 = __toESM(require_react(), 1);
272584
- var import_react49 = __toESM(require_react(), 1);
272583
+ var import_react51 = __toESM(require_react(), 1);
272585
272584
  import path18 from "path";
272586
272585
  import os8 from "os";
272587
272586
 
272587
+ // src/utils/memory.ts
272588
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "fs";
272589
+ import path16 from "path";
272590
+ import os6 from "os";
272591
+ var LENS_DIR = path16.join(os6.homedir(), ".lens");
272592
+ var MEMORY_PATH = path16.join(LENS_DIR, "memory.json");
272593
+ function loadMemoryFile() {
272594
+ if (!existsSync12(MEMORY_PATH))
272595
+ return { entries: [], memories: [] };
272596
+ try {
272597
+ const data = JSON.parse(readFileSync11(MEMORY_PATH, "utf-8"));
272598
+ return {
272599
+ entries: data.entries ?? [],
272600
+ memories: data.memories ?? []
272601
+ };
272602
+ } catch {
272603
+ return { entries: [], memories: [] };
272604
+ }
272605
+ }
272606
+ function saveMemoryFile(m) {
272607
+ if (!existsSync12(LENS_DIR))
272608
+ mkdirSync6(LENS_DIR, { recursive: true });
272609
+ writeFileSync8(MEMORY_PATH, JSON.stringify(m, null, 2), "utf-8");
272610
+ }
272611
+ function appendMemory(entry) {
272612
+ const m = loadMemoryFile();
272613
+ m.entries.push({ ...entry, timestamp: new Date().toISOString() });
272614
+ if (m.entries.length > 500)
272615
+ m.entries = m.entries.slice(-500);
272616
+ saveMemoryFile(m);
272617
+ }
272618
+ function buildMemorySummary(repoPath) {
272619
+ const m = loadMemoryFile();
272620
+ const relevant = m.entries.slice(-50);
272621
+ const memories = m.memories;
272622
+ const parts = [];
272623
+ if (memories.length > 0) {
272624
+ parts.push(`## MEMORIES ABOUT THIS REPO
272625
+
272626
+ ${memories.map((mem) => `- [${mem.id}] ${mem.content}`).join(`
272627
+ `)}`);
272628
+ }
272629
+ if (relevant.length > 0) {
272630
+ const lines = relevant.map((e) => {
272631
+ const ts = new Date(e.timestamp).toLocaleString();
272632
+ return `[${ts}] ${e.kind}: ${e.detail} — ${e.summary}`;
272633
+ });
272634
+ parts.push(`## WHAT YOU HAVE ALREADY DONE IN THIS REPO
272635
+
272636
+ The following actions have already been completed. Do NOT repeat them unless the user explicitly asks you to redo something:
272637
+
272638
+ ${lines.join(`
272639
+ `)}`);
272640
+ }
272641
+ return parts.join(`
272642
+
272643
+ `);
272644
+ }
272645
+ function clearRepoMemory(repoPath) {
272646
+ const m = loadMemoryFile();
272647
+ m.entries = m.entries = [];
272648
+ m.memories = m.memories = [];
272649
+ saveMemoryFile(m);
272650
+ }
272651
+ function generateId() {
272652
+ return Math.random().toString(36).slice(2, 8);
272653
+ }
272654
+ function addMemory(content, repoPath) {
272655
+ const m = loadMemoryFile();
272656
+ const memory = {
272657
+ id: generateId(),
272658
+ content,
272659
+ timestamp: new Date().toISOString()
272660
+ };
272661
+ m.memories.push(memory);
272662
+ saveMemoryFile(m);
272663
+ return memory;
272664
+ }
272665
+ function deleteMemory(id, repoPath) {
272666
+ const m = loadMemoryFile();
272667
+ const before2 = m.memories.length;
272668
+ m.memories = m.memories.filter((mem) => !(mem.id === id));
272669
+ if (m.memories.length === before2)
272670
+ return false;
272671
+ saveMemoryFile(m);
272672
+ return true;
272673
+ }
272674
+ function listMemories(repoPath) {
272675
+ return loadMemoryFile().memories;
272676
+ }
272677
+
272588
272678
  // src/utils/chatHistory.ts
272589
272679
  import {
272590
- existsSync as existsSync12,
272591
- mkdirSync as mkdirSync6,
272592
- readFileSync as readFileSync11,
272680
+ existsSync as existsSync13,
272681
+ mkdirSync as mkdirSync7,
272682
+ readFileSync as readFileSync12,
272593
272683
  readdirSync as readdirSync4,
272594
- writeFileSync as writeFileSync8,
272684
+ writeFileSync as writeFileSync9,
272595
272685
  unlinkSync
272596
272686
  } from "fs";
272597
- import path16 from "path";
272598
- import os6 from "os";
272599
- var LENS_DIR = path16.join(os6.homedir(), ".lens");
272600
- var CHATS_DIR = path16.join(LENS_DIR, "chats");
272687
+ import path17 from "path";
272688
+ import os7 from "os";
272689
+ var LENS_DIR2 = path17.join(os7.homedir(), ".lens");
272690
+ var CHATS_DIR = path17.join(LENS_DIR2, "chats");
272601
272691
  function ensureChatsDir() {
272602
- if (!existsSync12(CHATS_DIR))
272603
- mkdirSync6(CHATS_DIR, { recursive: true });
272692
+ if (!existsSync13(CHATS_DIR))
272693
+ mkdirSync7(CHATS_DIR, { recursive: true });
272604
272694
  }
272605
272695
  function chatFilePath(name) {
272606
272696
  const safe = name.replace(/[^a-z0-9_-]/gi, "-").toLowerCase();
272607
- return path16.join(CHATS_DIR, `${safe}.json`);
272697
+ return path17.join(CHATS_DIR, `${safe}.json`);
272608
272698
  }
272609
272699
  function saveChat(name, repoPath, messages) {
272610
272700
  ensureChatsDir();
@@ -272615,14 +272705,14 @@ function saveChat(name, repoPath, messages) {
272615
272705
  savedAt: new Date().toISOString(),
272616
272706
  userMessageCount: messages.filter((m) => m.role === "user").length
272617
272707
  };
272618
- writeFileSync8(chatFilePath(name), JSON.stringify(data, null, 2), "utf-8");
272708
+ writeFileSync9(chatFilePath(name), JSON.stringify(data, null, 2), "utf-8");
272619
272709
  }
272620
272710
  function loadChat(name) {
272621
272711
  const filePath = chatFilePath(name);
272622
- if (!existsSync12(filePath))
272712
+ if (!existsSync13(filePath))
272623
272713
  return null;
272624
272714
  try {
272625
- return JSON.parse(readFileSync11(filePath, "utf-8"));
272715
+ return JSON.parse(readFileSync12(filePath, "utf-8"));
272626
272716
  } catch {
272627
272717
  return null;
272628
272718
  }
@@ -272633,7 +272723,7 @@ function listChats(repoPath) {
272633
272723
  const chats = [];
272634
272724
  for (const file of files) {
272635
272725
  try {
272636
- const data = JSON.parse(readFileSync11(path16.join(CHATS_DIR, file), "utf-8"));
272726
+ const data = JSON.parse(readFileSync12(path17.join(CHATS_DIR, file), "utf-8"));
272637
272727
  if (!repoPath || data.repoPath === repoPath)
272638
272728
  chats.push(data);
272639
272729
  } catch {}
@@ -272642,7 +272732,7 @@ function listChats(repoPath) {
272642
272732
  }
272643
272733
  function deleteChat(name) {
272644
272734
  const filePath = chatFilePath(name);
272645
- if (!existsSync12(filePath))
272735
+ if (!existsSync13(filePath))
272646
272736
  return false;
272647
272737
  try {
272648
272738
  unlinkSync(filePath);
@@ -274643,342 +274733,63 @@ function TimelineRunner({
274643
274733
  }, undefined, true, undefined, this);
274644
274734
  }
274645
274735
 
274646
- // src/utils/memory.ts
274647
- import { existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "fs";
274648
- import path17 from "path";
274649
- import os7 from "os";
274650
- var LENS_DIR2 = path17.join(os7.homedir(), ".lens");
274651
- var MEMORY_PATH = path17.join(LENS_DIR2, "memory.json");
274652
- function loadMemoryFile() {
274653
- if (!existsSync13(MEMORY_PATH))
274654
- return { entries: [], memories: [] };
274655
- try {
274656
- const data = JSON.parse(readFileSync12(MEMORY_PATH, "utf-8"));
274657
- return {
274658
- entries: data.entries ?? [],
274659
- memories: data.memories ?? []
274736
+ // src/components/chat/hooks/useChat.ts
274737
+ var import_react48 = __toESM(require_react(), 1);
274738
+ var import_react49 = __toESM(require_react(), 1);
274739
+ function useChat(repoPath) {
274740
+ const [stage, setStage] = import_react48.useState({ type: "picking-provider" });
274741
+ const [committed, setCommitted] = import_react48.useState([]);
274742
+ const [provider, setProvider] = import_react48.useState(null);
274743
+ const [systemPrompt, setSystemPrompt] = import_react48.useState("");
274744
+ const [pendingMsgIndex, setPendingMsgIndex] = import_react48.useState(null);
274745
+ const [allMessages, setAllMessages] = import_react48.useState([]);
274746
+ const [clonedUrls, setClonedUrls] = import_react48.useState(new Set);
274747
+ const [showTimeline, setShowTimeline] = import_react48.useState(false);
274748
+ const [showReview, setShowReview] = import_react48.useState(false);
274749
+ const [autoApprove, setAutoApprove] = import_react48.useState(false);
274750
+ const [forceApprove, setForceApprove] = import_react48.useState(false);
274751
+ const [showForceWarning, setShowForceWarning] = import_react48.useState(false);
274752
+ const [chatName, setChatName] = import_react48.useState(null);
274753
+ const [recentChats, setRecentChats] = import_react48.useState([]);
274754
+ const chatNameRef = import_react48.useRef(null);
274755
+ const providerRef = import_react48.useRef(null);
274756
+ const systemPromptRef = import_react48.useRef("");
274757
+ const abortControllerRef = import_react48.useRef(null);
274758
+ const toolResultCache = import_react48.useRef(new Map);
274759
+ const batchApprovedRef = import_react48.useRef(false);
274760
+ const updateChatName = (name) => {
274761
+ chatNameRef.current = name;
274762
+ setChatName(name);
274763
+ };
274764
+ import_react49.default.useEffect(() => {
274765
+ providerRef.current = provider;
274766
+ }, [provider]);
274767
+ import_react49.default.useEffect(() => {
274768
+ systemPromptRef.current = systemPrompt;
274769
+ }, [systemPrompt]);
274770
+ import_react49.default.useEffect(() => {
274771
+ const chats = listChats(repoPath);
274772
+ setRecentChats(chats.slice(0, 10).map((c) => c.name));
274773
+ }, [repoPath]);
274774
+ import_react49.default.useEffect(() => {
274775
+ if (chatNameRef.current && allMessages.length > 1) {
274776
+ saveChat(chatNameRef.current, repoPath, allMessages);
274777
+ }
274778
+ }, [allMessages]);
274779
+ const handleError = (currentAll) => (err) => {
274780
+ batchApprovedRef.current = false;
274781
+ if (err instanceof Error && err.name === "AbortError") {
274782
+ setStage({ type: "idle" });
274783
+ return;
274784
+ }
274785
+ const errMsg = {
274786
+ role: "assistant",
274787
+ content: `Error: ${err instanceof Error ? err.message : "Something went wrong"}`,
274788
+ type: "text"
274660
274789
  };
274661
- } catch {
274662
- return { entries: [], memories: [] };
274663
- }
274664
- }
274665
- function saveMemoryFile(m) {
274666
- if (!existsSync13(LENS_DIR2))
274667
- mkdirSync7(LENS_DIR2, { recursive: true });
274668
- writeFileSync9(MEMORY_PATH, JSON.stringify(m, null, 2), "utf-8");
274669
- }
274670
- function appendMemory(entry) {
274671
- const m = loadMemoryFile();
274672
- m.entries.push({ ...entry, timestamp: new Date().toISOString() });
274673
- if (m.entries.length > 500)
274674
- m.entries = m.entries.slice(-500);
274675
- saveMemoryFile(m);
274676
- }
274677
- function buildMemorySummary(repoPath) {
274678
- const m = loadMemoryFile();
274679
- const relevant = m.entries.slice(-50);
274680
- const memories = m.memories;
274681
- const parts = [];
274682
- if (memories.length > 0) {
274683
- parts.push(`## MEMORIES ABOUT THIS REPO
274684
-
274685
- ${memories.map((mem) => `- [${mem.id}] ${mem.content}`).join(`
274686
- `)}`);
274687
- }
274688
- if (relevant.length > 0) {
274689
- const lines = relevant.map((e) => {
274690
- const ts = new Date(e.timestamp).toLocaleString();
274691
- return `[${ts}] ${e.kind}: ${e.detail} — ${e.summary}`;
274692
- });
274693
- parts.push(`## WHAT YOU HAVE ALREADY DONE IN THIS REPO
274694
-
274695
- The following actions have already been completed. Do NOT repeat them unless the user explicitly asks you to redo something:
274696
-
274697
- ${lines.join(`
274698
- `)}`);
274699
- }
274700
- return parts.join(`
274701
-
274702
- `);
274703
- }
274704
- function clearRepoMemory(repoPath) {
274705
- const m = loadMemoryFile();
274706
- m.entries = m.entries = [];
274707
- m.memories = m.memories = [];
274708
- saveMemoryFile(m);
274709
- }
274710
- function generateId() {
274711
- return Math.random().toString(36).slice(2, 8);
274712
- }
274713
- function addMemory(content, repoPath) {
274714
- const m = loadMemoryFile();
274715
- const memory = {
274716
- id: generateId(),
274717
- content,
274718
- timestamp: new Date().toISOString()
274719
- };
274720
- m.memories.push(memory);
274721
- saveMemoryFile(m);
274722
- return memory;
274723
- }
274724
- function deleteMemory(id, repoPath) {
274725
- const m = loadMemoryFile();
274726
- const before2 = m.memories.length;
274727
- m.memories = m.memories.filter((mem) => !(mem.id === id));
274728
- if (m.memories.length === before2)
274729
- return false;
274730
- saveMemoryFile(m);
274731
- return true;
274732
- }
274733
- function listMemories(repoPath) {
274734
- return loadMemoryFile().memories;
274735
- }
274736
-
274737
- // src/components/chat/ChatRunner.tsx
274738
- var jsx_dev_runtime22 = __toESM(require_jsx_dev_runtime(), 1);
274739
- var COMMANDS = [
274740
- { cmd: "/timeline", desc: "browse commit history" },
274741
- { cmd: "/clear history", desc: "wipe session memory for this repo" },
274742
- { cmd: "/review", desc: "review current codebase" },
274743
- { cmd: "/auto", desc: "toggle auto-approve for read/search tools" },
274744
- {
274745
- cmd: "/auto --force-all",
274746
- desc: "auto-approve ALL tools including shell and writes (⚠ dangerous)"
274747
- },
274748
- { cmd: "/chat", desc: "chat history commands" },
274749
- { cmd: "/chat list", desc: "list saved chats for this repo" },
274750
- { cmd: "/chat load", desc: "load a saved chat by name" },
274751
- { cmd: "/chat rename", desc: "rename the current chat" },
274752
- { cmd: "/chat delete", desc: "delete a saved chat by name" },
274753
- { cmd: "/memory", desc: "memory commands" },
274754
- { cmd: "/memory list", desc: "list all memories for this repo" },
274755
- { cmd: "/memory add", desc: "add a memory" },
274756
- { cmd: "/memory delete", desc: "delete a memory by id" },
274757
- { cmd: "/memory clear", desc: "clear all memories for this repo" }
274758
- ];
274759
- function CommandPalette({
274760
- query,
274761
- onSelect,
274762
- recentChats
274763
- }) {
274764
- const q = query.toLowerCase();
274765
- const isChatLoad = q.startsWith("/chat load") || q.startsWith("/chat delete");
274766
- const chatFilter = isChatLoad ? q.startsWith("/chat load") ? q.slice("/chat load".length).trim() : q.slice("/chat delete".length).trim() : "";
274767
- const filteredChats = chatFilter ? recentChats.filter((n) => n.toLowerCase().includes(chatFilter)) : recentChats;
274768
- const matches2 = COMMANDS.filter((c) => c.cmd.startsWith(q));
274769
- if (!matches2.length && !isChatLoad)
274770
- return null;
274771
- if (!matches2.length && isChatLoad && filteredChats.length === 0)
274772
- return null;
274773
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
274774
- flexDirection: "column",
274775
- marginBottom: 1,
274776
- marginLeft: 2,
274777
- children: [
274778
- matches2.map((c, i) => {
274779
- const isExact = c.cmd === query;
274780
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
274781
- gap: 2,
274782
- children: [
274783
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
274784
- color: isExact ? ACCENT : "white",
274785
- bold: isExact,
274786
- children: c.cmd
274787
- }, undefined, false, undefined, this),
274788
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
274789
- color: "gray",
274790
- dimColor: true,
274791
- children: c.desc
274792
- }, undefined, false, undefined, this)
274793
- ]
274794
- }, i, true, undefined, this);
274795
- }),
274796
- isChatLoad && filteredChats.length > 0 && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
274797
- flexDirection: "column",
274798
- marginTop: matches2.length ? 1 : 0,
274799
- children: [
274800
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
274801
- color: "gray",
274802
- dimColor: true,
274803
- children: chatFilter ? `matching "${chatFilter}":` : "recent chats:"
274804
- }, undefined, false, undefined, this),
274805
- filteredChats.map((name, i) => /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
274806
- gap: 1,
274807
- marginLeft: 2,
274808
- children: [
274809
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
274810
- color: ACCENT,
274811
- children: "·"
274812
- }, undefined, false, undefined, this),
274813
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
274814
- color: "white",
274815
- children: name
274816
- }, undefined, false, undefined, this)
274817
- ]
274818
- }, i, true, undefined, this))
274819
- ]
274820
- }, undefined, true, undefined, this)
274821
- ]
274822
- }, undefined, true, undefined, this);
274823
- }
274824
- function ForceAllWarning({
274825
- onConfirm
274826
- }) {
274827
- const [input, setInput] = import_react49.useState("");
274828
- return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
274829
- flexDirection: "column",
274830
- marginY: 1,
274831
- gap: 1,
274832
- children: [
274833
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
274834
- gap: 1,
274835
- children: /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
274836
- color: "red",
274837
- bold: true,
274838
- children: "⚠ WARNING"
274839
- }, undefined, false, undefined, this)
274840
- }, undefined, false, undefined, this),
274841
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
274842
- flexDirection: "column",
274843
- marginLeft: 2,
274844
- gap: 1,
274845
- children: [
274846
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
274847
- color: "yellow",
274848
- children: "Force-all mode auto-approves EVERY tool without asking — including:"
274849
- }, undefined, false, undefined, this),
274850
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
274851
- color: "red",
274852
- dimColor: true,
274853
- children: [
274854
- " ",
274855
- "· shell commands (rm, git, npm, anything)"
274856
- ]
274857
- }, undefined, true, undefined, this),
274858
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
274859
- color: "red",
274860
- dimColor: true,
274861
- children: [
274862
- " ",
274863
- "· file writes and deletes"
274864
- ]
274865
- }, undefined, true, undefined, this),
274866
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
274867
- color: "red",
274868
- dimColor: true,
274869
- children: [
274870
- " ",
274871
- "· folder deletes"
274872
- ]
274873
- }, undefined, true, undefined, this),
274874
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
274875
- color: "red",
274876
- dimColor: true,
274877
- children: [
274878
- " ",
274879
- "· external fetches and URL opens"
274880
- ]
274881
- }, undefined, true, undefined, this),
274882
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
274883
- color: "yellow",
274884
- dimColor: true,
274885
- children: "The AI can modify or delete files without any confirmation."
274886
- }, undefined, false, undefined, this),
274887
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
274888
- color: "yellow",
274889
- dimColor: true,
274890
- children: "Only use this in throwaway environments or when you fully trust the task."
274891
- }, undefined, false, undefined, this)
274892
- ]
274893
- }, undefined, true, undefined, this),
274894
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
274895
- gap: 1,
274896
- marginTop: 1,
274897
- children: [
274898
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
274899
- color: "gray",
274900
- children: "Type "
274901
- }, undefined, false, undefined, this),
274902
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
274903
- color: "white",
274904
- bold: true,
274905
- children: "yes"
274906
- }, undefined, false, undefined, this),
274907
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
274908
- color: "gray",
274909
- children: " to enable, or press "
274910
- }, undefined, false, undefined, this),
274911
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
274912
- color: "white",
274913
- bold: true,
274914
- children: "esc"
274915
- }, undefined, false, undefined, this),
274916
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
274917
- color: "gray",
274918
- children: " to cancel: "
274919
- }, undefined, false, undefined, this),
274920
- /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(build_default2, {
274921
- value: input,
274922
- onChange: setInput,
274923
- onSubmit: (v) => onConfirm(v.trim().toLowerCase() === "yes"),
274924
- placeholder: "yes / esc to cancel"
274925
- }, undefined, false, undefined, this)
274926
- ]
274927
- }, undefined, true, undefined, this)
274928
- ]
274929
- }, undefined, true, undefined, this);
274930
- }
274931
- var ChatRunner = ({ repoPath }) => {
274932
- const [stage, setStage] = import_react49.useState({ type: "picking-provider" });
274933
- const [committed, setCommitted] = import_react49.useState([]);
274934
- const [provider, setProvider] = import_react49.useState(null);
274935
- const [systemPrompt, setSystemPrompt] = import_react49.useState("");
274936
- const [inputValue, setInputValue] = import_react49.useState("");
274937
- const [pendingMsgIndex, setPendingMsgIndex] = import_react49.useState(null);
274938
- const [allMessages, setAllMessages] = import_react49.useState([]);
274939
- const [clonedUrls, setClonedUrls] = import_react49.useState(new Set);
274940
- const [showTimeline, setShowTimeline] = import_react49.useState(false);
274941
- const [showReview, setShowReview] = import_react49.useState(false);
274942
- const [autoApprove, setAutoApprove] = import_react49.useState(false);
274943
- const [forceApprove, setForceApprove] = import_react49.useState(false);
274944
- const [showForceWarning, setShowForceWarning] = import_react49.useState(false);
274945
- const [chatName, setChatName] = import_react49.useState(null);
274946
- const chatNameRef = import_react49.useRef(null);
274947
- const [recentChats, setRecentChats] = import_react49.useState([]);
274948
- const inputHistoryRef = import_react49.useRef([]);
274949
- const historyIndexRef = import_react49.useRef(-1);
274950
- const [inputKey, setInputKey] = import_react49.useState(0);
274951
- const updateChatName = (name) => {
274952
- chatNameRef.current = name;
274953
- setChatName(name);
274954
- };
274955
- const abortControllerRef = import_react49.useRef(null);
274956
- const toolResultCache = import_react49.useRef(new Map);
274957
- const batchApprovedRef = import_react49.useRef(false);
274958
- const thinkingPhrase = useThinkingPhrase(stage.type === "thinking");
274959
- import_react48.default.useEffect(() => {
274960
- const chats = listChats(repoPath);
274961
- setRecentChats(chats.slice(0, 10).map((c) => c.name));
274962
- }, [repoPath]);
274963
- import_react48.default.useEffect(() => {
274964
- if (chatNameRef.current && allMessages.length > 1) {
274965
- saveChat(chatNameRef.current, repoPath, allMessages);
274966
- }
274967
- }, [allMessages]);
274968
- const handleError = (currentAll) => (err) => {
274969
- batchApprovedRef.current = false;
274970
- if (err instanceof Error && err.name === "AbortError") {
274971
- setStage({ type: "idle" });
274972
- return;
274973
- }
274974
- const errMsg = {
274975
- role: "assistant",
274976
- content: `Error: ${err instanceof Error ? err.message : "Something went wrong"}`,
274977
- type: "text"
274978
- };
274979
- setAllMessages([...currentAll, errMsg]);
274980
- setCommitted((prev) => [...prev, errMsg]);
274981
- setStage({ type: "idle" });
274790
+ setAllMessages([...currentAll, errMsg]);
274791
+ setCommitted((prev) => [...prev, errMsg]);
274792
+ setStage({ type: "idle" });
274982
274793
  };
274983
274794
  const TOOL_TAG_NAMES = [
274984
274795
  "shell",
@@ -275135,6 +274946,13 @@ var ChatRunner = ({ repoPath }) => {
275135
274946
  if (approved && remainder) {
275136
274947
  batchApprovedRef.current = true;
275137
274948
  }
274949
+ const currentProvider = providerRef.current;
274950
+ const currentSystemPrompt = systemPromptRef.current;
274951
+ if (!currentProvider) {
274952
+ batchApprovedRef.current = false;
274953
+ setStage({ type: "idle" });
274954
+ return;
274955
+ }
275138
274956
  let result2 = "(denied by user)";
275139
274957
  if (approved) {
275140
274958
  const cacheKey = isSafe ? `${parsed.toolName}:${parsed.rawInput}` : null;
@@ -275186,7 +275004,22 @@ var ChatRunner = ({ repoPath }) => {
275186
275004
  const nextAbort = new AbortController;
275187
275005
  abortControllerRef.current = nextAbort;
275188
275006
  setStage({ type: "thinking" });
275189
- callChat(provider, systemPrompt, withTool, nextAbort.signal).then((r) => processResponse(r, withTool, nextAbort.signal)).catch(handleError(withTool));
275007
+ callChat(currentProvider, currentSystemPrompt, withTool, nextAbort.signal).then((r) => {
275008
+ if (nextAbort.signal.aborted)
275009
+ return;
275010
+ if (!r.trim()) {
275011
+ const nudged = [
275012
+ ...withTool,
275013
+ { role: "user", content: "Please continue.", type: "text" }
275014
+ ];
275015
+ return callChat(currentProvider, currentSystemPrompt, nudged, nextAbort.signal);
275016
+ }
275017
+ return r;
275018
+ }).then((r) => {
275019
+ if (nextAbort.signal.aborted)
275020
+ return;
275021
+ processResponse(r ?? "", withTool, nextAbort.signal);
275022
+ }).catch(handleError(withTool));
275190
275023
  };
275191
275024
  if (forceApprove || autoApprove && isSafe || batchApprovedRef.current) {
275192
275025
  executeAndContinue(true);
@@ -275205,291 +275038,327 @@ var ChatRunner = ({ repoPath }) => {
275205
275038
  resolve: executeAndContinue
275206
275039
  });
275207
275040
  };
275208
- const sendMessage = (text) => {
275209
- if (!provider)
275210
- return;
275211
- if (text.trim().toLowerCase() === "/timeline") {
275212
- setShowTimeline(true);
275213
- return;
275214
- }
275215
- if (text.trim().toLowerCase() === "/review") {
275216
- setShowReview(true);
275217
- return;
275218
- }
275219
- if (text.trim().toLowerCase() === "/auto --force-all") {
275220
- if (forceApprove) {
275221
- setForceApprove(false);
275222
- setAutoApprove(false);
275223
- const msg = {
275224
- role: "assistant",
275225
- content: "Force-all mode OFF — tools will ask for permission again.",
275226
- type: "text"
275227
- };
275228
- setCommitted((prev) => [...prev, msg]);
275229
- setAllMessages((prev) => [...prev, msg]);
275230
- } else {
275231
- setShowForceWarning(true);
275232
- }
275233
- return;
275041
+ const sendMessage = (text, currentProvider, currentSystemPrompt, currentAllMessages) => {
275042
+ const userMsg = { role: "user", content: text, type: "text" };
275043
+ const nextAll = [...currentAllMessages, userMsg];
275044
+ setCommitted((prev) => [...prev, userMsg]);
275045
+ setAllMessages(nextAll);
275046
+ batchApprovedRef.current = false;
275047
+ if (!chatName) {
275048
+ const name = getChatNameSuggestions(nextAll)[0] ?? `chat-${new Date().toISOString().slice(0, 10)}`;
275049
+ updateChatName(name);
275050
+ setRecentChats((prev) => [name, ...prev.filter((n) => n !== name)].slice(0, 10));
275051
+ saveChat(name, repoPath, nextAll);
275234
275052
  }
275235
- if (text.trim().toLowerCase() === "/auto") {
275236
- if (forceApprove) {
275237
- setForceApprove(false);
275238
- setAutoApprove(true);
275239
- const msg2 = {
275240
- role: "assistant",
275241
- content: "Force-all mode OFF — switched to normal auto-approve (safe tools only).",
275242
- type: "text"
275243
- };
275244
- setCommitted((prev) => [...prev, msg2]);
275245
- setAllMessages((prev) => [...prev, msg2]);
275246
- return;
275247
- }
275248
- const next = !autoApprove;
275249
- setAutoApprove(next);
275250
- const msg = {
275053
+ const abort = new AbortController;
275054
+ abortControllerRef.current = abort;
275055
+ setStage({ type: "thinking" });
275056
+ callChat(currentProvider, currentSystemPrompt, nextAll, abort.signal).then((raw) => processResponse(raw, nextAll, abort.signal)).catch(handleError(nextAll));
275057
+ };
275058
+ const handleProviderDone = (p) => {
275059
+ setProvider(p);
275060
+ providerRef.current = p;
275061
+ setStage({ type: "loading" });
275062
+ fetchFileTree(repoPath).catch(() => walkDir(repoPath)).then((fileTree) => {
275063
+ const importantFiles = readImportantFiles(repoPath, fileTree);
275064
+ const historySummary = buildMemorySummary(repoPath);
275065
+ const lensFile = readLensFile(repoPath);
275066
+ const lensContext = lensFile ? `
275067
+
275068
+ ## LENS.md (previous analysis)
275069
+ ${lensFile.overview}
275070
+
275071
+ Important folders: ${lensFile.importantFolders.join(", ")}
275072
+ Suggestions: ${lensFile.suggestions.slice(0, 3).join("; ")}` : "";
275073
+ const toolsSection = registry.buildSystemPromptSection();
275074
+ const prompt = buildSystemPrompt(importantFiles, historySummary, toolsSection) + lensContext;
275075
+ setSystemPrompt(prompt);
275076
+ systemPromptRef.current = prompt;
275077
+ const greeting = {
275251
275078
  role: "assistant",
275252
- content: next ? "Auto-approve ON — safe tools (read, search, fetch) will run without asking." : "Auto-approve OFF — all tools will ask for permission.",
275079
+ content: `Welcome to Lens
275080
+ Codebase loaded — ${importantFiles.length} files indexed.${historySummary ? `
275081
+
275082
+ I have memory of previous actions in this repo.` : ""}${lensFile ? `
275083
+
275084
+ Found LENS.md — I have context from a previous analysis of this repo.` : ""}
275085
+ Ask me anything, tell me what to build, share a URL, or ask me to read/write files.
275086
+
275087
+ Tip: type /timeline to browse commit history.
275088
+ Tip: ⭐ Star Lens on GitHub — github.com/ridit-jangra/Lens`,
275253
275089
  type: "text"
275254
275090
  };
275255
- setCommitted((prev) => [...prev, msg]);
275256
- setAllMessages((prev) => [...prev, msg]);
275257
- return;
275091
+ setCommitted([greeting]);
275092
+ setAllMessages([greeting]);
275093
+ setStage({ type: "idle" });
275094
+ }).catch(() => setStage({ type: "idle" }));
275095
+ };
275096
+ const abortThinking = () => {
275097
+ abortControllerRef.current?.abort();
275098
+ abortControllerRef.current = null;
275099
+ batchApprovedRef.current = false;
275100
+ setStage({ type: "idle" });
275101
+ };
275102
+ const applyPatchesAndContinue = (patches) => {
275103
+ try {
275104
+ applyPatches2(repoPath, patches);
275105
+ appendMemory({
275106
+ kind: "code-applied",
275107
+ detail: patches.map((p) => p.path).join(", "),
275108
+ summary: `Applied changes to ${patches.length} file(s)`
275109
+ });
275110
+ } catch {}
275111
+ };
275112
+ const skipPatches = (patches) => {
275113
+ appendMemory({
275114
+ kind: "code-skipped",
275115
+ detail: patches.map((p) => p.path).join(", "),
275116
+ summary: `Skipped changes to ${patches.length} file(s)`
275117
+ });
275118
+ };
275119
+ return {
275120
+ stage,
275121
+ setStage,
275122
+ committed,
275123
+ setCommitted,
275124
+ provider,
275125
+ setProvider,
275126
+ systemPrompt,
275127
+ allMessages,
275128
+ setAllMessages,
275129
+ clonedUrls,
275130
+ setClonedUrls,
275131
+ showTimeline,
275132
+ setShowTimeline,
275133
+ showReview,
275134
+ setShowReview,
275135
+ autoApprove,
275136
+ setAutoApprove,
275137
+ forceApprove,
275138
+ setForceApprove,
275139
+ showForceWarning,
275140
+ setShowForceWarning,
275141
+ chatName,
275142
+ setChatName,
275143
+ recentChats,
275144
+ setRecentChats,
275145
+ pendingMsgIndex,
275146
+ setPendingMsgIndex,
275147
+ chatNameRef,
275148
+ providerRef,
275149
+ batchApprovedRef,
275150
+ updateChatName,
275151
+ sendMessage,
275152
+ handleProviderDone,
275153
+ abortThinking,
275154
+ applyPatchesAndContinue,
275155
+ skipPatches,
275156
+ processResponse,
275157
+ handleError
275158
+ };
275159
+ }
275160
+
275161
+ // src/components/chat/hooks/useChatInput.ts
275162
+ var import_react50 = __toESM(require_react(), 1);
275163
+
275164
+ // src/components/chat/hooks/useCommandHandlers.ts
275165
+ var COMMANDS = [
275166
+ { cmd: "/timeline", desc: "browse commit history" },
275167
+ { cmd: "/clear history", desc: "wipe session memory for this repo" },
275168
+ { cmd: "/review", desc: "review current codebase" },
275169
+ { cmd: "/auto", desc: "toggle auto-approve for read/search tools" },
275170
+ {
275171
+ cmd: "/auto --force-all",
275172
+ desc: "auto-approve ALL tools including shell and writes (⚠ dangerous)"
275173
+ },
275174
+ { cmd: "/chat", desc: "chat history commands" },
275175
+ { cmd: "/chat list", desc: "list saved chats for this repo" },
275176
+ { cmd: "/chat load", desc: "load a saved chat by name" },
275177
+ { cmd: "/chat rename", desc: "rename the current chat" },
275178
+ { cmd: "/chat delete", desc: "delete a saved chat by name" },
275179
+ { cmd: "/memory", desc: "memory commands" },
275180
+ { cmd: "/memory list", desc: "list all memories for this repo" },
275181
+ { cmd: "/memory add", desc: "add a memory" },
275182
+ { cmd: "/memory delete", desc: "delete a memory by id" },
275183
+ { cmd: "/memory clear", desc: "clear all memories for this repo" }
275184
+ ];
275185
+ function pushMsg(msg, setCommitted, setAllMessages) {
275186
+ setCommitted((prev) => [...prev, msg]);
275187
+ setAllMessages((prev) => [...prev, msg]);
275188
+ }
275189
+ function makeMsg(content) {
275190
+ return { role: "assistant", content, type: "text" };
275191
+ }
275192
+ function handleCommand(text, ctx) {
275193
+ const t = text.trim().toLowerCase();
275194
+ if (t === "/timeline") {
275195
+ ctx.setShowTimeline(true);
275196
+ return true;
275197
+ }
275198
+ if (t === "/review") {
275199
+ ctx.setShowReview(true);
275200
+ return true;
275201
+ }
275202
+ if (t === "/auto --force-all") {
275203
+ if (ctx.forceApprove) {
275204
+ ctx.setForceApprove(false);
275205
+ ctx.setAutoApprove(false);
275206
+ pushMsg(makeMsg("Force-all mode OFF — tools will ask for permission again."), ctx.setCommitted, ctx.setAllMessages);
275207
+ } else {
275208
+ ctx.setShowForceWarning(true);
275258
275209
  }
275259
- if (text.trim().toLowerCase() === "/clear history") {
275260
- clearRepoMemory(repoPath);
275261
- const msg = {
275262
- role: "assistant",
275263
- content: "History cleared for this repo.",
275264
- type: "text"
275265
- };
275266
- setCommitted((prev) => [...prev, msg]);
275267
- setAllMessages((prev) => [...prev, msg]);
275268
- return;
275210
+ return true;
275211
+ }
275212
+ if (t === "/auto") {
275213
+ if (ctx.forceApprove) {
275214
+ ctx.setForceApprove(false);
275215
+ ctx.setAutoApprove(true);
275216
+ pushMsg(makeMsg("Force-all mode OFF — switched to normal auto-approve (safe tools only)."), ctx.setCommitted, ctx.setAllMessages);
275217
+ return true;
275269
275218
  }
275270
- if (text.trim().toLowerCase() === "/chat") {
275271
- const msg = {
275272
- role: "assistant",
275273
- content: "Chat commands: `/chat list` · `/chat load <n>` · `/chat rename <n>` · `/chat delete <n>`",
275274
- type: "text"
275275
- };
275276
- setCommitted((prev) => [...prev, msg]);
275277
- setAllMessages((prev) => [...prev, msg]);
275278
- return;
275219
+ const next = !ctx.autoApprove;
275220
+ ctx.setAutoApprove(next);
275221
+ pushMsg(makeMsg(next ? "Auto-approve ON — safe tools (read, search, fetch) will run without asking." : "Auto-approve OFF — all tools will ask for permission."), ctx.setCommitted, ctx.setAllMessages);
275222
+ return true;
275223
+ }
275224
+ if (t === "/clear history") {
275225
+ clearRepoMemory(ctx.repoPath);
275226
+ pushMsg(makeMsg("History cleared for this repo."), ctx.setCommitted, ctx.setAllMessages);
275227
+ return true;
275228
+ }
275229
+ if (t === "/chat") {
275230
+ pushMsg(makeMsg("Chat commands: `/chat list` · `/chat load <n>` · `/chat rename <n>` · `/chat delete <n>`"), ctx.setCommitted, ctx.setAllMessages);
275231
+ return true;
275232
+ }
275233
+ if (t.startsWith("/chat rename")) {
275234
+ const parts = text.trim().split(/\s+/);
275235
+ const newName = parts.slice(2).join("-");
275236
+ if (!newName) {
275237
+ pushMsg(makeMsg("Usage: `/chat rename <new-name>`"), ctx.setCommitted, ctx.setAllMessages);
275238
+ return true;
275279
275239
  }
275280
- if (text.trim().toLowerCase().startsWith("/chat rename")) {
275281
- const parts = text.trim().split(/\s+/);
275282
- const newName = parts.slice(2).join("-");
275283
- if (!newName) {
275284
- const msg2 = {
275285
- role: "assistant",
275286
- content: "Usage: `/chat rename <new-name>`",
275287
- type: "text"
275288
- };
275289
- setCommitted((prev) => [...prev, msg2]);
275290
- setAllMessages((prev) => [...prev, msg2]);
275291
- return;
275292
- }
275293
- const oldName = chatNameRef.current;
275294
- if (oldName)
275295
- deleteChat(oldName);
275296
- updateChatName(newName);
275297
- saveChat(newName, repoPath, allMessages);
275298
- setRecentChats((prev) => [newName, ...prev.filter((n) => n !== newName && n !== oldName)].slice(0, 10));
275299
- const msg = {
275300
- role: "assistant",
275301
- content: `Chat renamed to **${newName}**.`,
275302
- type: "text"
275303
- };
275304
- setCommitted((prev) => [...prev, msg]);
275305
- setAllMessages((prev) => [...prev, msg]);
275306
- return;
275240
+ const oldName = ctx.chatNameRef.current;
275241
+ if (oldName)
275242
+ deleteChat(oldName);
275243
+ ctx.updateChatName(newName);
275244
+ saveChat(newName, ctx.repoPath, ctx.allMessages);
275245
+ ctx.setRecentChats((prev) => [newName, ...prev.filter((n) => n !== newName && n !== oldName)].slice(0, 10));
275246
+ pushMsg(makeMsg(`Chat renamed to **${newName}**.`), ctx.setCommitted, ctx.setAllMessages);
275247
+ return true;
275248
+ }
275249
+ if (t.startsWith("/chat delete")) {
275250
+ const parts = text.trim().split(/\s+/);
275251
+ const name = parts.slice(2).join("-");
275252
+ if (!name) {
275253
+ pushMsg(makeMsg("Usage: `/chat delete <n>`"), ctx.setCommitted, ctx.setAllMessages);
275254
+ return true;
275307
275255
  }
275308
- if (text.trim().toLowerCase().startsWith("/chat delete")) {
275309
- const parts = text.trim().split(/\s+/);
275310
- const name = parts.slice(2).join("-");
275311
- if (!name) {
275312
- const msg2 = {
275313
- role: "assistant",
275314
- content: "Usage: `/chat delete <n>`",
275315
- type: "text"
275316
- };
275317
- setCommitted((prev) => [...prev, msg2]);
275318
- setAllMessages((prev) => [...prev, msg2]);
275319
- return;
275320
- }
275321
- const deleted = deleteChat(name);
275322
- if (!deleted) {
275323
- const msg2 = {
275324
- role: "assistant",
275325
- content: `Chat **${name}** not found.`,
275326
- type: "text"
275327
- };
275328
- setCommitted((prev) => [...prev, msg2]);
275329
- setAllMessages((prev) => [...prev, msg2]);
275330
- return;
275331
- }
275332
- if (chatNameRef.current === name) {
275333
- chatNameRef.current = null;
275334
- setChatName(null);
275335
- }
275336
- setRecentChats((prev) => prev.filter((n) => n !== name));
275337
- const msg = {
275338
- role: "assistant",
275339
- content: `Chat **${name}** deleted.`,
275340
- type: "text"
275341
- };
275342
- setCommitted((prev) => [...prev, msg]);
275343
- setAllMessages((prev) => [...prev, msg]);
275344
- return;
275256
+ const deleted = deleteChat(name);
275257
+ if (!deleted) {
275258
+ pushMsg(makeMsg(`Chat **${name}** not found.`), ctx.setCommitted, ctx.setAllMessages);
275259
+ return true;
275345
275260
  }
275346
- if (text.trim().toLowerCase() === "/chat list") {
275347
- const chats = listChats(repoPath);
275348
- const content = chats.length === 0 ? "No saved chats for this repo yet." : `Saved chats:
275261
+ if (ctx.chatNameRef.current === name) {
275262
+ ctx.chatNameRef.current = null;
275263
+ ctx.updateChatName("");
275264
+ }
275265
+ ctx.setRecentChats((prev) => prev.filter((n) => n !== name));
275266
+ pushMsg(makeMsg(`Chat **${name}** deleted.`), ctx.setCommitted, ctx.setAllMessages);
275267
+ return true;
275268
+ }
275269
+ if (t === "/chat list") {
275270
+ const chats = listChats(ctx.repoPath);
275271
+ const content = chats.length === 0 ? "No saved chats for this repo yet." : `Saved chats:
275349
275272
 
275350
275273
  ${chats.map((c) => `- **${c.name}** · ${c.userMessageCount} messages · ${new Date(c.savedAt).toLocaleString()}`).join(`
275351
275274
  `)}`;
275352
- const msg = { role: "assistant", content, type: "text" };
275353
- setCommitted((prev) => [...prev, msg]);
275354
- setAllMessages((prev) => [...prev, msg]);
275355
- return;
275356
- }
275357
- if (text.trim().toLowerCase().startsWith("/chat load")) {
275358
- const parts = text.trim().split(/\s+/);
275359
- const name = parts.slice(2).join("-");
275360
- if (!name) {
275361
- const chats = listChats(repoPath);
275362
- const content = chats.length === 0 ? "No saved chats found." : `Specify a chat name. Recent chats:
275275
+ pushMsg(makeMsg(content), ctx.setCommitted, ctx.setAllMessages);
275276
+ return true;
275277
+ }
275278
+ if (t.startsWith("/chat load")) {
275279
+ const parts = text.trim().split(/\s+/);
275280
+ const name = parts.slice(2).join("-");
275281
+ if (!name) {
275282
+ const chats = listChats(ctx.repoPath);
275283
+ const content = chats.length === 0 ? "No saved chats found." : `Specify a chat name. Recent chats:
275363
275284
 
275364
275285
  ${chats.slice(0, 10).map((c) => `- **${c.name}**`).join(`
275365
275286
  `)}`;
275366
- const msg = { role: "assistant", content, type: "text" };
275367
- setCommitted((prev) => [...prev, msg]);
275368
- setAllMessages((prev) => [...prev, msg]);
275369
- return;
275370
- }
275371
- const saved = loadChat(name);
275372
- if (!saved) {
275373
- const msg = {
275374
- role: "assistant",
275375
- content: `Chat **${name}** not found. Use \`/chat list\` to see saved chats.`,
275376
- type: "text"
275377
- };
275378
- setCommitted((prev) => [...prev, msg]);
275379
- setAllMessages((prev) => [...prev, msg]);
275380
- return;
275381
- }
275382
- updateChatName(name);
275383
- setAllMessages(saved.messages);
275384
- setCommitted(saved.messages);
275385
- const notice = {
275386
- role: "assistant",
275387
- content: `Loaded chat **${name}** · ${saved.userMessageCount} messages · saved ${new Date(saved.savedAt).toLocaleString()}`,
275388
- type: "text"
275389
- };
275390
- setCommitted((prev) => [...prev, notice]);
275391
- setAllMessages((prev) => [...prev, notice]);
275392
- return;
275287
+ pushMsg(makeMsg(content), ctx.setCommitted, ctx.setAllMessages);
275288
+ return true;
275289
+ }
275290
+ const saved = loadChat(name);
275291
+ if (!saved) {
275292
+ pushMsg(makeMsg(`Chat **${name}** not found. Use \`/chat list\` to see saved chats.`), ctx.setCommitted, ctx.setAllMessages);
275293
+ return true;
275393
275294
  }
275394
- if (text.trim().toLowerCase() === "/memory list" || text.trim().toLowerCase() === "/memory") {
275395
- const mems = listMemories(repoPath);
275396
- const content = mems.length === 0 ? "No memories stored for this repo yet." : `Memories for this repo:
275295
+ ctx.updateChatName(name);
275296
+ ctx.setAllMessages(() => saved.messages);
275297
+ ctx.setCommitted(() => saved.messages);
275298
+ const notice = makeMsg(`Loaded chat **${name}** · ${saved.userMessageCount} messages · saved ${new Date(saved.savedAt).toLocaleString()}`);
275299
+ ctx.setCommitted((prev) => [...prev, notice]);
275300
+ ctx.setAllMessages((prev) => [...prev, notice]);
275301
+ return true;
275302
+ }
275303
+ if (t === "/memory list" || t === "/memory") {
275304
+ const mems = listMemories(ctx.repoPath);
275305
+ const content = mems.length === 0 ? "No memories stored for this repo yet." : `Memories for this repo:
275397
275306
 
275398
275307
  ${mems.map((m) => `- [${m.id}] ${m.content}`).join(`
275399
275308
  `)}`;
275400
- const msg = { role: "assistant", content, type: "text" };
275401
- setCommitted((prev) => [...prev, msg]);
275402
- setAllMessages((prev) => [...prev, msg]);
275403
- return;
275404
- }
275405
- if (text.trim().toLowerCase().startsWith("/memory add")) {
275406
- const content = text.trim().slice("/memory add".length).trim();
275407
- if (!content) {
275408
- const msg2 = {
275409
- role: "assistant",
275410
- content: "Usage: `/memory add <content>`",
275411
- type: "text"
275412
- };
275413
- setCommitted((prev) => [...prev, msg2]);
275414
- setAllMessages((prev) => [...prev, msg2]);
275415
- return;
275416
- }
275417
- const mem = addMemory(content, repoPath);
275418
- const msg = {
275419
- role: "assistant",
275420
- content: `Memory saved **[${mem.id}]**: ${mem.content}`,
275421
- type: "text"
275422
- };
275423
- setCommitted((prev) => [...prev, msg]);
275424
- setAllMessages((prev) => [...prev, msg]);
275425
- return;
275426
- }
275427
- if (text.trim().toLowerCase().startsWith("/memory delete")) {
275428
- const id = text.trim().split(/\s+/)[2];
275429
- if (!id) {
275430
- const msg2 = {
275431
- role: "assistant",
275432
- content: "Usage: `/memory delete <id>`",
275433
- type: "text"
275434
- };
275435
- setCommitted((prev) => [...prev, msg2]);
275436
- setAllMessages((prev) => [...prev, msg2]);
275437
- return;
275438
- }
275439
- const deleted = deleteMemory(id, repoPath);
275440
- const msg = {
275441
- role: "assistant",
275442
- content: deleted ? `Memory **[${id}]** deleted.` : `Memory **[${id}]** not found.`,
275443
- type: "text"
275444
- };
275445
- setCommitted((prev) => [...prev, msg]);
275446
- setAllMessages((prev) => [...prev, msg]);
275447
- return;
275309
+ pushMsg(makeMsg(content), ctx.setCommitted, ctx.setAllMessages);
275310
+ return true;
275311
+ }
275312
+ if (t.startsWith("/memory add")) {
275313
+ const content = text.trim().slice("/memory add".length).trim();
275314
+ if (!content) {
275315
+ pushMsg(makeMsg("Usage: `/memory add <content>`"), ctx.setCommitted, ctx.setAllMessages);
275316
+ return true;
275448
275317
  }
275449
- if (text.trim().toLowerCase() === "/memory clear") {
275450
- clearRepoMemory(repoPath);
275451
- const msg = {
275452
- role: "assistant",
275453
- content: "All memories cleared for this repo.",
275454
- type: "text"
275455
- };
275456
- setCommitted((prev) => [...prev, msg]);
275457
- setAllMessages((prev) => [...prev, msg]);
275458
- return;
275318
+ const mem = addMemory(content, ctx.repoPath);
275319
+ pushMsg(makeMsg(`Memory saved **[${mem.id}]**: ${mem.content}`), ctx.setCommitted, ctx.setAllMessages);
275320
+ return true;
275321
+ }
275322
+ if (t.startsWith("/memory delete")) {
275323
+ const id = text.trim().split(/\s+/)[2];
275324
+ if (!id) {
275325
+ pushMsg(makeMsg("Usage: `/memory delete <id>`"), ctx.setCommitted, ctx.setAllMessages);
275326
+ return true;
275459
275327
  }
275460
- const userMsg = { role: "user", content: text, type: "text" };
275461
- const nextAll = [...allMessages, userMsg];
275462
- setCommitted((prev) => [...prev, userMsg]);
275463
- setAllMessages(nextAll);
275464
- batchApprovedRef.current = false;
275328
+ const deleted = deleteMemory(id, ctx.repoPath);
275329
+ pushMsg(makeMsg(deleted ? `Memory **[${id}]** deleted.` : `Memory **[${id}]** not found.`), ctx.setCommitted, ctx.setAllMessages);
275330
+ return true;
275331
+ }
275332
+ if (t === "/memory clear") {
275333
+ clearRepoMemory(ctx.repoPath);
275334
+ pushMsg(makeMsg("All memories cleared for this repo."), ctx.setCommitted, ctx.setAllMessages);
275335
+ return true;
275336
+ }
275337
+ return false;
275338
+ }
275339
+
275340
+ // src/components/chat/hooks/useChatInput.ts
275341
+ function useChatInput(stage, showTimeline, showForceWarning, onAbortThinking, onStageKeyInput) {
275342
+ const [inputValue, setInputValue] = import_react50.useState("");
275343
+ const [inputKey, setInputKey] = import_react50.useState(0);
275344
+ const inputHistoryRef = import_react50.useRef([]);
275345
+ const historyIndexRef = import_react50.useRef(-1);
275346
+ const pushHistory = (text) => {
275465
275347
  inputHistoryRef.current = [
275466
275348
  text,
275467
275349
  ...inputHistoryRef.current.filter((m) => m !== text)
275468
275350
  ].slice(0, 50);
275469
275351
  historyIndexRef.current = -1;
275470
- if (!chatName) {
275471
- const name = getChatNameSuggestions(nextAll)[0] ?? `chat-${new Date().toISOString().slice(0, 10)}`;
275472
- updateChatName(name);
275473
- setRecentChats((prev) => [name, ...prev.filter((n) => n !== name)].slice(0, 10));
275474
- saveChat(name, repoPath, nextAll);
275475
- }
275476
- const abort = new AbortController;
275477
- abortControllerRef.current = abort;
275478
- setStage({ type: "thinking" });
275479
- callChat(provider, systemPrompt, nextAll, abort.signal).then((raw) => processResponse(raw, nextAll, abort.signal)).catch(handleError(nextAll));
275480
275352
  };
275481
275353
  use_input_default((input, key) => {
275482
275354
  if (showTimeline)
275483
275355
  return;
275484
275356
  if (showForceWarning && key.escape) {
275485
- setShowForceWarning(false);
275357
+ onStageKeyInput(input, key);
275486
275358
  return;
275487
275359
  }
275488
275360
  if (stage.type === "thinking" && key.escape) {
275489
- abortControllerRef.current?.abort();
275490
- abortControllerRef.current = null;
275491
- batchApprovedRef.current = false;
275492
- setStage({ type: "idle" });
275361
+ onAbortThinking();
275493
275362
  return;
275494
275363
  }
275495
275364
  if (stage.type === "idle") {
@@ -275520,12 +275389,204 @@ ${mems.map((m) => `- [${m.id}] ${m.content}`).join(`
275520
275389
  }
275521
275390
  return;
275522
275391
  }
275523
- if (stage.type === "clone-offer") {
275392
+ onStageKeyInput(input, key);
275393
+ });
275394
+ return {
275395
+ inputValue,
275396
+ setInputValue,
275397
+ inputKey,
275398
+ pushHistory
275399
+ };
275400
+ }
275401
+
275402
+ // src/components/chat/ChatRunner.tsx
275403
+ var jsx_dev_runtime22 = __toESM(require_jsx_dev_runtime(), 1);
275404
+ function CommandPalette({
275405
+ query,
275406
+ recentChats
275407
+ }) {
275408
+ const q = query.toLowerCase();
275409
+ const isChatLoad = q.startsWith("/chat load") || q.startsWith("/chat delete");
275410
+ const chatFilter = isChatLoad ? q.startsWith("/chat load") ? q.slice("/chat load".length).trim() : q.slice("/chat delete".length).trim() : "";
275411
+ const filteredChats = chatFilter ? recentChats.filter((n) => n.toLowerCase().includes(chatFilter)) : recentChats;
275412
+ const matches2 = COMMANDS.filter((c) => c.cmd.startsWith(q));
275413
+ if (!matches2.length && !isChatLoad)
275414
+ return null;
275415
+ if (!matches2.length && isChatLoad && filteredChats.length === 0)
275416
+ return null;
275417
+ return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
275418
+ flexDirection: "column",
275419
+ marginBottom: 1,
275420
+ marginLeft: 2,
275421
+ children: [
275422
+ matches2.map((c, i) => {
275423
+ const isExact = c.cmd === query;
275424
+ return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
275425
+ gap: 2,
275426
+ children: [
275427
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
275428
+ color: isExact ? ACCENT : "white",
275429
+ bold: isExact,
275430
+ children: c.cmd
275431
+ }, undefined, false, undefined, this),
275432
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
275433
+ color: "gray",
275434
+ dimColor: true,
275435
+ children: c.desc
275436
+ }, undefined, false, undefined, this)
275437
+ ]
275438
+ }, i, true, undefined, this);
275439
+ }),
275440
+ isChatLoad && filteredChats.length > 0 && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
275441
+ flexDirection: "column",
275442
+ marginTop: matches2.length ? 1 : 0,
275443
+ children: [
275444
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
275445
+ color: "gray",
275446
+ dimColor: true,
275447
+ children: chatFilter ? `matching "${chatFilter}":` : "recent chats:"
275448
+ }, undefined, false, undefined, this),
275449
+ filteredChats.map((name, i) => /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
275450
+ gap: 1,
275451
+ marginLeft: 2,
275452
+ children: [
275453
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
275454
+ color: ACCENT,
275455
+ children: "·"
275456
+ }, undefined, false, undefined, this),
275457
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
275458
+ color: "white",
275459
+ children: name
275460
+ }, undefined, false, undefined, this)
275461
+ ]
275462
+ }, i, true, undefined, this))
275463
+ ]
275464
+ }, undefined, true, undefined, this)
275465
+ ]
275466
+ }, undefined, true, undefined, this);
275467
+ }
275468
+ function ForceAllWarning({
275469
+ onConfirm
275470
+ }) {
275471
+ const [input, setInput] = import_react51.useState("");
275472
+ return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
275473
+ flexDirection: "column",
275474
+ marginY: 1,
275475
+ gap: 1,
275476
+ children: [
275477
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
275478
+ gap: 1,
275479
+ children: /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
275480
+ color: "red",
275481
+ bold: true,
275482
+ children: "⚠ WARNING"
275483
+ }, undefined, false, undefined, this)
275484
+ }, undefined, false, undefined, this),
275485
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
275486
+ flexDirection: "column",
275487
+ marginLeft: 2,
275488
+ gap: 1,
275489
+ children: [
275490
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
275491
+ color: "yellow",
275492
+ children: "Force-all mode auto-approves EVERY tool without asking — including:"
275493
+ }, undefined, false, undefined, this),
275494
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
275495
+ color: "red",
275496
+ dimColor: true,
275497
+ children: [
275498
+ " ",
275499
+ "· shell commands (rm, git, npm, anything)"
275500
+ ]
275501
+ }, undefined, true, undefined, this),
275502
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
275503
+ color: "red",
275504
+ dimColor: true,
275505
+ children: [
275506
+ " ",
275507
+ "· file writes and deletes"
275508
+ ]
275509
+ }, undefined, true, undefined, this),
275510
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
275511
+ color: "red",
275512
+ dimColor: true,
275513
+ children: [
275514
+ " ",
275515
+ "· folder deletes"
275516
+ ]
275517
+ }, undefined, true, undefined, this),
275518
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
275519
+ color: "red",
275520
+ dimColor: true,
275521
+ children: [
275522
+ " ",
275523
+ "· external fetches and URL opens"
275524
+ ]
275525
+ }, undefined, true, undefined, this),
275526
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
275527
+ color: "yellow",
275528
+ dimColor: true,
275529
+ children: "The AI can modify or delete files without any confirmation."
275530
+ }, undefined, false, undefined, this),
275531
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
275532
+ color: "yellow",
275533
+ dimColor: true,
275534
+ children: "Only use this in throwaway environments or when you fully trust the task."
275535
+ }, undefined, false, undefined, this)
275536
+ ]
275537
+ }, undefined, true, undefined, this),
275538
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
275539
+ gap: 1,
275540
+ marginTop: 1,
275541
+ children: [
275542
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
275543
+ color: "gray",
275544
+ children: "Type "
275545
+ }, undefined, false, undefined, this),
275546
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
275547
+ color: "white",
275548
+ bold: true,
275549
+ children: "yes"
275550
+ }, undefined, false, undefined, this),
275551
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
275552
+ color: "gray",
275553
+ children: " to enable, or press "
275554
+ }, undefined, false, undefined, this),
275555
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
275556
+ color: "white",
275557
+ bold: true,
275558
+ children: "esc"
275559
+ }, undefined, false, undefined, this),
275560
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
275561
+ color: "gray",
275562
+ children: " to cancel: "
275563
+ }, undefined, false, undefined, this),
275564
+ /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(build_default2, {
275565
+ value: input,
275566
+ onChange: setInput,
275567
+ onSubmit: (v) => onConfirm(v.trim().toLowerCase() === "yes"),
275568
+ placeholder: "yes / esc to cancel"
275569
+ }, undefined, false, undefined, this)
275570
+ ]
275571
+ }, undefined, true, undefined, this)
275572
+ ]
275573
+ }, undefined, true, undefined, this);
275574
+ }
275575
+ var ChatRunner = ({ repoPath }) => {
275576
+ const chat = useChat(repoPath);
275577
+ const thinkingPhrase = useThinkingPhrase(chat.stage.type === "thinking");
275578
+ const handleStageKey = (input, key) => {
275579
+ const { stage: stage2 } = chat;
275580
+ if (chat.showForceWarning && key.escape) {
275581
+ chat.setShowForceWarning(false);
275582
+ return;
275583
+ }
275584
+ if (stage2.type === "clone-offer") {
275524
275585
  if (input === "y" || input === "Y" || key.return) {
275525
- const { repoUrl } = stage;
275526
- const launch = stage.launchAnalysis ?? false;
275586
+ const { repoUrl } = stage2;
275587
+ const launch = stage2.launchAnalysis ?? false;
275527
275588
  const cloneUrl = toCloneUrl(repoUrl);
275528
- setStage({ type: "cloning", repoUrl });
275589
+ chat.setStage({ type: "cloning", repoUrl });
275529
275590
  startCloneRepo(cloneUrl).then((result2) => {
275530
275591
  if (result2.done) {
275531
275592
  const repoName = cloneUrl.split("/").pop()?.replace(/\.git$/, "") ?? "repo";
@@ -275536,8 +275597,8 @@ ${mems.map((m) => `- [${m.id}] ${m.content}`).join(`
275536
275597
  detail: repoUrl,
275537
275598
  summary: `Cloned ${repoName} — ${fileCount} files`
275538
275599
  });
275539
- setClonedUrls((prev) => new Set([...prev, repoUrl]));
275540
- setStage({
275600
+ chat.setClonedUrls((prev) => new Set([...prev, repoUrl]));
275601
+ chat.setStage({
275541
275602
  type: "clone-done",
275542
275603
  repoUrl,
275543
275604
  destPath,
@@ -275545,13 +275606,13 @@ ${mems.map((m) => `- [${m.id}] ${m.content}`).join(`
275545
275606
  launchAnalysis: launch
275546
275607
  });
275547
275608
  } else if (result2.folderExists && result2.repoPath) {
275548
- setStage({
275609
+ chat.setStage({
275549
275610
  type: "clone-exists",
275550
275611
  repoUrl,
275551
275612
  repoPath: result2.repoPath
275552
275613
  });
275553
275614
  } else {
275554
- setStage({
275615
+ chat.setStage({
275555
275616
  type: "clone-error",
275556
275617
  message: !result2.folderExists && result2.error ? result2.error : "Clone failed"
275557
275618
  });
@@ -275560,23 +275621,23 @@ ${mems.map((m) => `- [${m.id}] ${m.content}`).join(`
275560
275621
  return;
275561
275622
  }
275562
275623
  if (input === "n" || input === "N" || key.escape)
275563
- setStage({ type: "idle" });
275624
+ chat.setStage({ type: "idle" });
275564
275625
  return;
275565
275626
  }
275566
- if (stage.type === "clone-exists") {
275627
+ if (stage2.type === "clone-exists") {
275567
275628
  if (input === "y" || input === "Y") {
275568
- const { repoUrl, repoPath: existingPath } = stage;
275569
- setStage({ type: "cloning", repoUrl });
275629
+ const { repoUrl, repoPath: existingPath } = stage2;
275630
+ chat.setStage({ type: "cloning", repoUrl });
275570
275631
  startCloneRepo(toCloneUrl(repoUrl), { forceReclone: true }).then((result2) => {
275571
275632
  if (result2.done) {
275572
- setStage({
275633
+ chat.setStage({
275573
275634
  type: "clone-done",
275574
275635
  repoUrl,
275575
275636
  destPath: existingPath,
275576
275637
  fileCount: walkDir(existingPath).length
275577
275638
  });
275578
275639
  } else {
275579
- setStage({
275640
+ chat.setStage({
275580
275641
  type: "clone-error",
275581
275642
  message: !result2.folderExists && result2.error ? result2.error : "Clone failed"
275582
275643
  });
@@ -275585,8 +275646,8 @@ ${mems.map((m) => `- [${m.id}] ${m.content}`).join(`
275585
275646
  return;
275586
275647
  }
275587
275648
  if (input === "n" || input === "N") {
275588
- const { repoUrl, repoPath: existingPath } = stage;
275589
- setStage({
275649
+ const { repoUrl, repoPath: existingPath } = stage2;
275650
+ chat.setStage({
275590
275651
  type: "clone-done",
275591
275652
  repoUrl,
275592
275653
  destPath: existingPath,
@@ -275596,14 +275657,14 @@ ${mems.map((m) => `- [${m.id}] ${m.content}`).join(`
275596
275657
  }
275597
275658
  return;
275598
275659
  }
275599
- if (stage.type === "clone-done" || stage.type === "clone-error") {
275660
+ if (stage2.type === "clone-done" || stage2.type === "clone-error") {
275600
275661
  if (key.return || key.escape) {
275601
- if (stage.type === "clone-done") {
275602
- const repoName = stage.repoUrl.split("/").pop() ?? "repo";
275662
+ if (stage2.type === "clone-done") {
275663
+ const repoName = stage2.repoUrl.split("/").pop() ?? "repo";
275603
275664
  const summaryMsg = {
275604
275665
  role: "assistant",
275605
275666
  type: "text",
275606
- content: `Cloned **${repoName}** (${stage.fileCount} files) to \`${stage.destPath}\`.
275667
+ content: `Cloned **${repoName}** (${stage2.fileCount} files) to \`${stage2.destPath}\`.
275607
275668
 
275608
275669
  Ask me anything about it — I can read files, explain how it works, or suggest improvements.`
275609
275670
  };
@@ -275611,139 +275672,128 @@ Ask me anything about it — I can read files, explain how it works, or suggest
275611
275672
  role: "assistant",
275612
275673
  type: "tool",
275613
275674
  toolName: "fetch",
275614
- content: stage.repoUrl,
275615
- result: `Clone complete. Repo: ${repoName}. Local path: ${stage.destPath}. ${stage.fileCount} files.`,
275675
+ content: stage2.repoUrl,
275676
+ result: `Clone complete. Repo: ${repoName}. Local path: ${stage2.destPath}. ${stage2.fileCount} files.`,
275616
275677
  approved: true
275617
275678
  };
275618
- const withClone = [...allMessages, contextMsg, summaryMsg];
275619
- setAllMessages(withClone);
275620
- setCommitted((prev) => [...prev, summaryMsg]);
275621
- setStage({ type: "idle" });
275679
+ chat.setAllMessages([...chat.allMessages, contextMsg, summaryMsg]);
275680
+ chat.setCommitted((prev) => [...prev, summaryMsg]);
275681
+ chat.setStage({ type: "idle" });
275622
275682
  } else {
275623
- setStage({ type: "idle" });
275683
+ chat.setStage({ type: "idle" });
275624
275684
  }
275625
275685
  }
275626
275686
  return;
275627
275687
  }
275628
- if (stage.type === "cloning")
275688
+ if (stage2.type === "cloning")
275629
275689
  return;
275630
- if (stage.type === "permission") {
275690
+ if (stage2.type === "permission") {
275631
275691
  if (input === "y" || input === "Y" || key.return) {
275632
- stage.resolve(true);
275692
+ stage2.resolve(true);
275633
275693
  return;
275634
275694
  }
275635
275695
  if (input === "n" || input === "N" || key.escape) {
275636
- batchApprovedRef.current = false;
275637
- stage.resolve(false);
275696
+ chat.batchApprovedRef.current = false;
275697
+ stage2.resolve(false);
275638
275698
  return;
275639
275699
  }
275640
275700
  return;
275641
275701
  }
275642
- if (stage.type === "preview") {
275702
+ if (stage2.type === "preview") {
275643
275703
  if (key.upArrow) {
275644
- setStage({
275645
- ...stage,
275646
- scrollOffset: Math.max(0, stage.scrollOffset - 1)
275704
+ chat.setStage({
275705
+ ...stage2,
275706
+ scrollOffset: Math.max(0, stage2.scrollOffset - 1)
275647
275707
  });
275648
275708
  return;
275649
275709
  }
275650
275710
  if (key.downArrow) {
275651
- setStage({ ...stage, scrollOffset: stage.scrollOffset + 1 });
275711
+ chat.setStage({ ...stage2, scrollOffset: stage2.scrollOffset + 1 });
275652
275712
  return;
275653
275713
  }
275654
275714
  if (key.escape || input === "s" || input === "S") {
275655
- if (pendingMsgIndex !== null) {
275656
- const msg = allMessages[pendingMsgIndex];
275715
+ if (chat.pendingMsgIndex !== null) {
275716
+ const msg = chat.allMessages[chat.pendingMsgIndex];
275657
275717
  if (msg?.type === "plan") {
275658
- setCommitted((prev) => [...prev, { ...msg, applied: false }]);
275659
- appendMemory({
275660
- kind: "code-skipped",
275661
- detail: msg.patches.map((p) => p.path).join(", "),
275662
- summary: `Skipped changes to ${msg.patches.length} file(s)`
275663
- });
275718
+ chat.setCommitted((prev) => [...prev, { ...msg, applied: false }]);
275719
+ chat.skipPatches(msg.patches);
275664
275720
  }
275665
275721
  }
275666
- setPendingMsgIndex(null);
275667
- setStage({ type: "idle" });
275722
+ chat.setPendingMsgIndex(null);
275723
+ chat.setStage({ type: "idle" });
275668
275724
  return;
275669
275725
  }
275670
275726
  if (key.return || input === "a" || input === "A") {
275671
- try {
275672
- applyPatches2(repoPath, stage.patches);
275673
- appendMemory({
275674
- kind: "code-applied",
275675
- detail: stage.patches.map((p) => p.path).join(", "),
275676
- summary: `Applied changes to ${stage.patches.length} file(s)`
275677
- });
275678
- } catch {}
275679
- if (pendingMsgIndex !== null) {
275680
- const msg = allMessages[pendingMsgIndex];
275727
+ if (chat.pendingMsgIndex !== null) {
275728
+ const msg = chat.allMessages[chat.pendingMsgIndex];
275681
275729
  if (msg?.type === "plan") {
275730
+ chat.applyPatchesAndContinue(msg.patches);
275682
275731
  const applied = { ...msg, applied: true };
275683
- setAllMessages((prev) => prev.map((m, i) => i === pendingMsgIndex ? applied : m));
275684
- setCommitted((prev) => [...prev, applied]);
275732
+ chat.setAllMessages((prev) => prev.map((m, i) => i === chat.pendingMsgIndex ? applied : m));
275733
+ chat.setCommitted((prev) => [...prev, applied]);
275685
275734
  }
275686
275735
  }
275687
- setPendingMsgIndex(null);
275688
- setStage({ type: "idle" });
275736
+ chat.setPendingMsgIndex(null);
275737
+ chat.setStage({ type: "idle" });
275689
275738
  return;
275690
275739
  }
275691
275740
  }
275692
- if (stage.type === "viewing-file") {
275741
+ if (stage2.type === "viewing-file") {
275693
275742
  if (key.upArrow) {
275694
- setStage({
275695
- ...stage,
275696
- scrollOffset: Math.max(0, stage.scrollOffset - 1)
275743
+ chat.setStage({
275744
+ ...stage2,
275745
+ scrollOffset: Math.max(0, stage2.scrollOffset - 1)
275697
275746
  });
275698
275747
  return;
275699
275748
  }
275700
275749
  if (key.downArrow) {
275701
- setStage({ ...stage, scrollOffset: stage.scrollOffset + 1 });
275750
+ chat.setStage({ ...stage2, scrollOffset: stage2.scrollOffset + 1 });
275702
275751
  return;
275703
275752
  }
275704
275753
  if (key.escape || key.return) {
275705
- setStage({ type: "idle" });
275754
+ chat.setStage({ type: "idle" });
275706
275755
  return;
275707
275756
  }
275708
275757
  }
275709
- });
275710
- const handleProviderDone = (p) => {
275711
- setProvider(p);
275712
- setStage({ type: "loading" });
275713
- fetchFileTree(repoPath).catch(() => walkDir(repoPath)).then((fileTree) => {
275714
- const importantFiles = readImportantFiles(repoPath, fileTree);
275715
- const historySummary = buildMemorySummary(repoPath);
275716
- const lensFile = readLensFile(repoPath);
275717
- const lensContext = lensFile ? `
275718
-
275719
- ## LENS.md (previous analysis)
275720
- ${lensFile.overview}
275721
-
275722
- Important folders: ${lensFile.importantFolders.join(", ")}
275723
- Suggestions: ${lensFile.suggestions.slice(0, 3).join("; ")}` : "";
275724
- const toolsSection = registry.buildSystemPromptSection();
275725
- setSystemPrompt(buildSystemPrompt(importantFiles, historySummary, toolsSection) + lensContext);
275726
- const greeting = {
275727
- role: "assistant",
275728
- content: `Welcome to Lens
275729
- Codebase loaded — ${importantFiles.length} files indexed.${historySummary ? `
275730
-
275731
- I have memory of previous actions in this repo.` : ""}${lensFile ? `
275732
-
275733
- Found LENS.md — I have context from a previous analysis of this repo.` : ""}
275734
- Ask me anything, tell me what to build, share a URL, or ask me to read/write files.
275735
-
275736
- Tip: type /timeline to browse commit history.`,
275737
- type: "text"
275738
- };
275739
- setCommitted([greeting]);
275740
- setAllMessages([greeting]);
275741
- setStage({ type: "idle" });
275742
- }).catch(() => setStage({ type: "idle" }));
275743
275758
  };
275759
+ const chatInput = useChatInput(chat.stage, chat.showTimeline, chat.showForceWarning, chat.abortThinking, handleStageKey);
275760
+ const sendMessage = (text) => {
275761
+ if (!chat.provider)
275762
+ return;
275763
+ const handled = handleCommand(text, {
275764
+ repoPath,
275765
+ allMessages: chat.allMessages,
275766
+ autoApprove: chat.autoApprove,
275767
+ forceApprove: chat.forceApprove,
275768
+ chatName: chat.chatName,
275769
+ chatNameRef: chat.chatNameRef,
275770
+ setShowTimeline: chat.setShowTimeline,
275771
+ setShowReview: chat.setShowReview,
275772
+ setShowForceWarning: chat.setShowForceWarning,
275773
+ setForceApprove: chat.setForceApprove,
275774
+ setAutoApprove: chat.setAutoApprove,
275775
+ setAllMessages: chat.setAllMessages,
275776
+ setCommitted: chat.setCommitted,
275777
+ setRecentChats: chat.setRecentChats,
275778
+ updateChatName: chat.updateChatName
275779
+ });
275780
+ if (handled)
275781
+ return;
275782
+ chatInput.pushHistory(text);
275783
+ chat.sendMessage(text, chat.provider, chat.systemPrompt, chat.allMessages);
275784
+ if (!chat.chatName) {
275785
+ const name = getChatNameSuggestions([
275786
+ ...chat.allMessages,
275787
+ { role: "user", content: text, type: "text" }
275788
+ ])[0] ?? `chat-${new Date().toISOString().slice(0, 10)}`;
275789
+ chat.updateChatName(name);
275790
+ chat.setRecentChats((prev) => [name, ...prev.filter((n) => n !== name)].slice(0, 10));
275791
+ }
275792
+ };
275793
+ const { stage } = chat;
275744
275794
  if (stage.type === "picking-provider")
275745
275795
  return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(ProviderPicker, {
275746
- onDone: handleProviderDone
275796
+ onDone: chat.handleProviderDone
275747
275797
  }, undefined, false, undefined, this);
275748
275798
  if (stage.type === "loading")
275749
275799
  return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
@@ -275765,85 +275815,85 @@ Tip: type /timeline to browse commit history.`,
275765
275815
  }, undefined, false, undefined, this)
275766
275816
  ]
275767
275817
  }, undefined, true, undefined, this);
275768
- if (showTimeline)
275818
+ if (chat.showTimeline)
275769
275819
  return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(TimelineRunner, {
275770
275820
  repoPath,
275771
- onExit: () => setShowTimeline(false)
275821
+ onExit: () => chat.setShowTimeline(false)
275772
275822
  }, undefined, false, undefined, this);
275773
- if (showReview)
275823
+ if (chat.showReview)
275774
275824
  return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(ReviewCommand, {
275775
275825
  path: repoPath,
275776
- onExit: () => setShowReview(false)
275826
+ onExit: () => chat.setShowReview(false)
275777
275827
  }, undefined, false, undefined, this);
275778
275828
  if (stage.type === "clone-offer")
275779
275829
  return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(CloneOfferView, {
275780
275830
  stage,
275781
- committed
275831
+ committed: chat.committed
275782
275832
  }, undefined, false, undefined, this);
275783
275833
  if (stage.type === "cloning")
275784
275834
  return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(CloningView, {
275785
275835
  stage,
275786
- committed
275836
+ committed: chat.committed
275787
275837
  }, undefined, false, undefined, this);
275788
275838
  if (stage.type === "clone-exists")
275789
275839
  return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(CloneExistsView, {
275790
275840
  stage,
275791
- committed
275841
+ committed: chat.committed
275792
275842
  }, undefined, false, undefined, this);
275793
275843
  if (stage.type === "clone-done")
275794
275844
  return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(CloneDoneView, {
275795
275845
  stage,
275796
- committed
275846
+ committed: chat.committed
275797
275847
  }, undefined, false, undefined, this);
275798
275848
  if (stage.type === "clone-error")
275799
275849
  return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(CloneErrorView, {
275800
275850
  stage,
275801
- committed
275851
+ committed: chat.committed
275802
275852
  }, undefined, false, undefined, this);
275803
275853
  if (stage.type === "preview")
275804
275854
  return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(PreviewView, {
275805
275855
  stage,
275806
- committed
275856
+ committed: chat.committed
275807
275857
  }, undefined, false, undefined, this);
275808
275858
  if (stage.type === "viewing-file")
275809
275859
  return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(ViewingFileView, {
275810
275860
  stage,
275811
- committed
275861
+ committed: chat.committed
275812
275862
  }, undefined, false, undefined, this);
275813
275863
  return /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
275814
275864
  flexDirection: "column",
275815
275865
  children: [
275816
275866
  /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Static, {
275817
- items: committed,
275867
+ items: chat.committed,
275818
275868
  children: (msg, i) => /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(StaticMessage, {
275819
275869
  msg
275820
275870
  }, i, false, undefined, this)
275821
275871
  }, undefined, false, undefined, this),
275822
- showForceWarning && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(ForceAllWarning, {
275872
+ chat.showForceWarning && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(ForceAllWarning, {
275823
275873
  onConfirm: (confirmed) => {
275824
- setShowForceWarning(false);
275874
+ chat.setShowForceWarning(false);
275825
275875
  if (confirmed) {
275826
- setForceApprove(true);
275827
- setAutoApprove(true);
275876
+ chat.setForceApprove(true);
275877
+ chat.setAutoApprove(true);
275828
275878
  const msg = {
275829
275879
  role: "assistant",
275830
275880
  content: "⚡⚡ Force-all mode ON — ALL tools auto-approved including shell and writes. Type /auto --force-all again to disable.",
275831
275881
  type: "text"
275832
275882
  };
275833
- setCommitted((prev) => [...prev, msg]);
275834
- setAllMessages((prev) => [...prev, msg]);
275883
+ chat.setCommitted((prev) => [...prev, msg]);
275884
+ chat.setAllMessages((prev) => [...prev, msg]);
275835
275885
  } else {
275836
275886
  const msg = {
275837
275887
  role: "assistant",
275838
275888
  content: "Force-all cancelled.",
275839
275889
  type: "text"
275840
275890
  };
275841
- setCommitted((prev) => [...prev, msg]);
275842
- setAllMessages((prev) => [...prev, msg]);
275891
+ chat.setCommitted((prev) => [...prev, msg]);
275892
+ chat.setAllMessages((prev) => [...prev, msg]);
275843
275893
  }
275844
275894
  }
275845
275895
  }, undefined, false, undefined, this),
275846
- !showForceWarning && stage.type === "thinking" && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
275896
+ !chat.showForceWarning && stage.type === "thinking" && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
275847
275897
  gap: 1,
275848
275898
  children: [
275849
275899
  /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Text, {
@@ -275860,34 +275910,30 @@ Tip: type /timeline to browse commit history.`,
275860
275910
  }, undefined, false, undefined, this)
275861
275911
  ]
275862
275912
  }, undefined, true, undefined, this),
275863
- !showForceWarning && stage.type === "permission" && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(PermissionPrompt, {
275913
+ !chat.showForceWarning && stage.type === "permission" && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(PermissionPrompt, {
275864
275914
  tool: stage.tool,
275865
275915
  onDecide: stage.resolve
275866
275916
  }, undefined, false, undefined, this),
275867
- !showForceWarning && stage.type === "idle" && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
275917
+ !chat.showForceWarning && stage.type === "idle" && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(Box_default, {
275868
275918
  flexDirection: "column",
275869
275919
  children: [
275870
- inputValue.startsWith("/") && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(CommandPalette, {
275871
- query: inputValue,
275872
- onSelect: (cmd) => setInputValue(cmd),
275873
- recentChats
275920
+ chatInput.inputValue.startsWith("/") && /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(CommandPalette, {
275921
+ query: chatInput.inputValue,
275922
+ recentChats: chat.recentChats
275874
275923
  }, undefined, false, undefined, this),
275875
275924
  /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(InputBox, {
275876
- value: inputValue,
275877
- onChange: (v) => {
275878
- historyIndexRef.current = -1;
275879
- setInputValue(v);
275880
- },
275925
+ value: chatInput.inputValue,
275926
+ onChange: (v) => chatInput.setInputValue(v),
275881
275927
  onSubmit: (val) => {
275882
275928
  if (val.trim())
275883
275929
  sendMessage(val.trim());
275884
- setInputValue("");
275930
+ chatInput.setInputValue("");
275885
275931
  },
275886
- inputKey
275932
+ inputKey: chatInput.inputKey
275887
275933
  }, undefined, false, undefined, this),
275888
275934
  /* @__PURE__ */ jsx_dev_runtime22.jsxDEV(ShortcutBar, {
275889
- autoApprove,
275890
- forceApprove
275935
+ autoApprove: chat.autoApprove,
275936
+ forceApprove: chat.forceApprove
275891
275937
  }, undefined, false, undefined, this)
275892
275938
  ]
275893
275939
  }, undefined, true, undefined, this)
@@ -275917,12 +275963,12 @@ var ChatCommand = ({ path: inputPath }) => {
275917
275963
  }, undefined, false, undefined, this);
275918
275964
  };
275919
275965
 
275920
- // src/commands/watch.tsx
275966
+ // src/commands/run.tsx
275921
275967
  import path21 from "path";
275922
275968
  import { existsSync as existsSync16 } from "fs";
275923
275969
 
275924
- // src/components/watch/WatchRunner.tsx
275925
- var import_react50 = __toESM(require_react(), 1);
275970
+ // src/components/watch/RunRunner.tsx
275971
+ var import_react52 = __toESM(require_react(), 1);
275926
275972
 
275927
275973
  // src/utils/watch.ts
275928
275974
  import { spawn as spawn2 } from "child_process";
@@ -276102,7 +276148,7 @@ function readPackageJson(repoPath) {
276102
276148
  }
276103
276149
  }
276104
276150
 
276105
- // src/components/watch/WatchRunner.tsx
276151
+ // src/components/watch/RunRunner.tsx
276106
276152
  var jsx_dev_runtime24 = __toESM(require_jsx_dev_runtime(), 1);
276107
276153
  var MAX_LOGS2 = 120;
276108
276154
  var MAX_SUGGESTIONS = 8;
@@ -276180,7 +276226,7 @@ function SuggestionCard({
276180
276226
  }) {
276181
276227
  const w = process.stdout.columns ?? 80;
276182
276228
  const divider = "─".repeat(Math.min(w - 4, 60));
276183
- const [patchState, setPatchState] = import_react50.useState(fixAll && suggestion.patch ? "applied" : null);
276229
+ const [patchState, setPatchState] = import_react52.useState(fixAll && suggestion.patch ? "applied" : null);
276184
276230
  use_input_default((input) => {
276185
276231
  if (!isNew || !suggestion.patch || patchState !== null || fixAll)
276186
276232
  return;
@@ -276372,8 +276418,8 @@ function ThinkingCard({
276372
276418
  toolLog,
276373
276419
  startTime
276374
276420
  }) {
276375
- const [elapsed, setElapsed] = import_react50.useState(0);
276376
- import_react50.useEffect(() => {
276421
+ const [elapsed, setElapsed] = import_react52.useState(0);
276422
+ import_react52.useEffect(() => {
276377
276423
  const t = setInterval(() => setElapsed(Math.floor((Date.now() - startTime) / 1000)), 1000);
276378
276424
  return () => clearInterval(t);
276379
276425
  }, [startTime]);
@@ -276558,7 +276604,7 @@ function InputCard({ prompt, value }) {
276558
276604
  ]
276559
276605
  }, undefined, true, undefined, this);
276560
276606
  }
276561
- function WatchRunner({
276607
+ function RunRunner({
276562
276608
  cmd,
276563
276609
  repoPath,
276564
276610
  clean,
@@ -276566,22 +276612,22 @@ function WatchRunner({
276566
276612
  autoRestart,
276567
276613
  extraPrompt
276568
276614
  }) {
276569
- const [stage, setStage] = import_react50.useState({ type: "picking-provider" });
276570
- const [logs, setLogs] = import_react50.useState([]);
276571
- const [suggestions, setSuggestions] = import_react50.useState([]);
276572
- const [active, setActive] = import_react50.useState([]);
276573
- const [lensLoaded, setLensLoaded] = import_react50.useState(false);
276574
- const [pendingQueue, setPendingQueue] = import_react50.useState([]);
276575
- const [fixedCount, setFixedCount] = import_react50.useState(0);
276576
- const [inputRequest, setInputRequest] = import_react50.useState(null);
276577
- const [inputValue, setInputValue] = import_react50.useState("");
276578
- const processRef = import_react50.useRef(null);
276579
- const providerRef = import_react50.useRef(null);
276580
- const systemPromptRef = import_react50.useRef("");
276581
- const activeCountRef = import_react50.useRef(0);
276582
- const pendingExitCode = import_react50.useRef(undefined);
276583
- const abortControllersRef = import_react50.useRef(new Map);
276584
- const patchedThisRunRef = import_react50.useRef(0);
276615
+ const [stage, setStage] = import_react52.useState({ type: "picking-provider" });
276616
+ const [logs, setLogs] = import_react52.useState([]);
276617
+ const [suggestions, setSuggestions] = import_react52.useState([]);
276618
+ const [active, setActive] = import_react52.useState([]);
276619
+ const [lensLoaded, setLensLoaded] = import_react52.useState(false);
276620
+ const [pendingQueue, setPendingQueue] = import_react52.useState([]);
276621
+ const [fixedCount, setFixedCount] = import_react52.useState(0);
276622
+ const [inputRequest, setInputRequest] = import_react52.useState(null);
276623
+ const [inputValue, setInputValue] = import_react52.useState("");
276624
+ const processRef = import_react52.useRef(null);
276625
+ const providerRef = import_react52.useRef(null);
276626
+ const systemPromptRef = import_react52.useRef("");
276627
+ const activeCountRef = import_react52.useRef(0);
276628
+ const pendingExitCode = import_react52.useRef(undefined);
276629
+ const abortControllersRef = import_react52.useRef(new Map);
276630
+ const patchedThisRunRef = import_react52.useRef(0);
276585
276631
  const { stdout } = use_stdout_default();
276586
276632
  const currentPending = pendingQueue[0] ?? null;
276587
276633
  const handleRestart = () => {
@@ -276649,10 +276695,6 @@ function WatchRunner({
276649
276695
  lensContext = `Overview: ${lensFile.overview}
276650
276696
 
276651
276697
  Important folders: ${lensFile.importantFolders.join(", ")}
276652
- ${lensFile.securityIssues.length > 0 ? `
276653
- Known security issues:
276654
- ${lensFile.securityIssues.map((s) => `- ${s}`).join(`
276655
- `)}` : ""}
276656
276698
  ${lensFile.suggestions.length > 0 ? `
276657
276699
  Project suggestions:
276658
276700
  ${lensFile.suggestions.map((s) => `- ${s}`).join(`
@@ -276723,13 +276765,13 @@ ${lensFile.suggestions.map((s) => `- ${s}`).join(`
276723
276765
  ];
276724
276766
  runInvestigation(id, chunk2, initialMessages, abort.signal, t);
276725
276767
  };
276726
- import_react50.useEffect(() => {
276768
+ import_react52.useEffect(() => {
276727
276769
  return () => {
276728
276770
  processRef.current?.kill();
276729
276771
  abortControllersRef.current.forEach((a) => a.abort());
276730
276772
  };
276731
276773
  }, []);
276732
- import_react50.useEffect(() => {
276774
+ import_react52.useEffect(() => {
276733
276775
  if (autoRestart && stage.type === "crashed") {
276734
276776
  const t = setTimeout(() => handleRestart(), 1500);
276735
276777
  return () => clearTimeout(t);
@@ -277133,9 +277175,9 @@ ${result2}` : `Tool <${parsed.toolName}> was denied.`,
277133
277175
  }, undefined, true, undefined, this);
277134
277176
  }
277135
277177
 
277136
- // src/commands/watch.tsx
277178
+ // src/commands/run.tsx
277137
277179
  var jsx_dev_runtime25 = __toESM(require_jsx_dev_runtime(), 1);
277138
- function WatchCommand({
277180
+ function RunCommand({
277139
277181
  cmd,
277140
277182
  path: inputPath,
277141
277183
  clean,
@@ -277169,7 +277211,7 @@ function WatchCommand({
277169
277211
  }, undefined, true, undefined, this)
277170
277212
  }, undefined, false, undefined, this);
277171
277213
  }
277172
- return /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(WatchRunner, {
277214
+ return /* @__PURE__ */ jsx_dev_runtime25.jsxDEV(RunRunner, {
277173
277215
  cmd,
277174
277216
  repoPath,
277175
277217
  clean,
@@ -277204,7 +277246,7 @@ var TimelineCommand = ({ path: inputPath }) => {
277204
277246
  };
277205
277247
 
277206
277248
  // src/commands/commit.tsx
277207
- var import_react51 = __toESM(require_react(), 1);
277249
+ var import_react53 = __toESM(require_react(), 1);
277208
277250
  import { execSync as execSync5 } from "child_process";
277209
277251
  import { existsSync as existsSync18 } from "fs";
277210
277252
  import path23 from "path";
@@ -277359,9 +277401,9 @@ function CommitRunner({
277359
277401
  push,
277360
277402
  confirm
277361
277403
  }) {
277362
- const [phase, setPhase] = import_react51.useState({ type: "checking" });
277404
+ const [phase, setPhase] = import_react53.useState({ type: "checking" });
277363
277405
  const phraseText = useThinkingPhrase(phase.type === "generating", "commit", 2800);
277364
- import_react51.useEffect(() => {
277406
+ import_react53.useEffect(() => {
277365
277407
  (async () => {
277366
277408
  if (!gitRun3("git rev-parse --git-dir", cwd2).ok) {
277367
277409
  setPhase({ type: "error", message: "not a git repository" });
@@ -277929,7 +277971,7 @@ function CommitCommand({
277929
277971
  confirm
277930
277972
  }) {
277931
277973
  const cwd2 = path23.resolve(inputPath);
277932
- const [provider, setProvider] = import_react51.useState(null);
277974
+ const [provider, setProvider] = import_react53.useState(null);
277933
277975
  if (!existsSync18(cwd2)) {
277934
277976
  return /* @__PURE__ */ jsx_dev_runtime27.jsxDEV(Box_default, {
277935
277977
  marginTop: 1,
@@ -278840,7 +278882,7 @@ var jsx_dev_runtime28 = __toESM(require_jsx_dev_runtime(), 1);
278840
278882
  registerBuiltins();
278841
278883
  await loadAddons();
278842
278884
  var program2 = new Command;
278843
- program2.command("stalk <url>").alias("repo").description("Analyze a remote repository").action((url) => {
278885
+ program2.command("repo <url>").description("Analyze a remote repository").action((url) => {
278844
278886
  render_default(/* @__PURE__ */ jsx_dev_runtime28.jsxDEV(RepoCommand, {
278845
278887
  url
278846
278888
  }, undefined, false, undefined, this));
@@ -278848,28 +278890,28 @@ program2.command("stalk <url>").alias("repo").description("Analyze a remote repo
278848
278890
  program2.command("provider").description("Configure AI providers").action(() => {
278849
278891
  render_default(/* @__PURE__ */ jsx_dev_runtime28.jsxDEV(InitCommand, {}, undefined, false, undefined, this));
278850
278892
  });
278851
- program2.command("judge [path]").alias("review").description("Review a local codebase").action((inputPath) => {
278893
+ program2.command("review [path]").description("Review a local codebase").action((inputPath) => {
278852
278894
  render_default(/* @__PURE__ */ jsx_dev_runtime28.jsxDEV(ReviewCommand, {
278853
278895
  path: inputPath ?? "."
278854
278896
  }, undefined, false, undefined, this));
278855
278897
  });
278856
- program2.command("cook <text>").alias("task").description("Apply a natural language change to the codebase").option("-p, --path <path>", "Path to the repo", ".").action((text, opts) => {
278898
+ program2.command("task <text>").description("Apply a natural language change to the codebase").option("-p, --path <path>", "Path to the repo", ".").action((text, opts) => {
278857
278899
  render_default(/* @__PURE__ */ jsx_dev_runtime28.jsxDEV(TaskCommand, {
278858
278900
  prompt: text,
278859
278901
  path: opts.path
278860
278902
  }, undefined, false, undefined, this));
278861
278903
  });
278862
- program2.command("vibe").alias("chat").description("Chat with your codebase — ask questions or make changes").option("-p, --path <path>", "Path to the repo", ".").action((opts) => {
278904
+ program2.command("chat").description("Chat with your codebase — ask questions or make changes").option("-p, --path <path>", "Path to the repo", ".").action((opts) => {
278863
278905
  render_default(/* @__PURE__ */ jsx_dev_runtime28.jsxDEV(ChatCommand, {
278864
278906
  path: opts.path
278865
278907
  }, undefined, false, undefined, this));
278866
278908
  });
278867
- program2.command("history").alias("timeline").description("Explore your code history — see commits, changes, and evolution").option("-p, --path <path>", "Path to the repo", ".").action((opts) => {
278909
+ program2.command("timeline").description("Explore your code history — see commits, changes, and evolution").option("-p, --path <path>", "Path to the repo", ".").action((opts) => {
278868
278910
  render_default(/* @__PURE__ */ jsx_dev_runtime28.jsxDEV(TimelineCommand, {
278869
278911
  path: opts.path
278870
278912
  }, undefined, false, undefined, this));
278871
278913
  });
278872
- program2.command("crimes [files...]").alias("commit").description("Generate a smart conventional commit message from staged changes or specific files").option("-p, --path <path>", "Path to the repo", ".").option("--auto", "Stage all changes (or the given files) and commit without confirmation").option("--confirm", "Show preview before committing even when using --auto").option("--preview", "Show the generated message without committing").option("--push", "Push to remote after committing").action((files, opts) => {
278914
+ program2.command("commit [files...]").description("Generate a smart conventional commit message from staged changes or specific files").option("-p, --path <path>", "Path to the repo", ".").option("--auto", "Stage all changes (or the given files) and commit without confirmation").option("--confirm", "Show preview before committing even when using --auto").option("--preview", "Show the generated message without committing").option("--push", "Push to remote after committing").action((files, opts) => {
278873
278915
  render_default(/* @__PURE__ */ jsx_dev_runtime28.jsxDEV(CommitCommand, {
278874
278916
  path: opts.path,
278875
278917
  files: files ?? [],
@@ -278879,8 +278921,8 @@ program2.command("crimes [files...]").alias("commit").description("Generate a sm
278879
278921
  push: opts.push ?? false
278880
278922
  }, undefined, false, undefined, this));
278881
278923
  });
278882
- program2.command("watch <cmd>").alias("spy").description("Watch a dev command and get AI suggestions for errors").option("-p, --path <path>", "Path to the repo", ".").option("--clean", "Only show AI suggestions, hide raw logs").option("--fix-all", "Auto-apply fixes as errors are detected").option("--auto-restart", "Automatically re-run the command after a crash").option("--prompt <text>", "Extra context for the AI about your project").action((cmd, opts) => {
278883
- render_default(/* @__PURE__ */ jsx_dev_runtime28.jsxDEV(WatchCommand, {
278924
+ program2.command("run <cmd>").description("Run your dev server. Lens detects and fixes errors automatically").option("-p, --path <path>", "Path to the repo", ".").option("--clean", "Only show AI suggestions, hide raw logs").option("--fix-all", "Auto-apply fixes as errors are detected").option("--auto-restart", "Automatically re-run the command after a crash").option("--prompt <text>", "Extra context for the AI about your project").action((cmd, opts) => {
278925
+ render_default(/* @__PURE__ */ jsx_dev_runtime28.jsxDEV(RunCommand, {
278884
278926
  cmd,
278885
278927
  path: opts.path,
278886
278928
  clean: opts.clean ?? false,