@tyvm/knowhow 0.0.129 → 0.0.131
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/package.json +4 -4
- package/src/agents/base/base.ts +40 -5
- package/src/agents/configurable/ConfigAgent.ts +0 -1
- package/src/agents/tools/index.ts +1 -0
- package/src/agents/tools/list.ts +2 -0
- package/src/agents/tools/skills.ts +79 -0
- package/src/agents/tools/startAgentTask.ts +17 -13
- package/src/ai.ts +55 -24
- package/src/chat/CliChatService.ts +7 -1
- package/src/chat/modules/AgentModule.ts +156 -15
- package/src/chat/renderer/PlainRenderer.ts +218 -0
- package/src/chat/renderer/index.ts +1 -0
- package/src/chat/renderer/loadRenderer.ts +2 -0
- package/src/chat.ts +2 -2
- package/src/cli.ts +10 -0
- package/src/clients/anthropic.ts +68 -2
- package/src/clients/fireworks.ts +10 -1
- package/src/clients/index.ts +5 -3
- package/src/clients/meta.ts +9 -0
- package/src/clients/openai.ts +28 -5
- package/src/clients/pricing/anthropic.ts +11 -0
- package/src/clients/pricing/fireworks.ts +12 -0
- package/src/clients/pricing/openai.ts +3 -0
- package/src/clients/types.ts +27 -4
- package/src/clients/xai.ts +2 -2
- package/src/commands/agent.ts +70 -18
- package/src/commands/agents.ts +556 -0
- package/src/commands/behaviors.ts +47 -0
- package/src/commands/hash.ts +124 -0
- package/src/commands/misc.ts +157 -3
- package/src/commands/services.ts +6 -2
- package/src/commands/skills.ts +107 -0
- package/src/config.ts +51 -1
- package/src/generate.ts +507 -0
- package/src/index.ts +34 -216
- package/src/plugins/PluginBase.ts +0 -2
- package/src/plugins/SkillsPlugin.ts +11 -124
- package/src/plugins/language.ts +0 -1
- package/src/plugins/plugins.ts +24 -13
- package/src/plugins/types.ts +8 -2
- package/src/services/BehaviorsService.ts +464 -0
- package/src/services/KnowhowClient.ts +61 -0
- package/src/services/SkillsService.ts +820 -0
- package/src/services/Tools.ts +2 -0
- package/src/services/index.ts +8 -2
- package/src/services/modules/types.ts +2 -0
- package/src/types.ts +36 -4
- package/ts_build/package.json +4 -4
- package/ts_build/src/agents/base/base.d.ts +9 -1
- package/ts_build/src/agents/base/base.js +29 -4
- package/ts_build/src/agents/base/base.js.map +1 -1
- package/ts_build/src/agents/configurable/ConfigAgent.js.map +1 -1
- package/ts_build/src/agents/tools/index.d.ts +1 -0
- package/ts_build/src/agents/tools/index.js +1 -0
- package/ts_build/src/agents/tools/index.js.map +1 -1
- package/ts_build/src/agents/tools/list.js +2 -0
- package/ts_build/src/agents/tools/list.js.map +1 -1
- package/ts_build/src/agents/tools/skills.d.ts +33 -0
- package/ts_build/src/agents/tools/skills.js +62 -0
- package/ts_build/src/agents/tools/skills.js.map +1 -0
- package/ts_build/src/agents/tools/startAgentTask.d.ts +2 -2
- package/ts_build/src/agents/tools/startAgentTask.js +13 -12
- package/ts_build/src/agents/tools/startAgentTask.js.map +1 -1
- package/ts_build/src/ai.d.ts +10 -4
- package/ts_build/src/ai.js +27 -25
- package/ts_build/src/ai.js.map +1 -1
- package/ts_build/src/chat/CliChatService.d.ts +1 -1
- package/ts_build/src/chat/CliChatService.js +4 -1
- package/ts_build/src/chat/CliChatService.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.d.ts +2 -0
- package/ts_build/src/chat/modules/AgentModule.js +130 -4
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/modules/CustomCommandsModule.d.ts +1 -1
- package/ts_build/src/chat/renderer/PlainRenderer.d.ts +28 -0
- package/ts_build/src/chat/renderer/PlainRenderer.js +169 -0
- package/ts_build/src/chat/renderer/PlainRenderer.js.map +1 -0
- package/ts_build/src/chat/renderer/index.d.ts +1 -0
- package/ts_build/src/chat/renderer/index.js +3 -1
- package/ts_build/src/chat/renderer/index.js.map +1 -1
- package/ts_build/src/chat/renderer/loadRenderer.js +2 -0
- package/ts_build/src/chat/renderer/loadRenderer.js.map +1 -1
- package/ts_build/src/chat.d.ts +1 -1
- package/ts_build/src/chat.js +2 -2
- package/ts_build/src/chat.js.map +1 -1
- package/ts_build/src/cli.js +9 -0
- package/ts_build/src/cli.js.map +1 -1
- package/ts_build/src/clients/anthropic.d.ts +2 -0
- package/ts_build/src/clients/anthropic.js +39 -1
- package/ts_build/src/clients/anthropic.js.map +1 -1
- package/ts_build/src/clients/fireworks.js +7 -0
- package/ts_build/src/clients/fireworks.js.map +1 -1
- package/ts_build/src/clients/index.js +3 -1
- package/ts_build/src/clients/index.js.map +1 -1
- package/ts_build/src/clients/meta.d.ts +2 -0
- package/ts_build/src/clients/meta.js +5 -0
- package/ts_build/src/clients/meta.js.map +1 -1
- package/ts_build/src/clients/openai.d.ts +1 -0
- package/ts_build/src/clients/openai.js +25 -5
- package/ts_build/src/clients/openai.js.map +1 -1
- package/ts_build/src/clients/pricing/anthropic.d.ts +3 -0
- package/ts_build/src/clients/pricing/anthropic.js +6 -0
- package/ts_build/src/clients/pricing/anthropic.js.map +1 -1
- package/ts_build/src/clients/pricing/fireworks.d.ts +1 -0
- package/ts_build/src/clients/pricing/fireworks.js +5 -1
- package/ts_build/src/clients/pricing/fireworks.js.map +1 -1
- package/ts_build/src/clients/pricing/openai.js +3 -0
- package/ts_build/src/clients/pricing/openai.js.map +1 -1
- package/ts_build/src/clients/types.d.ts +6 -1
- package/ts_build/src/clients/xai.js +1 -1
- package/ts_build/src/clients/xai.js.map +1 -1
- package/ts_build/src/commands/agent.js +58 -15
- package/ts_build/src/commands/agent.js.map +1 -1
- package/ts_build/src/commands/agents.d.ts +2 -0
- package/ts_build/src/commands/agents.js +460 -0
- package/ts_build/src/commands/agents.js.map +1 -0
- package/ts_build/src/commands/behaviors.d.ts +2 -0
- package/ts_build/src/commands/behaviors.js +43 -0
- package/ts_build/src/commands/behaviors.js.map +1 -0
- package/ts_build/src/commands/hash.d.ts +2 -0
- package/ts_build/src/commands/hash.js +117 -0
- package/ts_build/src/commands/hash.js.map +1 -0
- package/ts_build/src/commands/misc.js +108 -2
- package/ts_build/src/commands/misc.js.map +1 -1
- package/ts_build/src/commands/services.js +4 -2
- package/ts_build/src/commands/services.js.map +1 -1
- package/ts_build/src/commands/skills.d.ts +2 -0
- package/ts_build/src/commands/skills.js +85 -0
- package/ts_build/src/commands/skills.js.map +1 -0
- package/ts_build/src/config.js +34 -0
- package/ts_build/src/config.js.map +1 -1
- package/ts_build/src/generate.d.ts +16 -0
- package/ts_build/src/generate.js +304 -0
- package/ts_build/src/generate.js.map +1 -0
- package/ts_build/src/index.d.ts +3 -3
- package/ts_build/src/index.js +13 -118
- package/ts_build/src/index.js.map +1 -1
- package/ts_build/src/plugins/PluginBase.d.ts +0 -2
- package/ts_build/src/plugins/PluginBase.js.map +1 -1
- package/ts_build/src/plugins/SkillsPlugin.d.ts +1 -6
- package/ts_build/src/plugins/SkillsPlugin.js +6 -135
- package/ts_build/src/plugins/SkillsPlugin.js.map +1 -1
- package/ts_build/src/plugins/language.js.map +1 -1
- package/ts_build/src/plugins/plugins.d.ts +4 -1
- package/ts_build/src/plugins/plugins.js +17 -11
- package/ts_build/src/plugins/plugins.js.map +1 -1
- package/ts_build/src/plugins/types.d.ts +3 -2
- package/ts_build/src/services/BehaviorsService.d.ts +48 -0
- package/ts_build/src/services/BehaviorsService.js +382 -0
- package/ts_build/src/services/BehaviorsService.js.map +1 -0
- package/ts_build/src/services/KnowhowClient.d.ts +8 -0
- package/ts_build/src/services/KnowhowClient.js +20 -0
- package/ts_build/src/services/KnowhowClient.js.map +1 -1
- package/ts_build/src/services/SkillsService.d.ts +60 -0
- package/ts_build/src/services/SkillsService.js +641 -0
- package/ts_build/src/services/SkillsService.js.map +1 -0
- package/ts_build/src/services/Tools.d.ts +2 -0
- package/ts_build/src/services/Tools.js.map +1 -1
- package/ts_build/src/services/index.d.ts +3 -0
- package/ts_build/src/services/index.js +8 -2
- package/ts_build/src/services/index.js.map +1 -1
- package/ts_build/src/services/modules/index.d.ts +4 -2
- package/ts_build/src/services/modules/types.d.ts +2 -0
- package/ts_build/src/types.d.ts +14 -4
- package/ts_build/src/types.js.map +1 -1
package/src/index.ts
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
import { summarizeFiles, summarizeFile } from "./ai";
|
|
2
|
-
import {
|
|
3
|
-
saveAllFileHashes,
|
|
4
|
-
saveHashes,
|
|
5
|
-
getHashes,
|
|
6
|
-
checkNoFilesChanged,
|
|
7
|
-
} from "./hashes";
|
|
2
|
+
import type { AgentOptions } from "./ai";
|
|
8
3
|
import * as fs from "fs";
|
|
9
4
|
import * as path from "path";
|
|
10
5
|
import * as crypto from "crypto";
|
|
11
|
-
import { promisify } from "util";
|
|
12
6
|
import { globSync } from "glob";
|
|
13
7
|
|
|
14
8
|
import { Prompts } from "./prompts";
|
|
@@ -37,12 +31,25 @@ import {
|
|
|
37
31
|
getConfiguredEmbeddingMap,
|
|
38
32
|
} from "./embeddings";
|
|
39
33
|
|
|
40
|
-
import { abort } from "process";
|
|
41
34
|
import { convertToText } from "./conversion";
|
|
42
35
|
import { knowhowMcpClient } from "./services/Mcp";
|
|
43
36
|
import { services } from "./services/";
|
|
44
37
|
import { Models } from "./types";
|
|
45
38
|
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// Re-export generate pipeline (moved to src/generate.ts)
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
export {
|
|
43
|
+
generate,
|
|
44
|
+
GenerateOptions,
|
|
45
|
+
buildWaves,
|
|
46
|
+
normalizeInputPattern,
|
|
47
|
+
withOutputTarget,
|
|
48
|
+
writeAgentOrSummaryOutput,
|
|
49
|
+
handleMultiOutputGeneration,
|
|
50
|
+
handleSingleOutputGeneration,
|
|
51
|
+
} from "./generate";
|
|
52
|
+
|
|
46
53
|
export * as clients from "./clients";
|
|
47
54
|
export * as agents from "./agents";
|
|
48
55
|
export * as services from "./services";
|
|
@@ -60,11 +67,16 @@ export { ConversionService } from "./services/conversion/ConversionService";
|
|
|
60
67
|
// Export plugin types for external plugins
|
|
61
68
|
export { PluginBase } from "./plugins/PluginBase";
|
|
62
69
|
export { PluginMeta, Plugin, PluginContext } from "./plugins/types";
|
|
70
|
+
export { PluginService } from "./plugins/plugins";
|
|
71
|
+
export { SkillsPlugin } from "./plugins/SkillsPlugin";
|
|
63
72
|
// Export embedding types
|
|
64
73
|
export { MinimalEmbedding, Embeddable } from "./types";
|
|
65
74
|
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// Embed
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
|
|
66
79
|
export async function embed() {
|
|
67
|
-
// load config
|
|
68
80
|
const config = await getConfig();
|
|
69
81
|
const ignorePattern = await getIgnorePattern();
|
|
70
82
|
|
|
@@ -72,7 +84,6 @@ export async function embed() {
|
|
|
72
84
|
config.embeddingModel || EmbeddingModels.openai.EmbeddingAda2;
|
|
73
85
|
|
|
74
86
|
if (!config.embedSources) {
|
|
75
|
-
// No embeddings configured
|
|
76
87
|
return;
|
|
77
88
|
}
|
|
78
89
|
|
|
@@ -81,6 +92,10 @@ export async function embed() {
|
|
|
81
92
|
}
|
|
82
93
|
}
|
|
83
94
|
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// Purge
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
|
|
84
99
|
export async function purge(globPath: string) {
|
|
85
100
|
const files = globSync(globPath);
|
|
86
101
|
const embeddings = await getConfiguredEmbeddingMap();
|
|
@@ -107,6 +122,10 @@ export async function purge(globPath: string) {
|
|
|
107
122
|
}
|
|
108
123
|
}
|
|
109
124
|
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
// Upload
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
|
|
110
129
|
export async function upload() {
|
|
111
130
|
const config = await getConfig();
|
|
112
131
|
const { AwsS3, Embeddings, knowhowApiClient } = services();
|
|
@@ -124,10 +143,6 @@ export async function upload() {
|
|
|
124
143
|
}
|
|
125
144
|
const items = JSON.parse(await readFile(source.output, "utf8"));
|
|
126
145
|
const { name: embeddingName } = path.parse(source.output);
|
|
127
|
-
const data = {
|
|
128
|
-
embeddingName,
|
|
129
|
-
items,
|
|
130
|
-
};
|
|
131
146
|
|
|
132
147
|
if (Embeddings.hasResolver(source.remoteType) && source.remoteType !== "knowhow") {
|
|
133
148
|
console.log(
|
|
@@ -142,7 +157,6 @@ export async function upload() {
|
|
|
142
157
|
if (!source.remoteId) {
|
|
143
158
|
throw new Error("remoteId is required for knowhow uploads");
|
|
144
159
|
}
|
|
145
|
-
// Warn if the local embeddingModel differs from the one stored on the backend
|
|
146
160
|
try {
|
|
147
161
|
const remoteEmbedding = await knowhowApiClient.getOrgEmbedding(source.remoteId);
|
|
148
162
|
const localModel = config.embeddingModel || EmbeddingModels.openai.EmbeddingAda2;
|
|
@@ -157,12 +171,11 @@ export async function upload() {
|
|
|
157
171
|
);
|
|
158
172
|
}
|
|
159
173
|
} catch (e) {
|
|
160
|
-
// Non-fatal
|
|
174
|
+
// Non-fatal
|
|
161
175
|
}
|
|
162
176
|
const url = await knowhowApiClient.getPresignedUploadUrl(source);
|
|
163
177
|
console.log("Uploading to", url);
|
|
164
178
|
await AwsS3.uploadToPresignedUrl(url, source.output);
|
|
165
|
-
// Sync config metadata back to the backend DB
|
|
166
179
|
await knowhowApiClient.updateEmbeddingMetadata(source.remoteId, {
|
|
167
180
|
inputGlob: source.input,
|
|
168
181
|
outputPath: source.output,
|
|
@@ -181,201 +194,9 @@ export async function upload() {
|
|
|
181
194
|
}
|
|
182
195
|
}
|
|
183
196
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
* - Standard glob patterns (e.g. "src/**\/*.ts")
|
|
188
|
-
* - Brace expansion (e.g. "{src/a.ts,src/b.ts}")
|
|
189
|
-
* - Comma-separated file paths (e.g. "src/a.ts,src/b.ts") — auto-converted to brace expansion
|
|
190
|
-
* - Mixed comma-separated list with globs (e.g. "src/a.ts,src/commands/**\/*.ts")
|
|
191
|
-
*/
|
|
192
|
-
function normalizeInputPattern(input: string): string {
|
|
193
|
-
// If it already has braces, use as-is (already brace-expanded)
|
|
194
|
-
if (input.includes("{")) {
|
|
195
|
-
return input;
|
|
196
|
-
}
|
|
197
|
-
// If it contains commas, treat as comma-separated list and wrap in braces
|
|
198
|
-
// This also handles the mixed case: "src/a.ts,src/commands/**/*.ts"
|
|
199
|
-
if (input.includes(",")) {
|
|
200
|
-
const parts = input.split(",").map((p) => p.trim());
|
|
201
|
-
return `{${parts.join(",")}}`;
|
|
202
|
-
}
|
|
203
|
-
return input;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export async function generate(): Promise<void> {
|
|
207
|
-
const config = await getConfig();
|
|
208
|
-
for (const source of config.sources) {
|
|
209
|
-
console.log("Generating", source.input, "to", source.output);
|
|
210
|
-
if (source.kind === "file" || !source.kind) {
|
|
211
|
-
const files = globSync(normalizeInputPattern(source.input));
|
|
212
|
-
const prompt = await loadPrompt(source.prompt);
|
|
213
|
-
|
|
214
|
-
if (source.output.endsWith("/")) {
|
|
215
|
-
await handleMultiOutputGeneration(
|
|
216
|
-
source.model,
|
|
217
|
-
source.input,
|
|
218
|
-
files,
|
|
219
|
-
prompt,
|
|
220
|
-
source.output,
|
|
221
|
-
source.outputExt,
|
|
222
|
-
source.outputName,
|
|
223
|
-
source.kind,
|
|
224
|
-
source.agent
|
|
225
|
-
);
|
|
226
|
-
} else {
|
|
227
|
-
await handleSingleOutputGeneration(
|
|
228
|
-
source.model,
|
|
229
|
-
files,
|
|
230
|
-
prompt,
|
|
231
|
-
source.output,
|
|
232
|
-
source.kind,
|
|
233
|
-
source.agent
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
} else {
|
|
237
|
-
await handleAllKindsGeneration(source);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
async function handleAllKindsGeneration(source: GenerationSource) {
|
|
243
|
-
const { Plugins } = services();
|
|
244
|
-
const { kind, input } = source;
|
|
245
|
-
if (Plugins.isPlugin(kind)) {
|
|
246
|
-
const data = await Plugins.call(kind, input);
|
|
247
|
-
if (source.output.endsWith("/")) {
|
|
248
|
-
throw new Error(`Plugin ${kind} can only output to a single file`);
|
|
249
|
-
}
|
|
250
|
-
await writeFile(source.output, data);
|
|
251
|
-
}
|
|
252
|
-
return handleFileKindGeneration(source);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
async function handleFileKindGeneration(source: GenerationSource) {
|
|
256
|
-
const prompt = await loadPrompt(source.prompt);
|
|
257
|
-
const files = globSync(normalizeInputPattern(source.input));
|
|
258
|
-
console.log("Analyzing files: ", files);
|
|
259
|
-
|
|
260
|
-
if (source.output.endsWith("/")) {
|
|
261
|
-
await handleMultiOutputGeneration(
|
|
262
|
-
source.model,
|
|
263
|
-
source.input,
|
|
264
|
-
files,
|
|
265
|
-
prompt,
|
|
266
|
-
source.output,
|
|
267
|
-
source.outputExt,
|
|
268
|
-
source.outputName,
|
|
269
|
-
source.kind,
|
|
270
|
-
source.agent
|
|
271
|
-
);
|
|
272
|
-
} else {
|
|
273
|
-
await handleSingleOutputGeneration(
|
|
274
|
-
source.model,
|
|
275
|
-
files,
|
|
276
|
-
prompt,
|
|
277
|
-
source.output,
|
|
278
|
-
source.kind,
|
|
279
|
-
source.agent
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
export async function handleMultiOutputGeneration(
|
|
284
|
-
model: string,
|
|
285
|
-
inputPattern: string,
|
|
286
|
-
files: string[],
|
|
287
|
-
prompt: string,
|
|
288
|
-
output: string,
|
|
289
|
-
outputExt = "mdx",
|
|
290
|
-
outputName?: string,
|
|
291
|
-
kind?: string,
|
|
292
|
-
agent?: string
|
|
293
|
-
) {
|
|
294
|
-
// get the hash of the prompt
|
|
295
|
-
const promptHash = crypto.createHash("md5").update(prompt).digest("hex");
|
|
296
|
-
|
|
297
|
-
// get the files matching the input pattern
|
|
298
|
-
const hashes = await getHashes();
|
|
299
|
-
|
|
300
|
-
const inputPath = inputPattern.includes("**")
|
|
301
|
-
? inputPattern.split("**")[0]
|
|
302
|
-
: "";
|
|
303
|
-
|
|
304
|
-
for (const file of files) {
|
|
305
|
-
// get the hash of the file
|
|
306
|
-
const fileContent = await convertToText(file);
|
|
307
|
-
const fileHash = crypto.createHash("md5").update(fileContent).digest("hex");
|
|
308
|
-
|
|
309
|
-
if (!hashes[file]) {
|
|
310
|
-
hashes[file] = { promptHash: "", fileHash: "" };
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// write the summary to the output file
|
|
314
|
-
const { name, ext, dir } = path.parse(file);
|
|
315
|
-
const nestedFolder = inputPath ? (dir + "/").replace(inputPath, "") : "";
|
|
316
|
-
const outputFolder = path.join(output, nestedFolder);
|
|
317
|
-
|
|
318
|
-
if (!fs.existsSync(outputFolder)) {
|
|
319
|
-
fs.mkdirSync(outputFolder, { recursive: true });
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
const outputFileName = outputName || name;
|
|
323
|
-
const outputFile = path.join(
|
|
324
|
-
outputFolder,
|
|
325
|
-
outputFileName + "." + outputExt
|
|
326
|
-
);
|
|
327
|
-
console.log({ dir, inputPath, nestedFolder, outputFile });
|
|
328
|
-
|
|
329
|
-
const toCheck = [file, outputFile];
|
|
330
|
-
const noChanges = await checkNoFilesChanged(toCheck, promptHash, hashes);
|
|
331
|
-
if (noChanges) {
|
|
332
|
-
console.log("Skipping file", file, "because it hasn't changed");
|
|
333
|
-
continue;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// summarize the file
|
|
337
|
-
console.log("Summarizing", file);
|
|
338
|
-
const summary = prompt
|
|
339
|
-
? await summarizeFile(file, prompt, model, agent)
|
|
340
|
-
: fileContent;
|
|
341
|
-
|
|
342
|
-
console.log("Writing summary to", outputFile);
|
|
343
|
-
await writeFile(outputFile, summary);
|
|
344
|
-
|
|
345
|
-
await saveAllFileHashes(toCheck, promptHash);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
export async function handleSingleOutputGeneration(
|
|
350
|
-
model: string,
|
|
351
|
-
files: string[],
|
|
352
|
-
prompt: string,
|
|
353
|
-
outputFile: string,
|
|
354
|
-
kind?: string,
|
|
355
|
-
agent?: string
|
|
356
|
-
) {
|
|
357
|
-
const hashes = await getHashes();
|
|
358
|
-
const promptHash = crypto.createHash("md5").update(prompt).digest("hex");
|
|
359
|
-
|
|
360
|
-
const filesToCheck = [outputFile, ...files];
|
|
361
|
-
const noChanges = await checkNoFilesChanged(filesToCheck, promptHash, hashes);
|
|
362
|
-
if (noChanges) {
|
|
363
|
-
console.log(`Skipping ${files.length} files because they haven't changed`);
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
console.log("Summarizing", files.length, "files");
|
|
368
|
-
const summary = prompt
|
|
369
|
-
? await summarizeFiles(files, prompt, model, agent)
|
|
370
|
-
: (await Promise.all(files.map(convertToText))).join("\n\n");
|
|
371
|
-
|
|
372
|
-
const fileHash = crypto.createHash("md5").update(summary).digest("hex");
|
|
373
|
-
|
|
374
|
-
console.log("Writing summary to", outputFile);
|
|
375
|
-
await writeFile(outputFile, summary);
|
|
376
|
-
|
|
377
|
-
await saveAllFileHashes(filesToCheck, promptHash);
|
|
378
|
-
}
|
|
197
|
+
// ---------------------------------------------------------------------------
|
|
198
|
+
// Download
|
|
199
|
+
// ---------------------------------------------------------------------------
|
|
379
200
|
|
|
380
201
|
export async function download() {
|
|
381
202
|
const config = await getConfig();
|
|
@@ -419,10 +240,7 @@ export async function download() {
|
|
|
419
240
|
"to",
|
|
420
241
|
destinationPath
|
|
421
242
|
);
|
|
422
|
-
const preSignedUrl = await knowhowApiClient.getPresignedDownloadUrl(
|
|
423
|
-
source
|
|
424
|
-
);
|
|
425
|
-
// Ensure output directory exists
|
|
243
|
+
const preSignedUrl = await knowhowApiClient.getPresignedDownloadUrl(source);
|
|
426
244
|
const outputDir = path.dirname(destinationPath);
|
|
427
245
|
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
|
|
428
246
|
await AwsS3.downloadFromPresignedUrl(preSignedUrl, destinationPath);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { MinimalEmbedding } from "../types";
|
|
2
1
|
import { Plugin, PluginContext, PluginMeta } from "./types";
|
|
3
2
|
|
|
4
3
|
export abstract class PluginBase implements Plugin {
|
|
@@ -66,7 +65,6 @@ export abstract class PluginBase implements Plugin {
|
|
|
66
65
|
/** Mandatory plugin actions ---------------------------------------- */
|
|
67
66
|
/* ------------------------------------------------------------------ */
|
|
68
67
|
abstract call(input?: string): Promise<string>;
|
|
69
|
-
abstract embed(input: string): Promise<MinimalEmbedding[]>;
|
|
70
68
|
}
|
|
71
69
|
|
|
72
70
|
export { PluginMeta, Plugin } from "./types";
|
|
@@ -1,33 +1,11 @@
|
|
|
1
|
-
import { promises as fs, Dirent } from "fs";
|
|
2
|
-
import * as path from "path";
|
|
3
1
|
import { PluginBase, PluginMeta } from "./PluginBase";
|
|
4
2
|
import { Plugin, PluginContext } from "./types";
|
|
5
|
-
import { MinimalEmbedding } from "../types";
|
|
6
|
-
import { getConfig } from "../config";
|
|
7
|
-
|
|
8
|
-
interface SkillMeta {
|
|
9
|
-
name: string;
|
|
10
|
-
description: string;
|
|
11
|
-
filePath: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function parseFrontmatter(content: string): Record<string, string> {
|
|
15
|
-
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
16
|
-
if (!match) return {};
|
|
17
|
-
const result: Record<string, string> = {};
|
|
18
|
-
for (const line of match[1].split("\n")) {
|
|
19
|
-
const [key, ...rest] = line.split(":");
|
|
20
|
-
if (key && rest.length) result[key.trim()] = rest.join(":").trim();
|
|
21
|
-
}
|
|
22
|
-
return result;
|
|
23
|
-
}
|
|
24
3
|
|
|
25
4
|
export class SkillsPlugin extends PluginBase implements Plugin {
|
|
26
5
|
static readonly meta: PluginMeta = {
|
|
27
6
|
key: "skills",
|
|
28
7
|
name: "Skills Plugin",
|
|
29
|
-
description:
|
|
30
|
-
"Scans configured skills directories for SKILL.md files and provides skill content to agents",
|
|
8
|
+
description: "Lists available skills",
|
|
31
9
|
requires: [],
|
|
32
10
|
};
|
|
33
11
|
|
|
@@ -37,114 +15,23 @@ export class SkillsPlugin extends PluginBase implements Plugin {
|
|
|
37
15
|
super(context);
|
|
38
16
|
}
|
|
39
17
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
async
|
|
46
|
-
const
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
if (skillDirs.length === 0) {
|
|
50
|
-
return [];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const skills = await this.scanSkills(skillDirs);
|
|
18
|
+
/**
|
|
19
|
+
* Returns a summary list of in-memory skills (name + description).
|
|
20
|
+
* This is called by the plugin system to inject context into agent prompts.
|
|
21
|
+
* Skills must be loaded into memory first via Behaviors.initFromDisk() at startup.
|
|
22
|
+
*/
|
|
23
|
+
async call(_input?: string): Promise<string> {
|
|
24
|
+
const { Behaviors } = this.context;
|
|
25
|
+
const skills = Behaviors.listSkills();
|
|
54
26
|
|
|
55
27
|
if (skills.length === 0) {
|
|
56
|
-
return
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Check if any skill name appears in the prompt
|
|
60
|
-
const matchedSkills = skills.filter((skill) =>
|
|
61
|
-
userPrompt.toLowerCase().includes(skill.name.toLowerCase())
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
if (matchedSkills.length > 0) {
|
|
65
|
-
// Return full skill content for matched skills
|
|
66
|
-
const results: MinimalEmbedding[] = [];
|
|
67
|
-
for (const skill of matchedSkills) {
|
|
68
|
-
try {
|
|
69
|
-
const content = await fs.readFile(skill.filePath, "utf-8");
|
|
70
|
-
results.push({
|
|
71
|
-
id: skill.filePath,
|
|
72
|
-
text: `## Skill: ${skill.name}\nFile: ${skill.filePath}\n\n${content}`,
|
|
73
|
-
metadata: { filePath: skill.filePath },
|
|
74
|
-
});
|
|
75
|
-
} catch {
|
|
76
|
-
// Skip unreadable files
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return results;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Return skill discovery summary
|
|
83
|
-
const summary = this.buildSkillSummary(skills);
|
|
84
|
-
return [
|
|
85
|
-
{
|
|
86
|
-
id: "skills:summary",
|
|
87
|
-
text: summary,
|
|
88
|
-
metadata: {},
|
|
89
|
-
},
|
|
90
|
-
];
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
private async scanSkills(dirs: string[]): Promise<SkillMeta[]> {
|
|
94
|
-
const skills: SkillMeta[] = [];
|
|
95
|
-
for (const dir of dirs) {
|
|
96
|
-
const resolvedDir = dir.replace(/^~/, process.env.HOME || "");
|
|
97
|
-
try {
|
|
98
|
-
const found = await this.findSkillFiles(resolvedDir);
|
|
99
|
-
skills.push(...found);
|
|
100
|
-
} catch {
|
|
101
|
-
// Skip directories that don't exist or can't be read
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return skills;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
private async findSkillFiles(dir: string): Promise<SkillMeta[]> {
|
|
108
|
-
const skills: SkillMeta[] = [];
|
|
109
|
-
let entries: Dirent[];
|
|
110
|
-
try {
|
|
111
|
-
entries = await fs.readdir(dir, { withFileTypes: true });
|
|
112
|
-
} catch {
|
|
113
|
-
return skills;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
for (const entry of entries) {
|
|
117
|
-
const fullPath = path.join(dir, entry.name);
|
|
118
|
-
if (entry.isDirectory()) {
|
|
119
|
-
const nested = await this.findSkillFiles(fullPath);
|
|
120
|
-
skills.push(...nested);
|
|
121
|
-
} else if (entry.isFile() && entry.name === "SKILL.md") {
|
|
122
|
-
try {
|
|
123
|
-
const content = await fs.readFile(fullPath, "utf-8");
|
|
124
|
-
const frontmatter = parseFrontmatter(content);
|
|
125
|
-
const name = frontmatter.name;
|
|
126
|
-
const description = frontmatter.description || "";
|
|
127
|
-
if (name) {
|
|
128
|
-
skills.push({ name, description, filePath: fullPath });
|
|
129
|
-
}
|
|
130
|
-
} catch {
|
|
131
|
-
// Skip unreadable files
|
|
132
|
-
}
|
|
133
|
-
}
|
|
28
|
+
return "No skills available";
|
|
134
29
|
}
|
|
135
30
|
|
|
136
|
-
return skills;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
private buildSkillSummary(skills: SkillMeta[]): string {
|
|
140
31
|
const lines = ["Available skills:"];
|
|
141
32
|
for (const skill of skills) {
|
|
142
|
-
lines.push(`- ${skill.name}
|
|
33
|
+
lines.push(`- ${skill.name}: ${skill.description}`);
|
|
143
34
|
}
|
|
144
|
-
lines.push("");
|
|
145
|
-
lines.push(
|
|
146
|
-
"To use a skill, reference its name in your request and I will load the full instructions."
|
|
147
|
-
);
|
|
148
35
|
return lines.join("\n");
|
|
149
36
|
}
|
|
150
37
|
}
|
package/src/plugins/language.ts
CHANGED
|
@@ -6,7 +6,6 @@ import { getConfig, getLanguageConfig } from "../config";
|
|
|
6
6
|
import { getEnabledPlugins } from "../types";
|
|
7
7
|
import { PluginBase, PluginMeta } from "./PluginBase";
|
|
8
8
|
import { Plugin, PluginContext } from "./types";
|
|
9
|
-
import { PluginService } from "./plugins";
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* Language Plugin with event-driven context loading
|
package/src/plugins/plugins.ts
CHANGED
|
@@ -14,24 +14,12 @@ import { SkillsPlugin } from "./SkillsPlugin";
|
|
|
14
14
|
import { EventService } from "../services/EventService";
|
|
15
15
|
|
|
16
16
|
export class PluginService {
|
|
17
|
-
|
|
17
|
+
protected pluginMap = new Map<string, Plugin>();
|
|
18
18
|
private events?: EventService;
|
|
19
19
|
|
|
20
20
|
constructor(context: PluginContext) {
|
|
21
21
|
this.events = context.Events;
|
|
22
22
|
context.Plugins = this;
|
|
23
|
-
|
|
24
|
-
// Register migrated PluginBase plugins
|
|
25
|
-
this.pluginMap.set("embeddings", new EmbeddingPlugin(context));
|
|
26
|
-
this.pluginMap.set("vim", new VimPlugin(context));
|
|
27
|
-
this.pluginMap.set("linter", new LinterPlugin(context));
|
|
28
|
-
this.pluginMap.set("language", new LanguagePlugin(context));
|
|
29
|
-
this.pluginMap.set("url", new UrlPlugin(context));
|
|
30
|
-
this.pluginMap.set("git", new GitPlugin(context));
|
|
31
|
-
this.pluginMap.set("tmux", new TmuxPlugin(context));
|
|
32
|
-
this.pluginMap.set("agents-md", new AgentsMdPlugin(context));
|
|
33
|
-
this.pluginMap.set("exec", new ExecPlugin(context));
|
|
34
|
-
this.pluginMap.set("skills", new SkillsPlugin(context));
|
|
35
23
|
}
|
|
36
24
|
|
|
37
25
|
/* -------- lifecycle helpers ------------------------------------ */
|
|
@@ -146,3 +134,26 @@ export class PluginService {
|
|
|
146
134
|
return newPlugin.embed ? newPlugin.embed(userInput) : [];
|
|
147
135
|
}
|
|
148
136
|
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* CliPluginService extends PluginService with the full set of default plugins
|
|
140
|
+
* used by the Knowhow CLI. Use this class when setting up agents for CLI usage.
|
|
141
|
+
* Use the base PluginService when you want a clean slate to register your own plugins.
|
|
142
|
+
*/
|
|
143
|
+
export class CliPluginService extends PluginService {
|
|
144
|
+
constructor(context: PluginContext) {
|
|
145
|
+
super(context);
|
|
146
|
+
|
|
147
|
+
// Register CLI default plugins
|
|
148
|
+
this.pluginMap.set("embeddings", new EmbeddingPlugin(context));
|
|
149
|
+
this.pluginMap.set("vim", new VimPlugin(context));
|
|
150
|
+
this.pluginMap.set("linter", new LinterPlugin(context));
|
|
151
|
+
this.pluginMap.set("language", new LanguagePlugin(context));
|
|
152
|
+
this.pluginMap.set("url", new UrlPlugin(context));
|
|
153
|
+
this.pluginMap.set("git", new GitPlugin(context));
|
|
154
|
+
this.pluginMap.set("tmux", new TmuxPlugin(context));
|
|
155
|
+
this.pluginMap.set("agents-md", new AgentsMdPlugin(context));
|
|
156
|
+
this.pluginMap.set("exec", new ExecPlugin(context));
|
|
157
|
+
this.pluginMap.set("skills", new SkillsPlugin(context));
|
|
158
|
+
}
|
|
159
|
+
}
|
package/src/plugins/types.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { Embeddable, MinimalEmbedding } from "../types";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
AgentService,
|
|
4
|
+
BehaviorsService,
|
|
5
|
+
EventService,
|
|
6
|
+
ToolsService,
|
|
7
|
+
} from "../services";
|
|
3
8
|
import { AIClient } from "../clients";
|
|
4
9
|
import { PluginService } from "./plugins";
|
|
5
10
|
|
|
@@ -13,7 +18,7 @@ export interface PluginMeta {
|
|
|
13
18
|
export interface Plugin {
|
|
14
19
|
callMany(userInput?: string): Promise<string>;
|
|
15
20
|
call(userInput?: string): Promise<string>;
|
|
16
|
-
embed(userInput?: string): Promise<MinimalEmbedding[]>;
|
|
21
|
+
embed?(userInput?: string): Promise<MinimalEmbedding[]>;
|
|
17
22
|
enable(): void;
|
|
18
23
|
disable(): void;
|
|
19
24
|
isEnabled(): boolean;
|
|
@@ -27,4 +32,5 @@ export interface PluginContext {
|
|
|
27
32
|
Clients?: AIClient;
|
|
28
33
|
Tools?: ToolsService;
|
|
29
34
|
Plugins?: PluginService;
|
|
35
|
+
Behaviors?: BehaviorsService;
|
|
30
36
|
}
|