@tyvm/knowhow 0.0.129 → 0.0.130

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.
Files changed (96) hide show
  1. package/package.json +4 -4
  2. package/src/agents/base/base.ts +40 -5
  3. package/src/agents/tools/index.ts +1 -0
  4. package/src/agents/tools/list.ts +2 -0
  5. package/src/agents/tools/skills.ts +79 -0
  6. package/src/chat/modules/AgentModule.ts +84 -1
  7. package/src/cli.ts +4 -0
  8. package/src/clients/anthropic.ts +59 -1
  9. package/src/clients/openai.ts +28 -5
  10. package/src/clients/pricing/openai.ts +3 -0
  11. package/src/clients/types.ts +27 -4
  12. package/src/clients/xai.ts +2 -2
  13. package/src/commands/agent.ts +19 -0
  14. package/src/commands/behaviors.ts +47 -0
  15. package/src/commands/services.ts +6 -2
  16. package/src/commands/skills.ts +71 -0
  17. package/src/index.ts +2 -0
  18. package/src/plugins/PluginBase.ts +0 -2
  19. package/src/plugins/SkillsPlugin.ts +11 -124
  20. package/src/plugins/language.ts +0 -1
  21. package/src/plugins/plugins.ts +24 -13
  22. package/src/plugins/types.ts +8 -2
  23. package/src/services/BehaviorsService.ts +432 -0
  24. package/src/services/KnowhowClient.ts +61 -0
  25. package/src/services/SkillsService.ts +622 -0
  26. package/src/services/Tools.ts +2 -0
  27. package/src/services/index.ts +8 -2
  28. package/src/services/modules/types.ts +2 -0
  29. package/ts_build/package.json +4 -4
  30. package/ts_build/src/agents/base/base.d.ts +9 -1
  31. package/ts_build/src/agents/base/base.js +29 -4
  32. package/ts_build/src/agents/base/base.js.map +1 -1
  33. package/ts_build/src/agents/tools/index.d.ts +1 -0
  34. package/ts_build/src/agents/tools/index.js +1 -0
  35. package/ts_build/src/agents/tools/index.js.map +1 -1
  36. package/ts_build/src/agents/tools/list.js +2 -0
  37. package/ts_build/src/agents/tools/list.js.map +1 -1
  38. package/ts_build/src/agents/tools/skills.d.ts +33 -0
  39. package/ts_build/src/agents/tools/skills.js +62 -0
  40. package/ts_build/src/agents/tools/skills.js.map +1 -0
  41. package/ts_build/src/chat/modules/AgentModule.d.ts +2 -0
  42. package/ts_build/src/chat/modules/AgentModule.js +69 -0
  43. package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
  44. package/ts_build/src/chat/modules/CustomCommandsModule.d.ts +1 -1
  45. package/ts_build/src/cli.js +4 -0
  46. package/ts_build/src/cli.js.map +1 -1
  47. package/ts_build/src/clients/anthropic.d.ts +2 -0
  48. package/ts_build/src/clients/anthropic.js +35 -0
  49. package/ts_build/src/clients/anthropic.js.map +1 -1
  50. package/ts_build/src/clients/openai.d.ts +1 -0
  51. package/ts_build/src/clients/openai.js +25 -5
  52. package/ts_build/src/clients/openai.js.map +1 -1
  53. package/ts_build/src/clients/pricing/openai.js +3 -0
  54. package/ts_build/src/clients/pricing/openai.js.map +1 -1
  55. package/ts_build/src/clients/types.d.ts +6 -1
  56. package/ts_build/src/clients/xai.js +1 -1
  57. package/ts_build/src/clients/xai.js.map +1 -1
  58. package/ts_build/src/commands/agent.js +16 -0
  59. package/ts_build/src/commands/agent.js.map +1 -1
  60. package/ts_build/src/commands/behaviors.d.ts +2 -0
  61. package/ts_build/src/commands/behaviors.js +43 -0
  62. package/ts_build/src/commands/behaviors.js.map +1 -0
  63. package/ts_build/src/commands/services.js +4 -2
  64. package/ts_build/src/commands/services.js.map +1 -1
  65. package/ts_build/src/commands/skills.d.ts +2 -0
  66. package/ts_build/src/commands/skills.js +61 -0
  67. package/ts_build/src/commands/skills.js.map +1 -0
  68. package/ts_build/src/index.d.ts +2 -0
  69. package/ts_build/src/index.js +5 -1
  70. package/ts_build/src/index.js.map +1 -1
  71. package/ts_build/src/plugins/PluginBase.d.ts +0 -2
  72. package/ts_build/src/plugins/PluginBase.js.map +1 -1
  73. package/ts_build/src/plugins/SkillsPlugin.d.ts +1 -6
  74. package/ts_build/src/plugins/SkillsPlugin.js +6 -135
  75. package/ts_build/src/plugins/SkillsPlugin.js.map +1 -1
  76. package/ts_build/src/plugins/language.js.map +1 -1
  77. package/ts_build/src/plugins/plugins.d.ts +4 -1
  78. package/ts_build/src/plugins/plugins.js +17 -11
  79. package/ts_build/src/plugins/plugins.js.map +1 -1
  80. package/ts_build/src/plugins/types.d.ts +3 -2
  81. package/ts_build/src/services/BehaviorsService.d.ts +46 -0
  82. package/ts_build/src/services/BehaviorsService.js +358 -0
  83. package/ts_build/src/services/BehaviorsService.js.map +1 -0
  84. package/ts_build/src/services/KnowhowClient.d.ts +8 -0
  85. package/ts_build/src/services/KnowhowClient.js +20 -0
  86. package/ts_build/src/services/KnowhowClient.js.map +1 -1
  87. package/ts_build/src/services/SkillsService.d.ts +53 -0
  88. package/ts_build/src/services/SkillsService.js +478 -0
  89. package/ts_build/src/services/SkillsService.js.map +1 -0
  90. package/ts_build/src/services/Tools.d.ts +2 -0
  91. package/ts_build/src/services/Tools.js.map +1 -1
  92. package/ts_build/src/services/index.d.ts +3 -0
  93. package/ts_build/src/services/index.js +8 -2
  94. package/ts_build/src/services/index.js.map +1 -1
  95. package/ts_build/src/services/modules/index.d.ts +4 -2
  96. package/ts_build/src/services/modules/types.d.ts +2 -0
@@ -0,0 +1,47 @@
1
+ import { Command } from "commander";
2
+ import { BehaviorsService } from "../services/BehaviorsService";
3
+
4
+ export function addBehaviorsCommand(program: Command): void {
5
+ const behaviors = program
6
+ .command("behaviors")
7
+ .description("Manage org behaviors from the CLI");
8
+
9
+ behaviors
10
+ .command("list")
11
+ .description("List behaviors from backend")
12
+ .option("--skills-only", "Only show skill behaviors")
13
+ .option("--include-internal", "Include platform skills")
14
+ .action(async (opts) => {
15
+ const svc = new BehaviorsService();
16
+ await svc.list({
17
+ skillsOnly: opts.skillsOnly,
18
+ includeInternal: opts.includeInternal,
19
+ });
20
+ });
21
+
22
+ behaviors
23
+ .command("download")
24
+ .description("Download behaviors from backend to .knowhow/behaviors/")
25
+ .option("--skills-only", "Only download skill behaviors")
26
+ .option("--include-internal", "Include platform skills")
27
+ .option(
28
+ "--md",
29
+ "Save as Markdown files with YAML frontmatter instead of JSON"
30
+ )
31
+ .action(async (opts) => {
32
+ const svc = new BehaviorsService();
33
+ await svc.download({
34
+ skillsOnly: opts.skillsOnly,
35
+ includeInternal: opts.includeInternal,
36
+ md: opts.md,
37
+ });
38
+ });
39
+
40
+ behaviors
41
+ .command("upload")
42
+ .description("Upload behaviors from .knowhow/behaviors/ to backend")
43
+ .action(async () => {
44
+ const svc = new BehaviorsService();
45
+ await svc.upload();
46
+ });
47
+ }
@@ -3,6 +3,7 @@ import * as allTools from "../agents/tools";
3
3
  import { LazyToolsService, services, MinimalToolsService } from "../services";
4
4
  import { agents } from "../agents";
5
5
  import { ModulesService } from "../services/modules";
6
+ import { getConfig } from "../config";
6
7
 
7
8
  /**
8
9
  * Shared service setup used by commands that need full services (chat, agent, worker, etc.)
@@ -17,12 +18,14 @@ export async function setupServices() {
17
18
  Plugins,
18
19
  Events,
19
20
  MediaProcessor,
21
+ Behaviors,
20
22
  } = services();
21
23
 
22
-
23
24
  // cli uses LazyTools to keep context slim
24
25
  const Tools = new LazyToolsService();
26
+ await Behaviors.initFromConfig();
25
27
 
28
+ // Give LazyToolsService the same context that ToolService had
26
29
  Tools.setContext({
27
30
  ...AllTools.getContext(),
28
31
  });
@@ -70,7 +73,8 @@ export async function setupServices() {
70
73
  Clients,
71
74
  Tools,
72
75
  MediaProcessor,
73
- Events
76
+ Behaviors,
77
+ Events,
74
78
  });
75
79
 
76
80
  return { Tools, Clients };
@@ -0,0 +1,71 @@
1
+ import { Command } from "commander";
2
+ import { SkillsService } from "../services/SkillsService";
3
+
4
+ export function addSkillsCommand(program: Command): void {
5
+ const skills = program
6
+ .command("skills")
7
+ .description("Manage agent skills from the skills.sh ecosystem");
8
+
9
+ skills
10
+ .command("add <package>")
11
+ .description(
12
+ "Add a skill. Accepts:\n" +
13
+ " owner/repo@skill-name\n" +
14
+ " https://github.com/owner/repo (with --skill <name>)\n" +
15
+ " https://github.com/owner/repo@skill-name"
16
+ )
17
+ .option("--skill <name>", "Skill name when using a GitHub URL as the package argument")
18
+ .option("--global", "Install skill globally to ~/.knowhow/skills/")
19
+ .option(
20
+ "--install-dir <dir>",
21
+ "Custom install directory (overrides default and --global)"
22
+ )
23
+ .action(async (pkg: string, opts: { skill?: string; global?: boolean; installDir?: string }) => {
24
+ const svc = new SkillsService({
25
+ global: opts.global,
26
+ installDir: opts.installDir,
27
+ });
28
+ await svc.add(pkg, opts.skill);
29
+ });
30
+
31
+ skills
32
+ .command("remove <skill>")
33
+ .alias("rm")
34
+ .description("Remove an installed skill by name")
35
+ .option("--global", "Remove from global ~/.knowhow/skills/")
36
+ .action((skillName: string, opts: { global?: boolean }) => {
37
+ const svc = new SkillsService({ global: opts.global });
38
+ svc.remove(skillName);
39
+ });
40
+
41
+ skills
42
+ .command("list")
43
+ .alias("ls")
44
+ .description("List installed skills from the lock file")
45
+ .option("--global", "List skills from global ~/.knowhow/skills/")
46
+ .action((opts: { global?: boolean }) => {
47
+ const svc = new SkillsService({ global: opts.global });
48
+ svc.list();
49
+ });
50
+
51
+ skills
52
+ .command("install")
53
+ .description(
54
+ "Install all skills from skills-lock.json (restores from lock file)"
55
+ )
56
+ .option("--global", "Install from global ~/.knowhow/skills/skills-lock.json")
57
+ .action(async (opts: { global?: boolean }) => {
58
+ const svc = new SkillsService({ global: opts.global });
59
+ await svc.install();
60
+ });
61
+
62
+ skills
63
+ .command("update [skills...]")
64
+ .alias("upgrade")
65
+ .description("Update installed skills to their latest versions")
66
+ .option("--global", "Update skills in global ~/.knowhow/skills/")
67
+ .action(async (skillNames: string[], opts: { global?: boolean }) => {
68
+ const svc = new SkillsService({ global: opts.global });
69
+ await svc.update(skillNames);
70
+ });
71
+ }
package/src/index.ts CHANGED
@@ -60,6 +60,8 @@ export { ConversionService } from "./services/conversion/ConversionService";
60
60
  // Export plugin types for external plugins
61
61
  export { PluginBase } from "./plugins/PluginBase";
62
62
  export { PluginMeta, Plugin, PluginContext } from "./plugins/types";
63
+ export { PluginService } from "./plugins/plugins";
64
+ export { SkillsPlugin } from "./plugins/SkillsPlugin";
63
65
  // Export embedding types
64
66
  export { MinimalEmbedding, Embeddable } from "./types";
65
67
 
@@ -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
- async call(input?: string): Promise<string> {
41
- const result = await this.embed(input || "");
42
- return result.map((e) => e.text).join("\n\n");
43
- }
44
-
45
- async embed(userPrompt: string): Promise<MinimalEmbedding[]> {
46
- const config = await getConfig();
47
- const skillDirs: string[] = (config as any).skills || [];
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} (${skill.filePath}): ${skill.description}`);
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
  }
@@ -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
@@ -14,24 +14,12 @@ import { SkillsPlugin } from "./SkillsPlugin";
14
14
  import { EventService } from "../services/EventService";
15
15
 
16
16
  export class PluginService {
17
- private pluginMap = new Map<string, Plugin>();
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
+ }
@@ -1,5 +1,10 @@
1
1
  import { Embeddable, MinimalEmbedding } from "../types";
2
- import { AgentService, EventService, ToolsService } from "../services";
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
  }