TUI5: Added 4 new feature modules - Session Memory, Context Manager, Skills Library, Debug Logger
This commit is contained in:
181
lib/context-manager.mjs
Normal file
181
lib/context-manager.mjs
Normal file
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* Context Manager - Intelligent context window management for TUI 5
|
||||
* Auto-summarizes conversation history when approaching token limits
|
||||
*
|
||||
* Original implementation for OpenQode TUI
|
||||
*/
|
||||
|
||||
import { getQwen } from '../qwen-oauth.mjs';
|
||||
|
||||
// Rough token estimation: ~4 chars per token for English
|
||||
const CHARS_PER_TOKEN = 4;
|
||||
|
||||
/**
|
||||
* ContextManager class - Manages conversation context and auto-summarization
|
||||
*/
|
||||
export class ContextManager {
|
||||
constructor(options = {}) {
|
||||
this.tokenLimit = options.tokenLimit || 100000; // Default 100K token context
|
||||
this.summarizeThreshold = options.summarizeThreshold || 0.5; // Summarize at 50%
|
||||
this.minMessagesToKeep = options.minMessagesToKeep || 4; // Keep last 4 messages
|
||||
this.summaryBlock = null; // Stores summarized context
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimate token count for text
|
||||
* @param {string} text - Text to count
|
||||
* @returns {number} Estimated token count
|
||||
*/
|
||||
countTokens(text) {
|
||||
if (!text) return 0;
|
||||
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count tokens for all messages
|
||||
* @param {Array} messages - Array of message objects
|
||||
* @returns {number} Total estimated tokens
|
||||
*/
|
||||
countMessageTokens(messages) {
|
||||
return messages.reduce((total, msg) => {
|
||||
return total + this.countTokens(msg.content || '');
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current context usage as percentage
|
||||
* @param {Array} messages - Current messages
|
||||
* @returns {number} Percentage (0-100)
|
||||
*/
|
||||
getUsagePercent(messages) {
|
||||
const used = this.countMessageTokens(messages);
|
||||
return Math.round((used / this.tokenLimit) * 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if summarization is needed
|
||||
* @param {Array} messages - Current messages
|
||||
* @returns {boolean}
|
||||
*/
|
||||
shouldSummarize(messages) {
|
||||
const usage = this.getUsagePercent(messages) / 100;
|
||||
return usage >= this.summarizeThreshold && messages.length > this.minMessagesToKeep;
|
||||
}
|
||||
|
||||
/**
|
||||
* Summarize older messages to free up context
|
||||
* @param {Array} messages - All messages
|
||||
* @param {Function} onProgress - Progress callback
|
||||
* @returns {Object} { summary, keptMessages }
|
||||
*/
|
||||
async summarize(messages, onProgress = null) {
|
||||
if (messages.length <= this.minMessagesToKeep) {
|
||||
return { summary: null, keptMessages: messages };
|
||||
}
|
||||
|
||||
// Split: messages to summarize vs messages to keep
|
||||
const toSummarize = messages.slice(0, -this.minMessagesToKeep);
|
||||
const toKeep = messages.slice(-this.minMessagesToKeep);
|
||||
|
||||
if (onProgress) onProgress('Summarizing context...');
|
||||
|
||||
// Create summary prompt
|
||||
const summaryPrompt = `Summarize the following conversation history into a concise context summary.
|
||||
Focus on:
|
||||
- Key decisions made
|
||||
- Important context established
|
||||
- User preferences expressed
|
||||
- Current project/task state
|
||||
|
||||
Keep it under 500 words.
|
||||
|
||||
CONVERSATION TO SUMMARIZE:
|
||||
${toSummarize.map(m => `[${m.role}]: ${m.content}`).join('\n\n')}
|
||||
|
||||
SUMMARY:`;
|
||||
|
||||
try {
|
||||
// Use AI to generate summary
|
||||
const oauth = getQwen();
|
||||
const result = await oauth.sendMessage(summaryPrompt, 'qwen-turbo');
|
||||
|
||||
if (result.success) {
|
||||
this.summaryBlock = {
|
||||
type: 'context_summary',
|
||||
content: result.response,
|
||||
originalMessageCount: toSummarize.length,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
return {
|
||||
summary: this.summaryBlock,
|
||||
keptMessages: toKeep
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Context summarization failed:', error.message);
|
||||
}
|
||||
|
||||
// Fallback: simple truncation
|
||||
this.summaryBlock = {
|
||||
type: 'context_truncated',
|
||||
content: `[Previous ${toSummarize.length} messages truncated to save context]`,
|
||||
originalMessageCount: toSummarize.length,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
return {
|
||||
summary: this.summaryBlock,
|
||||
keptMessages: toKeep
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get context summary as system prompt addition
|
||||
*/
|
||||
getSummaryContext() {
|
||||
if (!this.summaryBlock) return '';
|
||||
|
||||
return `
|
||||
=== PREVIOUS CONTEXT SUMMARY ===
|
||||
The following is a summary of earlier conversation (${this.summaryBlock.originalMessageCount} messages):
|
||||
|
||||
${this.summaryBlock.content}
|
||||
|
||||
=== END SUMMARY ===
|
||||
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stats for UI display
|
||||
* @param {Array} messages - Current messages
|
||||
* @returns {Object} Stats object
|
||||
*/
|
||||
getStats(messages) {
|
||||
const tokens = this.countMessageTokens(messages);
|
||||
const percent = this.getUsagePercent(messages);
|
||||
const needsSummary = this.shouldSummarize(messages);
|
||||
|
||||
return {
|
||||
tokens,
|
||||
limit: this.tokenLimit,
|
||||
percent,
|
||||
needsSummary,
|
||||
hasSummary: !!this.summaryBlock,
|
||||
color: percent > 80 ? 'red' : percent > 50 ? 'yellow' : 'green'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton instance
|
||||
let _contextManager = null;
|
||||
|
||||
export function getContextManager(options = {}) {
|
||||
if (!_contextManager) {
|
||||
_contextManager = new ContextManager(options);
|
||||
}
|
||||
return _contextManager;
|
||||
}
|
||||
|
||||
export default ContextManager;
|
||||
219
lib/debug-logger.mjs
Normal file
219
lib/debug-logger.mjs
Normal file
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* Debug Logger - Comprehensive request/response logging for TUI 5
|
||||
* Enabled via --debug flag or /debug command
|
||||
*
|
||||
* Original implementation for OpenQode TUI
|
||||
*/
|
||||
|
||||
import { appendFile, writeFile, readFile, access } from 'fs/promises';
|
||||
import { join } from 'path';
|
||||
|
||||
const DEBUG_FILE = '.openqode-debug.log';
|
||||
|
||||
/**
|
||||
* DebugLogger class - Logs all API requests, responses, and tool executions
|
||||
*/
|
||||
export class DebugLogger {
|
||||
constructor(options = {}) {
|
||||
this.enabled = options.enabled || false;
|
||||
this.logPath = options.logPath || join(process.cwd(), DEBUG_FILE);
|
||||
this.maxLogSize = options.maxLogSize || 5 * 1024 * 1024; // 5MB max
|
||||
this.sessionId = Date.now().toString(36);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable debug logging
|
||||
*/
|
||||
enable() {
|
||||
this.enabled = true;
|
||||
this.log('DEBUG', 'Debug logging enabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable debug logging
|
||||
*/
|
||||
disable() {
|
||||
this.log('DEBUG', 'Debug logging disabled');
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle debug mode
|
||||
*/
|
||||
toggle() {
|
||||
if (this.enabled) {
|
||||
this.disable();
|
||||
} else {
|
||||
this.enable();
|
||||
}
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format timestamp
|
||||
*/
|
||||
timestamp() {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message
|
||||
* @param {string} level - Log level (INFO, DEBUG, WARN, ERROR, API, TOOL)
|
||||
* @param {string} message - Log message
|
||||
* @param {Object} data - Optional data to log
|
||||
*/
|
||||
async log(level, message, data = null) {
|
||||
if (!this.enabled && level !== 'DEBUG') return;
|
||||
|
||||
const entry = {
|
||||
timestamp: this.timestamp(),
|
||||
session: this.sessionId,
|
||||
level,
|
||||
message,
|
||||
...(data && { data: this.truncate(data) })
|
||||
};
|
||||
|
||||
const logLine = JSON.stringify(entry) + '\n';
|
||||
|
||||
try {
|
||||
await appendFile(this.logPath, logLine);
|
||||
} catch (error) {
|
||||
// Silent fail - debug logging shouldn't break the app
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log API request
|
||||
*/
|
||||
async logRequest(provider, model, prompt, options = {}) {
|
||||
await this.log('API_REQUEST', `${provider}/${model}`, {
|
||||
promptLength: prompt?.length || 0,
|
||||
promptPreview: prompt?.substring(0, 200) + '...',
|
||||
options
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Log API response
|
||||
*/
|
||||
async logResponse(provider, model, response, duration) {
|
||||
await this.log('API_RESPONSE', `${provider}/${model}`, {
|
||||
success: response?.success,
|
||||
responseLength: response?.response?.length || 0,
|
||||
responsePreview: response?.response?.substring(0, 200) + '...',
|
||||
durationMs: duration,
|
||||
usage: response?.usage
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Log tool execution
|
||||
*/
|
||||
async logTool(toolName, input, output, duration) {
|
||||
await this.log('TOOL', toolName, {
|
||||
input: this.truncate(input),
|
||||
output: this.truncate(output),
|
||||
durationMs: duration
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Log error
|
||||
*/
|
||||
async logError(context, error) {
|
||||
await this.log('ERROR', context, {
|
||||
message: error?.message,
|
||||
stack: error?.stack?.substring(0, 500)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Log user command
|
||||
*/
|
||||
async logCommand(command, args) {
|
||||
await this.log('COMMAND', command, { args });
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate large objects for logging
|
||||
*/
|
||||
truncate(obj, maxLength = 1000) {
|
||||
if (!obj) return obj;
|
||||
|
||||
if (typeof obj === 'string') {
|
||||
return obj.length > maxLength
|
||||
? obj.substring(0, maxLength) + '...[truncated]'
|
||||
: obj;
|
||||
}
|
||||
|
||||
try {
|
||||
const str = JSON.stringify(obj);
|
||||
if (str.length > maxLength) {
|
||||
return JSON.parse(str.substring(0, maxLength) + '..."}}');
|
||||
}
|
||||
return obj;
|
||||
} catch {
|
||||
return '[Object]';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear log file
|
||||
*/
|
||||
async clear() {
|
||||
try {
|
||||
await writeFile(this.logPath, '');
|
||||
await this.log('DEBUG', 'Log file cleared');
|
||||
} catch (error) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recent log entries
|
||||
* @param {number} count - Number of entries to return
|
||||
*/
|
||||
async getRecent(count = 50) {
|
||||
try {
|
||||
const content = await readFile(this.logPath, 'utf8');
|
||||
const lines = content.trim().split('\n').filter(l => l);
|
||||
return lines.slice(-count).map(l => {
|
||||
try {
|
||||
return JSON.parse(l);
|
||||
} catch {
|
||||
return { raw: l };
|
||||
}
|
||||
});
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get log file path
|
||||
*/
|
||||
getPath() {
|
||||
return this.logPath;
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton instance
|
||||
let _logger = null;
|
||||
|
||||
export function getDebugLogger(options = {}) {
|
||||
if (!_logger) {
|
||||
_logger = new DebugLogger(options);
|
||||
}
|
||||
return _logger;
|
||||
}
|
||||
|
||||
// Check CLI args for --debug flag
|
||||
export function initFromArgs() {
|
||||
const logger = getDebugLogger();
|
||||
if (process.argv.includes('--debug')) {
|
||||
logger.enable();
|
||||
}
|
||||
return logger;
|
||||
}
|
||||
|
||||
export default DebugLogger;
|
||||
176
lib/session-memory.mjs
Normal file
176
lib/session-memory.mjs
Normal file
@@ -0,0 +1,176 @@
|
||||
/**
|
||||
* Session Memory - Persistent context storage for TUI 5
|
||||
* Allows AI to remember important facts across sessions
|
||||
*
|
||||
* Original implementation for OpenQode TUI
|
||||
*/
|
||||
|
||||
import { readFile, writeFile, access } from 'fs/promises';
|
||||
import { join } from 'path';
|
||||
import { homedir } from 'os';
|
||||
|
||||
const MEMORY_FILE = '.openqode-memory.json';
|
||||
|
||||
/**
|
||||
* SessionMemory class - Manages persistent facts/context across TUI sessions
|
||||
*/
|
||||
export class SessionMemory {
|
||||
constructor(projectPath = null) {
|
||||
this.projectPath = projectPath || process.cwd();
|
||||
this.memoryPath = join(this.projectPath, MEMORY_FILE);
|
||||
this.facts = [];
|
||||
this.metadata = {
|
||||
created: null,
|
||||
lastModified: null,
|
||||
version: '1.0'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Load memory from disk
|
||||
*/
|
||||
async load() {
|
||||
try {
|
||||
await access(this.memoryPath);
|
||||
const data = await readFile(this.memoryPath, 'utf8');
|
||||
const parsed = JSON.parse(data);
|
||||
this.facts = parsed.facts || [];
|
||||
this.metadata = parsed.metadata || this.metadata;
|
||||
return true;
|
||||
} catch (error) {
|
||||
// No memory file exists yet - that's OK
|
||||
this.facts = [];
|
||||
this.metadata.created = new Date().toISOString();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save memory to disk
|
||||
*/
|
||||
async save() {
|
||||
this.metadata.lastModified = new Date().toISOString();
|
||||
if (!this.metadata.created) {
|
||||
this.metadata.created = this.metadata.lastModified;
|
||||
}
|
||||
|
||||
const data = {
|
||||
version: '1.0',
|
||||
metadata: this.metadata,
|
||||
facts: this.facts
|
||||
};
|
||||
|
||||
await writeFile(this.memoryPath, JSON.stringify(data, null, 2), 'utf8');
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remember a new fact
|
||||
* @param {string} fact - The fact to remember
|
||||
* @param {string} category - Optional category (context, decision, preference, etc.)
|
||||
*/
|
||||
async remember(fact, category = 'context') {
|
||||
const entry = {
|
||||
id: Date.now(),
|
||||
fact: fact.trim(),
|
||||
category,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
this.facts.push(entry);
|
||||
await this.save();
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forget a fact by ID or index
|
||||
* @param {number} identifier - Fact ID or index (1-based for user convenience)
|
||||
*/
|
||||
async forget(identifier) {
|
||||
// Try by index first (1-based)
|
||||
if (identifier > 0 && identifier <= this.facts.length) {
|
||||
const removed = this.facts.splice(identifier - 1, 1)[0];
|
||||
await this.save();
|
||||
return removed;
|
||||
}
|
||||
|
||||
// Try by ID
|
||||
const index = this.facts.findIndex(f => f.id === identifier);
|
||||
if (index !== -1) {
|
||||
const removed = this.facts.splice(index, 1)[0];
|
||||
await this.save();
|
||||
return removed;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all memory
|
||||
*/
|
||||
async clear() {
|
||||
this.facts = [];
|
||||
await this.save();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all facts as a formatted string for AI context
|
||||
*/
|
||||
getContextString() {
|
||||
if (this.facts.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const header = '=== SESSION MEMORY ===\nThe following facts were remembered from previous sessions:\n';
|
||||
const factsList = this.facts.map((f, i) =>
|
||||
`${i + 1}. [${f.category}] ${f.fact}`
|
||||
).join('\n');
|
||||
|
||||
return header + factsList + '\n=== END MEMORY ===\n\n';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get facts formatted for display in UI
|
||||
*/
|
||||
getDisplayList() {
|
||||
return this.facts.map((f, i) => ({
|
||||
index: i + 1,
|
||||
...f,
|
||||
displayDate: new Date(f.timestamp).toLocaleDateString()
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory summary for welcome screen
|
||||
*/
|
||||
getSummary() {
|
||||
const count = this.facts.length;
|
||||
if (count === 0) {
|
||||
return 'No session memory stored';
|
||||
}
|
||||
|
||||
const categories = {};
|
||||
this.facts.forEach(f => {
|
||||
categories[f.category] = (categories[f.category] || 0) + 1;
|
||||
});
|
||||
|
||||
const breakdown = Object.entries(categories)
|
||||
.map(([cat, num]) => `${num} ${cat}`)
|
||||
.join(', ');
|
||||
|
||||
return `${count} facts remembered (${breakdown})`;
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton instance for easy import
|
||||
let _memoryInstance = null;
|
||||
|
||||
export function getSessionMemory(projectPath = null) {
|
||||
if (!_memoryInstance) {
|
||||
_memoryInstance = new SessionMemory(projectPath);
|
||||
}
|
||||
return _memoryInstance;
|
||||
}
|
||||
|
||||
export default SessionMemory;
|
||||
251
lib/skills.mjs
Normal file
251
lib/skills.mjs
Normal file
@@ -0,0 +1,251 @@
|
||||
/**
|
||||
* Skills Library - Pre-built AI prompts for common tasks
|
||||
* Provides /skills and /skill <name> commands
|
||||
*
|
||||
* Original implementation for OpenQode TUI
|
||||
*/
|
||||
|
||||
/**
|
||||
* Skill definition structure
|
||||
*/
|
||||
const SKILLS = {
|
||||
// Development Skills
|
||||
test: {
|
||||
name: 'Unit Tests',
|
||||
description: 'Generate comprehensive unit tests for code',
|
||||
category: 'development',
|
||||
prompt: `Generate comprehensive unit tests for the provided code.
|
||||
Include:
|
||||
- Edge cases and boundary conditions
|
||||
- Error handling scenarios
|
||||
- Mock dependencies where appropriate
|
||||
- Clear test descriptions
|
||||
- Setup and teardown if needed
|
||||
|
||||
Format: Use the appropriate testing framework for the language (Jest, pytest, etc.)`
|
||||
},
|
||||
|
||||
refactor: {
|
||||
name: 'Refactor Code',
|
||||
description: 'Suggest refactoring improvements',
|
||||
category: 'development',
|
||||
prompt: `Analyze the provided code and suggest refactoring improvements.
|
||||
Focus on:
|
||||
- Code clarity and readability
|
||||
- DRY principle violations
|
||||
- Performance optimizations
|
||||
- Design pattern opportunities
|
||||
- Type safety improvements
|
||||
|
||||
Provide before/after examples for each suggestion.`
|
||||
},
|
||||
|
||||
review: {
|
||||
name: 'Code Review',
|
||||
description: 'Perform a thorough code review',
|
||||
category: 'development',
|
||||
prompt: `Perform a thorough code review of the provided code.
|
||||
Check for:
|
||||
- Bugs and logic errors
|
||||
- Security vulnerabilities
|
||||
- Performance issues
|
||||
- Code style and consistency
|
||||
- Documentation gaps
|
||||
- Error handling
|
||||
|
||||
Rate severity: 🔴 Critical | 🟡 Warning | 🟢 Suggestion`
|
||||
},
|
||||
|
||||
debug: {
|
||||
name: 'Debug Helper',
|
||||
description: 'Help diagnose and fix bugs',
|
||||
category: 'development',
|
||||
prompt: `Help debug the provided code/error.
|
||||
Approach:
|
||||
1. Identify the root cause
|
||||
2. Explain why the error occurs
|
||||
3. Provide the fix with explanation
|
||||
4. Suggest prevention strategies
|
||||
|
||||
Include stack trace analysis if provided.`
|
||||
},
|
||||
|
||||
// Documentation Skills
|
||||
docs: {
|
||||
name: 'Documentation',
|
||||
description: 'Generate comprehensive documentation',
|
||||
category: 'documentation',
|
||||
prompt: `Generate comprehensive documentation for the provided code.
|
||||
Include:
|
||||
- Overview/purpose
|
||||
- Installation/setup (if applicable)
|
||||
- API reference with parameters and return values
|
||||
- Usage examples
|
||||
- Configuration options
|
||||
- Common issues/FAQ
|
||||
|
||||
Format: Markdown with proper headings.`
|
||||
},
|
||||
|
||||
readme: {
|
||||
name: 'README Generator',
|
||||
description: 'Create a professional README.md',
|
||||
category: 'documentation',
|
||||
prompt: `Create a professional README.md for this project.
|
||||
Include:
|
||||
- Project title and badges
|
||||
- Description
|
||||
- Features list
|
||||
- Quick start guide
|
||||
- Installation steps
|
||||
- Usage examples
|
||||
- Configuration
|
||||
- Contributing guidelines
|
||||
- License
|
||||
|
||||
Make it visually appealing with emojis and formatting.`
|
||||
},
|
||||
|
||||
// Analysis Skills
|
||||
explain: {
|
||||
name: 'Code Explainer',
|
||||
description: 'Explain code in simple terms',
|
||||
category: 'analysis',
|
||||
prompt: `Explain the provided code in simple, clear terms.
|
||||
Structure:
|
||||
1. High-level purpose (what it does)
|
||||
2. Step-by-step walkthrough
|
||||
3. Key concepts used
|
||||
4. How it fits in larger context
|
||||
|
||||
Use analogies where helpful. Suitable for juniors.`
|
||||
},
|
||||
|
||||
security: {
|
||||
name: 'Security Audit',
|
||||
description: 'Check for security vulnerabilities',
|
||||
category: 'analysis',
|
||||
prompt: `Perform a security audit of the provided code.
|
||||
Check for:
|
||||
- Injection vulnerabilities (SQL, XSS, etc.)
|
||||
- Authentication/authorization issues
|
||||
- Sensitive data exposure
|
||||
- Insecure dependencies
|
||||
- Cryptographic weaknesses
|
||||
- OWASP Top 10 issues
|
||||
|
||||
Severity: 🔴 Critical | 🟠 High | 🟡 Medium | 🟢 Low`
|
||||
},
|
||||
|
||||
// Generation Skills
|
||||
api: {
|
||||
name: 'API Design',
|
||||
description: 'Design REST API endpoints',
|
||||
category: 'generation',
|
||||
prompt: `Design REST API endpoints for the described functionality.
|
||||
Include:
|
||||
- Endpoint paths and methods
|
||||
- Request/response schemas (JSON)
|
||||
- Status codes
|
||||
- Authentication requirements
|
||||
- Rate limiting suggestions
|
||||
- OpenAPI/Swagger format if helpful`
|
||||
},
|
||||
|
||||
schema: {
|
||||
name: 'Database Schema',
|
||||
description: 'Design database schema',
|
||||
category: 'generation',
|
||||
prompt: `Design a database schema for the described requirements.
|
||||
Include:
|
||||
- Tables and columns with types
|
||||
- Primary/foreign keys
|
||||
- Indexes for performance
|
||||
- Relationships diagram (text-based)
|
||||
- Migration script if helpful
|
||||
|
||||
Consider normalization and query patterns.`
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get all available skills
|
||||
*/
|
||||
export function getAllSkills() {
|
||||
return Object.entries(SKILLS).map(([id, skill]) => ({
|
||||
id,
|
||||
...skill
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get skills grouped by category
|
||||
*/
|
||||
export function getSkillsByCategory() {
|
||||
const categories = {};
|
||||
|
||||
Object.entries(SKILLS).forEach(([id, skill]) => {
|
||||
if (!categories[skill.category]) {
|
||||
categories[skill.category] = [];
|
||||
}
|
||||
categories[skill.category].push({ id, ...skill });
|
||||
});
|
||||
|
||||
return categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific skill by ID
|
||||
*/
|
||||
export function getSkill(skillId) {
|
||||
return SKILLS[skillId] ? { id: skillId, ...SKILLS[skillId] } : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a skill - returns the prompt to inject
|
||||
* @param {string} skillId - Skill ID
|
||||
* @param {string} userInput - User's additional input/code
|
||||
*/
|
||||
export function executeSkill(skillId, userInput = '') {
|
||||
const skill = getSkill(skillId);
|
||||
if (!skill) return null;
|
||||
|
||||
const fullPrompt = `[SKILL: ${skill.name}]
|
||||
|
||||
${skill.prompt}
|
||||
|
||||
USER INPUT/CODE:
|
||||
${userInput}
|
||||
|
||||
Please proceed with the ${skill.name.toLowerCase()} task.`;
|
||||
|
||||
return {
|
||||
skill,
|
||||
prompt: fullPrompt
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get formatted skill list for display
|
||||
*/
|
||||
export function getSkillListDisplay() {
|
||||
const categories = getSkillsByCategory();
|
||||
let output = '';
|
||||
|
||||
for (const [category, skills] of Object.entries(categories)) {
|
||||
output += `\n📁 ${category.toUpperCase()}\n`;
|
||||
skills.forEach(skill => {
|
||||
output += ` /skill ${skill.id.padEnd(10)} - ${skill.description}\n`;
|
||||
});
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
export default {
|
||||
getAllSkills,
|
||||
getSkillsByCategory,
|
||||
getSkill,
|
||||
executeSkill,
|
||||
getSkillListDisplay
|
||||
};
|
||||
Reference in New Issue
Block a user