nex-code 0.3.5 → 0.3.7

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/cli/tool-tiers.js DELETED
@@ -1,164 +0,0 @@
1
- /**
2
- * cli/tool-tiers.js — Dynamic Tool Set Selection
3
- * Reduces tool count for less capable models to improve reliability.
4
- */
5
-
6
- const { getActiveModel, getActiveProviderName } = require('./providers/registry');
7
-
8
- /**
9
- * Tool tier definitions.
10
- * - essential: Core tools every model gets (5 tools)
11
- * - standard: Good set for capable models (12 tools)
12
- * - full: All tools for the most capable models (15 tools)
13
- */
14
- const TIERS = {
15
- essential: ['bash', 'read_file', 'write_file', 'edit_file', 'list_directory'],
16
- standard: ['bash', 'read_file', 'write_file', 'edit_file', 'list_directory', 'search_files', 'glob', 'grep', 'ask_user', 'git_status', 'git_diff', 'git_log', 'task_list'],
17
- full: null, // null = all tools, no filtering
18
- };
19
-
20
- /**
21
- * Model capability classification.
22
- * Maps known models to their tier. Unknown models default based on provider.
23
- */
24
- const MODEL_TIERS = {
25
- // Ollama Cloud — tier by capability
26
- 'qwen3-coder:480b': 'full',
27
- 'qwen3-coder-next': 'full',
28
- 'kimi-k2.5': 'full',
29
- 'kimi-k2:1t': 'full',
30
- 'kimi-k2-thinking': 'full',
31
- 'deepseek-v3.2': 'full',
32
- 'deepseek-v3.1:671b': 'full',
33
- 'devstral-2:123b': 'full',
34
- 'devstral-small-2:24b': 'standard',
35
- 'cogito-2.1:671b': 'full',
36
- 'qwen3-next:80b': 'full',
37
- 'qwen3.5:397b': 'full',
38
- 'mistral-large-3:675b': 'full',
39
- 'gpt-oss:120b': 'full',
40
- 'minimax-m2.5': 'full',
41
- 'glm-5': 'full',
42
- 'glm-4.7': 'standard',
43
- 'gemma3:27b': 'standard',
44
- 'gemma3:12b': 'essential',
45
- 'gemma3:4b': 'essential',
46
- 'ministral-3:14b': 'standard',
47
- 'ministral-3:8b': 'essential',
48
-
49
- // OpenAI — all full
50
- 'gpt-4o': 'full',
51
- 'gpt-4.1': 'full',
52
- 'o1': 'full',
53
- 'o3': 'full',
54
- 'o4-mini': 'full',
55
-
56
- // Anthropic — all full
57
- 'claude-sonnet': 'full',
58
- 'claude-sonnet-4-5': 'full',
59
- 'claude-opus': 'full',
60
- 'claude-haiku': 'standard',
61
- 'claude-sonnet-4': 'full',
62
-
63
- // Gemini — all full
64
- 'gemini-2.5-pro': 'full',
65
- 'gemini-2.5-flash': 'full',
66
- 'gemini-2.0-flash': 'standard',
67
- 'gemini-2.0-flash-lite': 'essential',
68
- };
69
-
70
- /**
71
- * Default tier per provider (for unknown models).
72
- */
73
- const PROVIDER_DEFAULT_TIER = {
74
- ollama: 'standard',
75
- openai: 'full',
76
- anthropic: 'full',
77
- gemini: 'standard',
78
- local: 'essential',
79
- };
80
-
81
- /**
82
- * Override tiers from config.
83
- * Loaded once from .nex/config.json at startup.
84
- */
85
- let configOverrides = {};
86
-
87
- function loadConfigOverrides() {
88
- try {
89
- const fs = require('fs');
90
- const path = require('path');
91
- const configPath = path.join(process.cwd(), '.nex', 'config.json');
92
- if (fs.existsSync(configPath)) {
93
- const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
94
- configOverrides = config.toolTiers || {};
95
- }
96
- } catch {
97
- configOverrides = {};
98
- }
99
- }
100
-
101
- // Load on require
102
- loadConfigOverrides();
103
-
104
- /**
105
- * Get the tool tier for the active model.
106
- * Priority: config override > MODEL_TIERS > PROVIDER_DEFAULT_TIER > 'standard'
107
- */
108
- function getActiveTier() {
109
- const model = getActiveModel();
110
- const modelId = model?.id;
111
- const provider = getActiveProviderName();
112
-
113
- // Config override (user can set per-model tiers)
114
- if (modelId && configOverrides[modelId]) return configOverrides[modelId];
115
- if (provider && configOverrides[`${provider}:*`]) return configOverrides[`${provider}:*`];
116
-
117
- // Built-in tier mapping
118
- if (modelId && MODEL_TIERS[modelId]) return MODEL_TIERS[modelId];
119
-
120
- // Provider default
121
- if (provider && PROVIDER_DEFAULT_TIER[provider]) return PROVIDER_DEFAULT_TIER[provider];
122
-
123
- return 'standard';
124
- }
125
-
126
- /**
127
- * Get the tool tier for a specific model.
128
- * Priority: config override > MODEL_TIERS > PROVIDER_DEFAULT_TIER > 'standard'
129
- * @param {string} modelId
130
- * @param {string} providerName
131
- * @returns {string} Tier name ('essential', 'standard', or 'full')
132
- */
133
- function getModelTier(modelId, providerName) {
134
- if (modelId && configOverrides[modelId]) return configOverrides[modelId];
135
- if (providerName && configOverrides[`${providerName}:*`]) return configOverrides[`${providerName}:*`];
136
- if (modelId && MODEL_TIERS[modelId]) return MODEL_TIERS[modelId];
137
- if (providerName && PROVIDER_DEFAULT_TIER[providerName]) return PROVIDER_DEFAULT_TIER[providerName];
138
- return 'standard';
139
- }
140
-
141
- /**
142
- * Filter tool definitions based on the active model's tier.
143
- * @param {Array} tools - Full tool definitions array
144
- * @param {string} [overrideTier] - Override tier (bypasses active model detection)
145
- * @returns {Array} Filtered tool definitions
146
- */
147
- function filterToolsForModel(tools, overrideTier) {
148
- const tier = overrideTier || getActiveTier();
149
- if (tier === 'full' || !TIERS[tier]) return tools;
150
-
151
- const allowedNames = new Set(TIERS[tier]);
152
- return tools.filter(t => allowedNames.has(t.function.name));
153
- }
154
-
155
- /**
156
- * Get the current tier name (for display in /status etc.)
157
- */
158
- function getTierInfo() {
159
- const tier = getActiveTier();
160
- const toolCount = TIERS[tier] ? TIERS[tier].length : 'all';
161
- return { tier, toolCount };
162
- }
163
-
164
- module.exports = { filterToolsForModel, getActiveTier, getModelTier, getTierInfo, TIERS, MODEL_TIERS, PROVIDER_DEFAULT_TIER, loadConfigOverrides };
@@ -1,138 +0,0 @@
1
- /**
2
- * cli/tool-validator.js — Tool Argument Validation
3
- * Validates tool call arguments against their JSON Schema definitions.
4
- * Returns helpful error messages for open-source models.
5
- */
6
-
7
- const { getSkillToolDefinitions } = require('./skills');
8
- const { getMCPToolDefinitions } = require('./mcp');
9
-
10
- /**
11
- * Find the closest matching string from a list (Levenshtein distance).
12
- */
13
- function closestMatch(input, candidates) {
14
- if (!input || candidates.length === 0) return null;
15
-
16
- let best = null;
17
- let bestDist = Infinity;
18
-
19
- for (const c of candidates) {
20
- const dist = levenshtein(input.toLowerCase(), c.toLowerCase());
21
- if (dist < bestDist) {
22
- bestDist = dist;
23
- best = c;
24
- }
25
- }
26
-
27
- // Only suggest if reasonably close (within half the length)
28
- return bestDist <= Math.ceil(input.length / 2) ? best : null;
29
- }
30
-
31
- function levenshtein(a, b) {
32
- const m = a.length, n = b.length;
33
- const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
34
- for (let i = 0; i <= m; i++) dp[i][0] = i;
35
- for (let j = 0; j <= n; j++) dp[0][j] = j;
36
- for (let i = 1; i <= m; i++) {
37
- for (let j = 1; j <= n; j++) {
38
- dp[i][j] = a[i - 1] === b[j - 1]
39
- ? dp[i - 1][j - 1]
40
- : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
41
- }
42
- }
43
- return dp[m][n];
44
- }
45
-
46
- /**
47
- * Validate tool arguments against schema.
48
- * @param {string} toolName - Name of the tool
49
- * @param {object} args - Parsed arguments
50
- * @returns {{ valid: boolean, error?: string, corrected?: object }}
51
- */
52
- function validateToolArgs(toolName, args) {
53
- // Lazy require to break circular dependency (tools.js → fuzzy-match.js → tool-validator.js → tools.js)
54
- const { TOOL_DEFINITIONS } = require('./tools');
55
- const allTools = [...TOOL_DEFINITIONS, ...getSkillToolDefinitions(), ...getMCPToolDefinitions()];
56
- const toolDef = allTools.find(t => t.function.name === toolName);
57
-
58
- if (!toolDef) {
59
- // Unknown tool — check for close matches
60
- const allNames = allTools.map(t => t.function.name);
61
- const suggestion = closestMatch(toolName, allNames);
62
- return {
63
- valid: false,
64
- error: `Unknown tool "${toolName}".${suggestion ? ` Did you mean "${suggestion}"?` : ''}\nAvailable tools: ${allNames.join(', ')}`,
65
- };
66
- }
67
-
68
- const schema = toolDef.function.parameters;
69
- if (!schema || !schema.properties) return { valid: true };
70
-
71
- const required = schema.required || [];
72
- const expectedKeys = Object.keys(schema.properties);
73
- const providedKeys = Object.keys(args);
74
- const errors = [];
75
- const corrected = { ...args };
76
- let wasCorrected = false;
77
-
78
- // Check for required fields
79
- for (const key of required) {
80
- if (!(key in args) || args[key] === undefined || args[key] === null) {
81
- // Check if a similar key was provided (e.g. "file" instead of "path")
82
- const suggestion = closestMatch(key, providedKeys);
83
- if (suggestion && !expectedKeys.includes(suggestion)) {
84
- // Auto-correct: use the similar key's value
85
- corrected[key] = args[suggestion];
86
- delete corrected[suggestion];
87
- wasCorrected = true;
88
- } else {
89
- errors.push(`Missing required parameter "${key}" (${schema.properties[key]?.description || schema.properties[key]?.type || 'unknown'})`);
90
- }
91
- }
92
- }
93
-
94
- // Check for unknown keys and suggest corrections
95
- for (const key of providedKeys) {
96
- if (!expectedKeys.includes(key)) {
97
- const suggestion = closestMatch(key, expectedKeys);
98
- if (suggestion && !(suggestion in corrected)) {
99
- corrected[suggestion] = args[key];
100
- delete corrected[key];
101
- wasCorrected = true;
102
- } else if (!wasCorrected) {
103
- errors.push(`Unknown parameter "${key}".${suggestion ? ` Did you mean "${suggestion}"?` : ''}`);
104
- }
105
- }
106
- }
107
-
108
- // Type checking for provided values
109
- for (const key of Object.keys(corrected)) {
110
- if (!schema.properties[key]) continue;
111
- const expected = schema.properties[key].type;
112
- const actual = typeof corrected[key];
113
-
114
- if (expected === 'string' && actual === 'number') {
115
- corrected[key] = String(corrected[key]);
116
- wasCorrected = true;
117
- } else if (expected === 'number' && actual === 'string' && !isNaN(corrected[key])) {
118
- corrected[key] = Number(corrected[key]);
119
- wasCorrected = true;
120
- } else if (expected === 'boolean' && actual === 'string') {
121
- corrected[key] = corrected[key] === 'true';
122
- wasCorrected = true;
123
- }
124
- }
125
-
126
- if (errors.length > 0 && !wasCorrected) {
127
- return {
128
- valid: false,
129
- error: `Tool "${toolName}" argument errors:\n` +
130
- errors.map(e => ` - ${e}`).join('\n') +
131
- `\n\nExpected parameters: ${JSON.stringify(schema.properties, null, 2)}`,
132
- };
133
- }
134
-
135
- return { valid: true, corrected: wasCorrected ? corrected : null };
136
- }
137
-
138
- module.exports = { validateToolArgs, closestMatch, levenshtein };