add-skill-kit 3.2.3 → 3.2.5
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 +1 -1
- package/bin/lib/commands/help.js +0 -4
- package/bin/lib/commands/install.js +90 -9
- package/bin/lib/ui.js +1 -1
- package/lib/agent-cli/__tests__/adaptive_engine.test.js +190 -0
- package/lib/agent-cli/__tests__/integration/cross_script.test.js +222 -0
- package/lib/agent-cli/__tests__/integration/full_cycle.test.js +230 -0
- package/lib/agent-cli/__tests__/pattern_analyzer.test.js +173 -0
- package/lib/agent-cli/__tests__/pre_execution_check.test.js +167 -0
- package/lib/agent-cli/__tests__/skill_injector.test.js +191 -0
- package/lib/agent-cli/bin/agent.js +191 -0
- package/lib/agent-cli/dashboard/dashboard_server.js +340 -0
- package/lib/agent-cli/dashboard/index.html +538 -0
- package/lib/agent-cli/lib/audit.js +154 -0
- package/lib/agent-cli/lib/audit.test.js +100 -0
- package/lib/agent-cli/lib/auto-learn.js +319 -0
- package/lib/agent-cli/lib/auto_preview.py +148 -0
- package/lib/agent-cli/lib/backup.js +138 -0
- package/lib/agent-cli/lib/backup.test.js +78 -0
- package/lib/agent-cli/lib/checklist.py +222 -0
- package/lib/agent-cli/lib/cognitive-lesson.js +476 -0
- package/lib/agent-cli/lib/completion.js +149 -0
- package/lib/agent-cli/lib/config.js +35 -0
- package/lib/agent-cli/lib/eslint-fix.js +238 -0
- package/lib/agent-cli/lib/evolution-signal.js +215 -0
- package/lib/agent-cli/lib/export.js +86 -0
- package/lib/agent-cli/lib/export.test.js +65 -0
- package/lib/agent-cli/lib/fix.js +337 -0
- package/lib/agent-cli/lib/fix.test.js +80 -0
- package/lib/agent-cli/lib/gemini-export.js +83 -0
- package/lib/agent-cli/lib/generate-registry.js +42 -0
- package/lib/agent-cli/lib/hooks/install-hooks.js +152 -0
- package/lib/agent-cli/lib/hooks/lint-learn.js +172 -0
- package/lib/agent-cli/lib/ignore.js +116 -0
- package/lib/agent-cli/lib/ignore.test.js +58 -0
- package/lib/agent-cli/lib/init.js +124 -0
- package/lib/agent-cli/lib/learn.js +255 -0
- package/lib/agent-cli/lib/learn.test.js +70 -0
- package/lib/agent-cli/lib/migrate-to-v4.js +322 -0
- package/lib/agent-cli/lib/proposals.js +199 -0
- package/lib/agent-cli/lib/proposals.test.js +56 -0
- package/lib/agent-cli/lib/recall.js +820 -0
- package/lib/agent-cli/lib/recall.test.js +107 -0
- package/lib/agent-cli/lib/selfevolution-bridge.js +167 -0
- package/lib/agent-cli/lib/session_manager.py +120 -0
- package/lib/agent-cli/lib/settings.js +227 -0
- package/lib/agent-cli/lib/skill-learn.js +296 -0
- package/lib/agent-cli/lib/stats.js +132 -0
- package/lib/agent-cli/lib/stats.test.js +94 -0
- package/lib/agent-cli/lib/types.js +33 -0
- package/lib/agent-cli/lib/ui/audit-ui.js +146 -0
- package/lib/agent-cli/lib/ui/backup-ui.js +107 -0
- package/lib/agent-cli/lib/ui/clack-helpers.js +317 -0
- package/lib/agent-cli/lib/ui/common.js +83 -0
- package/lib/agent-cli/lib/ui/completion-ui.js +126 -0
- package/lib/agent-cli/lib/ui/custom-select.js +69 -0
- package/lib/agent-cli/lib/ui/dashboard-ui.js +222 -0
- package/lib/agent-cli/lib/ui/evolution-signals-ui.js +107 -0
- package/lib/agent-cli/lib/ui/export-ui.js +94 -0
- package/lib/agent-cli/lib/ui/fix-all-ui.js +191 -0
- package/lib/agent-cli/lib/ui/help-ui.js +49 -0
- package/lib/agent-cli/lib/ui/index.js +199 -0
- package/lib/agent-cli/lib/ui/init-ui.js +56 -0
- package/lib/agent-cli/lib/ui/knowledge-ui.js +55 -0
- package/lib/agent-cli/lib/ui/learn-ui.js +706 -0
- package/lib/agent-cli/lib/ui/lessons-ui.js +148 -0
- package/lib/agent-cli/lib/ui/pretty.js +145 -0
- package/lib/agent-cli/lib/ui/proposals-ui.js +99 -0
- package/lib/agent-cli/lib/ui/recall-ui.js +342 -0
- package/lib/agent-cli/lib/ui/routing-demo.js +79 -0
- package/lib/agent-cli/lib/ui/routing-ui.js +325 -0
- package/lib/agent-cli/lib/ui/settings-ui.js +381 -0
- package/lib/agent-cli/lib/ui/stats-ui.js +123 -0
- package/lib/agent-cli/lib/ui/watch-ui.js +236 -0
- package/lib/agent-cli/lib/verify_all.py +327 -0
- package/lib/agent-cli/lib/watcher.js +181 -0
- package/lib/agent-cli/lib/watcher.test.js +85 -0
- package/lib/agent-cli/package.json +51 -0
- package/lib/agent-cli/scripts/adaptive_engine.js +381 -0
- package/lib/agent-cli/scripts/dashboard_server.js +224 -0
- package/lib/agent-cli/scripts/error_sensor.js +565 -0
- package/lib/agent-cli/scripts/learn_from_failure.js +225 -0
- package/lib/agent-cli/scripts/pattern_analyzer.js +781 -0
- package/lib/agent-cli/scripts/pre_execution_check.js +623 -0
- package/lib/agent-cli/scripts/rule_sharing.js +374 -0
- package/lib/agent-cli/scripts/skill_injector.js +387 -0
- package/lib/agent-cli/scripts/success_sensor.js +500 -0
- package/lib/agent-cli/scripts/user_correction_sensor.js +426 -0
- package/lib/agent-cli/services/auto-learn-service.js +247 -0
- package/lib/agent-cli/src/MIGRATION.md +418 -0
- package/lib/agent-cli/src/README.md +367 -0
- package/lib/agent-cli/src/core/evolution/evolution-signal.js +42 -0
- package/lib/agent-cli/src/core/evolution/index.js +17 -0
- package/lib/agent-cli/src/core/evolution/review-gate.js +40 -0
- package/lib/agent-cli/src/core/evolution/signal-detector.js +137 -0
- package/lib/agent-cli/src/core/evolution/signal-queue.js +79 -0
- package/lib/agent-cli/src/core/evolution/threshold-checker.js +79 -0
- package/lib/agent-cli/src/core/index.js +15 -0
- package/lib/agent-cli/src/core/learning/cognitive-enhancer.js +282 -0
- package/lib/agent-cli/src/core/learning/index.js +12 -0
- package/lib/agent-cli/src/core/learning/lesson-synthesizer.js +83 -0
- package/lib/agent-cli/src/core/scanning/index.js +14 -0
- package/lib/agent-cli/src/data/index.js +13 -0
- package/lib/agent-cli/src/data/repositories/index.js +8 -0
- package/lib/agent-cli/src/data/repositories/lesson-repository.js +130 -0
- package/lib/agent-cli/src/data/repositories/signal-repository.js +119 -0
- package/lib/agent-cli/src/data/storage/index.js +8 -0
- package/lib/agent-cli/src/data/storage/json-storage.js +64 -0
- package/lib/agent-cli/src/data/storage/yaml-storage.js +66 -0
- package/lib/agent-cli/src/infrastructure/index.js +13 -0
- package/lib/agent-cli/src/presentation/formatters/skill-formatter.js +232 -0
- package/lib/agent-cli/src/services/export-service.js +162 -0
- package/lib/agent-cli/src/services/index.js +13 -0
- package/lib/agent-cli/src/services/learning-service.js +99 -0
- package/lib/agent-cli/types/index.d.ts +343 -0
- package/lib/agent-cli/utils/benchmark.js +269 -0
- package/lib/agent-cli/utils/logger.js +303 -0
- package/lib/agent-cli/utils/ml_patterns.js +300 -0
- package/lib/agent-cli/utils/recovery.js +312 -0
- package/lib/agent-cli/utils/telemetry.js +290 -0
- package/lib/agentskillskit-cli/README.md +21 -0
- package/{node_modules/agentskillskit-cli/bin → lib/agentskillskit-cli}/ag-smart.js +15 -15
- package/lib/agentskillskit-cli/package.json +51 -0
- package/package.json +19 -9
- /package/bin/{cli.js → kit.js} +0 -0
- /package/{node_modules/agentskillskit-cli → lib/agent-cli}/README.md +0 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger Utility - Configurable logging with levels and file output
|
|
3
|
+
*
|
|
4
|
+
* Part of FAANG-Grade Auto-Learn System
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - 4 log levels: DEBUG, INFO, WARN, ERROR
|
|
8
|
+
* - Console output (default)
|
|
9
|
+
* - File output (optional)
|
|
10
|
+
* - Timestamps
|
|
11
|
+
* - Lazy evaluation for debug messages
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* import { logger, setLogLevel, enableFileLogging } from './logger.js';
|
|
15
|
+
* logger.debug('Debug message', { data });
|
|
16
|
+
* logger.info('Info message');
|
|
17
|
+
* logger.warn('Warning message');
|
|
18
|
+
* logger.error('Error message', error);
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import fs from 'fs';
|
|
22
|
+
import path from 'path';
|
|
23
|
+
import { fileURLToPath } from 'url';
|
|
24
|
+
|
|
25
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
26
|
+
const __dirname = path.dirname(__filename);
|
|
27
|
+
|
|
28
|
+
// ==================== LOG LEVELS ====================
|
|
29
|
+
|
|
30
|
+
const LOG_LEVELS = {
|
|
31
|
+
DEBUG: 0,
|
|
32
|
+
INFO: 1,
|
|
33
|
+
WARN: 2,
|
|
34
|
+
ERROR: 3,
|
|
35
|
+
SILENT: 4
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// ==================== COLORS ====================
|
|
39
|
+
|
|
40
|
+
const colors = {
|
|
41
|
+
reset: '\x1b[0m',
|
|
42
|
+
red: '\x1b[31m',
|
|
43
|
+
green: '\x1b[32m',
|
|
44
|
+
yellow: '\x1b[33m',
|
|
45
|
+
blue: '\x1b[34m',
|
|
46
|
+
cyan: '\x1b[36m',
|
|
47
|
+
gray: '\x1b[90m',
|
|
48
|
+
bold: '\x1b[1m'
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const levelColors = {
|
|
52
|
+
DEBUG: colors.gray,
|
|
53
|
+
INFO: colors.cyan,
|
|
54
|
+
WARN: colors.yellow,
|
|
55
|
+
ERROR: colors.red
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const levelIcons = {
|
|
59
|
+
DEBUG: '🔍',
|
|
60
|
+
INFO: 'ℹ️',
|
|
61
|
+
WARN: '⚠️',
|
|
62
|
+
ERROR: '❌'
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// ==================== STATE ====================
|
|
66
|
+
|
|
67
|
+
let currentLevel = LOG_LEVELS.INFO;
|
|
68
|
+
let fileLoggingEnabled = false;
|
|
69
|
+
let logFilePath = null;
|
|
70
|
+
|
|
71
|
+
// ==================== CONFIGURATION ====================
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Set the minimum log level
|
|
75
|
+
* @param {keyof typeof LOG_LEVELS} level - 'DEBUG', 'INFO', 'WARN', 'ERROR', or 'SILENT'
|
|
76
|
+
*/
|
|
77
|
+
function setLogLevel(level) {
|
|
78
|
+
if (LOG_LEVELS[level] !== undefined) {
|
|
79
|
+
currentLevel = LOG_LEVELS[level];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Enable file logging
|
|
85
|
+
* @param {string} filePath - Path to log file
|
|
86
|
+
*/
|
|
87
|
+
function enableFileLogging(filePath) {
|
|
88
|
+
fileLoggingEnabled = true;
|
|
89
|
+
logFilePath = filePath;
|
|
90
|
+
|
|
91
|
+
// Ensure directory exists
|
|
92
|
+
const dir = path.dirname(filePath);
|
|
93
|
+
if (!fs.existsSync(dir)) {
|
|
94
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Disable file logging
|
|
100
|
+
*/
|
|
101
|
+
function disableFileLogging() {
|
|
102
|
+
fileLoggingEnabled = false;
|
|
103
|
+
logFilePath = null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ==================== FORMATTING ====================
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Format log message with timestamp
|
|
110
|
+
*/
|
|
111
|
+
function formatMessage(level, message, data) {
|
|
112
|
+
const timestamp = new Date().toISOString();
|
|
113
|
+
const icon = levelIcons[level];
|
|
114
|
+
const color = levelColors[level];
|
|
115
|
+
|
|
116
|
+
let formatted = `${color}${icon} [${timestamp.split('T')[1].split('.')[0]}] [${level}]${colors.reset} ${message}`;
|
|
117
|
+
|
|
118
|
+
if (data !== undefined) {
|
|
119
|
+
if (typeof data === 'object') {
|
|
120
|
+
formatted += ` ${colors.gray}${JSON.stringify(data)}${colors.reset}`;
|
|
121
|
+
} else {
|
|
122
|
+
formatted += ` ${colors.gray}${data}${colors.reset}`;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return formatted;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Format for file (no colors)
|
|
131
|
+
*/
|
|
132
|
+
function formatForFile(level, message, data) {
|
|
133
|
+
const timestamp = new Date().toISOString();
|
|
134
|
+
let formatted = `[${timestamp}] [${level}] ${message}`;
|
|
135
|
+
|
|
136
|
+
if (data !== undefined) {
|
|
137
|
+
if (typeof data === 'object') {
|
|
138
|
+
formatted += ` ${JSON.stringify(data)}`;
|
|
139
|
+
} else {
|
|
140
|
+
formatted += ` ${data}`;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return formatted;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ==================== WRITE FUNCTIONS ====================
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Write to console
|
|
151
|
+
*/
|
|
152
|
+
function writeToConsole(level, formatted) {
|
|
153
|
+
if (level === 'ERROR') {
|
|
154
|
+
console.error(formatted);
|
|
155
|
+
} else if (level === 'WARN') {
|
|
156
|
+
console.warn(formatted);
|
|
157
|
+
} else {
|
|
158
|
+
console.log(formatted);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Write to file (async)
|
|
164
|
+
*/
|
|
165
|
+
function writeToFile(formatted) {
|
|
166
|
+
if (fileLoggingEnabled && logFilePath) {
|
|
167
|
+
try {
|
|
168
|
+
fs.appendFileSync(logFilePath, formatted + '\n');
|
|
169
|
+
} catch (err) {
|
|
170
|
+
// Silently fail file logging
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ==================== LOG FUNCTIONS ====================
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Log at specified level
|
|
179
|
+
*/
|
|
180
|
+
function log(level, message, data) {
|
|
181
|
+
if (LOG_LEVELS[level] < currentLevel) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Console output
|
|
186
|
+
const formatted = formatMessage(level, message, data);
|
|
187
|
+
writeToConsole(level, formatted);
|
|
188
|
+
|
|
189
|
+
// File output
|
|
190
|
+
if (fileLoggingEnabled) {
|
|
191
|
+
const fileFormatted = formatForFile(level, message, data);
|
|
192
|
+
writeToFile(fileFormatted);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Debug log (lowest priority)
|
|
198
|
+
* @param {string} message - Log message
|
|
199
|
+
* @param {any} [data] - Optional data to log
|
|
200
|
+
*/
|
|
201
|
+
function debug(message, data) {
|
|
202
|
+
log('DEBUG', message, data);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Info log
|
|
207
|
+
* @param {string} message - Log message
|
|
208
|
+
* @param {any} [data] - Optional data to log
|
|
209
|
+
*/
|
|
210
|
+
function info(message, data) {
|
|
211
|
+
log('INFO', message, data);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Warning log
|
|
216
|
+
* @param {string} message - Log message
|
|
217
|
+
* @param {any} [data] - Optional data to log
|
|
218
|
+
*/
|
|
219
|
+
function warn(message, data) {
|
|
220
|
+
log('WARN', message, data);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Error log (highest priority)
|
|
225
|
+
* @param {string} message - Log message
|
|
226
|
+
* @param {any} [data] - Optional error or data
|
|
227
|
+
*/
|
|
228
|
+
function error(message, data) {
|
|
229
|
+
log('ERROR', message, data);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// ==================== SPECIALIZED LOGGERS ====================
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Create a logger with fixed prefix
|
|
236
|
+
* @param {string} prefix - Prefix for all log messages
|
|
237
|
+
*/
|
|
238
|
+
function createLogger(prefix) {
|
|
239
|
+
return {
|
|
240
|
+
debug: (msg, data) => debug(`[${prefix}] ${msg}`, data),
|
|
241
|
+
info: (msg, data) => info(`[${prefix}] ${msg}`, data),
|
|
242
|
+
warn: (msg, data) => warn(`[${prefix}] ${msg}`, data),
|
|
243
|
+
error: (msg, data) => error(`[${prefix}] ${msg}`, data)
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Log timing for operations
|
|
249
|
+
* @param {string} label - Operation label
|
|
250
|
+
*/
|
|
251
|
+
function time(label) {
|
|
252
|
+
const start = Date.now();
|
|
253
|
+
return {
|
|
254
|
+
end: () => {
|
|
255
|
+
const duration = Date.now() - start;
|
|
256
|
+
debug(`${label} completed`, { duration: `${duration}ms` });
|
|
257
|
+
return duration;
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// ==================== LOGGER OBJECT ====================
|
|
263
|
+
|
|
264
|
+
const logger = {
|
|
265
|
+
debug,
|
|
266
|
+
info,
|
|
267
|
+
warn,
|
|
268
|
+
error,
|
|
269
|
+
time,
|
|
270
|
+
createLogger,
|
|
271
|
+
setLevel: setLogLevel,
|
|
272
|
+
enableFile: enableFileLogging,
|
|
273
|
+
disableFile: disableFileLogging,
|
|
274
|
+
LOG_LEVELS
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
// ==================== CLI SUPPORT ====================
|
|
278
|
+
|
|
279
|
+
// Check for environment variable
|
|
280
|
+
if (process.env.LOG_LEVEL) {
|
|
281
|
+
setLogLevel(process.env.LOG_LEVEL.toUpperCase());
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Check for LOG_FILE environment variable
|
|
285
|
+
if (process.env.LOG_FILE) {
|
|
286
|
+
enableFileLogging(process.env.LOG_FILE);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export {
|
|
290
|
+
logger,
|
|
291
|
+
debug,
|
|
292
|
+
info,
|
|
293
|
+
warn,
|
|
294
|
+
error,
|
|
295
|
+
setLogLevel,
|
|
296
|
+
enableFileLogging,
|
|
297
|
+
disableFileLogging,
|
|
298
|
+
createLogger,
|
|
299
|
+
time,
|
|
300
|
+
LOG_LEVELS
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
export default logger;
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ML Pattern Matching Utility
|
|
3
|
+
*
|
|
4
|
+
* Provides intelligent pattern matching using similarity algorithms:
|
|
5
|
+
* - Levenshtein distance
|
|
6
|
+
* - Jaccard similarity
|
|
7
|
+
* - Fuzzy matching with confidence scores
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* import { findSimilar, matchWithConfidence } from './ml_patterns.js';
|
|
11
|
+
*
|
|
12
|
+
* const matches = findSimilar('missing-await', patterns, 0.7);
|
|
13
|
+
* const result = matchWithConfidence(intent, rules);
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// ==================== LEVENSHTEIN DISTANCE ====================
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Calculate Levenshtein distance between two strings
|
|
20
|
+
* @param {string} a - First string
|
|
21
|
+
* @param {string} b - Second string
|
|
22
|
+
* @returns {number} Edit distance
|
|
23
|
+
*/
|
|
24
|
+
function levenshtein(a, b) {
|
|
25
|
+
const matrix = [];
|
|
26
|
+
|
|
27
|
+
for (let i = 0; i <= b.length; i++) {
|
|
28
|
+
matrix[i] = [i];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
for (let j = 0; j <= a.length; j++) {
|
|
32
|
+
matrix[0][j] = j;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
for (let i = 1; i <= b.length; i++) {
|
|
36
|
+
for (let j = 1; j <= a.length; j++) {
|
|
37
|
+
if (b.charAt(i - 1) === a.charAt(j - 1)) {
|
|
38
|
+
matrix[i][j] = matrix[i - 1][j - 1];
|
|
39
|
+
} else {
|
|
40
|
+
matrix[i][j] = Math.min(
|
|
41
|
+
matrix[i - 1][j - 1] + 1, // substitution
|
|
42
|
+
matrix[i][j - 1] + 1, // insertion
|
|
43
|
+
matrix[i - 1][j] + 1 // deletion
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return matrix[b.length][a.length];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Calculate similarity ratio (0-1) using Levenshtein
|
|
54
|
+
* @param {string} a - First string
|
|
55
|
+
* @param {string} b - Second string
|
|
56
|
+
* @returns {number} Similarity ratio (1 = identical)
|
|
57
|
+
*/
|
|
58
|
+
function levenshteinSimilarity(a, b) {
|
|
59
|
+
const distance = levenshtein(a.toLowerCase(), b.toLowerCase());
|
|
60
|
+
const maxLen = Math.max(a.length, b.length);
|
|
61
|
+
return maxLen === 0 ? 1 : 1 - distance / maxLen;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ==================== JACCARD SIMILARITY ====================
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Tokenize string into words/n-grams
|
|
68
|
+
* @param {string} str - Input string
|
|
69
|
+
* @param {number} n - N-gram size (default: 2)
|
|
70
|
+
*/
|
|
71
|
+
function tokenize(str, n = 2) {
|
|
72
|
+
const normalized = str.toLowerCase().replace(/[^a-z0-9]/g, ' ').trim();
|
|
73
|
+
const words = normalized.split(/\s+/).filter(w => w.length > 0);
|
|
74
|
+
|
|
75
|
+
if (n === 1) return new Set(words);
|
|
76
|
+
|
|
77
|
+
const ngrams = new Set();
|
|
78
|
+
for (const word of words) {
|
|
79
|
+
for (let i = 0; i <= word.length - n; i++) {
|
|
80
|
+
ngrams.add(word.slice(i, i + n));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return ngrams;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Calculate Jaccard similarity (0-1)
|
|
88
|
+
* @param {string} a - First string
|
|
89
|
+
* @param {string} b - Second string
|
|
90
|
+
* @returns {number} Similarity ratio
|
|
91
|
+
*/
|
|
92
|
+
function jaccardSimilarity(a, b) {
|
|
93
|
+
const setA = tokenize(a);
|
|
94
|
+
const setB = tokenize(b);
|
|
95
|
+
|
|
96
|
+
const intersection = new Set([...setA].filter(x => setB.has(x)));
|
|
97
|
+
const union = new Set([...setA, ...setB]);
|
|
98
|
+
|
|
99
|
+
return union.size === 0 ? 0 : intersection.size / union.size;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ==================== COMBINED SIMILARITY ====================
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Calculate weighted combined similarity
|
|
106
|
+
* @param {string} a - First string
|
|
107
|
+
* @param {string} b - Second string
|
|
108
|
+
* @returns {number} Combined similarity (0-1)
|
|
109
|
+
*/
|
|
110
|
+
function combinedSimilarity(a, b) {
|
|
111
|
+
const lev = levenshteinSimilarity(a, b);
|
|
112
|
+
const jac = jaccardSimilarity(a, b);
|
|
113
|
+
|
|
114
|
+
// Weighted average: Levenshtein more weight for short strings
|
|
115
|
+
const lenFactor = Math.min(a.length, b.length) / 10;
|
|
116
|
+
const levWeight = Math.max(0.3, 0.7 - lenFactor * 0.1);
|
|
117
|
+
const jacWeight = 1 - levWeight;
|
|
118
|
+
|
|
119
|
+
return lev * levWeight + jac * jacWeight;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// ==================== FUZZY MATCHING ====================
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Find similar patterns from a list
|
|
126
|
+
* @param {string} query - Query string
|
|
127
|
+
* @param {string[]} patterns - List of patterns to search
|
|
128
|
+
* @param {number} threshold - Minimum similarity (default: 0.6)
|
|
129
|
+
* @returns {Array<{pattern: string, similarity: number}>}
|
|
130
|
+
*/
|
|
131
|
+
function findSimilar(query, patterns, threshold = 0.6) {
|
|
132
|
+
const matches = [];
|
|
133
|
+
|
|
134
|
+
for (const pattern of patterns) {
|
|
135
|
+
const similarity = combinedSimilarity(query, pattern);
|
|
136
|
+
if (similarity >= threshold) {
|
|
137
|
+
matches.push({ pattern, similarity: Math.round(similarity * 100) / 100 });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return matches.sort((a, b) => b.similarity - a.similarity);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Check if two strings are fuzzy equal
|
|
146
|
+
* @param {string} a - First string
|
|
147
|
+
* @param {string} b - Second string
|
|
148
|
+
* @param {number} threshold - Minimum similarity
|
|
149
|
+
*/
|
|
150
|
+
function fuzzyEquals(a, b, threshold = 0.8) {
|
|
151
|
+
return combinedSimilarity(a, b) >= threshold;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ==================== RULE MATCHING ====================
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Match intent against rules with confidence scores
|
|
158
|
+
* @param {string} intent - User intent
|
|
159
|
+
* @param {Array<{id: string, pattern: string, severity: string}>} rules
|
|
160
|
+
* @param {number} threshold - Minimum confidence
|
|
161
|
+
* @returns {Array<{rule: object, confidence: number, matchType: string}>}
|
|
162
|
+
*/
|
|
163
|
+
function matchWithConfidence(intent, rules, threshold = 0.5) {
|
|
164
|
+
const matches = [];
|
|
165
|
+
const intentLower = intent.toLowerCase();
|
|
166
|
+
|
|
167
|
+
for (const rule of rules) {
|
|
168
|
+
const patternLower = rule.pattern.toLowerCase();
|
|
169
|
+
|
|
170
|
+
// Exact match
|
|
171
|
+
if (intentLower.includes(patternLower)) {
|
|
172
|
+
matches.push({
|
|
173
|
+
rule,
|
|
174
|
+
confidence: 1.0,
|
|
175
|
+
matchType: 'EXACT'
|
|
176
|
+
});
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Word boundary match
|
|
181
|
+
const words = intentLower.split(/\s+/);
|
|
182
|
+
const patternWords = patternLower.split(/[-_\s]+/);
|
|
183
|
+
const wordMatch = patternWords.some(pw =>
|
|
184
|
+
words.some(w => w.includes(pw) || pw.includes(w))
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
if (wordMatch) {
|
|
188
|
+
const similarity = combinedSimilarity(intent, rule.pattern);
|
|
189
|
+
if (similarity >= threshold) {
|
|
190
|
+
matches.push({
|
|
191
|
+
rule,
|
|
192
|
+
confidence: Math.round(similarity * 100) / 100,
|
|
193
|
+
matchType: 'PARTIAL'
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Fuzzy match
|
|
200
|
+
const similarity = combinedSimilarity(intent, rule.pattern);
|
|
201
|
+
if (similarity >= threshold) {
|
|
202
|
+
matches.push({
|
|
203
|
+
rule,
|
|
204
|
+
confidence: Math.round(similarity * 100) / 100,
|
|
205
|
+
matchType: 'FUZZY'
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return matches.sort((a, b) => b.confidence - a.confidence);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ==================== PATTERN CLUSTERING ====================
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Cluster similar patterns together
|
|
217
|
+
* @param {string[]} patterns - List of patterns
|
|
218
|
+
* @param {number} threshold - Similarity threshold for clustering
|
|
219
|
+
* @returns {Array<{representative: string, members: string[]}>}
|
|
220
|
+
*/
|
|
221
|
+
function clusterPatterns(patterns, threshold = 0.7) {
|
|
222
|
+
const clusters = [];
|
|
223
|
+
const assigned = new Set();
|
|
224
|
+
|
|
225
|
+
for (const pattern of patterns) {
|
|
226
|
+
if (assigned.has(pattern)) continue;
|
|
227
|
+
|
|
228
|
+
const cluster = {
|
|
229
|
+
representative: pattern,
|
|
230
|
+
members: [pattern]
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
for (const other of patterns) {
|
|
234
|
+
if (other === pattern || assigned.has(other)) continue;
|
|
235
|
+
|
|
236
|
+
if (combinedSimilarity(pattern, other) >= threshold) {
|
|
237
|
+
cluster.members.push(other);
|
|
238
|
+
assigned.add(other);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
assigned.add(pattern);
|
|
243
|
+
clusters.push(cluster);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return clusters;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Find representative pattern for a group
|
|
251
|
+
* @param {string[]} patterns - Group of similar patterns
|
|
252
|
+
*/
|
|
253
|
+
function findRepresentative(patterns) {
|
|
254
|
+
if (patterns.length === 0) return null;
|
|
255
|
+
if (patterns.length === 1) return patterns[0];
|
|
256
|
+
|
|
257
|
+
// Find pattern closest to all others
|
|
258
|
+
let bestPattern = patterns[0];
|
|
259
|
+
let bestScore = 0;
|
|
260
|
+
|
|
261
|
+
for (const candidate of patterns) {
|
|
262
|
+
let score = 0;
|
|
263
|
+
for (const other of patterns) {
|
|
264
|
+
score += combinedSimilarity(candidate, other);
|
|
265
|
+
}
|
|
266
|
+
if (score > bestScore) {
|
|
267
|
+
bestScore = score;
|
|
268
|
+
bestPattern = candidate;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return bestPattern;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ==================== EXPORTS ====================
|
|
276
|
+
|
|
277
|
+
export {
|
|
278
|
+
levenshtein,
|
|
279
|
+
levenshteinSimilarity,
|
|
280
|
+
jaccardSimilarity,
|
|
281
|
+
combinedSimilarity,
|
|
282
|
+
tokenize,
|
|
283
|
+
findSimilar,
|
|
284
|
+
fuzzyEquals,
|
|
285
|
+
matchWithConfidence,
|
|
286
|
+
clusterPatterns,
|
|
287
|
+
findRepresentative
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
export default {
|
|
291
|
+
levenshtein,
|
|
292
|
+
levenshteinSimilarity,
|
|
293
|
+
jaccardSimilarity,
|
|
294
|
+
combinedSimilarity,
|
|
295
|
+
findSimilar,
|
|
296
|
+
fuzzyEquals,
|
|
297
|
+
matchWithConfidence,
|
|
298
|
+
clusterPatterns,
|
|
299
|
+
findRepresentative
|
|
300
|
+
};
|