hcent 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/LICENSE +21 -0
- package/README.md +160 -0
- package/dist/agent/context-manager.d.ts +14 -0
- package/dist/agent/context-manager.js +109 -0
- package/dist/agent/context-manager.js.map +1 -0
- package/dist/agent/index.d.ts +27 -0
- package/dist/agent/index.js +120 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +40 -0
- package/dist/bin.js.map +1 -0
- package/dist/config/index.d.ts +25 -0
- package/dist/config/index.js +109 -0
- package/dist/config/index.js.map +1 -0
- package/dist/main.d.ts +1 -0
- package/dist/main.js +43 -0
- package/dist/main.js.map +1 -0
- package/dist/provider/index.d.ts +23 -0
- package/dist/provider/index.js +124 -0
- package/dist/provider/index.js.map +1 -0
- package/dist/provider/provider-manager.d.ts +9 -0
- package/dist/provider/provider-manager.js +43 -0
- package/dist/provider/provider-manager.js.map +1 -0
- package/dist/session/index.d.ts +39 -0
- package/dist/session/index.js +69 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/session-store.d.ts +19 -0
- package/dist/session/session-store.js +109 -0
- package/dist/session/session-store.js.map +1 -0
- package/dist/setup/index.d.ts +11 -0
- package/dist/setup/index.js +97 -0
- package/dist/setup/index.js.map +1 -0
- package/dist/skill/command-palette.d.ts +12 -0
- package/dist/skill/command-palette.js +62 -0
- package/dist/skill/command-palette.js.map +1 -0
- package/dist/skill/index.d.ts +15 -0
- package/dist/skill/index.js +145 -0
- package/dist/skill/index.js.map +1 -0
- package/dist/skill/skill-pointer.d.ts +18 -0
- package/dist/skill/skill-pointer.js +70 -0
- package/dist/skill/skill-pointer.js.map +1 -0
- package/dist/tools/index.d.ts +26 -0
- package/dist/tools/index.js +745 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/permission.d.ts +11 -0
- package/dist/tools/permission.js +11 -0
- package/dist/tools/permission.js.map +1 -0
- package/dist/tui/aligned-text.d.ts +14 -0
- package/dist/tui/aligned-text.js +46 -0
- package/dist/tui/aligned-text.js.map +1 -0
- package/dist/tui/builtin-commands.d.ts +40 -0
- package/dist/tui/builtin-commands.js +137 -0
- package/dist/tui/builtin-commands.js.map +1 -0
- package/dist/tui/editor-history.d.ts +6 -0
- package/dist/tui/editor-history.js +11 -0
- package/dist/tui/editor-history.js.map +1 -0
- package/dist/tui/editor-placeholder.d.ts +2 -0
- package/dist/tui/editor-placeholder.js +16 -0
- package/dist/tui/editor-placeholder.js.map +1 -0
- package/dist/tui/editor-with-placeholder.d.ts +8 -0
- package/dist/tui/editor-with-placeholder.js +49 -0
- package/dist/tui/editor-with-placeholder.js.map +1 -0
- package/dist/tui/hcent-autocomplete.d.ts +19 -0
- package/dist/tui/hcent-autocomplete.js +63 -0
- package/dist/tui/hcent-autocomplete.js.map +1 -0
- package/dist/tui/help-text.d.ts +1 -0
- package/dist/tui/help-text.js +15 -0
- package/dist/tui/help-text.js.map +1 -0
- package/dist/tui/index.d.ts +18 -0
- package/dist/tui/index.js +520 -0
- package/dist/tui/index.js.map +1 -0
- package/dist/tui/message-display.d.ts +8 -0
- package/dist/tui/message-display.js +41 -0
- package/dist/tui/message-display.js.map +1 -0
- package/dist/tui/pi-themes.d.ts +4 -0
- package/dist/tui/pi-themes.js +30 -0
- package/dist/tui/pi-themes.js.map +1 -0
- package/dist/tui/render-message.d.ts +3 -0
- package/dist/tui/render-message.js +32 -0
- package/dist/tui/render-message.js.map +1 -0
- package/dist/tui/thinking-loader.d.ts +17 -0
- package/dist/tui/thinking-loader.js +108 -0
- package/dist/tui/thinking-loader.js.map +1 -0
- package/dist/tui/user-message.d.ts +12 -0
- package/dist/tui/user-message.js +35 -0
- package/dist/tui/user-message.js.map +1 -0
- package/package.json +75 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const BUILTIN_COMMANDS = [
|
|
2
|
+
{ type: 'command', name: 'help', description: '显示帮助信息', keywords: ['help', '帮助'] },
|
|
3
|
+
{ type: 'command', name: 'clear', description: '清除当前会话', keywords: ['clear', '清除'] },
|
|
4
|
+
{ type: 'command', name: 'model', description: '列出或切换模型', keywords: ['model', '模型'] },
|
|
5
|
+
{ type: 'command', name: 'status', description: '显示当前状态', keywords: ['status', '状态'] },
|
|
6
|
+
{ type: 'command', name: 'save', description: '保存当前会话', keywords: ['save', '保存'] },
|
|
7
|
+
{ type: 'command', name: 'load', description: '加载已保存会话', keywords: ['load', '加载'] },
|
|
8
|
+
{ type: 'command', name: 'sessions', description: '列出已保存会话', keywords: ['sessions', '会话'] },
|
|
9
|
+
{ type: 'command', name: 'exit', description: '退出程序', keywords: ['exit', '退出'] },
|
|
10
|
+
];
|
|
11
|
+
function subsequenceMatch(query, target) {
|
|
12
|
+
const q = query.toLowerCase();
|
|
13
|
+
const t = target.toLowerCase();
|
|
14
|
+
let qi = 0;
|
|
15
|
+
let lastMatch = -1;
|
|
16
|
+
let compactness = 0;
|
|
17
|
+
for (let ti = 0; ti < t.length && qi < q.length; ti++) {
|
|
18
|
+
if (t[ti] === q[qi]) {
|
|
19
|
+
if (lastMatch >= 0)
|
|
20
|
+
compactness += ti - lastMatch;
|
|
21
|
+
lastMatch = ti;
|
|
22
|
+
qi++;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (qi < q.length)
|
|
26
|
+
return { matched: false, score: Infinity };
|
|
27
|
+
return { matched: true, score: compactness };
|
|
28
|
+
}
|
|
29
|
+
export function createCommandPalette(skills) {
|
|
30
|
+
return {
|
|
31
|
+
entries() {
|
|
32
|
+
const skillEntries = skills.list().map((s) => ({
|
|
33
|
+
type: 'skill',
|
|
34
|
+
name: s.name,
|
|
35
|
+
description: s.description,
|
|
36
|
+
keywords: [s.name, s.description, ...s.triggers],
|
|
37
|
+
}));
|
|
38
|
+
return [...BUILTIN_COMMANDS, ...skillEntries];
|
|
39
|
+
},
|
|
40
|
+
search(query) {
|
|
41
|
+
if (!query)
|
|
42
|
+
return this.entries();
|
|
43
|
+
const all = this.entries();
|
|
44
|
+
const results = [];
|
|
45
|
+
for (const entry of all) {
|
|
46
|
+
const fields = [entry.name, entry.description, ...entry.keywords];
|
|
47
|
+
let bestScore = Infinity;
|
|
48
|
+
for (const field of fields) {
|
|
49
|
+
const result = subsequenceMatch(query, field);
|
|
50
|
+
if (result.matched && result.score < bestScore) {
|
|
51
|
+
bestScore = result.score;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (bestScore < Infinity) {
|
|
55
|
+
results.push({ entry, score: bestScore });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return results.sort((a, b) => a.score - b.score).map((r) => r.entry);
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=command-palette.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-palette.js","sourceRoot":"","sources":["../../src/skill/command-palette.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface Skill {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
triggers: string[];
|
|
5
|
+
prompt: string;
|
|
6
|
+
filePath: string;
|
|
7
|
+
}
|
|
8
|
+
export interface SkillRegistry {
|
|
9
|
+
list(): Skill[];
|
|
10
|
+
get(name: string): Skill | undefined;
|
|
11
|
+
match(userInput: string): Skill | undefined;
|
|
12
|
+
fuzzySearch(query: string): Skill[];
|
|
13
|
+
loadFromDirectory(dir: string): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export declare function createSkillRegistry(): SkillRegistry;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { readFileSync, readdirSync, existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
function parseSkillFile(filePath) {
|
|
4
|
+
try {
|
|
5
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
6
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
|
|
7
|
+
if (!match)
|
|
8
|
+
return null;
|
|
9
|
+
const frontmatter = match[1];
|
|
10
|
+
const prompt = match[2].trim();
|
|
11
|
+
if (!prompt)
|
|
12
|
+
return null;
|
|
13
|
+
const name = extractField(frontmatter, 'name');
|
|
14
|
+
const description = extractField(frontmatter, 'description');
|
|
15
|
+
const triggersRaw = extractField(frontmatter, 'triggers');
|
|
16
|
+
if (!name || !description)
|
|
17
|
+
return null;
|
|
18
|
+
let triggers = [];
|
|
19
|
+
if (triggersRaw) {
|
|
20
|
+
const arrMatch = triggersRaw.match(/^\[(.*)\]$/);
|
|
21
|
+
if (arrMatch) {
|
|
22
|
+
triggers = arrMatch[1].split(',').map((s) => s.trim()).filter(Boolean);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
triggers = [triggersRaw];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return { name, description, triggers, prompt, filePath };
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function extractField(frontmatter, key) {
|
|
35
|
+
const regex = new RegExp(`^${key}:\\s*(.+)$`, 'm');
|
|
36
|
+
const match = frontmatter.match(regex);
|
|
37
|
+
return match ? match[1].trim() : null;
|
|
38
|
+
}
|
|
39
|
+
function subsequenceMatch(query, target) {
|
|
40
|
+
const q = query.toLowerCase();
|
|
41
|
+
const t = target.toLowerCase();
|
|
42
|
+
let qi = 0;
|
|
43
|
+
let lastMatch = -1;
|
|
44
|
+
let compactness = 0;
|
|
45
|
+
for (let ti = 0; ti < t.length && qi < q.length; ti++) {
|
|
46
|
+
if (t[ti] === q[qi]) {
|
|
47
|
+
if (lastMatch >= 0)
|
|
48
|
+
compactness += ti - lastMatch;
|
|
49
|
+
lastMatch = ti;
|
|
50
|
+
qi++;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (qi < q.length)
|
|
54
|
+
return { matched: false, score: Infinity };
|
|
55
|
+
return { matched: true, score: compactness };
|
|
56
|
+
}
|
|
57
|
+
function scoreTriggerMatch(input, trigger) {
|
|
58
|
+
const t = trigger.toLowerCase();
|
|
59
|
+
if (input === t)
|
|
60
|
+
return 3;
|
|
61
|
+
if (input.startsWith(t))
|
|
62
|
+
return 2;
|
|
63
|
+
if (input.includes(t))
|
|
64
|
+
return 1;
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
67
|
+
function isBetterSkillMatch(priority, triggerLen, bestPriority, bestTriggerLen) {
|
|
68
|
+
return priority > bestPriority || (priority === bestPriority && triggerLen > bestTriggerLen);
|
|
69
|
+
}
|
|
70
|
+
function findBestSkillMatch(skills, userInput) {
|
|
71
|
+
const input = userInput.trim().toLowerCase();
|
|
72
|
+
if (!input)
|
|
73
|
+
return undefined;
|
|
74
|
+
let best;
|
|
75
|
+
let bestPriority = -1;
|
|
76
|
+
for (const skill of skills.values()) {
|
|
77
|
+
for (const trigger of skill.triggers) {
|
|
78
|
+
const priority = scoreTriggerMatch(input, trigger);
|
|
79
|
+
if (priority === 0)
|
|
80
|
+
continue;
|
|
81
|
+
if (isBetterSkillMatch(priority, trigger.length, bestPriority, best?.triggers[0]?.length ?? 0)) {
|
|
82
|
+
bestPriority = priority;
|
|
83
|
+
best = skill;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return best;
|
|
88
|
+
}
|
|
89
|
+
export function createSkillRegistry() {
|
|
90
|
+
const skills = new Map();
|
|
91
|
+
return {
|
|
92
|
+
list() {
|
|
93
|
+
return [...skills.values()];
|
|
94
|
+
},
|
|
95
|
+
get(name) {
|
|
96
|
+
return skills.get(name);
|
|
97
|
+
},
|
|
98
|
+
match(userInput) {
|
|
99
|
+
return findBestSkillMatch(skills, userInput);
|
|
100
|
+
},
|
|
101
|
+
fuzzySearch(query) {
|
|
102
|
+
if (!query)
|
|
103
|
+
return [...skills.values()];
|
|
104
|
+
const results = [];
|
|
105
|
+
for (const skill of skills.values()) {
|
|
106
|
+
const fields = [skill.name, skill.description, ...skill.triggers];
|
|
107
|
+
let bestScore = Infinity;
|
|
108
|
+
for (const field of fields) {
|
|
109
|
+
const result = subsequenceMatch(query, field);
|
|
110
|
+
if (result.matched && result.score < bestScore) {
|
|
111
|
+
bestScore = result.score;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (bestScore < Infinity) {
|
|
115
|
+
results.push({ skill, score: bestScore });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return results.sort((a, b) => a.score - b.score).map((r) => r.skill);
|
|
119
|
+
},
|
|
120
|
+
async loadFromDirectory(dir) {
|
|
121
|
+
if (!existsSync(dir))
|
|
122
|
+
return;
|
|
123
|
+
// 方式1:子目录 skill(每个子文件夹下有 SKILL.md)
|
|
124
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
125
|
+
for (const entry of entries) {
|
|
126
|
+
if (entry.isDirectory()) {
|
|
127
|
+
const skillFile = join(dir, entry.name, 'SKILL.md');
|
|
128
|
+
const skill = parseSkillFile(skillFile);
|
|
129
|
+
if (skill) {
|
|
130
|
+
skills.set(skill.name, skill);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// 方式2:平铺 .md 文件(兼容旧格式)
|
|
135
|
+
const flatFiles = readdirSync(dir).filter((f) => f.endsWith('.md'));
|
|
136
|
+
for (const file of flatFiles) {
|
|
137
|
+
const skill = parseSkillFile(join(dir, file));
|
|
138
|
+
if (skill) {
|
|
139
|
+
skills.set(skill.name, skill);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/skill/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAkBjC,SAAS,cAAc,CAAC,QAAgB;IACtC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAChD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAC1E,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAExB,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QAC7B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/B,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAEzB,MAAM,IAAI,GAAG,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAC/C,MAAM,WAAW,GAAG,YAAY,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;QAC7D,MAAM,WAAW,GAAG,YAAY,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QAE1D,IAAI,CAAC,IAAI,IAAI,CAAC,WAAW;YAAE,OAAO,IAAI,CAAC;QAEvC,IAAI,QAAQ,GAAa,EAAE,CAAC;QAC5B,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YACjD,IAAI,QAAQ,EAAE,CAAC;gBACb,QAAQ,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACzE,CAAC;iBAAM,CAAC;gBACN,QAAQ,GAAG,CAAC,WAAW,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QAED,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,WAAmB,EAAE,GAAW;IACpD,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,GAAG,YAAY,EAAE,GAAG,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACvC,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACxC,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAa,EAAE,MAAc;IACrD,MAAM,CAAC,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAC9B,MAAM,CAAC,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IAC/B,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;IACnB,IAAI,WAAW,GAAG,CAAC,CAAC;IAEpB,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,MAAM,IAAI,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC;QACtD,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;YACpB,IAAI,SAAS,IAAI,CAAC;gBAAE,WAAW,IAAI,EAAE,GAAG,SAAS,CAAC;YAClD,SAAS,GAAG,EAAE,CAAC;YACf,EAAE,EAAE,CAAC;QACP,CAAC;IACH,CAAC;IAED,IAAI,EAAE,GAAG,CAAC,CAAC,MAAM;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IAC9D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;AAC/C,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAa,EAAE,OAAe;IACvD,MAAM,CAAC,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAChC,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC1B,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IAClC,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IAChC,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,kBAAkB,CACzB,QAAgB,EAChB,UAAkB,EAClB,YAAoB,EACpB,cAAsB;IAEtB,OAAO,QAAQ,GAAG,YAAY,IAAI,CAAC,QAAQ,KAAK,YAAY,IAAI,UAAU,GAAG,cAAc,CAAC,CAAC;AAC/F,CAAC;AAED,SAAS,kBAAkB,CAAC,MAA0B,EAAE,SAAiB;IACvE,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC7C,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAE7B,IAAI,IAAuB,CAAC;IAC5B,IAAI,YAAY,GAAG,CAAC,CAAC,CAAC;IAEtB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;QACpC,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACrC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YACnD,IAAI,QAAQ,KAAK,CAAC;gBAAE,SAAS;YAC7B,IAAI,kBAAkB,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,EAAE,CAAC;gBAC/F,YAAY,GAAG,QAAQ,CAAC;gBACxB,IAAI,GAAG,KAAK,CAAC;YACf,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAiB,CAAC;IAExC,OAAO;QACL,IAAI;YACF,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAC9B,CAAC;QAED,GAAG,CAAC,IAAY;YACd,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAED,KAAK,CAAC,SAAiB;YACrB,OAAO,kBAAkB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAC/C,CAAC;QAED,WAAW,CAAC,KAAa;YACvB,IAAI,CAAC,KAAK;gBAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;YAExC,MAAM,OAAO,GAAsC,EAAE,CAAC;YACtD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC;gBACpC,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;gBAClE,IAAI,SAAS,GAAG,QAAQ,CAAC;gBACzB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;oBAC3B,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;oBAC9C,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,GAAG,SAAS,EAAE,CAAC;wBAC/C,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC;oBAC3B,CAAC;gBACH,CAAC;gBACD,IAAI,SAAS,GAAG,QAAQ,EAAE,CAAC;oBACzB,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;gBAC5C,CAAC;YACH,CAAC;YAED,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACvE,CAAC;QAED,KAAK,CAAC,iBAAiB,CAAC,GAAW;YACjC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,OAAO;YAE7B,mCAAmC;YACnC,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;oBACxB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;oBACpD,MAAM,KAAK,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;oBACxC,IAAI,KAAK,EAAE,CAAC;wBACV,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;oBAChC,CAAC;gBACH,CAAC;YACH,CAAC;YAED,uBAAuB;YACvB,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;YACpE,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;gBAC7B,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;gBAC9C,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;gBAChC,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Skill } from './index.js';
|
|
2
|
+
/** 从用户输入中提取 `/skill-name` 引用(支持一行多个,含句读符后直接跟 /) */
|
|
3
|
+
export declare const SKILL_REF_RE: RegExp;
|
|
4
|
+
export interface ExtractedSkillRefs {
|
|
5
|
+
skills: Skill[];
|
|
6
|
+
unknown: string[];
|
|
7
|
+
}
|
|
8
|
+
export declare function buildSkillPointerContent(skill: Pick<Skill, 'name' | 'description' | 'filePath'>): string;
|
|
9
|
+
export declare function extractSkillReferences(text: string, getSkill: (name: string) => Skill | undefined): ExtractedSkillRefs;
|
|
10
|
+
/** 去掉 skill 引用后是否还有实质用户内容 */
|
|
11
|
+
export declare function hasUserContentBeyondSkills(text: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* 光标处正在输入的斜杠 token(如 `/k-feat` 或行中 `。/k`)。
|
|
14
|
+
* 取最后一个 `/` 到光标之间的片段,排除 `./`、`../` 等路径前缀。
|
|
15
|
+
*/
|
|
16
|
+
export declare function extractSlashTokenAtCursor(textBeforeCursor: string): string | null;
|
|
17
|
+
/** 刚输入 `/` 时是否应打开斜杠命令/ skill 补全 */
|
|
18
|
+
export declare function shouldTriggerSlashMenu(textBeforeCursor: string): boolean;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/** 从用户输入中提取 `/skill-name` 引用(支持一行多个,含句读符后直接跟 /) */
|
|
2
|
+
export const SKILL_REF_RE = /(?:^|[\s。,!?;:])\/([a-zA-Z0-9_-]+)/g;
|
|
3
|
+
export function buildSkillPointerContent(skill) {
|
|
4
|
+
return [
|
|
5
|
+
`用户引用了 skill **${skill.name}**。`,
|
|
6
|
+
`Skill 文件路径: ${skill.filePath}`,
|
|
7
|
+
`说明: ${skill.description}`,
|
|
8
|
+
'',
|
|
9
|
+
'请按需读取上述 SKILL.md(渐进式披露),仅在任务相关时加载内容,不要预加载全文。',
|
|
10
|
+
].join('\n');
|
|
11
|
+
}
|
|
12
|
+
export function extractSkillReferences(text, getSkill) {
|
|
13
|
+
const skills = [];
|
|
14
|
+
const unknown = [];
|
|
15
|
+
const seen = new Set();
|
|
16
|
+
for (const match of text.matchAll(SKILL_REF_RE)) {
|
|
17
|
+
const name = match[1];
|
|
18
|
+
if (seen.has(name))
|
|
19
|
+
continue;
|
|
20
|
+
seen.add(name);
|
|
21
|
+
const skill = getSkill(name);
|
|
22
|
+
if (skill) {
|
|
23
|
+
skills.push(skill);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
unknown.push(name);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return { skills, unknown };
|
|
30
|
+
}
|
|
31
|
+
/** 去掉 skill 引用后是否还有实质用户内容 */
|
|
32
|
+
export function hasUserContentBeyondSkills(text) {
|
|
33
|
+
return text.replace(SKILL_REF_RE, '').trim().length > 0;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 光标处正在输入的斜杠 token(如 `/k-feat` 或行中 `。/k`)。
|
|
37
|
+
* 取最后一个 `/` 到光标之间的片段,排除 `./`、`../` 等路径前缀。
|
|
38
|
+
*/
|
|
39
|
+
export function extractSlashTokenAtCursor(textBeforeCursor) {
|
|
40
|
+
const slashIdx = textBeforeCursor.lastIndexOf('/');
|
|
41
|
+
if (slashIdx === -1)
|
|
42
|
+
return null;
|
|
43
|
+
const token = textBeforeCursor.slice(slashIdx);
|
|
44
|
+
if (!token.startsWith('/'))
|
|
45
|
+
return null;
|
|
46
|
+
if (/\s/.test(token.slice(1)))
|
|
47
|
+
return null;
|
|
48
|
+
if (/^\.\//.test(token) || /^\.\.\//.test(token) || /^~\//.test(token))
|
|
49
|
+
return null;
|
|
50
|
+
const charBefore = slashIdx > 0 ? textBeforeCursor[slashIdx - 1] : '';
|
|
51
|
+
if (/[a-zA-Z0-9_]/.test(charBefore))
|
|
52
|
+
return null;
|
|
53
|
+
return token;
|
|
54
|
+
}
|
|
55
|
+
/** 刚输入 `/` 时是否应打开斜杠命令/ skill 补全 */
|
|
56
|
+
export function shouldTriggerSlashMenu(textBeforeCursor) {
|
|
57
|
+
if (!textBeforeCursor.endsWith('/'))
|
|
58
|
+
return false;
|
|
59
|
+
if (textBeforeCursor === '/')
|
|
60
|
+
return true;
|
|
61
|
+
const prev = textBeforeCursor[textBeforeCursor.length - 2];
|
|
62
|
+
if (prev === ' ' || prev === '\t')
|
|
63
|
+
return true;
|
|
64
|
+
// 中文或句读符后直接跟 skill 引用(如 `你好。/k`)
|
|
65
|
+
if (/[\u4e00-\u9fff\u3000-\u303f\uff00-\uffef。,!?;:]/.test(prev ?? ''))
|
|
66
|
+
return true;
|
|
67
|
+
// 行首仅空白后的 /
|
|
68
|
+
return textBeforeCursor.trimStart() === '/';
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=skill-pointer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-pointer.js","sourceRoot":"","sources":["../../src/skill/skill-pointer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface ToolSchema {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
parameters: object;
|
|
5
|
+
}
|
|
6
|
+
export type ToolPermission = 'auto' | 'confirm';
|
|
7
|
+
export interface ToolResult {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
output: string;
|
|
10
|
+
error?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ToolContext {
|
|
13
|
+
cwd: string;
|
|
14
|
+
signal: AbortSignal;
|
|
15
|
+
}
|
|
16
|
+
export interface Tool {
|
|
17
|
+
schema: ToolSchema;
|
|
18
|
+
permission: ToolPermission;
|
|
19
|
+
run(args: Record<string, unknown>, ctx: ToolContext): Promise<ToolResult>;
|
|
20
|
+
}
|
|
21
|
+
export interface ToolRegistry {
|
|
22
|
+
list(): Tool[];
|
|
23
|
+
schemas(): ToolSchema[];
|
|
24
|
+
get(name: string): Tool | undefined;
|
|
25
|
+
}
|
|
26
|
+
export declare function createToolRegistry(): ToolRegistry;
|