@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
@@ -0,0 +1,174 @@
1
+ # Shell Command Execution
2
+
3
+ Knowhow provides multiple ways to execute shell commands during chat sessions, both for interactive terminal use and for sending command output to the AI agent.
4
+
5
+ ## Quick Commands: `/!` and `/!!`
6
+
7
+ Available in `agent` and `agent:attached` modes:
8
+
9
+ ### `/!` - Interactive Shell Command
10
+ Execute a command and display output in the console. The command runs interactively, allowing you to interact with it if needed.
11
+
12
+ ```
13
+ /! git status
14
+ /! npm test
15
+ /! terraform plan
16
+ ```
17
+
18
+ Output is displayed to you but **not sent to the AI agent**.
19
+
20
+ ### `/!!` - Send Output to AI
21
+ Execute a command and send the output to the AI agent for analysis.
22
+
23
+ ```
24
+ /!! git diff
25
+ /!! npm run lint
26
+ /!! cat error.log
27
+ ```
28
+
29
+ Output is displayed to you **and sent to the AI agent** for processing.
30
+
31
+ ## Custom Commands via Language Config
32
+
33
+ You can define custom shell commands in `.knowhow/language.json` that integrate with the language plugin system.
34
+
35
+ ### Example: `/git` Command
36
+
37
+ ```json
38
+ {
39
+ "/git": {
40
+ "events": [],
41
+ "handled": true,
42
+ "sources": [
43
+ {
44
+ "kind": "exec",
45
+ "data": [
46
+ "git status"
47
+ ]
48
+ }
49
+ ]
50
+ }
51
+ }
52
+ ```
53
+
54
+ This creates a `/git` command that runs `git status` when invoked.
55
+
56
+ ### Example: `/tfplan` Command
57
+
58
+ ```json
59
+ {
60
+ "/tfplan": {
61
+ "events": [],
62
+ "handled": true,
63
+ "sources": [
64
+ {
65
+ "kind": "exec",
66
+ "data": [
67
+ "cd terraform && terraform plan"
68
+ ]
69
+ }
70
+ ]
71
+ }
72
+ }
73
+ ```
74
+
75
+ ### The `handled` Property
76
+
77
+ - **`handled: true`** - Command output is displayed to the user only, **not sent to the AI agent**
78
+ - **`handled: false`** (default) - Command output is sent to the AI agent for processing
79
+
80
+ This allows you to:
81
+ - Use `handled: true` for commands where you just want to see output (like `/tfplan`)
82
+ - Use `handled: false` for commands where you want the AI to analyze the output
83
+
84
+ ### Multiple Commands
85
+
86
+ You can also chain multiple commands or execute more complex operations:
87
+
88
+ ```json
89
+ {
90
+ "/deploy-status": {
91
+ "events": [],
92
+ "handled": false,
93
+ "sources": [
94
+ {
95
+ "kind": "exec",
96
+ "data": [
97
+ "kubectl get pods && kubectl get services"
98
+ ]
99
+ }
100
+ ]
101
+ }
102
+ }
103
+ ```
104
+
105
+ ## Exec Plugin
106
+
107
+ The exec plugin is automatically enabled and allows language config entries to execute shell commands. It's used internally by the custom command system.
108
+
109
+ ### Plugin Configuration
110
+
111
+ The exec plugin is enabled by default in `.knowhow/knowhow.json`:
112
+
113
+ ```json
114
+ {
115
+ "plugins": {
116
+ "enabled": [
117
+ "exec",
118
+ // ... other plugins
119
+ ]
120
+ }
121
+ }
122
+ ```
123
+
124
+ ## Use Cases
125
+
126
+ ### Development Workflow
127
+ ```json
128
+ {
129
+ "/build": {
130
+ "handled": false,
131
+ "sources": [{ "kind": "exec", "data": ["npm run build"] }]
132
+ },
133
+ "/test": {
134
+ "handled": false,
135
+ "sources": [{ "kind": "exec", "data": ["npm test"] }]
136
+ }
137
+ }
138
+ ```
139
+
140
+ ### Infrastructure Management
141
+ ```json
142
+ {
143
+ "/infra": {
144
+ "handled": true,
145
+ "sources": [{ "kind": "exec", "data": ["terraform plan"] }]
146
+ },
147
+ "/pods": {
148
+ "handled": false,
149
+ "sources": [{ "kind": "exec", "data": ["kubectl get pods"] }]
150
+ }
151
+ }
152
+ ```
153
+
154
+ ### Git Workflows
155
+ ```json
156
+ {
157
+ "/changes": {
158
+ "handled": false,
159
+ "sources": [{ "kind": "exec", "data": ["git diff --cached"] }]
160
+ },
161
+ "/branches": {
162
+ "handled": true,
163
+ "sources": [{ "kind": "exec", "data": ["git branch -a"] }]
164
+ }
165
+ }
166
+ ```
167
+
168
+ ## Security Notes
169
+
170
+ - Commands are executed in your current working directory
171
+ - Commands run with your user permissions
172
+ - Be cautious with commands that modify system state
173
+ - The exec plugin has a 10MB output buffer limit
174
+ - Interactive commands work with `/!` but not with language config exec sources
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tyvm/knowhow",
3
- "version": "0.0.68",
3
+ "version": "0.0.70",
4
4
  "description": "ai cli with plugins and agents",
5
5
  "main": "ts_build/src/index.js",
6
6
  "bin": {
@@ -40,7 +40,7 @@
40
40
  "dependencies": {
41
41
  "@anthropic-ai/sdk": "^0.39.0",
42
42
  "@aws-sdk/client-s3": "^3.588.0",
43
- "@tyvm/knowhow-tunnel": "0.0.2",
43
+ "@tyvm/knowhow-tunnel": "0.0.3",
44
44
  "@google/genai": "^0.14.1",
45
45
  "@inquirer/editor": "^4.2.18",
46
46
  "@linear/sdk": "^12.0.0",
@@ -758,9 +758,7 @@ export abstract class BaseAgent implements IAgent {
758
758
  logStatus() {
759
759
  const statusMessage = this.getStatusMessage();
760
760
  console.log(
761
- `\n● ${this.name} status: $${this.getTotalCostUsd().toPrecision(
762
- 3
763
- )}\n${statusMessage}`
761
+ `\n● ${this.name} status: ${statusMessage}`
764
762
  );
765
763
  }
766
764
 
@@ -8,15 +8,10 @@ export class DeveloperAgent extends BaseAgent {
8
8
 
9
9
  constructor(context: AgentContext) {
10
10
  super(context);
11
- this.disableTool("patchFile");
12
- this.disableTool("openFileInVim");
13
- this.disableTool("sendVimInput");
14
- this.disableTool("saveVimFile");
15
-
16
11
  this.setModelPreferences([
17
12
  {
18
- model: Models.google.Gemini_20_Flash,
19
- provider: "google",
13
+ model: Models.anthropic.Sonnet4_6,
14
+ provider: "anthropic",
20
15
  },
21
16
  ]);
22
17
  }
@@ -35,8 +30,21 @@ export class DeveloperAgent extends BaseAgent {
35
30
  You delegate some tasks to specialized agents. If a request doesn't require the use of a specialized agent, you can handle it yourself.
36
31
 
37
32
  # How to call other agents
38
- You can use the agentCall tool to call other agents.
39
- Do not try to use VIM/ or Patching tools directly. If you must write to a file yourself use writeFileChunk
33
+ You can use the startAgentTask tool to call other agents.
34
+ This is a wrapper for the shell command knowhow agent --input "your prompt"
35
+
36
+ If sync-fs is active:
37
+ When you start a knowhow agent, it will create a folder in .knowhow/processes/agents/
38
+ For that agent you can use the input.txt file to send it messages.
39
+
40
+ If you send a message to an agent, you can tell it your task directory/input.txt file path and they can write there to respond
41
+ Your task id is:
42
+ ${this.currentTaskId}
43
+
44
+ If you need to write a longer task, you can you knowhow agent --prompt-file <filepath>
45
+ This way you can write out specs and launch the agent on that
46
+
47
+ You can use the status.txt to pause an agent, or pause yourself and have another agent unpause you, or you can use shell commands to wait for an agent's status to change, with a timeout
40
48
 
41
49
  # Which Agent to Use:
42
50
  Researcher -
@@ -46,16 +54,13 @@ export class DeveloperAgent extends BaseAgent {
46
54
  - General Questions about codebase or file structure
47
55
 
48
56
  Patcher
57
+ - this is the default agent
49
58
  - For making modifications to files / code
50
59
  - Great for big files
51
60
 
52
- # Thought process
53
- 1. Is the user asking you a question about the codebase or files? Foreward the question to the Researcher.
54
- 2. Do you need to make changes to files?
55
- 2.a Do we have enough information to know exactly what to modify? If not, ask the Researcher.
56
- 2.b If we know what to modify, ask Patcher to make the changes with all the context required.
57
- 3. If the agent you call has declared it has completed a task, you may need to check it's modifications to see if there's some follow up work required.
58
- 4. If the user is asking for a general task, like webbrowsing or terminal commands, or general questions you may accomplish this yourself.
61
+
62
+ If the user has asked you to do multiple things that are parallelizable, you can start an agent for each task
63
+ Each agent will have it's own log files. You can check the logs of each agent to see their progress.
59
64
  `,
60
65
  },
61
66
  {
@@ -1,5 +1,6 @@
1
1
  import { getConfig } from "../../config";
2
2
  import { services, ToolsService } from "../../services";
3
+ import { getEnabledPlugins } from "../../types";
3
4
 
4
5
  export async function agentCall(agentName: string, userInput: string) {
5
6
  return new Promise(async (resolve, reject) => {
@@ -11,8 +12,9 @@ export async function agentCall(agentName: string, userInput: string) {
11
12
  const { Events, Plugins } = toolService.getContext();
12
13
 
13
14
  let fullPrompt = `${userInput}`;
14
- if (config.plugins?.length) {
15
- const pluginText = await Plugins.callMany(config.plugins, userInput);
15
+ const enabledPlugins = getEnabledPlugins(config.plugins);
16
+ if (enabledPlugins?.length) {
17
+ const pluginText = await Plugins.callMany(enabledPlugins, userInput);
16
18
  fullPrompt += `\n ${pluginText}`;
17
19
  }
18
20
 
@@ -15,7 +15,11 @@ export async function fileSearch(searchTerm) {
15
15
  });
16
16
 
17
17
  const embeddings = await getConfiguredEmbeddings();
18
- const embeddingFiles = embeddings.filter((embedding) =>
18
+
19
+ // Ensure embeddings is always an array
20
+ const embeddingsArray = Array.isArray(embeddings) ? embeddings : [];
21
+
22
+ const embeddingFiles = embeddingsArray.filter((embedding) =>
19
23
  embedding.id.toLowerCase().includes(searchTermLower)
20
24
  );
21
25
 
@@ -390,40 +390,6 @@ export const includedTools = [
390
390
  description: "Create a completion using the knowhow AI client",
391
391
  parameters: {
392
392
  type: "object",
393
- positional: true,
394
- additionalProperties: false,
395
- $defs: {
396
- message: {
397
- type: "object",
398
- additionalProperties: false,
399
- properties: {
400
- role: {
401
- type: "string",
402
- enum: ["system", "user", "assistant", "tool"],
403
- },
404
- content: { type: "string" },
405
- },
406
- required: ["role", "content"],
407
- },
408
- toolLite: {
409
- type: "object",
410
- additionalProperties: false,
411
- properties: {
412
- type: { type: "string", enum: ["function"] },
413
- function: {
414
- type: "object",
415
- additionalProperties: false,
416
- properties: {
417
- name: { type: "string" },
418
- description: { type: "string" },
419
- parameters: { type: "object" },
420
- },
421
- required: ["name", "parameters"],
422
- },
423
- },
424
- required: ["type", "function"],
425
- },
426
- },
427
393
  properties: {
428
394
  provider: {
429
395
  type: "string",
@@ -432,14 +398,27 @@ export const includedTools = [
432
398
  },
433
399
  options: {
434
400
  type: "object",
435
- additionalProperties: false,
436
401
  description: "Provider-specific completion options",
437
402
  properties: {
438
403
  model: { type: "string", description: "The model to use" },
439
404
  messages: {
440
405
  type: "array",
441
406
  description: "The chat history for the completion",
442
- items: { $ref: "#/$defs/message" },
407
+ items: {
408
+ type: "object",
409
+ properties: {
410
+ role: {
411
+ type: "string",
412
+ enum: ["system", "user", "assistant", "tool"],
413
+ description: "The role of the message sender",
414
+ },
415
+ content: {
416
+ type: "string",
417
+ description: "The content of the message",
418
+ },
419
+ },
420
+ required: ["role", "content"],
421
+ },
443
422
  minItems: 1,
444
423
  },
445
424
  max_tokens: {
@@ -450,7 +429,32 @@ export const includedTools = [
450
429
  type: "array",
451
430
  description:
452
431
  "Tool definitions the model may call (non-recursive subset)",
453
- items: { $ref: "#/$defs/toolLite" },
432
+ items: {
433
+ type: "object",
434
+ properties: {
435
+ type: {
436
+ type: "string",
437
+ enum: ["function"],
438
+ description: "The type of tool",
439
+ },
440
+ function: {
441
+ type: "object",
442
+ properties: {
443
+ name: { type: "string", description: "Function name" },
444
+ description: {
445
+ type: "string",
446
+ description: "Function description",
447
+ },
448
+ parameters: {
449
+ type: "object",
450
+ description: "Function parameters schema",
451
+ },
452
+ },
453
+ required: ["name", "parameters"],
454
+ },
455
+ },
456
+ required: ["type", "function"],
457
+ },
454
458
  },
455
459
  },
456
460
  required: ["model", "messages"],
@@ -1,8 +1,11 @@
1
1
  import { Tool } from "../../clients/types";
2
- import { execCommand } from "./execCommand";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import { spawn } from "child_process";
3
5
 
4
6
  interface StartAgentTaskParams {
5
- messageId: string;
7
+ messageId?: string;
8
+ syncFs?: boolean;
6
9
  prompt: string;
7
10
  provider?: string;
8
11
  model?: string;
@@ -11,56 +14,154 @@ interface StartAgentTaskParams {
11
14
  maxSpendLimit?: number;
12
15
  }
13
16
 
17
+ const PROCESSES_DIR = path.join(process.cwd(), ".knowhow", "processes");
18
+ const AGENTS_DIR = path.join(process.cwd(), ".knowhow", "processes", "agents");
19
+
20
+ /**
21
+ * Generate a task ID matching the format used by SessionManager.generateTaskId()
22
+ * Format: {epochSeconds}-{words-from-prompt}
23
+ */
24
+ function generateTaskId(prompt: string): string {
25
+ const words = prompt
26
+ .toLowerCase()
27
+ .replace(/[^\w\s]/g, "")
28
+ .split(/\s+/)
29
+ .filter((word) => word.length > 2)
30
+ .slice(0, 9);
31
+ const wordPart = words.join("-") || "task";
32
+ const epochSeconds = Math.floor(Date.now() / 1000);
33
+ return `${epochSeconds}-${wordPart}`;
34
+ }
35
+
14
36
  /**
15
37
  * Creates a chat task in Knowhow based on a message ID and prompt.
16
- * This allows external agents to start tasks that can receive real-time updates.
38
+ * Spawns the knowhow CLI with the prompt piped via stdin to avoid
39
+ * shell escaping issues with special characters (quotes, backticks,
40
+ * newlines, template expressions, etc.).
41
+ *
42
+ * When syncFs is true, the agent creates a directory at:
43
+ * .knowhow/processes/agents/{taskId}/
44
+ * with files: status.txt, input.txt, metadata.json
45
+ *
46
+ * To send follow-up messages to the agent, write content to:
47
+ * .knowhow/processes/agents/{taskId}/input.txt
48
+ * The agent will pick up the new content and process it as a new message.
17
49
  */
18
- export async function startAgentTask(params: StartAgentTaskParams) {
50
+ export async function startAgentTask(params: StartAgentTaskParams): Promise<string> {
19
51
  const {
20
52
  messageId,
21
53
  prompt,
54
+ syncFs,
22
55
  provider,
23
56
  model,
24
57
  agentName,
25
58
  maxTimeLimit,
26
59
  maxSpendLimit,
27
60
  } = params;
28
-
29
- if (!messageId) {
30
- throw new Error("messageId is required to create a chat task");
31
- }
32
-
33
61
  if (!prompt) {
34
62
  throw new Error("prompt is required to create a chat task");
35
63
  }
36
64
 
37
- const escapedPrompt = prompt.replace(/"/g, '\\"');
65
+ // Pre-generate taskId so we can return the agents dir path to the caller
66
+ const taskId = generateTaskId(prompt);
67
+ const agentTaskDir = path.join(AGENTS_DIR, taskId);
38
68
 
39
- // Build the command with all optional parameters
40
- let command = `knowhow agent --input "${escapedPrompt}" --message-id ${messageId}`;
69
+ // Build args array (no shell escaping needed - args are passed directly)
70
+ const args: string[] = ["agent"];
71
+
72
+ if (messageId) {
73
+ args.push("--message-id", messageId);
74
+ } else if (syncFs) {
75
+ args.push("--sync-fs");
76
+ // Pass the pre-generated taskId so the agent dir path is predictable
77
+ args.push("--task-id", taskId);
78
+ }
41
79
 
42
80
  if (provider) {
43
- command += ` --provider ${provider}`;
81
+ args.push("--provider", provider);
44
82
  }
45
83
 
46
84
  if (model) {
47
- command += ` --model "${model}"`;
85
+ args.push("--model", model);
48
86
  }
49
87
 
50
88
  if (agentName) {
51
- command += ` --agent-name "${agentName}"`;
89
+ args.push("--agent-name", agentName);
52
90
  }
53
91
 
54
92
  if (maxTimeLimit !== undefined) {
55
- command += ` --max-time-limit ${maxTimeLimit}`;
93
+ args.push("--max-time-limit", String(maxTimeLimit));
56
94
  }
57
95
 
58
96
  if (maxSpendLimit !== undefined) {
59
- command += ` --max-spend-limit ${maxSpendLimit}`;
97
+ args.push("--max-spend-limit", String(maxSpendLimit));
60
98
  }
61
99
 
62
- const timeout = maxTimeLimit || 60000;
63
- return execCommand(command, timeout, true);
100
+ const timeoutMs = maxTimeLimit ? maxTimeLimit * 60 * 1000 : 60 * 60 * 1000;
101
+
102
+ // Set up log file for background process output
103
+ fs.mkdirSync(PROCESSES_DIR, { recursive: true });
104
+ const logBaseName = `knowhow_${Math.floor(Date.now() / 1000)}`;
105
+ const logPath = path.join(PROCESSES_DIR, `${logBaseName}.txt`);
106
+ const fd = fs.openSync(logPath, "w");
107
+
108
+ const header =
109
+ `CMD: knowhow ${args.join(" ")}\n` +
110
+ `START: ${new Date().toISOString()}\n` +
111
+ `---\n`;
112
+ fs.writeSync(fd, header);
113
+
114
+ // Spawn with prompt piped via stdin - no shell escaping issues
115
+ const child = spawn("knowhow", args, {
116
+ stdio: ["pipe", fd, fd],
117
+ detached: true,
118
+ });
119
+
120
+ const pid = child.pid!;
121
+ fs.writeSync(fd, `PID: ${pid}\n`);
122
+
123
+ // Write prompt to stdin and close it so the process reads it
124
+ child.stdin!.write(prompt, "utf8");
125
+ child.stdin!.end();
126
+
127
+ return new Promise<string>((resolve) => {
128
+ let settled = false;
129
+ const done = (msg: string) => {
130
+ if (settled) return;
131
+ settled = true;
132
+ try { fs.closeSync(fd); } catch {}
133
+ resolve(msg);
134
+ };
135
+
136
+ child.once("error", (e) => {
137
+ done(`Failed to start agent: ${String(e)}\nLogs: ${logPath}`);
138
+ });
139
+
140
+ const syncFsNote = syncFs
141
+ ? `\nTask ID: ${taskId}\nAgent dir: ${agentTaskDir}\n` +
142
+ `To send follow-up messages, write to: ${agentTaskDir}/input.txt\n` +
143
+ `To check status, read: ${agentTaskDir}/status.txt\n`
144
+ : "";
145
+
146
+ // Give the agent 30 seconds to finish before detaching
147
+ const detachTime = 30 * 1000; // 30 seconds
148
+ const tid = setTimeout(() => {
149
+ try { child.unref(); } catch {}
150
+ done(
151
+ `Agent started (pid=${pid}), running in background.\n` +
152
+ `Logs: ${logPath}\n` +
153
+ syncFsNote
154
+ );
155
+ }, detachTime);
156
+
157
+ child.once("exit", (code) => {
158
+ clearTimeout(tid);
159
+ done(
160
+ `Agent finished with exit code ${code}.\nLogs: ${logPath}\n` +
161
+ syncFsNote
162
+ );
163
+ });
164
+ });
64
165
  }
65
166
 
66
167
  export const startAgentTaskDefinition: Tool = {
@@ -68,14 +169,22 @@ export const startAgentTaskDefinition: Tool = {
68
169
  function: {
69
170
  name: "startAgentTask",
70
171
  description:
71
- "Create a new chat task in Knowhow based on a message ID and prompt. This allows worker agents to start tasks and update knowhow's backend with all CLI agent options",
172
+ "Create a new chat task in Knowhow based on a message ID and prompt. This allows worker agents to start tasks and update knowhow's backend with all CLI agent options. " +
173
+ "When syncFs is true, the agent creates a directory at .knowhow/processes/agents/{taskId}/ with status.txt, input.txt, and metadata.json. " +
174
+ "You can send follow-up messages to the running agent by writing content to .knowhow/processes/agents/{taskId}/input.txt. " +
175
+ "The return value includes the taskId and agent directory path when syncFs is used.",
72
176
  parameters: {
73
177
  type: "object",
74
178
  properties: {
75
179
  messageId: {
76
180
  type: "string",
77
181
  description:
78
- "The ID of the message in Knowhow to associate with this task",
182
+ "The ID of the message in Knowhow to associate with this task (optional)",
183
+ },
184
+ syncFs: {
185
+ type: "boolean",
186
+ description:
187
+ "Enable filesystem-based synchronization for the task. Use this when no messageId is available.",
79
188
  },
80
189
  prompt: {
81
190
  type: "string",
@@ -103,7 +212,7 @@ export const startAgentTaskDefinition: Tool = {
103
212
  description: "Cost limit for agent execution in dollars. Default: 10",
104
213
  },
105
214
  },
106
- required: ["messageId", "prompt"],
215
+ required: ["prompt"],
107
216
  },
108
217
  },
109
218
  };