wave-agent-sdk 0.2.1 → 0.5.0
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/agent.d.ts +66 -20
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +156 -83
- package/dist/constants/prompts.d.ts +7 -2
- package/dist/constants/prompts.d.ts.map +1 -1
- package/dist/constants/prompts.js +41 -5
- package/dist/constants/tools.d.ts +2 -2
- package/dist/constants/tools.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/managers/MemoryRuleManager.d.ts.map +1 -1
- package/dist/managers/MemoryRuleManager.js +16 -2
- package/dist/managers/aiManager.d.ts +14 -4
- package/dist/managers/aiManager.d.ts.map +1 -1
- package/dist/managers/aiManager.js +61 -9
- package/dist/managers/backgroundBashManager.d.ts.map +1 -1
- package/dist/managers/backgroundBashManager.js +1 -0
- package/dist/managers/backgroundTaskManager.d.ts +35 -0
- package/dist/managers/backgroundTaskManager.d.ts.map +1 -0
- package/dist/managers/backgroundTaskManager.js +249 -0
- package/dist/managers/bashManager.d.ts.map +1 -1
- package/dist/managers/bashManager.js +0 -3
- package/dist/managers/foregroundTaskManager.d.ts +9 -0
- package/dist/managers/foregroundTaskManager.d.ts.map +1 -0
- package/dist/managers/foregroundTaskManager.js +20 -0
- package/dist/managers/liveConfigManager.d.ts +1 -1
- package/dist/managers/liveConfigManager.d.ts.map +1 -1
- package/dist/managers/lspManager.d.ts.map +1 -1
- package/dist/managers/lspManager.js +3 -1
- package/dist/managers/messageManager.d.ts +34 -4
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +104 -13
- package/dist/managers/permissionManager.d.ts.map +1 -1
- package/dist/managers/permissionManager.js +11 -13
- package/dist/managers/pluginManager.d.ts.map +1 -1
- package/dist/managers/pluginManager.js +3 -2
- package/dist/managers/pluginScopeManager.d.ts +13 -2
- package/dist/managers/pluginScopeManager.d.ts.map +1 -1
- package/dist/managers/pluginScopeManager.js +38 -0
- package/dist/managers/reversionManager.d.ts +39 -0
- package/dist/managers/reversionManager.d.ts.map +1 -0
- package/dist/managers/reversionManager.js +118 -0
- package/dist/managers/slashCommandManager.d.ts +4 -1
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +16 -6
- package/dist/managers/subagentManager.d.ts +13 -2
- package/dist/managers/subagentManager.d.ts.map +1 -1
- package/dist/managers/subagentManager.js +144 -35
- package/dist/managers/toolManager.d.ts +11 -1
- package/dist/managers/toolManager.d.ts.map +1 -1
- package/dist/managers/toolManager.js +11 -3
- package/dist/services/GitService.d.ts.map +1 -1
- package/dist/services/GitService.js +6 -2
- package/dist/services/MarketplaceService.d.ts +14 -1
- package/dist/services/MarketplaceService.d.ts.map +1 -1
- package/dist/services/MarketplaceService.js +72 -4
- package/dist/services/MemoryRuleService.d.ts +1 -1
- package/dist/services/MemoryRuleService.d.ts.map +1 -1
- package/dist/services/MemoryRuleService.js +13 -2
- package/dist/services/aiService.js +1 -1
- package/dist/services/configurationService.d.ts +18 -2
- package/dist/services/configurationService.d.ts.map +1 -1
- package/dist/services/configurationService.js +62 -0
- package/dist/services/fileWatcher.d.ts +0 -5
- package/dist/services/fileWatcher.d.ts.map +1 -1
- package/dist/services/fileWatcher.js +0 -11
- package/dist/services/memory.js +1 -1
- package/dist/services/pluginLoader.d.ts.map +1 -1
- package/dist/services/pluginLoader.js +6 -1
- package/dist/services/reversionService.d.ts +24 -0
- package/dist/services/reversionService.d.ts.map +1 -0
- package/dist/services/reversionService.js +76 -0
- package/dist/services/session.d.ts +7 -0
- package/dist/services/session.d.ts.map +1 -1
- package/dist/services/session.js +126 -3
- package/dist/tools/bashTool.d.ts +0 -8
- package/dist/tools/bashTool.d.ts.map +1 -1
- package/dist/tools/bashTool.js +52 -174
- package/dist/tools/deleteFileTool.d.ts.map +1 -1
- package/dist/tools/deleteFileTool.js +9 -0
- package/dist/tools/editTool.d.ts.map +1 -1
- package/dist/tools/editTool.js +15 -4
- package/dist/tools/multiEditTool.d.ts.map +1 -1
- package/dist/tools/multiEditTool.js +16 -5
- package/dist/tools/taskOutputTool.d.ts +3 -0
- package/dist/tools/taskOutputTool.d.ts.map +1 -0
- package/dist/tools/taskOutputTool.js +149 -0
- package/dist/tools/taskStopTool.d.ts +3 -0
- package/dist/tools/taskStopTool.d.ts.map +1 -0
- package/dist/tools/taskStopTool.js +65 -0
- package/dist/tools/taskTool.d.ts.map +1 -1
- package/dist/tools/taskTool.js +105 -63
- package/dist/tools/types.d.ts +7 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/writeTool.d.ts.map +1 -1
- package/dist/tools/writeTool.js +9 -0
- package/dist/types/commands.d.ts +1 -0
- package/dist/types/commands.d.ts.map +1 -1
- package/dist/types/configuration.d.ts +3 -0
- package/dist/types/configuration.d.ts.map +1 -1
- package/dist/types/environment.d.ts +2 -1
- package/dist/types/environment.d.ts.map +1 -1
- package/dist/types/environment.js +0 -6
- package/dist/types/history.d.ts +5 -0
- package/dist/types/history.d.ts.map +1 -0
- package/dist/types/history.js +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/marketplace.d.ts +4 -0
- package/dist/types/marketplace.d.ts.map +1 -1
- package/dist/types/messaging.d.ts +7 -1
- package/dist/types/messaging.d.ts.map +1 -1
- package/dist/types/processes.d.ts +24 -4
- package/dist/types/processes.d.ts.map +1 -1
- package/dist/types/reversion.d.ts +29 -0
- package/dist/types/reversion.d.ts.map +1 -0
- package/dist/types/reversion.js +1 -0
- package/dist/utils/builtinSubagents.d.ts.map +1 -1
- package/dist/utils/builtinSubagents.js +16 -0
- package/dist/utils/constants.d.ts +2 -2
- package/dist/utils/constants.d.ts.map +1 -1
- package/dist/utils/constants.js +2 -2
- package/dist/utils/editUtils.d.ts +4 -9
- package/dist/utils/editUtils.d.ts.map +1 -1
- package/dist/utils/editUtils.js +54 -55
- package/dist/utils/messageOperations.d.ts +3 -1
- package/dist/utils/messageOperations.d.ts.map +1 -1
- package/dist/utils/messageOperations.js +8 -1
- package/dist/utils/openaiClient.d.ts.map +1 -1
- package/dist/utils/openaiClient.js +56 -26
- package/dist/utils/promptHistory.d.ts +20 -0
- package/dist/utils/promptHistory.d.ts.map +1 -0
- package/dist/utils/promptHistory.js +117 -0
- package/package.json +5 -3
- package/src/agent.ts +193 -109
- package/src/constants/prompts.ts +45 -5
- package/src/constants/tools.ts +2 -2
- package/src/index.ts +1 -1
- package/src/managers/MemoryRuleManager.ts +18 -2
- package/src/managers/aiManager.ts +87 -18
- package/src/managers/backgroundBashManager.ts +1 -0
- package/src/managers/backgroundTaskManager.ts +306 -0
- package/src/managers/bashManager.ts +0 -4
- package/src/managers/foregroundTaskManager.ts +26 -0
- package/src/managers/liveConfigManager.ts +2 -1
- package/src/managers/lspManager.ts +3 -1
- package/src/managers/messageManager.ts +136 -18
- package/src/managers/permissionManager.ts +11 -13
- package/src/managers/pluginManager.ts +4 -3
- package/src/managers/pluginScopeManager.ts +57 -8
- package/src/managers/reversionManager.ts +152 -0
- package/src/managers/slashCommandManager.ts +30 -7
- package/src/managers/subagentManager.ts +176 -31
- package/src/managers/toolManager.ts +23 -4
- package/src/services/GitService.ts +6 -2
- package/src/services/MarketplaceService.ts +100 -4
- package/src/services/MemoryRuleService.ts +18 -6
- package/src/services/aiService.ts +1 -1
- package/src/services/configurationService.ts +79 -1
- package/src/services/fileWatcher.ts +0 -13
- package/src/services/memory.ts +1 -1
- package/src/services/pluginLoader.ts +7 -1
- package/src/services/reversionService.ts +94 -0
- package/src/services/session.ts +161 -3
- package/src/tools/bashTool.ts +73 -200
- package/src/tools/deleteFileTool.ts +15 -0
- package/src/tools/editTool.ts +20 -10
- package/src/tools/multiEditTool.ts +21 -11
- package/src/tools/taskOutputTool.ts +174 -0
- package/src/tools/taskStopTool.ts +72 -0
- package/src/tools/taskTool.ts +130 -74
- package/src/tools/types.ts +7 -0
- package/src/tools/writeTool.ts +14 -0
- package/src/types/commands.ts +3 -0
- package/src/types/configuration.ts +4 -0
- package/src/types/environment.ts +3 -1
- package/src/types/history.ts +4 -0
- package/src/types/index.ts +1 -0
- package/src/types/marketplace.ts +5 -0
- package/src/types/messaging.ts +9 -1
- package/src/types/processes.ts +33 -4
- package/src/types/reversion.ts +29 -0
- package/src/utils/builtinSubagents.ts +18 -0
- package/src/utils/constants.ts +2 -2
- package/src/utils/editUtils.ts +66 -58
- package/src/utils/messageOperations.ts +10 -0
- package/src/utils/openaiClient.ts +69 -35
- package/src/utils/promptHistory.ts +133 -0
- package/dist/utils/bashHistory.d.ts +0 -50
- package/dist/utils/bashHistory.d.ts.map +0 -1
- package/dist/utils/bashHistory.js +0 -256
- package/src/utils/bashHistory.ts +0 -320
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bash command history management module
|
|
3
|
-
* Used for persistent storage and searching of bash commands executed by users
|
|
4
|
-
*/
|
|
5
|
-
import fs from "fs";
|
|
6
|
-
import { BASH_HISTORY_FILE, DATA_DIRECTORY } from "./constants.js";
|
|
7
|
-
import { logger } from "./globalLogger.js";
|
|
8
|
-
const HISTORY_VERSION = 1;
|
|
9
|
-
const MAX_HISTORY_SIZE = 1000;
|
|
10
|
-
/**
|
|
11
|
-
* Ensure data directory exists
|
|
12
|
-
*/
|
|
13
|
-
const ensureDataDirectory = () => {
|
|
14
|
-
try {
|
|
15
|
-
if (!fs.existsSync(DATA_DIRECTORY)) {
|
|
16
|
-
fs.mkdirSync(DATA_DIRECTORY, { recursive: true });
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
catch (error) {
|
|
20
|
-
logger.debug("Failed to create data directory:", error);
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* Load bash history
|
|
25
|
-
*/
|
|
26
|
-
export const loadBashHistory = () => {
|
|
27
|
-
try {
|
|
28
|
-
ensureDataDirectory();
|
|
29
|
-
if (!fs.existsSync(BASH_HISTORY_FILE)) {
|
|
30
|
-
return {
|
|
31
|
-
commands: [],
|
|
32
|
-
version: HISTORY_VERSION,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
const data = fs.readFileSync(BASH_HISTORY_FILE, "utf-8");
|
|
36
|
-
const history = JSON.parse(data);
|
|
37
|
-
// Version compatibility check
|
|
38
|
-
if (history.version !== HISTORY_VERSION) {
|
|
39
|
-
logger.debug("Bash history version mismatch, resetting history");
|
|
40
|
-
return {
|
|
41
|
-
commands: [],
|
|
42
|
-
version: HISTORY_VERSION,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
return history;
|
|
46
|
-
}
|
|
47
|
-
catch (error) {
|
|
48
|
-
logger.debug("Failed to load bash history:", error);
|
|
49
|
-
return {
|
|
50
|
-
commands: [],
|
|
51
|
-
version: HISTORY_VERSION,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
/**
|
|
56
|
-
* Save bash history
|
|
57
|
-
*/
|
|
58
|
-
export const saveBashHistory = (history) => {
|
|
59
|
-
try {
|
|
60
|
-
// Skip saving to file when in test environment
|
|
61
|
-
if (process.env.NODE_ENV === "test") {
|
|
62
|
-
logger.debug("Skipping bash history save in test environment");
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
ensureDataDirectory();
|
|
66
|
-
// Limit history size
|
|
67
|
-
if (history.commands.length > MAX_HISTORY_SIZE) {
|
|
68
|
-
history.commands = history.commands.slice(-MAX_HISTORY_SIZE);
|
|
69
|
-
}
|
|
70
|
-
const data = JSON.stringify(history, null, 2);
|
|
71
|
-
fs.writeFileSync(BASH_HISTORY_FILE, data, "utf-8");
|
|
72
|
-
}
|
|
73
|
-
catch (error) {
|
|
74
|
-
logger.debug("Failed to save bash history:", error);
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
/**
|
|
78
|
-
* Add command to bash history
|
|
79
|
-
*/
|
|
80
|
-
export const addBashCommandToHistory = (command, workdir) => {
|
|
81
|
-
try {
|
|
82
|
-
const history = loadBashHistory();
|
|
83
|
-
const timestamp = Date.now();
|
|
84
|
-
// Check if it's a duplicate consecutive command to avoid duplicate recording
|
|
85
|
-
const lastCommand = history.commands[history.commands.length - 1];
|
|
86
|
-
if (lastCommand &&
|
|
87
|
-
lastCommand.command === command &&
|
|
88
|
-
lastCommand.workdir === workdir) {
|
|
89
|
-
// Update timestamp of the last record
|
|
90
|
-
lastCommand.timestamp = timestamp;
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
// Add new command record
|
|
94
|
-
history.commands.push({
|
|
95
|
-
command,
|
|
96
|
-
timestamp,
|
|
97
|
-
workdir,
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
saveBashHistory(history);
|
|
101
|
-
logger.debug("Added bash command to history:", { command, workdir });
|
|
102
|
-
}
|
|
103
|
-
catch (error) {
|
|
104
|
-
logger.debug("Failed to add bash command to history:", error);
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
/**
|
|
108
|
-
* Search bash history by keywords
|
|
109
|
-
*/
|
|
110
|
-
export const searchBashHistory = (query, limit = 10) => {
|
|
111
|
-
try {
|
|
112
|
-
const history = loadBashHistory();
|
|
113
|
-
const normalizedQuery = query.toLowerCase().trim();
|
|
114
|
-
const filteredCommands = history.commands;
|
|
115
|
-
if (!normalizedQuery) {
|
|
116
|
-
// If no search query, return recent commands (deduplicated)
|
|
117
|
-
const deduped = deduplicateCommands(filteredCommands);
|
|
118
|
-
return deduped.slice(0, limit); // Latest first
|
|
119
|
-
}
|
|
120
|
-
// Search by relevance
|
|
121
|
-
const matches = filteredCommands
|
|
122
|
-
.filter((entry) => {
|
|
123
|
-
// Command content matching
|
|
124
|
-
const command = entry.command.toLowerCase();
|
|
125
|
-
return command.includes(normalizedQuery);
|
|
126
|
-
})
|
|
127
|
-
.map((entry) => {
|
|
128
|
-
// Calculate match score
|
|
129
|
-
const command = entry.command.toLowerCase();
|
|
130
|
-
let score = 0;
|
|
131
|
-
// Exact match gets higher score
|
|
132
|
-
if (command.includes(normalizedQuery)) {
|
|
133
|
-
score += 10;
|
|
134
|
-
}
|
|
135
|
-
// Command prefix match gets higher score
|
|
136
|
-
if (command.startsWith(normalizedQuery)) {
|
|
137
|
-
score += 20;
|
|
138
|
-
}
|
|
139
|
-
// Word boundary match gets higher score
|
|
140
|
-
const words = command.split(/\s+/);
|
|
141
|
-
if (words.some((word) => word.startsWith(normalizedQuery))) {
|
|
142
|
-
score += 15;
|
|
143
|
-
}
|
|
144
|
-
// Timestamp influence (newer gets higher score)
|
|
145
|
-
score += entry.timestamp / 1000000; // Normalize timestamp
|
|
146
|
-
return { entry, score };
|
|
147
|
-
})
|
|
148
|
-
.sort((a, b) => b.score - a.score) // Sort by score descending
|
|
149
|
-
.map((item) => item.entry);
|
|
150
|
-
// Deduplicate search results, keep latest record
|
|
151
|
-
const dedupedMatches = deduplicateCommands(matches);
|
|
152
|
-
const result = dedupedMatches.slice(0, limit);
|
|
153
|
-
logger.debug("Bash history search results:", {
|
|
154
|
-
query,
|
|
155
|
-
workdir: process.cwd(),
|
|
156
|
-
originalCount: matches.length,
|
|
157
|
-
dedupedCount: result.length,
|
|
158
|
-
});
|
|
159
|
-
return result;
|
|
160
|
-
}
|
|
161
|
-
catch (error) {
|
|
162
|
-
logger.debug("Failed to search bash history:", error);
|
|
163
|
-
return [];
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
/**
|
|
167
|
-
* Deduplicate command list, keep latest record for each command
|
|
168
|
-
*/
|
|
169
|
-
const deduplicateCommands = (commands) => {
|
|
170
|
-
const commandMap = new Map();
|
|
171
|
-
// Iterate through all commands, keep latest record for each
|
|
172
|
-
for (const entry of commands) {
|
|
173
|
-
const existing = commandMap.get(entry.command);
|
|
174
|
-
if (!existing || entry.timestamp > existing.timestamp) {
|
|
175
|
-
commandMap.set(entry.command, entry);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
// Sort by timestamp and return (new to old)
|
|
179
|
-
return Array.from(commandMap.values()).sort((a, b) => b.timestamp - a.timestamp);
|
|
180
|
-
};
|
|
181
|
-
/**
|
|
182
|
-
* Get recently used bash commands
|
|
183
|
-
*/
|
|
184
|
-
export const getRecentBashCommands = (limit = 10) => {
|
|
185
|
-
try {
|
|
186
|
-
const history = loadBashHistory();
|
|
187
|
-
// Return recent commands after deduplication
|
|
188
|
-
const deduped = deduplicateCommands(history.commands);
|
|
189
|
-
return deduped.slice(0, limit); // Latest first
|
|
190
|
-
}
|
|
191
|
-
catch (error) {
|
|
192
|
-
logger.debug("Failed to get recent bash commands:", error);
|
|
193
|
-
return [];
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
/**
|
|
197
|
-
* Delete a specific command from bash history
|
|
198
|
-
*/
|
|
199
|
-
export const deleteBashCommandFromHistory = (command, workdir) => {
|
|
200
|
-
try {
|
|
201
|
-
const history = loadBashHistory();
|
|
202
|
-
const initialLength = history.commands.length;
|
|
203
|
-
history.commands = history.commands.filter((entry) => {
|
|
204
|
-
if (workdir) {
|
|
205
|
-
return !(entry.command === command && entry.workdir === workdir);
|
|
206
|
-
}
|
|
207
|
-
return entry.command !== command;
|
|
208
|
-
});
|
|
209
|
-
if (history.commands.length !== initialLength) {
|
|
210
|
-
saveBashHistory(history);
|
|
211
|
-
logger.debug("Deleted bash command from history:", { command, workdir });
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
catch (error) {
|
|
215
|
-
logger.debug("Failed to delete bash command from history:", error);
|
|
216
|
-
}
|
|
217
|
-
};
|
|
218
|
-
/**
|
|
219
|
-
* Clear bash history
|
|
220
|
-
*/
|
|
221
|
-
export const clearBashHistory = () => {
|
|
222
|
-
try {
|
|
223
|
-
const history = {
|
|
224
|
-
commands: [],
|
|
225
|
-
version: HISTORY_VERSION,
|
|
226
|
-
};
|
|
227
|
-
saveBashHistory(history);
|
|
228
|
-
logger.debug("Bash history cleared");
|
|
229
|
-
}
|
|
230
|
-
catch (error) {
|
|
231
|
-
logger.debug("Failed to clear bash history:", error);
|
|
232
|
-
}
|
|
233
|
-
};
|
|
234
|
-
/**
|
|
235
|
-
* Get bash command statistics
|
|
236
|
-
*/
|
|
237
|
-
export const getBashCommandStats = () => {
|
|
238
|
-
try {
|
|
239
|
-
const history = loadBashHistory();
|
|
240
|
-
const uniqueCommands = new Set(history.commands.map((entry) => entry.command));
|
|
241
|
-
const workdirs = Array.from(new Set(history.commands.map((entry) => entry.workdir)));
|
|
242
|
-
return {
|
|
243
|
-
totalCommands: history.commands.length,
|
|
244
|
-
uniqueCommands: uniqueCommands.size,
|
|
245
|
-
workdirs,
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
catch (error) {
|
|
249
|
-
logger.debug("Failed to get bash command stats:", error);
|
|
250
|
-
return {
|
|
251
|
-
totalCommands: 0,
|
|
252
|
-
uniqueCommands: 0,
|
|
253
|
-
workdirs: [],
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
};
|
package/src/utils/bashHistory.ts
DELETED
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Bash command history management module
|
|
3
|
-
* Used for persistent storage and searching of bash commands executed by users
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import fs from "fs";
|
|
7
|
-
import { BASH_HISTORY_FILE, DATA_DIRECTORY } from "./constants.js";
|
|
8
|
-
import { logger } from "./globalLogger.js";
|
|
9
|
-
|
|
10
|
-
export interface BashHistoryEntry {
|
|
11
|
-
command: string;
|
|
12
|
-
timestamp: number;
|
|
13
|
-
workdir: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface BashHistory {
|
|
17
|
-
commands: BashHistoryEntry[];
|
|
18
|
-
version: number;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const HISTORY_VERSION = 1;
|
|
22
|
-
const MAX_HISTORY_SIZE = 1000;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Ensure data directory exists
|
|
26
|
-
*/
|
|
27
|
-
const ensureDataDirectory = (): void => {
|
|
28
|
-
try {
|
|
29
|
-
if (!fs.existsSync(DATA_DIRECTORY)) {
|
|
30
|
-
fs.mkdirSync(DATA_DIRECTORY, { recursive: true });
|
|
31
|
-
}
|
|
32
|
-
} catch (error) {
|
|
33
|
-
logger.debug("Failed to create data directory:", error);
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Load bash history
|
|
39
|
-
*/
|
|
40
|
-
export const loadBashHistory = (): BashHistory => {
|
|
41
|
-
try {
|
|
42
|
-
ensureDataDirectory();
|
|
43
|
-
|
|
44
|
-
if (!fs.existsSync(BASH_HISTORY_FILE)) {
|
|
45
|
-
return {
|
|
46
|
-
commands: [],
|
|
47
|
-
version: HISTORY_VERSION,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const data = fs.readFileSync(BASH_HISTORY_FILE, "utf-8");
|
|
52
|
-
const history: BashHistory = JSON.parse(data);
|
|
53
|
-
|
|
54
|
-
// Version compatibility check
|
|
55
|
-
if (history.version !== HISTORY_VERSION) {
|
|
56
|
-
logger.debug("Bash history version mismatch, resetting history");
|
|
57
|
-
return {
|
|
58
|
-
commands: [],
|
|
59
|
-
version: HISTORY_VERSION,
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return history;
|
|
64
|
-
} catch (error) {
|
|
65
|
-
logger.debug("Failed to load bash history:", error);
|
|
66
|
-
return {
|
|
67
|
-
commands: [],
|
|
68
|
-
version: HISTORY_VERSION,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Save bash history
|
|
75
|
-
*/
|
|
76
|
-
export const saveBashHistory = (history: BashHistory): void => {
|
|
77
|
-
try {
|
|
78
|
-
// Skip saving to file when in test environment
|
|
79
|
-
if (process.env.NODE_ENV === "test") {
|
|
80
|
-
logger.debug("Skipping bash history save in test environment");
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
ensureDataDirectory();
|
|
85
|
-
|
|
86
|
-
// Limit history size
|
|
87
|
-
if (history.commands.length > MAX_HISTORY_SIZE) {
|
|
88
|
-
history.commands = history.commands.slice(-MAX_HISTORY_SIZE);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const data = JSON.stringify(history, null, 2);
|
|
92
|
-
fs.writeFileSync(BASH_HISTORY_FILE, data, "utf-8");
|
|
93
|
-
} catch (error) {
|
|
94
|
-
logger.debug("Failed to save bash history:", error);
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Add command to bash history
|
|
100
|
-
*/
|
|
101
|
-
export const addBashCommandToHistory = (
|
|
102
|
-
command: string,
|
|
103
|
-
workdir: string,
|
|
104
|
-
): void => {
|
|
105
|
-
try {
|
|
106
|
-
const history = loadBashHistory();
|
|
107
|
-
const timestamp = Date.now();
|
|
108
|
-
|
|
109
|
-
// Check if it's a duplicate consecutive command to avoid duplicate recording
|
|
110
|
-
const lastCommand = history.commands[history.commands.length - 1];
|
|
111
|
-
if (
|
|
112
|
-
lastCommand &&
|
|
113
|
-
lastCommand.command === command &&
|
|
114
|
-
lastCommand.workdir === workdir
|
|
115
|
-
) {
|
|
116
|
-
// Update timestamp of the last record
|
|
117
|
-
lastCommand.timestamp = timestamp;
|
|
118
|
-
} else {
|
|
119
|
-
// Add new command record
|
|
120
|
-
history.commands.push({
|
|
121
|
-
command,
|
|
122
|
-
timestamp,
|
|
123
|
-
workdir,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
saveBashHistory(history);
|
|
128
|
-
logger.debug("Added bash command to history:", { command, workdir });
|
|
129
|
-
} catch (error) {
|
|
130
|
-
logger.debug("Failed to add bash command to history:", error);
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Search bash history by keywords
|
|
136
|
-
*/
|
|
137
|
-
export const searchBashHistory = (
|
|
138
|
-
query: string,
|
|
139
|
-
limit: number = 10,
|
|
140
|
-
): BashHistoryEntry[] => {
|
|
141
|
-
try {
|
|
142
|
-
const history = loadBashHistory();
|
|
143
|
-
const normalizedQuery = query.toLowerCase().trim();
|
|
144
|
-
|
|
145
|
-
const filteredCommands = history.commands;
|
|
146
|
-
|
|
147
|
-
if (!normalizedQuery) {
|
|
148
|
-
// If no search query, return recent commands (deduplicated)
|
|
149
|
-
const deduped = deduplicateCommands(filteredCommands);
|
|
150
|
-
return deduped.slice(0, limit); // Latest first
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Search by relevance
|
|
154
|
-
const matches = filteredCommands
|
|
155
|
-
.filter((entry) => {
|
|
156
|
-
// Command content matching
|
|
157
|
-
const command = entry.command.toLowerCase();
|
|
158
|
-
return command.includes(normalizedQuery);
|
|
159
|
-
})
|
|
160
|
-
.map((entry) => {
|
|
161
|
-
// Calculate match score
|
|
162
|
-
const command = entry.command.toLowerCase();
|
|
163
|
-
let score = 0;
|
|
164
|
-
|
|
165
|
-
// Exact match gets higher score
|
|
166
|
-
if (command.includes(normalizedQuery)) {
|
|
167
|
-
score += 10;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Command prefix match gets higher score
|
|
171
|
-
if (command.startsWith(normalizedQuery)) {
|
|
172
|
-
score += 20;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Word boundary match gets higher score
|
|
176
|
-
const words = command.split(/\s+/);
|
|
177
|
-
if (words.some((word) => word.startsWith(normalizedQuery))) {
|
|
178
|
-
score += 15;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Timestamp influence (newer gets higher score)
|
|
182
|
-
score += entry.timestamp / 1000000; // Normalize timestamp
|
|
183
|
-
|
|
184
|
-
return { entry, score };
|
|
185
|
-
})
|
|
186
|
-
.sort((a, b) => b.score - a.score) // Sort by score descending
|
|
187
|
-
.map((item) => item.entry);
|
|
188
|
-
|
|
189
|
-
// Deduplicate search results, keep latest record
|
|
190
|
-
const dedupedMatches = deduplicateCommands(matches);
|
|
191
|
-
const result = dedupedMatches.slice(0, limit);
|
|
192
|
-
|
|
193
|
-
logger.debug("Bash history search results:", {
|
|
194
|
-
query,
|
|
195
|
-
workdir: process.cwd(),
|
|
196
|
-
originalCount: matches.length,
|
|
197
|
-
dedupedCount: result.length,
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
return result;
|
|
201
|
-
} catch (error) {
|
|
202
|
-
logger.debug("Failed to search bash history:", error);
|
|
203
|
-
return [];
|
|
204
|
-
}
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Deduplicate command list, keep latest record for each command
|
|
209
|
-
*/
|
|
210
|
-
const deduplicateCommands = (
|
|
211
|
-
commands: BashHistoryEntry[],
|
|
212
|
-
): BashHistoryEntry[] => {
|
|
213
|
-
const commandMap = new Map<string, BashHistoryEntry>();
|
|
214
|
-
|
|
215
|
-
// Iterate through all commands, keep latest record for each
|
|
216
|
-
for (const entry of commands) {
|
|
217
|
-
const existing = commandMap.get(entry.command);
|
|
218
|
-
if (!existing || entry.timestamp > existing.timestamp) {
|
|
219
|
-
commandMap.set(entry.command, entry);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Sort by timestamp and return (new to old)
|
|
224
|
-
return Array.from(commandMap.values()).sort(
|
|
225
|
-
(a, b) => b.timestamp - a.timestamp,
|
|
226
|
-
);
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Get recently used bash commands
|
|
231
|
-
*/
|
|
232
|
-
export const getRecentBashCommands = (
|
|
233
|
-
limit: number = 10,
|
|
234
|
-
): BashHistoryEntry[] => {
|
|
235
|
-
try {
|
|
236
|
-
const history = loadBashHistory();
|
|
237
|
-
|
|
238
|
-
// Return recent commands after deduplication
|
|
239
|
-
const deduped = deduplicateCommands(history.commands);
|
|
240
|
-
return deduped.slice(0, limit); // Latest first
|
|
241
|
-
} catch (error) {
|
|
242
|
-
logger.debug("Failed to get recent bash commands:", error);
|
|
243
|
-
return [];
|
|
244
|
-
}
|
|
245
|
-
};
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Delete a specific command from bash history
|
|
249
|
-
*/
|
|
250
|
-
export const deleteBashCommandFromHistory = (
|
|
251
|
-
command: string,
|
|
252
|
-
workdir?: string,
|
|
253
|
-
): void => {
|
|
254
|
-
try {
|
|
255
|
-
const history = loadBashHistory();
|
|
256
|
-
const initialLength = history.commands.length;
|
|
257
|
-
|
|
258
|
-
history.commands = history.commands.filter((entry) => {
|
|
259
|
-
if (workdir) {
|
|
260
|
-
return !(entry.command === command && entry.workdir === workdir);
|
|
261
|
-
}
|
|
262
|
-
return entry.command !== command;
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
if (history.commands.length !== initialLength) {
|
|
266
|
-
saveBashHistory(history);
|
|
267
|
-
logger.debug("Deleted bash command from history:", { command, workdir });
|
|
268
|
-
}
|
|
269
|
-
} catch (error) {
|
|
270
|
-
logger.debug("Failed to delete bash command from history:", error);
|
|
271
|
-
}
|
|
272
|
-
};
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Clear bash history
|
|
276
|
-
*/
|
|
277
|
-
export const clearBashHistory = (): void => {
|
|
278
|
-
try {
|
|
279
|
-
const history: BashHistory = {
|
|
280
|
-
commands: [],
|
|
281
|
-
version: HISTORY_VERSION,
|
|
282
|
-
};
|
|
283
|
-
saveBashHistory(history);
|
|
284
|
-
logger.debug("Bash history cleared");
|
|
285
|
-
} catch (error) {
|
|
286
|
-
logger.debug("Failed to clear bash history:", error);
|
|
287
|
-
}
|
|
288
|
-
};
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Get bash command statistics
|
|
292
|
-
*/
|
|
293
|
-
export const getBashCommandStats = (): {
|
|
294
|
-
totalCommands: number;
|
|
295
|
-
uniqueCommands: number;
|
|
296
|
-
workdirs: string[];
|
|
297
|
-
} => {
|
|
298
|
-
try {
|
|
299
|
-
const history = loadBashHistory();
|
|
300
|
-
const uniqueCommands = new Set(
|
|
301
|
-
history.commands.map((entry) => entry.command),
|
|
302
|
-
);
|
|
303
|
-
const workdirs = Array.from(
|
|
304
|
-
new Set(history.commands.map((entry) => entry.workdir)),
|
|
305
|
-
);
|
|
306
|
-
|
|
307
|
-
return {
|
|
308
|
-
totalCommands: history.commands.length,
|
|
309
|
-
uniqueCommands: uniqueCommands.size,
|
|
310
|
-
workdirs,
|
|
311
|
-
};
|
|
312
|
-
} catch (error) {
|
|
313
|
-
logger.debug("Failed to get bash command stats:", error);
|
|
314
|
-
return {
|
|
315
|
-
totalCommands: 0,
|
|
316
|
-
uniqueCommands: 0,
|
|
317
|
-
workdirs: [],
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
};
|