@within-7/minto 0.3.5 → 0.3.9
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/{cli.js → cli.cjs} +25 -23
- package/dist/commands/language.js +137 -0
- package/dist/commands/language.js.map +7 -0
- package/dist/commands/new.js +56 -0
- package/dist/commands/new.js.map +7 -0
- package/dist/commands/resume.js +251 -16
- package/dist/commands/resume.js.map +2 -2
- package/dist/commands/sessions.js +224 -0
- package/dist/commands/sessions.js.map +7 -0
- package/dist/commands/setup.js +3 -2
- package/dist/commands/setup.js.map +2 -2
- package/dist/commands/stats.js +235 -0
- package/dist/commands/stats.js.map +7 -0
- package/dist/commands/status.js +11 -5
- package/dist/commands/status.js.map +2 -2
- package/dist/commands/undo.js +26 -16
- package/dist/commands/undo.js.map +2 -2
- package/dist/commands.js +6 -0
- package/dist/commands.js.map +2 -2
- package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js +3 -2
- package/dist/components/AskUserQuestionDialog/AskUserQuestionDialog.js.map +2 -2
- package/dist/components/Config.js +9 -8
- package/dist/components/Config.js.map +2 -2
- package/dist/components/HeaderBar.js +2 -1
- package/dist/components/HeaderBar.js.map +2 -2
- package/dist/components/Help.js +2 -1
- package/dist/components/Help.js.map +2 -2
- package/dist/components/HotkeyHelpPanel.js +46 -44
- package/dist/components/HotkeyHelpPanel.js.map +2 -2
- package/dist/components/Logo.js +5 -2
- package/dist/components/Logo.js.map +2 -2
- package/dist/components/MCPServerApprovalDialog.js +6 -5
- package/dist/components/MCPServerApprovalDialog.js.map +2 -2
- package/dist/components/MCPServerMultiselectDialog.js +5 -4
- package/dist/components/MCPServerMultiselectDialog.js.map +2 -2
- package/dist/components/MessageSelector.js +4 -3
- package/dist/components/MessageSelector.js.map +2 -2
- package/dist/components/ModelConfig.js +13 -12
- package/dist/components/ModelConfig.js.map +2 -2
- package/dist/components/ModelListManager.js +4 -3
- package/dist/components/ModelListManager.js.map +2 -2
- package/dist/components/PromptInput.js +72 -39
- package/dist/components/PromptInput.js.map +2 -2
- package/dist/components/SensitiveFileWarning.js +12 -8
- package/dist/components/SensitiveFileWarning.js.map +2 -2
- package/dist/components/TabbedListView/ScrollableList.js +91 -0
- package/dist/components/TabbedListView/ScrollableList.js.map +7 -0
- package/dist/components/TabbedListView/SearchInput.js +23 -0
- package/dist/components/TabbedListView/SearchInput.js.map +7 -0
- package/dist/components/TabbedListView/TabBar.js +20 -0
- package/dist/components/TabbedListView/TabBar.js.map +7 -0
- package/dist/components/TabbedListView/TabbedListView.js +171 -0
- package/dist/components/TabbedListView/TabbedListView.js.map +7 -0
- package/dist/components/TabbedListView/index.js +11 -0
- package/dist/components/TabbedListView/index.js.map +7 -0
- package/dist/components/TabbedListView/types.js +1 -0
- package/dist/components/TabbedListView/types.js.map +7 -0
- package/dist/components/TodoChangeBlock.js +6 -5
- package/dist/components/TodoChangeBlock.js.map +3 -3
- package/dist/components/TodoPanel.js +6 -3
- package/dist/components/TodoPanel.js.map +3 -3
- package/dist/components/TrustDialog.js +6 -5
- package/dist/components/TrustDialog.js.map +2 -2
- package/dist/components/messages/UserToolResultMessage/UserToolCanceledMessage.js +2 -1
- package/dist/components/messages/UserToolResultMessage/UserToolCanceledMessage.js.map +2 -2
- package/dist/constants/macros.js +1 -1
- package/dist/constants/macros.js.map +1 -1
- package/dist/constants/product.js +2 -2
- package/dist/constants/product.js.map +1 -1
- package/dist/constants/prompts.js +17 -0
- package/dist/constants/prompts.js.map +2 -2
- package/dist/constants/toolInputExamples.js +5 -1
- package/dist/constants/toolInputExamples.js.map +2 -2
- package/dist/core/tokenStatsManager.js +5 -0
- package/dist/core/tokenStatsManager.js.map +2 -2
- package/dist/entrypoints/bootstrap.js +54 -0
- package/dist/entrypoints/bootstrap.js.map +7 -0
- package/dist/entrypoints/cli.js +132 -23
- package/dist/entrypoints/cli.js.map +3 -3
- package/dist/history.js +75 -15
- package/dist/history.js.map +2 -2
- package/dist/i18n/index.js +2 -2
- package/dist/i18n/index.js.map +2 -2
- package/dist/i18n/locales/en.js +283 -1
- package/dist/i18n/locales/en.js.map +2 -2
- package/dist/i18n/locales/zh-CN.js +283 -1
- package/dist/i18n/locales/zh-CN.js.map +2 -2
- package/dist/i18n/types.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +2 -2
- package/dist/messages.js +11 -0
- package/dist/messages.js.map +2 -2
- package/dist/permissions.js.map +2 -2
- package/dist/query.js +9 -0
- package/dist/query.js.map +2 -2
- package/dist/screens/REPL.js +45 -7
- package/dist/screens/REPL.js.map +2 -2
- package/dist/services/customCommands.js +14 -8
- package/dist/services/customCommands.js.map +2 -2
- package/dist/tools/TaskTool/TaskTool.js +176 -1
- package/dist/tools/TaskTool/TaskTool.js.map +2 -2
- package/dist/tools/TodoWriteTool/prompt.js +21 -0
- package/dist/tools/TodoWriteTool/prompt.js.map +2 -2
- package/dist/tools/URLFetcherTool/prompt.js +14 -9
- package/dist/tools/URLFetcherTool/prompt.js.map +2 -2
- package/dist/tools/WebSearchTool/prompt.js +12 -6
- package/dist/tools/WebSearchTool/prompt.js.map +2 -2
- package/dist/types/PermissionMode.js +30 -1
- package/dist/types/PermissionMode.js.map +2 -2
- package/dist/types/plugin.js.map +2 -2
- package/dist/utils/agentHookExecutor.js +106 -0
- package/dist/utils/agentHookExecutor.js.map +7 -0
- package/dist/utils/agentLoader.js +212 -26
- package/dist/utils/agentLoader.js.map +2 -2
- package/dist/utils/agentMemory.js +134 -0
- package/dist/utils/agentMemory.js.map +7 -0
- package/dist/utils/config.js +51 -1
- package/dist/utils/config.js.map +2 -2
- package/dist/utils/configPaths.js +199 -0
- package/dist/utils/configPaths.js.map +7 -0
- package/dist/utils/historyManager.js +234 -0
- package/dist/utils/historyManager.js.map +7 -0
- package/dist/utils/messages.js +13 -8
- package/dist/utils/messages.js.map +2 -2
- package/dist/utils/migration/index.js +37 -0
- package/dist/utils/migration/index.js.map +7 -0
- package/dist/utils/migration/migrateHistory.js +273 -0
- package/dist/utils/migration/migrateHistory.js.map +7 -0
- package/dist/utils/migration/migrateTodos.js +323 -0
- package/dist/utils/migration/migrateTodos.js.map +7 -0
- package/dist/utils/pasteCache.js +309 -0
- package/dist/utils/pasteCache.js.map +7 -0
- package/dist/utils/pluginLoader.js +6 -3
- package/dist/utils/pluginLoader.js.map +2 -2
- package/dist/utils/sessionIndex.js +192 -0
- package/dist/utils/sessionIndex.js.map +7 -0
- package/dist/utils/sessionTracker.js +170 -0
- package/dist/utils/sessionTracker.js.map +7 -0
- package/dist/utils/skillLoader.js +91 -5
- package/dist/utils/skillLoader.js.map +2 -2
- package/dist/utils/stats.js +417 -0
- package/dist/utils/stats.js.map +7 -0
- package/dist/utils/stringSubstitution.js +107 -0
- package/dist/utils/stringSubstitution.js.map +7 -0
- package/dist/utils/teamConfig.js +3 -1
- package/dist/utils/teamConfig.js.map +2 -2
- package/dist/utils/todoStorage.js +51 -19
- package/dist/utils/todoStorage.js.map +2 -2
- package/dist/utils/tooling/safeRender.js.map +2 -2
- package/dist/version.js +2 -2
- package/dist/version.js.map +1 -1
- package/package.json +71 -28
- package/scripts/{postinstall.js → postinstall.cjs} +1 -1
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { join } from "path";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { existsSync, mkdirSync } from "fs";
|
|
4
|
+
import { CONFIG_BASE_DIR } from "../constants/product.js";
|
|
5
|
+
function getConfigBaseDir() {
|
|
6
|
+
if (process.env.MINTO_CONFIG_DIR) {
|
|
7
|
+
return process.env.MINTO_CONFIG_DIR;
|
|
8
|
+
}
|
|
9
|
+
if (process.env.CLAUDE_CONFIG_DIR) {
|
|
10
|
+
return process.env.CLAUDE_CONFIG_DIR;
|
|
11
|
+
}
|
|
12
|
+
if (process.env.XDG_CONFIG_HOME) {
|
|
13
|
+
return join(process.env.XDG_CONFIG_HOME, "minto");
|
|
14
|
+
}
|
|
15
|
+
return join(homedir(), CONFIG_BASE_DIR);
|
|
16
|
+
}
|
|
17
|
+
const CONFIG_PATHS = {
|
|
18
|
+
/** Base directory for all Minto configuration and data */
|
|
19
|
+
get base() {
|
|
20
|
+
return getConfigBaseDir();
|
|
21
|
+
},
|
|
22
|
+
// ============================================
|
|
23
|
+
// Configuration Files Directory
|
|
24
|
+
// ============================================
|
|
25
|
+
/** Configuration files directory */
|
|
26
|
+
get config() {
|
|
27
|
+
return join(this.base, "config");
|
|
28
|
+
},
|
|
29
|
+
/** User settings configuration file */
|
|
30
|
+
get settingsFile() {
|
|
31
|
+
return join(this.config, "settings.json");
|
|
32
|
+
},
|
|
33
|
+
/** Model profiles configuration file */
|
|
34
|
+
get modelsFile() {
|
|
35
|
+
return join(this.config, "models.json");
|
|
36
|
+
},
|
|
37
|
+
/** MCP servers configuration file */
|
|
38
|
+
get mcpServersFile() {
|
|
39
|
+
return join(this.config, "mcp-servers.json");
|
|
40
|
+
},
|
|
41
|
+
/** Projects configuration file */
|
|
42
|
+
get projectsFile() {
|
|
43
|
+
return join(this.config, "projects.json");
|
|
44
|
+
},
|
|
45
|
+
// ============================================
|
|
46
|
+
// History Directory
|
|
47
|
+
// ============================================
|
|
48
|
+
/** History directory */
|
|
49
|
+
get history() {
|
|
50
|
+
return join(this.base, "history");
|
|
51
|
+
},
|
|
52
|
+
/** Global command history file (JSONL format) */
|
|
53
|
+
get globalHistoryFile() {
|
|
54
|
+
return join(this.history, "global.jsonl");
|
|
55
|
+
},
|
|
56
|
+
/** Paste cache directory */
|
|
57
|
+
get pasteCache() {
|
|
58
|
+
return join(this.history, "paste-cache");
|
|
59
|
+
},
|
|
60
|
+
// ============================================
|
|
61
|
+
// Sessions Directory
|
|
62
|
+
// ============================================
|
|
63
|
+
/** Sessions directory */
|
|
64
|
+
get sessions() {
|
|
65
|
+
return join(this.base, "sessions");
|
|
66
|
+
},
|
|
67
|
+
/** Session index file */
|
|
68
|
+
get sessionIndexFile() {
|
|
69
|
+
return join(this.sessions, "index.json");
|
|
70
|
+
},
|
|
71
|
+
// ============================================
|
|
72
|
+
// Statistics Directory
|
|
73
|
+
// ============================================
|
|
74
|
+
/** Statistics directory */
|
|
75
|
+
get stats() {
|
|
76
|
+
return join(this.base, "stats");
|
|
77
|
+
},
|
|
78
|
+
/** Activity statistics file */
|
|
79
|
+
get activityFile() {
|
|
80
|
+
return join(this.stats, "activity.json");
|
|
81
|
+
},
|
|
82
|
+
// ============================================
|
|
83
|
+
// Backups Directory
|
|
84
|
+
// ============================================
|
|
85
|
+
/** Backups directory */
|
|
86
|
+
get backups() {
|
|
87
|
+
return join(this.base, "backups");
|
|
88
|
+
},
|
|
89
|
+
// ============================================
|
|
90
|
+
// Logs Directory
|
|
91
|
+
// ============================================
|
|
92
|
+
/** Logs directory */
|
|
93
|
+
get logs() {
|
|
94
|
+
return join(this.base, "logs");
|
|
95
|
+
},
|
|
96
|
+
/** Debug logs directory */
|
|
97
|
+
get debugLogs() {
|
|
98
|
+
return join(this.logs, "debug");
|
|
99
|
+
},
|
|
100
|
+
/** Error logs directory */
|
|
101
|
+
get errorLogs() {
|
|
102
|
+
return join(this.logs, "error");
|
|
103
|
+
},
|
|
104
|
+
// ============================================
|
|
105
|
+
// Agents Directory
|
|
106
|
+
// ============================================
|
|
107
|
+
/** User-defined agents directory */
|
|
108
|
+
get agents() {
|
|
109
|
+
return join(this.base, "agents");
|
|
110
|
+
},
|
|
111
|
+
// ============================================
|
|
112
|
+
// Memory Directory (existing)
|
|
113
|
+
// ============================================
|
|
114
|
+
/** Memory storage directory */
|
|
115
|
+
get memory() {
|
|
116
|
+
return join(this.base, "memory");
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
const REQUIRED_DIRECTORIES = [
|
|
120
|
+
() => CONFIG_PATHS.base,
|
|
121
|
+
() => CONFIG_PATHS.config,
|
|
122
|
+
() => CONFIG_PATHS.history,
|
|
123
|
+
() => CONFIG_PATHS.pasteCache,
|
|
124
|
+
() => CONFIG_PATHS.sessions,
|
|
125
|
+
() => CONFIG_PATHS.stats,
|
|
126
|
+
() => CONFIG_PATHS.backups,
|
|
127
|
+
() => CONFIG_PATHS.logs,
|
|
128
|
+
() => CONFIG_PATHS.debugLogs,
|
|
129
|
+
() => CONFIG_PATHS.errorLogs,
|
|
130
|
+
() => CONFIG_PATHS.agents,
|
|
131
|
+
() => CONFIG_PATHS.memory
|
|
132
|
+
];
|
|
133
|
+
function ensureConfigDirs() {
|
|
134
|
+
const result = {
|
|
135
|
+
created: [],
|
|
136
|
+
existing: [],
|
|
137
|
+
errors: []
|
|
138
|
+
};
|
|
139
|
+
for (const getDirPath of REQUIRED_DIRECTORIES) {
|
|
140
|
+
const dirPath = getDirPath();
|
|
141
|
+
try {
|
|
142
|
+
if (existsSync(dirPath)) {
|
|
143
|
+
result.existing.push(dirPath);
|
|
144
|
+
} else {
|
|
145
|
+
mkdirSync(dirPath, { recursive: true });
|
|
146
|
+
result.created.push(dirPath);
|
|
147
|
+
}
|
|
148
|
+
} catch (error) {
|
|
149
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
150
|
+
const errno = error?.code;
|
|
151
|
+
if (errno === "EACCES" || errno === "EPERM" || errno === "EROFS") {
|
|
152
|
+
result.errors.push({
|
|
153
|
+
path: dirPath,
|
|
154
|
+
error: `Permission denied: ${errorMessage}`
|
|
155
|
+
});
|
|
156
|
+
} else {
|
|
157
|
+
result.errors.push({
|
|
158
|
+
path: dirPath,
|
|
159
|
+
error: errorMessage
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
function checkConfigDirs() {
|
|
167
|
+
return REQUIRED_DIRECTORIES.every((getDirPath) => existsSync(getDirPath()));
|
|
168
|
+
}
|
|
169
|
+
function getConfigPathsSummary() {
|
|
170
|
+
return {
|
|
171
|
+
base: CONFIG_PATHS.base,
|
|
172
|
+
config: CONFIG_PATHS.config,
|
|
173
|
+
settingsFile: CONFIG_PATHS.settingsFile,
|
|
174
|
+
modelsFile: CONFIG_PATHS.modelsFile,
|
|
175
|
+
mcpServersFile: CONFIG_PATHS.mcpServersFile,
|
|
176
|
+
projectsFile: CONFIG_PATHS.projectsFile,
|
|
177
|
+
history: CONFIG_PATHS.history,
|
|
178
|
+
globalHistoryFile: CONFIG_PATHS.globalHistoryFile,
|
|
179
|
+
pasteCache: CONFIG_PATHS.pasteCache,
|
|
180
|
+
sessions: CONFIG_PATHS.sessions,
|
|
181
|
+
sessionIndexFile: CONFIG_PATHS.sessionIndexFile,
|
|
182
|
+
stats: CONFIG_PATHS.stats,
|
|
183
|
+
activityFile: CONFIG_PATHS.activityFile,
|
|
184
|
+
backups: CONFIG_PATHS.backups,
|
|
185
|
+
logs: CONFIG_PATHS.logs,
|
|
186
|
+
debugLogs: CONFIG_PATHS.debugLogs,
|
|
187
|
+
errorLogs: CONFIG_PATHS.errorLogs,
|
|
188
|
+
agents: CONFIG_PATHS.agents,
|
|
189
|
+
memory: CONFIG_PATHS.memory
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
export {
|
|
193
|
+
CONFIG_PATHS,
|
|
194
|
+
checkConfigDirs,
|
|
195
|
+
ensureConfigDirs,
|
|
196
|
+
getConfigBaseDir,
|
|
197
|
+
getConfigPathsSummary
|
|
198
|
+
};
|
|
199
|
+
//# sourceMappingURL=configPaths.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/utils/configPaths.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Configuration Paths Module\n *\n * Defines the standardized directory structure for Minto's configuration and data files.\n * All paths are centralized here to ensure consistency across the application.\n *\n * Directory Structure:\n * ~/.minto/\n * \u251C\u2500\u2500 config/ # Configuration files\n * \u2502 \u251C\u2500\u2500 settings.json # User settings\n * \u2502 \u251C\u2500\u2500 models.json # Model profiles\n * \u2502 \u251C\u2500\u2500 mcp-servers.json # MCP server configurations\n * \u2502 \u2514\u2500\u2500 projects.json # Project-specific configs\n * \u251C\u2500\u2500 history/ # Command history\n * \u2502 \u251C\u2500\u2500 global.jsonl # Global command history\n * \u2502 \u2514\u2500\u2500 paste-cache/ # Clipboard paste cache\n * \u251C\u2500\u2500 sessions/ # Session data\n * \u2502 \u2514\u2500\u2500 index.json # Session index\n * \u251C\u2500\u2500 stats/ # Usage statistics\n * \u2502 \u2514\u2500\u2500 activity.json # Activity tracking\n * \u251C\u2500\u2500 backups/ # File backups\n * \u251C\u2500\u2500 logs/ # Log files\n * \u2502 \u251C\u2500\u2500 debug/ # Debug logs\n * \u2502 \u2514\u2500\u2500 error/ # Error logs\n * \u2514\u2500\u2500 agents/ # User-defined agents\n */\n\nimport { join } from 'path'\nimport { homedir } from 'os'\nimport { existsSync, mkdirSync } from 'fs'\nimport { CONFIG_BASE_DIR } from '@constants/product'\n\n/**\n * Get the base configuration directory.\n * Supports XDG_CONFIG_HOME environment variable override.\n *\n * Priority:\n * 1. MINTO_CONFIG_DIR - Explicit Minto config directory\n * 2. CLAUDE_CONFIG_DIR - Claude Code compatibility\n * 3. XDG_CONFIG_HOME/minto - XDG Base Directory specification\n * 4. ~/.minto - Default fallback\n */\nexport function getConfigBaseDir(): string {\n // Check for explicit Minto config directory\n if (process.env.MINTO_CONFIG_DIR) {\n return process.env.MINTO_CONFIG_DIR\n }\n\n // Check for Claude Code compatibility\n if (process.env.CLAUDE_CONFIG_DIR) {\n return process.env.CLAUDE_CONFIG_DIR\n }\n\n // Check for XDG Base Directory specification\n if (process.env.XDG_CONFIG_HOME) {\n return join(process.env.XDG_CONFIG_HOME, 'minto')\n }\n\n // Default to ~/.minto\n return join(homedir(), CONFIG_BASE_DIR)\n}\n\n/**\n * Configuration paths constant object.\n * All paths are lazily computed to ensure environment variables are resolved at runtime.\n */\nexport const CONFIG_PATHS = {\n /** Base directory for all Minto configuration and data */\n get base(): string {\n return getConfigBaseDir()\n },\n\n // ============================================\n // Configuration Files Directory\n // ============================================\n\n /** Configuration files directory */\n get config(): string {\n return join(this.base, 'config')\n },\n\n /** User settings configuration file */\n get settingsFile(): string {\n return join(this.config, 'settings.json')\n },\n\n /** Model profiles configuration file */\n get modelsFile(): string {\n return join(this.config, 'models.json')\n },\n\n /** MCP servers configuration file */\n get mcpServersFile(): string {\n return join(this.config, 'mcp-servers.json')\n },\n\n /** Projects configuration file */\n get projectsFile(): string {\n return join(this.config, 'projects.json')\n },\n\n // ============================================\n // History Directory\n // ============================================\n\n /** History directory */\n get history(): string {\n return join(this.base, 'history')\n },\n\n /** Global command history file (JSONL format) */\n get globalHistoryFile(): string {\n return join(this.history, 'global.jsonl')\n },\n\n /** Paste cache directory */\n get pasteCache(): string {\n return join(this.history, 'paste-cache')\n },\n\n // ============================================\n // Sessions Directory\n // ============================================\n\n /** Sessions directory */\n get sessions(): string {\n return join(this.base, 'sessions')\n },\n\n /** Session index file */\n get sessionIndexFile(): string {\n return join(this.sessions, 'index.json')\n },\n\n // ============================================\n // Statistics Directory\n // ============================================\n\n /** Statistics directory */\n get stats(): string {\n return join(this.base, 'stats')\n },\n\n /** Activity statistics file */\n get activityFile(): string {\n return join(this.stats, 'activity.json')\n },\n\n // ============================================\n // Backups Directory\n // ============================================\n\n /** Backups directory */\n get backups(): string {\n return join(this.base, 'backups')\n },\n\n // ============================================\n // Logs Directory\n // ============================================\n\n /** Logs directory */\n get logs(): string {\n return join(this.base, 'logs')\n },\n\n /** Debug logs directory */\n get debugLogs(): string {\n return join(this.logs, 'debug')\n },\n\n /** Error logs directory */\n get errorLogs(): string {\n return join(this.logs, 'error')\n },\n\n // ============================================\n // Agents Directory\n // ============================================\n\n /** User-defined agents directory */\n get agents(): string {\n return join(this.base, 'agents')\n },\n\n // ============================================\n // Memory Directory (existing)\n // ============================================\n\n /** Memory storage directory */\n get memory(): string {\n return join(this.base, 'memory')\n },\n} as const\n\n/**\n * List of all directories that need to be created during initialization.\n * Order matters for nested directories.\n */\nconst REQUIRED_DIRECTORIES = [\n () => CONFIG_PATHS.base,\n () => CONFIG_PATHS.config,\n () => CONFIG_PATHS.history,\n () => CONFIG_PATHS.pasteCache,\n () => CONFIG_PATHS.sessions,\n () => CONFIG_PATHS.stats,\n () => CONFIG_PATHS.backups,\n () => CONFIG_PATHS.logs,\n () => CONFIG_PATHS.debugLogs,\n () => CONFIG_PATHS.errorLogs,\n () => CONFIG_PATHS.agents,\n () => CONFIG_PATHS.memory,\n]\n\n/**\n * Initialize all required configuration directories.\n *\n * Creates the directory structure if it doesn't exist:\n * - ~/.minto/config/\n * - ~/.minto/history/\n * - ~/.minto/history/paste-cache/\n * - ~/.minto/sessions/\n * - ~/.minto/stats/\n * - ~/.minto/backups/\n * - ~/.minto/logs/debug/\n * - ~/.minto/logs/error/\n * - ~/.minto/agents/\n * - ~/.minto/memory/\n *\n * @returns Object with creation status and any errors\n */\nexport function ensureConfigDirs(): {\n created: string[]\n existing: string[]\n errors: Array<{ path: string; error: string }>\n} {\n const result = {\n created: [] as string[],\n existing: [] as string[],\n errors: [] as Array<{ path: string; error: string }>,\n }\n\n for (const getDirPath of REQUIRED_DIRECTORIES) {\n const dirPath = getDirPath()\n\n try {\n if (existsSync(dirPath)) {\n result.existing.push(dirPath)\n } else {\n mkdirSync(dirPath, { recursive: true })\n result.created.push(dirPath)\n }\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error)\n\n // Handle permission errors gracefully\n const errno = (error as NodeJS.ErrnoException)?.code\n if (errno === 'EACCES' || errno === 'EPERM' || errno === 'EROFS') {\n // Permission error - log but don't fail\n result.errors.push({\n path: dirPath,\n error: `Permission denied: ${errorMessage}`,\n })\n } else {\n // Other errors\n result.errors.push({\n path: dirPath,\n error: errorMessage,\n })\n }\n }\n }\n\n return result\n}\n\n/**\n * Check if all required directories exist.\n *\n * @returns true if all directories exist, false otherwise\n */\nexport function checkConfigDirs(): boolean {\n return REQUIRED_DIRECTORIES.every(getDirPath => existsSync(getDirPath()))\n}\n\n/**\n * Get a summary of the current configuration paths.\n * Useful for debugging and diagnostics.\n *\n * @returns Object with all resolved paths\n */\nexport function getConfigPathsSummary(): Record<string, string> {\n return {\n base: CONFIG_PATHS.base,\n config: CONFIG_PATHS.config,\n settingsFile: CONFIG_PATHS.settingsFile,\n modelsFile: CONFIG_PATHS.modelsFile,\n mcpServersFile: CONFIG_PATHS.mcpServersFile,\n projectsFile: CONFIG_PATHS.projectsFile,\n history: CONFIG_PATHS.history,\n globalHistoryFile: CONFIG_PATHS.globalHistoryFile,\n pasteCache: CONFIG_PATHS.pasteCache,\n sessions: CONFIG_PATHS.sessions,\n sessionIndexFile: CONFIG_PATHS.sessionIndexFile,\n stats: CONFIG_PATHS.stats,\n activityFile: CONFIG_PATHS.activityFile,\n backups: CONFIG_PATHS.backups,\n logs: CONFIG_PATHS.logs,\n debugLogs: CONFIG_PATHS.debugLogs,\n errorLogs: CONFIG_PATHS.errorLogs,\n agents: CONFIG_PATHS.agents,\n memory: CONFIG_PATHS.memory,\n }\n}\n"],
|
|
5
|
+
"mappings": "AA2BA,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,SAAS,YAAY,iBAAiB;AACtC,SAAS,uBAAuB;AAYzB,SAAS,mBAA2B;AAEzC,MAAI,QAAQ,IAAI,kBAAkB;AAChC,WAAO,QAAQ,IAAI;AAAA,EACrB;AAGA,MAAI,QAAQ,IAAI,mBAAmB;AACjC,WAAO,QAAQ,IAAI;AAAA,EACrB;AAGA,MAAI,QAAQ,IAAI,iBAAiB;AAC/B,WAAO,KAAK,QAAQ,IAAI,iBAAiB,OAAO;AAAA,EAClD;AAGA,SAAO,KAAK,QAAQ,GAAG,eAAe;AACxC;AAMO,MAAM,eAAe;AAAA;AAAA,EAE1B,IAAI,OAAe;AACjB,WAAO,iBAAiB;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,SAAiB;AACnB,WAAO,KAAK,KAAK,MAAM,QAAQ;AAAA,EACjC;AAAA;AAAA,EAGA,IAAI,eAAuB;AACzB,WAAO,KAAK,KAAK,QAAQ,eAAe;AAAA,EAC1C;AAAA;AAAA,EAGA,IAAI,aAAqB;AACvB,WAAO,KAAK,KAAK,QAAQ,aAAa;AAAA,EACxC;AAAA;AAAA,EAGA,IAAI,iBAAyB;AAC3B,WAAO,KAAK,KAAK,QAAQ,kBAAkB;AAAA,EAC7C;AAAA;AAAA,EAGA,IAAI,eAAuB;AACzB,WAAO,KAAK,KAAK,QAAQ,eAAe;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,UAAkB;AACpB,WAAO,KAAK,KAAK,MAAM,SAAS;AAAA,EAClC;AAAA;AAAA,EAGA,IAAI,oBAA4B;AAC9B,WAAO,KAAK,KAAK,SAAS,cAAc;AAAA,EAC1C;AAAA;AAAA,EAGA,IAAI,aAAqB;AACvB,WAAO,KAAK,KAAK,SAAS,aAAa;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,WAAmB;AACrB,WAAO,KAAK,KAAK,MAAM,UAAU;AAAA,EACnC;AAAA;AAAA,EAGA,IAAI,mBAA2B;AAC7B,WAAO,KAAK,KAAK,UAAU,YAAY;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,QAAgB;AAClB,WAAO,KAAK,KAAK,MAAM,OAAO;AAAA,EAChC;AAAA;AAAA,EAGA,IAAI,eAAuB;AACzB,WAAO,KAAK,KAAK,OAAO,eAAe;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,UAAkB;AACpB,WAAO,KAAK,KAAK,MAAM,SAAS;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,OAAe;AACjB,WAAO,KAAK,KAAK,MAAM,MAAM;AAAA,EAC/B;AAAA;AAAA,EAGA,IAAI,YAAoB;AACtB,WAAO,KAAK,KAAK,MAAM,OAAO;AAAA,EAChC;AAAA;AAAA,EAGA,IAAI,YAAoB;AACtB,WAAO,KAAK,KAAK,MAAM,OAAO;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,SAAiB;AACnB,WAAO,KAAK,KAAK,MAAM,QAAQ;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,SAAiB;AACnB,WAAO,KAAK,KAAK,MAAM,QAAQ;AAAA,EACjC;AACF;AAMA,MAAM,uBAAuB;AAAA,EAC3B,MAAM,aAAa;AAAA,EACnB,MAAM,aAAa;AAAA,EACnB,MAAM,aAAa;AAAA,EACnB,MAAM,aAAa;AAAA,EACnB,MAAM,aAAa;AAAA,EACnB,MAAM,aAAa;AAAA,EACnB,MAAM,aAAa;AAAA,EACnB,MAAM,aAAa;AAAA,EACnB,MAAM,aAAa;AAAA,EACnB,MAAM,aAAa;AAAA,EACnB,MAAM,aAAa;AAAA,EACnB,MAAM,aAAa;AACrB;AAmBO,SAAS,mBAId;AACA,QAAM,SAAS;AAAA,IACb,SAAS,CAAC;AAAA,IACV,UAAU,CAAC;AAAA,IACX,QAAQ,CAAC;AAAA,EACX;AAEA,aAAW,cAAc,sBAAsB;AAC7C,UAAM,UAAU,WAAW;AAE3B,QAAI;AACF,UAAI,WAAW,OAAO,GAAG;AACvB,eAAO,SAAS,KAAK,OAAO;AAAA,MAC9B,OAAO;AACL,kBAAU,SAAS,EAAE,WAAW,KAAK,CAAC;AACtC,eAAO,QAAQ,KAAK,OAAO;AAAA,MAC7B;AAAA,IACF,SAAS,OAAO;AACd,YAAM,eACJ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAGvD,YAAM,QAAS,OAAiC;AAChD,UAAI,UAAU,YAAY,UAAU,WAAW,UAAU,SAAS;AAEhE,eAAO,OAAO,KAAK;AAAA,UACjB,MAAM;AAAA,UACN,OAAO,sBAAsB,YAAY;AAAA,QAC3C,CAAC;AAAA,MACH,OAAO;AAEL,eAAO,OAAO,KAAK;AAAA,UACjB,MAAM;AAAA,UACN,OAAO;AAAA,QACT,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAOO,SAAS,kBAA2B;AACzC,SAAO,qBAAqB,MAAM,gBAAc,WAAW,WAAW,CAAC,CAAC;AAC1E;AAQO,SAAS,wBAAgD;AAC9D,SAAO;AAAA,IACL,MAAM,aAAa;AAAA,IACnB,QAAQ,aAAa;AAAA,IACrB,cAAc,aAAa;AAAA,IAC3B,YAAY,aAAa;AAAA,IACzB,gBAAgB,aAAa;AAAA,IAC7B,cAAc,aAAa;AAAA,IAC3B,SAAS,aAAa;AAAA,IACtB,mBAAmB,aAAa;AAAA,IAChC,YAAY,aAAa;AAAA,IACzB,UAAU,aAAa;AAAA,IACvB,kBAAkB,aAAa;AAAA,IAC/B,OAAO,aAAa;AAAA,IACpB,cAAc,aAAa;AAAA,IAC3B,SAAS,aAAa;AAAA,IACtB,MAAM,aAAa;AAAA,IACnB,WAAW,aAAa;AAAA,IACxB,WAAW,aAAa;AAAA,IACxB,QAAQ,aAAa;AAAA,IACrB,QAAQ,aAAa;AAAA,EACvB;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
appendFileSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
writeFileSync,
|
|
6
|
+
mkdirSync
|
|
7
|
+
} from "fs";
|
|
8
|
+
import { dirname, resolve } from "path";
|
|
9
|
+
import { CONFIG_PATHS } from "./configPaths.js";
|
|
10
|
+
import { sanitizeInput, containsDangerousSequences } from "./sanitizeInput.js";
|
|
11
|
+
import { debug as debugLogger } from "./debugLogger.js";
|
|
12
|
+
import { getCwd } from "./state.js";
|
|
13
|
+
const DEFAULT_LIMIT = 100;
|
|
14
|
+
const MAX_HISTORY_ENTRIES = 1e4;
|
|
15
|
+
let historyCache = null;
|
|
16
|
+
let cacheTimestamp = 0;
|
|
17
|
+
const CACHE_TTL = 5e3;
|
|
18
|
+
function invalidateCache() {
|
|
19
|
+
historyCache = null;
|
|
20
|
+
cacheTimestamp = 0;
|
|
21
|
+
}
|
|
22
|
+
function getHistoryFilePath() {
|
|
23
|
+
return CONFIG_PATHS.globalHistoryFile;
|
|
24
|
+
}
|
|
25
|
+
function ensureHistoryDir() {
|
|
26
|
+
const historyDir = dirname(getHistoryFilePath());
|
|
27
|
+
if (!existsSync(historyDir)) {
|
|
28
|
+
mkdirSync(historyDir, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function appendHistoryEntry(entry) {
|
|
32
|
+
try {
|
|
33
|
+
ensureHistoryDir();
|
|
34
|
+
const line = JSON.stringify(entry) + "\n";
|
|
35
|
+
appendFileSync(getHistoryFilePath(), line, "utf-8");
|
|
36
|
+
historyCache = null;
|
|
37
|
+
debugLogger.trace("HISTORY_ENTRY_APPENDED", {
|
|
38
|
+
command: entry.command.slice(0, 50),
|
|
39
|
+
projectPath: entry.projectPath
|
|
40
|
+
});
|
|
41
|
+
} catch (error) {
|
|
42
|
+
debugLogger.error("HISTORY_APPEND_FAILED", {
|
|
43
|
+
error: error instanceof Error ? error.message : String(error)
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function addToHistoryNew(command, mode = "prompt") {
|
|
48
|
+
const sanitizedCommand = sanitizeInput(command);
|
|
49
|
+
if (!sanitizedCommand.trim()) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (containsDangerousSequences(command)) {
|
|
53
|
+
debugLogger.warn("HISTORY_DANGEROUS_SEQUENCE", {
|
|
54
|
+
command: command.slice(0, 50)
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
let projectPath;
|
|
58
|
+
try {
|
|
59
|
+
projectPath = getCwd();
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
const entry = {
|
|
63
|
+
command: sanitizedCommand,
|
|
64
|
+
timestamp: Date.now(),
|
|
65
|
+
projectPath,
|
|
66
|
+
mode
|
|
67
|
+
};
|
|
68
|
+
const recent = readHistoryEntries({ limit: 1 });
|
|
69
|
+
if (recent.length > 0 && recent[0].command === sanitizedCommand) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
appendHistoryEntry(entry);
|
|
73
|
+
}
|
|
74
|
+
function readAllHistoryEntries() {
|
|
75
|
+
if (historyCache && Date.now() - cacheTimestamp < CACHE_TTL) {
|
|
76
|
+
return historyCache;
|
|
77
|
+
}
|
|
78
|
+
const filePath = getHistoryFilePath();
|
|
79
|
+
if (!existsSync(filePath)) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const content = readFileSync(filePath, "utf-8");
|
|
84
|
+
const lines = content.split("\n").filter((line) => line.trim());
|
|
85
|
+
const entries = [];
|
|
86
|
+
for (const line of lines) {
|
|
87
|
+
try {
|
|
88
|
+
const entry = JSON.parse(line);
|
|
89
|
+
entries.push(entry);
|
|
90
|
+
} catch {
|
|
91
|
+
debugLogger.trace("HISTORY_INVALID_LINE", { line: line.slice(0, 50) });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
historyCache = entries;
|
|
95
|
+
cacheTimestamp = Date.now();
|
|
96
|
+
return entries;
|
|
97
|
+
} catch (error) {
|
|
98
|
+
debugLogger.error("HISTORY_READ_FAILED", {
|
|
99
|
+
error: error instanceof Error ? error.message : String(error)
|
|
100
|
+
});
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function readHistoryEntries(options = {}) {
|
|
105
|
+
const { limit = DEFAULT_LIMIT, projectPath, mode, since } = options;
|
|
106
|
+
let entries = readAllHistoryEntries();
|
|
107
|
+
if (projectPath) {
|
|
108
|
+
const normalizedPath = resolve(projectPath).toLowerCase();
|
|
109
|
+
entries = entries.filter(
|
|
110
|
+
(e) => e.projectPath && resolve(e.projectPath).toLowerCase() === normalizedPath
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
if (mode) {
|
|
114
|
+
entries = entries.filter((e) => e.mode === mode);
|
|
115
|
+
}
|
|
116
|
+
if (since) {
|
|
117
|
+
entries = entries.filter((e) => e.timestamp > since);
|
|
118
|
+
}
|
|
119
|
+
return entries.sort((a, b) => b.timestamp - a.timestamp).slice(0, limit);
|
|
120
|
+
}
|
|
121
|
+
function getHistoryStrings(options = {}) {
|
|
122
|
+
return readHistoryEntries(options).map((e) => e.command);
|
|
123
|
+
}
|
|
124
|
+
function getCurrentProjectHistory(limit = DEFAULT_LIMIT) {
|
|
125
|
+
let projectPath;
|
|
126
|
+
try {
|
|
127
|
+
projectPath = getCwd();
|
|
128
|
+
} catch {
|
|
129
|
+
return getHistoryStrings({ limit });
|
|
130
|
+
}
|
|
131
|
+
return getHistoryStrings({ limit, projectPath });
|
|
132
|
+
}
|
|
133
|
+
function searchHistory(searchTerm, options = {}) {
|
|
134
|
+
const term = searchTerm.toLowerCase();
|
|
135
|
+
const entries = readHistoryEntries({ ...options, limit: MAX_HISTORY_ENTRIES });
|
|
136
|
+
return entries.filter((e) => e.command.toLowerCase().includes(term)).slice(0, options.limit || DEFAULT_LIMIT);
|
|
137
|
+
}
|
|
138
|
+
function rotateHistoryIfNeeded() {
|
|
139
|
+
const entries = readAllHistoryEntries();
|
|
140
|
+
if (entries.length <= MAX_HISTORY_ENTRIES) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
debugLogger.info("HISTORY_ROTATION_STARTED", {
|
|
144
|
+
currentEntries: entries.length,
|
|
145
|
+
maxEntries: MAX_HISTORY_ENTRIES
|
|
146
|
+
});
|
|
147
|
+
const keepEntries = entries.sort((a, b) => b.timestamp - a.timestamp).slice(0, MAX_HISTORY_ENTRIES);
|
|
148
|
+
keepEntries.sort((a, b) => a.timestamp - b.timestamp);
|
|
149
|
+
try {
|
|
150
|
+
const filePath = getHistoryFilePath();
|
|
151
|
+
const content = keepEntries.map((e) => JSON.stringify(e)).join("\n") + "\n";
|
|
152
|
+
writeFileSync(filePath, content, "utf-8");
|
|
153
|
+
historyCache = null;
|
|
154
|
+
debugLogger.info("HISTORY_ROTATION_COMPLETE", {
|
|
155
|
+
removedEntries: entries.length - keepEntries.length,
|
|
156
|
+
keptEntries: keepEntries.length
|
|
157
|
+
});
|
|
158
|
+
} catch (error) {
|
|
159
|
+
debugLogger.error("HISTORY_ROTATION_FAILED", {
|
|
160
|
+
error: error instanceof Error ? error.message : String(error)
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function clearHistory() {
|
|
165
|
+
const filePath = getHistoryFilePath();
|
|
166
|
+
if (existsSync(filePath)) {
|
|
167
|
+
try {
|
|
168
|
+
writeFileSync(filePath, "", "utf-8");
|
|
169
|
+
historyCache = null;
|
|
170
|
+
debugLogger.info("HISTORY_CLEARED", {});
|
|
171
|
+
} catch (error) {
|
|
172
|
+
debugLogger.error("HISTORY_CLEAR_FAILED", {
|
|
173
|
+
error: error instanceof Error ? error.message : String(error)
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function getHistoryStats() {
|
|
179
|
+
const entries = readAllHistoryEntries();
|
|
180
|
+
if (entries.length === 0) {
|
|
181
|
+
return {
|
|
182
|
+
totalEntries: 0,
|
|
183
|
+
oldestEntry: null,
|
|
184
|
+
newestEntry: null,
|
|
185
|
+
uniqueProjects: 0
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
const timestamps = entries.map((e) => e.timestamp).filter((t) => t > 0);
|
|
189
|
+
const projects = new Set(entries.map((e) => e.projectPath).filter(Boolean));
|
|
190
|
+
return {
|
|
191
|
+
totalEntries: entries.length,
|
|
192
|
+
oldestEntry: timestamps.length > 0 ? Math.min(...timestamps) : null,
|
|
193
|
+
newestEntry: timestamps.length > 0 ? Math.max(...timestamps) : null,
|
|
194
|
+
uniqueProjects: projects.size
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function exportHistory() {
|
|
198
|
+
return readAllHistoryEntries();
|
|
199
|
+
}
|
|
200
|
+
function importHistory(entries) {
|
|
201
|
+
const existing = readAllHistoryEntries();
|
|
202
|
+
const existingSet = new Set(existing.map((e) => `${e.command}:${e.timestamp}`));
|
|
203
|
+
let imported = 0;
|
|
204
|
+
for (const entry of entries) {
|
|
205
|
+
const key = `${entry.command}:${entry.timestamp}`;
|
|
206
|
+
if (!existingSet.has(key)) {
|
|
207
|
+
appendHistoryEntry(entry);
|
|
208
|
+
imported++;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
debugLogger.info("HISTORY_IMPORTED", {
|
|
212
|
+
total: entries.length,
|
|
213
|
+
imported,
|
|
214
|
+
skipped: entries.length - imported
|
|
215
|
+
});
|
|
216
|
+
return imported;
|
|
217
|
+
}
|
|
218
|
+
export {
|
|
219
|
+
addToHistoryNew,
|
|
220
|
+
appendHistoryEntry,
|
|
221
|
+
clearHistory,
|
|
222
|
+
exportHistory,
|
|
223
|
+
getCurrentProjectHistory,
|
|
224
|
+
getHistoryFilePath,
|
|
225
|
+
getHistoryStats,
|
|
226
|
+
getHistoryStrings,
|
|
227
|
+
importHistory,
|
|
228
|
+
invalidateCache,
|
|
229
|
+
readAllHistoryEntries,
|
|
230
|
+
readHistoryEntries,
|
|
231
|
+
rotateHistoryIfNeeded,
|
|
232
|
+
searchHistory
|
|
233
|
+
};
|
|
234
|
+
//# sourceMappingURL=historyManager.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/utils/historyManager.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * History Manager Module\n *\n * Provides high-performance JSONL-based command history management.\n * Uses append-only writes for efficiency and supports project-level filtering.\n *\n * Storage: ~/.minto/history/global.jsonl\n *\n * Entry format (one JSON object per line):\n * {\n * \"command\": \"git status\",\n * \"timestamp\": 1706976000000,\n * \"projectPath\": \"/path/to/project\",\n * \"mode\": \"prompt\" | \"bash\" | \"koding\"\n * }\n */\n\nimport {\n existsSync,\n appendFileSync,\n readFileSync,\n writeFileSync,\n mkdirSync,\n} from 'fs'\nimport { dirname, resolve } from 'path'\nimport { CONFIG_PATHS, ensureConfigDirs } from './configPaths'\nimport { sanitizeInput, containsDangerousSequences } from './sanitizeInput'\nimport { debug as debugLogger } from './debugLogger'\nimport { getCwd } from './state'\n\n/**\n * History entry stored in JSONL format\n */\nexport interface HistoryEntry {\n /** The command/input that was entered */\n command: string\n /** Unix timestamp when the command was entered */\n timestamp: number\n /** Project path where the command was executed */\n projectPath?: string\n /** Input mode: prompt, bash, or koding */\n mode?: 'prompt' | 'bash' | 'koding'\n}\n\n/**\n * Options for reading history\n */\nexport interface HistoryReadOptions {\n /** Maximum number of entries to return (default: 100) */\n limit?: number\n /** Filter by project path */\n projectPath?: string\n /** Filter by mode */\n mode?: 'prompt' | 'bash' | 'koding'\n /** Return entries newer than this timestamp */\n since?: number\n}\n\n// Default limits\nconst DEFAULT_LIMIT = 100\nconst MAX_HISTORY_ENTRIES = 10000 // Maximum entries before rotation\n\n// In-memory cache for performance\nlet historyCache: HistoryEntry[] | null = null\nlet cacheTimestamp: number = 0\nconst CACHE_TTL = 5000 // 5 seconds cache TTL\n\n/**\n * Invalidate the in-memory cache\n * Useful for testing and after external file modifications\n */\nexport function invalidateCache(): void {\n historyCache = null\n cacheTimestamp = 0\n}\n\n/**\n * Get the path to the global history file\n */\nexport function getHistoryFilePath(): string {\n return CONFIG_PATHS.globalHistoryFile\n}\n\n/**\n * Ensure the history directory exists\n */\nfunction ensureHistoryDir(): void {\n const historyDir = dirname(getHistoryFilePath())\n if (!existsSync(historyDir)) {\n mkdirSync(historyDir, { recursive: true })\n }\n}\n\n/**\n * Append a history entry to the JSONL file\n * Uses append mode for high performance\n */\nexport function appendHistoryEntry(entry: HistoryEntry): void {\n try {\n ensureHistoryDir()\n\n const line = JSON.stringify(entry) + '\\n'\n appendFileSync(getHistoryFilePath(), line, 'utf-8')\n\n // Invalidate cache\n historyCache = null\n\n debugLogger.trace('HISTORY_ENTRY_APPENDED', {\n command: entry.command.slice(0, 50),\n projectPath: entry.projectPath,\n })\n } catch (error) {\n debugLogger.error('HISTORY_APPEND_FAILED', {\n error: error instanceof Error ? error.message : String(error),\n })\n }\n}\n\n/**\n * Add a command to history with automatic metadata\n *\n * @param command The command to add\n * @param mode The input mode (prompt, bash, koding)\n */\nexport function addToHistoryNew(\n command: string,\n mode: 'prompt' | 'bash' | 'koding' = 'prompt',\n): void {\n // Sanitize the command before saving\n const sanitizedCommand = sanitizeInput(command)\n\n // Skip empty commands after sanitization\n if (!sanitizedCommand.trim()) {\n return\n }\n\n // Warn if command contains dangerous sequences\n if (containsDangerousSequences(command)) {\n debugLogger.warn('HISTORY_DANGEROUS_SEQUENCE', {\n command: command.slice(0, 50),\n })\n }\n\n // Get current project path\n let projectPath: string | undefined\n try {\n projectPath = getCwd()\n } catch {\n // Ignore if we can't get cwd\n }\n\n // Create entry\n const entry: HistoryEntry = {\n command: sanitizedCommand,\n timestamp: Date.now(),\n projectPath,\n mode,\n }\n\n // Check for duplicate (same as last entry)\n const recent = readHistoryEntries({ limit: 1 })\n if (recent.length > 0 && recent[0].command === sanitizedCommand) {\n return // Skip duplicate\n }\n\n appendHistoryEntry(entry)\n}\n\n/**\n * Read all history entries from the JSONL file\n * Uses caching for performance\n */\nexport function readAllHistoryEntries(): HistoryEntry[] {\n // Check cache\n if (historyCache && Date.now() - cacheTimestamp < CACHE_TTL) {\n return historyCache\n }\n\n const filePath = getHistoryFilePath()\n\n if (!existsSync(filePath)) {\n return []\n }\n\n try {\n const content = readFileSync(filePath, 'utf-8')\n const lines = content.split('\\n').filter(line => line.trim())\n\n const entries: HistoryEntry[] = []\n\n for (const line of lines) {\n try {\n const entry = JSON.parse(line) as HistoryEntry\n entries.push(entry)\n } catch {\n // Skip invalid lines\n debugLogger.trace('HISTORY_INVALID_LINE', { line: line.slice(0, 50) })\n }\n }\n\n // Update cache\n historyCache = entries\n cacheTimestamp = Date.now()\n\n return entries\n } catch (error) {\n debugLogger.error('HISTORY_READ_FAILED', {\n error: error instanceof Error ? error.message : String(error),\n })\n return []\n }\n}\n\n/**\n * Read history entries with filtering and limiting\n *\n * @param options Read options for filtering\n * @returns Filtered history entries (newest first)\n */\nexport function readHistoryEntries(\n options: HistoryReadOptions = {},\n): HistoryEntry[] {\n const { limit = DEFAULT_LIMIT, projectPath, mode, since } = options\n\n let entries = readAllHistoryEntries()\n\n // Apply filters\n if (projectPath) {\n const normalizedPath = resolve(projectPath).toLowerCase()\n entries = entries.filter(\n e =>\n e.projectPath &&\n resolve(e.projectPath).toLowerCase() === normalizedPath,\n )\n }\n\n if (mode) {\n entries = entries.filter(e => e.mode === mode)\n }\n\n if (since) {\n entries = entries.filter(e => e.timestamp > since)\n }\n\n // Sort by timestamp (newest first) and limit\n return entries.sort((a, b) => b.timestamp - a.timestamp).slice(0, limit)\n}\n\n/**\n * Get history as simple string array (for backward compatibility)\n *\n * @param options Read options\n * @returns Array of command strings (newest first)\n */\nexport function getHistoryStrings(options: HistoryReadOptions = {}): string[] {\n return readHistoryEntries(options).map(e => e.command)\n}\n\n/**\n * Get history for the current project\n *\n * @param limit Maximum number of entries\n * @returns Array of command strings (newest first)\n */\nexport function getCurrentProjectHistory(\n limit: number = DEFAULT_LIMIT,\n): string[] {\n let projectPath: string | undefined\n try {\n projectPath = getCwd()\n } catch {\n // Fallback to global history if we can't get cwd\n return getHistoryStrings({ limit })\n }\n\n return getHistoryStrings({ limit, projectPath })\n}\n\n/**\n * Search history for matching commands\n *\n * @param searchTerm Term to search for\n * @param options Additional read options\n * @returns Matching history entries (newest first)\n */\nexport function searchHistory(\n searchTerm: string,\n options: HistoryReadOptions = {},\n): HistoryEntry[] {\n const term = searchTerm.toLowerCase()\n const entries = readHistoryEntries({ ...options, limit: MAX_HISTORY_ENTRIES })\n\n return entries\n .filter(e => e.command.toLowerCase().includes(term))\n .slice(0, options.limit || DEFAULT_LIMIT)\n}\n\n/**\n * Rotate history file if it exceeds maximum entries\n * Keeps the most recent entries and removes older ones\n */\nexport function rotateHistoryIfNeeded(): void {\n const entries = readAllHistoryEntries()\n\n if (entries.length <= MAX_HISTORY_ENTRIES) {\n return\n }\n\n debugLogger.info('HISTORY_ROTATION_STARTED', {\n currentEntries: entries.length,\n maxEntries: MAX_HISTORY_ENTRIES,\n })\n\n // Keep the most recent entries\n const keepEntries = entries\n .sort((a, b) => b.timestamp - a.timestamp)\n .slice(0, MAX_HISTORY_ENTRIES)\n\n // Re-sort by timestamp ascending for writing\n keepEntries.sort((a, b) => a.timestamp - b.timestamp)\n\n // Rewrite the file\n try {\n const filePath = getHistoryFilePath()\n const content = keepEntries.map(e => JSON.stringify(e)).join('\\n') + '\\n'\n writeFileSync(filePath, content, 'utf-8')\n\n // Invalidate cache\n historyCache = null\n\n debugLogger.info('HISTORY_ROTATION_COMPLETE', {\n removedEntries: entries.length - keepEntries.length,\n keptEntries: keepEntries.length,\n })\n } catch (error) {\n debugLogger.error('HISTORY_ROTATION_FAILED', {\n error: error instanceof Error ? error.message : String(error),\n })\n }\n}\n\n/**\n * Clear all history entries\n * Use with caution - this is destructive\n */\nexport function clearHistory(): void {\n const filePath = getHistoryFilePath()\n\n if (existsSync(filePath)) {\n try {\n writeFileSync(filePath, '', 'utf-8')\n historyCache = null\n debugLogger.info('HISTORY_CLEARED', {})\n } catch (error) {\n debugLogger.error('HISTORY_CLEAR_FAILED', {\n error: error instanceof Error ? error.message : String(error),\n })\n }\n }\n}\n\n/**\n * Get history statistics\n */\nexport function getHistoryStats(): {\n totalEntries: number\n oldestEntry: number | null\n newestEntry: number | null\n uniqueProjects: number\n} {\n const entries = readAllHistoryEntries()\n\n if (entries.length === 0) {\n return {\n totalEntries: 0,\n oldestEntry: null,\n newestEntry: null,\n uniqueProjects: 0,\n }\n }\n\n const timestamps = entries.map(e => e.timestamp).filter(t => t > 0)\n const projects = new Set(entries.map(e => e.projectPath).filter(Boolean))\n\n return {\n totalEntries: entries.length,\n oldestEntry: timestamps.length > 0 ? Math.min(...timestamps) : null,\n newestEntry: timestamps.length > 0 ? Math.max(...timestamps) : null,\n uniqueProjects: projects.size,\n }\n}\n\n/**\n * Export history to a JSON array (for backup/migration)\n */\nexport function exportHistory(): HistoryEntry[] {\n return readAllHistoryEntries()\n}\n\n/**\n * Import history from a JSON array (for migration)\n * Merges with existing history, avoiding duplicates\n */\nexport function importHistory(entries: HistoryEntry[]): number {\n const existing = readAllHistoryEntries()\n const existingSet = new Set(existing.map(e => `${e.command}:${e.timestamp}`))\n\n let imported = 0\n\n for (const entry of entries) {\n const key = `${entry.command}:${entry.timestamp}`\n if (!existingSet.has(key)) {\n appendHistoryEntry(entry)\n imported++\n }\n }\n\n debugLogger.info('HISTORY_IMPORTED', {\n total: entries.length,\n imported,\n skipped: entries.length - imported,\n })\n\n return imported\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/utils/messages.js
CHANGED
|
@@ -284,18 +284,23 @@ async function getMessagesForSlashCommand(commandName, args, setToolJSX, context
|
|
|
284
284
|
switch (command.type) {
|
|
285
285
|
case "local-jsx": {
|
|
286
286
|
return new Promise((resolve2) => {
|
|
287
|
-
command.call(
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
287
|
+
command.call(
|
|
288
|
+
(r) => {
|
|
289
|
+
setToolJSX(null);
|
|
290
|
+
resolve2([
|
|
291
|
+
createUserMessage(`<command-name>${command.userFacingName()}</command-name>
|
|
291
292
|
<command-message>${command.userFacingName()}</command-message>
|
|
292
293
|
<command-args>${args}</command-args>`),
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
294
|
+
r ? createAssistantMessage(r) : createAssistantMessage(NO_RESPONSE_REQUESTED)
|
|
295
|
+
]);
|
|
296
|
+
},
|
|
297
|
+
context,
|
|
298
|
+
args
|
|
299
|
+
).then((jsx) => {
|
|
296
300
|
setToolJSX({
|
|
297
301
|
jsx,
|
|
298
|
-
|
|
302
|
+
// Use command's showBelowPrompt property if defined, otherwise default to hiding
|
|
303
|
+
shouldHidePromptInput: !command.showBelowPrompt
|
|
299
304
|
});
|
|
300
305
|
});
|
|
301
306
|
});
|