toolpack-cli 0.1.0-SNAPSHOT
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 +201 -0
- package/README.md +131 -0
- package/dist/app.d.ts +1 -0
- package/dist/app.js +15 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +29 -0
- package/dist/commands/clear.d.ts +3 -0
- package/dist/commands/clear.js +15 -0
- package/dist/commands/help.d.ts +3 -0
- package/dist/commands/help.js +29 -0
- package/dist/commands/index.d.ts +15 -0
- package/dist/commands/index.js +16 -0
- package/dist/commands/info.d.ts +3 -0
- package/dist/commands/info.js +24 -0
- package/dist/commands/mode.d.ts +3 -0
- package/dist/commands/mode.js +51 -0
- package/dist/commands/model.d.ts +3 -0
- package/dist/commands/model.js +14 -0
- package/dist/commands/registry.d.ts +32 -0
- package/dist/commands/registry.js +86 -0
- package/dist/commands/tool-log.d.ts +3 -0
- package/dist/commands/tool-log.js +17 -0
- package/dist/commands/tool-search.d.ts +3 -0
- package/dist/commands/tool-search.js +57 -0
- package/dist/commands/tools.d.ts +3 -0
- package/dist/commands/tools.js +45 -0
- package/dist/commands/types.d.ts +25 -0
- package/dist/commands/types.js +4 -0
- package/dist/commands/version.d.ts +3 -0
- package/dist/commands/version.js +25 -0
- package/dist/components/AppInfo.d.ts +1 -0
- package/dist/components/AppInfo.js +10 -0
- package/dist/components/HomeInput.d.ts +11 -0
- package/dist/components/HomeInput.js +328 -0
- package/dist/components/Logo.d.ts +1 -0
- package/dist/components/Logo.js +15 -0
- package/dist/components/Markdown.d.ts +5 -0
- package/dist/components/Markdown.js +121 -0
- package/dist/components/ProviderBar.d.ts +12 -0
- package/dist/components/ProviderBar.js +32 -0
- package/dist/components/ShimmerText.d.ts +8 -0
- package/dist/components/ShimmerText.js +20 -0
- package/dist/components/ToolLogPopup.d.ts +7 -0
- package/dist/components/ToolLogPopup.js +87 -0
- package/dist/components/common/HistorySelect.d.ts +6 -0
- package/dist/components/common/HistorySelect.js +57 -0
- package/dist/components/common/Modal.d.ts +10 -0
- package/dist/components/common/Modal.js +13 -0
- package/dist/components/common/ModeSelect.d.ts +6 -0
- package/dist/components/common/ModeSelect.js +13 -0
- package/dist/components/common/ModelSelect.d.ts +9 -0
- package/dist/components/common/ModelSelect.js +45 -0
- package/dist/context/ConversationContext.d.ts +44 -0
- package/dist/context/ConversationContext.js +113 -0
- package/dist/context/ToolpackContext.d.ts +55 -0
- package/dist/context/ToolpackContext.js +221 -0
- package/dist/custom-providers/AnthropicCustomAdapter.d.ts +49 -0
- package/dist/custom-providers/AnthropicCustomAdapter.js +297 -0
- package/dist/custom-providers/XAIAdapter.d.ts +40 -0
- package/dist/custom-providers/XAIAdapter.js +295 -0
- package/dist/custom-tools/skill-tools/index.d.ts +33 -0
- package/dist/custom-tools/skill-tools/index.js +63 -0
- package/dist/custom-tools/skill-tools/tools/create/index.d.ts +2 -0
- package/dist/custom-tools/skill-tools/tools/create/index.js +93 -0
- package/dist/custom-tools/skill-tools/tools/create/schema.d.ts +6 -0
- package/dist/custom-tools/skill-tools/tools/create/schema.js +41 -0
- package/dist/custom-tools/skill-tools/tools/list/index.d.ts +2 -0
- package/dist/custom-tools/skill-tools/tools/list/index.js +113 -0
- package/dist/custom-tools/skill-tools/tools/list/schema.d.ts +6 -0
- package/dist/custom-tools/skill-tools/tools/list/schema.js +19 -0
- package/dist/custom-tools/skill-tools/tools/read/index.d.ts +2 -0
- package/dist/custom-tools/skill-tools/tools/read/index.js +124 -0
- package/dist/custom-tools/skill-tools/tools/read/schema.d.ts +6 -0
- package/dist/custom-tools/skill-tools/tools/read/schema.js +27 -0
- package/dist/custom-tools/skill-tools/tools/search/bm25.d.ts +71 -0
- package/dist/custom-tools/skill-tools/tools/search/bm25.js +305 -0
- package/dist/custom-tools/skill-tools/tools/search/index.d.ts +8 -0
- package/dist/custom-tools/skill-tools/tools/search/index.js +63 -0
- package/dist/custom-tools/skill-tools/tools/search/schema.d.ts +6 -0
- package/dist/custom-tools/skill-tools/tools/search/schema.js +19 -0
- package/dist/custom-tools/skill-tools/tools/search/skill-index.d.ts +54 -0
- package/dist/custom-tools/skill-tools/tools/search/skill-index.js +251 -0
- package/dist/custom-tools/skill-tools/tools/update/index.d.ts +2 -0
- package/dist/custom-tools/skill-tools/tools/update/index.js +115 -0
- package/dist/custom-tools/skill-tools/tools/update/schema.d.ts +6 -0
- package/dist/custom-tools/skill-tools/tools/update/schema.js +41 -0
- package/dist/screens/ChatScreen.d.ts +1 -0
- package/dist/screens/ChatScreen.js +327 -0
- package/dist/screens/HomeScreen.d.ts +1 -0
- package/dist/screens/HomeScreen.js +68 -0
- package/dist/screens/SettingsScreen.d.ts +1 -0
- package/dist/screens/SettingsScreen.js +35 -0
- package/dist/services/db.d.ts +31 -0
- package/dist/services/db.js +108 -0
- package/dist/theme/ThemeContext.d.ts +11 -0
- package/dist/theme/ThemeContext.js +31 -0
- package/dist/theme/theme.d.ts +17 -0
- package/dist/theme/theme.js +82 -0
- package/package.json +101 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* xAI Adapter - Custom Provider Example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates how to create a custom provider adapter for the Toolpack SDK.
|
|
5
|
+
* xAI provides Grok models for fast, intelligent AI responses.
|
|
6
|
+
*
|
|
7
|
+
* API Docs: https://docs.x.ai/api
|
|
8
|
+
*/
|
|
9
|
+
export class XAIAdapter {
|
|
10
|
+
constructor(config = {}) {
|
|
11
|
+
Object.defineProperty(this, "name", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
value: 'xai'
|
|
16
|
+
});
|
|
17
|
+
Object.defineProperty(this, "apiKey", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
value: void 0
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(this, "baseUrl", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
value: void 0
|
|
28
|
+
});
|
|
29
|
+
Object.defineProperty(this, "defaultModel", {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
configurable: true,
|
|
32
|
+
writable: true,
|
|
33
|
+
value: void 0
|
|
34
|
+
});
|
|
35
|
+
// Resolve API key from config or environment (defer error to usage time)
|
|
36
|
+
this.apiKey =
|
|
37
|
+
config.apiKey ||
|
|
38
|
+
process.env['XAI_API_KEY'] ||
|
|
39
|
+
process.env['TOOLPACK_XAI_KEY'] ||
|
|
40
|
+
null;
|
|
41
|
+
this.baseUrl = config.baseUrl || 'https://api.x.ai/v1';
|
|
42
|
+
this.defaultModel = config.defaultModel || 'grok-3-latest';
|
|
43
|
+
}
|
|
44
|
+
getDisplayName() {
|
|
45
|
+
return 'xAI (Grok)';
|
|
46
|
+
}
|
|
47
|
+
async getModels() {
|
|
48
|
+
return [
|
|
49
|
+
{
|
|
50
|
+
id: 'grok-3-latest',
|
|
51
|
+
displayName: 'Grok 3 (Latest)',
|
|
52
|
+
capabilities: {
|
|
53
|
+
chat: true,
|
|
54
|
+
streaming: true,
|
|
55
|
+
toolCalling: true,
|
|
56
|
+
embeddings: false,
|
|
57
|
+
vision: false,
|
|
58
|
+
},
|
|
59
|
+
contextWindow: 131072,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 'grok-beta',
|
|
63
|
+
displayName: 'Grok Beta',
|
|
64
|
+
capabilities: {
|
|
65
|
+
chat: true,
|
|
66
|
+
streaming: true,
|
|
67
|
+
toolCalling: true,
|
|
68
|
+
embeddings: false,
|
|
69
|
+
vision: false,
|
|
70
|
+
},
|
|
71
|
+
contextWindow: 131072,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: 'grok-vision-beta',
|
|
75
|
+
displayName: 'Grok Vision Beta',
|
|
76
|
+
capabilities: {
|
|
77
|
+
chat: true,
|
|
78
|
+
streaming: true,
|
|
79
|
+
toolCalling: true,
|
|
80
|
+
embeddings: false,
|
|
81
|
+
vision: true,
|
|
82
|
+
},
|
|
83
|
+
contextWindow: 8192,
|
|
84
|
+
},
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
ensureApiKey() {
|
|
88
|
+
if (!this.apiKey) {
|
|
89
|
+
throw new Error('No API key found for xAI. Set XAI_API_KEY or TOOLPACK_XAI_KEY environment variable, ' +
|
|
90
|
+
'or pass apiKey in config.');
|
|
91
|
+
}
|
|
92
|
+
return this.apiKey;
|
|
93
|
+
}
|
|
94
|
+
async generate(request) {
|
|
95
|
+
const apiKey = this.ensureApiKey();
|
|
96
|
+
const model = request.model || this.defaultModel;
|
|
97
|
+
const response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
98
|
+
method: 'POST',
|
|
99
|
+
headers: {
|
|
100
|
+
Authorization: `Bearer ${apiKey}`,
|
|
101
|
+
'Content-Type': 'application/json',
|
|
102
|
+
},
|
|
103
|
+
body: JSON.stringify({
|
|
104
|
+
model,
|
|
105
|
+
messages: request.messages.map(m => ({
|
|
106
|
+
role: m.role,
|
|
107
|
+
content: m.content,
|
|
108
|
+
...(m.tool_call_id ? { tool_call_id: m.tool_call_id } : {}),
|
|
109
|
+
...(m.tool_calls ? { tool_calls: m.tool_calls } : {}),
|
|
110
|
+
})),
|
|
111
|
+
tools: request.tools?.map(t => ({
|
|
112
|
+
type: 'function',
|
|
113
|
+
function: {
|
|
114
|
+
name: t.function.name,
|
|
115
|
+
description: t.function.description,
|
|
116
|
+
parameters: t.function.parameters,
|
|
117
|
+
},
|
|
118
|
+
})),
|
|
119
|
+
tool_choice: request.tool_choice,
|
|
120
|
+
temperature: request.temperature ?? 0.7,
|
|
121
|
+
max_tokens: request.max_tokens ?? 4096,
|
|
122
|
+
top_p: request.top_p,
|
|
123
|
+
}),
|
|
124
|
+
});
|
|
125
|
+
if (!response.ok) {
|
|
126
|
+
const error = await response.text();
|
|
127
|
+
throw new Error(`xAI API error: ${response.status} - ${error}`);
|
|
128
|
+
}
|
|
129
|
+
const data = (await response.json());
|
|
130
|
+
const choice = data.choices[0];
|
|
131
|
+
// Extract tool calls if present
|
|
132
|
+
let toolCalls = undefined;
|
|
133
|
+
if (choice.message.tool_calls) {
|
|
134
|
+
toolCalls = choice.message.tool_calls.map((tc) => ({
|
|
135
|
+
id: tc.id,
|
|
136
|
+
name: tc.function.name,
|
|
137
|
+
arguments: typeof tc.function.arguments === 'string'
|
|
138
|
+
? JSON.parse(tc.function.arguments)
|
|
139
|
+
: tc.function.arguments,
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
content: choice.message.content,
|
|
144
|
+
usage: data.usage
|
|
145
|
+
? {
|
|
146
|
+
prompt_tokens: data.usage.prompt_tokens,
|
|
147
|
+
completion_tokens: data.usage.completion_tokens,
|
|
148
|
+
total_tokens: data.usage.total_tokens,
|
|
149
|
+
}
|
|
150
|
+
: undefined,
|
|
151
|
+
finish_reason: choice.finish_reason === 'tool_calls'
|
|
152
|
+
? 'tool_calls'
|
|
153
|
+
: choice.finish_reason === 'length'
|
|
154
|
+
? 'length'
|
|
155
|
+
: 'stop',
|
|
156
|
+
tool_calls: toolCalls,
|
|
157
|
+
raw: data,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
async *stream(request) {
|
|
161
|
+
const apiKey = this.ensureApiKey();
|
|
162
|
+
const model = request.model || this.defaultModel;
|
|
163
|
+
const response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
164
|
+
method: 'POST',
|
|
165
|
+
headers: {
|
|
166
|
+
Authorization: `Bearer ${apiKey}`,
|
|
167
|
+
'Content-Type': 'application/json',
|
|
168
|
+
},
|
|
169
|
+
body: JSON.stringify({
|
|
170
|
+
model,
|
|
171
|
+
messages: request.messages.map(m => ({
|
|
172
|
+
role: m.role,
|
|
173
|
+
content: m.content,
|
|
174
|
+
...(m.tool_call_id ? { tool_call_id: m.tool_call_id } : {}),
|
|
175
|
+
...(m.tool_calls ? { tool_calls: m.tool_calls } : {}),
|
|
176
|
+
})),
|
|
177
|
+
tools: request.tools?.map(t => ({
|
|
178
|
+
type: 'function',
|
|
179
|
+
function: {
|
|
180
|
+
name: t.function.name,
|
|
181
|
+
description: t.function.description,
|
|
182
|
+
parameters: t.function.parameters,
|
|
183
|
+
},
|
|
184
|
+
})),
|
|
185
|
+
tool_choice: request.tool_choice,
|
|
186
|
+
temperature: request.temperature ?? 0.7,
|
|
187
|
+
max_tokens: request.max_tokens ?? 4096,
|
|
188
|
+
stream: true,
|
|
189
|
+
}),
|
|
190
|
+
});
|
|
191
|
+
if (!response.ok) {
|
|
192
|
+
const error = await response.text();
|
|
193
|
+
throw new Error(`xAI API error: ${response.status} - ${error}`);
|
|
194
|
+
}
|
|
195
|
+
const reader = response.body.getReader();
|
|
196
|
+
const decoder = new TextDecoder();
|
|
197
|
+
let buffer = '';
|
|
198
|
+
// Accumulate tool calls across chunks
|
|
199
|
+
const toolCallsAccumulator = new Map();
|
|
200
|
+
while (true) {
|
|
201
|
+
const { done, value } = await reader.read();
|
|
202
|
+
if (done)
|
|
203
|
+
break;
|
|
204
|
+
buffer += decoder.decode(value, { stream: true });
|
|
205
|
+
const lines = buffer.split('\n');
|
|
206
|
+
buffer = lines.pop() || '';
|
|
207
|
+
for (const line of lines) {
|
|
208
|
+
if (!line.startsWith('data: '))
|
|
209
|
+
continue;
|
|
210
|
+
const data = line.slice(6).trim();
|
|
211
|
+
if (data === '[DONE]') {
|
|
212
|
+
// Emit accumulated tool calls if any
|
|
213
|
+
if (toolCallsAccumulator.size > 0) {
|
|
214
|
+
const toolCalls = Array.from(toolCallsAccumulator.values()).map(tc => ({
|
|
215
|
+
id: tc.id,
|
|
216
|
+
name: tc.name,
|
|
217
|
+
arguments: JSON.parse(tc.arguments || '{}'),
|
|
218
|
+
}));
|
|
219
|
+
yield {
|
|
220
|
+
delta: '',
|
|
221
|
+
finish_reason: 'tool_calls',
|
|
222
|
+
tool_calls: toolCalls,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
const chunk = JSON.parse(data);
|
|
229
|
+
const delta = chunk.choices[0]?.delta;
|
|
230
|
+
const finishReason = chunk.choices[0]?.finish_reason;
|
|
231
|
+
// Handle text content
|
|
232
|
+
if (delta?.content) {
|
|
233
|
+
yield { delta: delta.content };
|
|
234
|
+
}
|
|
235
|
+
// Accumulate tool calls (they come in fragments)
|
|
236
|
+
if (delta?.tool_calls) {
|
|
237
|
+
for (const tc of delta.tool_calls) {
|
|
238
|
+
const idx = tc.index ?? 0;
|
|
239
|
+
if (!toolCallsAccumulator.has(idx)) {
|
|
240
|
+
toolCallsAccumulator.set(idx, {
|
|
241
|
+
id: '',
|
|
242
|
+
name: '',
|
|
243
|
+
arguments: '',
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
const acc = toolCallsAccumulator.get(idx);
|
|
247
|
+
if (tc.id)
|
|
248
|
+
acc.id = tc.id;
|
|
249
|
+
if (tc.function?.name)
|
|
250
|
+
acc.name = tc.function.name;
|
|
251
|
+
if (tc.function?.arguments)
|
|
252
|
+
acc.arguments += tc.function.arguments;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// Handle finish reasons
|
|
256
|
+
if (finishReason === 'stop') {
|
|
257
|
+
yield { delta: '', finish_reason: 'stop' };
|
|
258
|
+
}
|
|
259
|
+
else if (finishReason === 'tool_calls') {
|
|
260
|
+
const toolCalls = Array.from(toolCallsAccumulator.values()).map(tc => ({
|
|
261
|
+
id: tc.id,
|
|
262
|
+
name: tc.name,
|
|
263
|
+
arguments: JSON.parse(tc.arguments || '{}'),
|
|
264
|
+
}));
|
|
265
|
+
yield {
|
|
266
|
+
delta: '',
|
|
267
|
+
finish_reason: 'tool_calls',
|
|
268
|
+
tool_calls: toolCalls,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
else if (finishReason === 'length') {
|
|
272
|
+
yield { delta: '', finish_reason: 'length' };
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
// Ignore parse errors from partial lines
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
async embed(_request) {
|
|
282
|
+
// xAI doesn't support embeddings - throw a clear error
|
|
283
|
+
throw new Error('xAI does not support embeddings. Use OpenAI or another provider for embeddings.');
|
|
284
|
+
}
|
|
285
|
+
// File upload is not supported by xAI
|
|
286
|
+
supportsFileUpload() {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
async uploadFile(_request) {
|
|
290
|
+
throw new Error('xAI does not support file uploads.');
|
|
291
|
+
}
|
|
292
|
+
async deleteFile(_fileId) {
|
|
293
|
+
throw new Error('xAI does not support file operations.');
|
|
294
|
+
}
|
|
295
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Tools Project
|
|
3
|
+
*
|
|
4
|
+
* Tools for managing SKILL.md files - structured documentation for AI agent
|
|
5
|
+
* skills, capabilities, and learned behaviors.
|
|
6
|
+
*
|
|
7
|
+
* Skills are stored in .skills/ directory and follow a structured markdown
|
|
8
|
+
* format with:
|
|
9
|
+
* - Frontmatter metadata (name, title, version, tags)
|
|
10
|
+
* - Description section
|
|
11
|
+
* - Triggers (phrases that activate the skill)
|
|
12
|
+
* - Instructions (how the AI should behave)
|
|
13
|
+
* - Examples (sample interactions)
|
|
14
|
+
*
|
|
15
|
+
* Structure:
|
|
16
|
+
* skill-tools/
|
|
17
|
+
* ├── toolproject.json
|
|
18
|
+
* ├── index.ts (this file)
|
|
19
|
+
* └── tools/
|
|
20
|
+
* ├── create/
|
|
21
|
+
* ├── read/
|
|
22
|
+
* ├── update/
|
|
23
|
+
* ├── list/
|
|
24
|
+
* └── search/ (BM25-powered skill search)
|
|
25
|
+
*/
|
|
26
|
+
import { ToolProject } from 'toolpack-sdk';
|
|
27
|
+
export { skillCreateTool } from './tools/create/index.js';
|
|
28
|
+
export { skillReadTool } from './tools/read/index.js';
|
|
29
|
+
export { skillUpdateTool } from './tools/update/index.js';
|
|
30
|
+
export { skillListTool } from './tools/list/index.js';
|
|
31
|
+
export { skillSearchTool } from './tools/search/index.js';
|
|
32
|
+
export declare const skillToolsProject: ToolProject;
|
|
33
|
+
export default skillToolsProject;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Tools Project
|
|
3
|
+
*
|
|
4
|
+
* Tools for managing SKILL.md files - structured documentation for AI agent
|
|
5
|
+
* skills, capabilities, and learned behaviors.
|
|
6
|
+
*
|
|
7
|
+
* Skills are stored in .skills/ directory and follow a structured markdown
|
|
8
|
+
* format with:
|
|
9
|
+
* - Frontmatter metadata (name, title, version, tags)
|
|
10
|
+
* - Description section
|
|
11
|
+
* - Triggers (phrases that activate the skill)
|
|
12
|
+
* - Instructions (how the AI should behave)
|
|
13
|
+
* - Examples (sample interactions)
|
|
14
|
+
*
|
|
15
|
+
* Structure:
|
|
16
|
+
* skill-tools/
|
|
17
|
+
* ├── toolproject.json
|
|
18
|
+
* ├── index.ts (this file)
|
|
19
|
+
* └── tools/
|
|
20
|
+
* ├── create/
|
|
21
|
+
* ├── read/
|
|
22
|
+
* ├── update/
|
|
23
|
+
* ├── list/
|
|
24
|
+
* └── search/ (BM25-powered skill search)
|
|
25
|
+
*/
|
|
26
|
+
import { skillCreateTool } from './tools/create/index.js';
|
|
27
|
+
import { skillReadTool } from './tools/read/index.js';
|
|
28
|
+
import { skillUpdateTool } from './tools/update/index.js';
|
|
29
|
+
import { skillListTool } from './tools/list/index.js';
|
|
30
|
+
import { skillSearchTool } from './tools/search/index.js';
|
|
31
|
+
// Re-export individual tools for direct access
|
|
32
|
+
export { skillCreateTool } from './tools/create/index.js';
|
|
33
|
+
export { skillReadTool } from './tools/read/index.js';
|
|
34
|
+
export { skillUpdateTool } from './tools/update/index.js';
|
|
35
|
+
export { skillListTool } from './tools/list/index.js';
|
|
36
|
+
export { skillSearchTool } from './tools/search/index.js';
|
|
37
|
+
// Export the complete ToolProject
|
|
38
|
+
export const skillToolsProject = {
|
|
39
|
+
manifest: {
|
|
40
|
+
key: 'skill',
|
|
41
|
+
name: 'skill-tools',
|
|
42
|
+
displayName: 'Skill Tools',
|
|
43
|
+
version: '1.0.0',
|
|
44
|
+
description: 'Tools for managing SKILL.md files - structured documentation for AI agent skills, capabilities, and learned behaviors.',
|
|
45
|
+
author: 'Toolpack Team',
|
|
46
|
+
tools: [
|
|
47
|
+
'skill.create',
|
|
48
|
+
'skill.read',
|
|
49
|
+
'skill.update',
|
|
50
|
+
'skill.list',
|
|
51
|
+
'skill.search',
|
|
52
|
+
],
|
|
53
|
+
category: 'productivity',
|
|
54
|
+
},
|
|
55
|
+
tools: [
|
|
56
|
+
skillCreateTool,
|
|
57
|
+
skillReadTool,
|
|
58
|
+
skillUpdateTool,
|
|
59
|
+
skillListTool,
|
|
60
|
+
skillSearchTool,
|
|
61
|
+
],
|
|
62
|
+
};
|
|
63
|
+
export default skillToolsProject;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { name, displayName, description, parameters, category, } from './schema.js';
|
|
4
|
+
const SKILLS_DIR = '.skills';
|
|
5
|
+
async function execute(args) {
|
|
6
|
+
const skillName = args['name'];
|
|
7
|
+
const title = args['title'];
|
|
8
|
+
const desc = args['description'];
|
|
9
|
+
const triggers = args['triggers'] || [];
|
|
10
|
+
const instructions = args['instructions'];
|
|
11
|
+
const examples = args['examples'] || [];
|
|
12
|
+
const tags = args['tags'] || [];
|
|
13
|
+
// Validate skill name (kebab-case)
|
|
14
|
+
if (!/^[a-z][a-z0-9-]*$/.test(skillName)) {
|
|
15
|
+
return JSON.stringify({
|
|
16
|
+
error: 'invalid_name',
|
|
17
|
+
message: 'Skill name must be kebab-case (lowercase letters, numbers, hyphens, starting with a letter)',
|
|
18
|
+
}, null, 2);
|
|
19
|
+
}
|
|
20
|
+
// Ensure .skills directory exists
|
|
21
|
+
const skillsDir = path.join(process.cwd(), SKILLS_DIR);
|
|
22
|
+
try {
|
|
23
|
+
await fs.mkdir(skillsDir, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
// Directory might already exist
|
|
27
|
+
}
|
|
28
|
+
// Check if skill already exists
|
|
29
|
+
const skillPath = path.join(skillsDir, `${skillName}.skill.md`);
|
|
30
|
+
try {
|
|
31
|
+
await fs.access(skillPath);
|
|
32
|
+
return JSON.stringify({
|
|
33
|
+
error: 'skill_exists',
|
|
34
|
+
message: `Skill "${skillName}" already exists at ${skillPath}. Use skill.update to modify it.`,
|
|
35
|
+
}, null, 2);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// File doesn't exist, we can create it
|
|
39
|
+
}
|
|
40
|
+
// Generate SKILL.md content
|
|
41
|
+
const now = new Date().toISOString();
|
|
42
|
+
const content = `---
|
|
43
|
+
name: ${skillName}
|
|
44
|
+
title: ${title}
|
|
45
|
+
version: 1.0.0
|
|
46
|
+
created: ${now}
|
|
47
|
+
updated: ${now}
|
|
48
|
+
tags: [${tags.map(t => `"${t}"`).join(', ')}]
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
# ${title}
|
|
52
|
+
|
|
53
|
+
## Description
|
|
54
|
+
|
|
55
|
+
${desc}
|
|
56
|
+
|
|
57
|
+
## Triggers
|
|
58
|
+
|
|
59
|
+
${triggers.length > 0
|
|
60
|
+
? triggers.map(t => `- "${t}"`).join('\n')
|
|
61
|
+
: '_No specific triggers defined_'}
|
|
62
|
+
|
|
63
|
+
## Instructions
|
|
64
|
+
|
|
65
|
+
${instructions}
|
|
66
|
+
|
|
67
|
+
## Examples
|
|
68
|
+
|
|
69
|
+
${examples.length > 0
|
|
70
|
+
? examples.map((ex, i) => `### Example ${i + 1}\n\n${ex}`).join('\n\n')
|
|
71
|
+
: '_No examples provided_'}
|
|
72
|
+
|
|
73
|
+
## Usage Notes
|
|
74
|
+
|
|
75
|
+
_Add any additional notes about when and how to use this skill._
|
|
76
|
+
`;
|
|
77
|
+
// Write the skill file
|
|
78
|
+
await fs.writeFile(skillPath, content, 'utf-8');
|
|
79
|
+
return JSON.stringify({
|
|
80
|
+
success: true,
|
|
81
|
+
skill: skillName,
|
|
82
|
+
path: skillPath,
|
|
83
|
+
message: `Skill "${title}" created successfully`,
|
|
84
|
+
}, null, 2);
|
|
85
|
+
}
|
|
86
|
+
export const skillCreateTool = {
|
|
87
|
+
name,
|
|
88
|
+
displayName,
|
|
89
|
+
description,
|
|
90
|
+
parameters,
|
|
91
|
+
category,
|
|
92
|
+
execute,
|
|
93
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ToolParameters } from 'toolpack-sdk';
|
|
2
|
+
export declare const name = "skill.create";
|
|
3
|
+
export declare const displayName = "Create Skill";
|
|
4
|
+
export declare const description = "Create a new SKILL.md file documenting an AI agent skill or capability. Skills are stored in .skills/ directory and follow a structured markdown format with metadata, description, examples, and usage guidelines.";
|
|
5
|
+
export declare const category = "productivity";
|
|
6
|
+
export declare const parameters: ToolParameters;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export const name = 'skill.create';
|
|
2
|
+
export const displayName = 'Create Skill';
|
|
3
|
+
export const description = 'Create a new SKILL.md file documenting an AI agent skill or capability. Skills are stored in .skills/ directory and follow a structured markdown format with metadata, description, examples, and usage guidelines.';
|
|
4
|
+
export const category = 'productivity';
|
|
5
|
+
export const parameters = {
|
|
6
|
+
type: 'object',
|
|
7
|
+
properties: {
|
|
8
|
+
name: {
|
|
9
|
+
type: 'string',
|
|
10
|
+
description: 'Unique skill name in kebab-case (e.g., "code-review", "data-analysis", "api-integration")',
|
|
11
|
+
},
|
|
12
|
+
title: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
description: 'Human-readable title for the skill (e.g., "Code Review Assistant")',
|
|
15
|
+
},
|
|
16
|
+
description: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'Brief description of what this skill enables the AI to do',
|
|
19
|
+
},
|
|
20
|
+
triggers: {
|
|
21
|
+
type: 'array',
|
|
22
|
+
description: 'List of phrases or keywords that should activate this skill',
|
|
23
|
+
items: { type: 'string' },
|
|
24
|
+
},
|
|
25
|
+
instructions: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
description: 'Detailed instructions for how the AI should behave when using this skill',
|
|
28
|
+
},
|
|
29
|
+
examples: {
|
|
30
|
+
type: 'array',
|
|
31
|
+
description: 'Example prompts and expected behaviors',
|
|
32
|
+
items: { type: 'string' },
|
|
33
|
+
},
|
|
34
|
+
tags: {
|
|
35
|
+
type: 'array',
|
|
36
|
+
description: 'Tags for categorizing the skill (e.g., ["coding", "review", "quality"])',
|
|
37
|
+
items: { type: 'string' },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
required: ['name', 'title', 'description', 'instructions'],
|
|
41
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { name, displayName, description, parameters, category, } from './schema.js';
|
|
4
|
+
const SKILLS_DIR = '.skills';
|
|
5
|
+
async function execute(args) {
|
|
6
|
+
const filterTag = args['tag'];
|
|
7
|
+
const verbose = args['verbose'] || false;
|
|
8
|
+
const skillsDir = path.join(process.cwd(), SKILLS_DIR);
|
|
9
|
+
// Check if .skills directory exists
|
|
10
|
+
try {
|
|
11
|
+
await fs.access(skillsDir);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return JSON.stringify({
|
|
15
|
+
skills: [],
|
|
16
|
+
count: 0,
|
|
17
|
+
message: 'No .skills directory found. Use skill.create to create your first skill.',
|
|
18
|
+
}, null, 2);
|
|
19
|
+
}
|
|
20
|
+
// Read all .skill.md files
|
|
21
|
+
const files = await fs.readdir(skillsDir);
|
|
22
|
+
const skillFiles = files.filter(f => f.endsWith('.skill.md'));
|
|
23
|
+
if (skillFiles.length === 0) {
|
|
24
|
+
return JSON.stringify({
|
|
25
|
+
skills: [],
|
|
26
|
+
count: 0,
|
|
27
|
+
message: 'No skills found. Use skill.create to create your first skill.',
|
|
28
|
+
}, null, 2);
|
|
29
|
+
}
|
|
30
|
+
const skills = [];
|
|
31
|
+
for (const file of skillFiles) {
|
|
32
|
+
const filePath = path.join(skillsDir, file);
|
|
33
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
34
|
+
// Parse frontmatter
|
|
35
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
36
|
+
let skillName = file.replace('.skill.md', '');
|
|
37
|
+
let title = skillName;
|
|
38
|
+
let tags = [];
|
|
39
|
+
if (frontmatterMatch && frontmatterMatch[1]) {
|
|
40
|
+
const lines = frontmatterMatch[1].split('\n');
|
|
41
|
+
for (const line of lines) {
|
|
42
|
+
if (line.startsWith('name:')) {
|
|
43
|
+
skillName = line.replace('name:', '').trim();
|
|
44
|
+
}
|
|
45
|
+
if (line.startsWith('title:')) {
|
|
46
|
+
title = line.replace('title:', '').trim();
|
|
47
|
+
}
|
|
48
|
+
if (line.startsWith('tags:')) {
|
|
49
|
+
const tagsMatch = line.match(/\[(.*)\]/);
|
|
50
|
+
if (tagsMatch && tagsMatch[1]) {
|
|
51
|
+
tags = tagsMatch[1]
|
|
52
|
+
.split(',')
|
|
53
|
+
.map(t => t.trim().replace(/"/g, ''))
|
|
54
|
+
.filter(Boolean);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Filter by tag if specified
|
|
60
|
+
if (filterTag && !tags.includes(filterTag)) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
skills.push({
|
|
64
|
+
name: skillName,
|
|
65
|
+
title,
|
|
66
|
+
tags,
|
|
67
|
+
path: filePath,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (verbose) {
|
|
71
|
+
// Return full details for each skill
|
|
72
|
+
const detailedSkills = [];
|
|
73
|
+
for (const skill of skills) {
|
|
74
|
+
const content = await fs.readFile(skill.path, 'utf-8');
|
|
75
|
+
const descMatch = content.match(/## Description\n\n([\s\S]*?)(?=\n## |$)/);
|
|
76
|
+
const triggersMatch = content.match(/## Triggers\n\n([\s\S]*?)(?=\n## |$)/);
|
|
77
|
+
const triggers = [];
|
|
78
|
+
if (triggersMatch && triggersMatch[1]) {
|
|
79
|
+
const triggerLines = triggersMatch[1]
|
|
80
|
+
.split('\n')
|
|
81
|
+
.filter(l => l.startsWith('- '));
|
|
82
|
+
for (const line of triggerLines) {
|
|
83
|
+
const match = line.match(/- "(.*)"/);
|
|
84
|
+
if (match && match[1])
|
|
85
|
+
triggers.push(match[1]);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
detailedSkills.push({
|
|
89
|
+
...skill,
|
|
90
|
+
description: descMatch && descMatch[1] ? descMatch[1].trim() : '',
|
|
91
|
+
triggers,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return JSON.stringify({
|
|
95
|
+
skills: detailedSkills,
|
|
96
|
+
count: detailedSkills.length,
|
|
97
|
+
filtered: filterTag ? `by tag: ${filterTag}` : null,
|
|
98
|
+
}, null, 2);
|
|
99
|
+
}
|
|
100
|
+
return JSON.stringify({
|
|
101
|
+
skills: skills.map(s => ({ name: s.name, title: s.title, tags: s.tags })),
|
|
102
|
+
count: skills.length,
|
|
103
|
+
filtered: filterTag ? `by tag: ${filterTag}` : null,
|
|
104
|
+
}, null, 2);
|
|
105
|
+
}
|
|
106
|
+
export const skillListTool = {
|
|
107
|
+
name,
|
|
108
|
+
displayName,
|
|
109
|
+
description,
|
|
110
|
+
parameters,
|
|
111
|
+
category,
|
|
112
|
+
execute,
|
|
113
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ToolParameters } from 'toolpack-sdk';
|
|
2
|
+
export declare const name = "skill.list";
|
|
3
|
+
export declare const displayName = "List Skills";
|
|
4
|
+
export declare const description = "List all available SKILL.md files in the .skills directory with their metadata.";
|
|
5
|
+
export declare const category = "productivity";
|
|
6
|
+
export declare const parameters: ToolParameters;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const name = 'skill.list';
|
|
2
|
+
export const displayName = 'List Skills';
|
|
3
|
+
export const description = 'List all available SKILL.md files in the .skills directory with their metadata.';
|
|
4
|
+
export const category = 'productivity';
|
|
5
|
+
export const parameters = {
|
|
6
|
+
type: 'object',
|
|
7
|
+
properties: {
|
|
8
|
+
tag: {
|
|
9
|
+
type: 'string',
|
|
10
|
+
description: 'Optional: filter skills by tag',
|
|
11
|
+
},
|
|
12
|
+
verbose: {
|
|
13
|
+
type: 'boolean',
|
|
14
|
+
description: 'Include full metadata for each skill. Default: false',
|
|
15
|
+
default: false,
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
required: [],
|
|
19
|
+
};
|