@tyvm/knowhow 0.0.69 → 0.0.70

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 (210) hide show
  1. package/docs/shell-commands.md +174 -0
  2. package/package.json +1 -1
  3. package/src/agents/base/base.ts +1 -3
  4. package/src/agents/developer/developer.ts +21 -13
  5. package/src/agents/tools/agentCall.ts +4 -2
  6. package/src/agents/tools/fileSearch.ts +5 -1
  7. package/src/agents/tools/startAgentTask.ts +131 -22
  8. package/src/chat/CliChatService.ts +57 -11
  9. package/src/chat/modules/AgentModule.ts +72 -12
  10. package/src/chat/modules/CustomCommandsModule.ts +79 -0
  11. package/src/chat/modules/InternalChatModule.ts +11 -1
  12. package/src/chat/modules/ShellCommandModule.ts +96 -0
  13. package/src/chat/modules/index.ts +1 -0
  14. package/src/chat/types.ts +14 -2
  15. package/src/chat.ts +16 -13
  16. package/src/cli.ts +16 -6
  17. package/src/clients/anthropic.ts +41 -90
  18. package/src/clients/gemini.ts +445 -87
  19. package/src/clients/index.ts +125 -0
  20. package/src/clients/knowhow.ts +81 -0
  21. package/src/clients/openai.ts +256 -145
  22. package/src/clients/pricing/anthropic.ts +90 -0
  23. package/src/clients/pricing/google.ts +65 -0
  24. package/src/clients/pricing/index.ts +4 -0
  25. package/src/clients/pricing/openai.ts +134 -0
  26. package/src/clients/pricing/xai.ts +62 -0
  27. package/src/clients/types.ts +170 -1
  28. package/src/clients/xai.ts +275 -46
  29. package/src/config.ts +61 -15
  30. package/src/embeddings.ts +9 -1
  31. package/src/microphone.ts +15 -16
  32. package/src/migrations.ts +151 -0
  33. package/src/plugins/AgentsMdPlugin.ts +118 -0
  34. package/src/plugins/PluginBase.ts +8 -0
  35. package/src/plugins/downloader/downloader.ts +5 -6
  36. package/src/plugins/embedding.ts +10 -8
  37. package/src/plugins/exec.ts +70 -0
  38. package/src/plugins/github.ts +120 -74
  39. package/src/plugins/language.ts +11 -13
  40. package/src/plugins/plugins.ts +25 -4
  41. package/src/plugins/tmux.ts +132 -0
  42. package/src/plugins/types.ts +1 -0
  43. package/src/plugins/vim.ts +14 -1
  44. package/src/services/AgentSyncFs.ts +417 -0
  45. package/src/services/{AgentSynchronization.ts → AgentSyncKnowhowWeb.ts} +2 -2
  46. package/src/services/EventService.ts +0 -1
  47. package/src/services/KnowhowClient.ts +106 -0
  48. package/src/services/index.ts +4 -2
  49. package/src/types.ts +57 -4
  50. package/src/worker.ts +11 -6
  51. package/tests/manual/modalities/README.md +157 -0
  52. package/tests/manual/modalities/google.modalities.test.ts +335 -0
  53. package/tests/manual/modalities/openai.modalities.test.ts +329 -0
  54. package/tests/manual/modalities/streaming.test.ts +260 -0
  55. package/tests/manual/modalities/xai.modalities.test.ts +307 -0
  56. package/tests/plugins/language/languagePlugin-content-triggers.test.ts +5 -5
  57. package/tests/plugins/language/languagePlugin-integration.test.ts +1 -1
  58. package/tests/plugins/language/languagePlugin.test.ts +17 -8
  59. package/ts_build/package.json +1 -1
  60. package/ts_build/src/agents/base/base.js +1 -1
  61. package/ts_build/src/agents/base/base.js.map +1 -1
  62. package/ts_build/src/agents/developer/developer.js +21 -12
  63. package/ts_build/src/agents/developer/developer.js.map +1 -1
  64. package/ts_build/src/agents/tools/agentCall.js +4 -2
  65. package/ts_build/src/agents/tools/agentCall.js.map +1 -1
  66. package/ts_build/src/agents/tools/executeScript/index.d.ts +1 -1
  67. package/ts_build/src/agents/tools/fileSearch.js +2 -1
  68. package/ts_build/src/agents/tools/fileSearch.js.map +1 -1
  69. package/ts_build/src/agents/tools/github/index.d.ts +1 -1
  70. package/ts_build/src/agents/tools/startAgentTask.d.ts +2 -1
  71. package/ts_build/src/agents/tools/startAgentTask.js +118 -17
  72. package/ts_build/src/agents/tools/startAgentTask.js.map +1 -1
  73. package/ts_build/src/chat/CliChatService.d.ts +4 -0
  74. package/ts_build/src/chat/CliChatService.js +39 -5
  75. package/ts_build/src/chat/CliChatService.js.map +1 -1
  76. package/ts_build/src/chat/modules/AgentModule.d.ts +4 -1
  77. package/ts_build/src/chat/modules/AgentModule.js +49 -11
  78. package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
  79. package/ts_build/src/chat/modules/CustomCommandsModule.d.ts +9 -0
  80. package/ts_build/src/chat/modules/CustomCommandsModule.js +58 -0
  81. package/ts_build/src/chat/modules/CustomCommandsModule.js.map +1 -0
  82. package/ts_build/src/chat/modules/InternalChatModule.d.ts +2 -0
  83. package/ts_build/src/chat/modules/InternalChatModule.js +10 -0
  84. package/ts_build/src/chat/modules/InternalChatModule.js.map +1 -1
  85. package/ts_build/src/chat/modules/ShellCommandModule.d.ts +8 -0
  86. package/ts_build/src/chat/modules/ShellCommandModule.js +83 -0
  87. package/ts_build/src/chat/modules/ShellCommandModule.js.map +1 -0
  88. package/ts_build/src/chat/modules/index.d.ts +1 -0
  89. package/ts_build/src/chat/modules/index.js +3 -1
  90. package/ts_build/src/chat/modules/index.js.map +1 -1
  91. package/ts_build/src/chat/types.d.ts +11 -1
  92. package/ts_build/src/chat.js +16 -13
  93. package/ts_build/src/chat.js.map +1 -1
  94. package/ts_build/src/cli.js +10 -3
  95. package/ts_build/src/cli.js.map +1 -1
  96. package/ts_build/src/clients/anthropic.d.ts +5 -1
  97. package/ts_build/src/clients/anthropic.js +18 -91
  98. package/ts_build/src/clients/anthropic.js.map +1 -1
  99. package/ts_build/src/clients/gemini.d.ts +80 -2
  100. package/ts_build/src/clients/gemini.js +336 -74
  101. package/ts_build/src/clients/gemini.js.map +1 -1
  102. package/ts_build/src/clients/index.d.ts +9 -1
  103. package/ts_build/src/clients/index.js +65 -0
  104. package/ts_build/src/clients/index.js.map +1 -1
  105. package/ts_build/src/clients/knowhow.d.ts +9 -1
  106. package/ts_build/src/clients/knowhow.js +43 -0
  107. package/ts_build/src/clients/knowhow.js.map +1 -1
  108. package/ts_build/src/clients/openai.d.ts +9 -1
  109. package/ts_build/src/clients/openai.js +201 -133
  110. package/ts_build/src/clients/openai.js.map +1 -1
  111. package/ts_build/src/clients/pricing/anthropic.d.ts +17 -0
  112. package/ts_build/src/clients/pricing/anthropic.js +93 -0
  113. package/ts_build/src/clients/pricing/anthropic.js.map +1 -0
  114. package/ts_build/src/clients/pricing/google.d.ts +73 -0
  115. package/ts_build/src/clients/pricing/google.js +68 -0
  116. package/ts_build/src/clients/pricing/google.js.map +1 -0
  117. package/ts_build/src/clients/pricing/index.d.ts +4 -0
  118. package/ts_build/src/clients/pricing/index.js +14 -0
  119. package/ts_build/src/clients/pricing/index.js.map +1 -0
  120. package/ts_build/src/clients/pricing/openai.d.ts +7 -0
  121. package/ts_build/src/clients/pricing/openai.js +137 -0
  122. package/ts_build/src/clients/pricing/openai.js.map +1 -0
  123. package/ts_build/src/clients/pricing/xai.d.ts +26 -0
  124. package/ts_build/src/clients/pricing/xai.js +59 -0
  125. package/ts_build/src/clients/pricing/xai.js.map +1 -0
  126. package/ts_build/src/clients/types.d.ts +135 -0
  127. package/ts_build/src/clients/xai.d.ts +9 -1
  128. package/ts_build/src/clients/xai.js +178 -46
  129. package/ts_build/src/clients/xai.js.map +1 -1
  130. package/ts_build/src/config.d.ts +1 -0
  131. package/ts_build/src/config.js +45 -16
  132. package/ts_build/src/config.js.map +1 -1
  133. package/ts_build/src/embeddings.js +8 -1
  134. package/ts_build/src/embeddings.js.map +1 -1
  135. package/ts_build/src/microphone.js +7 -9
  136. package/ts_build/src/microphone.js.map +1 -1
  137. package/ts_build/src/migrations.d.ts +17 -0
  138. package/ts_build/src/migrations.js +86 -0
  139. package/ts_build/src/migrations.js.map +1 -0
  140. package/ts_build/src/plugins/AgentsMdPlugin.d.ts +13 -0
  141. package/ts_build/src/plugins/AgentsMdPlugin.js +118 -0
  142. package/ts_build/src/plugins/AgentsMdPlugin.js.map +1 -0
  143. package/ts_build/src/plugins/PluginBase.d.ts +1 -0
  144. package/ts_build/src/plugins/PluginBase.js +3 -0
  145. package/ts_build/src/plugins/PluginBase.js.map +1 -1
  146. package/ts_build/src/plugins/downloader/downloader.js +5 -5
  147. package/ts_build/src/plugins/downloader/downloader.js.map +1 -1
  148. package/ts_build/src/plugins/embedding.js +9 -8
  149. package/ts_build/src/plugins/embedding.js.map +1 -1
  150. package/ts_build/src/plugins/exec.d.ts +10 -0
  151. package/ts_build/src/plugins/exec.js +56 -0
  152. package/ts_build/src/plugins/exec.js.map +1 -0
  153. package/ts_build/src/plugins/github.js +93 -51
  154. package/ts_build/src/plugins/github.js.map +1 -1
  155. package/ts_build/src/plugins/language.js +14 -11
  156. package/ts_build/src/plugins/language.js.map +1 -1
  157. package/ts_build/src/plugins/plugins.d.ts +1 -0
  158. package/ts_build/src/plugins/plugins.js +19 -1
  159. package/ts_build/src/plugins/plugins.js.map +1 -1
  160. package/ts_build/src/plugins/tmux.d.ts +14 -0
  161. package/ts_build/src/plugins/tmux.js +108 -0
  162. package/ts_build/src/plugins/tmux.js.map +1 -0
  163. package/ts_build/src/plugins/types.d.ts +1 -0
  164. package/ts_build/src/plugins/vim.js +11 -1
  165. package/ts_build/src/plugins/vim.js.map +1 -1
  166. package/ts_build/src/services/AgentSyncFs.d.ts +34 -0
  167. package/ts_build/src/services/AgentSyncFs.js +325 -0
  168. package/ts_build/src/services/AgentSyncFs.js.map +1 -0
  169. package/ts_build/src/services/AgentSyncKnowhowWeb.d.ts +29 -0
  170. package/ts_build/src/services/AgentSyncKnowhowWeb.js +178 -0
  171. package/ts_build/src/services/AgentSyncKnowhowWeb.js.map +1 -0
  172. package/ts_build/src/services/AgentSynchronization.d.ts +1 -1
  173. package/ts_build/src/services/AgentSynchronization.js +3 -3
  174. package/ts_build/src/services/AgentSynchronization.js.map +1 -1
  175. package/ts_build/src/services/EventService.js.map +1 -1
  176. package/ts_build/src/services/KnowhowClient.d.ts +9 -1
  177. package/ts_build/src/services/KnowhowClient.js +58 -0
  178. package/ts_build/src/services/KnowhowClient.js.map +1 -1
  179. package/ts_build/src/services/index.d.ts +2 -1
  180. package/ts_build/src/services/index.js +2 -1
  181. package/ts_build/src/services/index.js.map +1 -1
  182. package/ts_build/src/types.d.ts +26 -1
  183. package/ts_build/src/types.js +45 -4
  184. package/ts_build/src/types.js.map +1 -1
  185. package/ts_build/src/utils/PersistentInputManager.d.ts +28 -0
  186. package/ts_build/src/utils/PersistentInputManager.js +293 -0
  187. package/ts_build/src/utils/PersistentInputManager.js.map +1 -0
  188. package/ts_build/src/worker.js +2 -2
  189. package/ts_build/src/worker.js.map +1 -1
  190. package/ts_build/tests/manual/modalities/google.modalities.test.d.ts +1 -0
  191. package/ts_build/tests/manual/modalities/google.modalities.test.js +252 -0
  192. package/ts_build/tests/manual/modalities/google.modalities.test.js.map +1 -0
  193. package/ts_build/tests/manual/modalities/openai.modalities.test.d.ts +1 -0
  194. package/ts_build/tests/manual/modalities/openai.modalities.test.js +252 -0
  195. package/ts_build/tests/manual/modalities/openai.modalities.test.js.map +1 -0
  196. package/ts_build/tests/manual/modalities/streaming.test.d.ts +1 -0
  197. package/ts_build/tests/manual/modalities/streaming.test.js +206 -0
  198. package/ts_build/tests/manual/modalities/streaming.test.js.map +1 -0
  199. package/ts_build/tests/manual/modalities/xai.modalities.test.d.ts +1 -0
  200. package/ts_build/tests/manual/modalities/xai.modalities.test.js +226 -0
  201. package/ts_build/tests/manual/modalities/xai.modalities.test.js.map +1 -0
  202. package/ts_build/tests/manual/persistent-input-test.d.ts +1 -0
  203. package/ts_build/tests/manual/persistent-input-test.js +35 -0
  204. package/ts_build/tests/manual/persistent-input-test.js.map +1 -0
  205. package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js +5 -5
  206. package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js.map +1 -1
  207. package/ts_build/tests/plugins/language/languagePlugin-integration.test.js +1 -1
  208. package/ts_build/tests/plugins/language/languagePlugin-integration.test.js.map +1 -1
  209. package/ts_build/tests/plugins/language/languagePlugin.test.js +17 -7
  210. package/ts_build/tests/plugins/language/languagePlugin.test.js.map +1 -1
@@ -0,0 +1,118 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import { PluginBase, PluginMeta } from "./PluginBase";
4
+ import { Plugin, PluginContext } from "./types";
5
+ import { MinimalEmbedding } from "../types";
6
+
7
+ /**
8
+ * AgentsMdPlugin - Traverses directory tree upward from edited files to find
9
+ * agents.md files and alerts the agent about their presence.
10
+ */
11
+ export class AgentsMdPlugin extends PluginBase implements Plugin {
12
+ static readonly meta: PluginMeta = {
13
+ key: "agents-md",
14
+ name: "AgentsMd Plugin",
15
+ description:
16
+ "Alerts the agent when an agents.md file is found near an edited file",
17
+ requires: [],
18
+ };
19
+
20
+ meta = AgentsMdPlugin.meta;
21
+
22
+ constructor(context: PluginContext) {
23
+ super(context);
24
+ this.setupEventHandlers();
25
+ }
26
+
27
+ private setupEventHandlers() {
28
+ const events = this.context?.Events;
29
+ if (!events) return;
30
+
31
+ const fileEvents = [
32
+ "file:pre-write",
33
+ "file:post-write",
34
+ "file:write",
35
+ "file:edit",
36
+ ];
37
+
38
+ fileEvents.forEach((eventType) => {
39
+ events.on(eventType, async (eventData: any) => {
40
+ await this.handleFileEvent(eventData);
41
+ });
42
+ });
43
+ }
44
+
45
+ private findAgentsMd(startPath: string): string | null {
46
+ // Resolve to absolute path
47
+ const absoluteStart = path.isAbsolute(startPath)
48
+ ? startPath
49
+ : path.resolve(startPath);
50
+
51
+ // Start from the directory of the file
52
+ let currentDir = fs.existsSync(absoluteStart)
53
+ ? fs.statSync(absoluteStart).isDirectory()
54
+ ? absoluteStart
55
+ : path.dirname(absoluteStart)
56
+ : path.dirname(absoluteStart);
57
+
58
+ const root = path.parse(currentDir).root;
59
+
60
+ while (true) {
61
+ const agentsMdPath = path.join(currentDir, "agents.md");
62
+ if (fs.existsSync(agentsMdPath)) {
63
+ return agentsMdPath;
64
+ }
65
+
66
+ // Stop at filesystem root
67
+ if (currentDir === root) {
68
+ break;
69
+ }
70
+
71
+ const parentDir = path.dirname(currentDir);
72
+ // If we can't go up anymore, stop
73
+ if (parentDir === currentDir) {
74
+ break;
75
+ }
76
+ currentDir = parentDir;
77
+ }
78
+
79
+ return null;
80
+ }
81
+
82
+ private async handleFileEvent(eventData: any) {
83
+ try {
84
+ const filePath = eventData?.filePath || eventData?.path || eventData;
85
+ if (!filePath || typeof filePath !== "string") {
86
+ return;
87
+ }
88
+
89
+ const agentsMdPath = this.findAgentsMd(filePath);
90
+ if (!agentsMdPath) {
91
+ return;
92
+ }
93
+
94
+ const events = this.context?.Events;
95
+ if (!events) return;
96
+
97
+ const alertMessage = `There is an agents.md file detected near the edited file at \`${agentsMdPath}\`. You should read it if you haven't already.`;
98
+
99
+ events.emit("agent:msg", alertMessage);
100
+ } catch (error) {
101
+ console.error("AGENTS-MD PLUGIN: Error handling file event:", error);
102
+ }
103
+ }
104
+
105
+ async call(input?: string): Promise<string> {
106
+ if (input) {
107
+ const agentsMdPath = this.findAgentsMd(input);
108
+ if (agentsMdPath) {
109
+ return `There is an agents.md file detected near \`${input}\` at \`${agentsMdPath}\`. You should read it if you haven't already.`;
110
+ }
111
+ }
112
+ return "";
113
+ }
114
+
115
+ async embed(input: string): Promise<MinimalEmbedding[]> {
116
+ return [];
117
+ }
118
+ }
@@ -43,6 +43,14 @@ export abstract class PluginBase implements Plugin {
43
43
  return true; // subclasses override if needed
44
44
  }
45
45
 
46
+ /* ------------------------------------------------------------------ */
47
+ /** Default callMany implementation - delegates to call ------------ */
48
+ /* ------------------------------------------------------------------ */
49
+ async callMany(input?: string): Promise<string> {
50
+ // Default behavior: callMany just calls call
51
+ return this.call(input);
52
+ }
53
+
46
54
  /* ------------------------------------------------------------------ */
47
55
  /** Mandatory plugin actions ---------------------------------------- */
48
56
  /* ------------------------------------------------------------------ */
@@ -5,10 +5,8 @@ import Logger from "progress-estimator";
5
5
  import { DownloadInfo, KeyframeInfo, TranscriptChunk } from "./types";
6
6
  import { visionTool } from "../../agents/tools/visionTool";
7
7
  import { execAsync, fileExists, readFile, mkdir } from "../../utils";
8
- import OpenAI from "openai";
9
8
  import { Clients } from "../../clients";
10
9
  import { Models } from "../../types";
11
- import { openai } from "../../ai";
12
10
 
13
11
  const logger = Logger();
14
12
 
@@ -132,7 +130,6 @@ export class DownloaderService {
132
130
  }
133
131
 
134
132
  const allTranscripts = [];
135
- const openAi = openai();
136
133
  for (const file of files) {
137
134
  const chunkName = path.parse(file).name;
138
135
  const chunkTranscriptPath = path.join(
@@ -159,9 +156,11 @@ export class DownloaderService {
159
156
  }
160
157
 
161
158
  console.log("Transcribing", file);
162
- const transcript = await openAi.audio.transcriptions
163
- .create({
164
- file: fs.createReadStream(file),
159
+ const fileBuffer = fs.readFileSync(file);
160
+ const transcript = await this.clients
161
+ .createAudioTranscription("openai", {
162
+ file: fileBuffer,
163
+ fileName: path.basename(file),
165
164
  model: "whisper-1",
166
165
  })
167
166
  .catch((e) => {
@@ -7,7 +7,7 @@ import {
7
7
  } from "../embeddings";
8
8
 
9
9
  import { PluginBase, PluginMeta } from "./PluginBase";
10
- import { embed as embedFunction } from "../index";
10
+ import { spawn } from "child_process";
11
11
 
12
12
  export class EmbeddingPlugin extends PluginBase {
13
13
  static readonly meta: PluginMeta = {
@@ -30,17 +30,19 @@ export class EmbeddingPlugin extends PluginBase {
30
30
  }
31
31
 
32
32
  /**
33
- * Handle file:post-edit events by triggering embedding
33
+ * Handle file:post-edit events by triggering embedding in a separate process
34
34
  * @param payload The event payload containing filePath
35
35
  * @returns Status message about embedding operation
36
36
  */
37
37
  async handleFilePostEdit(payload: { filePath: string }): Promise<string> {
38
- try {
39
- await embedFunction();
40
- return "Embedding completed successfully";
41
- } catch (error) {
42
- return `Embedding failed: ${error.message}`;
43
- }
38
+ const child = spawn("knowhow", ["embed"], {
39
+ detached: true,
40
+ stdio: "ignore",
41
+ cwd: process.cwd(),
42
+ });
43
+ child.unref();
44
+ console.log(`EMBEDDING PLUGIN: Started 'knowhow embed' in background (pid: ${child.pid})`);
45
+ return "Embedding started in background process";
44
46
  }
45
47
 
46
48
  async call(userPrompt: string): Promise<string> {
@@ -0,0 +1,70 @@
1
+ import { PluginBase, PluginMeta } from "./PluginBase";
2
+ import { PluginContext } from "./types";
3
+ import { execSync } from "child_process";
4
+
5
+ /**
6
+ * Exec Plugin - Execute shell commands from language config
7
+ * This allows language config entries to trigger shell commands
8
+ */
9
+ export class ExecPlugin extends PluginBase {
10
+ static readonly meta: PluginMeta = {
11
+ key: "exec",
12
+ name: "Exec Plugin",
13
+ requires: [],
14
+ };
15
+
16
+ meta = ExecPlugin.meta;
17
+
18
+ constructor(context: PluginContext) {
19
+ super(context);
20
+ }
21
+
22
+ async callMany(input?: string): Promise<string> {
23
+ // Only execute during callMany if input starts with ! or /!
24
+ if (!input) {
25
+ return "";
26
+ }
27
+ const trimmed = input.trim();
28
+ if (trimmed.startsWith("!") || trimmed.startsWith("/!")) {
29
+ return this.call(input);
30
+ }
31
+ return "";
32
+ }
33
+
34
+ async call(input: string): Promise<string> {
35
+ // Input should be the command to execute
36
+ const command = input.trim();
37
+
38
+ if (!command) {
39
+ return "EXEC PLUGIN: No command provided";
40
+ }
41
+
42
+ try {
43
+ console.log(`EXEC PLUGIN: Executing: ${command}`);
44
+
45
+ // Execute the command
46
+ const result = execSync(command, {
47
+ encoding: "utf8",
48
+ cwd: process.cwd(),
49
+ maxBuffer: 10 * 1024 * 1024, // 10MB buffer
50
+ });
51
+
52
+ return `EXEC PLUGIN: Command output from \`${command}\`:\n\`\`\`\n${result}\n\`\`\``;
53
+ } catch (error: any) {
54
+ const errorMessage = error.message;
55
+ const stderr = error.stderr || "";
56
+ const stdout = error.stdout || "";
57
+
58
+ console.error(`EXEC PLUGIN: Command failed: ${errorMessage}`);
59
+ if (stderr) {
60
+ console.error(stderr);
61
+ }
62
+
63
+ return `EXEC PLUGIN: Command \`${command}\` failed:\n\`\`\`\n${stdout}\n${stderr}\n${errorMessage}\n\`\`\``;
64
+ }
65
+ }
66
+
67
+ async embed(input: string) {
68
+ return [];
69
+ }
70
+ }
@@ -74,29 +74,45 @@ export class GitHubPlugin extends PluginBase {
74
74
  }
75
75
 
76
76
  async getDiff(url: string) {
77
- const { owner, repo, pullNumber } = this.parseUrl(url);
78
- console.log(
79
- `GITHUB PLUGIN: Loading diff for ${owner}/${repo}#${pullNumber}`
80
- );
81
- const { data: diff } = await this.octokit.rest.pulls.get({
82
- owner,
83
- repo,
84
- pull_number: parseInt(pullNumber, 10),
85
- mediaType: {
86
- format: "diff",
87
- },
88
- });
77
+ try {
78
+ const { owner, repo, pullNumber } = this.parseUrl(url);
79
+ console.log(
80
+ `GITHUB PLUGIN: Loading diff for ${owner}/${repo}#${pullNumber}`
81
+ );
82
+ const { data: diff } = await this.octokit.rest.pulls.get({
83
+ owner,
84
+ repo,
85
+ pull_number: parseInt(pullNumber, 10),
86
+ mediaType: {
87
+ format: "diff",
88
+ },
89
+ });
89
90
 
90
- return diff;
91
+ return diff;
92
+ } catch (error) {
93
+ console.error(`GITHUB PLUGIN: Failed to get diff for ${url}:`, error.message);
94
+ if (error.status === 401) {
95
+ console.error("GITHUB PLUGIN: Authentication failed. Please check your GITHUB_TOKEN.");
96
+ }
97
+ return null;
98
+ }
91
99
  }
92
100
 
93
- getPR(url: string) {
94
- const { owner, repo, pullNumber } = this.parseUrl(url);
95
- return this.octokit.rest.pulls.get({
96
- owner,
97
- repo,
98
- pull_number: parseInt(pullNumber, 10),
99
- });
101
+ async getPR(url: string) {
102
+ try {
103
+ const { owner, repo, pullNumber } = this.parseUrl(url);
104
+ return await this.octokit.rest.pulls.get({
105
+ owner,
106
+ repo,
107
+ pull_number: parseInt(pullNumber, 10),
108
+ });
109
+ } catch (error) {
110
+ console.error(`GITHUB PLUGIN: Failed to get PR for ${url}:`, error.message);
111
+ if (error.status === 401) {
112
+ console.error("GITHUB PLUGIN: Authentication failed. Please check your GITHUB_TOKEN.");
113
+ }
114
+ return null;
115
+ }
100
116
  }
101
117
 
102
118
  getLengthOfHunks(hunks: ReturnType<typeof parseHunks>) {
@@ -110,37 +126,49 @@ export class GitHubPlugin extends PluginBase {
110
126
  async getParsedDiffs(urls: string[]) {
111
127
  return Promise.all(
112
128
  urls.map(async (url) => {
113
- const diff = await this.getDiff(url);
114
- let parsed = parseHunks(diff.toString());
115
-
116
- console.log(`GITHUB PLUGIN: Parsed ${parsed.length} hunks`);
117
-
118
- const averageHunkSize =
119
- parsed.reduce((acc, hunk) => acc + hunk.lines.length, 0) /
120
- parsed.length;
121
-
122
- const totalCharacters = parsed
123
- .flatMap((hunk) => [...hunk.additions, ...hunk.subtractions])
124
- .reduce((acc, line) => acc + line.length, 0);
125
-
126
- console.log(
127
- `GITHUB PLUGIN: Average hunk size: ${averageHunkSize}, total characters: ${totalCharacters}`
128
- );
129
-
130
- const MAX_CHARACTERS = 10000;
131
- const average = MAX_CHARACTERS / averageHunkSize;
132
- const PER_HUNK_LIMIT = Math.max(average, 2000);
133
-
134
- parsed = parsed.filter((hunk) => {
135
- return this.getLengthOfHunks([hunk]) <= PER_HUNK_LIMIT;
136
- });
137
-
138
- console.log(
139
- `GITHUB PLUGIN: Filtered to ${
140
- parsed.length
141
- } hunks. ${this.getLengthOfHunks(parsed)} characters`
142
- );
143
- return parsed;
129
+ try {
130
+ const diff = await this.getDiff(url);
131
+
132
+ // If getDiff returned null (auth error), skip this URL
133
+ if (!diff) {
134
+ console.log(`GITHUB PLUGIN: Skipping ${url} due to error`);
135
+ return null;
136
+ }
137
+
138
+ let parsed = parseHunks(diff.toString());
139
+
140
+ console.log(`GITHUB PLUGIN: Parsed ${parsed.length} hunks`);
141
+
142
+ const averageHunkSize =
143
+ parsed.reduce((acc, hunk) => acc + hunk.lines.length, 0) /
144
+ parsed.length;
145
+
146
+ const totalCharacters = parsed
147
+ .flatMap((hunk) => [...hunk.additions, ...hunk.subtractions])
148
+ .reduce((acc, line) => acc + line.length, 0);
149
+
150
+ console.log(
151
+ `GITHUB PLUGIN: Average hunk size: ${averageHunkSize}, total characters: ${totalCharacters}`
152
+ );
153
+
154
+ const MAX_CHARACTERS = 10000;
155
+ const average = MAX_CHARACTERS / averageHunkSize;
156
+ const PER_HUNK_LIMIT = Math.max(average, 2000);
157
+
158
+ parsed = parsed.filter((hunk) => {
159
+ return this.getLengthOfHunks([hunk]) <= PER_HUNK_LIMIT;
160
+ });
161
+
162
+ console.log(
163
+ `GITHUB PLUGIN: Filtered to ${
164
+ parsed.length
165
+ } hunks. ${this.getLengthOfHunks(parsed)} characters`
166
+ );
167
+ return parsed;
168
+ } catch (error) {
169
+ console.error(`GITHUB PLUGIN: Error parsing diff for ${url}:`, error.message);
170
+ return null;
171
+ }
144
172
  })
145
173
  );
146
174
  }
@@ -153,30 +181,48 @@ export class GitHubPlugin extends PluginBase {
153
181
  const urls = this.extractUrls(userPrompt);
154
182
 
155
183
  if (urls) {
156
- const prs = [];
157
- for (const url of urls) {
158
- const { owner, repo, pullNumber } = this.parseUrl(url);
159
- const { data: pr } = await this.getPR(url);
160
- const responses = await this.getParsedDiffs(urls);
161
- // Format the diffs in Markdown
162
- const diffStrings = responses.map(hunksToPatch);
163
-
164
- prs.push({
165
- description: pr.title,
166
- url: pr.html_url,
167
- body: pr.body,
168
- author: pr.user.login,
169
- diff: diffStrings,
170
- });
184
+ try {
185
+ const prs = [];
186
+ for (const url of urls) {
187
+ const prResponse = await this.getPR(url);
188
+
189
+ // Skip this PR if we couldn't get its data
190
+ if (!prResponse) {
191
+ console.log(`GITHUB PLUGIN: Skipping ${url} - could not fetch PR data`);
192
+ continue;
193
+ }
194
+
195
+ const { data: pr } = prResponse;
196
+ const responses = await this.getParsedDiffs([url]);
197
+
198
+ // Format the diffs in Markdown
199
+ const diffStrings = responses
200
+ .filter(response => response !== null)
201
+ .map(hunksToPatch);
202
+
203
+ prs.push({
204
+ description: pr.title,
205
+ url: pr.html_url,
206
+ body: pr.body,
207
+ author: pr.user.login,
208
+ diff: diffStrings,
209
+ });
210
+ }
211
+
212
+ if (prs.length === 0) {
213
+ return "GITHUB PLUGIN: Could not fetch any pull request data. Please check your GITHUB_TOKEN and permissions.";
214
+ }
215
+
216
+ const context = `GITHUB PLUGIN: These ${urls} have automatically been expanded to include the changes:\n\n${JSON.stringify(
217
+ prs,
218
+ null,
219
+ 2
220
+ )}`;
221
+ console.log(context);
222
+ return context;
223
+ } catch (error) {
224
+ return `GITHUB PLUGIN: Error fetching pull request data: ${error.message}`;
171
225
  }
172
-
173
- const context = `GITHUB PLUGIN: These ${urls} have automatically been expanded to include the changes:\n\n${JSON.stringify(
174
- prs,
175
- null,
176
- 2
177
- )}`;
178
- console.log(context);
179
- return context;
180
226
  }
181
227
 
182
228
  return "GITHUB PLUGIN: No pull request URLs detected.";
@@ -3,6 +3,7 @@ import { minimatch } from "minimatch";
3
3
  import { EventService } from "../services/EventService";
4
4
  import { Language } from "../types";
5
5
  import { getConfig, getLanguageConfig } from "../config";
6
+ import { getEnabledPlugins } from "../types";
6
7
  import { PluginBase, PluginMeta } from "./PluginBase";
7
8
  import { Plugin, PluginContext } from "./types";
8
9
  import { GitHubPlugin } from "./github";
@@ -115,7 +116,7 @@ export class LanguagePlugin extends PluginBase implements Plugin {
115
116
 
116
117
  const plugins = this.context.Plugins.listPlugins();
117
118
  for (const plugin of plugins) {
118
- if (config.plugins.includes(plugin)) {
119
+ if (getEnabledPlugins(config.plugins).includes(plugin)) {
119
120
  const matchingSources = sources.filter((s) => s.kind === plugin);
120
121
  if (matchingSources.length === 0) {
121
122
  continue;
@@ -258,15 +259,15 @@ export class LanguagePlugin extends PluginBase implements Plugin {
258
259
  const terms = Object.keys(languageConfig);
259
260
 
260
261
  // Find all matching terms in the userPrompt using glob patterns
261
- const matchingTerms = terms.filter((term) =>
262
- term.split(",").some((pattern) => {
262
+ const matchingTerms = terms.filter((term) => {
263
+ return term.split(",").some((pattern) => {
263
264
  const trimmedPattern = pattern.trim();
264
- // Use minimatch for file patterns, fallback to string contains for non-glob patterns
265
+ // Use minimatch for glob patterns, fallback to string contains for simple patterns
265
266
  return trimmedPattern.includes("*")
266
267
  ? minimatch(userPrompt, trimmedPattern)
267
268
  : userPrompt.toLowerCase().includes(trimmedPattern.toLowerCase());
268
- })
269
- );
269
+ });
270
+ });
270
271
  return matchingTerms;
271
272
  }
272
273
 
@@ -281,16 +282,13 @@ export class LanguagePlugin extends PluginBase implements Plugin {
281
282
  // Use the extracted resolveSources method
282
283
  const contexts = await this.resolveSources(matchingTerms);
283
284
 
284
- if (!matchingTerms || !matchingTerms.length) {
285
- return "LANGUAGE PLUGIN: No matching terms found";
286
- }
287
-
288
285
  console.log("LANGUAGE PLUGIN: Matching terms found:", matchingTerms);
289
286
 
287
+ const output = contexts.every((c) => typeof c === "string")
288
+ ? contexts.join("")
289
+ : JSON.stringify(contexts, null, 2);
290
290
  // Return the file contents in a format that can be added to the prompt context
291
- return `LANGUAGE PLUGIN: The user mentioned these terms triggering contextual expansions ${matchingTerms} expanded to: ${JSON.stringify(
292
- contexts
293
- )}
291
+ return `LANGUAGE PLUGIN: The user mentioned these terms triggering contextual expansions ${matchingTerms} expanded to: ${output}
294
292
  These terms are directly related to what the user is asking about so be sure to contextualize your response to this information.
295
293
  `;
296
294
  }
@@ -12,6 +12,11 @@ import { DownloaderPlugin } from "./downloader/plugin";
12
12
  import { FigmaPlugin } from "./figma";
13
13
  import { UrlPlugin } from "./url";
14
14
  import { GitPlugin } from "./GitPlugin";
15
+ import { TmuxPlugin } from "./tmux";
16
+ import { AgentsMdPlugin } from "./AgentsMdPlugin";
17
+ import { ExecPlugin } from "./exec";
18
+ import { getConfig } from "../config";
19
+ import { getDisabledPlugins } from "../types";
15
20
 
16
21
  export class PluginService {
17
22
  private pluginMap = new Map<string, Plugin>();
@@ -33,9 +38,9 @@ export class PluginService {
33
38
  this.pluginMap.set("language", new LanguagePlugin(context));
34
39
  this.pluginMap.set("url", new UrlPlugin(context));
35
40
  this.pluginMap.set("git", new GitPlugin(context));
36
-
37
- // Keep legacy plugins for backward compatibility
38
- // These will be removed once all consumers are updated
41
+ this.pluginMap.set("tmux", new TmuxPlugin(context));
42
+ this.pluginMap.set("agents-md", new AgentsMdPlugin(context));
43
+ this.pluginMap.set("exec", new ExecPlugin(context));
39
44
  }
40
45
 
41
46
  /* -------- lifecycle helpers ------------------------------------ */
@@ -96,7 +101,7 @@ export class PluginService {
96
101
  return "";
97
102
  }
98
103
  const calls = plugins.map(async (p) => {
99
- return this.call(p, userInput);
104
+ return this.callManyForPlugin(p, userInput).catch();
100
105
  });
101
106
 
102
107
  const results = await Promise.all(calls);
@@ -119,6 +124,22 @@ export class PluginService {
119
124
  return newPlugin.call(userInput);
120
125
  }
121
126
 
127
+ async callManyForPlugin(kind: string, userInput?: string) {
128
+ // Check new plugin system first
129
+ const newPlugin = this.pluginMap.get(kind);
130
+
131
+ if (!newPlugin) {
132
+ throw new Error(`Plugin ${kind} not found`);
133
+ }
134
+
135
+ const enabled = await newPlugin.isEnabled();
136
+ if (!enabled) {
137
+ console.log(`Plugin ${kind} is disabled, skipping`);
138
+ return "";
139
+ }
140
+ return newPlugin.callMany(userInput);
141
+ }
142
+
122
143
  async embed(kind: string, userInput: string) {
123
144
  // Check new plugin system first
124
145
  const newPlugin = this.pluginMap.get(kind);