@react-frameui/loki-ai 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/LICENSE +21 -0
- package/README.md +158 -0
- package/dist/agents/devAgent.js +14 -0
- package/dist/agents/index.js +50 -0
- package/dist/agents/orchestrator.js +76 -0
- package/dist/cli/chat.js +142 -0
- package/dist/cli/doctor.js +67 -0
- package/dist/cli/tui.js +97 -0
- package/dist/context/fileContext.js +77 -0
- package/dist/context/projectContext.js +79 -0
- package/dist/core/agentRunner.js +156 -0
- package/dist/core/intentRouter.js +58 -0
- package/dist/index.js +74 -0
- package/dist/interfaces/whatsapp/client.js +185 -0
- package/dist/langchain/llmAdapter.js +44 -0
- package/dist/langchain/rag/index.js +71 -0
- package/dist/langchain/tools.js +74 -0
- package/dist/langgraph/refactorGraph.js +49 -0
- package/dist/llm/groq.js +46 -0
- package/dist/llm/ollama.js +112 -0
- package/dist/llm/providerFactory.js +14 -0
- package/dist/llm/types.js +2 -0
- package/dist/memory/memoryStore.js +57 -0
- package/dist/memory/semanticStore.js +88 -0
- package/dist/rag/indexer.js +83 -0
- package/dist/refactor/refactorEngine.js +35 -0
- package/dist/tools/fsTool.js +59 -0
- package/dist/tools/gitTool.js +54 -0
- package/dist/tools/mathTool.js +70 -0
- package/dist/tools/timeTool.js +71 -0
- package/dist/tools/toolRegistry.js +80 -0
- package/dist/utils/config.js +45 -0
- package/dist/utils/spinner.js +45 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LOKI Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# 🧠 LOKI - Local Omni Knowledge Interface
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@iamnishant51/loki-ai)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
**LOKI** is a powerful, privacy-first AI assistant that runs entirely on your local machine. No cloud, no subscriptions, just pure local AI power.
|
|
7
|
+
|
|
8
|
+
## ✨ Features
|
|
9
|
+
|
|
10
|
+
- 🔒 **100% Local** - Your data never leaves your machine
|
|
11
|
+
- 💬 **WhatsApp Integration** - Chat with LOKI via WhatsApp
|
|
12
|
+
- 🛠️ **Built-in Tools** - File system, math, time, and more
|
|
13
|
+
- 🧠 **Memory** - Remembers your conversations
|
|
14
|
+
- 🎨 **Beautiful TUI** - Intuitive terminal interface
|
|
15
|
+
- ⚡ **LangChain Powered** - Use multiple LLM providers (Ollama, Groq, OpenAI)
|
|
16
|
+
- 🔌 **VS Code Extension** - Available on the marketplace
|
|
17
|
+
|
|
18
|
+
## 🚀 Quick Start
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Install globally
|
|
22
|
+
npm install -g @iamnishant51/loki-ai
|
|
23
|
+
|
|
24
|
+
# Run LOKI
|
|
25
|
+
loki
|
|
26
|
+
|
|
27
|
+
# Or use npx (no install needed)
|
|
28
|
+
npx @iamnishant51/loki-ai
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## 📋 Prerequisites
|
|
32
|
+
|
|
33
|
+
- **Node.js** 18+
|
|
34
|
+
- **Ollama** (for local LLM) - [Install here](https://ollama.ai)
|
|
35
|
+
|
|
36
|
+
## 🎯 Usage
|
|
37
|
+
|
|
38
|
+
### Interactive TUI Mode
|
|
39
|
+
```bash
|
|
40
|
+
loki
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Chat Mode
|
|
44
|
+
```bash
|
|
45
|
+
loki chat
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### WhatsApp Integration
|
|
49
|
+
```bash
|
|
50
|
+
loki server
|
|
51
|
+
```
|
|
52
|
+
Then scan the QR code with WhatsApp!
|
|
53
|
+
|
|
54
|
+
### Direct Questions
|
|
55
|
+
```bash
|
|
56
|
+
loki ask "What time is it?"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## 🛠️ Available Tools
|
|
60
|
+
|
|
61
|
+
LOKI comes with built-in tools:
|
|
62
|
+
|
|
63
|
+
- **📁 File System** - List files, read directories
|
|
64
|
+
- **🧮 Math** - Calculate expressions
|
|
65
|
+
- **⏰ Time** - Get current time
|
|
66
|
+
- **💾 Memory** - Semantic and conversation memory
|
|
67
|
+
- **🔍 RAG** - Index repositories for context-aware responses
|
|
68
|
+
|
|
69
|
+
## 🔧 Configuration
|
|
70
|
+
|
|
71
|
+
Create a `.env` file:
|
|
72
|
+
|
|
73
|
+
```env
|
|
74
|
+
# LLM Provider (ollama, groq, openai)
|
|
75
|
+
LLM_PROVIDER=ollama
|
|
76
|
+
|
|
77
|
+
# Ollama settings
|
|
78
|
+
OLLAMA_BASE_URL=http://localhost:11434
|
|
79
|
+
OLLAMA_MODEL=llama3.2
|
|
80
|
+
|
|
81
|
+
# Optional: Groq API
|
|
82
|
+
GROQ_API_KEY=your_groq_key
|
|
83
|
+
|
|
84
|
+
# Optional: OpenAI API
|
|
85
|
+
OPENAI_API_KEY=your_openai_key
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 📱 WhatsApp Integration
|
|
89
|
+
|
|
90
|
+
1. Run `loki server`
|
|
91
|
+
2. Scan QR code with WhatsApp
|
|
92
|
+
3. Message yourself or share your number
|
|
93
|
+
4. LOKI responds automatically!
|
|
94
|
+
|
|
95
|
+
## 💻 VS Code Extension
|
|
96
|
+
|
|
97
|
+
Install from the [VS Code Marketplace](https://marketplace.visualstudio.com/) - Search for "LOKI AI"
|
|
98
|
+
|
|
99
|
+
Features:
|
|
100
|
+
- Explain code selections
|
|
101
|
+
- In-editor AI assistance
|
|
102
|
+
- Context-aware suggestions
|
|
103
|
+
|
|
104
|
+
## 🏗️ Development
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Clone repository
|
|
108
|
+
git clone https://github.com/IamNishant51/LOKI.git
|
|
109
|
+
cd LOKI
|
|
110
|
+
|
|
111
|
+
# Install dependencies
|
|
112
|
+
npm install
|
|
113
|
+
|
|
114
|
+
# Run in dev mode
|
|
115
|
+
npm run dev
|
|
116
|
+
|
|
117
|
+
# Build
|
|
118
|
+
npm run build
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## 📦 Project Structure
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
loki-ai/
|
|
125
|
+
├── src/
|
|
126
|
+
│ ├── agents/ # AI agent configurations
|
|
127
|
+
│ ├── cli/ # CLI & TUI interface
|
|
128
|
+
│ ├── core/ # Core logic & agent runner
|
|
129
|
+
│ ├── tools/ # Built-in tools
|
|
130
|
+
│ ├── memory/ # Memory management
|
|
131
|
+
│ ├── interfaces/ # WhatsApp, etc.
|
|
132
|
+
│ └── index.ts # Entry point
|
|
133
|
+
├── vscode-extension/ # VS Code extension
|
|
134
|
+
└── dist/ # Compiled output
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## 🤝 Contributing
|
|
138
|
+
|
|
139
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
140
|
+
|
|
141
|
+
## 📄 License
|
|
142
|
+
|
|
143
|
+
MIT © Nishant
|
|
144
|
+
|
|
145
|
+
## 🔗 Links
|
|
146
|
+
|
|
147
|
+
- [GitHub](https://github.com/IamNishant51/LOKI)
|
|
148
|
+
- [NPM Package](https://www.npmjs.com/package/@iamnishant51/loki-ai)
|
|
149
|
+
- [VS Code Extension](https://marketplace.visualstudio.com/)
|
|
150
|
+
- [Documentation](#) *(coming soon)*
|
|
151
|
+
|
|
152
|
+
## ⭐ Show Your Support
|
|
153
|
+
|
|
154
|
+
If you like LOKI, please give it a ⭐ on [GitHub](https://github.com/IamNishant51/LOKI)!
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
Made with ❤️ by [Nishant](https://github.com/IamNishant51)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEV_AGENT_SYSTEM_PROMPT = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* System prompt for the developer assistant agent.
|
|
6
|
+
* Designed to be helpful, concise, and coding-focused.
|
|
7
|
+
*/
|
|
8
|
+
exports.DEV_AGENT_SYSTEM_PROMPT = `
|
|
9
|
+
You are a senior software engineer assistant.
|
|
10
|
+
Your goal is to help with coding tasks, debugging, and architecture design.
|
|
11
|
+
Keep your answers concise, practical, and code-centric.
|
|
12
|
+
Avoid unnecessary fluff. Focus on modern best practices.
|
|
13
|
+
If you provide code explanation, keep it brief and relevant.
|
|
14
|
+
`.trim();
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AGENTS = void 0;
|
|
4
|
+
exports.getSystemPrompt = getSystemPrompt;
|
|
5
|
+
exports.AGENTS = {
|
|
6
|
+
dev: `
|
|
7
|
+
You are LOKI (Local Omni Knowledge Interface), a helpful AI assistant.
|
|
8
|
+
|
|
9
|
+
Your Capabilities:
|
|
10
|
+
- Answer questions and have conversations
|
|
11
|
+
- Use tools to check time, do math, list files/folders, and more
|
|
12
|
+
- Remember context from our conversation
|
|
13
|
+
|
|
14
|
+
Rules:
|
|
15
|
+
1. When using tools, execute them and present results in NATURAL LANGUAGE
|
|
16
|
+
2. NEVER show raw JSON or code blocks to the user unless they specifically ask for code
|
|
17
|
+
3. Format file/folder listings as a nice readable list
|
|
18
|
+
4. Be concise and professional - keep responses under 3 sentences when possible
|
|
19
|
+
5. Use minimal emojis - 1 at most per response, or none
|
|
20
|
+
6. If a tool fails, explain politely what went wrong
|
|
21
|
+
7. IMPORTANT: Use the paths from SYSTEM CONTEXT section (e.g., Desktop Path, Home Directory)
|
|
22
|
+
8. Don't over-explain - be direct and helpful
|
|
23
|
+
|
|
24
|
+
Example - If user asks "what folders are on my desktop":
|
|
25
|
+
- Check SYSTEM CONTEXT for Desktop Path
|
|
26
|
+
- Use the list_files tool with that exact path
|
|
27
|
+
- Then respond like: "Here are the folders on your Desktop: Projects, Documents, Downloads"
|
|
28
|
+
`.trim(),
|
|
29
|
+
explain: `
|
|
30
|
+
You are LOKI. Your task is to EXPLAIN code or concepts simply.
|
|
31
|
+
Target Audience: a developer who needs clarity.
|
|
32
|
+
Style: Educational, clear, using analogies if helpful.
|
|
33
|
+
Rules:
|
|
34
|
+
- Break down complex logic.
|
|
35
|
+
- Do not just read the code aloud; explain the *intent*.
|
|
36
|
+
`.trim(),
|
|
37
|
+
refactor: `
|
|
38
|
+
You are LOKI. Your task is to REFACTOR code.
|
|
39
|
+
Goal: Improve readability, performance, and structure.
|
|
40
|
+
Style: Strict and critical but constructive.
|
|
41
|
+
Rules:
|
|
42
|
+
- Maintain original functionality.
|
|
43
|
+
- Add comments where logic is complex.
|
|
44
|
+
- Remove dead code.
|
|
45
|
+
- Apply clean code principles (SOLID, DRY).
|
|
46
|
+
`.trim()
|
|
47
|
+
};
|
|
48
|
+
function getSystemPrompt(agentName = 'dev') {
|
|
49
|
+
return exports.AGENTS[agentName] || exports.AGENTS['dev'];
|
|
50
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runRefactorWorkflow = runRefactorWorkflow;
|
|
4
|
+
const providerFactory_1 = require("../llm/providerFactory");
|
|
5
|
+
const projectContext_1 = require("../context/projectContext");
|
|
6
|
+
// 1. Planner Agent
|
|
7
|
+
const PLANNER_PROMPT = `
|
|
8
|
+
You are the PLANNER Agent.
|
|
9
|
+
Analyze the request and project context.
|
|
10
|
+
Create a step-by-step plan to achieve the goal.
|
|
11
|
+
Do NOT write code. Just plan.
|
|
12
|
+
Output format:
|
|
13
|
+
- Step 1: [Action]
|
|
14
|
+
- Step 2: [Action]
|
|
15
|
+
...
|
|
16
|
+
`;
|
|
17
|
+
// 2. Analyzer Agent
|
|
18
|
+
const ANALYZER_PROMPT = `
|
|
19
|
+
You are the ANALYZER Agent.
|
|
20
|
+
Read the plan and relevant files.
|
|
21
|
+
Explain the current state and what needs to change.
|
|
22
|
+
Focus on impact and risks.
|
|
23
|
+
`;
|
|
24
|
+
// 3. Refactor Agent
|
|
25
|
+
const REFACTOR_PROMPT = `
|
|
26
|
+
You are the REFACTOR Agent.
|
|
27
|
+
Generate the actual code changes based on the plan and analysis.
|
|
28
|
+
Show clear Before/After blocks or unified diffs.
|
|
29
|
+
Do NOT apply changes. Just propose them.
|
|
30
|
+
`;
|
|
31
|
+
async function runRefactorWorkflow(instruction) {
|
|
32
|
+
const provider = (0, providerFactory_1.getProvider)('ollama'); // Default for internal thought loops
|
|
33
|
+
const context = (0, projectContext_1.getProjectContext)();
|
|
34
|
+
// --- Step 1: Plan ---
|
|
35
|
+
const planPrompt = `
|
|
36
|
+
${PLANNER_PROMPT}
|
|
37
|
+
Project: ${context.name}
|
|
38
|
+
User Goal: ${instruction}
|
|
39
|
+
Plan:`;
|
|
40
|
+
console.log('[Orchestrator] Planning...');
|
|
41
|
+
const plan = await provider.generate(planPrompt);
|
|
42
|
+
// --- Step 2: Analyze ---
|
|
43
|
+
const analysisPrompt = `
|
|
44
|
+
${ANALYZER_PROMPT}
|
|
45
|
+
Plan:
|
|
46
|
+
${plan}
|
|
47
|
+
User Goal: ${instruction}
|
|
48
|
+
Analysis:`;
|
|
49
|
+
console.log('[Orchestrator] Analyzing...');
|
|
50
|
+
const analysis = await provider.generate(analysisPrompt);
|
|
51
|
+
// --- Step 3: Propose Refactor ---
|
|
52
|
+
const refactorPrompt = `
|
|
53
|
+
${REFACTOR_PROMPT}
|
|
54
|
+
Plan:
|
|
55
|
+
${plan}
|
|
56
|
+
Analysis:
|
|
57
|
+
${analysis}
|
|
58
|
+
Proposal:`;
|
|
59
|
+
console.log('[Orchestrator] Generating Proposal...');
|
|
60
|
+
const proposal = await provider.generate(refactorPrompt);
|
|
61
|
+
// Provide full report
|
|
62
|
+
return {
|
|
63
|
+
success: true,
|
|
64
|
+
summary: "Refactor Proposal Generated",
|
|
65
|
+
details: `
|
|
66
|
+
=== PLAN ===
|
|
67
|
+
${plan.trim()}
|
|
68
|
+
|
|
69
|
+
=== ANALYSIS ===
|
|
70
|
+
${analysis.trim()}
|
|
71
|
+
|
|
72
|
+
=== PROPOSAL ===
|
|
73
|
+
${proposal.trim()}
|
|
74
|
+
`
|
|
75
|
+
};
|
|
76
|
+
}
|
package/dist/cli/chat.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.startChatSession = startChatSession;
|
|
7
|
+
exports.chatCommand = chatCommand;
|
|
8
|
+
const readline_1 = __importDefault(require("readline"));
|
|
9
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
+
const agentRunner_1 = require("../core/agentRunner");
|
|
11
|
+
const spinner_1 = require("../utils/spinner");
|
|
12
|
+
const LOKI_PREFIX = chalk_1.default.green.bold('LOKI │');
|
|
13
|
+
const USER_PREFIX = chalk_1.default.blue.bold('USER │');
|
|
14
|
+
const ERROR_PREFIX = chalk_1.default.red.bold('ERR │');
|
|
15
|
+
const BANNER = chalk_1.default.green.bold(`
|
|
16
|
+
██╗ ██████╗ ██╗ ██╗██╗
|
|
17
|
+
██║ ██╔═══██╗██║ ██╔╝██║
|
|
18
|
+
██║ ██║ ██║█████╔╝ ██║
|
|
19
|
+
██║ ██║ ██║██╔═██╗ ██║
|
|
20
|
+
███████╗╚██████╔╝██║ ██╗██║
|
|
21
|
+
╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝
|
|
22
|
+
`);
|
|
23
|
+
// Handle cancellation
|
|
24
|
+
let abortController = null;
|
|
25
|
+
let spinner = null;
|
|
26
|
+
async function activeChat(message, options) {
|
|
27
|
+
const contextPaths = [];
|
|
28
|
+
if (options.file)
|
|
29
|
+
contextPaths.push(options.file);
|
|
30
|
+
if (options.dir)
|
|
31
|
+
contextPaths.push(options.dir);
|
|
32
|
+
const shouldStream = options.stream !== false;
|
|
33
|
+
abortController = new AbortController();
|
|
34
|
+
try {
|
|
35
|
+
process.stdout.write(`\n${LOKI_PREFIX} `);
|
|
36
|
+
// Start spinner
|
|
37
|
+
spinner = new spinner_1.Spinner();
|
|
38
|
+
spinner.start();
|
|
39
|
+
const response = await (0, agentRunner_1.agentRunner)(message, {
|
|
40
|
+
provider: options.provider,
|
|
41
|
+
agent: options.agent,
|
|
42
|
+
useMemory: options.memory,
|
|
43
|
+
fileContextPaths: contextPaths,
|
|
44
|
+
stream: shouldStream,
|
|
45
|
+
onToken: (token) => {
|
|
46
|
+
// Stop spinner on first token
|
|
47
|
+
if (spinner) {
|
|
48
|
+
spinner.stop();
|
|
49
|
+
spinner = null;
|
|
50
|
+
}
|
|
51
|
+
process.stdout.write(token);
|
|
52
|
+
},
|
|
53
|
+
signal: abortController.signal
|
|
54
|
+
});
|
|
55
|
+
// Ensure spinner is stopped if logic completed without streaming or buffered
|
|
56
|
+
if (spinner) {
|
|
57
|
+
spinner.stop();
|
|
58
|
+
spinner = null;
|
|
59
|
+
}
|
|
60
|
+
if (!shouldStream) {
|
|
61
|
+
console.log(response);
|
|
62
|
+
}
|
|
63
|
+
console.log('\n');
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
if (spinner) {
|
|
67
|
+
spinner.stop();
|
|
68
|
+
spinner = null;
|
|
69
|
+
}
|
|
70
|
+
if (error.message.includes('cancelled') || error.message.includes('Aborted')) {
|
|
71
|
+
console.log(chalk_1.default.gray(' [Cancelled]\n'));
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
console.error(`\n${ERROR_PREFIX} ${error.message}\n`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
finally {
|
|
78
|
+
abortController = null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Interactive Chat Session.
|
|
83
|
+
* Returns a promise that resolves when the user types 'exit' or 'back'.
|
|
84
|
+
*/
|
|
85
|
+
async function startChatSession(options) {
|
|
86
|
+
// Note: We don't clear screen here to preserve context if coming from menu,
|
|
87
|
+
// or let the clear happen if desired.
|
|
88
|
+
// Actually, for a "sub-screen", a clear is nice, but maybe we just print a header.
|
|
89
|
+
process.stdout.write('\x1Bc');
|
|
90
|
+
console.log(chalk_1.default.green.bold('LOKI CHAT MODE'));
|
|
91
|
+
console.log(chalk_1.default.gray('Type "exit" or "back" to return to main menu.\n'));
|
|
92
|
+
const rl = readline_1.default.createInterface({
|
|
93
|
+
input: process.stdin,
|
|
94
|
+
output: process.stdout,
|
|
95
|
+
prompt: `${USER_PREFIX} `
|
|
96
|
+
});
|
|
97
|
+
return new Promise((resolve) => {
|
|
98
|
+
rl.prompt();
|
|
99
|
+
rl.on('SIGINT', () => {
|
|
100
|
+
if (abortController) {
|
|
101
|
+
abortController.abort();
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// Return to menu on Ctrl+C (twice?) or just ask?
|
|
105
|
+
// For simplicity, let's treat idle Ctrl+C as "Back"
|
|
106
|
+
console.log(chalk_1.default.gray('\nReturning to menu...'));
|
|
107
|
+
rl.close();
|
|
108
|
+
resolve();
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
rl.on('line', async (line) => {
|
|
112
|
+
const input = line.trim();
|
|
113
|
+
if (input.toLowerCase() === 'exit' || input.toLowerCase() === 'back') {
|
|
114
|
+
rl.close();
|
|
115
|
+
resolve();
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (input) {
|
|
119
|
+
rl.pause();
|
|
120
|
+
await activeChat(input, options);
|
|
121
|
+
rl.resume();
|
|
122
|
+
}
|
|
123
|
+
rl.prompt();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
async function chatCommand(message, options) {
|
|
128
|
+
const opts = {
|
|
129
|
+
provider: options.provider,
|
|
130
|
+
agent: options.agent,
|
|
131
|
+
memory: options.memory,
|
|
132
|
+
file: options.file,
|
|
133
|
+
dir: options.dir,
|
|
134
|
+
stream: options.stream
|
|
135
|
+
};
|
|
136
|
+
if (message) {
|
|
137
|
+
await activeChat(message, opts);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
await startChatSession(opts);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.doctorCommand = doctorCommand;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const config_1 = require("../utils/config");
|
|
9
|
+
const providerFactory_1 = require("../llm/providerFactory");
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
async function doctorCommand() {
|
|
12
|
+
console.log(`\nLOKI DOCTOR - SYSTEM HEALTH CHECK\n`);
|
|
13
|
+
let allGood = true;
|
|
14
|
+
// Helper for clean output
|
|
15
|
+
const check = (label, status, info) => {
|
|
16
|
+
const mark = status ? chalk_1.default.green('[OK] ') : chalk_1.default.red('[FAIL]');
|
|
17
|
+
console.log(`${mark} ${label.padEnd(20)} : ${info}`);
|
|
18
|
+
if (!status)
|
|
19
|
+
allGood = false;
|
|
20
|
+
};
|
|
21
|
+
const warn = (label, info) => {
|
|
22
|
+
console.log(`${chalk_1.default.yellow('[WARN]')} ${label.padEnd(20)} : ${info}`);
|
|
23
|
+
};
|
|
24
|
+
// 1. Check Config Dir
|
|
25
|
+
try {
|
|
26
|
+
if (fs_1.default.existsSync(config_1.CONFIG.CONFIG_DIR)) {
|
|
27
|
+
try {
|
|
28
|
+
fs_1.default.accessSync(config_1.CONFIG.CONFIG_DIR, fs_1.default.constants.W_OK);
|
|
29
|
+
check('Config Directory', true, config_1.CONFIG.CONFIG_DIR);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
check('Config Directory', false, `Not Writable (${config_1.CONFIG.CONFIG_DIR})`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
warn('Config Directory', 'Missing (Will be created on first run)');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
check('Config Directory', false, 'Check Failed');
|
|
41
|
+
}
|
|
42
|
+
// 2. Check Ollama
|
|
43
|
+
const ollama = (0, providerFactory_1.getProvider)('ollama');
|
|
44
|
+
const ollamaHealth = await ollama.checkHealth();
|
|
45
|
+
if (ollamaHealth) {
|
|
46
|
+
check('Ollama Connection', true, config_1.CONFIG.OLLAMA_HOST);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
check('Ollama Connection', false, 'Not running or unreachable');
|
|
50
|
+
console.log(chalk_1.default.gray(` -> Run 'ollama serve' to start`));
|
|
51
|
+
}
|
|
52
|
+
// 3. Check Groq (Optional)
|
|
53
|
+
if (config_1.CONFIG.GROQ_API_KEY) {
|
|
54
|
+
check('Groq API Key', true, 'Present');
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
// Just info, not warn/fail since it's optional
|
|
58
|
+
console.log(`${chalk_1.default.blue('[INFO]')} ${'Groq API Key'.padEnd(20)} : Not Set (Optional)`);
|
|
59
|
+
}
|
|
60
|
+
console.log(`\n----------------------------------------`);
|
|
61
|
+
if (allGood) {
|
|
62
|
+
console.log(chalk_1.default.green(`System ready.`));
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
console.log(chalk_1.default.red(`System has issues. Please fix above errors.`));
|
|
66
|
+
}
|
|
67
|
+
}
|
package/dist/cli/tui.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.startLoop = startLoop;
|
|
7
|
+
const readline_1 = __importDefault(require("readline"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const chat_1 = require("./chat");
|
|
10
|
+
const doctor_1 = require("./doctor");
|
|
11
|
+
const refactorEngine_1 = require("../refactor/refactorEngine");
|
|
12
|
+
const indexer_1 = require("../rag/indexer");
|
|
13
|
+
const BANNER = chalk_1.default.green.bold(`
|
|
14
|
+
██╗ ██████╗ ██╗ ██╗██╗
|
|
15
|
+
██║ ██╔═══██╗██║ ██╔╝██║
|
|
16
|
+
██║ ██║ ██║█████╔╝ ██║
|
|
17
|
+
██║ ██║ ██║██╔═██╗ ██║
|
|
18
|
+
███████╗╚██████╔╝██║ ██╗██║
|
|
19
|
+
╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝
|
|
20
|
+
`);
|
|
21
|
+
// Simple prompt helper
|
|
22
|
+
function ask(question) {
|
|
23
|
+
const rl = readline_1.default.createInterface({
|
|
24
|
+
input: process.stdin,
|
|
25
|
+
output: process.stdout,
|
|
26
|
+
});
|
|
27
|
+
return new Promise(resolve => {
|
|
28
|
+
rl.question(question, (answer) => {
|
|
29
|
+
rl.close();
|
|
30
|
+
resolve(answer);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
async function waitForKey() {
|
|
35
|
+
console.log(chalk_1.default.gray('\nPress any key to continue...'));
|
|
36
|
+
process.stdin.setRawMode(true);
|
|
37
|
+
return new Promise(resolve => {
|
|
38
|
+
process.stdin.once('data', () => {
|
|
39
|
+
process.stdin.setRawMode(false);
|
|
40
|
+
resolve();
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async function startLoop() {
|
|
45
|
+
while (true) {
|
|
46
|
+
process.stdout.write('\x1Bc'); // Clear screen
|
|
47
|
+
console.log(BANNER);
|
|
48
|
+
console.log(chalk_1.default.gray(' Local Omni Knowledge Interface\n'));
|
|
49
|
+
console.log(chalk_1.default.white.bold(' MAIN MENU'));
|
|
50
|
+
console.log(chalk_1.default.cyan(' [1]') + ' Chat with LOKI');
|
|
51
|
+
console.log(chalk_1.default.cyan(' [2]') + ' Agent Workflow (Refactor/Plan)');
|
|
52
|
+
console.log(chalk_1.default.cyan(' [3]') + ' Index Repository (RAG)');
|
|
53
|
+
console.log(chalk_1.default.cyan(' [4]') + ' WhatsApp Link (QR)');
|
|
54
|
+
console.log(chalk_1.default.cyan(' [5]') + ' System Doctor');
|
|
55
|
+
console.log(chalk_1.default.cyan(' [6]') + ' Exit');
|
|
56
|
+
console.log();
|
|
57
|
+
const answer = await ask(chalk_1.default.green.bold(' SELECT ▸ '));
|
|
58
|
+
switch (answer.trim()) {
|
|
59
|
+
case '1':
|
|
60
|
+
await (0, chat_1.startChatSession)({});
|
|
61
|
+
break;
|
|
62
|
+
case '2':
|
|
63
|
+
process.stdout.write('\x1Bc');
|
|
64
|
+
console.log(chalk_1.default.green.bold('LOKI AGENT WORKFLOW'));
|
|
65
|
+
console.log(chalk_1.default.gray('Example: "Refactor gitTool to use async/await"\n'));
|
|
66
|
+
const instruction = await ask(chalk_1.default.cyan('Describe task: '));
|
|
67
|
+
if (instruction.trim()) {
|
|
68
|
+
await (0, refactorEngine_1.engineCommand)(instruction);
|
|
69
|
+
}
|
|
70
|
+
await waitForKey();
|
|
71
|
+
break;
|
|
72
|
+
case '3':
|
|
73
|
+
process.stdout.write('\x1Bc');
|
|
74
|
+
console.log(chalk_1.default.green.bold('LOKI RAG INDEXER'));
|
|
75
|
+
await (0, indexer_1.indexRepository)();
|
|
76
|
+
await waitForKey();
|
|
77
|
+
break;
|
|
78
|
+
case '4':
|
|
79
|
+
process.stdout.write('\x1Bc');
|
|
80
|
+
const { startWhatsAppClient } = require('../interfaces/whatsapp/client');
|
|
81
|
+
await startWhatsAppClient();
|
|
82
|
+
await waitForKey();
|
|
83
|
+
break;
|
|
84
|
+
case '5':
|
|
85
|
+
process.stdout.write('\x1Bc');
|
|
86
|
+
await (0, doctor_1.doctorCommand)();
|
|
87
|
+
await waitForKey();
|
|
88
|
+
break;
|
|
89
|
+
case '6':
|
|
90
|
+
case 'exit':
|
|
91
|
+
console.log('Goodbye.');
|
|
92
|
+
process.exit(0);
|
|
93
|
+
default:
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|