@tyvm/knowhow 0.0.69 → 0.0.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/shell-commands.md +174 -0
- package/package.json +1 -1
- package/src/agents/base/base.ts +4 -5
- package/src/agents/developer/developer.ts +21 -13
- package/src/agents/tools/agentCall.ts +4 -2
- package/src/agents/tools/fileSearch.ts +5 -1
- package/src/agents/tools/startAgentTask.ts +131 -22
- package/src/agents/tools/stringReplace.ts +42 -12
- package/src/chat/CliChatService.ts +57 -11
- package/src/chat/modules/AgentModule.ts +72 -12
- package/src/chat/modules/CustomCommandsModule.ts +79 -0
- package/src/chat/modules/InternalChatModule.ts +11 -1
- package/src/chat/modules/ShellCommandModule.ts +96 -0
- package/src/chat/modules/index.ts +1 -0
- package/src/chat/types.ts +14 -2
- package/src/chat.ts +16 -13
- package/src/cli.ts +16 -6
- package/src/clients/anthropic.ts +82 -112
- package/src/clients/gemini.ts +445 -87
- package/src/clients/index.ts +125 -0
- package/src/clients/knowhow.ts +81 -0
- package/src/clients/openai.ts +256 -145
- package/src/clients/pricing/anthropic.ts +90 -0
- package/src/clients/pricing/google.ts +65 -0
- package/src/clients/pricing/index.ts +4 -0
- package/src/clients/pricing/openai.ts +134 -0
- package/src/clients/pricing/xai.ts +62 -0
- package/src/clients/types.ts +170 -1
- package/src/clients/xai.ts +275 -46
- package/src/config.ts +61 -15
- package/src/embeddings.ts +9 -1
- package/src/microphone.ts +15 -16
- package/src/migrations.ts +151 -0
- package/src/plugins/AgentsMdPlugin.ts +118 -0
- package/src/plugins/PluginBase.ts +8 -0
- package/src/plugins/downloader/downloader.ts +5 -6
- package/src/plugins/embedding.ts +10 -8
- package/src/plugins/exec.ts +70 -0
- package/src/plugins/github.ts +120 -74
- package/src/plugins/language.ts +11 -13
- package/src/plugins/plugins.ts +25 -4
- package/src/plugins/tmux.ts +132 -0
- package/src/plugins/types.ts +1 -0
- package/src/plugins/vim.ts +14 -1
- package/src/services/AgentSyncFs.ts +417 -0
- package/src/services/{AgentSynchronization.ts → AgentSyncKnowhowWeb.ts} +2 -2
- package/src/services/EventService.ts +0 -1
- package/src/services/KnowhowClient.ts +106 -0
- package/src/services/index.ts +4 -2
- package/src/types.ts +57 -4
- package/src/worker.ts +11 -6
- package/tests/manual/modalities/README.md +157 -0
- package/tests/manual/modalities/google.modalities.test.ts +335 -0
- package/tests/manual/modalities/openai.modalities.test.ts +329 -0
- package/tests/manual/modalities/streaming.test.ts +260 -0
- package/tests/manual/modalities/xai.modalities.test.ts +307 -0
- package/tests/plugins/language/languagePlugin-content-triggers.test.ts +5 -5
- package/tests/plugins/language/languagePlugin-integration.test.ts +1 -1
- package/tests/plugins/language/languagePlugin.test.ts +17 -8
- package/ts_build/package.json +1 -1
- package/ts_build/src/agents/base/base.d.ts +3 -3
- package/ts_build/src/agents/base/base.js +1 -1
- package/ts_build/src/agents/base/base.js.map +1 -1
- package/ts_build/src/agents/developer/developer.js +21 -12
- package/ts_build/src/agents/developer/developer.js.map +1 -1
- package/ts_build/src/agents/tools/agentCall.js +4 -2
- package/ts_build/src/agents/tools/agentCall.js.map +1 -1
- package/ts_build/src/agents/tools/executeScript/index.d.ts +1 -1
- package/ts_build/src/agents/tools/fileSearch.js +2 -1
- package/ts_build/src/agents/tools/fileSearch.js.map +1 -1
- package/ts_build/src/agents/tools/github/index.d.ts +1 -1
- package/ts_build/src/agents/tools/startAgentTask.d.ts +2 -1
- package/ts_build/src/agents/tools/startAgentTask.js +118 -17
- package/ts_build/src/agents/tools/startAgentTask.js.map +1 -1
- package/ts_build/src/agents/tools/stringReplace.js +29 -12
- package/ts_build/src/agents/tools/stringReplace.js.map +1 -1
- package/ts_build/src/chat/CliChatService.d.ts +4 -0
- package/ts_build/src/chat/CliChatService.js +39 -5
- package/ts_build/src/chat/CliChatService.js.map +1 -1
- package/ts_build/src/chat/modules/AgentModule.d.ts +4 -1
- package/ts_build/src/chat/modules/AgentModule.js +49 -11
- package/ts_build/src/chat/modules/AgentModule.js.map +1 -1
- package/ts_build/src/chat/modules/CustomCommandsModule.d.ts +9 -0
- package/ts_build/src/chat/modules/CustomCommandsModule.js +58 -0
- package/ts_build/src/chat/modules/CustomCommandsModule.js.map +1 -0
- package/ts_build/src/chat/modules/InternalChatModule.d.ts +2 -0
- package/ts_build/src/chat/modules/InternalChatModule.js +10 -0
- package/ts_build/src/chat/modules/InternalChatModule.js.map +1 -1
- package/ts_build/src/chat/modules/ShellCommandModule.d.ts +8 -0
- package/ts_build/src/chat/modules/ShellCommandModule.js +83 -0
- package/ts_build/src/chat/modules/ShellCommandModule.js.map +1 -0
- package/ts_build/src/chat/modules/index.d.ts +1 -0
- package/ts_build/src/chat/modules/index.js +3 -1
- package/ts_build/src/chat/modules/index.js.map +1 -1
- package/ts_build/src/chat/types.d.ts +11 -1
- package/ts_build/src/chat.js +16 -13
- package/ts_build/src/chat.js.map +1 -1
- package/ts_build/src/cli.js +10 -3
- package/ts_build/src/cli.js.map +1 -1
- package/ts_build/src/clients/anthropic.d.ts +5 -1
- package/ts_build/src/clients/anthropic.js +61 -112
- package/ts_build/src/clients/anthropic.js.map +1 -1
- package/ts_build/src/clients/gemini.d.ts +80 -2
- package/ts_build/src/clients/gemini.js +336 -74
- package/ts_build/src/clients/gemini.js.map +1 -1
- package/ts_build/src/clients/index.d.ts +9 -1
- package/ts_build/src/clients/index.js +65 -0
- package/ts_build/src/clients/index.js.map +1 -1
- package/ts_build/src/clients/knowhow.d.ts +9 -1
- package/ts_build/src/clients/knowhow.js +43 -0
- package/ts_build/src/clients/knowhow.js.map +1 -1
- package/ts_build/src/clients/openai.d.ts +9 -1
- package/ts_build/src/clients/openai.js +201 -133
- package/ts_build/src/clients/openai.js.map +1 -1
- package/ts_build/src/clients/pricing/anthropic.d.ts +17 -0
- package/ts_build/src/clients/pricing/anthropic.js +93 -0
- package/ts_build/src/clients/pricing/anthropic.js.map +1 -0
- package/ts_build/src/clients/pricing/google.d.ts +73 -0
- package/ts_build/src/clients/pricing/google.js +68 -0
- package/ts_build/src/clients/pricing/google.js.map +1 -0
- package/ts_build/src/clients/pricing/index.d.ts +4 -0
- package/ts_build/src/clients/pricing/index.js +14 -0
- package/ts_build/src/clients/pricing/index.js.map +1 -0
- package/ts_build/src/clients/pricing/openai.d.ts +7 -0
- package/ts_build/src/clients/pricing/openai.js +137 -0
- package/ts_build/src/clients/pricing/openai.js.map +1 -0
- package/ts_build/src/clients/pricing/xai.d.ts +26 -0
- package/ts_build/src/clients/pricing/xai.js +59 -0
- package/ts_build/src/clients/pricing/xai.js.map +1 -0
- package/ts_build/src/clients/types.d.ts +135 -0
- package/ts_build/src/clients/xai.d.ts +9 -1
- package/ts_build/src/clients/xai.js +178 -46
- package/ts_build/src/clients/xai.js.map +1 -1
- package/ts_build/src/config.d.ts +1 -0
- package/ts_build/src/config.js +45 -16
- package/ts_build/src/config.js.map +1 -1
- package/ts_build/src/embeddings.js +8 -1
- package/ts_build/src/embeddings.js.map +1 -1
- package/ts_build/src/microphone.js +7 -9
- package/ts_build/src/microphone.js.map +1 -1
- package/ts_build/src/migrations.d.ts +17 -0
- package/ts_build/src/migrations.js +86 -0
- package/ts_build/src/migrations.js.map +1 -0
- package/ts_build/src/plugins/AgentsMdPlugin.d.ts +13 -0
- package/ts_build/src/plugins/AgentsMdPlugin.js +118 -0
- package/ts_build/src/plugins/AgentsMdPlugin.js.map +1 -0
- package/ts_build/src/plugins/PluginBase.d.ts +1 -0
- package/ts_build/src/plugins/PluginBase.js +3 -0
- package/ts_build/src/plugins/PluginBase.js.map +1 -1
- package/ts_build/src/plugins/downloader/downloader.js +5 -5
- package/ts_build/src/plugins/downloader/downloader.js.map +1 -1
- package/ts_build/src/plugins/embedding.js +9 -8
- package/ts_build/src/plugins/embedding.js.map +1 -1
- package/ts_build/src/plugins/exec.d.ts +10 -0
- package/ts_build/src/plugins/exec.js +56 -0
- package/ts_build/src/plugins/exec.js.map +1 -0
- package/ts_build/src/plugins/github.js +93 -51
- package/ts_build/src/plugins/github.js.map +1 -1
- package/ts_build/src/plugins/language.js +14 -11
- package/ts_build/src/plugins/language.js.map +1 -1
- package/ts_build/src/plugins/plugins.d.ts +1 -0
- package/ts_build/src/plugins/plugins.js +19 -1
- package/ts_build/src/plugins/plugins.js.map +1 -1
- package/ts_build/src/plugins/tmux.d.ts +14 -0
- package/ts_build/src/plugins/tmux.js +108 -0
- package/ts_build/src/plugins/tmux.js.map +1 -0
- package/ts_build/src/plugins/types.d.ts +1 -0
- package/ts_build/src/plugins/vim.js +11 -1
- package/ts_build/src/plugins/vim.js.map +1 -1
- package/ts_build/src/services/AgentSyncFs.d.ts +34 -0
- package/ts_build/src/services/AgentSyncFs.js +325 -0
- package/ts_build/src/services/AgentSyncFs.js.map +1 -0
- package/ts_build/src/services/AgentSyncKnowhowWeb.d.ts +29 -0
- package/ts_build/src/services/AgentSyncKnowhowWeb.js +178 -0
- package/ts_build/src/services/AgentSyncKnowhowWeb.js.map +1 -0
- package/ts_build/src/services/AgentSynchronization.d.ts +1 -1
- package/ts_build/src/services/AgentSynchronization.js +3 -3
- package/ts_build/src/services/AgentSynchronization.js.map +1 -1
- package/ts_build/src/services/EventService.js.map +1 -1
- package/ts_build/src/services/KnowhowClient.d.ts +9 -1
- package/ts_build/src/services/KnowhowClient.js +58 -0
- package/ts_build/src/services/KnowhowClient.js.map +1 -1
- package/ts_build/src/services/index.d.ts +2 -1
- package/ts_build/src/services/index.js +2 -1
- package/ts_build/src/services/index.js.map +1 -1
- package/ts_build/src/types.d.ts +26 -1
- package/ts_build/src/types.js +45 -4
- package/ts_build/src/types.js.map +1 -1
- package/ts_build/src/utils/PersistentInputManager.d.ts +28 -0
- package/ts_build/src/utils/PersistentInputManager.js +293 -0
- package/ts_build/src/utils/PersistentInputManager.js.map +1 -0
- package/ts_build/src/worker.js +2 -2
- package/ts_build/src/worker.js.map +1 -1
- package/ts_build/tests/manual/modalities/google.modalities.test.d.ts +1 -0
- package/ts_build/tests/manual/modalities/google.modalities.test.js +252 -0
- package/ts_build/tests/manual/modalities/google.modalities.test.js.map +1 -0
- package/ts_build/tests/manual/modalities/openai.modalities.test.d.ts +1 -0
- package/ts_build/tests/manual/modalities/openai.modalities.test.js +252 -0
- package/ts_build/tests/manual/modalities/openai.modalities.test.js.map +1 -0
- package/ts_build/tests/manual/modalities/streaming.test.d.ts +1 -0
- package/ts_build/tests/manual/modalities/streaming.test.js +206 -0
- package/ts_build/tests/manual/modalities/streaming.test.js.map +1 -0
- package/ts_build/tests/manual/modalities/xai.modalities.test.d.ts +1 -0
- package/ts_build/tests/manual/modalities/xai.modalities.test.js +226 -0
- package/ts_build/tests/manual/modalities/xai.modalities.test.js.map +1 -0
- package/ts_build/tests/manual/persistent-input-test.d.ts +1 -0
- package/ts_build/tests/manual/persistent-input-test.js +35 -0
- package/ts_build/tests/manual/persistent-input-test.js.map +1 -0
- package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js +5 -5
- package/ts_build/tests/plugins/language/languagePlugin-content-triggers.test.js.map +1 -1
- package/ts_build/tests/plugins/language/languagePlugin-integration.test.js +1 -1
- package/ts_build/tests/plugins/language/languagePlugin-integration.test.js.map +1 -1
- package/ts_build/tests/plugins/language/languagePlugin.test.js +17 -7
- 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
package/src/agents/base/base.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { EventEmitter } from "events";
|
|
|
2
2
|
import {
|
|
3
3
|
GenericClient,
|
|
4
4
|
Message,
|
|
5
|
+
MessageContent,
|
|
5
6
|
OutputMessage,
|
|
6
7
|
Tool,
|
|
7
8
|
ToolCall,
|
|
@@ -318,7 +319,7 @@ export abstract class BaseAgent implements IAgent {
|
|
|
318
319
|
return this.summaries;
|
|
319
320
|
}
|
|
320
321
|
|
|
321
|
-
abstract getInitialMessages(userInput: string): Promise<Message[]>;
|
|
322
|
+
abstract getInitialMessages(userInput: string | MessageContent[]): Promise<Message[]>;
|
|
322
323
|
|
|
323
324
|
async processToolMessages(toolCall: ToolCall) {
|
|
324
325
|
this.agentEvents.emit(this.eventTypes.toolCall, { toolCall });
|
|
@@ -479,7 +480,7 @@ export abstract class BaseAgent implements IAgent {
|
|
|
479
480
|
} as Message);
|
|
480
481
|
}
|
|
481
482
|
|
|
482
|
-
async call(userInput: string, _messages?: Message[]) {
|
|
483
|
+
async call(userInput: string | MessageContent[], _messages?: Message[]) {
|
|
483
484
|
if (this.status === this.eventTypes.notStarted) {
|
|
484
485
|
this.status = this.eventTypes.inProgress;
|
|
485
486
|
}
|
|
@@ -758,9 +759,7 @@ export abstract class BaseAgent implements IAgent {
|
|
|
758
759
|
logStatus() {
|
|
759
760
|
const statusMessage = this.getStatusMessage();
|
|
760
761
|
console.log(
|
|
761
|
-
`\n● ${this.name} status:
|
|
762
|
-
3
|
|
763
|
-
)}\n${statusMessage}`
|
|
762
|
+
`\n● ${this.name} status: ${statusMessage}`
|
|
764
763
|
);
|
|
765
764
|
}
|
|
766
765
|
|
|
@@ -8,12 +8,10 @@ export class DeveloperAgent extends BaseAgent {
|
|
|
8
8
|
|
|
9
9
|
constructor(context: AgentContext) {
|
|
10
10
|
super(context);
|
|
11
|
-
this.disableTool("patchFile");
|
|
12
|
-
|
|
13
11
|
this.setModelPreferences([
|
|
14
12
|
{
|
|
15
|
-
model: Models.
|
|
16
|
-
provider: "
|
|
13
|
+
model: Models.anthropic.Sonnet4_6,
|
|
14
|
+
provider: "anthropic",
|
|
17
15
|
},
|
|
18
16
|
]);
|
|
19
17
|
}
|
|
@@ -32,8 +30,21 @@ export class DeveloperAgent extends BaseAgent {
|
|
|
32
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.
|
|
33
31
|
|
|
34
32
|
# How to call other agents
|
|
35
|
-
You can use the
|
|
36
|
-
|
|
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
|
|
37
48
|
|
|
38
49
|
# Which Agent to Use:
|
|
39
50
|
Researcher -
|
|
@@ -43,16 +54,13 @@ export class DeveloperAgent extends BaseAgent {
|
|
|
43
54
|
- General Questions about codebase or file structure
|
|
44
55
|
|
|
45
56
|
Patcher
|
|
57
|
+
- this is the default agent
|
|
46
58
|
- For making modifications to files / code
|
|
47
59
|
- Great for big files
|
|
48
60
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
2.a Do we have enough information to know exactly what to modify? If not, ask the Researcher.
|
|
53
|
-
2.b If we know what to modify, ask Patcher to make the changes with all the context required.
|
|
54
|
-
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.
|
|
55
|
-
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.
|
|
56
64
|
`,
|
|
57
65
|
},
|
|
58
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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { Tool } from "../../clients/types";
|
|
2
|
-
import
|
|
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
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
|
40
|
-
|
|
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
|
-
|
|
81
|
+
args.push("--provider", provider);
|
|
44
82
|
}
|
|
45
83
|
|
|
46
84
|
if (model) {
|
|
47
|
-
|
|
85
|
+
args.push("--model", model);
|
|
48
86
|
}
|
|
49
87
|
|
|
50
88
|
if (agentName) {
|
|
51
|
-
|
|
89
|
+
args.push("--agent-name", agentName);
|
|
52
90
|
}
|
|
53
91
|
|
|
54
92
|
if (maxTimeLimit !== undefined) {
|
|
55
|
-
|
|
93
|
+
args.push("--max-time-limit", String(maxTimeLimit));
|
|
56
94
|
}
|
|
57
95
|
|
|
58
96
|
if (maxSpendLimit !== undefined) {
|
|
59
|
-
|
|
97
|
+
args.push("--max-spend-limit", String(maxSpendLimit));
|
|
60
98
|
}
|
|
61
99
|
|
|
62
|
-
const
|
|
63
|
-
|
|
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: ["
|
|
215
|
+
required: ["prompt"],
|
|
107
216
|
},
|
|
108
217
|
},
|
|
109
218
|
};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
|
-
import {
|
|
3
|
-
import { lintFile } from ".";
|
|
2
|
+
import { services, ToolsService } from "../../services";
|
|
4
3
|
import { fileExists } from "../../utils";
|
|
5
4
|
|
|
6
5
|
export async function stringReplace(
|
|
@@ -8,6 +7,12 @@ export async function stringReplace(
|
|
|
8
7
|
replaceString: string,
|
|
9
8
|
filePaths: string[]
|
|
10
9
|
): Promise<string> {
|
|
10
|
+
// Get context from bound ToolsService
|
|
11
|
+
const toolService = (
|
|
12
|
+
this instanceof ToolsService ? this : services().Tools
|
|
13
|
+
) as ToolsService;
|
|
14
|
+
const context = toolService.getContext();
|
|
15
|
+
|
|
11
16
|
if (
|
|
12
17
|
!findString ||
|
|
13
18
|
replaceString === undefined ||
|
|
@@ -41,6 +46,20 @@ export async function stringReplace(
|
|
|
41
46
|
continue;
|
|
42
47
|
}
|
|
43
48
|
|
|
49
|
+
// Emit pre-edit blocking event
|
|
50
|
+
const eventResults: any[] = [];
|
|
51
|
+
if (context.Events) {
|
|
52
|
+
eventResults.push(
|
|
53
|
+
...(await context.Events.emitBlocking("file:pre-edit", {
|
|
54
|
+
filePath,
|
|
55
|
+
operation: "stringReplace",
|
|
56
|
+
findString,
|
|
57
|
+
replaceString,
|
|
58
|
+
originalContent,
|
|
59
|
+
}))
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
44
63
|
// Perform the replacement
|
|
45
64
|
const newContent = content.replace(
|
|
46
65
|
new RegExp(findString.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"),
|
|
@@ -53,18 +72,29 @@ export async function stringReplace(
|
|
|
53
72
|
totalReplacements += matches;
|
|
54
73
|
results.push(`✅ Replaced ${matches} occurrence(s) in: ${filePath}`);
|
|
55
74
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
75
|
+
// Emit post-edit blocking event to get event results
|
|
76
|
+
if (context.Events) {
|
|
77
|
+
eventResults.push(
|
|
78
|
+
...(await context.Events.emitBlocking("file:post-edit", {
|
|
79
|
+
filePath,
|
|
80
|
+
operation: "stringReplace",
|
|
81
|
+
findString,
|
|
82
|
+
replaceString,
|
|
83
|
+
originalContent,
|
|
84
|
+
updatedContent: newContent,
|
|
85
|
+
}))
|
|
86
|
+
);
|
|
65
87
|
}
|
|
66
88
|
|
|
67
|
-
|
|
89
|
+
// Format event results if any
|
|
90
|
+
if (eventResults && eventResults.length > 0) {
|
|
91
|
+
const eventResultsText = eventResults
|
|
92
|
+
.filter((r) => r && typeof r === "string" && r.trim())
|
|
93
|
+
.join("\n");
|
|
94
|
+
if (eventResultsText) {
|
|
95
|
+
results.push(eventResultsText);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
68
98
|
} catch (error) {
|
|
69
99
|
results.push(`❌ Error processing ${filePath}: ${error.message}`);
|
|
70
100
|
}
|