vigthoria-cli 1.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/README.md +413 -0
- package/dist/commands/auth.d.ts +24 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +194 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/chat.d.ts +64 -0
- package/dist/commands/chat.d.ts.map +1 -0
- package/dist/commands/chat.js +596 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/config.d.ts +25 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +291 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/edit.d.ts +28 -0
- package/dist/commands/edit.d.ts.map +1 -0
- package/dist/commands/edit.js +257 -0
- package/dist/commands/edit.js.map +1 -0
- package/dist/commands/explain.d.ts +21 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +98 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/generate.d.ts +25 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +155 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/review.d.ts +24 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +153 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +205 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/api.d.ts +88 -0
- package/dist/utils/api.d.ts.map +1 -0
- package/dist/utils/api.js +431 -0
- package/dist/utils/api.js.map +1 -0
- package/dist/utils/config.d.ts +57 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +167 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/files.d.ts +31 -0
- package/dist/utils/files.d.ts.map +1 -0
- package/dist/utils/files.js +217 -0
- package/dist/utils/files.js.map +1 -0
- package/dist/utils/logger.d.ts +23 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +104 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/session.d.ts +61 -0
- package/dist/utils/session.d.ts.map +1 -0
- package/dist/utils/session.js +172 -0
- package/dist/utils/session.js.map +1 -0
- package/dist/utils/tools.d.ts +145 -0
- package/dist/utils/tools.d.ts.map +1 -0
- package/dist/utils/tools.js +781 -0
- package/dist/utils/tools.js.map +1 -0
- package/install.sh +248 -0
- package/package.json +52 -0
- package/src/commands/auth.ts +225 -0
- package/src/commands/chat.ts +690 -0
- package/src/commands/config.ts +297 -0
- package/src/commands/edit.ts +310 -0
- package/src/commands/explain.ts +115 -0
- package/src/commands/generate.ts +177 -0
- package/src/commands/review.ts +186 -0
- package/src/index.ts +221 -0
- package/src/types/marked-terminal.d.ts +31 -0
- package/src/utils/api.ts +531 -0
- package/src/utils/config.ts +224 -0
- package/src/utils/files.ts +212 -0
- package/src/utils/logger.ts +125 -0
- package/src/utils/session.ts +167 -0
- package/src/utils/tools.ts +933 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive Chat Command for Vigthoria CLI
|
|
3
|
+
*
|
|
4
|
+
* Now with Claude Code-like agentic capabilities!
|
|
5
|
+
*/
|
|
6
|
+
import { Config } from '../utils/config.js';
|
|
7
|
+
import { Logger } from '../utils/logger.js';
|
|
8
|
+
interface ChatOptions {
|
|
9
|
+
model: string;
|
|
10
|
+
project: string;
|
|
11
|
+
agent?: boolean;
|
|
12
|
+
autoApprove?: boolean;
|
|
13
|
+
resume?: boolean;
|
|
14
|
+
stream?: boolean;
|
|
15
|
+
local?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare class ChatCommand {
|
|
18
|
+
private config;
|
|
19
|
+
private logger;
|
|
20
|
+
private api;
|
|
21
|
+
private messages;
|
|
22
|
+
private fileUtils;
|
|
23
|
+
private marked;
|
|
24
|
+
private tools;
|
|
25
|
+
private agentMode;
|
|
26
|
+
private rl;
|
|
27
|
+
private sessionManager;
|
|
28
|
+
private currentSession;
|
|
29
|
+
private streamMode;
|
|
30
|
+
private localMode;
|
|
31
|
+
constructor(config: Config, logger: Logger);
|
|
32
|
+
run(options: ChatOptions): Promise<void>;
|
|
33
|
+
private buildSystemPrompt;
|
|
34
|
+
private printWelcome;
|
|
35
|
+
private startRepl;
|
|
36
|
+
private chat;
|
|
37
|
+
/**
|
|
38
|
+
* Execute tool calls from AI response (Claude Code-like)
|
|
39
|
+
*/
|
|
40
|
+
private executeToolCalls;
|
|
41
|
+
/**
|
|
42
|
+
* Format tool results for AI context
|
|
43
|
+
*/
|
|
44
|
+
private formatToolResults;
|
|
45
|
+
/**
|
|
46
|
+
* Truncate long output for context management
|
|
47
|
+
*/
|
|
48
|
+
private truncateOutput;
|
|
49
|
+
/**
|
|
50
|
+
* Ask user for permission to execute dangerous action
|
|
51
|
+
*/
|
|
52
|
+
private askPermission;
|
|
53
|
+
private addFileToContext;
|
|
54
|
+
private startEditMode;
|
|
55
|
+
private showPendingDiff;
|
|
56
|
+
private applyChanges;
|
|
57
|
+
private printHelp;
|
|
58
|
+
private listSessions;
|
|
59
|
+
private showHistory;
|
|
60
|
+
private compactContext;
|
|
61
|
+
private printModels;
|
|
62
|
+
}
|
|
63
|
+
export {};
|
|
64
|
+
//# sourceMappingURL=chat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAM5C,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,GAAG,CAAY;IACvB,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,EAAE,CAAmC;IAC7C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,UAAU,CAAiB;IACnC,OAAO,CAAC,SAAS,CAAkB;gBAEvB,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAYpC,GAAG,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IA+D9C,OAAO,CAAC,iBAAiB;IAqCzB,OAAO,CAAC,YAAY;YAmBN,SAAS;YAoMT,IAAI;IA+ClB;;OAEG;YACW,gBAAgB;IA+B9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAqBzB;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;YACW,aAAa;YAgBb,gBAAgB;YAkBhB,aAAa;IAqB3B,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,SAAS;IA4BjB,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,WAAW;YAkBL,cAAc;IA6C5B,OAAO,CAAC,WAAW;CAUpB"}
|
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Interactive Chat Command for Vigthoria CLI
|
|
4
|
+
*
|
|
5
|
+
* Now with Claude Code-like agentic capabilities!
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
41
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
42
|
+
};
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.ChatCommand = void 0;
|
|
45
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
46
|
+
const ora_1 = __importDefault(require("ora"));
|
|
47
|
+
const readline = __importStar(require("readline"));
|
|
48
|
+
const marked_1 = require("marked");
|
|
49
|
+
const marked_terminal_1 = require("marked-terminal");
|
|
50
|
+
const api_js_1 = require("../utils/api.js");
|
|
51
|
+
const files_js_1 = require("../utils/files.js");
|
|
52
|
+
const tools_js_1 = require("../utils/tools.js");
|
|
53
|
+
const session_js_1 = require("../utils/session.js");
|
|
54
|
+
class ChatCommand {
|
|
55
|
+
config;
|
|
56
|
+
logger;
|
|
57
|
+
api;
|
|
58
|
+
messages = [];
|
|
59
|
+
fileUtils;
|
|
60
|
+
marked;
|
|
61
|
+
tools = null;
|
|
62
|
+
agentMode = false;
|
|
63
|
+
rl = null;
|
|
64
|
+
sessionManager;
|
|
65
|
+
currentSession = null;
|
|
66
|
+
streamMode = true;
|
|
67
|
+
localMode = false;
|
|
68
|
+
constructor(config, logger) {
|
|
69
|
+
this.config = config;
|
|
70
|
+
this.logger = logger;
|
|
71
|
+
this.api = new api_js_1.APIClient(config, logger);
|
|
72
|
+
this.fileUtils = new files_js_1.FileUtils(process.cwd(), config.get('project').ignorePatterns);
|
|
73
|
+
this.sessionManager = new session_js_1.SessionManager();
|
|
74
|
+
// Setup marked for terminal rendering
|
|
75
|
+
this.marked = new marked_1.Marked();
|
|
76
|
+
this.marked.use((0, marked_terminal_1.markedTerminal)());
|
|
77
|
+
}
|
|
78
|
+
async run(options) {
|
|
79
|
+
// Check authentication (skip for local testing mode)
|
|
80
|
+
if (!options.local && !this.config.isAuthenticated()) {
|
|
81
|
+
this.logger.error('Not authenticated. Run: vigthoria login');
|
|
82
|
+
this.logger.info('Tip: Use --local flag to test with local Ollama models');
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (!options.local && !this.config.hasValidSubscription()) {
|
|
86
|
+
this.logger.warn('Your subscription has expired. Some features may be limited.');
|
|
87
|
+
}
|
|
88
|
+
if (options.local) {
|
|
89
|
+
this.logger.info('Local mode: Using Ollama for AI responses');
|
|
90
|
+
}
|
|
91
|
+
// Setup agentic tools if enabled
|
|
92
|
+
this.agentMode = options.agent || false;
|
|
93
|
+
this.streamMode = options.stream !== false; // Default to true
|
|
94
|
+
this.localMode = options.local || false;
|
|
95
|
+
if (this.agentMode) {
|
|
96
|
+
this.tools = new tools_js_1.AgenticTools(this.logger, options.project, this.askPermission.bind(this), options.autoApprove || false);
|
|
97
|
+
}
|
|
98
|
+
// Try to resume previous session or create new one
|
|
99
|
+
if (options.resume) {
|
|
100
|
+
this.currentSession = this.sessionManager.getLatest(options.project);
|
|
101
|
+
if (this.currentSession) {
|
|
102
|
+
this.messages = [...this.currentSession.messages];
|
|
103
|
+
this.logger.success(`Resumed session: ${this.currentSession.id}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (!this.currentSession) {
|
|
107
|
+
this.currentSession = this.sessionManager.create(options.project, options.model, this.agentMode);
|
|
108
|
+
}
|
|
109
|
+
// Get project context
|
|
110
|
+
const projectContext = await this.fileUtils.getProjectContext();
|
|
111
|
+
// Setup system message
|
|
112
|
+
const systemMessage = {
|
|
113
|
+
role: 'system',
|
|
114
|
+
content: this.buildSystemPrompt(projectContext, options),
|
|
115
|
+
};
|
|
116
|
+
this.messages.push(systemMessage);
|
|
117
|
+
this.printWelcome(options);
|
|
118
|
+
// Start REPL
|
|
119
|
+
await this.startRepl(options);
|
|
120
|
+
}
|
|
121
|
+
buildSystemPrompt(projectContext, options) {
|
|
122
|
+
let prompt = `You are Vigthoria, a premium AI coding assistant. You help developers write, understand, and improve code.
|
|
123
|
+
|
|
124
|
+
Project Context:
|
|
125
|
+
- Type: ${projectContext.type}
|
|
126
|
+
- Root: ${options.project}
|
|
127
|
+
- Key files: ${projectContext.files.slice(0, 10).join(', ')}
|
|
128
|
+
${projectContext.type === 'node' ? `- Dependencies: ${Object.keys(projectContext.dependencies).slice(0, 15).join(', ')}` : ''}
|
|
129
|
+
|
|
130
|
+
Guidelines:
|
|
131
|
+
- Provide complete, production-ready code
|
|
132
|
+
- Use modern best practices (2024-2026 standards)
|
|
133
|
+
- Include proper error handling
|
|
134
|
+
- Explain your reasoning when helpful
|
|
135
|
+
- Be concise but thorough
|
|
136
|
+
|
|
137
|
+
Special Commands (user may use these):
|
|
138
|
+
- /file <path> - Read and include a file in context
|
|
139
|
+
- /edit <path> - Switch to file editing mode
|
|
140
|
+
- /diff - Show pending changes
|
|
141
|
+
- /apply - Apply pending changes
|
|
142
|
+
- /clear - Clear conversation history
|
|
143
|
+
- /model <name> - Switch AI model
|
|
144
|
+
- /agent - Toggle agentic mode (Claude Code-like autonomous actions)
|
|
145
|
+
- /help - Show available commands`;
|
|
146
|
+
// Add tool instructions if in agent mode
|
|
147
|
+
if (this.agentMode && this.tools) {
|
|
148
|
+
prompt += `\n\n${tools_js_1.AgenticTools.getToolsForPrompt()}`;
|
|
149
|
+
}
|
|
150
|
+
return prompt;
|
|
151
|
+
}
|
|
152
|
+
printWelcome(options) {
|
|
153
|
+
const sub = this.config.get('subscription');
|
|
154
|
+
console.log();
|
|
155
|
+
this.logger.box(`Model: ${chalk_1.default.cyan(options.model)}\n` +
|
|
156
|
+
`Plan: ${chalk_1.default.green(sub.plan || 'free')}\n` +
|
|
157
|
+
`Project: ${chalk_1.default.gray(options.project)}\n` +
|
|
158
|
+
`Agent Mode: ${this.agentMode ? chalk_1.default.green('ON ✓') : chalk_1.default.gray('OFF')}`, 'Vigthoria Chat');
|
|
159
|
+
console.log();
|
|
160
|
+
if (this.agentMode) {
|
|
161
|
+
console.log(chalk_1.default.yellow('🤖 Agent Mode: AI can read files, edit code, and run commands autonomously.'));
|
|
162
|
+
}
|
|
163
|
+
console.log(chalk_1.default.gray('Type your message or /help for commands. Press Ctrl+C to exit.'));
|
|
164
|
+
console.log();
|
|
165
|
+
}
|
|
166
|
+
async startRepl(options) {
|
|
167
|
+
this.rl = readline.createInterface({
|
|
168
|
+
input: process.stdin,
|
|
169
|
+
output: process.stdout,
|
|
170
|
+
prompt: chalk_1.default.cyan('you › '),
|
|
171
|
+
});
|
|
172
|
+
let currentModel = options.model;
|
|
173
|
+
let pendingChanges = null;
|
|
174
|
+
this.rl.prompt();
|
|
175
|
+
for await (const line of this.rl) {
|
|
176
|
+
const input = line.trim();
|
|
177
|
+
if (!input) {
|
|
178
|
+
this.rl.prompt();
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
// Handle special commands
|
|
182
|
+
if (input.startsWith('/')) {
|
|
183
|
+
const [cmd, ...args] = input.slice(1).split(' ');
|
|
184
|
+
switch (cmd) {
|
|
185
|
+
case 'help':
|
|
186
|
+
this.printHelp();
|
|
187
|
+
break;
|
|
188
|
+
case 'clear':
|
|
189
|
+
this.messages = [this.messages[0]]; // Keep system message
|
|
190
|
+
this.logger.success('Conversation cleared');
|
|
191
|
+
break;
|
|
192
|
+
case 'model':
|
|
193
|
+
if (args[0]) {
|
|
194
|
+
currentModel = args[0];
|
|
195
|
+
this.logger.success(`Model switched to: ${currentModel}`);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
this.printModels();
|
|
199
|
+
}
|
|
200
|
+
break;
|
|
201
|
+
case 'agent':
|
|
202
|
+
this.agentMode = !this.agentMode;
|
|
203
|
+
if (this.agentMode && !this.tools) {
|
|
204
|
+
this.tools = new tools_js_1.AgenticTools(this.logger, options.project, this.askPermission.bind(this), options.autoApprove || false);
|
|
205
|
+
}
|
|
206
|
+
// Rebuild system prompt with/without tools
|
|
207
|
+
this.messages[0] = {
|
|
208
|
+
role: 'system',
|
|
209
|
+
content: this.buildSystemPrompt(await this.fileUtils.getProjectContext(), { ...options, agent: this.agentMode }),
|
|
210
|
+
};
|
|
211
|
+
this.logger.success(`Agent mode: ${this.agentMode ? chalk_1.default.green('ON') : chalk_1.default.red('OFF')}`);
|
|
212
|
+
if (this.agentMode) {
|
|
213
|
+
console.log(chalk_1.default.yellow(' AI can now read files, edit code, and run commands.'));
|
|
214
|
+
}
|
|
215
|
+
break;
|
|
216
|
+
case 'approve':
|
|
217
|
+
if (this.tools) {
|
|
218
|
+
options.autoApprove = !options.autoApprove;
|
|
219
|
+
this.tools = new tools_js_1.AgenticTools(this.logger, options.project, this.askPermission.bind(this), options.autoApprove);
|
|
220
|
+
this.logger.success(`Auto-approve: ${options.autoApprove ? chalk_1.default.green('ON') : chalk_1.default.red('OFF')}`);
|
|
221
|
+
if (options.autoApprove) {
|
|
222
|
+
console.log(chalk_1.default.red(' ⚠️ AI actions will be executed without confirmation!'));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
break;
|
|
226
|
+
case 'file':
|
|
227
|
+
if (args[0]) {
|
|
228
|
+
await this.addFileToContext(args[0]);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
this.logger.error('Usage: /file <path>');
|
|
232
|
+
}
|
|
233
|
+
break;
|
|
234
|
+
case 'edit':
|
|
235
|
+
if (args[0]) {
|
|
236
|
+
pendingChanges = await this.startEditMode(args[0], currentModel);
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
this.logger.error('Usage: /edit <path>');
|
|
240
|
+
}
|
|
241
|
+
break;
|
|
242
|
+
case 'diff':
|
|
243
|
+
if (pendingChanges) {
|
|
244
|
+
this.showPendingDiff(pendingChanges);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
this.logger.info('No pending changes');
|
|
248
|
+
}
|
|
249
|
+
break;
|
|
250
|
+
case 'apply':
|
|
251
|
+
if (pendingChanges) {
|
|
252
|
+
this.applyChanges(pendingChanges);
|
|
253
|
+
pendingChanges = null;
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
this.logger.info('No pending changes to apply');
|
|
257
|
+
}
|
|
258
|
+
break;
|
|
259
|
+
case 'sessions':
|
|
260
|
+
this.listSessions();
|
|
261
|
+
break;
|
|
262
|
+
case 'history':
|
|
263
|
+
this.showHistory();
|
|
264
|
+
break;
|
|
265
|
+
case 'save':
|
|
266
|
+
if (this.currentSession) {
|
|
267
|
+
this.sessionManager.save(this.currentSession);
|
|
268
|
+
this.logger.success(`Session saved: ${this.currentSession.id}`);
|
|
269
|
+
}
|
|
270
|
+
break;
|
|
271
|
+
case 'new':
|
|
272
|
+
// Start new session
|
|
273
|
+
this.currentSession = this.sessionManager.create(options.project, currentModel, this.agentMode);
|
|
274
|
+
this.messages = [this.messages[0]]; // Keep only system message
|
|
275
|
+
this.logger.success(`New session: ${this.currentSession.id}`);
|
|
276
|
+
break;
|
|
277
|
+
case 'compact':
|
|
278
|
+
// Compact context by summarizing older messages
|
|
279
|
+
await this.compactContext(currentModel);
|
|
280
|
+
break;
|
|
281
|
+
case 'undo':
|
|
282
|
+
// Undo last file operation
|
|
283
|
+
if (this.tools) {
|
|
284
|
+
const undoResult = await this.tools.undo();
|
|
285
|
+
if (undoResult.success) {
|
|
286
|
+
this.logger.success(undoResult.output || 'Undo completed');
|
|
287
|
+
if (undoResult.metadata?.remainingUndos !== undefined) {
|
|
288
|
+
console.log(chalk_1.default.gray(` ${undoResult.metadata.remainingUndos} more undo(s) available`));
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
this.logger.error(undoResult.error || 'Nothing to undo');
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
this.logger.info('Undo is only available in agent mode. Use /agent to enable.');
|
|
297
|
+
}
|
|
298
|
+
break;
|
|
299
|
+
case 'exit':
|
|
300
|
+
case 'quit':
|
|
301
|
+
// Auto-save session on exit
|
|
302
|
+
if (this.currentSession && this.messages.length > 1) {
|
|
303
|
+
this.sessionManager.save(this.currentSession);
|
|
304
|
+
console.log(chalk_1.default.gray(`Session saved: ${this.currentSession.id}`));
|
|
305
|
+
}
|
|
306
|
+
console.log(chalk_1.default.cyan('\nGoodbye! 👋\n'));
|
|
307
|
+
this.rl.close();
|
|
308
|
+
return;
|
|
309
|
+
default:
|
|
310
|
+
this.logger.warn(`Unknown command: /${cmd}`);
|
|
311
|
+
}
|
|
312
|
+
this.rl.prompt();
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
// Regular chat message
|
|
316
|
+
await this.chat(input, currentModel);
|
|
317
|
+
// Save message to session
|
|
318
|
+
if (this.currentSession) {
|
|
319
|
+
this.currentSession.messages = [...this.messages];
|
|
320
|
+
this.sessionManager.save(this.currentSession);
|
|
321
|
+
}
|
|
322
|
+
this.rl.prompt();
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
async chat(userInput, model) {
|
|
326
|
+
// Add user message (skip if empty - used for tool continuation)
|
|
327
|
+
if (userInput) {
|
|
328
|
+
this.messages.push({ role: 'user', content: userInput });
|
|
329
|
+
}
|
|
330
|
+
const spinner = (0, ora_1.default)({
|
|
331
|
+
text: chalk_1.default.gray('Thinking...'),
|
|
332
|
+
spinner: 'dots',
|
|
333
|
+
}).start();
|
|
334
|
+
try {
|
|
335
|
+
const response = await this.api.chat(this.messages, model, this.localMode);
|
|
336
|
+
spinner.stop();
|
|
337
|
+
// Add assistant message
|
|
338
|
+
this.messages.push({ role: 'assistant', content: response.message });
|
|
339
|
+
// Render markdown response
|
|
340
|
+
console.log();
|
|
341
|
+
console.log(chalk_1.default.cyan('vigthoria ›'));
|
|
342
|
+
console.log(this.marked.parse(response.message));
|
|
343
|
+
// Show token usage
|
|
344
|
+
if (response.usage) {
|
|
345
|
+
console.log(chalk_1.default.gray(`[${response.usage.total_tokens} tokens]`));
|
|
346
|
+
}
|
|
347
|
+
console.log();
|
|
348
|
+
// In agent mode, check for and execute tool calls
|
|
349
|
+
if (this.agentMode && this.tools) {
|
|
350
|
+
const toolCalls = tools_js_1.AgenticTools.parseToolCalls(response.message);
|
|
351
|
+
if (toolCalls.length > 0) {
|
|
352
|
+
await this.executeToolCalls(toolCalls, model);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
spinner.stop();
|
|
358
|
+
this.logger.error('Failed to get response:', error.message);
|
|
359
|
+
// Remove failed user message
|
|
360
|
+
this.messages.pop();
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Execute tool calls from AI response (Claude Code-like)
|
|
365
|
+
*/
|
|
366
|
+
async executeToolCalls(calls, model) {
|
|
367
|
+
const results = [];
|
|
368
|
+
for (const call of calls) {
|
|
369
|
+
console.log(chalk_1.default.yellow(`\n⚙ Executing: ${call.tool}`));
|
|
370
|
+
const result = await this.tools.execute(call);
|
|
371
|
+
results.push({ tool: call.tool, result });
|
|
372
|
+
if (result.success) {
|
|
373
|
+
this.logger.success(`${call.tool}: Success`);
|
|
374
|
+
if (result.output) {
|
|
375
|
+
console.log(chalk_1.default.gray(this.truncateOutput(result.output)));
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
this.logger.error(`${call.tool}: ${result.error}`);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// Send tool results back to AI for continuation
|
|
383
|
+
const toolResultsMessage = {
|
|
384
|
+
role: 'user',
|
|
385
|
+
content: this.formatToolResults(results),
|
|
386
|
+
};
|
|
387
|
+
this.messages.push(toolResultsMessage);
|
|
388
|
+
// Get AI's follow-up response
|
|
389
|
+
console.log();
|
|
390
|
+
await this.chat('', model);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Format tool results for AI context
|
|
394
|
+
*/
|
|
395
|
+
formatToolResults(results) {
|
|
396
|
+
let msg = 'Tool execution results:\n\n';
|
|
397
|
+
for (const { tool, result } of results) {
|
|
398
|
+
msg += `## ${tool}\n`;
|
|
399
|
+
msg += `Status: ${result.success ? 'Success' : 'Failed'}\n`;
|
|
400
|
+
if (result.output) {
|
|
401
|
+
msg += `Output:\n\`\`\`\n${this.truncateOutput(result.output)}\n\`\`\`\n`;
|
|
402
|
+
}
|
|
403
|
+
if (result.error) {
|
|
404
|
+
msg += `Error: ${result.error}\n`;
|
|
405
|
+
}
|
|
406
|
+
msg += '\n';
|
|
407
|
+
}
|
|
408
|
+
msg += 'Continue with your analysis or next steps.';
|
|
409
|
+
return msg;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Truncate long output for context management
|
|
413
|
+
*/
|
|
414
|
+
truncateOutput(output, maxLines = 50) {
|
|
415
|
+
const lines = output.split('\n');
|
|
416
|
+
if (lines.length <= maxLines) {
|
|
417
|
+
return output;
|
|
418
|
+
}
|
|
419
|
+
return lines.slice(0, maxLines).join('\n') + `\n... (${lines.length - maxLines} more lines)`;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Ask user for permission to execute dangerous action
|
|
423
|
+
*/
|
|
424
|
+
async askPermission(action) {
|
|
425
|
+
return new Promise((resolve) => {
|
|
426
|
+
console.log('\n' + action);
|
|
427
|
+
if (!this.rl) {
|
|
428
|
+
resolve(false);
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
this.rl.question(chalk_1.default.yellow('Allow? [y/N] '), (answer) => {
|
|
432
|
+
const allowed = answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
|
|
433
|
+
resolve(allowed);
|
|
434
|
+
});
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
async addFileToContext(filePath) {
|
|
438
|
+
const file = this.fileUtils.readFile(filePath);
|
|
439
|
+
if (!file) {
|
|
440
|
+
this.logger.error(`File not found: ${filePath}`);
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
// Add file content to messages
|
|
444
|
+
const fileMessage = {
|
|
445
|
+
role: 'user',
|
|
446
|
+
content: `Here is the content of ${file.relativePath} (${file.language}, ${file.lines} lines):\n\n\`\`\`${file.language}\n${file.content}\n\`\`\``,
|
|
447
|
+
};
|
|
448
|
+
this.messages.push(fileMessage);
|
|
449
|
+
this.logger.success(`Added ${file.relativePath} to context (${file.lines} lines)`);
|
|
450
|
+
}
|
|
451
|
+
async startEditMode(filePath, model) {
|
|
452
|
+
const file = this.fileUtils.readFile(filePath);
|
|
453
|
+
if (!file) {
|
|
454
|
+
this.logger.error(`File not found: ${filePath}`);
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
console.log();
|
|
458
|
+
this.logger.section(`Editing: ${file.relativePath}`);
|
|
459
|
+
console.log(chalk_1.default.gray('What changes would you like to make?'));
|
|
460
|
+
console.log();
|
|
461
|
+
// This would enter a sub-REPL for editing
|
|
462
|
+
// For now, return the file info
|
|
463
|
+
return { file: file.path, content: file.content };
|
|
464
|
+
}
|
|
465
|
+
showPendingDiff(changes) {
|
|
466
|
+
const file = this.fileUtils.readFile(changes.file);
|
|
467
|
+
if (!file)
|
|
468
|
+
return;
|
|
469
|
+
const diff = this.fileUtils.createDiff(file.content, changes.content);
|
|
470
|
+
this.logger.section('Pending Changes');
|
|
471
|
+
this.logger.diff(diff.added, diff.removed);
|
|
472
|
+
}
|
|
473
|
+
applyChanges(changes) {
|
|
474
|
+
// Backup first
|
|
475
|
+
const backup = this.fileUtils.backupFile(changes.file);
|
|
476
|
+
if (backup) {
|
|
477
|
+
this.logger.info(`Backup created: ${backup}`);
|
|
478
|
+
}
|
|
479
|
+
// Apply
|
|
480
|
+
if (this.fileUtils.writeFile(changes.file, changes.content)) {
|
|
481
|
+
this.logger.success(`Changes applied to ${changes.file}`);
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
this.logger.error('Failed to apply changes');
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
printHelp() {
|
|
488
|
+
console.log();
|
|
489
|
+
this.logger.section('Available Commands');
|
|
490
|
+
console.log(chalk_1.default.cyan('/file <path>') + ' - Add file to conversation context');
|
|
491
|
+
console.log(chalk_1.default.cyan('/edit <path>') + ' - Start editing a file');
|
|
492
|
+
console.log(chalk_1.default.cyan('/diff') + ' - Show pending changes');
|
|
493
|
+
console.log(chalk_1.default.cyan('/apply') + ' - Apply pending changes');
|
|
494
|
+
console.log(chalk_1.default.cyan('/model <name>') + ' - Switch AI model');
|
|
495
|
+
console.log(chalk_1.default.cyan('/agent') + ' - Toggle agentic mode (Claude Code-like)');
|
|
496
|
+
console.log(chalk_1.default.cyan('/approve') + ' - Toggle auto-approve for agent actions');
|
|
497
|
+
console.log(chalk_1.default.cyan('/undo') + ' - Undo last file operation (agent mode)');
|
|
498
|
+
console.log(chalk_1.default.cyan('/clear') + ' - Clear conversation history');
|
|
499
|
+
console.log(chalk_1.default.cyan('/compact') + ' - Compact context (summarize older messages)');
|
|
500
|
+
console.log(chalk_1.default.cyan('/sessions') + ' - List saved sessions');
|
|
501
|
+
console.log(chalk_1.default.cyan('/history') + ' - Show conversation history');
|
|
502
|
+
console.log(chalk_1.default.cyan('/save') + ' - Save current session');
|
|
503
|
+
console.log(chalk_1.default.cyan('/new') + ' - Start new session');
|
|
504
|
+
console.log(chalk_1.default.cyan('/help') + ' - Show this help');
|
|
505
|
+
console.log(chalk_1.default.cyan('/exit') + ' - Exit Vigthoria (auto-saves)');
|
|
506
|
+
console.log();
|
|
507
|
+
if (this.agentMode) {
|
|
508
|
+
console.log(chalk_1.default.yellow('Agent Mode Tools:'));
|
|
509
|
+
console.log(chalk_1.default.gray(' read_file, write_file, edit_file, bash, grep, list_dir, glob, git'));
|
|
510
|
+
console.log();
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
listSessions() {
|
|
514
|
+
const sessions = this.sessionManager.list();
|
|
515
|
+
console.log();
|
|
516
|
+
this.logger.section('Saved Sessions');
|
|
517
|
+
if (sessions.length === 0) {
|
|
518
|
+
console.log(chalk_1.default.gray(' No saved sessions'));
|
|
519
|
+
}
|
|
520
|
+
else {
|
|
521
|
+
sessions.slice(0, 10).forEach(s => {
|
|
522
|
+
const current = this.currentSession?.id === s.id ? chalk_1.default.green(' (current)') : '';
|
|
523
|
+
const agent = s.agentMode ? chalk_1.default.yellow(' [agent]') : '';
|
|
524
|
+
console.log(chalk_1.default.cyan(s.id) + agent + current);
|
|
525
|
+
console.log(chalk_1.default.gray(` ${s.project} - ${new Date(s.updatedAt).toLocaleString()}`));
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
console.log();
|
|
529
|
+
}
|
|
530
|
+
showHistory() {
|
|
531
|
+
console.log();
|
|
532
|
+
this.logger.section('Conversation History');
|
|
533
|
+
const userMessages = this.messages.filter(m => m.role !== 'system');
|
|
534
|
+
if (userMessages.length === 0) {
|
|
535
|
+
console.log(chalk_1.default.gray(' No messages yet'));
|
|
536
|
+
}
|
|
537
|
+
else {
|
|
538
|
+
userMessages.forEach((m, i) => {
|
|
539
|
+
const role = m.role === 'user' ? chalk_1.default.cyan('you') : chalk_1.default.green('vigthoria');
|
|
540
|
+
const preview = m.content.substring(0, 60).replace(/\n/g, ' ');
|
|
541
|
+
console.log(`${i + 1}. ${role}: ${chalk_1.default.gray(preview)}...`);
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
console.log();
|
|
545
|
+
}
|
|
546
|
+
async compactContext(model) {
|
|
547
|
+
// If we have too many messages, summarize older ones
|
|
548
|
+
if (this.messages.length < 10) {
|
|
549
|
+
this.logger.info('Context is already compact');
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
const spinner = (0, ora_1.default)({
|
|
553
|
+
text: chalk_1.default.gray('Compacting context...'),
|
|
554
|
+
spinner: 'dots',
|
|
555
|
+
}).start();
|
|
556
|
+
try {
|
|
557
|
+
// Keep system message and last 4 messages
|
|
558
|
+
const systemMessage = this.messages[0];
|
|
559
|
+
const recentMessages = this.messages.slice(-4);
|
|
560
|
+
const olderMessages = this.messages.slice(1, -4);
|
|
561
|
+
if (olderMessages.length === 0) {
|
|
562
|
+
spinner.stop();
|
|
563
|
+
this.logger.info('Nothing to compact');
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
// Ask AI to summarize older conversation
|
|
567
|
+
const summaryResponse = await this.api.chat([
|
|
568
|
+
{ role: 'system', content: 'Summarize this conversation in a concise way, preserving key context and decisions.' },
|
|
569
|
+
...olderMessages,
|
|
570
|
+
], model, this.localMode);
|
|
571
|
+
// Create compacted context
|
|
572
|
+
this.messages = [
|
|
573
|
+
systemMessage,
|
|
574
|
+
{ role: 'system', content: `[Previous conversation summary]: ${summaryResponse.message}` },
|
|
575
|
+
...recentMessages,
|
|
576
|
+
];
|
|
577
|
+
spinner.stop();
|
|
578
|
+
this.logger.success(`Compacted ${olderMessages.length} messages into summary`);
|
|
579
|
+
}
|
|
580
|
+
catch (error) {
|
|
581
|
+
spinner.stop();
|
|
582
|
+
this.logger.error('Failed to compact context:', error.message);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
printModels() {
|
|
586
|
+
const models = this.config.getAvailableModels();
|
|
587
|
+
console.log();
|
|
588
|
+
this.logger.section('Available Models');
|
|
589
|
+
models.forEach(m => {
|
|
590
|
+
console.log(chalk_1.default.cyan(m.id.padEnd(20)) + chalk_1.default.gray(m.description));
|
|
591
|
+
});
|
|
592
|
+
console.log();
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
exports.ChatCommand = ChatCommand;
|
|
596
|
+
//# sourceMappingURL=chat.js.map
|