ys-code-agent 2.0.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/LICENSE +21 -0
- package/README.md +252 -0
- package/SETUP.md +228 -0
- package/USAGE.md +437 -0
- package/dist/agent/index.d.ts +29 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +501 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/cli/agent.d.ts +10 -0
- package/dist/cli/agent.d.ts.map +1 -0
- package/dist/cli/agent.js +38 -0
- package/dist/cli/agent.js.map +1 -0
- package/dist/cli/index.cjs +19187 -0
- package/dist/cli/index.cjs.map +7 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +326 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/interactive.d.ts +14 -0
- package/dist/cli/interactive.d.ts.map +1 -0
- package/dist/cli/interactive.js +125 -0
- package/dist/cli/interactive.js.map +1 -0
- package/dist/commands/CommandRegistry.d.ts +15 -0
- package/dist/commands/CommandRegistry.d.ts.map +1 -0
- package/dist/commands/CommandRegistry.js +1311 -0
- package/dist/commands/CommandRegistry.js.map +1 -0
- package/dist/commands/handlers/agents.d.ts +5 -0
- package/dist/commands/handlers/agents.d.ts.map +1 -0
- package/dist/commands/handlers/agents.js +124 -0
- package/dist/commands/handlers/agents.js.map +1 -0
- package/dist/commands/handlers/file.d.ts +8 -0
- package/dist/commands/handlers/file.d.ts.map +1 -0
- package/dist/commands/handlers/file.js +364 -0
- package/dist/commands/handlers/file.js.map +1 -0
- package/dist/commands/handlers/git.d.ts +2 -0
- package/dist/commands/handlers/git.d.ts.map +1 -0
- package/dist/commands/handlers/git.js +343 -0
- package/dist/commands/handlers/git.js.map +1 -0
- package/dist/commands/handlers/memory.d.ts +6 -0
- package/dist/commands/handlers/memory.d.ts.map +1 -0
- package/dist/commands/handlers/memory.js +149 -0
- package/dist/commands/handlers/memory.js.map +1 -0
- package/dist/commands/index.d.ts +3 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +2 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/config/index.d.ts +25 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +399 -0
- package/dist/config/index.js.map +1 -0
- package/dist/context/index.d.ts +31 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +208 -0
- package/dist/context/index.js.map +1 -0
- package/dist/filesystem/index.d.ts +52 -0
- package/dist/filesystem/index.d.ts.map +1 -0
- package/dist/filesystem/index.js +481 -0
- package/dist/filesystem/index.js.map +1 -0
- package/dist/git/index.d.ts +41 -0
- package/dist/git/index.d.ts.map +1 -0
- package/dist/git/index.js +381 -0
- package/dist/git/index.js.map +1 -0
- package/dist/logger/index.d.ts +32 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/logger/index.js +199 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/memory/index.d.ts +45 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +147 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/project/index.d.ts +14 -0
- package/dist/project/index.d.ts.map +1 -0
- package/dist/project/index.js +371 -0
- package/dist/project/index.js.map +1 -0
- package/dist/providers/anthropic.d.ts +22 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +202 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/base.d.ts +29 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +119 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/custom.d.ts +8 -0
- package/dist/providers/custom.d.ts.map +1 -0
- package/dist/providers/custom.js +13 -0
- package/dist/providers/custom.js.map +1 -0
- package/dist/providers/deepseek.d.ts +8 -0
- package/dist/providers/deepseek.d.ts.map +1 -0
- package/dist/providers/deepseek.js +13 -0
- package/dist/providers/deepseek.js.map +1 -0
- package/dist/providers/gemini.d.ts +18 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +183 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/groq.d.ts +8 -0
- package/dist/providers/groq.d.ts.map +1 -0
- package/dist/providers/groq.js +13 -0
- package/dist/providers/groq.js.map +1 -0
- package/dist/providers/index.d.ts +16 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +50 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/lmstudio.d.ts +9 -0
- package/dist/providers/lmstudio.d.ts.map +1 -0
- package/dist/providers/lmstudio.js +18 -0
- package/dist/providers/lmstudio.js.map +1 -0
- package/dist/providers/ollama.d.ts +8 -0
- package/dist/providers/ollama.d.ts.map +1 -0
- package/dist/providers/ollama.js +13 -0
- package/dist/providers/ollama.js.map +1 -0
- package/dist/providers/openai.d.ts +16 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +116 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/openrouter.d.ts +9 -0
- package/dist/providers/openrouter.d.ts.map +1 -0
- package/dist/providers/openrouter.js +21 -0
- package/dist/providers/openrouter.js.map +1 -0
- package/dist/security/index.d.ts +28 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +139 -0
- package/dist/security/index.js.map +1 -0
- package/dist/session/index.d.ts +36 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +203 -0
- package/dist/session/index.js.map +1 -0
- package/dist/src/agent/index.d.ts +29 -0
- package/dist/src/agent/index.d.ts.map +1 -0
- package/dist/src/agent/index.js +504 -0
- package/dist/src/agent/index.js.map +1 -0
- package/dist/src/cli/agent.d.ts +10 -0
- package/dist/src/cli/agent.d.ts.map +1 -0
- package/dist/src/cli/agent.js +38 -0
- package/dist/src/cli/agent.js.map +1 -0
- package/dist/src/cli/index.d.ts +3 -0
- package/dist/src/cli/index.d.ts.map +1 -0
- package/dist/src/cli/index.js +327 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/interactive.d.ts +12 -0
- package/dist/src/cli/interactive.d.ts.map +1 -0
- package/dist/src/cli/interactive.js +202 -0
- package/dist/src/cli/interactive.js.map +1 -0
- package/dist/src/config/index.d.ts +25 -0
- package/dist/src/config/index.d.ts.map +1 -0
- package/dist/src/config/index.js +398 -0
- package/dist/src/config/index.js.map +1 -0
- package/dist/src/context/index.d.ts +31 -0
- package/dist/src/context/index.d.ts.map +1 -0
- package/dist/src/context/index.js +208 -0
- package/dist/src/context/index.js.map +1 -0
- package/dist/src/filesystem/index.d.ts +52 -0
- package/dist/src/filesystem/index.d.ts.map +1 -0
- package/dist/src/filesystem/index.js +481 -0
- package/dist/src/filesystem/index.js.map +1 -0
- package/dist/src/git/index.d.ts +41 -0
- package/dist/src/git/index.d.ts.map +1 -0
- package/dist/src/git/index.js +381 -0
- package/dist/src/git/index.js.map +1 -0
- package/dist/src/logger/index.d.ts +32 -0
- package/dist/src/logger/index.d.ts.map +1 -0
- package/dist/src/logger/index.js +199 -0
- package/dist/src/logger/index.js.map +1 -0
- package/dist/src/memory/index.d.ts +45 -0
- package/dist/src/memory/index.d.ts.map +1 -0
- package/dist/src/memory/index.js +147 -0
- package/dist/src/memory/index.js.map +1 -0
- package/dist/src/project/index.d.ts +14 -0
- package/dist/src/project/index.d.ts.map +1 -0
- package/dist/src/project/index.js +371 -0
- package/dist/src/project/index.js.map +1 -0
- package/dist/src/providers/anthropic.d.ts +22 -0
- package/dist/src/providers/anthropic.d.ts.map +1 -0
- package/dist/src/providers/anthropic.js +202 -0
- package/dist/src/providers/anthropic.js.map +1 -0
- package/dist/src/providers/base.d.ts +29 -0
- package/dist/src/providers/base.d.ts.map +1 -0
- package/dist/src/providers/base.js +112 -0
- package/dist/src/providers/base.js.map +1 -0
- package/dist/src/providers/custom.d.ts +8 -0
- package/dist/src/providers/custom.d.ts.map +1 -0
- package/dist/src/providers/custom.js +13 -0
- package/dist/src/providers/custom.js.map +1 -0
- package/dist/src/providers/deepseek.d.ts +8 -0
- package/dist/src/providers/deepseek.d.ts.map +1 -0
- package/dist/src/providers/deepseek.js +13 -0
- package/dist/src/providers/deepseek.js.map +1 -0
- package/dist/src/providers/gemini.d.ts +18 -0
- package/dist/src/providers/gemini.d.ts.map +1 -0
- package/dist/src/providers/gemini.js +183 -0
- package/dist/src/providers/gemini.js.map +1 -0
- package/dist/src/providers/groq.d.ts +8 -0
- package/dist/src/providers/groq.d.ts.map +1 -0
- package/dist/src/providers/groq.js +13 -0
- package/dist/src/providers/groq.js.map +1 -0
- package/dist/src/providers/index.d.ts +16 -0
- package/dist/src/providers/index.d.ts.map +1 -0
- package/dist/src/providers/index.js +50 -0
- package/dist/src/providers/index.js.map +1 -0
- package/dist/src/providers/lmstudio.d.ts +9 -0
- package/dist/src/providers/lmstudio.d.ts.map +1 -0
- package/dist/src/providers/lmstudio.js +18 -0
- package/dist/src/providers/lmstudio.js.map +1 -0
- package/dist/src/providers/ollama.d.ts +8 -0
- package/dist/src/providers/ollama.d.ts.map +1 -0
- package/dist/src/providers/ollama.js +13 -0
- package/dist/src/providers/ollama.js.map +1 -0
- package/dist/src/providers/openai.d.ts +16 -0
- package/dist/src/providers/openai.d.ts.map +1 -0
- package/dist/src/providers/openai.js +116 -0
- package/dist/src/providers/openai.js.map +1 -0
- package/dist/src/providers/openrouter.d.ts +9 -0
- package/dist/src/providers/openrouter.d.ts.map +1 -0
- package/dist/src/providers/openrouter.js +21 -0
- package/dist/src/providers/openrouter.js.map +1 -0
- package/dist/src/security/index.d.ts +28 -0
- package/dist/src/security/index.d.ts.map +1 -0
- package/dist/src/security/index.js +139 -0
- package/dist/src/security/index.js.map +1 -0
- package/dist/src/session/index.d.ts +36 -0
- package/dist/src/session/index.d.ts.map +1 -0
- package/dist/src/session/index.js +203 -0
- package/dist/src/session/index.js.map +1 -0
- package/dist/src/terminal/index.d.ts +27 -0
- package/dist/src/terminal/index.d.ts.map +1 -0
- package/dist/src/terminal/index.js +211 -0
- package/dist/src/terminal/index.js.map +1 -0
- package/dist/src/tools/DeleteFileTool.d.ts +7 -0
- package/dist/src/tools/DeleteFileTool.d.ts.map +1 -0
- package/dist/src/tools/DeleteFileTool.js +50 -0
- package/dist/src/tools/DeleteFileTool.js.map +1 -0
- package/dist/src/tools/DirectoryTreeTool.d.ts +7 -0
- package/dist/src/tools/DirectoryTreeTool.d.ts.map +1 -0
- package/dist/src/tools/DirectoryTreeTool.js +50 -0
- package/dist/src/tools/DirectoryTreeTool.js.map +1 -0
- package/dist/src/tools/EditFileTool.d.ts +7 -0
- package/dist/src/tools/EditFileTool.d.ts.map +1 -0
- package/dist/src/tools/EditFileTool.js +69 -0
- package/dist/src/tools/EditFileTool.js.map +1 -0
- package/dist/src/tools/GitTool.d.ts +9 -0
- package/dist/src/tools/GitTool.d.ts.map +1 -0
- package/dist/src/tools/GitTool.js +115 -0
- package/dist/src/tools/GitTool.js.map +1 -0
- package/dist/src/tools/GlobTool.d.ts +7 -0
- package/dist/src/tools/GlobTool.d.ts.map +1 -0
- package/dist/src/tools/GlobTool.js +51 -0
- package/dist/src/tools/GlobTool.js.map +1 -0
- package/dist/src/tools/MemoryTool.d.ts +9 -0
- package/dist/src/tools/MemoryTool.d.ts.map +1 -0
- package/dist/src/tools/MemoryTool.js +109 -0
- package/dist/src/tools/MemoryTool.js.map +1 -0
- package/dist/src/tools/ReadFileTool.d.ts +7 -0
- package/dist/src/tools/ReadFileTool.d.ts.map +1 -0
- package/dist/src/tools/ReadFileTool.js +75 -0
- package/dist/src/tools/ReadFileTool.js.map +1 -0
- package/dist/src/tools/SearchTool.d.ts +7 -0
- package/dist/src/tools/SearchTool.d.ts.map +1 -0
- package/dist/src/tools/SearchTool.js +67 -0
- package/dist/src/tools/SearchTool.js.map +1 -0
- package/dist/src/tools/TerminalTool.d.ts +9 -0
- package/dist/src/tools/TerminalTool.d.ts.map +1 -0
- package/dist/src/tools/TerminalTool.js +93 -0
- package/dist/src/tools/TerminalTool.js.map +1 -0
- package/dist/src/tools/WebFetchTool.d.ts +7 -0
- package/dist/src/tools/WebFetchTool.d.ts.map +1 -0
- package/dist/src/tools/WebFetchTool.js +120 -0
- package/dist/src/tools/WebFetchTool.js.map +1 -0
- package/dist/src/tools/WriteFileTool.d.ts +7 -0
- package/dist/src/tools/WriteFileTool.d.ts.map +1 -0
- package/dist/src/tools/WriteFileTool.js +46 -0
- package/dist/src/tools/WriteFileTool.js.map +1 -0
- package/dist/src/tools/base.d.ts +36 -0
- package/dist/src/tools/base.d.ts.map +1 -0
- package/dist/src/tools/base.js +159 -0
- package/dist/src/tools/base.js.map +1 -0
- package/dist/src/tools/index.d.ts +14 -0
- package/dist/src/tools/index.d.ts.map +1 -0
- package/dist/src/tools/index.js +44 -0
- package/dist/src/tools/index.js.map +1 -0
- package/dist/src/types.d.ts +315 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/ui/index.d.ts +50 -0
- package/dist/src/ui/index.d.ts.map +1 -0
- package/dist/src/ui/index.js +274 -0
- package/dist/src/ui/index.js.map +1 -0
- package/dist/src/utils/index.d.ts +26 -0
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js +248 -0
- package/dist/src/utils/index.js.map +1 -0
- package/dist/terminal/index.d.ts +27 -0
- package/dist/terminal/index.d.ts.map +1 -0
- package/dist/terminal/index.js +211 -0
- package/dist/terminal/index.js.map +1 -0
- package/dist/tools/DeleteFileTool.d.ts +7 -0
- package/dist/tools/DeleteFileTool.d.ts.map +1 -0
- package/dist/tools/DeleteFileTool.js +50 -0
- package/dist/tools/DeleteFileTool.js.map +1 -0
- package/dist/tools/DirectoryTreeTool.d.ts +7 -0
- package/dist/tools/DirectoryTreeTool.d.ts.map +1 -0
- package/dist/tools/DirectoryTreeTool.js +50 -0
- package/dist/tools/DirectoryTreeTool.js.map +1 -0
- package/dist/tools/EditFileTool.d.ts +7 -0
- package/dist/tools/EditFileTool.d.ts.map +1 -0
- package/dist/tools/EditFileTool.js +69 -0
- package/dist/tools/EditFileTool.js.map +1 -0
- package/dist/tools/GitTool.d.ts +9 -0
- package/dist/tools/GitTool.d.ts.map +1 -0
- package/dist/tools/GitTool.js +115 -0
- package/dist/tools/GitTool.js.map +1 -0
- package/dist/tools/GlobTool.d.ts +7 -0
- package/dist/tools/GlobTool.d.ts.map +1 -0
- package/dist/tools/GlobTool.js +51 -0
- package/dist/tools/GlobTool.js.map +1 -0
- package/dist/tools/MemoryTool.d.ts +9 -0
- package/dist/tools/MemoryTool.d.ts.map +1 -0
- package/dist/tools/MemoryTool.js +109 -0
- package/dist/tools/MemoryTool.js.map +1 -0
- package/dist/tools/ReadFileTool.d.ts +7 -0
- package/dist/tools/ReadFileTool.d.ts.map +1 -0
- package/dist/tools/ReadFileTool.js +75 -0
- package/dist/tools/ReadFileTool.js.map +1 -0
- package/dist/tools/SearchTool.d.ts +7 -0
- package/dist/tools/SearchTool.d.ts.map +1 -0
- package/dist/tools/SearchTool.js +67 -0
- package/dist/tools/SearchTool.js.map +1 -0
- package/dist/tools/TerminalTool.d.ts +9 -0
- package/dist/tools/TerminalTool.d.ts.map +1 -0
- package/dist/tools/TerminalTool.js +93 -0
- package/dist/tools/TerminalTool.js.map +1 -0
- package/dist/tools/WebFetchTool.d.ts +7 -0
- package/dist/tools/WebFetchTool.d.ts.map +1 -0
- package/dist/tools/WebFetchTool.js +120 -0
- package/dist/tools/WebFetchTool.js.map +1 -0
- package/dist/tools/WriteFileTool.d.ts +7 -0
- package/dist/tools/WriteFileTool.d.ts.map +1 -0
- package/dist/tools/WriteFileTool.js +46 -0
- package/dist/tools/WriteFileTool.js.map +1 -0
- package/dist/tools/base.d.ts +36 -0
- package/dist/tools/base.d.ts.map +1 -0
- package/dist/tools/base.js +159 -0
- package/dist/tools/base.js.map +1 -0
- package/dist/tools/index.d.ts +14 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +44 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types.d.ts +315 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/CommandPopup.d.ts +33 -0
- package/dist/ui/CommandPopup.d.ts.map +1 -0
- package/dist/ui/CommandPopup.js +179 -0
- package/dist/ui/CommandPopup.js.map +1 -0
- package/dist/ui/WelcomeScreen.d.ts +3 -0
- package/dist/ui/WelcomeScreen.d.ts.map +1 -0
- package/dist/ui/WelcomeScreen.js +168 -0
- package/dist/ui/WelcomeScreen.js.map +1 -0
- package/dist/ui/index.d.ts +75 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +728 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/phoneOptimizer.d.ts +15 -0
- package/dist/ui/phoneOptimizer.d.ts.map +1 -0
- package/dist/ui/phoneOptimizer.js +58 -0
- package/dist/ui/phoneOptimizer.js.map +1 -0
- package/dist/utils/index.d.ts +26 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +248 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +80 -0
- package/scripts/build.mjs +53 -0
- package/scripts/setup-termux.sh +84 -0
|
@@ -0,0 +1,1311 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { getLogger } from '../logger/index.js';
|
|
3
|
+
import { tui } from '../ui/index.js';
|
|
4
|
+
import { configManager } from '../config/index.js';
|
|
5
|
+
import { agent } from '../agent/index.js';
|
|
6
|
+
import { memoryManager } from '../memory/index.js';
|
|
7
|
+
import { sessionManager } from '../session/index.js';
|
|
8
|
+
import { phoneConfig } from '../ui/phoneOptimizer.js';
|
|
9
|
+
import { handleRead, handleEdit, handleCreate, handleDelete, handleSearch, handleList, handleRefactor } from './handlers/file.js';
|
|
10
|
+
import { handleGit } from './handlers/git.js';
|
|
11
|
+
import { handleMemory, handleRemember, handleForget, handleInit, handleDream } from './handlers/memory.js';
|
|
12
|
+
import { handleAgents, handleArena, handleTasks, handleBackground } from './handlers/agents.js';
|
|
13
|
+
const logger = getLogger('commands');
|
|
14
|
+
const commands = new Map();
|
|
15
|
+
function reg(def) {
|
|
16
|
+
commands.set(def.name, def);
|
|
17
|
+
for (const alias of def.aliases) {
|
|
18
|
+
commands.set(alias, def);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
reg({
|
|
22
|
+
name: 'help',
|
|
23
|
+
aliases: ['h', '?'],
|
|
24
|
+
description: 'Show this help',
|
|
25
|
+
category: 'system',
|
|
26
|
+
handler: async (args) => {
|
|
27
|
+
if (args.length > 0) {
|
|
28
|
+
const category = args[0].toLowerCase();
|
|
29
|
+
const catMap = {
|
|
30
|
+
system: 'system', files: 'files', file: 'files',
|
|
31
|
+
git: 'git', agents: 'agents', agent: 'agents',
|
|
32
|
+
memory: 'memory', settings: 'settings', config: 'settings',
|
|
33
|
+
code: 'code', review: 'code', debug: 'code',
|
|
34
|
+
};
|
|
35
|
+
const resolvedCat = catMap[category];
|
|
36
|
+
if (resolvedCat) {
|
|
37
|
+
showCategoryHelp(resolvedCat);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
showSpecificHelp(category);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
tui.printHelp();
|
|
45
|
+
}
|
|
46
|
+
return true;
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
reg({
|
|
50
|
+
name: 'status',
|
|
51
|
+
aliases: ['stats', 'info'],
|
|
52
|
+
description: 'Agent status dashboard',
|
|
53
|
+
category: 'system',
|
|
54
|
+
handler: async () => {
|
|
55
|
+
const config = configManager.getConfig();
|
|
56
|
+
const provider = configManager.getActiveProvider();
|
|
57
|
+
const state = agent.getState();
|
|
58
|
+
const msgs = agent.getMessages();
|
|
59
|
+
const session = sessionManager.getCurrentSession();
|
|
60
|
+
const w = Math.min(phoneConfig.terminalWidth - 2, 54);
|
|
61
|
+
const top = `╔${'═'.repeat(w)}╗`;
|
|
62
|
+
const bottom = `╚${'═'.repeat(w)}╝`;
|
|
63
|
+
tui.printLine('');
|
|
64
|
+
tui.printLine(chalk.cyan(top));
|
|
65
|
+
tui.printLine(`║ ${chalk.white('Status')}: ${statusBadge(state.status)}${' '.repeat(Math.max(0, w - 12 - state.status.length))}║`);
|
|
66
|
+
tui.printLine(`║ ${chalk.white('Provider')}: ${chalk.yellow(provider.name.padEnd(w - 15))}║`);
|
|
67
|
+
tui.printLine(`║ ${chalk.white('Model')}: ${chalk.yellow((config.model.model.length > 30 ? config.model.model.slice(0, 28) + '…' : config.model.model).padEnd(w - 12))}║`);
|
|
68
|
+
tui.printLine(`║ ${chalk.white('Messages')}: ${chalk.yellow(String(msgs.length).padEnd(w - 15))}║`);
|
|
69
|
+
tui.printLine(`║ ${chalk.white('Session')}: ${chalk.yellow((session ? session.id.slice(0, 8) : 'none').padEnd(w - 14))}║`);
|
|
70
|
+
tui.printLine(`║ ${chalk.white('Mode')}: ${modeBadge(tui.getAgentMode())}${' '.repeat(Math.max(0, w - 11))}║`);
|
|
71
|
+
tui.printLine(`║ ${chalk.white('Approval')}: ${approvalBadge(tui.getApprovalMode())}${' '.repeat(Math.max(0, w - 15))}║`);
|
|
72
|
+
if (phoneConfig.isTermux) {
|
|
73
|
+
tui.printLine(`║ ${chalk.white('Termux')}: ${chalk.green('✓ Detected')}${' '.repeat(Math.max(0, w - 21))}║`);
|
|
74
|
+
}
|
|
75
|
+
tui.printLine(chalk.cyan(bottom));
|
|
76
|
+
return true;
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
reg({
|
|
80
|
+
name: 'clear',
|
|
81
|
+
aliases: ['cls'],
|
|
82
|
+
description: 'Clear screen',
|
|
83
|
+
category: 'system',
|
|
84
|
+
handler: async () => {
|
|
85
|
+
tui.clear();
|
|
86
|
+
tui.printWelcome();
|
|
87
|
+
return true;
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
reg({
|
|
91
|
+
name: 'exit',
|
|
92
|
+
aliases: ['quit', 'q'],
|
|
93
|
+
description: 'Exit YS Code Agent',
|
|
94
|
+
category: 'system',
|
|
95
|
+
handler: async () => {
|
|
96
|
+
tui.printLine(chalk.yellow('\nShutting down...'));
|
|
97
|
+
tui.stop();
|
|
98
|
+
process.exit(0);
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
reg({
|
|
102
|
+
name: 'reset',
|
|
103
|
+
aliases: ['clear-state'],
|
|
104
|
+
description: 'Reset agent state',
|
|
105
|
+
category: 'system',
|
|
106
|
+
handler: async () => {
|
|
107
|
+
agent.reset();
|
|
108
|
+
tui.printLine(chalk.green('✓ Agent state reset'));
|
|
109
|
+
return true;
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
reg({
|
|
113
|
+
name: 'history',
|
|
114
|
+
aliases: ['hist'],
|
|
115
|
+
description: 'Show message history',
|
|
116
|
+
category: 'system',
|
|
117
|
+
handler: async (args) => {
|
|
118
|
+
const messages = agent.getMessages();
|
|
119
|
+
const count = args[0] ? parseInt(args[0], 10) || 10 : 10;
|
|
120
|
+
const recent = messages.slice(-count);
|
|
121
|
+
tui.printLine(chalk.cyan(`\nRecent history (${recent.length} messages):`));
|
|
122
|
+
for (const msg of recent) {
|
|
123
|
+
if (msg.role === 'system')
|
|
124
|
+
continue;
|
|
125
|
+
const role = msg.role === 'user' ? chalk.gray('User:') : chalk.green('Asst:');
|
|
126
|
+
const content = msg.content.slice(0, 120) + (msg.content.length > 120 ? '…' : '');
|
|
127
|
+
tui.printLine(` ${role} ${content}`);
|
|
128
|
+
}
|
|
129
|
+
return true;
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
reg({
|
|
133
|
+
name: 'model',
|
|
134
|
+
aliases: ['m'],
|
|
135
|
+
description: 'Show current model info',
|
|
136
|
+
category: 'system',
|
|
137
|
+
handler: async () => {
|
|
138
|
+
const config = configManager.getConfig();
|
|
139
|
+
tui.printLine(chalk.cyan(`\nModel Configuration:`));
|
|
140
|
+
tui.printLine(` Provider: ${chalk.yellow(config.activeProvider)}`);
|
|
141
|
+
tui.printLine(` Model: ${chalk.yellow(config.model.model)}`);
|
|
142
|
+
tui.printLine(` Temperature: ${config.model.temperature}`);
|
|
143
|
+
tui.printLine(` Max Tokens: ${config.model.maxTokens.toLocaleString()}`);
|
|
144
|
+
return true;
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
reg({
|
|
148
|
+
name: 'models',
|
|
149
|
+
aliases: ['list-models'],
|
|
150
|
+
description: 'List available models',
|
|
151
|
+
category: 'system',
|
|
152
|
+
handler: async () => {
|
|
153
|
+
const config = configManager.getConfig();
|
|
154
|
+
const provider = configManager.getActiveProvider();
|
|
155
|
+
tui.printLine(chalk.cyan(`\nModels for ${provider.name}:`));
|
|
156
|
+
for (const model of provider.models) {
|
|
157
|
+
const isCurrent = model === config.model.model ? chalk.green(' ◀ current') : '';
|
|
158
|
+
tui.printLine(` ${chalk.yellow(model)}${isCurrent}`);
|
|
159
|
+
}
|
|
160
|
+
return true;
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
reg({
|
|
164
|
+
name: 'provider',
|
|
165
|
+
aliases: ['prov'],
|
|
166
|
+
description: 'List/switch providers',
|
|
167
|
+
category: 'system',
|
|
168
|
+
handler: async (args) => {
|
|
169
|
+
if (args.length === 0) {
|
|
170
|
+
const config = configManager.getConfig();
|
|
171
|
+
tui.printLine(chalk.cyan('\nConfigured Providers:'));
|
|
172
|
+
for (const p of config.providers) {
|
|
173
|
+
const active = p.name === config.activeProvider ? chalk.green(' (active)') : '';
|
|
174
|
+
const hasKey = p.apiKey ? chalk.gray(' [key set]') : chalk.red(' [no key]');
|
|
175
|
+
tui.printLine(` ${chalk.yellow(p.name)}${active}${hasKey}`);
|
|
176
|
+
}
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
const name = args[0].toLowerCase();
|
|
180
|
+
if (agent.switchProvider(name)) {
|
|
181
|
+
tui.printLine(chalk.green(`✓ Switched to provider: ${name}`));
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
tui.printLine(chalk.red(`✗ Provider not found: ${name}`));
|
|
185
|
+
}
|
|
186
|
+
return true;
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
reg({
|
|
190
|
+
name: 'doctor',
|
|
191
|
+
aliases: ['diagnostics', 'diag'],
|
|
192
|
+
description: 'Run system diagnostics',
|
|
193
|
+
category: 'system',
|
|
194
|
+
handler: async () => {
|
|
195
|
+
tui.printLine(chalk.cyan('\n═══ YS Code Agent Diagnostics ═══'));
|
|
196
|
+
tui.printLine(chalk.yellow('\nSystem:'));
|
|
197
|
+
tui.printLine(` Node: ${process.version}`);
|
|
198
|
+
tui.printLine(` OS: ${process.platform} ${process.arch}`);
|
|
199
|
+
tui.printLine(` Termux: ${phoneConfig.isTermux ? chalk.green('✓') : chalk.gray('Not detected')}`);
|
|
200
|
+
tui.printLine(chalk.yellow('\nProvider:'));
|
|
201
|
+
const config = configManager.getConfig();
|
|
202
|
+
const activeProv = configManager.getActiveProvider();
|
|
203
|
+
const keySet = activeProv.apiKey || process.env[`${activeProv.type.toUpperCase()}_API_KEY`];
|
|
204
|
+
tui.printLine(` Active: ${chalk.cyan(config.activeProvider)}`);
|
|
205
|
+
tui.printLine(` Model: ${chalk.cyan(config.model.model)}`);
|
|
206
|
+
tui.printLine(` API Key: ${keySet ? chalk.green('✓ Set') : chalk.red('✕ Not set')}`);
|
|
207
|
+
tui.printLine(chalk.yellow('\nTools:'));
|
|
208
|
+
const { toolRegistry } = await import('../tools/index.js');
|
|
209
|
+
const tools = toolRegistry.getToolNames();
|
|
210
|
+
tui.printLine(` Registered: ${chalk.green(String(tools.length))}`);
|
|
211
|
+
tui.printLine(chalk.yellow('\nGit:'));
|
|
212
|
+
const { gitManager } = await import('../git/index.js');
|
|
213
|
+
tui.printLine(` Available: ${gitManager.isAvailable() ? chalk.green('✓') : chalk.red('✕')}`);
|
|
214
|
+
tui.printLine(` Repository: ${gitManager.isRepo() ? chalk.green('✓') : chalk.gray('Not a git repo')}`);
|
|
215
|
+
tui.printLine(chalk.yellow('\nMemory:'));
|
|
216
|
+
const context = memoryManager.getContext();
|
|
217
|
+
tui.printLine(` Messages: ${context.shortTerm.messages.length}`);
|
|
218
|
+
tui.printLine(` Summaries: ${context.longTerm.summaries.length}`);
|
|
219
|
+
tui.printLine(chalk.green('\n✓ Diagnostics complete\n'));
|
|
220
|
+
return true;
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
reg({
|
|
224
|
+
name: 'recap',
|
|
225
|
+
aliases: ['summary'],
|
|
226
|
+
description: 'Session summary',
|
|
227
|
+
category: 'system',
|
|
228
|
+
handler: async () => {
|
|
229
|
+
const session = sessionManager.getCurrentSession();
|
|
230
|
+
if (!session) {
|
|
231
|
+
tui.printLine(chalk.yellow('No active session'));
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
const messages = agent.getMessages();
|
|
235
|
+
const userMsgs = messages.filter((m) => m.role === 'user');
|
|
236
|
+
const asstMsgs = messages.filter((m) => m.role === 'assistant');
|
|
237
|
+
const duration = Date.now() - session.createdAt;
|
|
238
|
+
tui.printLine(chalk.cyan(`\nSession Recap:`));
|
|
239
|
+
tui.printLine(` ID: ${chalk.yellow(session.id.slice(0, 8))}`);
|
|
240
|
+
tui.printLine(` Name: ${chalk.yellow(session.name)}`);
|
|
241
|
+
tui.printLine(` Duration: ${chalk.yellow(formatDuration(duration))}`);
|
|
242
|
+
tui.printLine(` Messages: ${chalk.yellow(String(messages.length))} (${userMsgs.length} user, ${asstMsgs.length} assistant)`);
|
|
243
|
+
return true;
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
reg({
|
|
247
|
+
name: 'rewind',
|
|
248
|
+
aliases: ['undo'],
|
|
249
|
+
description: 'Undo last turn',
|
|
250
|
+
category: 'system',
|
|
251
|
+
handler: async () => {
|
|
252
|
+
const messages = agent.getMessages();
|
|
253
|
+
if (messages.length < 2) {
|
|
254
|
+
tui.printLine(chalk.yellow('Nothing to rewind'));
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
let removed = 0;
|
|
258
|
+
while (messages.length > 0) {
|
|
259
|
+
const last = messages[messages.length - 1];
|
|
260
|
+
if (last.role === 'user')
|
|
261
|
+
break;
|
|
262
|
+
messages.pop();
|
|
263
|
+
removed++;
|
|
264
|
+
}
|
|
265
|
+
if (messages.length > 0 && messages[messages.length - 1].role === 'user') {
|
|
266
|
+
messages.pop();
|
|
267
|
+
removed++;
|
|
268
|
+
}
|
|
269
|
+
tui.printLine(chalk.green(`✓ Rewound ${removed} messages`));
|
|
270
|
+
return true;
|
|
271
|
+
},
|
|
272
|
+
});
|
|
273
|
+
reg({
|
|
274
|
+
name: 'plan',
|
|
275
|
+
aliases: [],
|
|
276
|
+
description: 'Enter plan mode',
|
|
277
|
+
category: 'code',
|
|
278
|
+
handler: async (args) => {
|
|
279
|
+
tui.setAgentMode('plan');
|
|
280
|
+
if (args.length > 0) {
|
|
281
|
+
const goal = args.join(' ');
|
|
282
|
+
tui.printLine(chalk.blue(`\nPlan mode activated for: ${chalk.white(goal)}`));
|
|
283
|
+
await handlePlanMode(goal);
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
tui.printLine(chalk.blue('\nPlan mode activated. Type your goal to create a plan.'));
|
|
287
|
+
tui.printLine(chalk.gray(' Use /apply to execute the plan once created.'));
|
|
288
|
+
}
|
|
289
|
+
return true;
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
reg({
|
|
293
|
+
name: 'goal',
|
|
294
|
+
aliases: ['auto', 'autonomous'],
|
|
295
|
+
description: 'Autonomous agent mode',
|
|
296
|
+
category: 'code',
|
|
297
|
+
handler: async (args) => {
|
|
298
|
+
if (args.length === 0) {
|
|
299
|
+
tui.printLine(chalk.red('Usage: /goal <task description>'));
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
const task = args.join(' ');
|
|
303
|
+
tui.setAgentMode('goal');
|
|
304
|
+
tui.printLine(chalk.magenta(`\n◆ Goal: ${chalk.white(task)}`));
|
|
305
|
+
await handleGoalMode(task);
|
|
306
|
+
tui.setAgentMode('chat');
|
|
307
|
+
return true;
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
reg({
|
|
311
|
+
name: 'debug',
|
|
312
|
+
aliases: ['fix'],
|
|
313
|
+
description: 'Auto debug',
|
|
314
|
+
category: 'code',
|
|
315
|
+
handler: async (args) => {
|
|
316
|
+
const errorText = args.join(' ') || '';
|
|
317
|
+
if (!errorText) {
|
|
318
|
+
tui.printLine(chalk.yellow('Paste your error message or use /debug <file>:<line>'));
|
|
319
|
+
tui.printLine(chalk.gray(' Example: /debug src/app.ts:42'));
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
tui.printLine(chalk.cyan('\n╔═ Debug Analysis ═╗'));
|
|
323
|
+
tui.printLine(chalk.white(` Error: ${errorText}`));
|
|
324
|
+
tui.printLine(chalk.gray(' Analyzing...'));
|
|
325
|
+
const result = await agent.chat(`Debug this error and find the root cause:\n\n${errorText}`);
|
|
326
|
+
if (result.content) {
|
|
327
|
+
tui.printLine(chalk.green(`\n${result.content}`));
|
|
328
|
+
}
|
|
329
|
+
return true;
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
reg({
|
|
333
|
+
name: 'context',
|
|
334
|
+
aliases: ['ctx', 'tokens'],
|
|
335
|
+
description: 'Context window usage',
|
|
336
|
+
category: 'system',
|
|
337
|
+
handler: async () => {
|
|
338
|
+
const messages = agent.getMessages();
|
|
339
|
+
const config = configManager.getConfig();
|
|
340
|
+
const maxCtx = config.context.maxTokens;
|
|
341
|
+
let totalTokens = 0;
|
|
342
|
+
for (const m of messages) {
|
|
343
|
+
totalTokens += countTokens(m.content);
|
|
344
|
+
}
|
|
345
|
+
const pct = Math.round((totalTokens / maxCtx) * 100);
|
|
346
|
+
const bar = getProgressBar(pct, 20);
|
|
347
|
+
tui.printLine(chalk.cyan('\nContext Window Usage:'));
|
|
348
|
+
tui.printLine(` Max: ${chalk.white(maxCtx.toLocaleString())} tokens`);
|
|
349
|
+
tui.printLine(` Used: ${chalk.yellow(totalTokens.toLocaleString())} tokens`);
|
|
350
|
+
tui.printLine(` Usage: ${bar} ${chalk.yellow(`${pct}%`)}`);
|
|
351
|
+
tui.printLine(` Messages: ${chalk.white(String(messages.length))}`);
|
|
352
|
+
if (pct > 80) {
|
|
353
|
+
tui.printLine(chalk.yellow(' ⚠ Context usage high. Use /compress to free tokens.'));
|
|
354
|
+
}
|
|
355
|
+
return true;
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
reg({
|
|
359
|
+
name: 'compress',
|
|
360
|
+
aliases: ['compress-context'],
|
|
361
|
+
description: 'Compress context',
|
|
362
|
+
category: 'system',
|
|
363
|
+
handler: async () => {
|
|
364
|
+
tui.printLine(chalk.cyan('\nCompressing context...'));
|
|
365
|
+
const messages = agent.getMessages();
|
|
366
|
+
if (messages.length <= 5) {
|
|
367
|
+
tui.printLine(chalk.yellow('Not enough messages to compress'));
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
const toCompress = messages.slice(1, -3);
|
|
371
|
+
const summary = `[Context compressed: ${toCompress.length} messages summarized]`;
|
|
372
|
+
agent.reset();
|
|
373
|
+
for (const m of messages.slice(-3)) {
|
|
374
|
+
agent.state.messages.push(m);
|
|
375
|
+
}
|
|
376
|
+
memoryManager.addSummary(summary, 'conversation');
|
|
377
|
+
tui.printLine(chalk.green(`✓ Compressed ${toCompress.length} messages into summary`));
|
|
378
|
+
return true;
|
|
379
|
+
},
|
|
380
|
+
});
|
|
381
|
+
reg({
|
|
382
|
+
name: 'read',
|
|
383
|
+
aliases: ['cat', 'show'],
|
|
384
|
+
description: 'Read file or directory',
|
|
385
|
+
category: 'files',
|
|
386
|
+
handler: handleRead,
|
|
387
|
+
});
|
|
388
|
+
reg({
|
|
389
|
+
name: 'edit',
|
|
390
|
+
aliases: ['modify'],
|
|
391
|
+
description: 'AI-powered file editing',
|
|
392
|
+
category: 'files',
|
|
393
|
+
handler: handleEdit,
|
|
394
|
+
});
|
|
395
|
+
reg({
|
|
396
|
+
name: 'create',
|
|
397
|
+
aliases: ['new', 'touch'],
|
|
398
|
+
description: 'Create file with template',
|
|
399
|
+
category: 'files',
|
|
400
|
+
handler: handleCreate,
|
|
401
|
+
});
|
|
402
|
+
reg({
|
|
403
|
+
name: 'delete',
|
|
404
|
+
aliases: ['rm', 'remove'],
|
|
405
|
+
description: 'Safe delete to trash',
|
|
406
|
+
category: 'files',
|
|
407
|
+
handler: handleDelete,
|
|
408
|
+
});
|
|
409
|
+
reg({
|
|
410
|
+
name: 'search',
|
|
411
|
+
aliases: ['find', 'grep'],
|
|
412
|
+
description: 'Search codebase',
|
|
413
|
+
category: 'files',
|
|
414
|
+
handler: handleSearch,
|
|
415
|
+
});
|
|
416
|
+
reg({
|
|
417
|
+
name: 'list',
|
|
418
|
+
aliases: ['ls', 'dir', 'tree'],
|
|
419
|
+
description: 'Directory tree view',
|
|
420
|
+
category: 'files',
|
|
421
|
+
handler: handleList,
|
|
422
|
+
});
|
|
423
|
+
reg({
|
|
424
|
+
name: 'refactor',
|
|
425
|
+
aliases: [],
|
|
426
|
+
description: 'AI refactor a file',
|
|
427
|
+
category: 'code',
|
|
428
|
+
handler: handleRefactor,
|
|
429
|
+
});
|
|
430
|
+
reg({
|
|
431
|
+
name: 'git',
|
|
432
|
+
aliases: ['g'],
|
|
433
|
+
description: 'Git operations',
|
|
434
|
+
category: 'git',
|
|
435
|
+
handler: handleGit,
|
|
436
|
+
});
|
|
437
|
+
reg({
|
|
438
|
+
name: 'memory',
|
|
439
|
+
aliases: ['mem', 'recall'],
|
|
440
|
+
description: 'View project memory',
|
|
441
|
+
category: 'memory',
|
|
442
|
+
handler: handleMemory,
|
|
443
|
+
});
|
|
444
|
+
reg({
|
|
445
|
+
name: 'remember',
|
|
446
|
+
aliases: ['save', 'store'],
|
|
447
|
+
description: 'Save a memory',
|
|
448
|
+
category: 'memory',
|
|
449
|
+
handler: handleRemember,
|
|
450
|
+
});
|
|
451
|
+
reg({
|
|
452
|
+
name: 'forget',
|
|
453
|
+
aliases: ['remove-mem', 'unremember'],
|
|
454
|
+
description: 'Remove memories',
|
|
455
|
+
category: 'memory',
|
|
456
|
+
handler: handleForget,
|
|
457
|
+
});
|
|
458
|
+
reg({
|
|
459
|
+
name: 'init',
|
|
460
|
+
aliases: ['setup', 'bootstrap'],
|
|
461
|
+
description: 'Scan & setup project',
|
|
462
|
+
category: 'memory',
|
|
463
|
+
handler: handleInit,
|
|
464
|
+
});
|
|
465
|
+
reg({
|
|
466
|
+
name: 'dream',
|
|
467
|
+
aliases: ['consolidate'],
|
|
468
|
+
description: 'Consolidate memory',
|
|
469
|
+
category: 'memory',
|
|
470
|
+
handler: handleDream,
|
|
471
|
+
});
|
|
472
|
+
reg({
|
|
473
|
+
name: 'resume',
|
|
474
|
+
aliases: ['load'],
|
|
475
|
+
description: 'Resume old session',
|
|
476
|
+
category: 'system',
|
|
477
|
+
handler: async (args) => {
|
|
478
|
+
const sessions = sessionManager.listSessions();
|
|
479
|
+
if (sessions.length === 0) {
|
|
480
|
+
tui.printLine(chalk.yellow('No saved sessions'));
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
if (args.length > 0) {
|
|
484
|
+
const id = args[0];
|
|
485
|
+
const session = sessionManager.setCurrentSession(id);
|
|
486
|
+
if (session) {
|
|
487
|
+
tui.printLine(chalk.green(`✓ Resumed session: ${session.name}`));
|
|
488
|
+
}
|
|
489
|
+
else {
|
|
490
|
+
tui.printLine(chalk.red(`Session not found: ${id}`));
|
|
491
|
+
}
|
|
492
|
+
return true;
|
|
493
|
+
}
|
|
494
|
+
tui.printLine(chalk.cyan('\nRecent Sessions:'));
|
|
495
|
+
for (let i = 0; i < sessions.length; i++) {
|
|
496
|
+
const s = sessions[i];
|
|
497
|
+
tui.printLine(` ${chalk.yellow(String(i + 1))}. [${chalk.white(s.id.slice(0, 4))}] ${s.name} — ${s.createdAt}, ${s.messageCount} msgs`);
|
|
498
|
+
}
|
|
499
|
+
tui.printLine(chalk.gray('\nResume which? (number or session ID):'));
|
|
500
|
+
return true;
|
|
501
|
+
},
|
|
502
|
+
});
|
|
503
|
+
reg({
|
|
504
|
+
name: 'rename',
|
|
505
|
+
aliases: ['rename-session'],
|
|
506
|
+
description: 'Rename current session',
|
|
507
|
+
category: 'system',
|
|
508
|
+
handler: async (args) => {
|
|
509
|
+
if (args.length === 0) {
|
|
510
|
+
tui.printLine(chalk.red('Usage: /rename <title>'));
|
|
511
|
+
return true;
|
|
512
|
+
}
|
|
513
|
+
const session = sessionManager.getCurrentSession();
|
|
514
|
+
if (session) {
|
|
515
|
+
sessionManager.renameSession(session.id, args.join(' '));
|
|
516
|
+
tui.printLine(chalk.green(`✓ Session renamed: ${args.join(' ')}`));
|
|
517
|
+
}
|
|
518
|
+
else {
|
|
519
|
+
tui.printLine(chalk.yellow('No active session'));
|
|
520
|
+
}
|
|
521
|
+
return true;
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
reg({
|
|
525
|
+
name: 'export',
|
|
526
|
+
aliases: ['export-session'],
|
|
527
|
+
description: 'Export session',
|
|
528
|
+
category: 'memory',
|
|
529
|
+
handler: async (args) => {
|
|
530
|
+
const fmt = args[0]?.toLowerCase() || 'md';
|
|
531
|
+
const session = sessionManager.getCurrentSession();
|
|
532
|
+
if (!session) {
|
|
533
|
+
tui.printLine(chalk.yellow('No active session'));
|
|
534
|
+
return true;
|
|
535
|
+
}
|
|
536
|
+
const messages = agent.getMessages();
|
|
537
|
+
const { writeFileSync, existsSync, mkdirSync } = await import('fs');
|
|
538
|
+
const { join } = await import('path');
|
|
539
|
+
const exportDir = join(process.cwd(), '.ys', 'exports');
|
|
540
|
+
if (!existsSync(exportDir))
|
|
541
|
+
mkdirSync(exportDir, { recursive: true });
|
|
542
|
+
const fileName = `session-${session.id.slice(0, 8)}-${Date.now()}`;
|
|
543
|
+
if (fmt === 'html' || fmt === 'htm') {
|
|
544
|
+
const html = generateHtmlExport(session, messages);
|
|
545
|
+
writeFileSync(join(exportDir, `${fileName}.html`), html, 'utf-8');
|
|
546
|
+
}
|
|
547
|
+
else if (fmt === 'json') {
|
|
548
|
+
const json = JSON.stringify({ session, messages }, null, 2);
|
|
549
|
+
writeFileSync(join(exportDir, `${fileName}.json`), json, 'utf-8');
|
|
550
|
+
}
|
|
551
|
+
else if (fmt === 'jsonl') {
|
|
552
|
+
const lines = messages.map((m) => JSON.stringify({ role: m.role, content: m.content, timestamp: m.timestamp }));
|
|
553
|
+
writeFileSync(join(exportDir, `${fileName}.jsonl`), lines.join('\n'), 'utf-8');
|
|
554
|
+
}
|
|
555
|
+
else {
|
|
556
|
+
const md = generateMdExport(session, messages);
|
|
557
|
+
writeFileSync(join(exportDir, `${fileName}.md`), md, 'utf-8');
|
|
558
|
+
}
|
|
559
|
+
tui.printLine(chalk.green(`✓ Exported session as ${fmt}`));
|
|
560
|
+
tui.printLine(chalk.gray(` ${join(exportDir, fileName)}.${fmt}`));
|
|
561
|
+
return true;
|
|
562
|
+
},
|
|
563
|
+
});
|
|
564
|
+
reg({
|
|
565
|
+
name: 'tools',
|
|
566
|
+
aliases: ['list-tools'],
|
|
567
|
+
description: 'List available tools',
|
|
568
|
+
category: 'settings',
|
|
569
|
+
handler: async () => {
|
|
570
|
+
const { toolRegistry } = await import('../tools/index.js');
|
|
571
|
+
const tools = toolRegistry.getAll();
|
|
572
|
+
const w = Math.min(phoneConfig.terminalWidth - 2, 50);
|
|
573
|
+
tui.printLine(chalk.cyan(`\n╔═ Available Tools (${tools.length} active) ${'═'.repeat(Math.max(0, w - 24 - String(tools.length).length))}╗`));
|
|
574
|
+
for (const tool of tools) {
|
|
575
|
+
const name = tool.getName();
|
|
576
|
+
const desc = tool.getDescription();
|
|
577
|
+
const padding = ' '.repeat(Math.max(0, w - name.length - desc.length - 6));
|
|
578
|
+
tui.printLine(`║ ${chalk.white(name.padEnd(18))} ${chalk.gray(desc)}${padding} ║`);
|
|
579
|
+
}
|
|
580
|
+
tui.printLine(chalk.cyan(`╚${'═'.repeat(w)}╝`));
|
|
581
|
+
return true;
|
|
582
|
+
},
|
|
583
|
+
});
|
|
584
|
+
reg({
|
|
585
|
+
name: 'key',
|
|
586
|
+
aliases: ['api-key', 'set-key'],
|
|
587
|
+
description: 'Set API key',
|
|
588
|
+
category: 'system',
|
|
589
|
+
handler: async (args) => {
|
|
590
|
+
if (args.length < 1) {
|
|
591
|
+
tui.printLine(chalk.red('Usage: /key <provider> <api_key>'));
|
|
592
|
+
return true;
|
|
593
|
+
}
|
|
594
|
+
const providerName = args[0];
|
|
595
|
+
const apiKey = args.slice(1).join(' ');
|
|
596
|
+
if (!apiKey) {
|
|
597
|
+
tui.printLine(chalk.red('API key is required'));
|
|
598
|
+
return true;
|
|
599
|
+
}
|
|
600
|
+
configManager.setApiKey(providerName, apiKey);
|
|
601
|
+
tui.printLine(chalk.green(`✓ API key set for ${providerName}`));
|
|
602
|
+
return true;
|
|
603
|
+
},
|
|
604
|
+
});
|
|
605
|
+
reg({
|
|
606
|
+
name: 'approval-mode',
|
|
607
|
+
aliases: ['approval', 'mode'],
|
|
608
|
+
description: 'Change approval mode',
|
|
609
|
+
category: 'settings',
|
|
610
|
+
handler: async (args) => {
|
|
611
|
+
const mode = args[0]?.toLowerCase();
|
|
612
|
+
if (mode === 'safe' || mode === 'normal' || mode === 'yolo') {
|
|
613
|
+
tui.setApprovalMode(mode);
|
|
614
|
+
if (mode === 'yolo') {
|
|
615
|
+
tui.printWarning('YOLO mode active. Agent will execute actions without confirmation.');
|
|
616
|
+
}
|
|
617
|
+
tui.printLine(chalk.green(`✓ Approval mode: ${mode}`));
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
tui.printLine(chalk.cyan(`Current approval mode: ${tui.getApprovalMode()}`));
|
|
621
|
+
tui.printLine(chalk.gray(' /approval-mode safe — Always ask before actions'));
|
|
622
|
+
tui.printLine(chalk.gray(' /approval-mode normal — Ask for important actions'));
|
|
623
|
+
tui.printLine(chalk.gray(' /approval-mode yolo — Auto-execute everything'));
|
|
624
|
+
}
|
|
625
|
+
return true;
|
|
626
|
+
},
|
|
627
|
+
});
|
|
628
|
+
reg({
|
|
629
|
+
name: 'theme',
|
|
630
|
+
aliases: [],
|
|
631
|
+
description: 'Change color theme',
|
|
632
|
+
category: 'settings',
|
|
633
|
+
handler: async (args) => {
|
|
634
|
+
const theme = args[0]?.toLowerCase();
|
|
635
|
+
if (theme === 'dark' || theme === 'light' || theme === 'matrix') {
|
|
636
|
+
configManager.set('theme.mode', theme);
|
|
637
|
+
tui.printLine(chalk.green(`✓ Theme: ${theme}`));
|
|
638
|
+
}
|
|
639
|
+
else {
|
|
640
|
+
tui.printLine(chalk.cyan('Usage: /theme [dark|light|matrix]'));
|
|
641
|
+
tui.printLine(chalk.gray(' Current: ' + configManager.getConfig().theme.mode));
|
|
642
|
+
}
|
|
643
|
+
return true;
|
|
644
|
+
},
|
|
645
|
+
});
|
|
646
|
+
reg({
|
|
647
|
+
name: 'config',
|
|
648
|
+
aliases: ['settings', 'preferences'],
|
|
649
|
+
description: 'View configuration',
|
|
650
|
+
category: 'settings',
|
|
651
|
+
handler: async () => {
|
|
652
|
+
const config = configManager.getConfig();
|
|
653
|
+
tui.printLine(chalk.cyan('\nConfiguration:'));
|
|
654
|
+
const { providers, ...rest } = config;
|
|
655
|
+
for (const [key, val] of Object.entries(rest)) {
|
|
656
|
+
if (typeof val === 'object') {
|
|
657
|
+
tui.printLine(` ${chalk.white(key)}:`);
|
|
658
|
+
for (const [sk, sv] of Object.entries(val)) {
|
|
659
|
+
const valStr = sk === 'apiKey' ? (sv ? '***' : '') : String(sv);
|
|
660
|
+
tui.printLine(` ${chalk.gray(sk)}: ${chalk.yellow(valStr)}`);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
tui.printLine(` ${chalk.white(key)}: ${chalk.yellow(String(val))}`);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return true;
|
|
668
|
+
},
|
|
669
|
+
});
|
|
670
|
+
reg({
|
|
671
|
+
name: 'permissions',
|
|
672
|
+
aliases: ['perms'],
|
|
673
|
+
description: 'Tool permissions',
|
|
674
|
+
category: 'settings',
|
|
675
|
+
handler: async () => {
|
|
676
|
+
const config = configManager.getConfig();
|
|
677
|
+
tui.printLine(chalk.cyan('\nPermissions Configuration:'));
|
|
678
|
+
tui.printLine(` Read-only mode: ${config.security.readOnlyMode ? chalk.green('ON') : chalk.gray('OFF')}`);
|
|
679
|
+
tui.printLine(` Sandbox mode: ${config.security.sandboxMode ? chalk.green('ON') : chalk.gray('OFF')}`);
|
|
680
|
+
tui.printLine(` Auto-approve: ${config.permissions.autoApprove ? chalk.green('ON') : chalk.gray('OFF')}`);
|
|
681
|
+
tui.printLine(` Confirm before: ${config.permissions.askForConfirmation ? chalk.green('ON') : chalk.red('OFF')}`);
|
|
682
|
+
tui.printLine(chalk.gray('\n Denied commands:'));
|
|
683
|
+
for (const cmd of config.permissions.deniedCommands) {
|
|
684
|
+
tui.printLine(` ${chalk.red('✕')} ${cmd}`);
|
|
685
|
+
}
|
|
686
|
+
return true;
|
|
687
|
+
},
|
|
688
|
+
});
|
|
689
|
+
reg({
|
|
690
|
+
name: 'review',
|
|
691
|
+
aliases: ['code-review'],
|
|
692
|
+
description: 'Code review',
|
|
693
|
+
category: 'code',
|
|
694
|
+
handler: async (args) => {
|
|
695
|
+
const filePath = args[0];
|
|
696
|
+
if (filePath) {
|
|
697
|
+
const { readFileSync, existsSync } = await import('fs');
|
|
698
|
+
const { resolve } = await import('path');
|
|
699
|
+
const fullPath = resolve(process.cwd(), filePath);
|
|
700
|
+
if (!existsSync(fullPath)) {
|
|
701
|
+
tui.printLine(chalk.red(`File not found: ${filePath}`));
|
|
702
|
+
return true;
|
|
703
|
+
}
|
|
704
|
+
const content = readFileSync(fullPath, 'utf-8');
|
|
705
|
+
tui.printLine(chalk.cyan(`\nReviewing: ${filePath}`));
|
|
706
|
+
const result = await agent.chat(`Code review this file. Find bugs, security issues, performance problems, and readability improvements:\n\n\`\`\`\n${content}\n\`\`\`\n\nFormat as:\n🐛 BUGS (count)\nSECURITY (count)\n⚡ PERFORMANCE (count)\n📖 READABILITY (count)`);
|
|
707
|
+
if (result.content) {
|
|
708
|
+
tui.printLine(chalk.green(`\n${result.content}`));
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
const { execSync } = await import('child_process');
|
|
713
|
+
try {
|
|
714
|
+
const diff = execSync('git diff --cached 2>/dev/null || git diff 2>/dev/null', { encoding: 'utf-8' });
|
|
715
|
+
if (diff.trim()) {
|
|
716
|
+
tui.printLine(chalk.cyan('\nReviewing staged changes...'));
|
|
717
|
+
const result = await agent.chat(`Code review these changes:\n\n${diff.slice(0, 8000)}\n\nFind bugs, security issues, and improvements.`);
|
|
718
|
+
if (result.content) {
|
|
719
|
+
tui.printLine(chalk.green(`\n${result.content}`));
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
else {
|
|
723
|
+
tui.printLine(chalk.yellow('No staged changes to review. Provide a file: /review <path>'));
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
catch {
|
|
727
|
+
tui.printLine(chalk.yellow('Not a git repo. Provide a file: /review <path>'));
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
return true;
|
|
731
|
+
},
|
|
732
|
+
});
|
|
733
|
+
reg({
|
|
734
|
+
name: 'agents',
|
|
735
|
+
aliases: ['subagents', 'agent-list'],
|
|
736
|
+
description: 'Manage subagents',
|
|
737
|
+
category: 'agents',
|
|
738
|
+
handler: handleAgents,
|
|
739
|
+
});
|
|
740
|
+
reg({
|
|
741
|
+
name: 'arena',
|
|
742
|
+
aliases: ['compete'],
|
|
743
|
+
description: 'Multi-model competition',
|
|
744
|
+
category: 'agents',
|
|
745
|
+
handler: handleArena,
|
|
746
|
+
});
|
|
747
|
+
reg({
|
|
748
|
+
name: 'tasks',
|
|
749
|
+
aliases: ['task-list', 'jobs'],
|
|
750
|
+
description: 'Background task manager',
|
|
751
|
+
category: 'agents',
|
|
752
|
+
handler: handleTasks,
|
|
753
|
+
});
|
|
754
|
+
reg({
|
|
755
|
+
name: 'background',
|
|
756
|
+
aliases: ['bg', 'spawn'],
|
|
757
|
+
description: 'Run command in background',
|
|
758
|
+
category: 'agents',
|
|
759
|
+
handler: handleBackground,
|
|
760
|
+
});
|
|
761
|
+
reg({
|
|
762
|
+
name: 'apply',
|
|
763
|
+
aliases: ['execute-plan'],
|
|
764
|
+
description: 'Apply last AI suggestion',
|
|
765
|
+
category: 'code',
|
|
766
|
+
handler: async () => {
|
|
767
|
+
const messages = agent.getMessages();
|
|
768
|
+
const lastAssistant = [...messages].reverse().find((m) => m.role === 'assistant');
|
|
769
|
+
if (!lastAssistant) {
|
|
770
|
+
tui.printLine(chalk.yellow('No suggestion to apply'));
|
|
771
|
+
return true;
|
|
772
|
+
}
|
|
773
|
+
tui.printLine(chalk.cyan('\nApplying last suggestion...'));
|
|
774
|
+
return true;
|
|
775
|
+
},
|
|
776
|
+
});
|
|
777
|
+
function statusBadge(s) {
|
|
778
|
+
const colors = {
|
|
779
|
+
idle: chalk.gray, thinking: chalk.cyan, planning: chalk.yellow,
|
|
780
|
+
executing: chalk.magenta, completed: chalk.green, error: chalk.red,
|
|
781
|
+
};
|
|
782
|
+
return (colors[s] || chalk.white)(s);
|
|
783
|
+
}
|
|
784
|
+
function modeBadge(m) {
|
|
785
|
+
const map = {
|
|
786
|
+
chat: chalk.gray('chat'), plan: chalk.blue('plan'),
|
|
787
|
+
goal: chalk.magenta('goal'), review: chalk.green('review'),
|
|
788
|
+
arena: chalk.yellow('arena'),
|
|
789
|
+
};
|
|
790
|
+
return map[m] || chalk.gray(m);
|
|
791
|
+
}
|
|
792
|
+
function approvalBadge(m) {
|
|
793
|
+
const map = {
|
|
794
|
+
safe: chalk.cyan('safe'), normal: chalk.gray('normal'), yolo: chalk.red('yolo'),
|
|
795
|
+
};
|
|
796
|
+
return map[m] || chalk.gray(m);
|
|
797
|
+
}
|
|
798
|
+
function getProgressBar(pct, width) {
|
|
799
|
+
const filled = Math.round((pct / 100) * width);
|
|
800
|
+
const empty = width - filled;
|
|
801
|
+
const fillChar = '█';
|
|
802
|
+
const emptyChar = '░';
|
|
803
|
+
const color = pct > 80 ? chalk.red : pct > 50 ? chalk.yellow : chalk.green;
|
|
804
|
+
return color(fillChar.repeat(filled)) + chalk.gray(emptyChar.repeat(empty));
|
|
805
|
+
}
|
|
806
|
+
function formatDuration(ms) {
|
|
807
|
+
if (ms < 1000)
|
|
808
|
+
return `${ms}ms`;
|
|
809
|
+
if (ms < 60000)
|
|
810
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
811
|
+
const m = Math.floor(ms / 60000);
|
|
812
|
+
const s = Math.floor((ms % 60000) / 1000);
|
|
813
|
+
return `${m}m ${s}s`;
|
|
814
|
+
}
|
|
815
|
+
function countTokens(text) {
|
|
816
|
+
let tokens = 0;
|
|
817
|
+
for (let i = 0; i < text.length; i++) {
|
|
818
|
+
const char = text[i];
|
|
819
|
+
if (char.match(/[\x00-\x7F]/)) {
|
|
820
|
+
tokens += char.match(/[a-zA-Z0-9]/) ? 0.25 : 1;
|
|
821
|
+
}
|
|
822
|
+
else if (char.match(/[\u4e00-\u9fff\u3040-\u309f\u30a0-\u30ff]/)) {
|
|
823
|
+
tokens += 2;
|
|
824
|
+
}
|
|
825
|
+
else {
|
|
826
|
+
tokens += 1;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
return Math.ceil(tokens);
|
|
830
|
+
}
|
|
831
|
+
async function handlePlanMode(goal) {
|
|
832
|
+
const w = Math.min(phoneConfig.terminalWidth - 2, 56);
|
|
833
|
+
const result = await agent.chat(`Create a detailed step-by-step plan for: ${goal}. Format each step as "- Step N: description" without any code. Just the plan.`);
|
|
834
|
+
if (result.content) {
|
|
835
|
+
const top = `╔${'═'.repeat(w)}╗`;
|
|
836
|
+
const planTitle = ` Plan: ${goal.slice(0, 40)}${goal.length > 40 ? '…' : ''} `;
|
|
837
|
+
tui.printLine('');
|
|
838
|
+
tui.printLine(chalk.blue(top));
|
|
839
|
+
tui.printLine(`║${chalk.white(planTitle)}${' '.repeat(Math.max(0, w - planTitle.length))}║`);
|
|
840
|
+
tui.printLine(`╠${'═'.repeat(w)}╣`);
|
|
841
|
+
const steps = result.content.split('\n').filter((l) => l.trim());
|
|
842
|
+
for (const step of steps) {
|
|
843
|
+
const cleanStep = step.replace(/^[-*]\s*/, '');
|
|
844
|
+
tui.printLine(`║ ${chalk.white(cleanStep)}${' '.repeat(Math.max(0, w - cleanStep.length - 3))}║`);
|
|
845
|
+
}
|
|
846
|
+
tui.printLine(chalk.blue(`╚${'═'.repeat(w)}╝`));
|
|
847
|
+
tui.printLine(chalk.gray('\nType /apply to execute this plan, or continue chatting to refine.'));
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
async function handleGoalMode(task) {
|
|
851
|
+
tui.setStatus('thinking');
|
|
852
|
+
const steps = [
|
|
853
|
+
'Scanned project structure',
|
|
854
|
+
'Analyzing requirements',
|
|
855
|
+
'Reading relevant files',
|
|
856
|
+
'Creating implementation',
|
|
857
|
+
'Verifying changes',
|
|
858
|
+
];
|
|
859
|
+
const progressBar = '─'.repeat(30);
|
|
860
|
+
let currentStep = 0;
|
|
861
|
+
for (const step of steps) {
|
|
862
|
+
currentStep++;
|
|
863
|
+
const pct = Math.round((currentStep / steps.length) * 100);
|
|
864
|
+
const filled = Math.round((currentStep / steps.length) * 30);
|
|
865
|
+
const bar = chalk.green('█'.repeat(filled)) + chalk.gray('░'.repeat(30 - filled));
|
|
866
|
+
tui.printLine(` ${chalk.cyan('◆')} Goal: ${chalk.white(task)}`);
|
|
867
|
+
tui.printLine(` ${bar} ${chalk.yellow(`${pct}%`)}`);
|
|
868
|
+
tui.printLine(` ${chalk.green('○')} ${step}...`);
|
|
869
|
+
tui.printLine('');
|
|
870
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
871
|
+
}
|
|
872
|
+
tui.printLine(chalk.green(`✓ Goal complete: ${task}`));
|
|
873
|
+
tui.setStatus('idle');
|
|
874
|
+
}
|
|
875
|
+
function showCategoryHelp(category) {
|
|
876
|
+
const { ALL_COMMANDS, CATEGORY_ICONS } = require('../ui/CommandPopup.js');
|
|
877
|
+
const icon = CATEGORY_ICONS[category] || ' ';
|
|
878
|
+
const filtered = ALL_COMMANDS.filter((c) => c.category === category);
|
|
879
|
+
tui.printLine(chalk.cyan(`\n${icon} ${category.toUpperCase()} Commands:`));
|
|
880
|
+
for (const cmd of filtered) {
|
|
881
|
+
const usage = cmd.usage || `/${cmd.command}`;
|
|
882
|
+
tui.printLine(` ${chalk.yellow(usage.padEnd(28))} ${chalk.gray(cmd.description)}`);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
function showSpecificHelp(command) {
|
|
886
|
+
const { ALL_COMMANDS } = require('../ui/CommandPopup.js');
|
|
887
|
+
const cmd = ALL_COMMANDS.find((c) => c.command === command);
|
|
888
|
+
if (cmd) {
|
|
889
|
+
tui.printLine(chalk.cyan(`\n/${cmd.command}:`));
|
|
890
|
+
tui.printLine(` ${cmd.description}`);
|
|
891
|
+
if (cmd.usage)
|
|
892
|
+
tui.printLine(` ${chalk.yellow('Usage')}: ${cmd.usage}`);
|
|
893
|
+
}
|
|
894
|
+
else {
|
|
895
|
+
tui.printLine(chalk.yellow(`Unknown command: ${command}`));
|
|
896
|
+
tui.printLine(chalk.gray('Type /help to see all commands'));
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
reg({
|
|
900
|
+
name: 'update',
|
|
901
|
+
aliases: ['upgrade', 'self-update'],
|
|
902
|
+
description: 'Self-update from git (pull + rebuild)',
|
|
903
|
+
category: 'system',
|
|
904
|
+
handler: async () => {
|
|
905
|
+
const { execSync } = await import('child_process');
|
|
906
|
+
const { existsSync } = await import('fs');
|
|
907
|
+
tui.printLine(chalk.cyan('\n◆ Checking for updates...'));
|
|
908
|
+
const isGit = existsSync('.git');
|
|
909
|
+
if (!isGit) {
|
|
910
|
+
tui.printLine(chalk.yellow('Not a git repository. Update manually via npm.'));
|
|
911
|
+
tui.printLine(chalk.gray(' npm install -g git+https://github.com/YSCodex/YSCode.git'));
|
|
912
|
+
return true;
|
|
913
|
+
}
|
|
914
|
+
try {
|
|
915
|
+
tui.printLine(chalk.gray(' Pulling latest code...'));
|
|
916
|
+
execSync('git pull origin main 2>/dev/null || git pull origin master 2>/dev/null', { stdio: 'pipe' });
|
|
917
|
+
tui.printLine(chalk.green(' ✓ Code updated'));
|
|
918
|
+
}
|
|
919
|
+
catch {
|
|
920
|
+
tui.printLine(chalk.yellow(' Already up to date or network issue'));
|
|
921
|
+
}
|
|
922
|
+
try {
|
|
923
|
+
tui.printLine(chalk.gray(' Installing dependencies...'));
|
|
924
|
+
execSync('npm install', { stdio: 'pipe' });
|
|
925
|
+
tui.printLine(chalk.green(' ✓ Dependencies installed'));
|
|
926
|
+
}
|
|
927
|
+
catch (e) {
|
|
928
|
+
tui.printLine(chalk.red(` ✗ npm install failed: ${e}`));
|
|
929
|
+
return true;
|
|
930
|
+
}
|
|
931
|
+
try {
|
|
932
|
+
tui.printLine(chalk.gray(' Building...'));
|
|
933
|
+
execSync('npm run build', { stdio: 'pipe' });
|
|
934
|
+
tui.printLine(chalk.green(' ✓ Build complete'));
|
|
935
|
+
}
|
|
936
|
+
catch (e) {
|
|
937
|
+
tui.printLine(chalk.red(` ✗ Build failed: ${e}`));
|
|
938
|
+
return true;
|
|
939
|
+
}
|
|
940
|
+
tui.printLine(chalk.green('\n✓ YS Code Agent updated successfully!'));
|
|
941
|
+
tui.printLine(chalk.gray(' Restart the agent to use the latest version.'));
|
|
942
|
+
return true;
|
|
943
|
+
},
|
|
944
|
+
});
|
|
945
|
+
reg({
|
|
946
|
+
name: 'vim',
|
|
947
|
+
aliases: ['vim-mode'],
|
|
948
|
+
description: 'Toggle vim keybindings',
|
|
949
|
+
category: 'settings',
|
|
950
|
+
handler: async (args) => {
|
|
951
|
+
const state = args[0]?.toLowerCase();
|
|
952
|
+
const current = configManager.get('vimMode') || false;
|
|
953
|
+
if (state === 'on' || state === 'true' || state === '1') {
|
|
954
|
+
configManager.set('vimMode', true);
|
|
955
|
+
tui.printLine(chalk.green('✓ Vim mode enabled'));
|
|
956
|
+
tui.printLine(chalk.gray(' j/k for up/down, h/l for left/right, dd to clear'));
|
|
957
|
+
}
|
|
958
|
+
else if (state === 'off' || state === 'false' || state === '0') {
|
|
959
|
+
configManager.set('vimMode', false);
|
|
960
|
+
tui.printLine(chalk.green('✓ Vim mode disabled'));
|
|
961
|
+
}
|
|
962
|
+
else {
|
|
963
|
+
const newState = !current;
|
|
964
|
+
configManager.set('vimMode', newState);
|
|
965
|
+
tui.printLine(chalk.green(`✓ Vim mode: ${newState ? 'ON' : 'OFF'}`));
|
|
966
|
+
if (newState)
|
|
967
|
+
tui.printLine(chalk.gray(' j/k: up/down | h/l: left/right | dd: clear line'));
|
|
968
|
+
}
|
|
969
|
+
return true;
|
|
970
|
+
},
|
|
971
|
+
});
|
|
972
|
+
reg({
|
|
973
|
+
name: 'workspace',
|
|
974
|
+
aliases: ['ws', 'workdir'],
|
|
975
|
+
description: 'Manage workspaces',
|
|
976
|
+
category: 'system',
|
|
977
|
+
handler: async (args) => {
|
|
978
|
+
const sub = args[0]?.toLowerCase();
|
|
979
|
+
const { existsSync, readFileSync, writeFileSync, mkdirSync } = await import('fs');
|
|
980
|
+
const { join } = await import('path');
|
|
981
|
+
const { homedir } = await import('os');
|
|
982
|
+
const wsDir = join(homedir(), '.ys', 'workspaces');
|
|
983
|
+
const wsFile = join(wsDir, 'workspaces.json');
|
|
984
|
+
if (!existsSync(wsDir))
|
|
985
|
+
mkdirSync(wsDir, { recursive: true });
|
|
986
|
+
let workspaces = {};
|
|
987
|
+
try {
|
|
988
|
+
if (existsSync(wsFile))
|
|
989
|
+
workspaces = JSON.parse(readFileSync(wsFile, 'utf-8'));
|
|
990
|
+
}
|
|
991
|
+
catch { }
|
|
992
|
+
if (sub === 'save' || sub === 'add') {
|
|
993
|
+
const name = args[1] || basename2(process.cwd());
|
|
994
|
+
workspaces[name] = process.cwd();
|
|
995
|
+
writeFileSync(wsFile, JSON.stringify(workspaces, null, 2), 'utf-8');
|
|
996
|
+
tui.printLine(chalk.green(`✓ Workspace saved: ${name} → ${process.cwd()}`));
|
|
997
|
+
}
|
|
998
|
+
else if (sub === 'load' || sub === 'switch') {
|
|
999
|
+
const name = args[1];
|
|
1000
|
+
if (name && workspaces[name]) {
|
|
1001
|
+
process.chdir(workspaces[name]);
|
|
1002
|
+
tui.printLine(chalk.green(`✓ Switched to workspace: ${name}`));
|
|
1003
|
+
tui.printLine(chalk.gray(` ${workspaces[name]}`));
|
|
1004
|
+
}
|
|
1005
|
+
else if (name) {
|
|
1006
|
+
tui.printLine(chalk.red(`Workspace not found: ${name}`));
|
|
1007
|
+
}
|
|
1008
|
+
else {
|
|
1009
|
+
const keys = Object.keys(workspaces);
|
|
1010
|
+
tui.printLine(chalk.cyan('\nSaved Workspaces:'));
|
|
1011
|
+
for (const k of keys)
|
|
1012
|
+
tui.printLine(` ${chalk.yellow(k)} → ${chalk.gray(workspaces[k])}`);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
else if (sub === 'list' || sub === 'ls') {
|
|
1016
|
+
const keys = Object.keys(workspaces);
|
|
1017
|
+
if (keys.length === 0) {
|
|
1018
|
+
tui.printLine(chalk.yellow('No saved workspaces'));
|
|
1019
|
+
}
|
|
1020
|
+
else {
|
|
1021
|
+
tui.printLine(chalk.cyan('\nSaved Workspaces:'));
|
|
1022
|
+
for (const k of keys)
|
|
1023
|
+
tui.printLine(` ${chalk.yellow(k)} → ${chalk.gray(workspaces[k])}`);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
else if (sub === 'remove' || sub === 'rm' || sub === 'delete') {
|
|
1027
|
+
const name = args[1];
|
|
1028
|
+
if (name && workspaces[name]) {
|
|
1029
|
+
delete workspaces[name];
|
|
1030
|
+
writeFileSync(wsFile, JSON.stringify(workspaces, null, 2), 'utf-8');
|
|
1031
|
+
tui.printLine(chalk.green(`✓ Removed workspace: ${name}`));
|
|
1032
|
+
}
|
|
1033
|
+
else {
|
|
1034
|
+
tui.printLine(chalk.red(`Workspace not found: ${name}`));
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
else {
|
|
1038
|
+
tui.printLine(chalk.cyan('\nWorkspace Manager:'));
|
|
1039
|
+
tui.printLine(` ${chalk.yellow('/workspace save [name]')} ${chalk.gray('Save current directory')}`);
|
|
1040
|
+
tui.printLine(` ${chalk.yellow('/workspace load <name>')} ${chalk.gray('Switch to workspace')}`);
|
|
1041
|
+
tui.printLine(` ${chalk.yellow('/workspace list')} ${chalk.gray('List workspaces')}`);
|
|
1042
|
+
tui.printLine(` ${chalk.yellow('/workspace remove <name>')} ${chalk.gray('Remove workspace')}`);
|
|
1043
|
+
}
|
|
1044
|
+
return true;
|
|
1045
|
+
},
|
|
1046
|
+
});
|
|
1047
|
+
reg({
|
|
1048
|
+
name: 'project-index',
|
|
1049
|
+
aliases: ['index', 'scan', 'code-index'],
|
|
1050
|
+
description: 'Index project for semantic search',
|
|
1051
|
+
category: 'files',
|
|
1052
|
+
handler: async (args) => {
|
|
1053
|
+
const { readFileSync, statSync, readdirSync, existsSync, writeFileSync, mkdirSync } = await import('fs');
|
|
1054
|
+
const { join, extname, relative, resolve } = await import('path');
|
|
1055
|
+
tui.printLine(chalk.cyan('\n◆ Indexing project...'));
|
|
1056
|
+
const startTime = Date.now();
|
|
1057
|
+
const projectRoot = process.cwd();
|
|
1058
|
+
const idxDir = join(projectRoot, '.ys', 'index');
|
|
1059
|
+
if (!existsSync(idxDir))
|
|
1060
|
+
mkdirSync(idxDir, { recursive: true });
|
|
1061
|
+
const ignore = ['node_modules', '.git', 'dist', 'build', '.next', '__pycache__', '.ys'];
|
|
1062
|
+
const extensions = ['.ts', '.tsx', '.js', '.jsx', '.py', '.rs', '.go', '.java', '.kt', '.swift', '.php', '.rb', '.c', '.cpp', '.h', '.hpp', '.cs', '.vue', '.svelte', '.html', '.css', '.scss', '.json', '.yaml', '.yml', '.md', '.toml'];
|
|
1063
|
+
let totalFiles = 0;
|
|
1064
|
+
let totalLines = 0;
|
|
1065
|
+
const index = [];
|
|
1066
|
+
function scan(dir) {
|
|
1067
|
+
let entries = [];
|
|
1068
|
+
try {
|
|
1069
|
+
entries = readdirSync(dir);
|
|
1070
|
+
}
|
|
1071
|
+
catch {
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
for (const entry of entries) {
|
|
1075
|
+
if (ignore.includes(entry) || entry.startsWith('.'))
|
|
1076
|
+
continue;
|
|
1077
|
+
const full = join(dir, entry);
|
|
1078
|
+
try {
|
|
1079
|
+
const stat = statSync(full);
|
|
1080
|
+
if (stat.isDirectory())
|
|
1081
|
+
scan(full);
|
|
1082
|
+
else if (stat.isFile()) {
|
|
1083
|
+
const ext = extname(entry).toLowerCase();
|
|
1084
|
+
if (extensions.includes(ext)) {
|
|
1085
|
+
const content = readFileSync(full, 'utf-8');
|
|
1086
|
+
const lines = content.split('\n').length;
|
|
1087
|
+
const rel = relative(projectRoot, full);
|
|
1088
|
+
const lang = getLangFromExt(ext);
|
|
1089
|
+
index.push({ path: rel, lines, size: stat.size, lang });
|
|
1090
|
+
totalFiles++;
|
|
1091
|
+
totalLines += lines;
|
|
1092
|
+
if (totalFiles % 100 === 0)
|
|
1093
|
+
tui.printLine(chalk.gray(` Indexed ${totalFiles} files...`));
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
catch { }
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
scan(projectRoot);
|
|
1101
|
+
const idxData = {
|
|
1102
|
+
project: basename2(projectRoot),
|
|
1103
|
+
indexedAt: new Date().toISOString(),
|
|
1104
|
+
totalFiles,
|
|
1105
|
+
totalLines,
|
|
1106
|
+
files: index,
|
|
1107
|
+
};
|
|
1108
|
+
writeFileSync(join(idxDir, 'code-index.json'), JSON.stringify(idxData, null, 2), 'utf-8');
|
|
1109
|
+
const duration = Date.now() - startTime;
|
|
1110
|
+
tui.printLine(chalk.green(`\n✓ Indexed ${totalFiles} files (${totalLines.toLocaleString()} lines) in ${duration}ms`));
|
|
1111
|
+
tui.printLine(chalk.gray(` Index: ${join(idxDir, 'code-index.json')}`));
|
|
1112
|
+
if (args.includes('--search') || args.includes('-s')) {
|
|
1113
|
+
const query = args.filter(a => !a.startsWith('-')).join(' ');
|
|
1114
|
+
if (query) {
|
|
1115
|
+
const results = index.filter(f => f.path.toLowerCase().includes(query.toLowerCase()) ||
|
|
1116
|
+
f.lang.toLowerCase().includes(query.toLowerCase())).slice(0, 20);
|
|
1117
|
+
tui.printLine(chalk.cyan(`\nSearch results for "${query}":`));
|
|
1118
|
+
for (const r of results) {
|
|
1119
|
+
tui.printLine(` ${chalk.white(r.path)} ${chalk.gray(`(${r.lines} lines, ${r.lang})`)}`);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
return true;
|
|
1124
|
+
},
|
|
1125
|
+
});
|
|
1126
|
+
reg({
|
|
1127
|
+
name: 'watch',
|
|
1128
|
+
aliases: ['monitor', 'fswatch'],
|
|
1129
|
+
description: 'Watch files for changes',
|
|
1130
|
+
category: 'files',
|
|
1131
|
+
handler: async (args) => {
|
|
1132
|
+
const path = args[0] || '.';
|
|
1133
|
+
const { watch, existsSync, statSync } = await import('fs');
|
|
1134
|
+
const { resolve } = await import('path');
|
|
1135
|
+
const fullPath = resolve(process.cwd(), path);
|
|
1136
|
+
if (!existsSync(fullPath)) {
|
|
1137
|
+
tui.printLine(chalk.red(`Path not found: ${path}`));
|
|
1138
|
+
return true;
|
|
1139
|
+
}
|
|
1140
|
+
tui.printLine(chalk.cyan(`\n◆ Watching: ${path}`));
|
|
1141
|
+
tui.printLine(chalk.gray(' Press Ctrl+C to stop watching'));
|
|
1142
|
+
tui.printLine('');
|
|
1143
|
+
const stat = statSync(fullPath);
|
|
1144
|
+
if (stat.isDirectory()) {
|
|
1145
|
+
try {
|
|
1146
|
+
const watcher = watch(fullPath, { recursive: true }, (eventType, filename) => {
|
|
1147
|
+
if (filename) {
|
|
1148
|
+
const ts = new Date().toLocaleTimeString();
|
|
1149
|
+
tui.printLine(chalk.gray(` [${ts}] ${chalk.white(String(filename))}: ${eventType === 'change' ? chalk.yellow('modified') : chalk.green('added/removed')}`));
|
|
1150
|
+
}
|
|
1151
|
+
});
|
|
1152
|
+
process._ysWatcher = watcher;
|
|
1153
|
+
}
|
|
1154
|
+
catch {
|
|
1155
|
+
tui.printLine(chalk.yellow(' File watching not available on this platform'));
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
else {
|
|
1159
|
+
try {
|
|
1160
|
+
const watcher = watch(fullPath, (eventType) => {
|
|
1161
|
+
const ts = new Date().toLocaleTimeString();
|
|
1162
|
+
tui.printLine(chalk.gray(` [${ts}] ${chalk.white(path)}: ${eventType === 'change' ? chalk.yellow('modified') : chalk.green('changed')}`));
|
|
1163
|
+
});
|
|
1164
|
+
process._ysWatcher = watcher;
|
|
1165
|
+
}
|
|
1166
|
+
catch {
|
|
1167
|
+
tui.printLine(chalk.yellow(' File watching not available on this platform'));
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
return true;
|
|
1171
|
+
},
|
|
1172
|
+
});
|
|
1173
|
+
reg({
|
|
1174
|
+
name: 'publish',
|
|
1175
|
+
aliases: ['npm-publish', 'release'],
|
|
1176
|
+
description: 'Publish to npm registry',
|
|
1177
|
+
category: 'system',
|
|
1178
|
+
handler: async () => {
|
|
1179
|
+
const { execSync } = await import('child_process');
|
|
1180
|
+
tui.printLine(chalk.cyan('\n◆ Publishing to npm...'));
|
|
1181
|
+
tui.printLine(chalk.gray(' This will:'));
|
|
1182
|
+
tui.printLine(chalk.gray(' 1. Build the project'));
|
|
1183
|
+
tui.printLine(chalk.gray(' 2. Publish to npm registry'));
|
|
1184
|
+
tui.printLine(chalk.yellow('\n Continue? [y/N]'));
|
|
1185
|
+
const { createInterface } = await import('readline');
|
|
1186
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1187
|
+
rl.question('', async (answer) => {
|
|
1188
|
+
rl.close();
|
|
1189
|
+
if (answer.trim().toLowerCase() !== 'y') {
|
|
1190
|
+
tui.printLine(chalk.yellow('Publish cancelled'));
|
|
1191
|
+
return;
|
|
1192
|
+
}
|
|
1193
|
+
try {
|
|
1194
|
+
tui.printLine(chalk.gray(' Building...'));
|
|
1195
|
+
execSync('npm run build', { stdio: 'pipe' });
|
|
1196
|
+
tui.printLine(chalk.green(' ✓ Build complete'));
|
|
1197
|
+
tui.printLine(chalk.gray(' Publishing...'));
|
|
1198
|
+
execSync('npm publish --access public', { stdio: 'pipe' });
|
|
1199
|
+
tui.printLine(chalk.green('\n✓ Published to npm!'));
|
|
1200
|
+
tui.printLine(chalk.gray(' npm install -g ys-code-agent'));
|
|
1201
|
+
}
|
|
1202
|
+
catch (e) {
|
|
1203
|
+
tui.printLine(chalk.red(`\n✗ Publish failed: ${e.stderr || e.message}`));
|
|
1204
|
+
tui.printLine(chalk.gray(' Make sure you are logged in: npm login'));
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1207
|
+
return true;
|
|
1208
|
+
},
|
|
1209
|
+
});
|
|
1210
|
+
function generateMdExport(session, messages) {
|
|
1211
|
+
const lines = [
|
|
1212
|
+
`# YS Code Agent — Session Export`,
|
|
1213
|
+
``,
|
|
1214
|
+
`**Session**: ${session.name}`,
|
|
1215
|
+
`**ID**: ${session.id.slice(0, 8)}`,
|
|
1216
|
+
`**Date**: ${new Date().toISOString()}`,
|
|
1217
|
+
`**Messages**: ${messages.length}`,
|
|
1218
|
+
``,
|
|
1219
|
+
`---`,
|
|
1220
|
+
``,
|
|
1221
|
+
];
|
|
1222
|
+
for (const msg of messages) {
|
|
1223
|
+
if (msg.role === 'system')
|
|
1224
|
+
continue;
|
|
1225
|
+
const role = msg.role === 'user' ? '**User**' : '**Assistant**';
|
|
1226
|
+
const date = new Date(msg.timestamp).toISOString().slice(0, 19).replace('T', ' ');
|
|
1227
|
+
lines.push(`### ${role} (${date})`);
|
|
1228
|
+
lines.push('');
|
|
1229
|
+
lines.push(msg.content);
|
|
1230
|
+
lines.push('');
|
|
1231
|
+
lines.push('---');
|
|
1232
|
+
lines.push('');
|
|
1233
|
+
}
|
|
1234
|
+
return lines.join('\n');
|
|
1235
|
+
}
|
|
1236
|
+
function generateHtmlExport(session, messages) {
|
|
1237
|
+
const msgHtml = messages
|
|
1238
|
+
.filter((m) => m.role !== 'system')
|
|
1239
|
+
.map((m) => {
|
|
1240
|
+
const role = m.role === 'user' ? 'user' : 'assistant';
|
|
1241
|
+
const content = m.content
|
|
1242
|
+
.replace(/&/g, '&')
|
|
1243
|
+
.replace(/</g, '<')
|
|
1244
|
+
.replace(/>/g, '>')
|
|
1245
|
+
.replace(/```(\w+)?\n([\s\S]*?)```/g, '<pre><code class="language-$1">$2</code></pre>')
|
|
1246
|
+
.replace(/\n/g, '<br>');
|
|
1247
|
+
return `<div class="message ${role}"><div class="role">${role}</div><div class="content">${content}</div></div>`;
|
|
1248
|
+
})
|
|
1249
|
+
.join('\n');
|
|
1250
|
+
return `<!DOCTYPE html>
|
|
1251
|
+
<html lang="en">
|
|
1252
|
+
<head>
|
|
1253
|
+
<meta charset="UTF-8">
|
|
1254
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1255
|
+
<title>YS Agent — ${session.name}</title>
|
|
1256
|
+
<style>
|
|
1257
|
+
body { font-family: monospace; max-width: 800px; margin: 0 auto; padding: 20px; background: #1a1b26; color: #c0caf5; }
|
|
1258
|
+
h1 { color: #7dcfff; border-bottom: 2px solid #7dcfff; }
|
|
1259
|
+
.message { margin: 10px 0; padding: 10px; border-radius: 4px; }
|
|
1260
|
+
.message.user { background: #1f2335; border-left: 3px solid #7aa2f7; }
|
|
1261
|
+
.message.assistant { background: #1f2335; border-left: 3px solid #9ece6a; }
|
|
1262
|
+
.role { font-weight: bold; color: #7dcfff; margin-bottom: 5px; text-transform: uppercase; }
|
|
1263
|
+
.content { line-height: 1.5; }
|
|
1264
|
+
pre { background: #0f0f1a; padding: 10px; border-radius: 4px; overflow-x: auto; }
|
|
1265
|
+
code { font-family: monospace; }
|
|
1266
|
+
.meta { color: #565f89; font-size: 0.9em; }
|
|
1267
|
+
</style>
|
|
1268
|
+
</head>
|
|
1269
|
+
<body>
|
|
1270
|
+
<h1>YS Code Agent — ${session.name}</h1>
|
|
1271
|
+
<p class="meta">Session: ${session.id.slice(0, 8)} | Messages: ${messages.length} | ${new Date().toISOString()}</p>
|
|
1272
|
+
${msgHtml}
|
|
1273
|
+
</body>
|
|
1274
|
+
</html>`;
|
|
1275
|
+
}
|
|
1276
|
+
function basename2(p) {
|
|
1277
|
+
return p.split(/[/\\]/).pop() || 'project';
|
|
1278
|
+
}
|
|
1279
|
+
function getLangFromExt(ext) {
|
|
1280
|
+
const map = {
|
|
1281
|
+
'.ts': 'TypeScript', '.tsx': 'TSX', '.js': 'JavaScript', '.jsx': 'JSX',
|
|
1282
|
+
'.py': 'Python', '.rs': 'Rust', '.go': 'Go', '.java': 'Java',
|
|
1283
|
+
'.kt': 'Kotlin', '.swift': 'Swift', '.php': 'PHP', '.rb': 'Ruby',
|
|
1284
|
+
'.c': 'C', '.cpp': 'C++', '.h': 'C Header', '.hpp': 'C++ Header',
|
|
1285
|
+
'.cs': 'C#', '.vue': 'Vue', '.svelte': 'Svelte',
|
|
1286
|
+
'.html': 'HTML', '.css': 'CSS', '.scss': 'SCSS',
|
|
1287
|
+
'.json': 'JSON', '.yaml': 'YAML', '.yml': 'YAML',
|
|
1288
|
+
'.md': 'Markdown', '.toml': 'TOML',
|
|
1289
|
+
};
|
|
1290
|
+
return map[ext] || 'Unknown';
|
|
1291
|
+
}
|
|
1292
|
+
export function getCommand(name) {
|
|
1293
|
+
return commands.get(name.toLowerCase());
|
|
1294
|
+
}
|
|
1295
|
+
export function getAllCommands() {
|
|
1296
|
+
return [...new Set(commands.values())];
|
|
1297
|
+
}
|
|
1298
|
+
export async function executeCommand(input) {
|
|
1299
|
+
const parts = input.slice(1).split(' ');
|
|
1300
|
+
const cmdName = parts[0].toLowerCase();
|
|
1301
|
+
const args = parts.slice(1);
|
|
1302
|
+
const cmd = getCommand(cmdName);
|
|
1303
|
+
if (!cmd) {
|
|
1304
|
+
tui.printLine(chalk.yellow(`Unknown command: ${input}`));
|
|
1305
|
+
tui.printLine(chalk.gray('Type /help to see available commands'));
|
|
1306
|
+
return false;
|
|
1307
|
+
}
|
|
1308
|
+
logger.info(`Executing command: ${cmdName}`);
|
|
1309
|
+
return cmd.handler(args, input);
|
|
1310
|
+
}
|
|
1311
|
+
//# sourceMappingURL=CommandRegistry.js.map
|