@tyvm/knowhow 0.0.68 → 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 (215) hide show
  1. package/docs/shell-commands.md +174 -0
  2. package/package.json +2 -2
  3. package/src/agents/base/base.ts +1 -3
  4. package/src/agents/developer/developer.ts +21 -16
  5. package/src/agents/tools/agentCall.ts +4 -2
  6. package/src/agents/tools/fileSearch.ts +5 -1
  7. package/src/agents/tools/list.ts +41 -37
  8. package/src/agents/tools/startAgentTask.ts +131 -22
  9. package/src/chat/CliChatService.ts +57 -11
  10. package/src/chat/modules/AgentModule.ts +72 -12
  11. package/src/chat/modules/CustomCommandsModule.ts +79 -0
  12. package/src/chat/modules/InternalChatModule.ts +11 -1
  13. package/src/chat/modules/ShellCommandModule.ts +96 -0
  14. package/src/chat/modules/index.ts +1 -0
  15. package/src/chat/types.ts +14 -2
  16. package/src/chat.ts +16 -13
  17. package/src/cli.ts +16 -6
  18. package/src/clients/anthropic.ts +88 -91
  19. package/src/clients/gemini.ts +495 -94
  20. package/src/clients/index.ts +125 -0
  21. package/src/clients/knowhow.ts +81 -0
  22. package/src/clients/openai.ts +256 -145
  23. package/src/clients/pricing/anthropic.ts +90 -0
  24. package/src/clients/pricing/google.ts +65 -0
  25. package/src/clients/pricing/index.ts +4 -0
  26. package/src/clients/pricing/openai.ts +134 -0
  27. package/src/clients/pricing/xai.ts +62 -0
  28. package/src/clients/types.ts +170 -1
  29. package/src/clients/xai.ts +275 -46
  30. package/src/config.ts +61 -15
  31. package/src/embeddings.ts +9 -1
  32. package/src/microphone.ts +15 -16
  33. package/src/migrations.ts +151 -0
  34. package/src/plugins/AgentsMdPlugin.ts +118 -0
  35. package/src/plugins/PluginBase.ts +8 -0
  36. package/src/plugins/downloader/downloader.ts +5 -6
  37. package/src/plugins/embedding.ts +10 -8
  38. package/src/plugins/exec.ts +70 -0
  39. package/src/plugins/github.ts +120 -74
  40. package/src/plugins/language.ts +11 -13
  41. package/src/plugins/plugins.ts +25 -4
  42. package/src/plugins/tmux.ts +132 -0
  43. package/src/plugins/types.ts +1 -0
  44. package/src/plugins/vim.ts +14 -1
  45. package/src/server/index.ts +2 -0
  46. package/src/services/AgentSyncFs.ts +417 -0
  47. package/src/services/{AgentSynchronization.ts → AgentSyncKnowhowWeb.ts} +2 -2
  48. package/src/services/EventService.ts +0 -1
  49. package/src/services/KnowhowClient.ts +106 -0
  50. package/src/services/index.ts +4 -2
  51. package/src/types.ts +57 -4
  52. package/src/worker.ts +25 -2
  53. package/tests/manual/modalities/README.md +157 -0
  54. package/tests/manual/modalities/google.modalities.test.ts +335 -0
  55. package/tests/manual/modalities/openai.modalities.test.ts +329 -0
  56. package/tests/manual/modalities/streaming.test.ts +260 -0
  57. package/tests/manual/modalities/xai.modalities.test.ts +307 -0
  58. package/tests/plugins/language/languagePlugin-content-triggers.test.ts +5 -5
  59. package/tests/plugins/language/languagePlugin-integration.test.ts +1 -1
  60. package/tests/plugins/language/languagePlugin.test.ts +17 -8
  61. package/ts_build/package.json +2 -2
  62. package/ts_build/src/agents/base/base.js +1 -1
  63. package/ts_build/src/agents/base/base.js.map +1 -1
  64. package/ts_build/src/agents/developer/developer.js +21 -15
  65. package/ts_build/src/agents/developer/developer.js.map +1 -1
  66. package/ts_build/src/agents/tools/agentCall.js +4 -2
  67. package/ts_build/src/agents/tools/agentCall.js.map +1 -1
  68. package/ts_build/src/agents/tools/executeScript/index.d.ts +1 -1
  69. package/ts_build/src/agents/tools/fileSearch.js +2 -1
  70. package/ts_build/src/agents/tools/fileSearch.js.map +1 -1
  71. package/ts_build/src/agents/tools/github/index.d.ts +1 -1
  72. package/ts_build/src/agents/tools/list.js +41 -37
  73. package/ts_build/src/agents/tools/list.js.map +1 -1
  74. package/ts_build/src/agents/tools/startAgentTask.d.ts +2 -1
  75. package/ts_build/src/agents/tools/startAgentTask.js +118 -17
  76. package/ts_build/src/agents/tools/startAgentTask.js.map +1 -1
  77. package/ts_build/src/chat/CliChatService.d.ts +4 -0
  78. package/ts_build/src/chat/CliChatService.js +39 -5
  79. package/ts_build/src/chat/CliChatService.js.map +1 -1
  80. package/ts_build/src/chat/modules/AgentModule.d.ts +4 -1
  81. package/ts_build/src/chat/modules/AgentModule.js +49 -11
  82. package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
  83. package/ts_build/src/chat/modules/CustomCommandsModule.d.ts +9 -0
  84. package/ts_build/src/chat/modules/CustomCommandsModule.js +58 -0
  85. package/ts_build/src/chat/modules/CustomCommandsModule.js.map +1 -0
  86. package/ts_build/src/chat/modules/InternalChatModule.d.ts +2 -0
  87. package/ts_build/src/chat/modules/InternalChatModule.js +10 -0
  88. package/ts_build/src/chat/modules/InternalChatModule.js.map +1 -1
  89. package/ts_build/src/chat/modules/ShellCommandModule.d.ts +8 -0
  90. package/ts_build/src/chat/modules/ShellCommandModule.js +83 -0
  91. package/ts_build/src/chat/modules/ShellCommandModule.js.map +1 -0
  92. package/ts_build/src/chat/modules/index.d.ts +1 -0
  93. package/ts_build/src/chat/modules/index.js +3 -1
  94. package/ts_build/src/chat/modules/index.js.map +1 -1
  95. package/ts_build/src/chat/types.d.ts +11 -1
  96. package/ts_build/src/chat.js +16 -13
  97. package/ts_build/src/chat.js.map +1 -1
  98. package/ts_build/src/cli.js +10 -3
  99. package/ts_build/src/cli.js.map +1 -1
  100. package/ts_build/src/clients/anthropic.d.ts +6 -1
  101. package/ts_build/src/clients/anthropic.js +47 -92
  102. package/ts_build/src/clients/anthropic.js.map +1 -1
  103. package/ts_build/src/clients/gemini.d.ts +81 -2
  104. package/ts_build/src/clients/gemini.js +362 -79
  105. package/ts_build/src/clients/gemini.js.map +1 -1
  106. package/ts_build/src/clients/index.d.ts +9 -1
  107. package/ts_build/src/clients/index.js +65 -0
  108. package/ts_build/src/clients/index.js.map +1 -1
  109. package/ts_build/src/clients/knowhow.d.ts +9 -1
  110. package/ts_build/src/clients/knowhow.js +43 -0
  111. package/ts_build/src/clients/knowhow.js.map +1 -1
  112. package/ts_build/src/clients/openai.d.ts +9 -1
  113. package/ts_build/src/clients/openai.js +201 -133
  114. package/ts_build/src/clients/openai.js.map +1 -1
  115. package/ts_build/src/clients/pricing/anthropic.d.ts +17 -0
  116. package/ts_build/src/clients/pricing/anthropic.js +93 -0
  117. package/ts_build/src/clients/pricing/anthropic.js.map +1 -0
  118. package/ts_build/src/clients/pricing/google.d.ts +73 -0
  119. package/ts_build/src/clients/pricing/google.js +68 -0
  120. package/ts_build/src/clients/pricing/google.js.map +1 -0
  121. package/ts_build/src/clients/pricing/index.d.ts +4 -0
  122. package/ts_build/src/clients/pricing/index.js +14 -0
  123. package/ts_build/src/clients/pricing/index.js.map +1 -0
  124. package/ts_build/src/clients/pricing/openai.d.ts +7 -0
  125. package/ts_build/src/clients/pricing/openai.js +137 -0
  126. package/ts_build/src/clients/pricing/openai.js.map +1 -0
  127. package/ts_build/src/clients/pricing/xai.d.ts +26 -0
  128. package/ts_build/src/clients/pricing/xai.js +59 -0
  129. package/ts_build/src/clients/pricing/xai.js.map +1 -0
  130. package/ts_build/src/clients/types.d.ts +135 -0
  131. package/ts_build/src/clients/xai.d.ts +9 -1
  132. package/ts_build/src/clients/xai.js +178 -46
  133. package/ts_build/src/clients/xai.js.map +1 -1
  134. package/ts_build/src/config.d.ts +1 -0
  135. package/ts_build/src/config.js +45 -16
  136. package/ts_build/src/config.js.map +1 -1
  137. package/ts_build/src/embeddings.js +8 -1
  138. package/ts_build/src/embeddings.js.map +1 -1
  139. package/ts_build/src/microphone.js +7 -9
  140. package/ts_build/src/microphone.js.map +1 -1
  141. package/ts_build/src/migrations.d.ts +17 -0
  142. package/ts_build/src/migrations.js +86 -0
  143. package/ts_build/src/migrations.js.map +1 -0
  144. package/ts_build/src/plugins/AgentsMdPlugin.d.ts +13 -0
  145. package/ts_build/src/plugins/AgentsMdPlugin.js +118 -0
  146. package/ts_build/src/plugins/AgentsMdPlugin.js.map +1 -0
  147. package/ts_build/src/plugins/PluginBase.d.ts +1 -0
  148. package/ts_build/src/plugins/PluginBase.js +3 -0
  149. package/ts_build/src/plugins/PluginBase.js.map +1 -1
  150. package/ts_build/src/plugins/downloader/downloader.js +5 -5
  151. package/ts_build/src/plugins/downloader/downloader.js.map +1 -1
  152. package/ts_build/src/plugins/embedding.js +9 -8
  153. package/ts_build/src/plugins/embedding.js.map +1 -1
  154. package/ts_build/src/plugins/exec.d.ts +10 -0
  155. package/ts_build/src/plugins/exec.js +56 -0
  156. package/ts_build/src/plugins/exec.js.map +1 -0
  157. package/ts_build/src/plugins/github.js +93 -51
  158. package/ts_build/src/plugins/github.js.map +1 -1
  159. package/ts_build/src/plugins/language.js +14 -11
  160. package/ts_build/src/plugins/language.js.map +1 -1
  161. package/ts_build/src/plugins/plugins.d.ts +1 -0
  162. package/ts_build/src/plugins/plugins.js +19 -1
  163. package/ts_build/src/plugins/plugins.js.map +1 -1
  164. package/ts_build/src/plugins/tmux.d.ts +14 -0
  165. package/ts_build/src/plugins/tmux.js +108 -0
  166. package/ts_build/src/plugins/tmux.js.map +1 -0
  167. package/ts_build/src/plugins/types.d.ts +1 -0
  168. package/ts_build/src/plugins/vim.js +11 -1
  169. package/ts_build/src/plugins/vim.js.map +1 -1
  170. package/ts_build/src/server/index.js.map +1 -1
  171. package/ts_build/src/services/AgentSyncFs.d.ts +34 -0
  172. package/ts_build/src/services/AgentSyncFs.js +325 -0
  173. package/ts_build/src/services/AgentSyncFs.js.map +1 -0
  174. package/ts_build/src/services/AgentSyncKnowhowWeb.d.ts +29 -0
  175. package/ts_build/src/services/AgentSyncKnowhowWeb.js +178 -0
  176. package/ts_build/src/services/AgentSyncKnowhowWeb.js.map +1 -0
  177. package/ts_build/src/services/AgentSynchronization.d.ts +1 -1
  178. package/ts_build/src/services/AgentSynchronization.js +3 -3
  179. package/ts_build/src/services/AgentSynchronization.js.map +1 -1
  180. package/ts_build/src/services/EventService.js.map +1 -1
  181. package/ts_build/src/services/KnowhowClient.d.ts +9 -1
  182. package/ts_build/src/services/KnowhowClient.js +58 -0
  183. package/ts_build/src/services/KnowhowClient.js.map +1 -1
  184. package/ts_build/src/services/index.d.ts +2 -1
  185. package/ts_build/src/services/index.js +2 -1
  186. package/ts_build/src/services/index.js.map +1 -1
  187. package/ts_build/src/types.d.ts +26 -1
  188. package/ts_build/src/types.js +45 -4
  189. package/ts_build/src/types.js.map +1 -1
  190. package/ts_build/src/utils/PersistentInputManager.d.ts +28 -0
  191. package/ts_build/src/utils/PersistentInputManager.js +293 -0
  192. package/ts_build/src/utils/PersistentInputManager.js.map +1 -0
  193. package/ts_build/src/worker.js +11 -2
  194. package/ts_build/src/worker.js.map +1 -1
  195. package/ts_build/tests/manual/modalities/google.modalities.test.d.ts +1 -0
  196. package/ts_build/tests/manual/modalities/google.modalities.test.js +252 -0
  197. package/ts_build/tests/manual/modalities/google.modalities.test.js.map +1 -0
  198. package/ts_build/tests/manual/modalities/openai.modalities.test.d.ts +1 -0
  199. package/ts_build/tests/manual/modalities/openai.modalities.test.js +252 -0
  200. package/ts_build/tests/manual/modalities/openai.modalities.test.js.map +1 -0
  201. package/ts_build/tests/manual/modalities/streaming.test.d.ts +1 -0
  202. package/ts_build/tests/manual/modalities/streaming.test.js +206 -0
  203. package/ts_build/tests/manual/modalities/streaming.test.js.map +1 -0
  204. package/ts_build/tests/manual/modalities/xai.modalities.test.d.ts +1 -0
  205. package/ts_build/tests/manual/modalities/xai.modalities.test.js +226 -0
  206. package/ts_build/tests/manual/modalities/xai.modalities.test.js.map +1 -0
  207. package/ts_build/tests/manual/persistent-input-test.d.ts +1 -0
  208. package/ts_build/tests/manual/persistent-input-test.js +35 -0
  209. package/ts_build/tests/manual/persistent-input-test.js.map +1 -0
  210. package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js +5 -5
  211. package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js.map +1 -1
  212. package/ts_build/tests/plugins/language/languagePlugin-integration.test.js +1 -1
  213. package/ts_build/tests/plugins/language/languagePlugin-integration.test.js.map +1 -1
  214. package/ts_build/tests/plugins/language/languagePlugin.test.js +17 -7
  215. package/ts_build/tests/plugins/language/languagePlugin.test.js.map +1 -1
@@ -5,6 +5,7 @@
5
5
  import {
6
6
  ChatService,
7
7
  ChatContext,
8
+ CommandResult,
8
9
  ChatCommand,
9
10
  ChatMode,
10
11
  InputMethod,
@@ -112,6 +113,10 @@ export class CliChatService implements ChatService {
112
113
  return this.context;
113
114
  }
114
115
 
116
+ getTools() {
117
+ return this.context.selectedAgent?.tools;
118
+ }
119
+
115
120
  setContext(context: Partial<ChatContext>): void {
116
121
  this.context = { ...this.context, ...context };
117
122
  // Keep chatHistory reference synchronized
@@ -144,6 +149,37 @@ export class CliChatService implements ChatService {
144
149
  return this.commands;
145
150
  }
146
151
 
152
+ /**
153
+ * Get commands available in the current mode
154
+ */
155
+ getCommandsForMode(mode: string): ChatCommand[] {
156
+ return this.commands.filter(
157
+ (cmd) => !cmd.modes || cmd.modes.length === 0 || cmd.modes.includes(mode)
158
+ );
159
+ }
160
+
161
+ getCommandsForActiveModes(): ChatCommand[] {
162
+ const activeModes = this.modes
163
+ .filter((mode) => mode.active)
164
+ .map((mode) => mode.name);
165
+ return this.commands.filter(
166
+ (cmd) =>
167
+ !cmd.modes ||
168
+ cmd.modes.length === 0 ||
169
+ cmd.modes.some((mode) => activeModes.includes(mode))
170
+ );
171
+ }
172
+
173
+ setMode(mode: string): void {
174
+ this.modes.forEach((m) => {
175
+ if (m.name !== "default" && m.name !== mode) {
176
+ m.active = false;
177
+ } else if (m.name === mode) {
178
+ m.active = true;
179
+ }
180
+ });
181
+ }
182
+
147
183
  getModes(): ChatMode[] {
148
184
  return this.modes;
149
185
  }
@@ -154,16 +190,28 @@ export class CliChatService implements ChatService {
154
190
 
155
191
  async processInput(input: string): Promise<boolean> {
156
192
  // Note: Input is added to history via setOnNewHistoryEntry callback when user presses Enter
157
- // Note: this actually sends all commands to modules, first to service takes it
193
+ // Note: this actually sends all commands to modules if not handled by a command
158
194
 
159
195
  // Check if input is a command
160
196
  if (input.startsWith("/")) {
161
197
  const [commandName, ...args] = input.slice(1).split(" ");
162
- const command = this.commands.find((cmd) => cmd.name === commandName);
198
+ const availableCommands = this.getCommandsForActiveModes();
199
+ const command = availableCommands.find((cmd) => cmd.name === commandName);
163
200
 
164
201
  if (command) {
165
- await command.handler(args);
166
- return true;
202
+ const result = await command.handler(args);
203
+
204
+ // If handler returns a CommandResult and it's not handled, pass to modules
205
+ if (result && typeof result === "object" && "handled" in result) {
206
+ if (result.handled) {
207
+ return true;
208
+ }
209
+ // Not handled, use contents if provided or original input
210
+ input = result.contents || input;
211
+ } else {
212
+ // Old-style void handler, consider it handled
213
+ return true;
214
+ }
167
215
  }
168
216
  }
169
217
 
@@ -198,7 +246,7 @@ export class CliChatService implements ChatService {
198
246
 
199
247
  async getInput(
200
248
  prompt: string = "> ",
201
- options: string[] = [],
249
+ options: string[] = []
202
250
  ): Promise<string> {
203
251
  if (this.context.inputMethod) {
204
252
  return await this.context.inputMethod.getInput(prompt);
@@ -264,8 +312,9 @@ export class CliChatService implements ChatService {
264
312
 
265
313
  async startChatLoop(): Promise<void> {
266
314
  // Display available commands like the original
267
- const commandNames = this.commands.map((cmd) => `/${cmd.name}`);
268
- console.log("Commands: ", commandNames.join(", "));
315
+ const availableCommands = this.getCommandsForActiveModes();
316
+ const commandNames = availableCommands.map((cmd) => `/${cmd.name}`);
317
+ console.log("Commands:", commandNames.join(", "));
269
318
 
270
319
  while (true) {
271
320
  const promptText =
@@ -274,10 +323,7 @@ export class CliChatService implements ChatService {
274
323
  : `\nAsk knowhow: `;
275
324
  try {
276
325
  // Pass command names as autocomplete options
277
- const input = await this.getInput(
278
- promptText,
279
- commandNames,
280
- );
326
+ const input = await this.getInput(promptText, commandNames);
281
327
 
282
328
  if (input.trim() === "") {
283
329
  continue;
@@ -2,7 +2,8 @@
2
2
  * Agent Chat Module - Handles agent interactions
3
3
  */
4
4
  import {
5
- AgentSynchronization,
5
+ AgentSyncKnowhowWeb,
6
+ AgentSyncFs,
6
7
  SessionManager,
7
8
  TaskRegistry,
8
9
  } from "../../services/index";
@@ -35,13 +36,15 @@ export class AgentModule extends BaseChatModule {
35
36
  // Service instances for task management, session management, and synchronization
36
37
  private taskRegistry: TaskRegistry;
37
38
  private sessionManager: SessionManager;
38
- private agentSync: AgentSynchronization;
39
+ private webSync: AgentSyncKnowhowWeb;
40
+ private fsSync: AgentSyncFs;
39
41
 
40
42
  constructor() {
41
43
  super();
42
44
  this.taskRegistry = new TaskRegistry();
43
45
  this.sessionManager = new SessionManager();
44
- this.agentSync = new AgentSynchronization();
46
+ this.webSync = new AgentSyncKnowhowWeb();
47
+ this.fsSync = new AgentSyncFs();
45
48
  }
46
49
 
47
50
  getCommands(): ChatCommand[] {
@@ -95,6 +98,7 @@ export class AgentModule extends BaseChatModule {
95
98
  context.agentMode = false;
96
99
  context.selectedAgent = undefined;
97
100
  context.currentAgent = undefined;
101
+ this.chatService.disableMode("agent");
98
102
  }
99
103
  console.log("Agent mode disabled. Switched to chat mode.");
100
104
  return;
@@ -122,7 +126,9 @@ export class AgentModule extends BaseChatModule {
122
126
  // so /model and /provider commands show accurate information
123
127
  context.currentModel = selectedAgent.getModel();
124
128
  context.currentProvider = selectedAgent.getProvider();
129
+ this.chatService.setMode("agent");
125
130
  }
131
+
126
132
  console.log(
127
133
  `Agent mode enabled. Selected agent: ${agentName}. Type your task to get started.`
128
134
  );
@@ -399,7 +405,7 @@ Please continue from where you left off and complete the original request.
399
405
  `;
400
406
 
401
407
  console.log("🚀 Session resuming...");
402
- const context = this.chatService?.getContext() || {};
408
+ const context = this.chatService?.getContext();
403
409
  const allAgents = agents();
404
410
  const selectedAgent =
405
411
  allAgents[session.agentName] || allAgents[context.currentAgent];
@@ -462,6 +468,7 @@ Please continue from where you left off and complete the original request.
462
468
  agentName: string;
463
469
  input: string;
464
470
  messageId?: string;
471
+ syncFs?: boolean;
465
472
  existingKnowhowTaskId?: string;
466
473
  provider?: string;
467
474
  model?: string;
@@ -469,6 +476,7 @@ Please continue from where you left off and complete the original request.
469
476
  maxSpendLimit?: number; // in dollars
470
477
  chatHistory?: ChatInteraction[];
471
478
  run?: boolean; // whether to run immediately
479
+ taskId?: string; // optional pre-generated taskId
472
480
  }) {
473
481
  const allAgents = agents();
474
482
 
@@ -485,7 +493,7 @@ Please continue from where you left off and complete the original request.
485
493
 
486
494
  let done = false;
487
495
  let output = "Done";
488
- const taskId = this.sessionManager.generateTaskId(input);
496
+ const taskId = options.taskId || this.sessionManager.generateTaskId(input);
489
497
  let knowhowTaskId: string | undefined;
490
498
 
491
499
  try {
@@ -521,8 +529,12 @@ Please continue from where you left off and complete the original request.
521
529
  this.saveSession(taskId, taskInfo, []);
522
530
 
523
531
  // Create Knowhow chat task if messageId provided
524
- if (options.messageId && !options.existingKnowhowTaskId) {
525
- knowhowTaskId = await this.agentSync.createChatTask({
532
+ if (
533
+ options.messageId &&
534
+ !options.existingKnowhowTaskId &&
535
+ !options.syncFs
536
+ ) {
537
+ knowhowTaskId = await this.webSync.createChatTask({
526
538
  messageId: options.messageId,
527
539
  prompt: input,
528
540
  });
@@ -533,8 +545,24 @@ Please continue from where you left off and complete the original request.
533
545
  this.taskRegistry.register(taskId, taskInfo);
534
546
 
535
547
  // Set up event-based synchronization with Knowhow API
536
- await this.agentSync.setupAgentSync(agent, knowhowTaskId);
548
+ await this.webSync.setupAgentSync(agent, knowhowTaskId);
537
549
  }
550
+ } else if (!options.messageId || options.syncFs) {
551
+ // Use filesystem sync when no messageId is provided or syncFs is explicitly enabled
552
+ console.log(
553
+ `📁 Using filesystem-based synchronization for task: ${taskId}`
554
+ );
555
+
556
+ const fsTaskId = await this.fsSync.createTask({
557
+ taskId,
558
+ prompt: input,
559
+ });
560
+
561
+ // Update TaskInfo with the fs sync info
562
+ taskInfo.knowhowTaskId = fsTaskId;
563
+ this.taskRegistry.register(taskId, taskInfo);
564
+
565
+ await this.fsSync.setupAgentSync(agent, fsTaskId);
538
566
  }
539
567
 
540
568
  // Set up session update listener
@@ -642,10 +670,15 @@ Please continue from where you left off and complete the original request.
642
670
 
643
671
  // Wait for AgentSync to finish before resolving
644
672
  if (knowhowTaskId) {
645
- console.log("🎯 [AgentModule] Waiting for sync finalization...");
646
- await this.agentSync.waitForFinalization();
647
- console.log("🎯 [AgentModule] Sync finalization complete");
673
+ console.log(
674
+ "🎯 [AgentModule] Waiting for web sync finalization..."
675
+ );
676
+ await this.webSync.waitForFinalization();
677
+ console.log("🎯 [AgentModule] Web sync finalization complete");
648
678
  }
679
+ console.log("🎯 [AgentModule] Waiting for fs sync finalization...");
680
+ await this.fsSync.waitForFinalization();
681
+ console.log("🎯 [AgentModule] Fs sync finalization complete");
649
682
 
650
683
  if (taskInfo) {
651
684
  taskInfo.status = "completed";
@@ -778,7 +811,25 @@ Please continue from where you left off and complete the original request.
778
811
  let agentFinalOutput: string | undefined;
779
812
 
780
813
  // Define available commands
781
- const commands = ["/pause", "/unpause", "/kill", "/detach", "/done"];
814
+ // Set mode to agent:attached so custom commands are available
815
+ if (this.chatService) {
816
+ this.chatService.setMode("agent:attached");
817
+ }
818
+
819
+ // Get mode-specific commands for autocomplete
820
+ const modeCommands =
821
+ this.chatService
822
+ ?.getCommandsForMode("agent:attached")
823
+ .map((cmd) => `/${cmd.name}`) || [];
824
+
825
+ const commands = [
826
+ ...modeCommands,
827
+ "/pause",
828
+ "/unpause",
829
+ "/kill",
830
+ "/detach",
831
+ "/done",
832
+ ];
782
833
  const history: string[] = [];
783
834
 
784
835
  // Set up the event listener BEFORE starting the agent to avoid race condition
@@ -836,6 +887,10 @@ Please continue from where you left off and complete the original request.
836
887
  break;
837
888
  case "/detach":
838
889
  console.log("Detached from agent");
890
+ // Reset mode back to default when detaching
891
+ if (this.chatService) {
892
+ this.chatService.setMode("default");
893
+ }
839
894
  return { result: true, finalOutput: agentFinalOutput };
840
895
  default:
841
896
  agent.addPendingUserMessage({
@@ -852,6 +907,11 @@ Please continue from where you left off and complete the original request.
852
907
  }
853
908
  }
854
909
 
910
+ // Reset mode back to default when exiting loop
911
+ if (this.chatService) {
912
+ this.chatService.setMode("default");
913
+ }
914
+
855
915
  // Update final task status and save session
856
916
  const finalTaskInfo = this.taskRegistry.get(taskId);
857
917
  if (finalTaskInfo) {
@@ -0,0 +1,79 @@
1
+ import { BaseChatModule } from "./BaseChatModule";
2
+ import { ChatCommand, ChatService, CommandResult } from "../types";
3
+ import { getLanguageConfig } from "../../config";
4
+ import { services } from "../../services";
5
+
6
+ /**
7
+ * CustomCommandsModule - Loads `/command`-style keys from the language config
8
+ * and registers them as chat commands. When invoked, the resolved language
9
+ * sources are sent to the active agent via addPendingUserMessage.
10
+ */
11
+ export class CustomCommandsModule extends BaseChatModule {
12
+ name = "custom-commands";
13
+ description =
14
+ "Dynamically registered commands from language config /command keys";
15
+
16
+ public getCommands(): ChatCommand[] {
17
+ // Commands are loaded asynchronously; return empty here and register dynamically in initialize
18
+ return [];
19
+ }
20
+
21
+ getPlugins() {
22
+ return this.chatService.getTools()?.getContext()?.Plugins;
23
+ }
24
+
25
+ async initialize(service: ChatService): Promise<void> {
26
+ this.chatService = service;
27
+
28
+ // Load language config and register /command-style keys
29
+ try {
30
+ const languageConfig = await getLanguageConfig();
31
+ const commandKeys = Object.keys(languageConfig).filter((key) =>
32
+ key.trim().startsWith("/")
33
+ );
34
+
35
+ for (const commandKey of commandKeys) {
36
+ // Strip the leading slash to get the command name
37
+ const commandName = commandKey.trim().slice(1);
38
+
39
+ console.log(
40
+ `CUSTOM-COMMANDS: Registering command /${commandName} from language config`
41
+ );
42
+
43
+ const command: ChatCommand = {
44
+ name: commandName,
45
+ description: `Custom command: ${commandKey}`,
46
+ modes: ["agent:attached", "agent"],
47
+ handler: async (args: string[]): Promise<CommandResult> => {
48
+ const config = languageConfig[commandKey];
49
+ const result = await this.getPlugins().call("language", commandKey);
50
+
51
+ // If handled is true, call the language plugin and display output but don't send to agent
52
+ if (config.handled === true) {
53
+ try {
54
+ console.log(result);
55
+ return {
56
+ handled: true,
57
+ contents: result,
58
+ };
59
+ } catch (error) {
60
+ console.error(`Error executing command ${commandKey}:`, error);
61
+ return { handled: true };
62
+ }
63
+ }
64
+
65
+ // By default (handled: false or undefined), return unhandled so agent module processes it
66
+ return {
67
+ handled: false,
68
+ contents: commandKey + " => " + result,
69
+ };
70
+ },
71
+ };
72
+
73
+ this.chatService.registerCommand(command);
74
+ }
75
+ } catch (error) {
76
+ console.error("CUSTOM-COMMANDS: Error loading language config:", error);
77
+ }
78
+ }
79
+ }
@@ -7,6 +7,8 @@ import { SearchModule } from "./SearchModule";
7
7
  import { VoiceModule } from "./VoiceModule";
8
8
  import { SystemModule } from "./SystemModule";
9
9
  import { SetupModule } from "./SetupModule";
10
+ import { CustomCommandsModule } from "./CustomCommandsModule";
11
+ import { ShellCommandModule } from "./ShellCommandModule";
10
12
 
11
13
  export class InternalChatModule implements ChatModule {
12
14
  private chatService?: CliChatService;
@@ -20,6 +22,8 @@ export class InternalChatModule implements ChatModule {
20
22
  private voiceModule = new VoiceModule();
21
23
  private systemModule = new SystemModule();
22
24
  private setupModule = new SetupModule();
25
+ private customCommandsModule = new CustomCommandsModule();
26
+ private shellCommandModule = new ShellCommandModule();
23
27
 
24
28
  async initialize(chatService: CliChatService): Promise<void> {
25
29
  this.chatService = chatService;
@@ -34,7 +38,9 @@ export class InternalChatModule implements ChatModule {
34
38
  await this.voiceModule.initialize(chatService);
35
39
  await this.systemModule.initialize(chatService);
36
40
  await this.setupModule.initialize(chatService);
37
-
41
+ await this.customCommandsModule.initialize(chatService);
42
+ await this.shellCommandModule.initialize(chatService);
43
+
38
44
  // Register our own commands (exit and multi) - not duplicated by BaseChatModule
39
45
  chatService.registerCommand({
40
46
  name: "exit",
@@ -62,6 +68,8 @@ export class InternalChatModule implements ChatModule {
62
68
  ...this.voiceModule.getCommands(),
63
69
  ...this.systemModule.getCommands(),
64
70
  ...this.setupModule.getCommands(),
71
+ ...this.customCommandsModule.getCommands(),
72
+ ...this.shellCommandModule.getCommands(),
65
73
  {
66
74
  name: "exit",
67
75
  description: "Exit the chat",
@@ -84,6 +92,8 @@ export class InternalChatModule implements ChatModule {
84
92
  ...this.voiceModule.getModes(),
85
93
  ...this.systemModule.getModes(),
86
94
  ...this.setupModule.getModes(),
95
+ ...this.customCommandsModule.getModes(),
96
+ ...this.shellCommandModule.getModes(),
87
97
  ];
88
98
  }
89
99
 
@@ -0,0 +1,96 @@
1
+ import { BaseChatModule } from "./BaseChatModule";
2
+ import { ChatCommand, ChatContext, CommandResult } from "../types";
3
+ import { execSync } from "child_process";
4
+
5
+ /**
6
+ * ShellCommandModule - Handles /! and /!! commands for executing shell commands
7
+ * /! - Execute command and display output in console (interactive if needed)
8
+ * /!! - Execute command and send output to the AI agent
9
+ */
10
+ export class ShellCommandModule extends BaseChatModule {
11
+ name = "shell-command";
12
+ description = "Execute shell commands with /! and /!!";
13
+
14
+ public getCommands(): ChatCommand[] {
15
+ return [
16
+ {
17
+ name: "!",
18
+ description: "Execute a shell command (interactive)",
19
+ modes: ["agent", "agent:attached"],
20
+ handler: async (args: string[]): Promise<CommandResult> => {
21
+ const command = args.join(" ");
22
+ if (!command) {
23
+ console.log("Usage: /! <command>");
24
+ return { handled: true };
25
+ }
26
+
27
+ try {
28
+ console.log(`Executing: ${command}`);
29
+ // Execute the command and inherit stdio for interactivity
30
+ const result = execSync(command, {
31
+ encoding: "utf8",
32
+ stdio: "inherit",
33
+ cwd: process.cwd(),
34
+ });
35
+
36
+ return { handled: true };
37
+ } catch (error: any) {
38
+ console.error(`Command failed: ${error.message}`);
39
+ if (error.stderr) {
40
+ console.error(error.stderr);
41
+ }
42
+ return { handled: true };
43
+ }
44
+ },
45
+ },
46
+ {
47
+ name: "!!",
48
+ description: "Execute a shell command and send output to AI",
49
+ modes: ["agent", "agent:attached"],
50
+ handler: async (args: string[]): Promise<CommandResult> => {
51
+ const command = args.join(" ");
52
+ if (!command) {
53
+ console.log("Usage: /!! <command>");
54
+ return { handled: true };
55
+ }
56
+
57
+ try {
58
+ console.log(`Executing: ${command}`);
59
+ const result = execSync(command, {
60
+ encoding: "utf8",
61
+ cwd: process.cwd(),
62
+ });
63
+
64
+ console.log(result);
65
+
66
+ // Return unhandled with the command output so it gets sent to the agent
67
+ return {
68
+ handled: false,
69
+ contents: `Command output from \`${command}\`:\n\`\`\`\n${result}\n\`\`\``,
70
+ };
71
+ } catch (error: any) {
72
+ const errorMessage = error.message;
73
+ const stderr = error.stderr || "";
74
+ const stdout = error.stdout || "";
75
+
76
+ console.error(`Command failed: ${errorMessage}`);
77
+ if (stderr) {
78
+ console.error(stderr);
79
+ }
80
+
81
+ // Send error output to agent as well
82
+ return {
83
+ handled: false,
84
+ contents: `Command \`${command}\` failed:\n\`\`\`\n${stdout}\n${stderr}\n${errorMessage}\n\`\`\``,
85
+ };
86
+ }
87
+ },
88
+ },
89
+ ];
90
+ }
91
+
92
+ async handleInput(input: string, context: ChatContext): Promise<boolean> {
93
+ // This module only handles commands registered above
94
+ return false;
95
+ }
96
+ }
@@ -3,3 +3,4 @@ export { SearchModule } from './SearchModule';
3
3
  export { AgentModule } from './AgentModule';
4
4
  export { VoiceModule } from './VoiceModule';
5
5
  export { InternalChatModule } from './InternalChatModule';
6
+ export { ShellCommandModule } from './ShellCommandModule';
package/src/chat/types.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Core Types for Modular Chat System
3
3
  */
4
- import { ChatInteraction } from "../types";
4
+ import { ChatInteraction, Config } from "../types";
5
5
  import { BaseAgent } from "../agents/base/base";
6
+ import { ToolsService } from "src/services";
6
7
 
7
8
  export interface ChatContext {
8
9
  debugMode?: boolean;
@@ -15,6 +16,8 @@ export interface ChatContext {
15
16
  currentProvider?: string;
16
17
  inputMethod?: InputMethod;
17
18
  selectedAgent?: BaseAgent;
19
+ plugins: string[];
20
+
18
21
  [key: string]: any;
19
22
  }
20
23
 
@@ -24,10 +27,16 @@ export interface ChatMode {
24
27
  active: boolean;
25
28
  }
26
29
 
30
+ export interface CommandResult {
31
+ handled: boolean;
32
+ contents?: string;
33
+ }
34
+
27
35
  export interface ChatCommand {
28
36
  name: string;
29
37
  description: string;
30
- handler: (args: string[]) => Promise<void>;
38
+ handler: (args: string[]) => Promise<void | CommandResult>;
39
+ modes?: string[]; // Commands can specify which modes they're available in
31
40
  }
32
41
 
33
42
  export interface InputMethod {
@@ -38,12 +47,15 @@ export interface InputMethod {
38
47
 
39
48
  export interface ChatService {
40
49
  getContext(): ChatContext;
50
+ getTools(): ToolsService | undefined;
41
51
  setContext(context: Partial<ChatContext>): void;
42
52
  setInputMethod(method: InputMethod): void;
43
53
  resetInputMethod(): void;
44
54
  registerCommand(command: ChatCommand): void;
45
55
  registerMode(mode: ChatMode): void;
46
56
  getCommands(): ChatCommand[];
57
+ getCommandsForMode(mode: string): ChatCommand[];
58
+ setMode(mode: string): void;
47
59
  getModes(): ChatMode[];
48
60
  getMode(name: string): ChatMode | undefined;
49
61
  enableMode(name: string): void;
package/src/chat.ts CHANGED
@@ -20,23 +20,26 @@ async function main() {
20
20
  configError
21
21
  );
22
22
  config = {
23
- plugins: [
24
- "embeddings",
25
- "language",
26
- "vim",
27
- "github",
28
- "asana",
29
- "jira",
30
- "linear",
31
- "download",
32
- "figma",
33
- "url",
34
- ],
23
+ plugins: {
24
+ enabled: [
25
+ "embeddings",
26
+ "language",
27
+ "vim",
28
+ "github",
29
+ "asana",
30
+ "jira",
31
+ "linear",
32
+ "download",
33
+ "figma",
34
+ "url",
35
+ ],
36
+ disabled: [],
37
+ },
35
38
  };
36
39
  }
37
40
 
38
41
  // Create chat service with plugins
39
- const chatService = new CliChatService(config.plugins);
42
+ const chatService = new CliChatService(config.plugins.enabled);
40
43
 
41
44
  // Load internal chat module (includes all core functionality)
42
45
  const internalModule = new InternalChatModule();