neuro-cli 4.1.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 +510 -0
- package/dist/advisor/advisor.d.ts +50 -0
- package/dist/advisor/advisor.js +178 -0
- package/dist/agents/base.d.ts +62 -0
- package/dist/agents/base.js +215 -0
- package/dist/agents/orchestrator.d.ts +46 -0
- package/dist/agents/orchestrator.js +192 -0
- package/dist/agents/team.d.ts +51 -0
- package/dist/agents/team.js +210 -0
- package/dist/api/models.d.ts +23 -0
- package/dist/api/models.js +514 -0
- package/dist/api/ollama.d.ts +153 -0
- package/dist/api/ollama.js +751 -0
- package/dist/api/openrouter.d.ts +55 -0
- package/dist/api/openrouter.js +223 -0
- package/dist/commands/commands.d.ts +43 -0
- package/dist/commands/commands.js +308 -0
- package/dist/config/config.d.ts +8 -0
- package/dist/config/config.js +311 -0
- package/dist/context/compaction.d.ts +54 -0
- package/dist/context/compaction.js +251 -0
- package/dist/context/custom-agents.d.ts +107 -0
- package/dist/context/custom-agents.js +397 -0
- package/dist/context/custom-tools.d.ts +120 -0
- package/dist/context/custom-tools.js +564 -0
- package/dist/context/git-checkpoint.d.ts +52 -0
- package/dist/context/git-checkpoint.js +240 -0
- package/dist/context/neuro-md.d.ts +48 -0
- package/dist/context/neuro-md.js +202 -0
- package/dist/context/neuroignore.d.ts +102 -0
- package/dist/context/neuroignore.js +441 -0
- package/dist/context/repo-map.d.ts +38 -0
- package/dist/context/repo-map.js +220 -0
- package/dist/context/skill-standard.d.ts +262 -0
- package/dist/context/skill-standard.js +1156 -0
- package/dist/context/skill-system.d.ts +75 -0
- package/dist/context/skill-system.js +578 -0
- package/dist/context/tree-sitter.d.ts +99 -0
- package/dist/context/tree-sitter.js +1956 -0
- package/dist/core/acp.d.ts +325 -0
- package/dist/core/acp.js +1498 -0
- package/dist/core/api-server.d.ts +143 -0
- package/dist/core/api-server.js +550 -0
- package/dist/core/approval.d.ts +81 -0
- package/dist/core/approval.js +432 -0
- package/dist/core/auto-compact.d.ts +127 -0
- package/dist/core/auto-compact.js +436 -0
- package/dist/core/auto-mode.d.ts +232 -0
- package/dist/core/auto-mode.js +831 -0
- package/dist/core/background-session.d.ts +166 -0
- package/dist/core/background-session.js +696 -0
- package/dist/core/cicd.d.ts +257 -0
- package/dist/core/cicd.js +1443 -0
- package/dist/core/cloud-sync.d.ts +156 -0
- package/dist/core/cloud-sync.js +582 -0
- package/dist/core/code-review.d.ts +132 -0
- package/dist/core/code-review.js +1191 -0
- package/dist/core/completion.d.ts +49 -0
- package/dist/core/completion.js +384 -0
- package/dist/core/context.d.ts +38 -0
- package/dist/core/context.js +144 -0
- package/dist/core/diff-preview.d.ts +35 -0
- package/dist/core/diff-preview.js +173 -0
- package/dist/core/doom-loop.d.ts +51 -0
- package/dist/core/doom-loop.js +179 -0
- package/dist/core/engine.d.ts +183 -0
- package/dist/core/engine.js +942 -0
- package/dist/core/extended-thinking.d.ts +103 -0
- package/dist/core/extended-thinking.js +269 -0
- package/dist/core/fallback.d.ts +54 -0
- package/dist/core/fallback.js +104 -0
- package/dist/core/git-worktree.d.ts +139 -0
- package/dist/core/git-worktree.js +614 -0
- package/dist/core/headless.d.ts +30 -0
- package/dist/core/headless.js +101 -0
- package/dist/core/i18n.d.ts +83 -0
- package/dist/core/i18n.js +739 -0
- package/dist/core/linting.d.ts +129 -0
- package/dist/core/linting.js +699 -0
- package/dist/core/model-router.d.ts +109 -0
- package/dist/core/model-router.js +581 -0
- package/dist/core/multi-model.d.ts +243 -0
- package/dist/core/multi-model.js +1099 -0
- package/dist/core/multi-session.d.ts +144 -0
- package/dist/core/multi-session.js +442 -0
- package/dist/core/multimodal.d.ts +125 -0
- package/dist/core/multimodal.js +286 -0
- package/dist/core/observability.d.ts +93 -0
- package/dist/core/observability.js +737 -0
- package/dist/core/os-sandbox.d.ts +122 -0
- package/dist/core/os-sandbox.js +1193 -0
- package/dist/core/outcome-grading.d.ts +228 -0
- package/dist/core/outcome-grading.js +1123 -0
- package/dist/core/output-styles.d.ts +57 -0
- package/dist/core/output-styles.js +382 -0
- package/dist/core/parallel-agents.d.ts +183 -0
- package/dist/core/parallel-agents.js +563 -0
- package/dist/core/plugin-bundle.d.ts +236 -0
- package/dist/core/plugin-bundle.js +887 -0
- package/dist/core/plugin-sdk.d.ts +139 -0
- package/dist/core/plugin-sdk.js +273 -0
- package/dist/core/prompt-cache.d.ts +163 -0
- package/dist/core/prompt-cache.js +599 -0
- package/dist/core/sandbox.d.ts +127 -0
- package/dist/core/sandbox.js +369 -0
- package/dist/core/scheduled-tasks.d.ts +151 -0
- package/dist/core/scheduled-tasks.js +502 -0
- package/dist/core/security-scanner.d.ts +160 -0
- package/dist/core/security-scanner.js +1494 -0
- package/dist/core/session.d.ts +83 -0
- package/dist/core/session.js +269 -0
- package/dist/core/shell-completion.d.ts +51 -0
- package/dist/core/shell-completion.js +674 -0
- package/dist/core/smart-monitor.d.ts +146 -0
- package/dist/core/smart-monitor.js +1199 -0
- package/dist/core/spec-driven.d.ts +233 -0
- package/dist/core/spec-driven.js +1485 -0
- package/dist/core/spending-warnings.d.ts +123 -0
- package/dist/core/spending-warnings.js +456 -0
- package/dist/core/sub-agent.d.ts +298 -0
- package/dist/core/sub-agent.js +1023 -0
- package/dist/core/telemetry.d.ts +157 -0
- package/dist/core/telemetry.js +412 -0
- package/dist/core/terminal-ux.d.ts +134 -0
- package/dist/core/terminal-ux.js +649 -0
- package/dist/core/testing.d.ts +146 -0
- package/dist/core/testing.js +1200 -0
- package/dist/core/types.d.ts +439 -0
- package/dist/core/types.js +6 -0
- package/dist/core/undo-redo.d.ts +112 -0
- package/dist/core/undo-redo.js +290 -0
- package/dist/core/updater.d.ts +159 -0
- package/dist/core/updater.js +608 -0
- package/dist/core/vim-mode.d.ts +151 -0
- package/dist/core/vim-mode.js +771 -0
- package/dist/core/voice.d.ts +137 -0
- package/dist/core/voice.js +538 -0
- package/dist/core/web-dashboard.d.ts +109 -0
- package/dist/core/web-dashboard.js +484 -0
- package/dist/hooks/hooks.d.ts +74 -0
- package/dist/hooks/hooks.js +160 -0
- package/dist/hooks/llm-evaluator.d.ts +165 -0
- package/dist/hooks/llm-evaluator.js +560 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1186 -0
- package/dist/lsp/lsp-manager.d.ts +63 -0
- package/dist/lsp/lsp-manager.js +351 -0
- package/dist/mcp/client.d.ts +133 -0
- package/dist/mcp/client.js +684 -0
- package/dist/mcp/mcp-apps.d.ts +70 -0
- package/dist/mcp/mcp-apps.js +1007 -0
- package/dist/tools/bash.d.ts +5 -0
- package/dist/tools/bash.js +195 -0
- package/dist/tools/browser.d.ts +92 -0
- package/dist/tools/browser.js +1570 -0
- package/dist/tools/extended.d.ts +6 -0
- package/dist/tools/extended.js +191 -0
- package/dist/tools/file.d.ts +10 -0
- package/dist/tools/file.js +382 -0
- package/dist/tools/github.d.ts +389 -0
- package/dist/tools/github.js +759 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.js +40 -0
- package/dist/tools/memory.d.ts +6 -0
- package/dist/tools/memory.js +197 -0
- package/dist/tools/registry.d.ts +29 -0
- package/dist/tools/registry.js +64 -0
- package/dist/tools/web.d.ts +6 -0
- package/dist/tools/web.js +150 -0
- package/dist/ui/renderer.d.ts +97 -0
- package/dist/ui/renderer.js +279 -0
- package/dist/ui/theme.d.ts +27 -0
- package/dist/ui/theme.js +106 -0
- package/package.json +83 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { ToolDefinition } from './types.js';
|
|
2
|
+
export interface NeuroPlugin {
|
|
3
|
+
/** Unique plugin name */
|
|
4
|
+
name: string;
|
|
5
|
+
/** Plugin version */
|
|
6
|
+
version: string;
|
|
7
|
+
/** Plugin description */
|
|
8
|
+
description: string;
|
|
9
|
+
/** Author name */
|
|
10
|
+
author?: string;
|
|
11
|
+
/** Tools provided by this plugin */
|
|
12
|
+
tools: NeuroTool[];
|
|
13
|
+
/** Initialize hook - called when plugin is loaded */
|
|
14
|
+
onInit?: (context: PluginContext) => void | Promise<void>;
|
|
15
|
+
/** Cleanup hook - called when plugin is unloaded */
|
|
16
|
+
onDestroy?: () => void | Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
export interface NeuroTool {
|
|
19
|
+
/** Tool definition for the AI model */
|
|
20
|
+
definition: ToolDefinition;
|
|
21
|
+
/** Risk level for approval system */
|
|
22
|
+
risk: 'low' | 'medium' | 'high';
|
|
23
|
+
/** Implementation function */
|
|
24
|
+
execute: (args: Record<string, unknown>, context: ToolExecutionContext) => Promise<ToolExecutionResult>;
|
|
25
|
+
}
|
|
26
|
+
export interface ToolExecutionContext {
|
|
27
|
+
/** Current working directory */
|
|
28
|
+
workingDirectory: string;
|
|
29
|
+
/** Session ID */
|
|
30
|
+
sessionId: string;
|
|
31
|
+
/** Agent name using this tool */
|
|
32
|
+
agentName: string;
|
|
33
|
+
/** Send progress updates to the UI */
|
|
34
|
+
onProgress: (message: string) => void;
|
|
35
|
+
/** Access to other tools */
|
|
36
|
+
callTool: (toolName: string, args: Record<string, unknown>) => Promise<ToolExecutionResult>;
|
|
37
|
+
/** Access to session memory */
|
|
38
|
+
memory: PluginMemory;
|
|
39
|
+
}
|
|
40
|
+
export interface ToolExecutionResult {
|
|
41
|
+
content: string;
|
|
42
|
+
isError?: boolean;
|
|
43
|
+
metadata?: Record<string, unknown>;
|
|
44
|
+
}
|
|
45
|
+
export interface PluginContext {
|
|
46
|
+
/** Plugin configuration directory (~/.neuro/plugins/<name>/) */
|
|
47
|
+
configDir: string;
|
|
48
|
+
/** Plugin data directory for persistent storage */
|
|
49
|
+
dataDir: string;
|
|
50
|
+
/** Logger */
|
|
51
|
+
log: (level: 'info' | 'warn' | 'error', message: string) => void;
|
|
52
|
+
/** Access NeuroCLI configuration */
|
|
53
|
+
getConfig: () => Record<string, unknown>;
|
|
54
|
+
}
|
|
55
|
+
export interface PluginMemory {
|
|
56
|
+
get(key: string): string | undefined;
|
|
57
|
+
set(key: string, value: string): void;
|
|
58
|
+
delete(key: string): void;
|
|
59
|
+
list(): Array<{
|
|
60
|
+
key: string;
|
|
61
|
+
value: string;
|
|
62
|
+
}>;
|
|
63
|
+
}
|
|
64
|
+
export declare class PluginManager {
|
|
65
|
+
private plugins;
|
|
66
|
+
private pluginContexts;
|
|
67
|
+
private pluginMemories;
|
|
68
|
+
/**
|
|
69
|
+
* Load a plugin from a file path
|
|
70
|
+
*/
|
|
71
|
+
loadFromPath(pluginPath: string): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Load a plugin from the plugins directory
|
|
74
|
+
*/
|
|
75
|
+
loadByName(name: string): Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Load all plugins from the plugins directory
|
|
78
|
+
*/
|
|
79
|
+
loadAll(): Promise<number>;
|
|
80
|
+
/**
|
|
81
|
+
* Register a plugin
|
|
82
|
+
*/
|
|
83
|
+
register(plugin: NeuroPlugin): Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Unregister a plugin
|
|
86
|
+
*/
|
|
87
|
+
unregister(name: string): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* Get all plugin tools as tool definitions
|
|
90
|
+
*/
|
|
91
|
+
getToolDefinitions(): Array<ToolDefinition & {
|
|
92
|
+
risk: 'low' | 'medium' | 'high';
|
|
93
|
+
pluginName: string;
|
|
94
|
+
}>;
|
|
95
|
+
/**
|
|
96
|
+
* Execute a plugin tool
|
|
97
|
+
*/
|
|
98
|
+
executeTool(fullToolName: string, args: Record<string, unknown>, context: ToolExecutionContext): Promise<ToolExecutionResult>;
|
|
99
|
+
/**
|
|
100
|
+
* Parse a plugin tool name (plugin_name__tool_name)
|
|
101
|
+
*/
|
|
102
|
+
parsePluginToolName(fullName: string): {
|
|
103
|
+
pluginName: string;
|
|
104
|
+
toolName: string;
|
|
105
|
+
} | null;
|
|
106
|
+
/**
|
|
107
|
+
* List all loaded plugins
|
|
108
|
+
*/
|
|
109
|
+
listPlugins(): Array<{
|
|
110
|
+
name: string;
|
|
111
|
+
version: string;
|
|
112
|
+
description: string;
|
|
113
|
+
toolCount: number;
|
|
114
|
+
author?: string;
|
|
115
|
+
}>;
|
|
116
|
+
/**
|
|
117
|
+
* Check if a tool name belongs to a plugin
|
|
118
|
+
*/
|
|
119
|
+
isPluginTool(toolName: string): boolean;
|
|
120
|
+
private loadPluginMemory;
|
|
121
|
+
private savePluginMemory;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Create a simple plugin with minimal boilerplate
|
|
125
|
+
*/
|
|
126
|
+
export declare function createPlugin(config: {
|
|
127
|
+
name: string;
|
|
128
|
+
version: string;
|
|
129
|
+
description: string;
|
|
130
|
+
author?: string;
|
|
131
|
+
tools: Array<{
|
|
132
|
+
name: string;
|
|
133
|
+
description: string;
|
|
134
|
+
parameters: ToolDefinition['parameters'];
|
|
135
|
+
risk?: 'low' | 'medium' | 'high';
|
|
136
|
+
execute: (args: Record<string, unknown>, context: ToolExecutionContext) => Promise<string>;
|
|
137
|
+
}>;
|
|
138
|
+
}): NeuroPlugin;
|
|
139
|
+
//# sourceMappingURL=plugin-sdk.d.ts.map
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// NeuroCLI - Plugin / Custom Tools SDK
|
|
3
|
+
// Allows users to define and register custom tools
|
|
4
|
+
// ============================================================
|
|
5
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { homedir } from 'os';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
// --- Plugin Manager ---
|
|
10
|
+
const PLUGINS_DIR = join(homedir(), '.neuro', 'plugins');
|
|
11
|
+
export class PluginManager {
|
|
12
|
+
plugins = new Map();
|
|
13
|
+
pluginContexts = new Map();
|
|
14
|
+
pluginMemories = new Map();
|
|
15
|
+
/**
|
|
16
|
+
* Load a plugin from a file path
|
|
17
|
+
*/
|
|
18
|
+
async loadFromPath(pluginPath) {
|
|
19
|
+
try {
|
|
20
|
+
// Dynamic import for ESM plugins
|
|
21
|
+
const module = await import(pluginPath);
|
|
22
|
+
const plugin = module.default || module;
|
|
23
|
+
if (!plugin.name || !plugin.tools) {
|
|
24
|
+
throw new Error('Invalid plugin: missing name or tools');
|
|
25
|
+
}
|
|
26
|
+
await this.register(plugin);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.error(chalk.red(` Failed to load plugin from ${pluginPath}: ${error instanceof Error ? error.message : String(error)}`));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Load a plugin from the plugins directory
|
|
34
|
+
*/
|
|
35
|
+
async loadByName(name) {
|
|
36
|
+
const pluginPath = join(PLUGINS_DIR, name, 'index.js');
|
|
37
|
+
if (!existsSync(pluginPath)) {
|
|
38
|
+
throw new Error(`Plugin "${name}" not found at ${pluginPath}`);
|
|
39
|
+
}
|
|
40
|
+
await this.loadFromPath(pluginPath);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Load all plugins from the plugins directory
|
|
44
|
+
*/
|
|
45
|
+
async loadAll() {
|
|
46
|
+
if (!existsSync(PLUGINS_DIR))
|
|
47
|
+
return 0;
|
|
48
|
+
let loaded = 0;
|
|
49
|
+
const entries = readdirSync(PLUGINS_DIR, { withFileTypes: true });
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
if (entry.isDirectory()) {
|
|
52
|
+
const indexPath = join(PLUGINS_DIR, entry.name, 'index.js');
|
|
53
|
+
if (existsSync(indexPath)) {
|
|
54
|
+
try {
|
|
55
|
+
await this.loadFromPath(indexPath);
|
|
56
|
+
loaded++;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
console.error(chalk.yellow(` Plugin "${entry.name}" failed to load: ${error instanceof Error ? error.message : String(error)}`));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return loaded;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Register a plugin
|
|
68
|
+
*/
|
|
69
|
+
async register(plugin) {
|
|
70
|
+
if (this.plugins.has(plugin.name)) {
|
|
71
|
+
await this.unregister(plugin.name);
|
|
72
|
+
}
|
|
73
|
+
// Set up plugin context
|
|
74
|
+
const pluginDir = join(PLUGINS_DIR, plugin.name);
|
|
75
|
+
const dataDir = join(pluginDir, 'data');
|
|
76
|
+
if (!existsSync(dataDir))
|
|
77
|
+
mkdirSync(dataDir, { recursive: true });
|
|
78
|
+
const memory = new Map();
|
|
79
|
+
this.pluginMemories.set(plugin.name, memory);
|
|
80
|
+
this.loadPluginMemory(plugin.name, memory);
|
|
81
|
+
const context = {
|
|
82
|
+
configDir: pluginDir,
|
|
83
|
+
dataDir,
|
|
84
|
+
log: (level, message) => {
|
|
85
|
+
const prefix = chalk.gray(`[plugin:${plugin.name}]`);
|
|
86
|
+
switch (level) {
|
|
87
|
+
case 'info':
|
|
88
|
+
console.log(`${prefix} ${message}`);
|
|
89
|
+
break;
|
|
90
|
+
case 'warn':
|
|
91
|
+
console.log(`${prefix} ${chalk.yellow(message)}`);
|
|
92
|
+
break;
|
|
93
|
+
case 'error':
|
|
94
|
+
console.log(`${prefix} ${chalk.red(message)}`);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
getConfig: () => {
|
|
99
|
+
const configPath = join(pluginDir, 'config.json');
|
|
100
|
+
if (existsSync(configPath)) {
|
|
101
|
+
try {
|
|
102
|
+
return JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return {};
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
this.pluginContexts.set(plugin.name, context);
|
|
112
|
+
this.plugins.set(plugin.name, plugin);
|
|
113
|
+
// Initialize plugin
|
|
114
|
+
if (plugin.onInit) {
|
|
115
|
+
try {
|
|
116
|
+
await plugin.onInit(context);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
console.error(chalk.yellow(` Plugin "${plugin.name}" init failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
console.log(chalk.green(` ✓ Plugin loaded: ${plugin.name} v${plugin.version} (${plugin.tools.length} tools)`));
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Unregister a plugin
|
|
126
|
+
*/
|
|
127
|
+
async unregister(name) {
|
|
128
|
+
const plugin = this.plugins.get(name);
|
|
129
|
+
if (!plugin)
|
|
130
|
+
return;
|
|
131
|
+
if (plugin.onDestroy) {
|
|
132
|
+
try {
|
|
133
|
+
await plugin.onDestroy();
|
|
134
|
+
}
|
|
135
|
+
catch { }
|
|
136
|
+
}
|
|
137
|
+
this.savePluginMemory(name);
|
|
138
|
+
this.plugins.delete(name);
|
|
139
|
+
this.pluginContexts.delete(name);
|
|
140
|
+
this.pluginMemories.delete(name);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get all plugin tools as tool definitions
|
|
144
|
+
*/
|
|
145
|
+
getToolDefinitions() {
|
|
146
|
+
const definitions = [];
|
|
147
|
+
for (const [pluginName, plugin] of this.plugins) {
|
|
148
|
+
for (const tool of plugin.tools) {
|
|
149
|
+
definitions.push({
|
|
150
|
+
...tool.definition,
|
|
151
|
+
name: `plugin_${pluginName}__${tool.definition.name}`,
|
|
152
|
+
risk: tool.risk,
|
|
153
|
+
pluginName,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return definitions;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Execute a plugin tool
|
|
161
|
+
*/
|
|
162
|
+
async executeTool(fullToolName, args, context) {
|
|
163
|
+
const parsed = this.parsePluginToolName(fullToolName);
|
|
164
|
+
if (!parsed)
|
|
165
|
+
throw new Error(`Not a plugin tool: ${fullToolName}`);
|
|
166
|
+
const { pluginName, toolName } = parsed;
|
|
167
|
+
const plugin = this.plugins.get(pluginName);
|
|
168
|
+
if (!plugin)
|
|
169
|
+
throw new Error(`Plugin not found: ${pluginName}`);
|
|
170
|
+
const tool = plugin.tools.find(t => t.definition.name === toolName);
|
|
171
|
+
if (!tool)
|
|
172
|
+
throw new Error(`Tool "${toolName}" not found in plugin "${pluginName}"`);
|
|
173
|
+
// Extend context with plugin memory
|
|
174
|
+
const memory = this.pluginMemories.get(pluginName) || new Map();
|
|
175
|
+
const extendedContext = {
|
|
176
|
+
...context,
|
|
177
|
+
memory: {
|
|
178
|
+
get: (key) => memory.get(key),
|
|
179
|
+
set: (key, value) => memory.set(key, value),
|
|
180
|
+
delete: (key) => memory.delete(key),
|
|
181
|
+
list: () => Array.from(memory.entries()).map(([key, value]) => ({ key, value })),
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
try {
|
|
185
|
+
return await tool.execute(args, extendedContext);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
return {
|
|
189
|
+
content: `Plugin tool error: ${error instanceof Error ? error.message : String(error)}`,
|
|
190
|
+
isError: true,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Parse a plugin tool name (plugin_name__tool_name)
|
|
196
|
+
*/
|
|
197
|
+
parsePluginToolName(fullName) {
|
|
198
|
+
if (!fullName.startsWith('plugin_'))
|
|
199
|
+
return null;
|
|
200
|
+
const rest = fullName.slice(7);
|
|
201
|
+
const sep = rest.indexOf('__');
|
|
202
|
+
if (sep === -1)
|
|
203
|
+
return null;
|
|
204
|
+
return { pluginName: rest.slice(0, sep), toolName: rest.slice(sep + 2) };
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* List all loaded plugins
|
|
208
|
+
*/
|
|
209
|
+
listPlugins() {
|
|
210
|
+
return Array.from(this.plugins.entries()).map(([name, plugin]) => ({
|
|
211
|
+
name: plugin.name,
|
|
212
|
+
version: plugin.version,
|
|
213
|
+
description: plugin.description,
|
|
214
|
+
toolCount: plugin.tools.length,
|
|
215
|
+
author: plugin.author,
|
|
216
|
+
}));
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Check if a tool name belongs to a plugin
|
|
220
|
+
*/
|
|
221
|
+
isPluginTool(toolName) {
|
|
222
|
+
return toolName.startsWith('plugin_') && toolName.includes('__');
|
|
223
|
+
}
|
|
224
|
+
// --- Plugin Memory Persistence ---
|
|
225
|
+
loadPluginMemory(pluginName, memory) {
|
|
226
|
+
const memPath = join(PLUGINS_DIR, pluginName, 'memory.json');
|
|
227
|
+
if (existsSync(memPath)) {
|
|
228
|
+
try {
|
|
229
|
+
const data = JSON.parse(readFileSync(memPath, 'utf-8'));
|
|
230
|
+
for (const [key, value] of Object.entries(data)) {
|
|
231
|
+
memory.set(key, value);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
catch { }
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
savePluginMemory(pluginName) {
|
|
238
|
+
const memory = this.pluginMemories.get(pluginName);
|
|
239
|
+
if (!memory || memory.size === 0)
|
|
240
|
+
return;
|
|
241
|
+
const memPath = join(PLUGINS_DIR, pluginName, 'memory.json');
|
|
242
|
+
const dir = join(memPath, '..');
|
|
243
|
+
if (!existsSync(dir))
|
|
244
|
+
mkdirSync(dir, { recursive: true });
|
|
245
|
+
const data = Object.fromEntries(memory);
|
|
246
|
+
writeFileSync(memPath, JSON.stringify(data, null, 2), 'utf-8');
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// --- Plugin Template Helper ---
|
|
250
|
+
/**
|
|
251
|
+
* Create a simple plugin with minimal boilerplate
|
|
252
|
+
*/
|
|
253
|
+
export function createPlugin(config) {
|
|
254
|
+
return {
|
|
255
|
+
name: config.name,
|
|
256
|
+
version: config.version,
|
|
257
|
+
description: config.description,
|
|
258
|
+
author: config.author,
|
|
259
|
+
tools: config.tools.map(tool => ({
|
|
260
|
+
definition: {
|
|
261
|
+
name: tool.name,
|
|
262
|
+
description: tool.description,
|
|
263
|
+
parameters: tool.parameters,
|
|
264
|
+
},
|
|
265
|
+
risk: tool.risk || 'medium',
|
|
266
|
+
execute: async (args, context) => {
|
|
267
|
+
const content = await tool.execute(args, context);
|
|
268
|
+
return { content };
|
|
269
|
+
},
|
|
270
|
+
})),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
//# sourceMappingURL=plugin-sdk.js.map
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
export interface CacheEntry {
|
|
2
|
+
key: string;
|
|
3
|
+
model: string;
|
|
4
|
+
promptHash: string;
|
|
5
|
+
response: string;
|
|
6
|
+
inputTokens: number;
|
|
7
|
+
outputTokens: number;
|
|
8
|
+
cost: number;
|
|
9
|
+
createdAt: number;
|
|
10
|
+
lastAccessedAt: number;
|
|
11
|
+
accessCount: number;
|
|
12
|
+
ttlMs: number;
|
|
13
|
+
}
|
|
14
|
+
export interface CacheStats {
|
|
15
|
+
totalEntries: number;
|
|
16
|
+
totalSizeBytes: number;
|
|
17
|
+
hits: number;
|
|
18
|
+
misses: number;
|
|
19
|
+
hitRate: number;
|
|
20
|
+
totalSavings: number;
|
|
21
|
+
evictions: number;
|
|
22
|
+
}
|
|
23
|
+
export interface CacheBreakdown {
|
|
24
|
+
byModel: Record<string, {
|
|
25
|
+
entries: number;
|
|
26
|
+
hits: number;
|
|
27
|
+
savings: number;
|
|
28
|
+
}>;
|
|
29
|
+
totalSavings: number;
|
|
30
|
+
totalHits: number;
|
|
31
|
+
totalMisses: number;
|
|
32
|
+
}
|
|
33
|
+
export interface CacheExportFormat {
|
|
34
|
+
version: number;
|
|
35
|
+
exportedAt: number;
|
|
36
|
+
entries: CacheEntry[];
|
|
37
|
+
}
|
|
38
|
+
export declare class PromptCache {
|
|
39
|
+
private cacheDir;
|
|
40
|
+
private maxEntries;
|
|
41
|
+
private ttlMs;
|
|
42
|
+
private similarityThreshold;
|
|
43
|
+
private enabled;
|
|
44
|
+
/** In-memory index mapping cache key to lightweight metadata. */
|
|
45
|
+
private index;
|
|
46
|
+
/** Runtime statistics -- not persisted beyond the process lifetime. */
|
|
47
|
+
private stats;
|
|
48
|
+
constructor(config: {
|
|
49
|
+
cacheDir?: string;
|
|
50
|
+
maxEntries?: number;
|
|
51
|
+
ttlMs?: number;
|
|
52
|
+
similarityThreshold?: number;
|
|
53
|
+
enabled?: boolean;
|
|
54
|
+
});
|
|
55
|
+
/**
|
|
56
|
+
* Look up a cached response for the given model + messages.
|
|
57
|
+
* Performs an exact-key lookup first, then falls back to
|
|
58
|
+
* similarity-based matching across entries for the same model.
|
|
59
|
+
*/
|
|
60
|
+
get(model: string, messages: Array<{
|
|
61
|
+
role: string;
|
|
62
|
+
content: string;
|
|
63
|
+
}>): CacheEntry | null;
|
|
64
|
+
/**
|
|
65
|
+
* Store a response in the cache.
|
|
66
|
+
*/
|
|
67
|
+
set(model: string, messages: Array<{
|
|
68
|
+
role: string;
|
|
69
|
+
content: string;
|
|
70
|
+
}>, response: string, usage: {
|
|
71
|
+
inputTokens: number;
|
|
72
|
+
outputTokens: number;
|
|
73
|
+
cost: number;
|
|
74
|
+
}): void;
|
|
75
|
+
/**
|
|
76
|
+
* Remove a specific cache entry by key.
|
|
77
|
+
*/
|
|
78
|
+
invalidate(key: string): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Remove all cache entries and reset statistics.
|
|
81
|
+
*/
|
|
82
|
+
clear(): void;
|
|
83
|
+
/**
|
|
84
|
+
* Return aggregate cache statistics.
|
|
85
|
+
*/
|
|
86
|
+
getStats(): CacheStats;
|
|
87
|
+
/**
|
|
88
|
+
* Return a cost breakdown grouped by model (for the /cost command).
|
|
89
|
+
*/
|
|
90
|
+
getBreakdown(): CacheBreakdown;
|
|
91
|
+
/**
|
|
92
|
+
* Pre-populate the cache with common prompts.
|
|
93
|
+
* Returns the number of entries successfully warmed.
|
|
94
|
+
*/
|
|
95
|
+
warmup(entries: Array<{
|
|
96
|
+
model: string;
|
|
97
|
+
messages: Array<{
|
|
98
|
+
role: string;
|
|
99
|
+
content: string;
|
|
100
|
+
}>;
|
|
101
|
+
response: string;
|
|
102
|
+
}>): Promise<number>;
|
|
103
|
+
/**
|
|
104
|
+
* Export all non-expired cache entries to a portable format.
|
|
105
|
+
*/
|
|
106
|
+
exportEntries(): CacheExportFormat;
|
|
107
|
+
/**
|
|
108
|
+
* Import cache entries from a previously exported format.
|
|
109
|
+
* Returns the number of entries imported.
|
|
110
|
+
*/
|
|
111
|
+
importEntries(data: CacheExportFormat): number;
|
|
112
|
+
/**
|
|
113
|
+
* Print a human-readable stats summary to stdout.
|
|
114
|
+
*/
|
|
115
|
+
printStats(): void;
|
|
116
|
+
private hashContent;
|
|
117
|
+
private messagesKey;
|
|
118
|
+
private serializeMessages;
|
|
119
|
+
private isExpired;
|
|
120
|
+
private evictIfNeeded;
|
|
121
|
+
/**
|
|
122
|
+
* Search for a non-expired entry for the same model whose prompt is
|
|
123
|
+
* similar enough to the provided text (Jaccard similarity on trigrams).
|
|
124
|
+
*/
|
|
125
|
+
private findSimilar;
|
|
126
|
+
private ensureCacheDir;
|
|
127
|
+
private loadIndex;
|
|
128
|
+
private saveIndex;
|
|
129
|
+
private entryFilePath;
|
|
130
|
+
/**
|
|
131
|
+
* Persist a StoredEntry (with _promptText) to its individual JSON file.
|
|
132
|
+
*/
|
|
133
|
+
private persistStoredEntry;
|
|
134
|
+
/**
|
|
135
|
+
* Load a StoredEntry from disk. Returns null if the file is missing or corrupt.
|
|
136
|
+
*/
|
|
137
|
+
private loadStoredEntry;
|
|
138
|
+
/**
|
|
139
|
+
* Remove an entry (index metadata + file on disk).
|
|
140
|
+
*/
|
|
141
|
+
private removeEntry;
|
|
142
|
+
/**
|
|
143
|
+
* Update the in-memory index metadata for a given entry.
|
|
144
|
+
*/
|
|
145
|
+
private updateIndexMeta;
|
|
146
|
+
/**
|
|
147
|
+
* Strip the internal _promptText field to return the public CacheEntry shape.
|
|
148
|
+
*/
|
|
149
|
+
private toPublicEntry;
|
|
150
|
+
/**
|
|
151
|
+
* Rough token estimate based on the ~4 chars per token heuristic.
|
|
152
|
+
* Used for warmup entries that lack actual token counts.
|
|
153
|
+
*/
|
|
154
|
+
private estimateTokens;
|
|
155
|
+
}
|
|
156
|
+
export declare const DEFAULT_PROMPT_CACHE_CONFIG: {
|
|
157
|
+
cacheDir: string;
|
|
158
|
+
maxEntries: number;
|
|
159
|
+
ttlMs: number;
|
|
160
|
+
similarityThreshold: number;
|
|
161
|
+
enabled: boolean;
|
|
162
|
+
};
|
|
163
|
+
//# sourceMappingURL=prompt-cache.d.ts.map
|