@hawon/nexus 0.2.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/dist/cli/index.js +44 -71
- package/dist/index.js +15 -24
- package/dist/mcp/server.js +7 -6
- package/package.json +1 -1
- package/scripts/benchmark.ts +13 -35
- package/src/cli/index.ts +45 -84
- package/src/index.ts +17 -27
- package/src/mcp/server.ts +8 -7
- package/src/memory-engine/index.ts +4 -6
- package/src/memory-engine/semantic.ts +38 -11
- package/src/promptguard/advanced-rules.ts +122 -5
- package/src/promptguard/entropy.ts +21 -2
- package/src/promptguard/rules.ts +87 -2
- package/src/promptguard/scanner.test.ts +1 -4
- 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.ts +106 -23
- package/src/skills/index.ts +11 -27
- package/src/testing/health-check.ts +19 -2
- 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 -731
- package/src/skills/library.ts +0 -189
- package/src/skills/pattern-engine.ts +0 -715
- package/src/skills/render-evolved.ts +0 -160
- package/src/skills/skill-reconciler.ts +0 -699
- package/src/skills/smart-extractor.ts +0 -849
- 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
package/src/skills/library.ts
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import type { ExtractedSkill, SkillLibrary } from "./types.js";
|
|
4
|
-
|
|
5
|
-
const LIBRARY_FILE = "skill-library.json";
|
|
6
|
-
|
|
7
|
-
export function loadSkillLibrary(dataDir: string): SkillLibrary {
|
|
8
|
-
const filePath = join(dataDir, LIBRARY_FILE);
|
|
9
|
-
|
|
10
|
-
if (!existsSync(filePath)) {
|
|
11
|
-
return {
|
|
12
|
-
skills: [],
|
|
13
|
-
version: 1,
|
|
14
|
-
updatedAt: new Date().toISOString(),
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
try {
|
|
19
|
-
const raw = readFileSync(filePath, "utf-8");
|
|
20
|
-
const data = JSON.parse(raw) as SkillLibrary;
|
|
21
|
-
return data;
|
|
22
|
-
} catch {
|
|
23
|
-
return {
|
|
24
|
-
skills: [],
|
|
25
|
-
version: 1,
|
|
26
|
-
updatedAt: new Date().toISOString(),
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export function saveSkillLibrary(library: SkillLibrary, dataDir: string): void {
|
|
32
|
-
if (!existsSync(dataDir)) {
|
|
33
|
-
mkdirSync(dataDir, { recursive: true });
|
|
34
|
-
}
|
|
35
|
-
const filePath = join(dataDir, LIBRARY_FILE);
|
|
36
|
-
library.updatedAt = new Date().toISOString();
|
|
37
|
-
writeFileSync(filePath, JSON.stringify(library, null, 2), "utf-8");
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Add skills to the library, deduplicating by name similarity.
|
|
42
|
-
* Returns the number of new skills actually added.
|
|
43
|
-
*/
|
|
44
|
-
export function addSkills(library: SkillLibrary, skills: ExtractedSkill[]): number {
|
|
45
|
-
let added = 0;
|
|
46
|
-
|
|
47
|
-
for (const skill of skills) {
|
|
48
|
-
const isDuplicate = library.skills.some(
|
|
49
|
-
(existing) => nameSimilarity(existing.name, skill.name) > 0.8,
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
if (!isDuplicate) {
|
|
53
|
-
library.skills.push(skill);
|
|
54
|
-
added++;
|
|
55
|
-
} else {
|
|
56
|
-
// Update existing skill if new one has higher confidence
|
|
57
|
-
const existingIdx = library.skills.findIndex(
|
|
58
|
-
(existing) => nameSimilarity(existing.name, skill.name) > 0.8,
|
|
59
|
-
);
|
|
60
|
-
if (existingIdx >= 0 && skill.confidence > library.skills[existingIdx].confidence) {
|
|
61
|
-
library.skills[existingIdx] = skill;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
if (added > 0) {
|
|
67
|
-
library.version++;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return added;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Simple keyword search across skill names, descriptions, triggers, and tools.
|
|
75
|
-
*/
|
|
76
|
-
export function searchSkills(library: SkillLibrary, query: string): ExtractedSkill[] {
|
|
77
|
-
const terms = query.toLowerCase().split(/\s+/).filter((t) => t.length > 0);
|
|
78
|
-
if (terms.length === 0) return [];
|
|
79
|
-
|
|
80
|
-
const scored: { skill: ExtractedSkill; score: number }[] = [];
|
|
81
|
-
|
|
82
|
-
for (const skill of library.skills) {
|
|
83
|
-
const searchable = [
|
|
84
|
-
skill.name,
|
|
85
|
-
skill.description,
|
|
86
|
-
skill.trigger,
|
|
87
|
-
...skill.toolsUsed,
|
|
88
|
-
...skill.filesInvolved,
|
|
89
|
-
...skill.steps,
|
|
90
|
-
]
|
|
91
|
-
.join(" ")
|
|
92
|
-
.toLowerCase();
|
|
93
|
-
|
|
94
|
-
let score = 0;
|
|
95
|
-
for (const term of terms) {
|
|
96
|
-
if (searchable.includes(term)) {
|
|
97
|
-
score++;
|
|
98
|
-
// Boost for name match
|
|
99
|
-
if (skill.name.toLowerCase().includes(term)) {
|
|
100
|
-
score += 2;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (score > 0) {
|
|
106
|
-
scored.push({ skill, score: score * skill.confidence });
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return scored
|
|
111
|
-
.sort((a, b) => b.score - a.score)
|
|
112
|
-
.map((s) => s.skill);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Export skills to Obsidian-compatible markdown files.
|
|
117
|
-
* Returns array of created file paths.
|
|
118
|
-
*/
|
|
119
|
-
export function exportToObsidian(library: SkillLibrary, vaultPath: string): string[] {
|
|
120
|
-
const skillsDir = join(vaultPath, "Skills");
|
|
121
|
-
if (!existsSync(skillsDir)) {
|
|
122
|
-
mkdirSync(skillsDir, { recursive: true });
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const createdFiles: string[] = [];
|
|
126
|
-
|
|
127
|
-
for (const skill of library.skills) {
|
|
128
|
-
const safeName = skill.name
|
|
129
|
-
.replace(/[<>:"/\\|?*]/g, "-")
|
|
130
|
-
.replace(/\s+/g, "-")
|
|
131
|
-
.slice(0, 80);
|
|
132
|
-
|
|
133
|
-
const fileName = `${safeName}.md`;
|
|
134
|
-
const filePath = join(skillsDir, fileName);
|
|
135
|
-
|
|
136
|
-
const toolsList = skill.toolsUsed.map((t) => `- ${t}`).join("\n");
|
|
137
|
-
const stepsList = skill.steps.map((s, i) => `${i + 1}. ${s}`).join("\n");
|
|
138
|
-
const filesList = skill.filesInvolved.map((f) => `- ${f}`).join("\n");
|
|
139
|
-
|
|
140
|
-
const content = `---
|
|
141
|
-
type: skill
|
|
142
|
-
name: "${skill.name.replace(/"/g, '\\"')}"
|
|
143
|
-
tools: [${skill.toolsUsed.map((t) => `"${t}"`).join(", ")}]
|
|
144
|
-
confidence: ${skill.confidence}
|
|
145
|
-
source_session: "${skill.sourceSession}"
|
|
146
|
-
tags: [claude/skill]
|
|
147
|
-
---
|
|
148
|
-
|
|
149
|
-
# Skill: ${skill.name}
|
|
150
|
-
|
|
151
|
-
## When to use
|
|
152
|
-
${skill.trigger}
|
|
153
|
-
|
|
154
|
-
## Steps
|
|
155
|
-
${stepsList}
|
|
156
|
-
|
|
157
|
-
## Tools Used
|
|
158
|
-
${toolsList}
|
|
159
|
-
|
|
160
|
-
## Files Typically Involved
|
|
161
|
-
${filesList || "- (none detected)"}
|
|
162
|
-
`;
|
|
163
|
-
|
|
164
|
-
writeFileSync(filePath, content, "utf-8");
|
|
165
|
-
createdFiles.push(filePath);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
return createdFiles;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Compute similarity between two skill names (0-1).
|
|
173
|
-
* Uses normalized token overlap (Jaccard-like).
|
|
174
|
-
*/
|
|
175
|
-
function nameSimilarity(a: string, b: string): number {
|
|
176
|
-
const tokensA = new Set(a.toLowerCase().split(/\s+/).filter((t) => t.length > 1));
|
|
177
|
-
const tokensB = new Set(b.toLowerCase().split(/\s+/).filter((t) => t.length > 1));
|
|
178
|
-
|
|
179
|
-
if (tokensA.size === 0 && tokensB.size === 0) return 1;
|
|
180
|
-
if (tokensA.size === 0 || tokensB.size === 0) return 0;
|
|
181
|
-
|
|
182
|
-
let intersection = 0;
|
|
183
|
-
for (const t of tokensA) {
|
|
184
|
-
if (tokensB.has(t)) intersection++;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
const union = new Set([...tokensA, ...tokensB]).size;
|
|
188
|
-
return intersection / union;
|
|
189
|
-
}
|