sigma-skills 0.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/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +31 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +176 -0
- package/dist/loader.js.map +1 -0
- package/dist/scanner.d.ts +21 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +169 -0
- package/dist/scanner.js.map +1 -0
- package/dist/types.d.ts +20 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +23 -0
- package/src/index.ts +3 -0
- package/src/loader.ts +166 -0
- package/src/scanner.ts +154 -0
- package/src/types.ts +21 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,cAAc,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.SkillLoader = exports.SkillScanner = void 0;
|
|
18
|
+
var scanner_js_1 = require("./scanner.js");
|
|
19
|
+
Object.defineProperty(exports, "SkillScanner", { enumerable: true, get: function () { return scanner_js_1.SkillScanner; } });
|
|
20
|
+
var loader_js_1 = require("./loader.js");
|
|
21
|
+
Object.defineProperty(exports, "SkillLoader", { enumerable: true, get: function () { return loader_js_1.SkillLoader; } });
|
|
22
|
+
__exportStar(require("./types.js"), exports);
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,2CAA4C;AAAnC,0GAAA,YAAY,OAAA;AACrB,yCAA0C;AAAjC,wGAAA,WAAW,OAAA;AACpB,6CAA2B"}
|
package/dist/loader.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Skill, SkillMatch } from './types.js';
|
|
2
|
+
import { SkillScanner } from './scanner.js';
|
|
3
|
+
export declare class SkillLoader {
|
|
4
|
+
private scanner;
|
|
5
|
+
private skills;
|
|
6
|
+
private lastScanTime;
|
|
7
|
+
private readonly SCAN_CACHE_MS;
|
|
8
|
+
constructor(scanner: SkillScanner);
|
|
9
|
+
/**
|
|
10
|
+
* Cherche les skills pertinents pour un prompt donné
|
|
11
|
+
*/
|
|
12
|
+
findRelevantSkills(prompt: string): SkillMatch[];
|
|
13
|
+
/**
|
|
14
|
+
* Retourne le contenu d'un skill par nom
|
|
15
|
+
*/
|
|
16
|
+
getSkillContext(skillName: string): string | null;
|
|
17
|
+
/**
|
|
18
|
+
* Liste tous les skills installés
|
|
19
|
+
*/
|
|
20
|
+
listSkills(): Skill[];
|
|
21
|
+
/**
|
|
22
|
+
* Copie un skill d'un chemin source vers le répertoire skills
|
|
23
|
+
*/
|
|
24
|
+
installSkill(source: string, targetDir?: string): boolean;
|
|
25
|
+
private refreshSkillsIfNeeded;
|
|
26
|
+
private refreshSkills;
|
|
27
|
+
private forceRefresh;
|
|
28
|
+
private extractWords;
|
|
29
|
+
private copyDirectory;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,UAAU,EAAgB,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,qBAAa,WAAW;IACtB,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAE3B,OAAO,EAAE,YAAY;IAKjC;;OAEG;IACH,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,EAAE;IA6ChD;;OAEG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAOjD;;OAEG;IACH,UAAU,IAAI,KAAK,EAAE;IAKrB;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO;IAqCzD,OAAO,CAAC,qBAAqB;IAO7B,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,aAAa;CAkBtB"}
|
package/dist/loader.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.SkillLoader = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
class SkillLoader {
|
|
40
|
+
scanner;
|
|
41
|
+
skills = [];
|
|
42
|
+
lastScanTime = 0;
|
|
43
|
+
SCAN_CACHE_MS = 60000; // Cache les scans pendant 1 minute
|
|
44
|
+
constructor(scanner) {
|
|
45
|
+
this.scanner = scanner;
|
|
46
|
+
this.refreshSkills();
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Cherche les skills pertinents pour un prompt donné
|
|
50
|
+
*/
|
|
51
|
+
findRelevantSkills(prompt) {
|
|
52
|
+
this.refreshSkillsIfNeeded();
|
|
53
|
+
const promptWords = this.extractWords(prompt.toLowerCase());
|
|
54
|
+
const matches = [];
|
|
55
|
+
for (const skill of this.skills) {
|
|
56
|
+
const matchedKeywords = [];
|
|
57
|
+
let score = 0;
|
|
58
|
+
// Vérifier les correspondances avec les keywords du skill
|
|
59
|
+
for (const keyword of skill.keywords) {
|
|
60
|
+
if (promptWords.includes(keyword)) {
|
|
61
|
+
matchedKeywords.push(keyword);
|
|
62
|
+
score += 1;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Bonus pour les correspondances dans le nom ou la description
|
|
66
|
+
const skillName = skill.name.toLowerCase();
|
|
67
|
+
const skillDesc = skill.description.toLowerCase();
|
|
68
|
+
for (const word of promptWords) {
|
|
69
|
+
if (skillName.includes(word)) {
|
|
70
|
+
score += 2; // Bonus pour le nom
|
|
71
|
+
}
|
|
72
|
+
if (skillDesc.includes(word)) {
|
|
73
|
+
score += 1; // Bonus pour la description
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Ajouter si pertinent
|
|
77
|
+
if (score > 0) {
|
|
78
|
+
matches.push({
|
|
79
|
+
skill,
|
|
80
|
+
matchedKeywords,
|
|
81
|
+
score
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Trier par score décroissant
|
|
86
|
+
return matches.sort((a, b) => b.score - a.score);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Retourne le contenu d'un skill par nom
|
|
90
|
+
*/
|
|
91
|
+
getSkillContext(skillName) {
|
|
92
|
+
this.refreshSkillsIfNeeded();
|
|
93
|
+
const skill = this.skills.find(s => s.name === skillName);
|
|
94
|
+
return skill ? skill.content : null;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Liste tous les skills installés
|
|
98
|
+
*/
|
|
99
|
+
listSkills() {
|
|
100
|
+
this.refreshSkillsIfNeeded();
|
|
101
|
+
return [...this.skills]; // Retourner une copie
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Copie un skill d'un chemin source vers le répertoire skills
|
|
105
|
+
*/
|
|
106
|
+
installSkill(source, targetDir) {
|
|
107
|
+
try {
|
|
108
|
+
// Déterminer le répertoire cible (par défaut: globalDir)
|
|
109
|
+
const targetBase = targetDir || this.scanner['config'].globalDir;
|
|
110
|
+
const skillName = path.basename(source);
|
|
111
|
+
const targetPath = path.join(targetBase, skillName);
|
|
112
|
+
// Vérifier que le source existe
|
|
113
|
+
if (!fs.existsSync(source)) {
|
|
114
|
+
console.error(`Source skill directory not found: ${source}`);
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
// Vérifier qu'il y a un SKILL.md dans le source
|
|
118
|
+
const sourceSkillMd = path.join(source, 'SKILL.md');
|
|
119
|
+
if (!fs.existsSync(sourceSkillMd)) {
|
|
120
|
+
console.error(`No SKILL.md found in source: ${source}`);
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
// Créer le répertoire cible si nécessaire
|
|
124
|
+
fs.mkdirSync(targetBase, { recursive: true });
|
|
125
|
+
// Copier récursivement
|
|
126
|
+
this.copyDirectory(source, targetPath);
|
|
127
|
+
// Rafraîchir la liste des skills
|
|
128
|
+
this.forceRefresh();
|
|
129
|
+
console.log(`Skill '${skillName}' installed successfully to ${targetPath}`);
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
console.error(`Failed to install skill from ${source}:`, error);
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
refreshSkillsIfNeeded() {
|
|
138
|
+
const now = Date.now();
|
|
139
|
+
if (now - this.lastScanTime > this.SCAN_CACHE_MS) {
|
|
140
|
+
this.refreshSkills();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
refreshSkills() {
|
|
144
|
+
this.skills = this.scanner.scan();
|
|
145
|
+
this.lastScanTime = Date.now();
|
|
146
|
+
}
|
|
147
|
+
forceRefresh() {
|
|
148
|
+
this.lastScanTime = 0;
|
|
149
|
+
this.refreshSkills();
|
|
150
|
+
}
|
|
151
|
+
extractWords(text) {
|
|
152
|
+
return text
|
|
153
|
+
.replace(/[^\w\s]/g, ' ')
|
|
154
|
+
.split(/\s+/)
|
|
155
|
+
.filter(word => word.length > 2)
|
|
156
|
+
.map(word => word.toLowerCase());
|
|
157
|
+
}
|
|
158
|
+
copyDirectory(source, target) {
|
|
159
|
+
if (!fs.existsSync(target)) {
|
|
160
|
+
fs.mkdirSync(target, { recursive: true });
|
|
161
|
+
}
|
|
162
|
+
const entries = fs.readdirSync(source, { withFileTypes: true });
|
|
163
|
+
for (const entry of entries) {
|
|
164
|
+
const sourcePath = path.join(source, entry.name);
|
|
165
|
+
const targetPath = path.join(target, entry.name);
|
|
166
|
+
if (entry.isDirectory()) {
|
|
167
|
+
this.copyDirectory(sourcePath, targetPath);
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
exports.SkillLoader = SkillLoader;
|
|
176
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Skill, SkillsConfig } from './types.js';
|
|
2
|
+
export declare class SkillScanner {
|
|
3
|
+
private config;
|
|
4
|
+
constructor(config: SkillsConfig);
|
|
5
|
+
/**
|
|
6
|
+
* Scanne tous les répertoires et charge les skills
|
|
7
|
+
*/
|
|
8
|
+
scan(): Skill[];
|
|
9
|
+
/**
|
|
10
|
+
* Charge un skill depuis un dossier (lit SKILL.md, extrait keywords des headers/bullet points)
|
|
11
|
+
*/
|
|
12
|
+
loadSkill(dir: string): Skill | null;
|
|
13
|
+
/**
|
|
14
|
+
* Extrait les mots-clés d'un SKILL.md (headers h1/h2, mots après "When to use", termes techniques)
|
|
15
|
+
*/
|
|
16
|
+
extractKeywords(content: string): string[];
|
|
17
|
+
private addWordsToKeywords;
|
|
18
|
+
private isStopWord;
|
|
19
|
+
private getSkillFiles;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEjD,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAe;gBAEjB,MAAM,EAAE,YAAY;IAIhC;;OAEG;IACH,IAAI,IAAI,KAAK,EAAE;IA6Bf;;OAEG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI;IA+CpC;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IA4B1C,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,aAAa;CAStB"}
|
package/dist/scanner.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.SkillScanner = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
class SkillScanner {
|
|
40
|
+
config;
|
|
41
|
+
constructor(config) {
|
|
42
|
+
this.config = config;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Scanne tous les répertoires et charge les skills
|
|
46
|
+
*/
|
|
47
|
+
scan() {
|
|
48
|
+
const skills = [];
|
|
49
|
+
// Scanner les trois répertoires possibles
|
|
50
|
+
const dirs = [
|
|
51
|
+
this.config.globalDir,
|
|
52
|
+
this.config.projectDir,
|
|
53
|
+
this.config.bundledDir
|
|
54
|
+
];
|
|
55
|
+
for (const dir of dirs) {
|
|
56
|
+
if (!fs.existsSync(dir))
|
|
57
|
+
continue;
|
|
58
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
59
|
+
for (const entry of entries) {
|
|
60
|
+
if (entry.isDirectory()) {
|
|
61
|
+
const skillPath = path.join(dir, entry.name);
|
|
62
|
+
const skill = this.loadSkill(skillPath);
|
|
63
|
+
if (skill) {
|
|
64
|
+
skills.push(skill);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return skills;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Charge un skill depuis un dossier (lit SKILL.md, extrait keywords des headers/bullet points)
|
|
73
|
+
*/
|
|
74
|
+
loadSkill(dir) {
|
|
75
|
+
const skillMdPath = path.join(dir, 'SKILL.md');
|
|
76
|
+
if (!fs.existsSync(skillMdPath)) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const content = fs.readFileSync(skillMdPath, 'utf-8');
|
|
81
|
+
const name = path.basename(dir);
|
|
82
|
+
// Extraire la description du premier paragraphe après le titre
|
|
83
|
+
const lines = content.split('\n');
|
|
84
|
+
let description = '';
|
|
85
|
+
let foundHeader = false;
|
|
86
|
+
for (const line of lines) {
|
|
87
|
+
if (line.startsWith('#')) {
|
|
88
|
+
foundHeader = true;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (foundHeader && line.trim()) {
|
|
92
|
+
description = line.trim();
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Extraire les mots-clés
|
|
97
|
+
const keywords = this.extractKeywords(content);
|
|
98
|
+
// Lister les fichiers du dossier
|
|
99
|
+
const files = this.getSkillFiles(dir);
|
|
100
|
+
return {
|
|
101
|
+
name,
|
|
102
|
+
description: description || `Skill: ${name}`,
|
|
103
|
+
content,
|
|
104
|
+
path: dir,
|
|
105
|
+
keywords,
|
|
106
|
+
files
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
console.warn(`Failed to load skill from ${dir}:`, error);
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Extrait les mots-clés d'un SKILL.md (headers h1/h2, mots après "When to use", termes techniques)
|
|
116
|
+
*/
|
|
117
|
+
extractKeywords(content) {
|
|
118
|
+
const keywords = new Set();
|
|
119
|
+
const lines = content.split('\n');
|
|
120
|
+
for (const line of lines) {
|
|
121
|
+
// Headers H1/H2
|
|
122
|
+
if (line.startsWith('#')) {
|
|
123
|
+
const headerText = line.replace(/^#+\s*/, '').toLowerCase();
|
|
124
|
+
this.addWordsToKeywords(headerText, keywords);
|
|
125
|
+
}
|
|
126
|
+
// Lignes contenant "when to use", "use when", etc.
|
|
127
|
+
if (line.toLowerCase().includes('when to use') ||
|
|
128
|
+
line.toLowerCase().includes('use when') ||
|
|
129
|
+
line.toLowerCase().includes('trigger on')) {
|
|
130
|
+
this.addWordsToKeywords(line, keywords);
|
|
131
|
+
}
|
|
132
|
+
// Listes à puces (bullet points)
|
|
133
|
+
if (line.trim().startsWith('-') || line.trim().startsWith('*')) {
|
|
134
|
+
const bulletText = line.replace(/^\s*[-*]\s*/, '');
|
|
135
|
+
this.addWordsToKeywords(bulletText, keywords);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return Array.from(keywords).filter(k => k.length > 2); // Filtrer les mots trop courts
|
|
139
|
+
}
|
|
140
|
+
addWordsToKeywords(text, keywords) {
|
|
141
|
+
// Nettoyer et extraire les mots
|
|
142
|
+
const words = text
|
|
143
|
+
.toLowerCase()
|
|
144
|
+
.replace(/[^\w\s]/g, ' ')
|
|
145
|
+
.split(/\s+/)
|
|
146
|
+
.filter(word => word.length > 2 && !this.isStopWord(word));
|
|
147
|
+
for (const word of words) {
|
|
148
|
+
keywords.add(word);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
isStopWord(word) {
|
|
152
|
+
const stopWords = new Set([
|
|
153
|
+
'the', 'and', 'for', 'are', 'but', 'not', 'you', 'all', 'can', 'had', 'her', 'was', 'one', 'our', 'out', 'day', 'get', 'has', 'him', 'his', 'how', 'its', 'may', 'new', 'now', 'old', 'see', 'two', 'way', 'who', 'boy', 'did', 'she', 'use', 'her', 'how', 'man', 'new', 'now', 'old', 'see', 'two', 'way', 'who', 'oil', 'sit', 'set'
|
|
154
|
+
]);
|
|
155
|
+
return stopWords.has(word);
|
|
156
|
+
}
|
|
157
|
+
getSkillFiles(dir) {
|
|
158
|
+
try {
|
|
159
|
+
return fs.readdirSync(dir, { withFileTypes: true })
|
|
160
|
+
.map(entry => entry.name)
|
|
161
|
+
.filter(name => !name.startsWith('.'));
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
return [];
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
exports.SkillScanner = SkillScanner;
|
|
169
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface Skill {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
content: string;
|
|
5
|
+
path: string;
|
|
6
|
+
keywords: string[];
|
|
7
|
+
files: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface SkillMatch {
|
|
10
|
+
skill: Skill;
|
|
11
|
+
matchedKeywords: string[];
|
|
12
|
+
score: number;
|
|
13
|
+
}
|
|
14
|
+
export interface SkillsConfig {
|
|
15
|
+
globalDir: string;
|
|
16
|
+
projectDir: string;
|
|
17
|
+
bundledDir: string;
|
|
18
|
+
autoInject: boolean;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,KAAK,CAAC;IACb,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;CACrB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sigma-skills",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Dynamic skill loading system for Phi Code",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"clean": "rm -rf dist"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"typescript": "^5.4.0"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/uglyswap/phi-code.git"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/uglyswap/phi-code",
|
|
20
|
+
"keywords": ["skills", "dynamic-loading", "typescript", "phi-code"],
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"files": ["dist", "src", "README.md"]
|
|
23
|
+
}
|
package/src/index.ts
ADDED
package/src/loader.ts
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { Skill, SkillMatch, SkillsConfig } from './types.js';
|
|
4
|
+
import { SkillScanner } from './scanner.js';
|
|
5
|
+
|
|
6
|
+
export class SkillLoader {
|
|
7
|
+
private scanner: SkillScanner;
|
|
8
|
+
private skills: Skill[] = [];
|
|
9
|
+
private lastScanTime: number = 0;
|
|
10
|
+
private readonly SCAN_CACHE_MS = 60000; // Cache les scans pendant 1 minute
|
|
11
|
+
|
|
12
|
+
constructor(scanner: SkillScanner) {
|
|
13
|
+
this.scanner = scanner;
|
|
14
|
+
this.refreshSkills();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Cherche les skills pertinents pour un prompt donné
|
|
19
|
+
*/
|
|
20
|
+
findRelevantSkills(prompt: string): SkillMatch[] {
|
|
21
|
+
this.refreshSkillsIfNeeded();
|
|
22
|
+
|
|
23
|
+
const promptWords = this.extractWords(prompt.toLowerCase());
|
|
24
|
+
const matches: SkillMatch[] = [];
|
|
25
|
+
|
|
26
|
+
for (const skill of this.skills) {
|
|
27
|
+
const matchedKeywords: string[] = [];
|
|
28
|
+
let score = 0;
|
|
29
|
+
|
|
30
|
+
// Vérifier les correspondances avec les keywords du skill
|
|
31
|
+
for (const keyword of skill.keywords) {
|
|
32
|
+
if (promptWords.includes(keyword)) {
|
|
33
|
+
matchedKeywords.push(keyword);
|
|
34
|
+
score += 1;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Bonus pour les correspondances dans le nom ou la description
|
|
39
|
+
const skillName = skill.name.toLowerCase();
|
|
40
|
+
const skillDesc = skill.description.toLowerCase();
|
|
41
|
+
|
|
42
|
+
for (const word of promptWords) {
|
|
43
|
+
if (skillName.includes(word)) {
|
|
44
|
+
score += 2; // Bonus pour le nom
|
|
45
|
+
}
|
|
46
|
+
if (skillDesc.includes(word)) {
|
|
47
|
+
score += 1; // Bonus pour la description
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Ajouter si pertinent
|
|
52
|
+
if (score > 0) {
|
|
53
|
+
matches.push({
|
|
54
|
+
skill,
|
|
55
|
+
matchedKeywords,
|
|
56
|
+
score
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Trier par score décroissant
|
|
62
|
+
return matches.sort((a, b) => b.score - a.score);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Retourne le contenu d'un skill par nom
|
|
67
|
+
*/
|
|
68
|
+
getSkillContext(skillName: string): string | null {
|
|
69
|
+
this.refreshSkillsIfNeeded();
|
|
70
|
+
|
|
71
|
+
const skill = this.skills.find(s => s.name === skillName);
|
|
72
|
+
return skill ? skill.content : null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Liste tous les skills installés
|
|
77
|
+
*/
|
|
78
|
+
listSkills(): Skill[] {
|
|
79
|
+
this.refreshSkillsIfNeeded();
|
|
80
|
+
return [...this.skills]; // Retourner une copie
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Copie un skill d'un chemin source vers le répertoire skills
|
|
85
|
+
*/
|
|
86
|
+
installSkill(source: string, targetDir?: string): boolean {
|
|
87
|
+
try {
|
|
88
|
+
// Déterminer le répertoire cible (par défaut: globalDir)
|
|
89
|
+
const targetBase = targetDir || this.scanner['config'].globalDir;
|
|
90
|
+
const skillName = path.basename(source);
|
|
91
|
+
const targetPath = path.join(targetBase, skillName);
|
|
92
|
+
|
|
93
|
+
// Vérifier que le source existe
|
|
94
|
+
if (!fs.existsSync(source)) {
|
|
95
|
+
console.error(`Source skill directory not found: ${source}`);
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Vérifier qu'il y a un SKILL.md dans le source
|
|
100
|
+
const sourceSkillMd = path.join(source, 'SKILL.md');
|
|
101
|
+
if (!fs.existsSync(sourceSkillMd)) {
|
|
102
|
+
console.error(`No SKILL.md found in source: ${source}`);
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Créer le répertoire cible si nécessaire
|
|
107
|
+
fs.mkdirSync(targetBase, { recursive: true });
|
|
108
|
+
|
|
109
|
+
// Copier récursivement
|
|
110
|
+
this.copyDirectory(source, targetPath);
|
|
111
|
+
|
|
112
|
+
// Rafraîchir la liste des skills
|
|
113
|
+
this.forceRefresh();
|
|
114
|
+
|
|
115
|
+
console.log(`Skill '${skillName}' installed successfully to ${targetPath}`);
|
|
116
|
+
return true;
|
|
117
|
+
} catch (error) {
|
|
118
|
+
console.error(`Failed to install skill from ${source}:`, error);
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private refreshSkillsIfNeeded() {
|
|
124
|
+
const now = Date.now();
|
|
125
|
+
if (now - this.lastScanTime > this.SCAN_CACHE_MS) {
|
|
126
|
+
this.refreshSkills();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private refreshSkills() {
|
|
131
|
+
this.skills = this.scanner.scan();
|
|
132
|
+
this.lastScanTime = Date.now();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private forceRefresh() {
|
|
136
|
+
this.lastScanTime = 0;
|
|
137
|
+
this.refreshSkills();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private extractWords(text: string): string[] {
|
|
141
|
+
return text
|
|
142
|
+
.replace(/[^\w\s]/g, ' ')
|
|
143
|
+
.split(/\s+/)
|
|
144
|
+
.filter(word => word.length > 2)
|
|
145
|
+
.map(word => word.toLowerCase());
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private copyDirectory(source: string, target: string) {
|
|
149
|
+
if (!fs.existsSync(target)) {
|
|
150
|
+
fs.mkdirSync(target, { recursive: true });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const entries = fs.readdirSync(source, { withFileTypes: true });
|
|
154
|
+
|
|
155
|
+
for (const entry of entries) {
|
|
156
|
+
const sourcePath = path.join(source, entry.name);
|
|
157
|
+
const targetPath = path.join(target, entry.name);
|
|
158
|
+
|
|
159
|
+
if (entry.isDirectory()) {
|
|
160
|
+
this.copyDirectory(sourcePath, targetPath);
|
|
161
|
+
} else {
|
|
162
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
package/src/scanner.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { Skill, SkillsConfig } from './types.js';
|
|
4
|
+
|
|
5
|
+
export class SkillScanner {
|
|
6
|
+
private config: SkillsConfig;
|
|
7
|
+
|
|
8
|
+
constructor(config: SkillsConfig) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Scanne tous les répertoires et charge les skills
|
|
14
|
+
*/
|
|
15
|
+
scan(): Skill[] {
|
|
16
|
+
const skills: Skill[] = [];
|
|
17
|
+
|
|
18
|
+
// Scanner les trois répertoires possibles
|
|
19
|
+
const dirs = [
|
|
20
|
+
this.config.globalDir,
|
|
21
|
+
this.config.projectDir,
|
|
22
|
+
this.config.bundledDir
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
for (const dir of dirs) {
|
|
26
|
+
if (!fs.existsSync(dir)) continue;
|
|
27
|
+
|
|
28
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
29
|
+
|
|
30
|
+
for (const entry of entries) {
|
|
31
|
+
if (entry.isDirectory()) {
|
|
32
|
+
const skillPath = path.join(dir, entry.name);
|
|
33
|
+
const skill = this.loadSkill(skillPath);
|
|
34
|
+
if (skill) {
|
|
35
|
+
skills.push(skill);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return skills;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Charge un skill depuis un dossier (lit SKILL.md, extrait keywords des headers/bullet points)
|
|
46
|
+
*/
|
|
47
|
+
loadSkill(dir: string): Skill | null {
|
|
48
|
+
const skillMdPath = path.join(dir, 'SKILL.md');
|
|
49
|
+
|
|
50
|
+
if (!fs.existsSync(skillMdPath)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const content = fs.readFileSync(skillMdPath, 'utf-8');
|
|
56
|
+
const name = path.basename(dir);
|
|
57
|
+
|
|
58
|
+
// Extraire la description du premier paragraphe après le titre
|
|
59
|
+
const lines = content.split('\n');
|
|
60
|
+
let description = '';
|
|
61
|
+
let foundHeader = false;
|
|
62
|
+
|
|
63
|
+
for (const line of lines) {
|
|
64
|
+
if (line.startsWith('#')) {
|
|
65
|
+
foundHeader = true;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (foundHeader && line.trim()) {
|
|
69
|
+
description = line.trim();
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Extraire les mots-clés
|
|
75
|
+
const keywords = this.extractKeywords(content);
|
|
76
|
+
|
|
77
|
+
// Lister les fichiers du dossier
|
|
78
|
+
const files = this.getSkillFiles(dir);
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
name,
|
|
82
|
+
description: description || `Skill: ${name}`,
|
|
83
|
+
content,
|
|
84
|
+
path: dir,
|
|
85
|
+
keywords,
|
|
86
|
+
files
|
|
87
|
+
};
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.warn(`Failed to load skill from ${dir}:`, error);
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Extrait les mots-clés d'un SKILL.md (headers h1/h2, mots après "When to use", termes techniques)
|
|
96
|
+
*/
|
|
97
|
+
extractKeywords(content: string): string[] {
|
|
98
|
+
const keywords = new Set<string>();
|
|
99
|
+
const lines = content.split('\n');
|
|
100
|
+
|
|
101
|
+
for (const line of lines) {
|
|
102
|
+
// Headers H1/H2
|
|
103
|
+
if (line.startsWith('#')) {
|
|
104
|
+
const headerText = line.replace(/^#+\s*/, '').toLowerCase();
|
|
105
|
+
this.addWordsToKeywords(headerText, keywords);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Lignes contenant "when to use", "use when", etc.
|
|
109
|
+
if (line.toLowerCase().includes('when to use') ||
|
|
110
|
+
line.toLowerCase().includes('use when') ||
|
|
111
|
+
line.toLowerCase().includes('trigger on')) {
|
|
112
|
+
this.addWordsToKeywords(line, keywords);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Listes à puces (bullet points)
|
|
116
|
+
if (line.trim().startsWith('-') || line.trim().startsWith('*')) {
|
|
117
|
+
const bulletText = line.replace(/^\s*[-*]\s*/, '');
|
|
118
|
+
this.addWordsToKeywords(bulletText, keywords);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return Array.from(keywords).filter(k => k.length > 2); // Filtrer les mots trop courts
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private addWordsToKeywords(text: string, keywords: Set<string>) {
|
|
126
|
+
// Nettoyer et extraire les mots
|
|
127
|
+
const words = text
|
|
128
|
+
.toLowerCase()
|
|
129
|
+
.replace(/[^\w\s]/g, ' ')
|
|
130
|
+
.split(/\s+/)
|
|
131
|
+
.filter(word => word.length > 2 && !this.isStopWord(word));
|
|
132
|
+
|
|
133
|
+
for (const word of words) {
|
|
134
|
+
keywords.add(word);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private isStopWord(word: string): boolean {
|
|
139
|
+
const stopWords = new Set([
|
|
140
|
+
'the', 'and', 'for', 'are', 'but', 'not', 'you', 'all', 'can', 'had', 'her', 'was', 'one', 'our', 'out', 'day', 'get', 'has', 'him', 'his', 'how', 'its', 'may', 'new', 'now', 'old', 'see', 'two', 'way', 'who', 'boy', 'did', 'she', 'use', 'her', 'how', 'man', 'new', 'now', 'old', 'see', 'two', 'way', 'who', 'oil', 'sit', 'set'
|
|
141
|
+
]);
|
|
142
|
+
return stopWords.has(word);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private getSkillFiles(dir: string): string[] {
|
|
146
|
+
try {
|
|
147
|
+
return fs.readdirSync(dir, { withFileTypes: true })
|
|
148
|
+
.map(entry => entry.name)
|
|
149
|
+
.filter(name => !name.startsWith('.'));
|
|
150
|
+
} catch {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface Skill {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
content: string; // Contenu du SKILL.md
|
|
5
|
+
path: string; // Chemin absolu
|
|
6
|
+
keywords: string[]; // Mots-clés extraits du SKILL.md
|
|
7
|
+
files: string[]; // Fichiers dans le dossier du skill
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface SkillMatch {
|
|
11
|
+
skill: Skill;
|
|
12
|
+
matchedKeywords: string[];
|
|
13
|
+
score: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SkillsConfig {
|
|
17
|
+
globalDir: string; // ~/.phi/agent/skills/
|
|
18
|
+
projectDir: string; // .phi/skills/
|
|
19
|
+
bundledDir: string; // Chemin vers skills/ dans le repo
|
|
20
|
+
autoInject: boolean; // Injecter auto le contexte des skills matchés
|
|
21
|
+
}
|