skimpyclaw 0.3.0 → 0.3.3

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/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import { spawn, spawnSync } from 'child_process';
6
6
  import { fileURLToPath } from 'url';
7
7
  import { loadConfig, loadRawConfig, getConfigPath, saveConfig } from './config.js';
8
8
  import { startRuntime } from './service.js';
9
- import { runSetup } from './setup.js';
9
+ import { runSetup, renderGatewayPlist } from './setup.js';
10
10
  import { runDoctor as runDoctorCommand } from './doctor/index.js';
11
11
  import { executeTool, getToolDefinitions, BUILTIN_TOOL_DEFINITIONS, BROWSER_TOOL_DEFINITION } from './tools.js';
12
12
  import { formatModelSelectionError, getModelSelectionUsage, resolveModelSelection } from './model-selection.js';
@@ -129,10 +129,22 @@ function startDaemon() {
129
129
  console.error('Daemon control is only supported on macOS with launchctl.');
130
130
  return 1;
131
131
  }
132
- if (!existsSync(LAUNCHD_PLIST)) {
133
- console.error(`Launchd plist not found: ${LAUNCHD_PLIST}`);
134
- console.error('Run `skimpyclaw onboard` first.');
135
- return 1;
132
+ // Regenerate plist to point at the current binary (pnpm changes path on upgrade)
133
+ try {
134
+ const plistContent = renderGatewayPlist();
135
+ const plistDir = join(homedir(), 'Library', 'LaunchAgents');
136
+ if (!existsSync(plistDir))
137
+ mkdirSync(plistDir, { recursive: true });
138
+ writeFileSync(LAUNCHD_PLIST, plistContent);
139
+ }
140
+ catch (err) {
141
+ // If template is missing (e.g. corrupted install), fall back to existing plist
142
+ if (!existsSync(LAUNCHD_PLIST)) {
143
+ console.error(`Launchd plist not found: ${LAUNCHD_PLIST}`);
144
+ console.error('Run `skimpyclaw onboard` first.');
145
+ return 1;
146
+ }
147
+ console.warn(`Warning: could not regenerate plist (${err instanceof Error ? err.message : err}), using existing`);
136
148
  }
137
149
  const result = runLaunchctl(['load', LAUNCHD_PLIST]);
138
150
  if (!result.ok && !result.output.includes('already loaded')) {
package/dist/setup.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  interface SetupOptions {
2
2
  dryRun?: boolean;
3
3
  }
4
+ export declare function renderGatewayPlist(): string;
4
5
  type ProviderChoice = 'anthropic-api' | 'anthropic-oauth' | 'openai-api' | 'codex-oauth' | 'minimax-api' | 'kimi-api';
5
6
  interface ProviderSecrets {
6
7
  anthropicKey?: string;
package/dist/setup.js CHANGED
@@ -128,7 +128,7 @@ function maskInput(input) {
128
128
  return '****';
129
129
  return input.slice(0, 4) + '****' + input.slice(-4);
130
130
  }
131
- function renderGatewayPlist() {
131
+ export function renderGatewayPlist() {
132
132
  if (!existsSync(GATEWAY_PLIST_TEMPLATE)) {
133
133
  throw new Error(`Gateway launchd template not found: ${GATEWAY_PLIST_TEMPLATE}`);
134
134
  }
@@ -235,7 +235,14 @@ function buildStarterCronJobs(starters) {
235
235
  },
236
236
  payload: {
237
237
  kind: 'agentTurn',
238
- message: 'Use WebSearch to fetch today\'s top 10 Hacker News stories. Reply with title, URL, and 1-line summary for each item.',
238
+ message: 'Use the Browser tool to visit https://news.ycombinator.com and fetch today\'s top 10 stories. Reply with title, URL, and 1-line summary for each item.',
239
+ tools: {
240
+ enabled: true,
241
+ allowedPaths: [`${homedir()}/.skimpyclaw`],
242
+ maxIterations: 30,
243
+ bashTimeout: 30000,
244
+ browser: { enabled: true, headless: true },
245
+ },
239
246
  },
240
247
  });
241
248
  }
@@ -250,7 +257,14 @@ function buildStarterCronJobs(starters) {
250
257
  },
251
258
  payload: {
252
259
  kind: 'agentTurn',
253
- message: `Check current weather and today forecast for ${starters.weatherLocation}. Keep it concise: current temp/conditions, highs/lows, precipitation chance, and 1 recommendation.`,
260
+ message: `Use the Browser tool to check current weather and today's forecast for ${starters.weatherLocation}. Keep it concise: current temp/conditions, highs/lows, precipitation chance, and 1 recommendation.`,
261
+ tools: {
262
+ enabled: true,
263
+ allowedPaths: [`${homedir()}/.skimpyclaw`],
264
+ maxIterations: 30,
265
+ bashTimeout: 30000,
266
+ browser: { enabled: true, headless: true },
267
+ },
254
268
  },
255
269
  });
256
270
  }
@@ -488,6 +502,13 @@ export function buildSetupConfig(input) {
488
502
  allowFrom: [parseInt(input.telegramId, 10) || input.telegramId],
489
503
  dailyNotesDir: '${HOME}/.skimpyclaw/Daily Notes',
490
504
  defaultAllowedPaths: allPaths,
505
+ tools: {
506
+ enabled: true,
507
+ allowedPaths: allPaths,
508
+ maxIterations: 100,
509
+ bashTimeout: 15000,
510
+ ...(features.browser ? { browser: { type: 'chromium', enabled: true, headless: true } } : {}),
511
+ },
491
512
  },
492
513
  discord: {
493
514
  enabled: useDiscord,
@@ -495,6 +516,13 @@ export function buildSetupConfig(input) {
495
516
  allowFrom: useDiscord ? [input.discordUserId || ''] : [],
496
517
  defaultAllowedPaths: allPaths,
497
518
  ...(input.discordDefaultChannelId ? { defaultChannelId: input.discordDefaultChannelId } : {}),
519
+ tools: {
520
+ enabled: true,
521
+ allowedPaths: allPaths,
522
+ maxIterations: 100,
523
+ bashTimeout: 15000,
524
+ ...(features.browser ? { browser: { type: 'chromium', enabled: true, headless: false } } : {}),
525
+ },
498
526
  },
499
527
  },
500
528
  cron: {
@@ -517,6 +545,13 @@ export function buildSetupConfig(input) {
517
545
  defaultProvider: 'macos',
518
546
  providers: {
519
547
  macos: { tts: { voice: 'Samantha' } },
548
+ ...(input.selectedProviders.has('openai-api') ? {
549
+ openai: {
550
+ apiKey: '${OPENAI_API_KEY}',
551
+ baseURL: 'https://api.openai.com/v1',
552
+ stt: { model: 'whisper-1' },
553
+ },
554
+ } : {}),
520
555
  },
521
556
  channels: {
522
557
  telegram: { enabled: true, acceptVoice: true, sendVoice: true },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skimpyclaw",
3
- "version": "0.3.0",
3
+ "version": "0.3.3",
4
4
  "description": "Lightweight personal AI assistant with Telegram and Discord integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,30 +1,328 @@
1
- # TOOLS.md - Local Environment
1
+ # TOOLS.md Available Tools
2
2
 
3
- Document the tools and environment available on this machine.
3
+ This file defines operational authority.
4
+ Core safety and integrity rules may not be removed or weakened.
4
5
 
5
- ## Available Commands
6
+ ---
6
7
 
7
- - Telegram bot commands (/ask, /model, /status, /cron, etc.)
8
- - Model switching (fast/smart/opus aliases)
9
- - Cron job execution
8
+ ## CRITICAL: Act, Don't Narrate
10
9
 
11
- ## File Locations
10
+ If a task requires a tool, call the tool immediately.
12
11
 
13
- - Config: ~/.skimpyclaw/config.json
14
- - Agent templates: ~/.skimpyclaw/agents/main/
15
- - Memory logs: ~/.skimpyclaw/agents/main/memory/
16
- - Session transcripts: ~/.skimpyclaw/sessions/
17
- - Gateway logs: ~/.skimpyclaw/logs/
12
+ Do not:
18
13
 
19
- ## Integrations
14
+ - Announce intent
15
+ - Explain the plan
16
+ - Simulate execution
17
+ - Output tool calls as text
20
18
 
21
- List any integrations set up:
19
+ Each response must either:
22
20
 
23
- - [ ] Telegram bot
24
- - [ ] Obsidian vault access
25
- - [ ] Calendar sync
26
- - [ ] Other...
21
+ - Call a tool, or
22
+ - Deliver a final result.
27
23
 
28
- ## Notes
24
+ ---
29
25
 
30
- [Add notes about the local environment here]
26
+ ## State Integrity
27
+
28
+ - If you have not Read it, you do not know it.
29
+ - Never assume file contents.
30
+ - Never partial-write structured files.
31
+ - Never fabricate tool output.
32
+ - If a tool fails, report the failure plainly.
33
+
34
+ ---
35
+
36
+ ## Tool Use Rules
37
+
38
+ Tools are provided dynamically via the API tool_use mechanism.
39
+
40
+ Available categories:
41
+
42
+ - Built-in tools (Read, Write, Glob, Bash)
43
+ - Browser (when enabled)
44
+ - `$web_search` (when injected)
45
+ - MCP tools (auto-discovered)
46
+
47
+ Rules:
48
+
49
+ - Do not hallucinate tools.
50
+ - Do not invent parameters.
51
+ - Do not output tool calls as JSON/XML/text.
52
+ - If functionality is unavailable, say so plainly.
53
+
54
+ ---
55
+
56
+ # Built-in Tools
57
+
58
+ ## Read
59
+
60
+ Read the contents of a file.
61
+
62
+ Parameter:
63
+
64
+ - `file_path` (absolute path, required)
65
+
66
+ Use when:
67
+
68
+ - Gathering context
69
+ - Verifying state
70
+ - Modifying any file
71
+
72
+ ---
73
+
74
+ ## Write
75
+
76
+ Write content to a file. Overwrites existing files.
77
+
78
+ Parameters:
79
+
80
+ - `file_path` (required)
81
+ - `content` (required)
82
+
83
+ Protocol:
84
+
85
+ 1. Read full file first.
86
+ 2. Modify in memory.
87
+ 3. Write entire file back.
88
+ 4. Never partial-write structured files.
89
+
90
+ ---
91
+
92
+ ## Glob
93
+
94
+ List files/directories at a path.
95
+
96
+ Parameter:
97
+
98
+ - `path` (absolute path)
99
+
100
+ Use for discovery.
101
+
102
+ ---
103
+
104
+ ## Bash
105
+
106
+ Execute shell commands.
107
+
108
+ Parameters:
109
+
110
+ - `command` (required)
111
+ - `cwd` (optional)
112
+
113
+ Use for:
114
+
115
+ - Date/time retrieval
116
+ - CLI utilities (gh, icalBuddy, curl, etc.)
117
+
118
+ Quote paths with spaces.
119
+
120
+ Never run interactive programs (vim, nano, less).
121
+
122
+ ---
123
+
124
+ ## spawn_subagent
125
+
126
+ Spawn a background agent. Returns immediately.
127
+
128
+ Parameters:
129
+
130
+ - `task` (required, self-contained)
131
+ - `type` (`coding`, `research`)
132
+ - `model` (optional)
133
+ - `label` (optional)
134
+ - `allowedPaths` (optional)
135
+
136
+ Use when:
137
+
138
+ - Task is complex and parallelizable
139
+ - Long-running coding would block conversation
140
+ - Multiple independent tasks are requested
141
+
142
+ Do not use for:
143
+
144
+ - Simple reads/writes
145
+ - Interactive back-and-forth tasks
146
+ - Single quick actions
147
+
148
+ The subagent must not assume shared context unless explicitly included.
149
+
150
+ ---
151
+
152
+ ## code_with_agent
153
+
154
+ Delegate non-trivial code changes to a dedicated coding CLI (Claude Code or Codex).
155
+
156
+ Parameters:
157
+
158
+ - `task` (required, detailed and specific)
159
+ - `agent` (`claude` default or `codex`)
160
+ - `workdir` (optional)
161
+ - `model` (optional)
162
+ - `max_turns` (optional, Claude only)
163
+ - `validate` (boolean, default true)
164
+
165
+ Use when:
166
+
167
+ - Modifying codebases
168
+ - Multi-file changes
169
+ - Changes requiring build/test validation
170
+
171
+ Do not use for:
172
+
173
+ - Simple config edits
174
+ - Information gathering
175
+
176
+ ---
177
+
178
+ ## code_with_team
179
+
180
+ Decompose a complex task into subtasks and run multiple `code_with_agent` instances in parallel.
181
+
182
+ Parameters:
183
+
184
+ - `task` (required, detailed and specific)
185
+ - `team_size` (2-5, default 3)
186
+ - `workdir` (optional)
187
+ - `model` (optional)
188
+ - `timeout_minutes` (optional, default 20, max 60)
189
+ - `validate` (boolean, default true — runs once after all agents finish)
190
+
191
+ Use when:
192
+
193
+ - Multi-file refactors with independent parts
194
+ - Cross-layer changes (frontend + backend + tests) that don't conflict
195
+ - Tasks with clearly separable subtasks
196
+
197
+ Do not use for:
198
+
199
+ - Simple single-file changes (use code_with_agent)
200
+ - Tightly coupled changes where agents would conflict on the same files
201
+ - Non-coding tasks
202
+ - Quick fixes or config edits
203
+
204
+ ---
205
+
206
+ ## Web Search
207
+
208
+ When available, `$web_search` is injected automatically.
209
+
210
+ Search hierarchy:
211
+
212
+ 1. `$web_search` for general discovery
213
+ 2. Browser for interactive or structured extraction
214
+ 3. Bash+curl only if Browser unavailable
215
+
216
+ Prefer `$web_search` for speed and cost efficiency.
217
+
218
+ ---
219
+
220
+ ## Browser
221
+
222
+ Playwright browser with persistent sessions.
223
+
224
+ Use for:
225
+
226
+ - Web scraping and reading web pages
227
+ - Fetching news, weather, prices, and other web content
228
+ - Reading dynamic pages
229
+ - Automation requiring login
230
+ - Structured extraction
231
+
232
+ When `$web_search` is not available, use Browser to navigate directly to websites.
233
+
234
+ Rules:
235
+
236
+ - Always include URLs in scraped results.
237
+ - Extract title, author, link, and key metrics when relevant.
238
+ - Do not perform destructive actions without explicit confirmation.
239
+
240
+ ---
241
+
242
+ ## MCP Tools
243
+
244
+ Auto-discovered at runtime (`mcp__{server}__{tool}`).
245
+
246
+ Load providers before execution:
247
+
248
+ - `context-a8c-load-provider`
249
+ - `context-a8c-execute-tool`
250
+
251
+ Load providers in parallel when needed.
252
+
253
+ ---
254
+
255
+ # Execution Guidelines
256
+
257
+ - Read before Write.
258
+ - Prefer minimal tool calls.
259
+ - Prefer deterministic tools.
260
+ - Use Bash for dates (`date +%m-%d-%Y`).
261
+ - Do not guess state.
262
+ - Update files when asked — do not suggest it.
263
+
264
+ ---
265
+
266
+ # Vision / Image Processing
267
+
268
+ You CAN see and analyze images. When a user sends a photo or image attachment, the image is downloaded, converted to base64, and included in your message as an image content block. You receive the actual pixel data — you are NOT guessing.
269
+
270
+ Rules:
271
+ - Describe what you actually see. Do not fabricate or embellish.
272
+ - If the image is unclear or you're uncertain about details, say so.
273
+ - You can read text in images (OCR), identify objects, describe scenes, and extract structured data (dates, events, etc.).
274
+ - If asked to act on image contents (e.g. "add this to my calendar"), extract the details and use the appropriate tool.
275
+ - Do NOT claim you cannot see images. You can.
276
+
277
+ ---
278
+
279
+ # Creating Skills
280
+
281
+ You can create new skills to extend your own capabilities. Skills are loaded automatically on each agent turn.
282
+
283
+ ## Skill Format
284
+
285
+ Skills are Markdown files at `~/.skimpyclaw/skills/{name}/SKILL.md`. Each skill requires YAML frontmatter:
286
+
287
+ ```yaml
288
+ ---
289
+ name: skill-name # Unique identifier (lowercase, hyphens)
290
+ description: What it does # One-line summary shown in skill list
291
+ emoji: 🔧 # Display emoji (optional)
292
+ tags: [category, topic] # Freeform tags for filtering (optional)
293
+ requires: # External requirements (optional)
294
+ bins: [curl] # Binaries that must be on PATH
295
+ env: [API_KEY] # Env vars that must be set
296
+ paths: [/some/dir] # Paths that must exist
297
+ tools: [Browser] # Tools that must be available
298
+ contexts: # When this skill activates (optional, default: always)
299
+ channels: [telegram] # Channel names: telegram, discord
300
+ cronJobs: [morning] # Specific cron job IDs
301
+ tags: [daily] # Tag-based activation
302
+ priority: 50 # Load order: lower = earlier (default: 100)
303
+ enabled: true # Set false to disable without deleting
304
+ ---
305
+ ```
306
+
307
+ ## How to Create a Skill
308
+
309
+ Use the Write tool to create `~/.skimpyclaw/skills/{name}/SKILL.md` with proper frontmatter and instructions.
310
+
311
+ The skill content after frontmatter is injected into the system prompt. Write it as instructions to yourself.
312
+
313
+ ## Best Practices
314
+
315
+ - Keep each skill focused on one domain
316
+ - Use `contexts` to avoid loading irrelevant skills (e.g. a cron-only skill shouldn't load in Telegram)
317
+ - Set `priority` to control load order when skills depend on each other
318
+ - Use `enabled: false` to temporarily disable a skill
319
+ - Skills are hot-reloaded — no restart needed
320
+
321
+ ---
322
+
323
+ # What You Cannot Do
324
+
325
+ - Cannot send external messages directly
326
+ - Cannot run interactive shell programs
327
+ - Cannot fabricate outputs
328
+ - Cannot bypass integrity rules