clippy-test 1.0.9 → 2.0.1

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.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -0
  3. package/bin/oncall.js +396 -0
  4. package/dist/api.js +10 -1
  5. package/dist/cli.js +20 -20
  6. package/dist/config.js +7 -7
  7. package/dist/helpers/cli-helpers.d.ts +25 -0
  8. package/dist/helpers/cli-helpers.js +329 -0
  9. package/dist/helpers/config-helpers.js +189 -0
  10. package/dist/helpers/ripgrep-tool.d.ts +15 -0
  11. package/dist/helpers/ripgrep-tool.js +126 -0
  12. package/dist/index.js +225 -62
  13. package/dist/logsManager.d.ts +31 -0
  14. package/dist/logsManager.js +90 -0
  15. package/dist/postinstall.js +20 -0
  16. package/dist/tools/ripgrep.js +3 -3
  17. package/dist/useWebSocket.d.ts +14 -6
  18. package/dist/useWebSocket.js +290 -45
  19. package/dist/utils/version-check.d.ts +2 -0
  20. package/dist/utils/version-check.js +124 -0
  21. package/dist/utils.d.ts +16 -0
  22. package/dist/utils.js +125 -4
  23. package/dist/websocket-server.d.ts +24 -0
  24. package/dist/websocket-server.js +235 -0
  25. package/package.json +18 -5
  26. package/bin/clippy.js +0 -109
  27. package/dist/api.js.map +0 -1
  28. package/dist/cli.js.map +0 -1
  29. package/dist/code_hierarchy.js.map +0 -1
  30. package/dist/config.js.map +0 -1
  31. package/dist/index.js.map +0 -1
  32. package/dist/tools/code_hierarchy.d.ts +0 -8
  33. package/dist/tools/code_hierarchy.js +0 -78
  34. package/dist/tools/code_hierarchy.js.map +0 -1
  35. package/dist/tools/fetch_context.d.ts +0 -1
  36. package/dist/tools/fetch_context.js +0 -8
  37. package/dist/tools/fetch_context.js.map +0 -1
  38. package/dist/tools/ripgrep.js.map +0 -1
  39. package/dist/ui-graph.js.map +0 -1
  40. package/dist/useWebSocket.js.map +0 -1
  41. package/dist/utils.js.map +0 -1
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Singleton Logs Manager
3
+ * Maintains logs data
4
+ */
5
+ export class LogsManager {
6
+ constructor() {
7
+ this.logs = "";
8
+ // Private constructor to prevent direct instantiation
9
+ }
10
+ /**
11
+ * Get the singleton instance
12
+ */
13
+ static getInstance() {
14
+ if (!LogsManager.instance) {
15
+ LogsManager.instance = new LogsManager();
16
+ }
17
+ return LogsManager.instance;
18
+ }
19
+ /**
20
+ * Append Logs
21
+ * @param chunk
22
+ */
23
+ addChunk(chunk) {
24
+ this.logs += chunk;
25
+ }
26
+ /**
27
+ * Retrieves logs
28
+ */
29
+ getLogs() {
30
+ return this.logs;
31
+ }
32
+ getTailLogs(n) {
33
+ const logs = this.getLogs() || "";
34
+ const linesArr = logs.split("\n");
35
+ const tail = linesArr.slice(-n).join("\n");
36
+ return tail;
37
+ }
38
+ getGrepLogs(pattern, before = 5, after = 5) {
39
+ const logs = this.getLogs() || "";
40
+ if (!pattern || !pattern.trim()) {
41
+ return "";
42
+ }
43
+ const lines = logs.split("\n");
44
+ const normalizedPattern = pattern.toLowerCase();
45
+ const beforeCount = before;
46
+ const afterCount = after;
47
+ const blocks = [];
48
+ for (let i = 0; i < lines.length; i++) {
49
+ const line = lines[i];
50
+ if (line.toLowerCase().includes(normalizedPattern)) {
51
+ const start = Math.max(0, i - beforeCount);
52
+ const end = Math.min(lines.length, i + afterCount + 1);
53
+ const contextBlock = lines.slice(start, end).join("\n");
54
+ blocks.push(`block (match line ${i + 1}): ${contextBlock}`);
55
+ }
56
+ }
57
+ if (blocks.length === 0) {
58
+ return `No matches found for pattern "${pattern}" in logs.`;
59
+ }
60
+ return blocks.join("\n\n");
61
+ }
62
+ getRecentErrors(n) {
63
+ const logs = this.getLogs() || "";
64
+ const lines = logs.split("\n");
65
+ const matched = [];
66
+ if (!Number.isFinite(n) || n <= 0) {
67
+ return "Invalid value for n. Please provide a positive number of error lines to fetch.";
68
+ }
69
+ const errorRegex = /\b(ERROR|ERR|FATAL|CRITICAL|WARN|WARNING|SEVERE|ALERT|PANIC|EMERGENCY)\b|(Exception|Unhandled|Uncaught|Traceback|stacktrace|Caused by:)|(TypeError|ReferenceError|RangeError|SyntaxError|RuntimeError|ValueError|NullPointerException|IllegalArgument)|(timeout|timed out|connection refused|connection reset|ECONNRESET|ECONNREFUSED|ETIMEDOUT|EAI_AGAIN)|(HTTP\s(4\d\d|5\d\d)|\b5\d\d\b|\b429\b|\b503\b)|(OOM|out of memory|disk full|quota exceeded|rate limited|deadlock|segfault|SIGKILL|panic)/i;
70
+ for (let i = lines.length - 1; i >= 0 && matched.length < n; i--) {
71
+ const line = lines[i];
72
+ if (errorRegex.test(line)) {
73
+ matched.push(line);
74
+ }
75
+ }
76
+ if (matched.length === 0) {
77
+ return "No recent error log lines found";
78
+ }
79
+ return matched.reverse().join("\n");
80
+ }
81
+ /**
82
+ * Clear all logs
83
+ */
84
+ clearAll() {
85
+ this.logs = "";
86
+ }
87
+ }
88
+ // Export the singleton instance
89
+ export default LogsManager.getInstance();
90
+ //# sourceMappingURL=logsManager.js.map
@@ -0,0 +1,20 @@
1
+ import { execSync } from 'child_process';
2
+ import { existsSync } from 'fs';
3
+ import { join } from 'path';
4
+
5
+ try {
6
+ const patchPackagePath = join(process.cwd(), 'node_modules', '.bin', 'patch-package');
7
+ const patchesDir = join(process.cwd(), 'patches');
8
+
9
+ if (existsSync(patchPackagePath) && existsSync(patchesDir)) {
10
+ execSync('patch-package', { stdio: 'ignore', timeout: 5000 });
11
+ }
12
+ } catch (error) {
13
+ }
14
+
15
+ console.log(`
16
+ OnCall installed.
17
+
18
+ Run:
19
+ oncall --help
20
+ to get started.`);
@@ -29,11 +29,11 @@ export async function ripgrepSearch(query, options = {}) {
29
29
  }
30
30
  const searchDir = workingDirectory || (typeof process !== "undefined" ? process.cwd() : undefined);
31
31
  if (!searchDir) {
32
- console.error("[ripgrep] workingDirectory is required for client-side usage");
32
+ // console.error("[ripgrep] workingDirectory is required for client-side usage");
33
33
  return [];
34
34
  }
35
35
  try {
36
- let rg = new RipGrep(query, workingDirectory);
36
+ let rg = new RipGrep(query, searchDir);
37
37
  rg.withFilename().lineNumber();
38
38
  if (!caseSensitive) {
39
39
  rg.ignoreCase();
@@ -82,7 +82,7 @@ export async function ripgrepSearch(query, options = {}) {
82
82
  error.code === 1) {
83
83
  return [];
84
84
  }
85
- console.error("[ripgrep] Error executing ripgrep:", error.message);
85
+ // console.error("[ripgrep] Error executing ripgrep:", error.message);
86
86
  return [];
87
87
  }
88
88
  }
@@ -1,10 +1,15 @@
1
- export declare function useWebSocket(url: string): {
1
+ import WebSocket from "ws";
2
+ import { BaseMessage } from "langchain";
3
+ import { LogsManager } from "./logsManager.js";
4
+ export declare function useWebSocket(url: string, rawLogData: LogsManager): {
2
5
  connectWebSocket: () => void;
3
6
  socketId: string;
4
- sendQuery: (query: string, logs: any, state?: any) => void;
5
- chatResponseMessages: any[];
6
- setChatResponseMessages: import("react").Dispatch<import("react").SetStateAction<any[]>>;
7
- setTrimmedChats: import("react").Dispatch<import("react").SetStateAction<any[]>>;
7
+ sendQuery: (messages: BaseMessage[], architecture: string, logs?: string, planningDoc?: string) => void;
8
+ chatResponseMessages: BaseMessage<import("@langchain/core/messages").MessageStructure, import("@langchain/core/messages").MessageType>[];
9
+ visibleChats: BaseMessage<import("@langchain/core/messages").MessageStructure, import("@langchain/core/messages").MessageType>[];
10
+ setVisibleChats: import("react").Dispatch<import("react").SetStateAction<BaseMessage<import("@langchain/core/messages").MessageStructure, import("@langchain/core/messages").MessageType>[]>>;
11
+ setChatResponseMessages: import("react").Dispatch<import("react").SetStateAction<BaseMessage<import("@langchain/core/messages").MessageStructure, import("@langchain/core/messages").MessageType>[]>>;
12
+ setTrimmedChats: import("react").Dispatch<import("react").SetStateAction<BaseMessage<import("@langchain/core/messages").MessageStructure, import("@langchain/core/messages").MessageType>[]>>;
8
13
  isConnected: boolean;
9
14
  connectionError: string;
10
15
  isLoading: boolean;
@@ -15,5 +20,8 @@ export declare function useWebSocket(url: string): {
15
20
  setIsLoading: import("react").Dispatch<import("react").SetStateAction<boolean>>;
16
21
  setShowControlR: import("react").Dispatch<import("react").SetStateAction<boolean>>;
17
22
  showControlR: boolean;
18
- setCompleteChatHistory: import("react").Dispatch<import("react").SetStateAction<any[]>>;
23
+ setCompleteChatHistory: import("react").Dispatch<import("react").SetStateAction<BaseMessage<import("@langchain/core/messages").MessageStructure, import("@langchain/core/messages").MessageType>[]>>;
24
+ customMessage: string;
25
+ graphState: any;
26
+ setGraphState: import("react").Dispatch<any>;
19
27
  };
@@ -1,13 +1,17 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
+ import WebSocket from "ws";
2
3
  import { getContextLines } from "./utils.js";
3
4
  import { toolFunctionCall } from "./api.js";
4
5
  import fs from "fs";
5
6
  import os from "os";
6
7
  import path from "path";
7
- // Load Clippy config from ~/.clippy/config
8
+ import { mapChatMessagesToStoredMessages, mapStoredMessagesToChatMessages, SystemMessage, } from "@langchain/core/messages";
9
+ import { ripgrepSearch } from "./helpers/ripgrep-tool.js";
10
+ import { loadProjectMetadata, logd } from "./helpers/cli-helpers.js";
11
+ // Load OnCall config from ~/.oncall/config
8
12
  const HOME_DIR = os.homedir();
9
- const CLIPPY_DIR = path.join(HOME_DIR, ".clippy");
10
- const CONFIG_PATH = path.join(CLIPPY_DIR, "config");
13
+ const ONCALL_DIR = path.join(HOME_DIR, ".oncall");
14
+ const CONFIG_PATH = path.join(ONCALL_DIR, "config");
11
15
  let API_KEY = "";
12
16
  try {
13
17
  const configText = fs.readFileSync(CONFIG_PATH, "utf8");
@@ -16,33 +20,54 @@ try {
16
20
  API_KEY = match[1].trim();
17
21
  }
18
22
  else {
19
- console.log("No API_KEY found in ~/.clippy/config");
23
+ console.log("No API_KEY found in ~/.oncall/config");
20
24
  }
21
25
  }
22
26
  catch (err) {
23
- console.log("Failed to read Clippy config:", err);
27
+ console.log("Failed to read OnCall config:", err);
24
28
  }
25
- export function useWebSocket(url) {
29
+ export function useWebSocket(url, rawLogData) {
26
30
  //refs
27
31
  const socketRef = useRef(null);
28
32
  const [socketId, setSocketId] = useState(null);
29
33
  const [chatResponseMessages, setChatResponseMessages] = useState([]);
30
34
  const [trimmedChats, setTrimmedChats] = useState([]);
31
- const [visibleChats, setVisibleChats] = useState([]);
35
+ const initialAssistantMessage = new SystemMessage("I’m watching your app run locally. You can ask me about errors, logs, performance,or anything else related to this run.");
36
+ const [visibleChats, setVisibleChats] = useState([
37
+ initialAssistantMessage,
38
+ ]);
32
39
  const [isConnected, setIsConnected] = useState(false);
33
40
  const [connectionError, setConnectionError] = useState(null);
34
41
  const [isLoading, setIsLoading] = useState(false);
35
42
  const [showControlR, setShowControlR] = useState(false);
43
+ const [customMessage, setCustomMessage] = useState(null);
44
+ const [graphState, setGraphState] = useState(null);
36
45
  const authKey = API_KEY;
46
+ const initialProjectMetadata = loadProjectMetadata();
47
+ const getProjectMetadata = () => loadProjectMetadata() || initialProjectMetadata;
48
+ const getServiceId = () => getProjectMetadata()?.window_id;
49
+ const hasLogsAccess = () => {
50
+ const value = getProjectMetadata()?.logs_available;
51
+ return value === undefined ? true : value;
52
+ };
53
+ const hasCodeAccess = () => {
54
+ const value = getProjectMetadata()?.code_available;
55
+ return value === undefined ? true : value;
56
+ };
37
57
  useEffect(() => {
38
58
  if (API_KEY === "") {
39
- setConnectionError("No API_KEY found in ~/.clippy/config");
40
- console.log("No API_KEY found in ~/.clippy/config");
59
+ setConnectionError("No API_KEY found in ~/.oncall/config");
60
+ console.log("No API_KEY found in ~/.oncall/config");
41
61
  process.exit();
42
62
  }
43
63
  }, [API_KEY]);
44
64
  //web socket connection
45
65
  const connectWebSocket = useCallback(() => {
66
+ // const d = fs.readFileSync('logs')
67
+ // fs.writeFileSync('logs', `${d} inside connectWS \n message: ${url} ${authKey} ${serviceId}`)
68
+ // if (!url || !authKey || serviceId.trim() === "") {
69
+ // return;
70
+ // }
46
71
  if (socketRef.current) {
47
72
  socketRef.current.close();
48
73
  }
@@ -52,7 +77,17 @@ export function useWebSocket(url) {
52
77
  setIsConnected(false);
53
78
  socket.onopen = () => {
54
79
  try {
55
- socket.send(JSON.stringify({ type: "recurring_connection", authKey }));
80
+ const md = getProjectMetadata();
81
+ if (!md?.window_id) {
82
+ setConnectionError("Missing serviceId (window_id) in oncall.yaml");
83
+ console.log("Missing serviceId (window_id) in oncall.yaml");
84
+ return;
85
+ }
86
+ socket.send(JSON.stringify({
87
+ type: "recurring_connection",
88
+ authKey,
89
+ serviceId: md.window_id,
90
+ }));
56
91
  }
57
92
  catch (error) {
58
93
  setConnectionError("error");
@@ -83,7 +118,8 @@ export function useWebSocket(url) {
83
118
  return; // ignore non-JSON frames
84
119
  }
85
120
  const data = JSON.parse(text);
86
- if (data.type === "user_assigned" && data.socketId) {
121
+ // if (data.type === "user_assigned" && data.socketId) {
122
+ if (data.type === "user_assigned") {
87
123
  setIsConnected(true);
88
124
  setSocketId(data.socketId);
89
125
  }
@@ -99,46 +135,126 @@ export function useWebSocket(url) {
99
135
  return;
100
136
  }
101
137
  if (data.type === "tool_function_call") {
102
- if (data.function_name === "get_context_lines") {
138
+ if (data.function_name === "read_file") {
139
+ logd(`[read_file] 📥 Tool call received: tool_call_id=${data.tool_call_id}, args=${JSON.stringify(data.args)}`);
140
+ if (!hasCodeAccess()) {
141
+ logd(`[read_file] ⚠️ No code access, denying tool`);
142
+ await denyToolAccess("read_file", data);
143
+ return;
144
+ }
103
145
  const argsData = {
104
- fileName: data.args.fileName,
146
+ filePath: data.args.filePath,
105
147
  lineNumber: data.args.lineNumber,
106
- before: data.args.before,
107
- after: data.args.after,
148
+ before: data.args.before || 30,
149
+ after: data.args.after || 30,
108
150
  };
109
- const result = getContextLines(argsData.fileName, argsData.lineNumber, argsData.before, argsData.after);
110
- try {
111
- await toolFunctionCall(data?.tool_call_id, result, data?.args, "tool_function_call");
151
+ logd(`[read_file] 📋 Extracted args: filePath=${argsData.filePath}, lineNumber=${argsData.lineNumber}, before=${argsData.before}, after=${argsData.after}`);
152
+ logd(`[read_file] 🔍 Calling getContextLines...`);
153
+ const result = getContextLines(argsData.filePath, argsData.lineNumber, argsData.before, argsData.after);
154
+ logd(`[read_file] ✅ File content extracted: resultLength=${result.length}, resultLines=${result.split("\n").length}, isEmpty=${result.trim().length === 0}`);
155
+ logd(`[read_file] 📄 Result preview (first 300 chars): ${result.substring(0, 300)}${result.length > 300 ? "..." : ""}`);
156
+ await postToolCallResult(data, result, setChatResponseMessages);
157
+ logd(`[read_file] 📤 Result sent to backend successfully`);
158
+ // await redisClient.set(data.tool_call_id, result, { EX: 120 });
159
+ return;
160
+ }
161
+ if (data.function_name === "grep_search") {
162
+ if (!hasCodeAccess()) {
163
+ await denyToolAccess("grep_search", data);
164
+ return;
112
165
  }
113
- catch (error) {
114
- setChatResponseMessages((prev) => [
115
- ...prev,
116
- error?.response?.message || "Error, please try again",
117
- ]);
166
+ await grepSearch(data.args.searchTerm, data);
167
+ return;
168
+ }
169
+ if (data.function_name === "read_logs") {
170
+ if (!hasLogsAccess()) {
171
+ await denyToolAccess("read_logs", data);
172
+ return;
118
173
  }
119
- // await redisClient.set(data.tool_call_id, result, { EX: 120 });
174
+ await readLogs(data.args.pageNumber, data);
175
+ return;
176
+ }
177
+ if (data.function_name === "tail_logs") {
178
+ if (!hasLogsAccess()) {
179
+ await denyToolAccess("tail_logs", data);
180
+ return;
181
+ }
182
+ await tailLogs(data.args.n, data);
183
+ return;
184
+ }
185
+ if (data.function_name === "grep_logs") {
186
+ if (!hasLogsAccess()) {
187
+ await denyToolAccess("grep_logs", data);
188
+ return;
189
+ }
190
+ await grepLogs(data.args.pattern, data.args.before, data.args.after, data);
191
+ return;
192
+ }
193
+ if (data.function_name === "get_recent_errors") {
194
+ if (!hasLogsAccess()) {
195
+ await denyToolAccess("get_recent_errors", data);
196
+ return;
197
+ }
198
+ await getRecentErrors(data.args.n, data);
199
+ return;
120
200
  }
121
- return;
122
201
  }
123
- setChatResponseMessages((prev) => [...prev, data]);
124
- setVisibleChats((prev) => [...prev, data]);
202
+ // trimmed - imp msgs
203
+ // visible - shown
204
+ // chatresponses - all
125
205
  // add non-progress responses to trimmedChats
126
- if (data.type !== "progress") {
127
- setTrimmedChats((prevTrimmed) => {
128
- const updated = [...prevTrimmed, data];
129
- // update visible chats with all trimmed messages
130
- setVisibleChats(updated);
131
- return updated;
132
- });
206
+ // @todo this is where the messages get trimmed
207
+ // if (data.type !== "progress") {
208
+ // setTrimmedChats((prevTrimmed) => {
209
+ // const updated = [...prevTrimmed, data];
210
+ // // update visible chats with all trimmed messages
211
+ // setVisibleChats(updated);
212
+ // return updated;
213
+ // });
214
+ // }
215
+ if (data.type === "response") {
216
+ let messages = data.data.messages ? data.data.messages : [];
217
+ messages = mapStoredMessagesToChatMessages(messages);
218
+ switch (data.data.type) {
219
+ case "messages":
220
+ if (data.data.sender !== "toolNode") {
221
+ setVisibleChats(old => [...old, ...messages]);
222
+ }
223
+ // logd(`LOGGING RESPONSE SENDER: ${data.data.sender}`);
224
+ break;
225
+ case "values":
226
+ if (data.data.state) {
227
+ setGraphState(data.data.state);
228
+ }
229
+ break;
230
+ case "updates":
231
+ if (data.data.sender !== "toolNode" && data.data.sender !== "routerNode") {
232
+ setChatResponseMessages(old => [...old, ...messages]);
233
+ }
234
+ if (data.data.sender === "answerNode") {
235
+ setTrimmedChats(prev => {
236
+ setVisibleChats([...prev, ...messages]);
237
+ return [...prev, ...messages];
238
+ });
239
+ setIsLoading(false);
240
+ }
241
+ if (data.data.sender === "userNode") {
242
+ setIsLoading(false);
243
+ }
244
+ break;
245
+ default:
246
+ break;
247
+ }
133
248
  }
134
- if (data.type === "response" ||
135
- data.type === "error" ||
249
+ if (data.type === "error" ||
136
250
  data.type === "ask_user") {
137
251
  setIsLoading(false);
252
+ setCustomMessage(null);
138
253
  }
139
254
  }
140
255
  catch (error) {
141
- console.warn("WebSocket message handling warning:", error);
256
+ setIsLoading(false);
257
+ // console.warn("WebSocket message handling warning:", error);
142
258
  return;
143
259
  }
144
260
  };
@@ -159,34 +275,160 @@ export function useWebSocket(url) {
159
275
  // };
160
276
  // }
161
277
  }, [url, authKey]);
162
- const sendQuery = useCallback((query, logs, state) => {
278
+ const sendQuery = useCallback((messages, architecture, logs, planningDoc) => {
163
279
  const socket = socketRef.current;
164
280
  if (socket && socket.readyState === WebSocket.OPEN) {
165
281
  setIsLoading(true);
166
- const payload = state
167
- ? { type: "query", socketId, logs, query, state }
168
- : { type: "query", socketId, logs, query };
282
+ setCustomMessage("🤔 Thinking...");
283
+ const md = getProjectMetadata();
284
+ const payload = {
285
+ type: "query",
286
+ authKey,
287
+ serviceId: md?.window_id,
288
+ userQuery: {
289
+ messages: graphState
290
+ ? mapChatMessagesToStoredMessages([messages[messages.length - 1]])
291
+ : mapChatMessagesToStoredMessages(messages),
292
+ architecture: architecture,
293
+ logs,
294
+ planningDoc: "",
295
+ },
296
+ planningDoc,
297
+ graphState: graphState || undefined,
298
+ };
169
299
  socket.send(JSON.stringify(payload));
170
300
  }
171
301
  else {
172
302
  setConnectionError("WebSocket not connected");
173
303
  throw new Error("Cannot send the message: Web socket not connected");
174
304
  }
175
- }, [socketId]);
305
+ }, [socketId, graphState]);
176
306
  // Clean up on unmount
177
307
  useEffect(() => {
178
308
  return () => {
179
309
  if (socketRef.current) {
180
- socketRef.current.close();
310
+ // socketRef.current.close();
181
311
  }
182
312
  };
183
313
  }, []);
314
+ async function grepSearch(searchTerm, data) {
315
+ logd(`[grepSearch] 🔍 Grep search invoked: searchTerm=${searchTerm}, tool_call_id=${data.tool_call_id}, args=${JSON.stringify(data.args)}`);
316
+ const projectMetadata = getProjectMetadata();
317
+ const workingDirectory = projectMetadata?.path || process.cwd();
318
+ logd(`[grepSearch] 📁 Working directory: workingDirectory=${workingDirectory}, projectPath=${projectMetadata?.path || 'N/A'}, processCwd=${process.cwd()}`);
319
+ const maxResults = data.args?.max_results || 20;
320
+ const caseSensitive = data.args?.case_sensitive || false;
321
+ const fileTypes = data.args?.file_types || [];
322
+ logd(`[grepSearch] ⚙️ Search options: maxResults=${maxResults}, caseSensitive=${caseSensitive}, fileTypes=${JSON.stringify(fileTypes)}`);
323
+ try {
324
+ const results = await ripgrepSearch(searchTerm, {
325
+ maxResults,
326
+ caseSensitive,
327
+ fileTypes,
328
+ workingDirectory,
329
+ });
330
+ logd(`[grepSearch] ✅ Search completed: resultCount=${results.length}, results=${JSON.stringify(results.map((r) => ({
331
+ filePath: r.filePath,
332
+ line: r.line,
333
+ previewLength: r.preview?.length || 0,
334
+ })))}`);
335
+ await postToolCallResult(data, results, setChatResponseMessages);
336
+ }
337
+ catch (error) {
338
+ logd(`[grepSearch] ❌ Error during grep search: ${error instanceof Error ? error.message : String(error)}, stack=${error instanceof Error ? error.stack : "N/A"}`);
339
+ await postToolCallResult(data, [], setChatResponseMessages);
340
+ }
341
+ }
342
+ async function readLogs(pageNumber, data) {
343
+ logd(`Triggered readLogs: ${pageNumber}`);
344
+ pageNumber = parseInt(pageNumber.toString());
345
+ const lines = rawLogData
346
+ .getLogs()
347
+ .split("\n")
348
+ .slice(-50 * pageNumber)
349
+ .join("\n");
350
+ logd(`Triggered readLogs - Posting result: ${lines.length}`);
351
+ await postToolCallResult(data, lines, setChatResponseMessages);
352
+ logd(`Triggered readLogs - done Posting result`);
353
+ }
354
+ async function tailLogs(n, data) {
355
+ try {
356
+ logd(`Triggered tailLogs: ${n}`);
357
+ n = parseInt(n.toString());
358
+ if (Number.isNaN(n) || n <= 0) {
359
+ await postToolCallResult(data, "Invalid value for n. Please provide a positive number of log lines to fetch.", setChatResponseMessages);
360
+ return;
361
+ }
362
+ const tail = rawLogData.getTailLogs(n);
363
+ logd(`Triggered tailLogs - Posting result: ${tail.length}`);
364
+ await postToolCallResult(data, tail, setChatResponseMessages);
365
+ logd(`Triggered tailLogs - done Posting result`);
366
+ }
367
+ catch (error) {
368
+ logd(`Triggered tailLogs - error: ${JSON.stringify(error)}`);
369
+ await postToolCallResult(data, "Failed to fetch tail logs from CLI.", setChatResponseMessages);
370
+ }
371
+ }
372
+ async function grepLogs(pattern, before, after, data) {
373
+ try {
374
+ logd(`Triggered grepLogs: pattern=${pattern}, before=${before}, after=${after}`);
375
+ if (!pattern || !pattern.trim()) {
376
+ await postToolCallResult(data, "Invalid pattern for grep_logs. Please provide a non-empty pattern.", setChatResponseMessages);
377
+ return;
378
+ }
379
+ const beforeCount = before !== undefined ? parseInt(before.toString()) : 5;
380
+ const afterCount = after !== undefined ? parseInt(after.toString()) : 5;
381
+ const result = rawLogData.getGrepLogs(pattern, beforeCount, afterCount);
382
+ await postToolCallResult(data, result, setChatResponseMessages);
383
+ logd(`Triggered grepLogs - done Posting result of length ${result.length}`);
384
+ }
385
+ catch (error) {
386
+ logd(`Triggered grepLogs - error: ${JSON.stringify(error)}`);
387
+ await postToolCallResult(data, "Failed to search logs with grep_logs.", setChatResponseMessages);
388
+ }
389
+ }
390
+ async function getRecentErrors(n, data) {
391
+ try {
392
+ logd(`Triggered getRecentErrors: n=${n}`);
393
+ n = parseInt(n.toString());
394
+ if (Number.isNaN(n) || n <= 0) {
395
+ await postToolCallResult(data, "Invalid value for n. Number Provided :" + n.toString(), setChatResponseMessages);
396
+ return;
397
+ }
398
+ const result = rawLogData.getRecentErrors(n);
399
+ await postToolCallResult(data, result, setChatResponseMessages);
400
+ logd(`Triggered getRecentErrors - done Posting result of length ${result.length}`);
401
+ }
402
+ catch (error) {
403
+ logd(`Triggered getRecentErrors - error: ${JSON.stringify(error)}`);
404
+ await postToolCallResult(data, "Failed to fetch recent error logs.", setChatResponseMessages);
405
+ }
406
+ }
407
+ async function postToolCallResult(data, result, setChatResponseMessages) {
408
+ try {
409
+ await toolFunctionCall(data?.tool_call_id, result, data?.args, "tool_function_call");
410
+ }
411
+ catch (error) {
412
+ logd(`Triggered readLogs - failer podting: ${JSON.stringify(error)}`);
413
+ // setChatResponseMessages((prev) => [
414
+ // ...prev,
415
+ // error?.response?.message || "Error, please try again",
416
+ // ]);
417
+ }
418
+ }
419
+ async function denyToolAccess(functionName, data) {
420
+ const serviceId = getServiceId();
421
+ const message = `No Access to execute ${functionName}. with the serviceId of ${serviceId ?? "unknown"}.`;
422
+ await postToolCallResult(data, message, setChatResponseMessages);
423
+ }
184
424
  return {
185
425
  connectWebSocket,
186
426
  socketId,
187
427
  sendQuery,
188
- chatResponseMessages: visibleChats,
189
- setChatResponseMessages: setVisibleChats,
428
+ chatResponseMessages: chatResponseMessages, // Full history including ToolMessage (needed for backend)
429
+ visibleChats: visibleChats, // Filtered for UI display (excludes ToolMessage except reflections)
430
+ setVisibleChats: setVisibleChats,
431
+ setChatResponseMessages,
190
432
  setTrimmedChats,
191
433
  isConnected,
192
434
  connectionError,
@@ -199,6 +441,9 @@ export function useWebSocket(url) {
199
441
  setShowControlR,
200
442
  showControlR,
201
443
  setCompleteChatHistory: setChatResponseMessages,
444
+ customMessage,
445
+ graphState,
446
+ setGraphState,
202
447
  };
203
448
  }
204
449
  //# sourceMappingURL=useWebSocket.js.map
@@ -0,0 +1,2 @@
1
+ export function checkVersionCompatibility(forceCheck?: boolean): Promise<boolean>;
2
+ export function checkVersionAndExit(): Promise<void>;