wispy-cli 1.1.0 ā 1.1.2
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/README.md +488 -190
- package/core/cron.mjs +2 -0
- package/core/engine.mjs +3 -0
- package/core/providers.mjs +53 -12
- package/core/tools.mjs +24 -4
- package/lib/wispy-repl.mjs +3 -0
- package/package.json +25 -6
package/README.md
CHANGED
|
@@ -1,273 +1,571 @@
|
|
|
1
1
|
# šæ Wispy CLI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Your personal AI assistant ā chat, automate, and orchestrate from the terminal.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/wispy-cli)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
Wispy is a powerful terminal AI assistant that talks to your files, runs commands, orchestrates sub-agents, sends messages to Telegram/Discord/Slack, and remembers things across sessions ā all from a single `wispy` command.
|
|
10
|
+
|
|
11
|
+
## ⨠Features
|
|
12
|
+
|
|
13
|
+
- š¬ **Interactive REPL + TUI** ā chat mode, slash commands, streaming responses
|
|
14
|
+
- š¤ **7 AI providers** ā Gemini, Claude, OpenAI, Groq, DeepSeek, Ollama, OpenRouter
|
|
15
|
+
- š§ **18+ built-in tools** ā files, shell commands, git, web search, and more
|
|
16
|
+
- š **MCP server integration** ā connect any Model Context Protocol server
|
|
17
|
+
- š± **Multi-channel bot** ā Telegram, Discord, Slack with session isolation
|
|
18
|
+
- š§ **Long-term memory** ā persistent facts, daily logs, project notes
|
|
19
|
+
- ā° **Cron scheduler** ā scheduled tasks with channel delivery
|
|
20
|
+
- š¤ **Sub-agent orchestration** ā spawn parallel agents, steer them, collect results
|
|
21
|
+
- š **Workstream isolation** ā separate contexts per project
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## š Quick Start
|
|
6
26
|
|
|
7
27
|
```bash
|
|
28
|
+
# Install globally
|
|
8
29
|
npm install -g wispy-cli
|
|
9
|
-
|
|
30
|
+
|
|
31
|
+
# Set your API key (any of these)
|
|
32
|
+
export GOOGLE_AI_KEY=your_key # Gemini (recommended, has free tier)
|
|
33
|
+
export ANTHROPIC_API_KEY=your_key # Claude
|
|
34
|
+
export OPENAI_API_KEY=your_key # OpenAI / GPT
|
|
35
|
+
|
|
36
|
+
# Start chatting
|
|
10
37
|
wispy
|
|
11
38
|
```
|
|
12
39
|
|
|
13
|
-
|
|
40
|
+
Wispy auto-detects your API key and starts the REPL immediately.
|
|
14
41
|
|
|
15
|
-
|
|
42
|
+
---
|
|
16
43
|
|
|
17
|
-
|
|
44
|
+
## š¦ Installation
|
|
45
|
+
|
|
46
|
+
### Requirements
|
|
47
|
+
- **Node.js >= 18** (ESM modules required)
|
|
48
|
+
|
|
49
|
+
### Global install
|
|
18
50
|
```bash
|
|
19
|
-
|
|
20
|
-
wispy "read my package.json" # One-shot with tool use
|
|
21
|
-
wispy --continue "and fix it"# Continue previous session
|
|
22
|
-
wispy --new "fresh start" # New session
|
|
51
|
+
npm install -g wispy-cli
|
|
23
52
|
```
|
|
24
53
|
|
|
25
|
-
|
|
54
|
+
### Local install
|
|
55
|
+
```bash
|
|
56
|
+
npm install wispy-cli
|
|
57
|
+
npx wispy
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## š£ļø Chat Modes
|
|
26
63
|
|
|
27
|
-
###
|
|
64
|
+
### REPL (default)
|
|
28
65
|
```bash
|
|
29
|
-
wispy
|
|
30
|
-
wispy -w
|
|
31
|
-
wispy
|
|
32
|
-
wispy search "auth" # Search across all workstreams
|
|
66
|
+
wispy # start interactive chat
|
|
67
|
+
wispy -w myproject # start in a specific workstream
|
|
68
|
+
wispy --session my-ses # resume a named session
|
|
33
69
|
```
|
|
34
70
|
|
|
35
|
-
|
|
71
|
+
**REPL slash commands:**
|
|
72
|
+
```
|
|
73
|
+
/help ā show all slash commands
|
|
74
|
+
/clear ā reset conversation history
|
|
75
|
+
/sessions ā list recent sessions
|
|
76
|
+
/memory ā show memory files
|
|
77
|
+
/tools ā list available tools
|
|
78
|
+
/model ā show current AI model
|
|
79
|
+
/workstream ā show active workstream
|
|
80
|
+
/exit ā exit Wispy
|
|
81
|
+
```
|
|
36
82
|
|
|
37
|
-
###
|
|
83
|
+
### TUI (Ink-based)
|
|
38
84
|
```bash
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# šØ worker ā implementation
|
|
43
|
-
# š reviewer ā code review & QA
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
#### Execution Modes
|
|
47
|
-
- **Pipeline** ā Sequential chaining: `explore ā plan ā implement ā review`
|
|
48
|
-
- **Ralph mode** ā Persistence: retry until reviewer confirms done (max 5 rounds)
|
|
49
|
-
- **Async agents** ā Fire-and-forget background tasks
|
|
50
|
-
|
|
51
|
-
### 7 Providers (Auto-detected)
|
|
52
|
-
|
|
53
|
-
| Provider | Env Variable | Free Tier |
|
|
54
|
-
|----------|-------------|-----------|
|
|
55
|
-
| Google AI (Gemini) | `GOOGLE_AI_KEY` | ā
|
|
|
56
|
-
| Anthropic (Claude) | `ANTHROPIC_API_KEY` | ā |
|
|
57
|
-
| OpenAI (GPT-4o) | `OPENAI_API_KEY` | ā |
|
|
58
|
-
| OpenRouter | `OPENROUTER_API_KEY` | Some |
|
|
59
|
-
| Groq | `GROQ_API_KEY` | ā
|
|
|
60
|
-
| DeepSeek | `DEEPSEEK_API_KEY` | ā
|
|
|
61
|
-
| Ollama (local) | `OLLAMA_HOST` | ā
|
|
|
62
|
-
|
|
63
|
-
Also auto-detects macOS Keychain credentials.
|
|
64
|
-
|
|
65
|
-
### Cost Optimization
|
|
66
|
-
- **Smart model routing** ā simple tasks ā cheap model, complex ā mid, critical ā expensive
|
|
67
|
-
- **Per-workstream budgets** ā `/budget set 5.00` to cap spending
|
|
68
|
-
- **Context window optimization** ā auto-trims old messages to save tokens
|
|
69
|
-
- **Token tracking** ā see cost after every response
|
|
70
|
-
|
|
71
|
-
### Security
|
|
72
|
-
- Sandboxed command execution (macOS Seatbelt)
|
|
73
|
-
- Process hardening (core dump disable, ptrace block)
|
|
74
|
-
- Audit logging (JSON Lines)
|
|
75
|
-
- Rate limiting + CORS restriction
|
|
76
|
-
- API key isolation per workstream
|
|
77
|
-
|
|
78
|
-
## In-Session Commands
|
|
79
|
-
|
|
80
|
-
| Command | Description |
|
|
81
|
-
|---------|-------------|
|
|
82
|
-
| `/help` | Show all commands |
|
|
83
|
-
| `/compact` | Compress conversation history |
|
|
84
|
-
| `/memory <type> <text>` | Save to persistent memory |
|
|
85
|
-
| `/budget` | Show spending per workstream |
|
|
86
|
-
| `/budget set <usd>` | Set budget limit |
|
|
87
|
-
| `/overview` | Director view ā all workstreams |
|
|
88
|
-
| `/search <keyword>` | Search across workstreams |
|
|
89
|
-
| `/cost` | Show session token usage |
|
|
90
|
-
| `/workstreams` | List all workstreams |
|
|
91
|
-
| `/provider` | Show current provider |
|
|
92
|
-
| `/model [name]` | Show/change model |
|
|
93
|
-
| `/clear` | Reset conversation |
|
|
94
|
-
| `/quit` | Exit |
|
|
95
|
-
|
|
96
|
-
## Project Context (WISPY.md)
|
|
97
|
-
|
|
98
|
-
Create a `WISPY.md` in your project root or `~/.wispy/WISPY.md`. It gets loaded into every conversation turn (up to 40K chars), just like Claude Code's `CLAUDE.md`.
|
|
99
|
-
|
|
100
|
-
```markdown
|
|
101
|
-
# WISPY.md
|
|
102
|
-
## About this project
|
|
103
|
-
- Next.js 15 app with TypeScript
|
|
104
|
-
- Use Tailwind CSS
|
|
105
|
-
- Korean comments preferred
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
## Workstream Context (work.md)
|
|
109
|
-
|
|
110
|
-
Each workstream can have its own `work.md` ā loaded every turn, like WISPY.md but scoped to that workstream.
|
|
85
|
+
wispy-tui # full terminal UI with scrollable history
|
|
86
|
+
wispy-tui -w myproject # workstream-specific TUI
|
|
87
|
+
```
|
|
111
88
|
|
|
89
|
+
### One-shot (non-interactive)
|
|
112
90
|
```bash
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
/work # View current work.md
|
|
91
|
+
wispy ask "What files are in the current directory?"
|
|
92
|
+
wispy exec "summarize README.md"
|
|
116
93
|
```
|
|
117
94
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
95
|
+
### Overview
|
|
96
|
+
```bash
|
|
97
|
+
wispy overview # show all workstreams and recent activity
|
|
98
|
+
```
|
|
122
99
|
|
|
123
|
-
|
|
124
|
-
# backend
|
|
125
|
-
## Goals
|
|
126
|
-
- Migrate to PostgreSQL
|
|
127
|
-
- Add rate limiting
|
|
100
|
+
---
|
|
128
101
|
|
|
129
|
-
##
|
|
130
|
-
- Using Express.js + TypeORM
|
|
131
|
-
- Staging server: staging.example.com
|
|
132
|
-
```
|
|
102
|
+
## š¤ AI Providers
|
|
133
103
|
|
|
134
|
-
|
|
104
|
+
Wispy detects your configured provider automatically. Priority order:
|
|
135
105
|
|
|
136
|
-
|
|
106
|
+
| Provider | Env Var | Default Model |
|
|
107
|
+
|----------|---------|---------------|
|
|
108
|
+
| **Google AI** (Gemini) | `GOOGLE_AI_KEY` or `GEMINI_API_KEY` | `gemini-2.5-flash` |
|
|
109
|
+
| **Anthropic** (Claude) | `ANTHROPIC_API_KEY` | `claude-sonnet-4-20250514` |
|
|
110
|
+
| **OpenAI** | `OPENAI_API_KEY` | `gpt-4o` |
|
|
111
|
+
| **OpenRouter** | `OPENROUTER_API_KEY` | `anthropic/claude-sonnet-4` |
|
|
112
|
+
| **Groq** | `GROQ_API_KEY` | `llama-3.3-70b-versatile` |
|
|
113
|
+
| **DeepSeek** | `DEEPSEEK_API_KEY` | `deepseek-chat` |
|
|
114
|
+
| **Ollama** (local) | `OLLAMA_HOST` | `llama3.2` |
|
|
137
115
|
|
|
116
|
+
### Override provider
|
|
138
117
|
```bash
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
/memory feedback "don't use var, use const"
|
|
142
|
-
/memory references "API docs: https://..."
|
|
118
|
+
WISPY_PROVIDER=anthropic wispy
|
|
119
|
+
WISPY_MODEL=claude-3-5-haiku wispy
|
|
143
120
|
```
|
|
144
121
|
|
|
145
|
-
|
|
122
|
+
### Config file
|
|
123
|
+
```json
|
|
124
|
+
// ~/.wispy/config.json
|
|
125
|
+
{
|
|
126
|
+
"provider": "google",
|
|
127
|
+
"model": "gemini-2.5-flash",
|
|
128
|
+
"apiKey": "optional-if-not-in-env"
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## š§ Built-in Tools
|
|
135
|
+
|
|
136
|
+
Wispy gives the AI access to these tools automatically:
|
|
146
137
|
|
|
147
|
-
|
|
138
|
+
| Tool | Description |
|
|
139
|
+
|------|-------------|
|
|
140
|
+
| `read_file` | Read any file's contents |
|
|
141
|
+
| `write_file` | Write or create files |
|
|
142
|
+
| `file_edit` | Edit files with find-and-replace |
|
|
143
|
+
| `file_search` | Search files with regex (like grep) |
|
|
144
|
+
| `list_directory` | List directory contents |
|
|
145
|
+
| `run_command` | Execute shell commands |
|
|
146
|
+
| `git_status` / `git_diff` / `git_log` | Git operations |
|
|
147
|
+
| `web_search` | Search the web |
|
|
148
|
+
| `memory_save` | Save facts to persistent memory |
|
|
149
|
+
| `memory_get` | Retrieve a memory by key |
|
|
150
|
+
| `memory_search` | Full-text search across all memories |
|
|
151
|
+
| `memory_list` | List all memory files |
|
|
152
|
+
| `memory_append` | Append to a memory file |
|
|
153
|
+
| `memory_delete` | Delete a memory file |
|
|
154
|
+
| `spawn_subagent` | Launch a parallel sub-agent |
|
|
155
|
+
| `list_subagents` | List running sub-agents |
|
|
156
|
+
| `get_subagent_result` | Get a sub-agent's result |
|
|
157
|
+
| `kill_subagent` | Cancel a sub-agent |
|
|
148
158
|
|
|
149
|
-
|
|
159
|
+
---
|
|
150
160
|
|
|
161
|
+
## š± Channel Setup
|
|
162
|
+
|
|
163
|
+
Run Wispy as a multi-platform bot that responds to messages in Telegram, Discord, or Slack.
|
|
164
|
+
|
|
165
|
+
### Start the bot server
|
|
151
166
|
```bash
|
|
152
|
-
#
|
|
153
|
-
wispy
|
|
154
|
-
|
|
155
|
-
|
|
167
|
+
wispy channels start # start all configured channels
|
|
168
|
+
wispy channels start --telegram # start only Telegram
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Telegram
|
|
172
|
+
1. Create a bot via [@BotFather](https://t.me/BotFather)
|
|
173
|
+
2. Copy the bot token
|
|
174
|
+
3. Configure:
|
|
175
|
+
```bash
|
|
176
|
+
# Option 1: env var
|
|
177
|
+
export WISPY_TELEGRAM_TOKEN=your_bot_token
|
|
178
|
+
|
|
179
|
+
# Option 2: config file
|
|
180
|
+
# ~/.wispy/channels.json
|
|
181
|
+
{
|
|
182
|
+
"telegram": {
|
|
183
|
+
"enabled": true,
|
|
184
|
+
"token": "your_bot_token"
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Discord
|
|
190
|
+
1. Create a bot at [discord.com/developers](https://discord.com/developers/applications)
|
|
191
|
+
2. Install peer dependency: `npm install -g discord.js`
|
|
192
|
+
3. Configure:
|
|
193
|
+
```json
|
|
194
|
+
{
|
|
195
|
+
"discord": {
|
|
196
|
+
"enabled": true,
|
|
197
|
+
"token": "your_discord_token",
|
|
198
|
+
"clientId": "your_client_id",
|
|
199
|
+
"guildId": "optional_guild_id"
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Slack
|
|
205
|
+
1. Create a Slack app at [api.slack.com](https://api.slack.com/apps)
|
|
206
|
+
2. Install peer dependency: `npm install -g @slack/bolt`
|
|
207
|
+
3. Configure:
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"slack": {
|
|
211
|
+
"enabled": true,
|
|
212
|
+
"botToken": "xoxb-your-token",
|
|
213
|
+
"appToken": "xapp-your-token",
|
|
214
|
+
"signingSecret": "your-signing-secret"
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Each chat/user gets an isolated session ā Wispy remembers context per conversation.
|
|
156
220
|
|
|
157
|
-
|
|
158
|
-
wispy --serve
|
|
221
|
+
---
|
|
159
222
|
|
|
160
|
-
|
|
161
|
-
wispy --telegram
|
|
162
|
-
wispy --discord
|
|
163
|
-
wispy --slack
|
|
223
|
+
## š§ Memory System
|
|
164
224
|
|
|
165
|
-
|
|
166
|
-
wispy channel list
|
|
225
|
+
Wispy has a file-based long-term memory stored in `~/.wispy/memory/`.
|
|
167
226
|
|
|
168
|
-
|
|
169
|
-
|
|
227
|
+
### Using memory in chat
|
|
228
|
+
Just ask Wispy to remember things:
|
|
229
|
+
```
|
|
230
|
+
You: Remember that my project uses TypeScript and Next.js 15
|
|
231
|
+
Wispy: Got it! I'll remember that. šæ
|
|
170
232
|
```
|
|
171
233
|
|
|
172
|
-
|
|
234
|
+
Or use memory tools explicitly:
|
|
235
|
+
```
|
|
236
|
+
You: Save this to memory: the deployment command is `vercel --prod`
|
|
237
|
+
```
|
|
173
238
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
239
|
+
### Memory structure
|
|
240
|
+
```
|
|
241
|
+
~/.wispy/memory/
|
|
242
|
+
āāā MEMORY.md ā main persistent memory (auto-included in every chat)
|
|
243
|
+
āāā user.md ā user preferences
|
|
244
|
+
āāā daily/
|
|
245
|
+
ā āāā 2026-04-03.md ā daily logs
|
|
246
|
+
āāā projects/
|
|
247
|
+
āāā myapp.md ā project-specific notes
|
|
248
|
+
```
|
|
177
249
|
|
|
178
|
-
|
|
250
|
+
### Memory CLI
|
|
179
251
|
```bash
|
|
180
|
-
|
|
181
|
-
wispy
|
|
252
|
+
wispy memory list # list all memory files
|
|
253
|
+
wispy memory get MEMORY # show main memory
|
|
254
|
+
wispy memory save user "I prefer TypeScript"
|
|
255
|
+
wispy memory search "deployment" # full-text search
|
|
256
|
+
wispy memory delete daily/old-date
|
|
182
257
|
```
|
|
183
258
|
|
|
184
|
-
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## ā° Cron Scheduler
|
|
185
262
|
|
|
186
|
-
|
|
263
|
+
Schedule tasks to run automatically and optionally deliver results to a channel.
|
|
187
264
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
265
|
+
### Add a scheduled job
|
|
266
|
+
```bash
|
|
267
|
+
# Every morning briefing
|
|
268
|
+
wispy cron add \
|
|
269
|
+
--name "Morning Briefing" \
|
|
270
|
+
--schedule "0 9 * * *" \
|
|
271
|
+
--task "Summarize my pending tasks and give me a morning overview" \
|
|
272
|
+
--channel "telegram:YOUR_CHAT_ID"
|
|
273
|
+
|
|
274
|
+
# Every 30 minutes
|
|
275
|
+
wispy cron add \
|
|
276
|
+
--name "Price Check" \
|
|
277
|
+
--schedule every:1800000 \
|
|
278
|
+
--task "Check the current price of BTC and ETH"
|
|
279
|
+
|
|
280
|
+
# One-shot at specific time
|
|
281
|
+
wispy cron add \
|
|
282
|
+
--name "Reminder" \
|
|
283
|
+
--schedule "at:2026-12-31T09:00:00Z" \
|
|
284
|
+
--task "Remind me it's New Year's Eve!"
|
|
285
|
+
```
|
|
193
286
|
|
|
194
|
-
|
|
287
|
+
### Schedule formats
|
|
288
|
+
| Format | Example | Description |
|
|
289
|
+
|--------|---------|-------------|
|
|
290
|
+
| Cron expression | `0 9 * * *` | Standard 5-field cron (daily at 9am) |
|
|
291
|
+
| Interval | `every:1800000` | Every N milliseconds (30 min) |
|
|
292
|
+
| One-shot | `at:2026-12-31T09:00:00Z` | Specific ISO timestamp |
|
|
293
|
+
|
|
294
|
+
### Manage jobs
|
|
195
295
|
```bash
|
|
196
|
-
|
|
197
|
-
wispy
|
|
296
|
+
wispy cron list # list all jobs
|
|
297
|
+
wispy cron run <id> # run a job immediately
|
|
298
|
+
wispy cron remove <id> # delete a job
|
|
299
|
+
wispy cron history <id> # view execution history
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Start the scheduler
|
|
303
|
+
```bash
|
|
304
|
+
wispy cron start # start the cron scheduler daemon
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## š¤ Sub-agents
|
|
310
|
+
|
|
311
|
+
Delegate complex tasks to parallel sub-agents and collect results.
|
|
312
|
+
|
|
313
|
+
### From chat
|
|
314
|
+
```
|
|
315
|
+
You: Spawn a sub-agent to analyze all TypeScript files in src/ and report issues
|
|
316
|
+
Wispy: Spawning sub-agent 'code-review'... šæ
|
|
198
317
|
```
|
|
199
318
|
|
|
200
|
-
|
|
319
|
+
### Sub-agent tools (available to Wispy in chat)
|
|
320
|
+
- `spawn_subagent` ā start a sub-agent with a task
|
|
321
|
+
- `list_subagents` ā see all active/recent sub-agents
|
|
322
|
+
- `get_subagent_result` ā wait for and retrieve results
|
|
323
|
+
- `kill_subagent` ā cancel a running sub-agent
|
|
324
|
+
- `steer_subagent` ā send mid-task guidance
|
|
201
325
|
|
|
202
|
-
###
|
|
326
|
+
### Sub-agent lifecycle
|
|
327
|
+
```
|
|
328
|
+
pending ā running ā completed
|
|
329
|
+
ā failed
|
|
330
|
+
ā killed
|
|
331
|
+
ā timeout
|
|
332
|
+
```
|
|
203
333
|
|
|
204
|
-
|
|
205
|
-
2. Enable **Socket Mode** ā copy App-Level Token (`xapp-...`)
|
|
206
|
-
3. OAuth & Permissions ā copy Bot User OAuth Token (`xoxb-...`)
|
|
207
|
-
4. Subscribe to `app_mention` + `message.im` events
|
|
208
|
-
5. Add `/wispy` slash command
|
|
209
|
-
6. Run `wispy channel setup slack`
|
|
334
|
+
Sub-agent results persist to `~/.wispy/subagents/` for later retrieval.
|
|
210
335
|
|
|
211
|
-
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## š MCP Integration
|
|
339
|
+
|
|
340
|
+
Connect any [Model Context Protocol](https://modelcontextprotocol.io) server and Wispy will automatically use its tools.
|
|
341
|
+
|
|
342
|
+
### Configuration
|
|
343
|
+
```json
|
|
344
|
+
// ~/.wispy/mcp.json
|
|
345
|
+
{
|
|
346
|
+
"servers": {
|
|
347
|
+
"filesystem": {
|
|
348
|
+
"command": "npx",
|
|
349
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"]
|
|
350
|
+
},
|
|
351
|
+
"github": {
|
|
352
|
+
"command": "npx",
|
|
353
|
+
"args": ["-y", "@modelcontextprotocol/server-github"],
|
|
354
|
+
"env": { "GITHUB_TOKEN": "your_token" }
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### MCP CLI
|
|
212
361
|
```bash
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
wispy
|
|
362
|
+
wispy mcp list # list configured servers
|
|
363
|
+
wispy mcp status # show connected servers and their tools
|
|
364
|
+
wispy mcp add # interactive server setup
|
|
216
365
|
```
|
|
217
366
|
|
|
218
|
-
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## āļø Configuration
|
|
370
|
+
|
|
371
|
+
All configuration lives in `~/.wispy/`:
|
|
219
372
|
|
|
220
|
-
|
|
373
|
+
```
|
|
374
|
+
~/.wispy/
|
|
375
|
+
āāā config.json ā main config (provider, model, apiKey)
|
|
376
|
+
āāā channels.json ā channel bot configuration
|
|
377
|
+
āāā mcp.json ā MCP server configuration
|
|
378
|
+
āāā sessions/ ā conversation history
|
|
379
|
+
āāā memory/ ā long-term memory files
|
|
380
|
+
āāā cron/ ā scheduler jobs and history
|
|
381
|
+
āāā subagents/ ā sub-agent results
|
|
382
|
+
āāā audit/ ā audit log (JSONL per day)
|
|
383
|
+
```
|
|
221
384
|
|
|
222
|
-
|
|
385
|
+
### config.json reference
|
|
223
386
|
```json
|
|
224
387
|
{
|
|
225
|
-
"
|
|
226
|
-
"
|
|
227
|
-
"
|
|
388
|
+
"provider": "google", // auto-detected if omitted
|
|
389
|
+
"model": "gemini-2.5-flash", // defaults to provider's default
|
|
390
|
+
"apiKey": "...", // env var takes precedence
|
|
391
|
+
"workstream": "default" // default workstream
|
|
228
392
|
}
|
|
229
393
|
```
|
|
230
394
|
|
|
231
|
-
###
|
|
395
|
+
### Environment variables
|
|
396
|
+
```bash
|
|
397
|
+
WISPY_PROVIDER=google # force provider
|
|
398
|
+
WISPY_MODEL=gemini-2.5-flash # force model
|
|
399
|
+
WISPY_WORKSTREAM=myproject # set active workstream
|
|
400
|
+
WISPY_DEBUG=1 # enable debug output
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
---
|
|
232
404
|
|
|
233
|
-
|
|
405
|
+
## š Workstream Isolation
|
|
406
|
+
|
|
407
|
+
Workstreams let you maintain separate AI contexts for different projects.
|
|
234
408
|
|
|
235
409
|
```bash
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
410
|
+
wispy -w frontend # chat in "frontend" workstream
|
|
411
|
+
wispy -w backend # completely separate context
|
|
412
|
+
wispy overview # see all workstreams
|
|
239
413
|
```
|
|
240
414
|
|
|
241
|
-
Each
|
|
415
|
+
Each workstream has its own:
|
|
416
|
+
- Conversation sessions
|
|
417
|
+
- Context/memory scope
|
|
418
|
+
- Sub-agent history
|
|
419
|
+
|
|
420
|
+
---
|
|
421
|
+
|
|
422
|
+
## šļø Architecture
|
|
423
|
+
|
|
424
|
+
```
|
|
425
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
426
|
+
ā WispyEngine ā
|
|
427
|
+
ā core/engine.mjs ā
|
|
428
|
+
ā processMessage() āāāāāāāāāāā
|
|
429
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
430
|
+
ā ā
|
|
431
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāā ā
|
|
432
|
+
ā ā ā ā ā
|
|
433
|
+
REPL TUI channels server.mjs ā
|
|
434
|
+
(bin/wispy) (bin/wispy-tui) (lib/channels/) ā
|
|
435
|
+
ā
|
|
436
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
437
|
+
ā
|
|
438
|
+
āāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
439
|
+
ā ā ā
|
|
440
|
+
ProviderRegistry ToolRegistry SessionManager
|
|
441
|
+
core/providers.mjs core/tools.mjs core/session.mjs
|
|
442
|
+
(Gemini/Claude/ (18+ built-in + (file-based,
|
|
443
|
+
OpenAI/Groq/ MCP tools) per-channel)
|
|
444
|
+
DeepSeek/Ollama/
|
|
445
|
+
OpenRouter)
|
|
446
|
+
ā
|
|
447
|
+
āāā MemoryManager CronManager SubAgentManager
|
|
448
|
+
core/memory.mjs core/cron.mjs core/subagents.mjs
|
|
449
|
+
(~/.wispy/ (~/.wispy/ (~/.wispy/
|
|
450
|
+
memory/) cron/) subagents/)
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
All surfaces (REPL, TUI, channels) call `WispyEngine.processMessage()` ā one core, many interfaces.
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
457
|
+
## š ļø Development
|
|
458
|
+
|
|
459
|
+
```bash
|
|
460
|
+
# Clone
|
|
461
|
+
git clone https://github.com/mindsurf0176-ui/poropo-workspace
|
|
462
|
+
cd wispy-cli
|
|
242
463
|
|
|
243
|
-
|
|
464
|
+
# Install
|
|
465
|
+
npm install
|
|
244
466
|
|
|
245
|
-
|
|
246
|
-
|
|
467
|
+
# Run tests (91 integration tests)
|
|
468
|
+
npm test
|
|
247
469
|
|
|
248
|
-
|
|
470
|
+
# Development REPL
|
|
471
|
+
node bin/wispy.mjs
|
|
472
|
+
```
|
|
249
473
|
|
|
250
|
-
|
|
474
|
+
### Running tests
|
|
475
|
+
```bash
|
|
476
|
+
npm test # run all integration tests
|
|
477
|
+
npm run test:verbose # run with spec reporter
|
|
478
|
+
npm run test:basic # run basic smoke tests only
|
|
479
|
+
```
|
|
251
480
|
|
|
481
|
+
### Project structure
|
|
482
|
+
```
|
|
483
|
+
wispy-cli/
|
|
484
|
+
āāā bin/
|
|
485
|
+
ā āāā wispy.mjs ā main CLI entry point
|
|
486
|
+
ā āāā wispy-tui.mjs ā TUI entry point (Ink)
|
|
487
|
+
āāā core/
|
|
488
|
+
ā āāā engine.mjs ā WispyEngine (main chat loop)
|
|
489
|
+
ā āāā session.mjs ā SessionManager
|
|
490
|
+
ā āāā providers.mjs ā ProviderRegistry (7 AI providers)
|
|
491
|
+
ā āāā tools.mjs ā ToolRegistry (18+ built-in tools)
|
|
492
|
+
ā āāā memory.mjs ā MemoryManager
|
|
493
|
+
ā āāā cron.mjs ā CronManager (scheduler)
|
|
494
|
+
ā āāā subagents.mjs ā SubAgentManager
|
|
495
|
+
ā āāā mcp.mjs ā MCP client/manager
|
|
496
|
+
ā āāā config.mjs ā config loader + provider detection
|
|
497
|
+
ā āāā index.mjs ā public API exports
|
|
498
|
+
āāā lib/
|
|
499
|
+
ā āāā wispy-repl.mjs ā REPL implementation
|
|
500
|
+
ā āāā wispy-tui.mjs ā TUI implementation (Ink/React)
|
|
501
|
+
ā āāā channels/
|
|
502
|
+
ā āāā index.mjs ā ChannelManager
|
|
503
|
+
ā āāā telegram.mjs ā Telegram adapter (grammY)
|
|
504
|
+
ā āāā discord.mjs ā Discord adapter (discord.js)
|
|
505
|
+
ā āāā slack.mjs ā Slack adapter (@slack/bolt)
|
|
506
|
+
ā āāā base.mjs ā ChannelAdapter base class
|
|
507
|
+
āāā tests/
|
|
508
|
+
āāā core-engine.test.mjs
|
|
509
|
+
āāā core-session.test.mjs
|
|
510
|
+
āāā core-memory.test.mjs
|
|
511
|
+
āāā core-cron.test.mjs
|
|
512
|
+
āāā core-subagents.test.mjs
|
|
513
|
+
āāā core-tools.test.mjs
|
|
514
|
+
āāā channels.test.mjs
|
|
252
515
|
```
|
|
253
|
-
wispy (CLI REPL / TUI)
|
|
254
|
-
ā Gemini/Claude/OpenAI API (tool calling)
|
|
255
|
-
ā AWOS Server (auto-started, sandboxed tools)
|
|
256
|
-
ā Local Node (macOS file/browser/app actions)
|
|
257
516
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
517
|
+
---
|
|
518
|
+
|
|
519
|
+
## š CLI Reference
|
|
520
|
+
|
|
521
|
+
```
|
|
522
|
+
Usage: wispy [options] [command]
|
|
523
|
+
|
|
524
|
+
Commands:
|
|
525
|
+
wispy ā start interactive REPL
|
|
526
|
+
wispy ask <message> ā one-shot chat
|
|
527
|
+
wispy exec <message> ā one-shot execution
|
|
528
|
+
wispy overview ā show workstream overview
|
|
529
|
+
wispy sessions ā list recent sessions
|
|
530
|
+
wispy memory <cmd> ā manage long-term memory
|
|
531
|
+
wispy cron <cmd> ā manage scheduled jobs
|
|
532
|
+
wispy channels <cmd> ā manage channel bots
|
|
533
|
+
wispy mcp <cmd> ā manage MCP servers
|
|
534
|
+
wispy server <cmd> ā start/stop HTTP/WS server
|
|
535
|
+
wispy audit <cmd> ā view audit log
|
|
536
|
+
|
|
537
|
+
Options:
|
|
538
|
+
-w, --workstream <name> ā set active workstream
|
|
539
|
+
--session <id> ā resume a specific session
|
|
540
|
+
--model <name> ā override AI model
|
|
541
|
+
--provider <name> ā override AI provider
|
|
542
|
+
--help ā show help
|
|
543
|
+
--version ā show version
|
|
263
544
|
```
|
|
264
545
|
|
|
265
|
-
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
## š API Keys
|
|
549
|
+
|
|
550
|
+
Get free/paid API keys from:
|
|
551
|
+
- **Google AI (Gemini)** ā [aistudio.google.com/apikey](https://aistudio.google.com/apikey) *(free tier available)*
|
|
552
|
+
- **Anthropic (Claude)** ā [console.anthropic.com/settings/keys](https://console.anthropic.com/settings/keys)
|
|
553
|
+
- **OpenAI** ā [platform.openai.com/api-keys](https://platform.openai.com/api-keys)
|
|
554
|
+
- **OpenRouter** ā [openrouter.ai/keys](https://openrouter.ai/keys) *(free credits)*
|
|
555
|
+
- **Groq** ā [console.groq.com/keys](https://console.groq.com/keys) *(free tier)*
|
|
556
|
+
- **DeepSeek** ā [platform.deepseek.com/api_keys](https://platform.deepseek.com/api_keys)
|
|
557
|
+
- **Ollama** ā [ollama.ai](https://ollama.ai) *(fully local, no key needed)*
|
|
558
|
+
|
|
559
|
+
---
|
|
560
|
+
|
|
561
|
+
## š License
|
|
562
|
+
|
|
563
|
+
MIT ā see [LICENSE](LICENSE)
|
|
564
|
+
|
|
565
|
+
---
|
|
266
566
|
|
|
267
|
-
|
|
268
|
-
- One API key (Google AI recommended for free tier)
|
|
269
|
-
- macOS or Linux
|
|
567
|
+
## š Credits
|
|
270
568
|
|
|
271
|
-
|
|
569
|
+
Built by Minseo & Poropo š«§
|
|
272
570
|
|
|
273
|
-
|
|
571
|
+
*Wispy is a small ghost that lives in terminals ā floating between code, files, and servers.*
|
package/core/cron.mjs
CHANGED
|
@@ -119,6 +119,8 @@ export class CronManager {
|
|
|
119
119
|
this._running = true;
|
|
120
120
|
this._tick();
|
|
121
121
|
this._timer = setInterval(() => this._tick(), TICK_INTERVAL_MS);
|
|
122
|
+
// unref() so the timer doesn't prevent the process from exiting
|
|
123
|
+
if (this._timer?.unref) this._timer.unref();
|
|
122
124
|
console.error(`[wispy-cron] Scheduler started (checking every ${TICK_INTERVAL_MS / 1000}s)`);
|
|
123
125
|
}
|
|
124
126
|
|
package/core/engine.mjs
CHANGED
|
@@ -1009,6 +1009,9 @@ export class WispyEngine {
|
|
|
1009
1009
|
|
|
1010
1010
|
destroy() {
|
|
1011
1011
|
try { this.mcpManager.disconnectAll(); } catch {}
|
|
1012
|
+
try { this.sessions.destroyAll?.(); } catch {}
|
|
1013
|
+
try { this.subagents.killAll?.(); } catch {}
|
|
1014
|
+
try { this.audit.close?.(); } catch {}
|
|
1012
1015
|
}
|
|
1013
1016
|
}
|
|
1014
1017
|
|
package/core/providers.mjs
CHANGED
|
@@ -345,7 +345,7 @@ export class ProviderRegistry {
|
|
|
345
345
|
if (this._provider === "openrouter") headers["HTTP-Referer"] = "https://wispy.dev";
|
|
346
346
|
|
|
347
347
|
const supportsTools = !["ollama"].includes(this._provider);
|
|
348
|
-
const body = { model, messages: openaiMessages, temperature: 0.7, max_tokens: 4096 };
|
|
348
|
+
const body = { model, messages: openaiMessages, temperature: 0.7, max_tokens: 4096, stream: true };
|
|
349
349
|
if (supportsTools && tools.length > 0) {
|
|
350
350
|
body.tools = tools.map(t => ({
|
|
351
351
|
type: "function",
|
|
@@ -364,24 +364,65 @@ export class ProviderRegistry {
|
|
|
364
364
|
throw new Error(`OpenAI API error ${response.status}: ${err.slice(0, 300)}`);
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
const
|
|
368
|
-
const
|
|
369
|
-
|
|
367
|
+
const reader = response.body.getReader();
|
|
368
|
+
const decoder = new TextDecoder();
|
|
369
|
+
let buffer = "";
|
|
370
|
+
let fullText = "";
|
|
371
|
+
const toolCallsMap = {};
|
|
372
|
+
|
|
373
|
+
while (true) {
|
|
374
|
+
const { done, value } = await reader.read();
|
|
375
|
+
if (done) break;
|
|
376
|
+
|
|
377
|
+
buffer += decoder.decode(value, { stream: true });
|
|
378
|
+
const lines = buffer.split("\n");
|
|
379
|
+
buffer = lines.pop() ?? "";
|
|
380
|
+
|
|
381
|
+
for (const line of lines) {
|
|
382
|
+
if (!line.startsWith("data: ")) continue;
|
|
383
|
+
const data = line.slice(6).trim();
|
|
384
|
+
if (!data || data === "[DONE]") continue;
|
|
385
|
+
|
|
386
|
+
try {
|
|
387
|
+
const event = JSON.parse(data);
|
|
388
|
+
const delta = event.choices?.[0]?.delta;
|
|
389
|
+
if (!delta) continue;
|
|
390
|
+
|
|
391
|
+
// Text content
|
|
392
|
+
if (delta.content) {
|
|
393
|
+
fullText += delta.content;
|
|
394
|
+
opts.onChunk?.(delta.content);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Tool calls (streamed as incremental chunks)
|
|
398
|
+
if (delta.tool_calls) {
|
|
399
|
+
for (const tc of delta.tool_calls) {
|
|
400
|
+
const idx = tc.index ?? 0;
|
|
401
|
+
if (!toolCallsMap[idx]) {
|
|
402
|
+
toolCallsMap[idx] = { id: tc.id ?? `call_${idx}`, name: "", arguments: "" };
|
|
403
|
+
}
|
|
404
|
+
if (tc.id) toolCallsMap[idx].id = tc.id;
|
|
405
|
+
if (tc.function?.name) toolCallsMap[idx].name += tc.function.name;
|
|
406
|
+
if (tc.function?.arguments) toolCallsMap[idx].arguments += tc.function.arguments;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
} catch { /* skip malformed chunks */ }
|
|
410
|
+
}
|
|
411
|
+
}
|
|
370
412
|
|
|
371
|
-
|
|
372
|
-
|
|
413
|
+
const toolCalls = Object.values(toolCallsMap);
|
|
414
|
+
if (toolCalls.length > 0) {
|
|
415
|
+
const calls = toolCalls.map(tc => ({
|
|
373
416
|
id: tc.id,
|
|
374
|
-
name: tc.
|
|
375
|
-
args: JSON.parse(tc.
|
|
417
|
+
name: tc.name,
|
|
418
|
+
args: (() => { try { return JSON.parse(tc.arguments); } catch { return {}; } })(),
|
|
376
419
|
}));
|
|
377
420
|
this._sessionTokens.output += this._estimateTokens(JSON.stringify(calls));
|
|
378
421
|
return { type: "tool_calls", calls };
|
|
379
422
|
}
|
|
380
423
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
opts.onChunk?.(text);
|
|
384
|
-
return { type: "text", text };
|
|
424
|
+
this._sessionTokens.output += this._estimateTokens(fullText);
|
|
425
|
+
return { type: "text", text: fullText };
|
|
385
426
|
}
|
|
386
427
|
|
|
387
428
|
formatCost() {
|
package/core/tools.mjs
CHANGED
|
@@ -337,10 +337,18 @@ export class ToolRegistry {
|
|
|
337
337
|
try {
|
|
338
338
|
const serverUrl = `http://127.0.0.1:${DEFAULT_SERVER_PORT}`;
|
|
339
339
|
if (name === "read_file") {
|
|
340
|
+
// Resolve path relative to process.cwd() before sending to server
|
|
341
|
+
let resolvedPath = args.path.replace(/^~/, os.homedir());
|
|
342
|
+
if (!path.isAbsolute(resolvedPath)) {
|
|
343
|
+
resolvedPath = path.resolve(process.cwd(), resolvedPath);
|
|
344
|
+
}
|
|
345
|
+
if (process.env.WISPY_DEBUG) {
|
|
346
|
+
process.stderr.write(`[wispy] read_file via server: "${args.path}" ā "${resolvedPath}"\n`);
|
|
347
|
+
}
|
|
340
348
|
const resp = await fetch(`${serverUrl}/api/node-filesystem-actions`, {
|
|
341
349
|
method: "POST",
|
|
342
350
|
headers: { "Content-Type": "application/json" },
|
|
343
|
-
body: JSON.stringify({ subAction: "read_file", path:
|
|
351
|
+
body: JSON.stringify({ subAction: "read_file", path: resolvedPath }),
|
|
344
352
|
signal: AbortSignal.timeout(10_000),
|
|
345
353
|
});
|
|
346
354
|
const data = await resp.json();
|
|
@@ -386,7 +394,13 @@ export class ToolRegistry {
|
|
|
386
394
|
try {
|
|
387
395
|
switch (name) {
|
|
388
396
|
case "read_file": {
|
|
389
|
-
|
|
397
|
+
let filePath = args.path.replace(/^~/, os.homedir());
|
|
398
|
+
if (!path.isAbsolute(filePath)) {
|
|
399
|
+
filePath = path.resolve(process.cwd(), filePath);
|
|
400
|
+
}
|
|
401
|
+
if (process.env.WISPY_DEBUG) {
|
|
402
|
+
process.stderr.write(`[wispy] read_file: "${args.path}" ā "${filePath}"\n`);
|
|
403
|
+
}
|
|
390
404
|
const content = await readFile(filePath, "utf8");
|
|
391
405
|
const truncated = content.length > 10_000
|
|
392
406
|
? content.slice(0, 10_000) + `\n\n... (truncated, ${content.length} chars total)`
|
|
@@ -395,7 +409,10 @@ export class ToolRegistry {
|
|
|
395
409
|
}
|
|
396
410
|
|
|
397
411
|
case "write_file": {
|
|
398
|
-
|
|
412
|
+
let filePath = args.path.replace(/^~/, os.homedir());
|
|
413
|
+
if (!path.isAbsolute(filePath)) {
|
|
414
|
+
filePath = path.resolve(process.cwd(), filePath);
|
|
415
|
+
}
|
|
399
416
|
const dir = path.dirname(filePath);
|
|
400
417
|
await mkdir(dir, { recursive: true });
|
|
401
418
|
await writeFile(filePath, args.content, "utf8");
|
|
@@ -453,7 +470,10 @@ export class ToolRegistry {
|
|
|
453
470
|
}
|
|
454
471
|
|
|
455
472
|
case "file_edit": {
|
|
456
|
-
|
|
473
|
+
let filePath = args.path.replace(/^~/, os.homedir());
|
|
474
|
+
if (!path.isAbsolute(filePath)) {
|
|
475
|
+
filePath = path.resolve(process.cwd(), filePath);
|
|
476
|
+
}
|
|
457
477
|
const content = await readFile(filePath, "utf8");
|
|
458
478
|
if (!content.includes(args.old_text)) {
|
|
459
479
|
return { success: false, error: `Text not found in ${filePath}` };
|
package/lib/wispy-repl.mjs
CHANGED
|
@@ -865,8 +865,11 @@ async function runOneShot(engine, message) {
|
|
|
865
865
|
} else {
|
|
866
866
|
console.error(red(`\nā ${err.message.slice(0, 200)}`));
|
|
867
867
|
}
|
|
868
|
+
engine.destroy();
|
|
868
869
|
process.exit(1);
|
|
869
870
|
}
|
|
871
|
+
engine.destroy();
|
|
872
|
+
process.exit(0);
|
|
870
873
|
}
|
|
871
874
|
|
|
872
875
|
// ---------------------------------------------------------------------------
|
package/package.json
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wispy-cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "šæ Wispy ā AI workspace assistant with multi-agent orchestration and multi-channel bot support",
|
|
5
|
-
"license": "
|
|
5
|
+
"license": "MIT",
|
|
6
6
|
"author": "Minseo & Poropo",
|
|
7
|
+
"homepage": "https://github.com/mindsurf0176-ui/poropo-workspace/tree/master/agent-workstream-os/wispy-cli#readme",
|
|
7
8
|
"repository": {
|
|
8
9
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/mindsurf0176-ui/poropo-workspace"
|
|
10
|
+
"url": "git+https://github.com/mindsurf0176-ui/poropo-workspace.git",
|
|
11
|
+
"directory": "agent-workstream-os/wispy-cli"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/mindsurf0176-ui/poropo-workspace/issues"
|
|
10
15
|
},
|
|
11
16
|
"keywords": [
|
|
12
17
|
"ai",
|
|
@@ -19,13 +24,25 @@
|
|
|
19
24
|
"gemini",
|
|
20
25
|
"claude",
|
|
21
26
|
"openai",
|
|
27
|
+
"groq",
|
|
28
|
+
"deepseek",
|
|
29
|
+
"ollama",
|
|
30
|
+
"openrouter",
|
|
22
31
|
"tui",
|
|
23
32
|
"ink",
|
|
24
33
|
"telegram",
|
|
25
34
|
"discord",
|
|
26
35
|
"slack",
|
|
27
36
|
"bot",
|
|
28
|
-
"chatbot"
|
|
37
|
+
"chatbot",
|
|
38
|
+
"mcp",
|
|
39
|
+
"model-context-protocol",
|
|
40
|
+
"memory",
|
|
41
|
+
"cron",
|
|
42
|
+
"scheduler",
|
|
43
|
+
"subagent",
|
|
44
|
+
"terminal",
|
|
45
|
+
"repl"
|
|
29
46
|
],
|
|
30
47
|
"type": "module",
|
|
31
48
|
"bin": {
|
|
@@ -41,7 +58,9 @@
|
|
|
41
58
|
],
|
|
42
59
|
"scripts": {
|
|
43
60
|
"postinstall": "node scripts/postinstall.mjs",
|
|
44
|
-
"test": "node --test test
|
|
61
|
+
"test": "node --test --test-force-exit --test-timeout=15000 tests/*.test.mjs",
|
|
62
|
+
"test:basic": "node --test --test-force-exit test/basic.test.mjs",
|
|
63
|
+
"test:verbose": "node --test --test-force-exit --test-timeout=15000 --test-reporter=spec tests/*.test.mjs"
|
|
45
64
|
},
|
|
46
65
|
"dependencies": {
|
|
47
66
|
"cron-parser": "^5.5.0",
|
|
@@ -68,6 +87,6 @@
|
|
|
68
87
|
}
|
|
69
88
|
},
|
|
70
89
|
"engines": {
|
|
71
|
-
"node": ">=
|
|
90
|
+
"node": ">=18.0.0"
|
|
72
91
|
}
|
|
73
92
|
}
|