@tyvm/knowhow 0.0.69 → 0.0.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/shell-commands.md +174 -0
- package/package.json +1 -1
- package/src/agents/base/base.ts +1 -3
- 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/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 +41 -90
- 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.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/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 +18 -91
- 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
|
@@ -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:
|
|
762
|
-
3
|
|
763
|
-
)}\n${statusMessage}`
|
|
761
|
+
`\n● ${this.name} status: ${statusMessage}`
|
|
764
762
|
);
|
|
765
763
|
}
|
|
766
764
|
|
|
@@ -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
|
};
|
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
|
268
|
-
|
|
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;
|