@hawon/nexus 0.1.0 → 0.3.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/README.md +60 -38
- package/dist/cli/index.js +76 -145
- package/dist/index.js +15 -26
- package/dist/mcp/server.js +61 -32
- package/package.json +2 -1
- package/scripts/auto-skill.sh +54 -0
- package/scripts/auto-sync.sh +11 -0
- package/scripts/benchmark.ts +444 -0
- package/scripts/scan-tool-result.sh +46 -0
- package/src/cli/index.ts +79 -172
- package/src/index.ts +17 -29
- package/src/mcp/server.ts +67 -41
- package/src/memory-engine/index.ts +4 -6
- package/src/memory-engine/nexus-memory.test.ts +437 -0
- package/src/memory-engine/nexus-memory.ts +631 -0
- package/src/memory-engine/semantic.ts +380 -0
- package/src/parser/parse.ts +1 -21
- package/src/promptguard/advanced-rules.ts +129 -12
- package/src/promptguard/entropy.ts +21 -2
- package/src/promptguard/evolution/auto-update.ts +16 -6
- package/src/promptguard/multilingual-rules.ts +68 -0
- package/src/promptguard/rules.ts +87 -2
- package/src/promptguard/scanner.test.ts +262 -0
- package/src/promptguard/scanner.ts +1 -1
- package/src/promptguard/semantic.ts +19 -4
- package/src/promptguard/token-analysis.ts +17 -5
- package/src/review/analyzer.test.ts +279 -0
- package/src/review/analyzer.ts +112 -28
- package/src/shared/stop-words.ts +21 -0
- package/src/skills/index.ts +11 -27
- package/src/skills/memory-skill-engine.ts +1044 -0
- package/src/testing/health-check.ts +19 -2
- package/src/cost/index.ts +0 -3
- package/src/cost/tracker.ts +0 -290
- package/src/cost/types.ts +0 -34
- package/src/memory-engine/compressor.ts +0 -97
- package/src/memory-engine/context-window.ts +0 -113
- package/src/memory-engine/store.ts +0 -371
- package/src/memory-engine/types.ts +0 -32
- package/src/skills/context-engine.ts +0 -863
- package/src/skills/extractor.ts +0 -224
- package/src/skills/global-context.ts +0 -726
- package/src/skills/library.ts +0 -189
- package/src/skills/pattern-engine.ts +0 -712
- package/src/skills/render-evolved.ts +0 -160
- package/src/skills/skill-reconciler.ts +0 -703
- package/src/skills/smart-extractor.ts +0 -843
- package/src/skills/types.ts +0 -18
- package/src/skills/wisdom-extractor.ts +0 -737
- package/src/superdev-evolution/index.ts +0 -3
- package/src/superdev-evolution/skill-manager.ts +0 -266
- package/src/superdev-evolution/types.ts +0 -20
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
-
import { join, dirname } from "node:path";
|
|
4
|
-
import { randomUUID } from "node:crypto";
|
|
5
|
-
import type { Skill, EvolutionLog } from "./types.js";
|
|
6
|
-
|
|
7
|
-
const RETIRE_THRESHOLD = 0.3; // retire if success rate below 30%
|
|
8
|
-
const MIN_INTERACTIONS_FOR_RETIRE = 5;
|
|
9
|
-
|
|
10
|
-
export type SkillManager = {
|
|
11
|
-
learn(interaction: { input: string; output: string; success: boolean }): Skill | null;
|
|
12
|
-
match(input: string): Skill | null;
|
|
13
|
-
evolve(): void;
|
|
14
|
-
listSkills(): Skill[];
|
|
15
|
-
getLog(): EvolutionLog;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
type PersistedData = {
|
|
19
|
-
skills: Skill[];
|
|
20
|
-
totalInteractions: number;
|
|
21
|
-
skillsCreated: number;
|
|
22
|
-
skillsRetired: number;
|
|
23
|
-
lastEvolvedAt: string;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
function tokenize(text: string): string[] {
|
|
27
|
-
return text
|
|
28
|
-
.toLowerCase()
|
|
29
|
-
.replace(/[^a-z0-9\s_-]/g, " ")
|
|
30
|
-
.split(/\s+/)
|
|
31
|
-
.filter((t) => t.length > 2);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function extractActionPattern(input: string): { name: string; trigger: string; template: string } | null {
|
|
35
|
-
const tokens = tokenize(input);
|
|
36
|
-
if (tokens.length < 2) return null;
|
|
37
|
-
|
|
38
|
-
// Find action verbs and their objects
|
|
39
|
-
const actionVerbs = [
|
|
40
|
-
"create", "build", "generate", "fix", "debug", "test", "deploy",
|
|
41
|
-
"refactor", "review", "analyze", "convert", "migrate", "update",
|
|
42
|
-
"add", "remove", "delete", "install", "configure", "setup",
|
|
43
|
-
"optimize", "format", "lint", "validate", "check", "run",
|
|
44
|
-
"explain", "document", "search", "find", "replace", "transform",
|
|
45
|
-
];
|
|
46
|
-
|
|
47
|
-
let verb: string | null = null;
|
|
48
|
-
let object: string | null = null;
|
|
49
|
-
|
|
50
|
-
for (const token of tokens) {
|
|
51
|
-
if (!verb && actionVerbs.includes(token)) {
|
|
52
|
-
verb = token;
|
|
53
|
-
} else if (verb && !object && token.length > 2) {
|
|
54
|
-
object = token;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (!verb || !object) return null;
|
|
59
|
-
|
|
60
|
-
// Build a trigger regex from the action pattern
|
|
61
|
-
const triggerPattern = `\\b${verb}\\b.*\\b${object}\\b`;
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
name: `${verb}-${object}`,
|
|
65
|
-
trigger: triggerPattern,
|
|
66
|
-
template: `Action: ${verb} ${object}\nSteps from learned interaction.`,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function extractKeyPhrases(input: string): string[] {
|
|
71
|
-
const phrases: string[] = [];
|
|
72
|
-
// Extract quoted strings
|
|
73
|
-
const quoted = input.matchAll(/["']([^"']+)["']/g);
|
|
74
|
-
for (const m of quoted) phrases.push(m[1]);
|
|
75
|
-
|
|
76
|
-
// Extract code-like identifiers
|
|
77
|
-
const identifiers = input.matchAll(/`([^`]+)`/g);
|
|
78
|
-
for (const m of identifiers) phrases.push(m[1]);
|
|
79
|
-
|
|
80
|
-
return phrases;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async function loadData(filePath: string): Promise<PersistedData> {
|
|
84
|
-
try {
|
|
85
|
-
const raw = await readFile(filePath, "utf-8");
|
|
86
|
-
return JSON.parse(raw) as PersistedData;
|
|
87
|
-
} catch {
|
|
88
|
-
return {
|
|
89
|
-
skills: [],
|
|
90
|
-
totalInteractions: 0,
|
|
91
|
-
skillsCreated: 0,
|
|
92
|
-
skillsRetired: 0,
|
|
93
|
-
lastEvolvedAt: new Date().toISOString(),
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
async function saveData(filePath: string, data: PersistedData): Promise<void> {
|
|
99
|
-
await mkdir(dirname(filePath), { recursive: true });
|
|
100
|
-
await writeFile(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export function createSkillManager(dataDir: string): SkillManager {
|
|
104
|
-
const filePath = join(dataDir, "skills.json");
|
|
105
|
-
|
|
106
|
-
let data: PersistedData = {
|
|
107
|
-
skills: [],
|
|
108
|
-
totalInteractions: 0,
|
|
109
|
-
skillsCreated: 0,
|
|
110
|
-
skillsRetired: 0,
|
|
111
|
-
lastEvolvedAt: new Date().toISOString(),
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
let loaded = false;
|
|
115
|
-
|
|
116
|
-
function ensureLoadedSync(): void {
|
|
117
|
-
if (loaded) return;
|
|
118
|
-
try {
|
|
119
|
-
const raw = readFileSync(filePath, "utf-8");
|
|
120
|
-
data = JSON.parse(raw) as PersistedData;
|
|
121
|
-
} catch {
|
|
122
|
-
// File doesn't exist yet — use defaults
|
|
123
|
-
}
|
|
124
|
-
loaded = true;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function persist(): void {
|
|
128
|
-
saveData(filePath, data).catch((err) => {
|
|
129
|
-
process.stderr.write(`superdev: skill persist failed: ${String(err)}\n`);
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return {
|
|
134
|
-
learn(interaction: { input: string; output: string; success: boolean }): Skill | null {
|
|
135
|
-
ensureLoadedSync();
|
|
136
|
-
|
|
137
|
-
data.totalInteractions++;
|
|
138
|
-
|
|
139
|
-
if (!interaction.success) {
|
|
140
|
-
// Update failure count for matching skills
|
|
141
|
-
const matchedSkill = this.match(interaction.input);
|
|
142
|
-
if (matchedSkill) {
|
|
143
|
-
const skill = data.skills.find((s) => s.id === matchedSkill.id);
|
|
144
|
-
if (skill) {
|
|
145
|
-
skill.failureCount++;
|
|
146
|
-
skill.updatedAt = new Date().toISOString();
|
|
147
|
-
persist();
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return null;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Check if we already have a matching skill
|
|
154
|
-
const existing = this.match(interaction.input);
|
|
155
|
-
if (existing) {
|
|
156
|
-
const skill = data.skills.find((s) => s.id === existing.id);
|
|
157
|
-
if (skill) {
|
|
158
|
-
skill.successCount++;
|
|
159
|
-
skill.updatedAt = new Date().toISOString();
|
|
160
|
-
persist();
|
|
161
|
-
}
|
|
162
|
-
return existing;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Try to extract a new skill pattern
|
|
166
|
-
const pattern = extractActionPattern(interaction.input);
|
|
167
|
-
if (!pattern) return null;
|
|
168
|
-
|
|
169
|
-
// Check for duplicate trigger patterns
|
|
170
|
-
const isDuplicate = data.skills.some((s) => s.trigger === pattern.trigger);
|
|
171
|
-
if (isDuplicate) return null;
|
|
172
|
-
|
|
173
|
-
const keyPhrases = extractKeyPhrases(interaction.input);
|
|
174
|
-
const description = keyPhrases.length > 0
|
|
175
|
-
? `Learned pattern for: ${pattern.name} (context: ${keyPhrases.join(", ")})`
|
|
176
|
-
: `Learned pattern for: ${pattern.name}`;
|
|
177
|
-
|
|
178
|
-
const now = new Date().toISOString();
|
|
179
|
-
const newSkill: Skill = {
|
|
180
|
-
id: randomUUID(),
|
|
181
|
-
name: pattern.name,
|
|
182
|
-
description,
|
|
183
|
-
trigger: pattern.trigger,
|
|
184
|
-
template: pattern.template,
|
|
185
|
-
learnedFrom: interaction.input.slice(0, 200),
|
|
186
|
-
successCount: 1,
|
|
187
|
-
failureCount: 0,
|
|
188
|
-
createdAt: now,
|
|
189
|
-
updatedAt: now,
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
data.skills.push(newSkill);
|
|
193
|
-
data.skillsCreated++;
|
|
194
|
-
persist();
|
|
195
|
-
|
|
196
|
-
return newSkill;
|
|
197
|
-
},
|
|
198
|
-
|
|
199
|
-
match(input: string): Skill | null {
|
|
200
|
-
ensureLoadedSync();
|
|
201
|
-
const lowerInput = input.toLowerCase();
|
|
202
|
-
let bestMatch: Skill | null = null;
|
|
203
|
-
let bestScore = 0;
|
|
204
|
-
|
|
205
|
-
for (const skill of data.skills) {
|
|
206
|
-
try {
|
|
207
|
-
const regex = new RegExp(skill.trigger, "i");
|
|
208
|
-
if (regex.test(lowerInput)) {
|
|
209
|
-
// Score by success rate and specificity
|
|
210
|
-
const total = skill.successCount + skill.failureCount;
|
|
211
|
-
const successRate = total > 0 ? skill.successCount / total : 0.5;
|
|
212
|
-
const specificity = skill.trigger.length;
|
|
213
|
-
const score = successRate * specificity;
|
|
214
|
-
|
|
215
|
-
if (score > bestScore) {
|
|
216
|
-
bestScore = score;
|
|
217
|
-
bestMatch = skill;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
} catch {
|
|
221
|
-
// Invalid regex, skip
|
|
222
|
-
continue;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return bestMatch;
|
|
227
|
-
},
|
|
228
|
-
|
|
229
|
-
evolve(): void {
|
|
230
|
-
ensureLoadedSync();
|
|
231
|
-
const retired: string[] = [];
|
|
232
|
-
|
|
233
|
-
data.skills = data.skills.filter((skill) => {
|
|
234
|
-
const total = skill.successCount + skill.failureCount;
|
|
235
|
-
if (total < MIN_INTERACTIONS_FOR_RETIRE) return true;
|
|
236
|
-
|
|
237
|
-
const successRate = skill.successCount / total;
|
|
238
|
-
if (successRate < RETIRE_THRESHOLD) {
|
|
239
|
-
retired.push(skill.id);
|
|
240
|
-
return false;
|
|
241
|
-
}
|
|
242
|
-
return true;
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
data.skillsRetired += retired.length;
|
|
246
|
-
data.lastEvolvedAt = new Date().toISOString();
|
|
247
|
-
persist();
|
|
248
|
-
},
|
|
249
|
-
|
|
250
|
-
listSkills(): Skill[] {
|
|
251
|
-
ensureLoadedSync();
|
|
252
|
-
return [...data.skills];
|
|
253
|
-
},
|
|
254
|
-
|
|
255
|
-
getLog(): EvolutionLog {
|
|
256
|
-
ensureLoadedSync();
|
|
257
|
-
return {
|
|
258
|
-
skills: [...data.skills],
|
|
259
|
-
totalInteractions: data.totalInteractions,
|
|
260
|
-
skillsCreated: data.skillsCreated,
|
|
261
|
-
skillsRetired: data.skillsRetired,
|
|
262
|
-
lastEvolvedAt: data.lastEvolvedAt,
|
|
263
|
-
};
|
|
264
|
-
},
|
|
265
|
-
};
|
|
266
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export type Skill = {
|
|
2
|
-
id: string;
|
|
3
|
-
name: string;
|
|
4
|
-
description: string;
|
|
5
|
-
trigger: string;
|
|
6
|
-
template: string;
|
|
7
|
-
learnedFrom: string;
|
|
8
|
-
successCount: number;
|
|
9
|
-
failureCount: number;
|
|
10
|
-
createdAt: string;
|
|
11
|
-
updatedAt: string;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export type EvolutionLog = {
|
|
15
|
-
skills: Skill[];
|
|
16
|
-
totalInteractions: number;
|
|
17
|
-
skillsCreated: number;
|
|
18
|
-
skillsRetired: number;
|
|
19
|
-
lastEvolvedAt: string;
|
|
20
|
-
};
|