walidsobhie-code commited on
Commit
3de42e7
·
1 Parent(s): 359acf6

feat: Add MCP server for Stack 2.9 tools exposure

Browse files
audit_async.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Async audit for Stack 2.9 tools - tests async tools properly."""
3
+
4
+ import asyncio
5
+ import sys
6
+ import time
7
+
8
+ sys.path.insert(0, '/Users/walidsobhi/stack-2.9/src')
9
+
10
+ from tools import tool_registry
11
+
12
+
13
+ def get_test_input(tool_name: str) -> dict:
14
+ """Generate appropriate test input for each tool."""
15
+ test_inputs = {
16
+ "file_read": {"path": "/Users/walidsobhi/stack-2.9/README.md"},
17
+ "file_write": {"path": "/tmp/test_audit.txt", "content": "test content"},
18
+ "file_exists": {"path": "/Users/walidsobhi/stack-2.9/README.md"},
19
+ "file_delete": {"path": "/tmp/test_audit.txt"},
20
+ "file_edit": {"operation": "insert", "path": "/tmp/test_edit.txt", "content": "test"},
21
+ "file_edit_insert": {"path": "/tmp/test_insert.txt", "content": "test", "offset": 0},
22
+ "file_edit_delete": {"path": "/tmp/test_delete.txt", "start": 0, "end": 5},
23
+ "file_edit_replace": {"path": "/tmp/test_replace.txt", "pattern": "old", "replacement": "new"},
24
+ "glob": {"pattern": "*.py"},
25
+ "glob_list": {"pattern": "*.md"},
26
+ "grep": {"pattern": "def ", "path": "/Users/walidsobhi/stack-2.9"},
27
+ "grep_count": {"pattern": "import", "path": "/Users/walidsobhi/stack-2.9/src"},
28
+ "web_fetch": {"url": "https://example.com"},
29
+ "web_fetch_meta": {"url": "https://example.com"},
30
+ "WebSearch": {"query": "python async", "num_results": 3},
31
+ "ask_question": {"question": "What is 2+2?"},
32
+ "get_pending_questions": {},
33
+ "answer_question": {"question_id": "1", "answer": "4"},
34
+ "brief": {"content": "This is a test content for brief summarization."},
35
+ "brief_summary": {"content": "Test content here."},
36
+ "agent_spawn": {"agent_type": "general-purpose", "task": "test task"},
37
+ "agent_status": {"agent_id": "test-123"},
38
+ "agent_list": {},
39
+ "sleep": {"seconds": 0.01},
40
+ "wait_for": {"condition": "true", "timeout": 1},
41
+ "skill_list": {},
42
+ "skill_execute": {"skill_name": "test", "args": {}},
43
+ "skill_info": {"skill_name": "test"},
44
+ "skill_chain": {"skills": [], "initial_input": {}},
45
+ "skill_search": {"query": "test"},
46
+ "TaskCreate": {"subject": "Test Task", "description": "Test description"},
47
+ "TaskList": {},
48
+ "TaskUpdate": {"taskId": "1", "status": "completed"},
49
+ "TaskDelete": {"taskId": "1"},
50
+ "TodoWrite": {"subject": "Test", "content": "Test content"},
51
+ "team_create": {"name": "test-team"},
52
+ "team_delete": {"team_id": "1"},
53
+ "team_disband": {"team_id": "1"},
54
+ "team_list": {},
55
+ "team_status": {"team_id": "1"},
56
+ "team_assign": {"team_id": "1", "agent_id": "1"},
57
+ "team_leave": {"team_id": "1"},
58
+ "Config": {"operation": "get", "key": "test"},
59
+ "CronCreate": {"expression": "* * * * *", "command": "echo test"},
60
+ "CronList": {},
61
+ "CronDelete": {"id": "1"},
62
+ "EnterPlanMode": {},
63
+ "ExitPlanMode": {},
64
+ "message_send": {"channel": "test", "content": "test"},
65
+ "message_list": {"channel": "test"},
66
+ "message_channel": {"name": "test"},
67
+ "message_template": {"template": "test", "vars": {}},
68
+ "remote_add": {"name": "test", "url": "https://example.com"},
69
+ "remote_list": {},
70
+ "remote_remove": {"name": "test"},
71
+ "remote_trigger": {"action": "test", "params": {}},
72
+ "mcp_list_servers": {},
73
+ "mcp_add_server": {"name": "test", "command": "test"},
74
+ "mcp_call": {"server": "test", "tool_name": "test", "args": {}},
75
+ "read_mcp_resource": {"resource_uri": "test://resource"},
76
+ "tool_search": {"query": "file"},
77
+ "tool_list_all": {},
78
+ "tool_info": {"name": "file_read"},
79
+ "tool_capabilities": {},
80
+ "synthetic_output": {"content": "test"},
81
+ "structure_data": {"content": "test", "format": "json"},
82
+ "enter_worktree": {"path": "/tmp/test"},
83
+ "exit_worktree": {},
84
+ "list_worktrees": {},
85
+ }
86
+ return test_inputs.get(tool_name, {})
87
+
88
+
89
+ async def test_tool(tool_name: str) -> dict:
90
+ """Test a single async tool."""
91
+ tool = tool_registry.get(tool_name)
92
+ if not tool:
93
+ return {"tool": tool_name, "status": "FAIL", "error": "Tool not found"}
94
+
95
+ test_input = get_test_input(tool_name)
96
+
97
+ start_time = time.time()
98
+ try:
99
+ result = await tool.execute(**test_input)
100
+ duration = time.time() - start_time
101
+ return {
102
+ "tool": tool_name,
103
+ "status": "PASS",
104
+ "duration": duration,
105
+ "result": str(result)[:100] if result else "None"
106
+ }
107
+ except Exception as e:
108
+ duration = time.time() - start_time
109
+ return {
110
+ "tool": tool_name,
111
+ "status": "FAIL",
112
+ "duration": duration,
113
+ "error": str(e)[:100]
114
+ }
115
+
116
+
117
+ async def main():
118
+ """Run async audit on all tools."""
119
+ print("=" * 60)
120
+ print("STACK 2.9 ASYNC TOOLS AUDIT")
121
+ print("=" * 60)
122
+
123
+ tools = tool_registry.list()
124
+ print(f"\nFound {len(tools)} registered tools\n")
125
+
126
+ results = []
127
+ passed = 0
128
+ failed = 0
129
+
130
+ for tool_name in tools:
131
+ result = await test_tool(tool_name)
132
+ results.append(result)
133
+
134
+ status = result["status"]
135
+ if status == "PASS":
136
+ passed += 1
137
+ print(f"✅ {tool_name}: PASS ({result['duration']:.3f}s)")
138
+ else:
139
+ failed += 1
140
+ error = result.get('error', 'Unknown error')
141
+ print(f"❌ {tool_name}: FAIL - {error[:50]}")
142
+
143
+ print("\n" + "=" * 60)
144
+ print("RESULTS SUMMARY")
145
+ print("=" * 60)
146
+ print(f"Total Tools: {len(tools)}")
147
+ print(f"Passed: {passed} ({passed*100//len(tools)}%)")
148
+ print(f"Failed: {failed} ({failed*100//len(tools)}%)")
149
+
150
+ if failed > 0:
151
+ print("\nFailed tools:")
152
+ for r in results:
153
+ if r["status"] == "FAIL":
154
+ print(f" - {r['tool']}: {r.get('error', 'Unknown')[:60]}")
155
+
156
+ return results
157
+
158
+
159
+ if __name__ == "__main__":
160
+ asyncio.run(main())
audit_report.txt ADDED
@@ -0,0 +1,577 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ ================================================================================
3
+ STACK 2.9 COMPREHENSIVE AUDIT REPORT
4
+ ================================================================================
5
+
6
+ Generated: 2026-04-09T19:39:39.247556
7
+
8
+ --------------------------------------------------------------------------------
9
+ TOOLS SUMMARY
10
+ --------------------------------------------------------------------------------
11
+
12
+ Total Tools Tested: 69
13
+ Passed: 5
14
+ Failed: 64
15
+ Pass Rate: 7.2%
16
+
17
+ --------------------------------------------------------------------------------
18
+ RESPONSE TIME STATISTICS
19
+ --------------------------------------------------------------------------------
20
+
21
+ Average Response Time: 0.0947s
22
+ Minimum Response Time: 0.0000s
23
+ Maximum Response Time: 6.5300s
24
+
25
+ --------------------------------------------------------------------------------
26
+ DETAILED RESULTS
27
+ --------------------------------------------------------------------------------
28
+
29
+
30
+ Tool: Config
31
+ Status: FAIL
32
+ Load Success: True
33
+ Response Time: 0.0000s
34
+ Error: Unknown operation: None
35
+
36
+
37
+ Tool: CronCreate
38
+ Status: FAIL
39
+ Load Success: True
40
+ Response Time: 0.0000s
41
+ Error: Invalid cron expression: Expected 5 fields, got 0
42
+
43
+
44
+ Tool: CronDelete
45
+ Status: FAIL
46
+ Load Success: True
47
+ Response Time: 0.0000s
48
+ Error: Error: id is required
49
+
50
+
51
+ Tool: CronList
52
+ Status: PASS
53
+ Load Success: True
54
+ Response Time: 0.0003s
55
+
56
+
57
+ Tool: EnterPlanMode
58
+ Status: PASS
59
+ Load Success: True
60
+ Response Time: 0.0016s
61
+
62
+
63
+ Tool: ExitPlanMode
64
+ Status: PASS
65
+ Load Success: True
66
+ Response Time: 0.0003s
67
+
68
+
69
+ Tool: TaskCreate
70
+ Status: FAIL
71
+ Load Success: True
72
+ Response Time: 0.0000s
73
+ Error: Error: subject is required
74
+
75
+
76
+ Tool: TaskDelete
77
+ Status: FAIL
78
+ Load Success: True
79
+ Response Time: 0.0000s
80
+ Error: Error: id is required
81
+
82
+
83
+ Tool: TaskList
84
+ Status: PASS
85
+ Load Success: True
86
+ Response Time: 0.0002s
87
+
88
+
89
+ Tool: TaskUpdate
90
+ Status: FAIL
91
+ Load Success: True
92
+ Response Time: 0.0000s
93
+ Error: Error: id is required
94
+
95
+
96
+ Tool: TodoWrite
97
+ Status: FAIL
98
+ Load Success: True
99
+ Response Time: 0.0002s
100
+ Error: Unknown operation: None
101
+
102
+
103
+ Tool: WebSearch
104
+ Status: PASS
105
+ Load Success: True
106
+ Response Time: 6.5300s
107
+
108
+
109
+ Tool: agent_list
110
+ Status: FAIL
111
+ Load Success: True
112
+ Response Time: 0.0000s
113
+ Error: AgentListTool.execute() takes 1 positional argument but 2 were given
114
+
115
+
116
+ Tool: agent_spawn
117
+ Status: FAIL
118
+ Load Success: True
119
+ Response Time: 0.0010s
120
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
121
+
122
+
123
+ Tool: agent_status
124
+ Status: FAIL
125
+ Load Success: True
126
+ Response Time: 0.0000s
127
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
128
+
129
+
130
+ Tool: answer_question
131
+ Status: FAIL
132
+ Load Success: True
133
+ Response Time: 0.0000s
134
+ Error: AnswerQuestionTool.execute() missing 1 required positional argument: 'answer'
135
+
136
+
137
+ Tool: ask_question
138
+ Status: FAIL
139
+ Load Success: True
140
+ Response Time: 0.0000s
141
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
142
+
143
+
144
+ Tool: brief
145
+ Status: FAIL
146
+ Load Success: True
147
+ Response Time: 0.0000s
148
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
149
+
150
+
151
+ Tool: brief_summary
152
+ Status: FAIL
153
+ Load Success: True
154
+ Response Time: 0.0000s
155
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
156
+
157
+
158
+ Tool: enter_worktree
159
+ Status: FAIL
160
+ Load Success: True
161
+ Response Time: 0.0002s
162
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
163
+
164
+
165
+ Tool: exit_worktree
166
+ Status: FAIL
167
+ Load Success: True
168
+ Response Time: 0.0000s
169
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
170
+
171
+
172
+ Tool: file_delete
173
+ Status: FAIL
174
+ Load Success: True
175
+ Response Time: 0.0000s
176
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
177
+
178
+
179
+ Tool: file_edit
180
+ Status: FAIL
181
+ Load Success: True
182
+ Response Time: 0.0000s
183
+ Error: FileEditTool.execute() missing 1 required positional argument: 'operation'
184
+
185
+
186
+ Tool: file_edit_delete
187
+ Status: FAIL
188
+ Load Success: True
189
+ Response Time: 0.0000s
190
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
191
+
192
+
193
+ Tool: file_edit_insert
194
+ Status: FAIL
195
+ Load Success: True
196
+ Response Time: 0.0000s
197
+ Error: FileEditInsertTool.execute() missing 1 required positional argument: 'content'
198
+
199
+
200
+ Tool: file_edit_replace
201
+ Status: FAIL
202
+ Load Success: True
203
+ Response Time: 0.0000s
204
+ Error: FileEditReplaceTool.execute() missing 2 required positional arguments: 'pattern' and 'replacement'
205
+
206
+
207
+ Tool: file_exists
208
+ Status: FAIL
209
+ Load Success: True
210
+ Response Time: 0.0000s
211
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
212
+
213
+
214
+ Tool: file_read
215
+ Status: FAIL
216
+ Load Success: True
217
+ Response Time: 0.0000s
218
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
219
+
220
+
221
+ Tool: file_write
222
+ Status: FAIL
223
+ Load Success: True
224
+ Response Time: 0.0000s
225
+ Error: FileWriteTool.execute() missing 1 required positional argument: 'content'
226
+
227
+
228
+ Tool: get_pending_questions
229
+ Status: FAIL
230
+ Load Success: True
231
+ Response Time: 0.0000s
232
+ Error: GetPendingQuestionsTool.execute() takes 1 positional argument but 2 were given
233
+
234
+
235
+ Tool: glob
236
+ Status: FAIL
237
+ Load Success: True
238
+ Response Time: 0.0000s
239
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
240
+
241
+
242
+ Tool: glob_list
243
+ Status: FAIL
244
+ Load Success: True
245
+ Response Time: 0.0000s
246
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
247
+
248
+
249
+ Tool: grep
250
+ Status: FAIL
251
+ Load Success: True
252
+ Response Time: 0.0000s
253
+ Error: GrepTool.execute() missing 1 required positional argument: 'path'
254
+
255
+
256
+ Tool: grep_count
257
+ Status: FAIL
258
+ Load Success: True
259
+ Response Time: 0.0000s
260
+ Error: GrepCountTool.execute() missing 1 required positional argument: 'path'
261
+
262
+
263
+ Tool: list_worktrees
264
+ Status: FAIL
265
+ Load Success: True
266
+ Response Time: 0.0000s
267
+ Error: ListWorktreesTool.execute() takes 1 positional argument but 2 were given
268
+
269
+
270
+ Tool: mcp_add_server
271
+ Status: FAIL
272
+ Load Success: True
273
+ Response Time: 0.0000s
274
+ Error: MCPServerAddTool.execute() missing 1 required positional argument: 'command'
275
+
276
+
277
+ Tool: mcp_call
278
+ Status: FAIL
279
+ Load Success: True
280
+ Response Time: 0.0000s
281
+ Error: MCPTool.execute() missing 1 required positional argument: 'tool_name'
282
+
283
+
284
+ Tool: mcp_list_servers
285
+ Status: FAIL
286
+ Load Success: True
287
+ Response Time: 0.0000s
288
+ Error: MCPServerListTool.execute() takes 1 positional argument but 2 were given
289
+
290
+
291
+ Tool: message_channel
292
+ Status: FAIL
293
+ Load Success: True
294
+ Response Time: 0.0000s
295
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
296
+
297
+
298
+ Tool: message_list
299
+ Status: FAIL
300
+ Load Success: True
301
+ Response Time: 0.0000s
302
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
303
+
304
+
305
+ Tool: message_send
306
+ Status: FAIL
307
+ Load Success: True
308
+ Response Time: 0.0000s
309
+ Error: MessageSendTool.execute() missing 1 required positional argument: 'content'
310
+
311
+
312
+ Tool: message_template
313
+ Status: FAIL
314
+ Load Success: True
315
+ Response Time: 0.0000s
316
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
317
+
318
+
319
+ Tool: read_mcp_resource
320
+ Status: FAIL
321
+ Load Success: True
322
+ Response Time: 0.0000s
323
+ Error: ReadMcpResourceTool.execute() missing 1 required positional argument: 'resource_uri'
324
+
325
+
326
+ Tool: remote_add
327
+ Status: FAIL
328
+ Load Success: True
329
+ Response Time: 0.0000s
330
+ Error: RemoteAddTool.execute() missing 1 required positional argument: 'url'
331
+
332
+
333
+ Tool: remote_list
334
+ Status: FAIL
335
+ Load Success: True
336
+ Response Time: 0.0000s
337
+ Error: RemoteListTool.execute() takes 1 positional argument but 2 were given
338
+
339
+
340
+ Tool: remote_remove
341
+ Status: FAIL
342
+ Load Success: True
343
+ Response Time: 0.0000s
344
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
345
+
346
+
347
+ Tool: remote_trigger
348
+ Status: FAIL
349
+ Load Success: True
350
+ Response Time: 0.0000s
351
+ Error: RemoteTriggerTool.execute() missing 1 required positional argument: 'action'
352
+
353
+
354
+ Tool: skill_chain
355
+ Status: FAIL
356
+ Load Success: True
357
+ Response Time: 0.0000s
358
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
359
+
360
+
361
+ Tool: skill_execute
362
+ Status: FAIL
363
+ Load Success: True
364
+ Response Time: 0.0000s
365
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
366
+
367
+
368
+ Tool: skill_info
369
+ Status: FAIL
370
+ Load Success: True
371
+ Response Time: 0.0000s
372
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
373
+
374
+
375
+ Tool: skill_list
376
+ Status: FAIL
377
+ Load Success: True
378
+ Response Time: 0.0000s
379
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
380
+
381
+
382
+ Tool: skill_search
383
+ Status: FAIL
384
+ Load Success: True
385
+ Response Time: 0.0000s
386
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
387
+
388
+
389
+ Tool: sleep
390
+ Status: FAIL
391
+ Load Success: True
392
+ Response Time: 0.0000s
393
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
394
+
395
+
396
+ Tool: structure_data
397
+ Status: FAIL
398
+ Load Success: True
399
+ Response Time: 0.0000s
400
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
401
+
402
+
403
+ Tool: synthetic_output
404
+ Status: FAIL
405
+ Load Success: True
406
+ Response Time: 0.0000s
407
+ Error: SyntheticOutputTool.execute() missing 1 required positional argument: 'content'
408
+
409
+
410
+ Tool: team_assign
411
+ Status: FAIL
412
+ Load Success: True
413
+ Response Time: 0.0000s
414
+ Error: TeamAssignTaskTool.execute() missing 1 required positional argument: 'task'
415
+
416
+
417
+ Tool: team_create
418
+ Status: FAIL
419
+ Load Success: True
420
+ Response Time: 0.0000s
421
+ Error: TeamCreateTool.execute() missing 1 required positional argument: 'agents'
422
+
423
+
424
+ Tool: team_delete
425
+ Status: FAIL
426
+ Load Success: True
427
+ Response Time: 0.0000s
428
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
429
+
430
+
431
+ Tool: team_disband
432
+ Status: FAIL
433
+ Load Success: True
434
+ Response Time: 0.0000s
435
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
436
+
437
+
438
+ Tool: team_leave
439
+ Status: FAIL
440
+ Load Success: True
441
+ Response Time: 0.0000s
442
+ Error: TeamLeaveTool.execute() missing 1 required positional argument: 'agent_name'
443
+
444
+
445
+ Tool: team_list
446
+ Status: FAIL
447
+ Load Success: True
448
+ Response Time: 0.0000s
449
+ Error: TeamListTool.execute() takes 1 positional argument but 2 were given
450
+
451
+
452
+ Tool: team_status
453
+ Status: FAIL
454
+ Load Success: True
455
+ Response Time: 0.0000s
456
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
457
+
458
+
459
+ Tool: tool_capabilities
460
+ Status: FAIL
461
+ Load Success: True
462
+ Response Time: 0.0000s
463
+ Error: ToolCapabilitiesTool.execute() takes 1 positional argument but 2 were given
464
+
465
+
466
+ Tool: tool_info
467
+ Status: FAIL
468
+ Load Success: True
469
+ Response Time: 0.0000s
470
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
471
+
472
+
473
+ Tool: tool_list_all
474
+ Status: FAIL
475
+ Load Success: True
476
+ Response Time: 0.0000s
477
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
478
+
479
+
480
+ Tool: tool_search
481
+ Status: FAIL
482
+ Load Success: True
483
+ Response Time: 0.0000s
484
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
485
+
486
+
487
+ Tool: wait_for
488
+ Status: FAIL
489
+ Load Success: True
490
+ Response Time: 0.0000s
491
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
492
+
493
+
494
+ Tool: web_fetch
495
+ Status: FAIL
496
+ Load Success: True
497
+ Response Time: 0.0000s
498
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
499
+
500
+
501
+ Tool: web_fetch_meta
502
+ Status: FAIL
503
+ Load Success: True
504
+ Response Time: 0.0000s
505
+ Error: 'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes
506
+
507
+
508
+ --------------------------------------------------------------------------------
509
+ SKILLS SUMMARY
510
+ --------------------------------------------------------------------------------
511
+
512
+ Skills File: /Users/walidsobhi/.stack-2.9/skills.json
513
+ Skills File Exists: False
514
+
515
+ Skill Directories:
516
+ [EXISTS] /Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills
517
+ [MISSING] /Users/walidsobhi/.openclaw/workspace/skills
518
+
519
+ Discovered Skills: 53
520
+ - nano-pdf: ---
521
+ - himalaya: ---
522
+ - bear-notes: ---
523
+ - peekaboo: ---
524
+ - model-usage: ---
525
+ - blogwatcher: ---
526
+ - discord: ---
527
+ - coding-agent: ---
528
+ - openhue: ---
529
+ - gemini: ---
530
+ - gifgrep: ---
531
+ - session-logs: ---
532
+ - node-connect: ---
533
+ - ordercli: ---
534
+ - openai-whisper: ---
535
+ - spotify-player: ---
536
+ - healthcheck: ---
537
+ - oracle: ---
538
+ - xurl: ---
539
+ - summarize: ---
540
+ - clawhub: ---
541
+ - bluebubbles: ---
542
+ - sherpa-onnx-tts: ---
543
+ - video-frames: ---
544
+ - eightctl: ---
545
+ - gog: ---
546
+ - canvas: # Canvas Skill
547
+ - notion: ---
548
+ - goplaces: ---
549
+ - apple-reminders: ---
550
+ - imsg: ---
551
+ - skill-creator: ---
552
+ - camsnap: ---
553
+ - github: ---
554
+ - taskflow-inbox-triage: name: taskflow-inbox-triage
555
+ - things-mac: ---
556
+ - blucli: ---
557
+ - sonoscli: ---
558
+ - mcporter: ---
559
+ - tmux: ---
560
+ - sag: ---
561
+ - slack: ---
562
+ - weather: ---
563
+ - obsidian: ---
564
+ - trello: ---
565
+ - wacli: ---
566
+ - songsee: ---
567
+ - apple-notes: ---
568
+ - taskflow: name: taskflow
569
+ - openai-whisper-api: ---
570
+ - gh-issues: ---
571
+ - 1password: ---
572
+ - voice-call: ---
573
+
574
+
575
+ ================================================================================
576
+ END OF AUDIT REPORT
577
+ ================================================================================
audit_results.json ADDED
@@ -0,0 +1,1093 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "timestamp": "2026-04-09T19:39:39.247896",
3
+ "total_tools": 69,
4
+ "passed": 5,
5
+ "failed": 64,
6
+ "tools": [
7
+ {
8
+ "tool_name": "agent_spawn",
9
+ "load_success": true,
10
+ "execution_success": false,
11
+ "response_time": 0.0009522499967715703,
12
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
13
+ "data": null,
14
+ "timestamp": "2026-04-09T19:39:32.703111"
15
+ },
16
+ {
17
+ "tool_name": "agent_status",
18
+ "load_success": true,
19
+ "execution_success": false,
20
+ "response_time": 2.300000051036477e-05,
21
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
22
+ "data": null,
23
+ "timestamp": "2026-04-09T19:39:32.705308"
24
+ },
25
+ {
26
+ "tool_name": "agent_list",
27
+ "load_success": true,
28
+ "execution_success": false,
29
+ "response_time": 3.374996595084667e-06,
30
+ "error": "AgentListTool.execute() takes 1 positional argument but 2 were given",
31
+ "data": null,
32
+ "timestamp": "2026-04-09T19:39:32.705343"
33
+ },
34
+ {
35
+ "tool_name": "ask_question",
36
+ "load_success": true,
37
+ "execution_success": false,
38
+ "response_time": 1.4166005712468177e-05,
39
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
40
+ "data": null,
41
+ "timestamp": "2026-04-09T19:39:32.705357"
42
+ },
43
+ {
44
+ "tool_name": "get_pending_questions",
45
+ "load_success": true,
46
+ "execution_success": false,
47
+ "response_time": 2.2500025806948543e-06,
48
+ "error": "GetPendingQuestionsTool.execute() takes 1 positional argument but 2 were given",
49
+ "data": null,
50
+ "timestamp": "2026-04-09T19:39:32.705379"
51
+ },
52
+ {
53
+ "tool_name": "answer_question",
54
+ "load_success": true,
55
+ "execution_success": false,
56
+ "response_time": 2.124994352925569e-06,
57
+ "error": "AnswerQuestionTool.execute() missing 1 required positional argument: 'answer'",
58
+ "data": null,
59
+ "timestamp": "2026-04-09T19:39:32.705389"
60
+ },
61
+ {
62
+ "tool_name": "brief",
63
+ "load_success": true,
64
+ "execution_success": false,
65
+ "response_time": 1.0833995474968106e-05,
66
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
67
+ "data": null,
68
+ "timestamp": "2026-04-09T19:39:32.705399"
69
+ },
70
+ {
71
+ "tool_name": "brief_summary",
72
+ "load_success": true,
73
+ "execution_success": false,
74
+ "response_time": 1.0333002137485892e-05,
75
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
76
+ "data": null,
77
+ "timestamp": "2026-04-09T19:39:32.705416"
78
+ },
79
+ {
80
+ "tool_name": "Config",
81
+ "load_success": true,
82
+ "execution_success": false,
83
+ "response_time": 3.749999450519681e-06,
84
+ "error": "Unknown operation: None",
85
+ "data": null,
86
+ "timestamp": "2026-04-09T19:39:32.705434"
87
+ },
88
+ {
89
+ "tool_name": "file_edit_insert",
90
+ "load_success": true,
91
+ "execution_success": false,
92
+ "response_time": 1.958003849722445e-06,
93
+ "error": "FileEditInsertTool.execute() missing 1 required positional argument: 'content'",
94
+ "data": null,
95
+ "timestamp": "2026-04-09T19:39:32.705445"
96
+ },
97
+ {
98
+ "tool_name": "file_edit_delete",
99
+ "load_success": true,
100
+ "execution_success": false,
101
+ "response_time": 2.5291999918408692e-05,
102
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
103
+ "data": null,
104
+ "timestamp": "2026-04-09T19:39:32.705454"
105
+ },
106
+ {
107
+ "tool_name": "file_edit_replace",
108
+ "load_success": true,
109
+ "execution_success": false,
110
+ "response_time": 2.3339962353929877e-06,
111
+ "error": "FileEditReplaceTool.execute() missing 2 required positional arguments: 'pattern' and 'replacement'",
112
+ "data": null,
113
+ "timestamp": "2026-04-09T19:39:32.705486"
114
+ },
115
+ {
116
+ "tool_name": "file_edit",
117
+ "load_success": true,
118
+ "execution_success": false,
119
+ "response_time": 1.6250050975941122e-06,
120
+ "error": "FileEditTool.execute() missing 1 required positional argument: 'operation'",
121
+ "data": null,
122
+ "timestamp": "2026-04-09T19:39:32.705496"
123
+ },
124
+ {
125
+ "tool_name": "file_read",
126
+ "load_success": true,
127
+ "execution_success": false,
128
+ "response_time": 9.333001798950136e-06,
129
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
130
+ "data": null,
131
+ "timestamp": "2026-04-09T19:39:32.705504"
132
+ },
133
+ {
134
+ "tool_name": "file_exists",
135
+ "load_success": true,
136
+ "execution_success": false,
137
+ "response_time": 8.957998943515122e-06,
138
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
139
+ "data": null,
140
+ "timestamp": "2026-04-09T19:39:32.705520"
141
+ },
142
+ {
143
+ "tool_name": "file_write",
144
+ "load_success": true,
145
+ "execution_success": false,
146
+ "response_time": 2.6670022634789348e-06,
147
+ "error": "FileWriteTool.execute() missing 1 required positional argument: 'content'",
148
+ "data": null,
149
+ "timestamp": "2026-04-09T19:39:32.705536"
150
+ },
151
+ {
152
+ "tool_name": "file_delete",
153
+ "load_success": true,
154
+ "execution_success": false,
155
+ "response_time": 8.916998922359198e-06,
156
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
157
+ "data": null,
158
+ "timestamp": "2026-04-09T19:39:32.705545"
159
+ },
160
+ {
161
+ "tool_name": "glob",
162
+ "load_success": true,
163
+ "execution_success": false,
164
+ "response_time": 1.0292002116329968e-05,
165
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
166
+ "data": null,
167
+ "timestamp": "2026-04-09T19:39:32.705561"
168
+ },
169
+ {
170
+ "tool_name": "glob_list",
171
+ "load_success": true,
172
+ "execution_success": false,
173
+ "response_time": 9.791998309083283e-06,
174
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
175
+ "data": null,
176
+ "timestamp": "2026-04-09T19:39:32.705578"
177
+ },
178
+ {
179
+ "tool_name": "grep",
180
+ "load_success": true,
181
+ "execution_success": false,
182
+ "response_time": 1.7909987946040928e-06,
183
+ "error": "GrepTool.execute() missing 1 required positional argument: 'path'",
184
+ "data": null,
185
+ "timestamp": "2026-04-09T19:39:32.705594"
186
+ },
187
+ {
188
+ "tool_name": "grep_count",
189
+ "load_success": true,
190
+ "execution_success": false,
191
+ "response_time": 1.3750031939707696e-06,
192
+ "error": "GrepCountTool.execute() missing 1 required positional argument: 'path'",
193
+ "data": null,
194
+ "timestamp": "2026-04-09T19:39:32.705603"
195
+ },
196
+ {
197
+ "tool_name": "mcp_call",
198
+ "load_success": true,
199
+ "execution_success": false,
200
+ "response_time": 1.4999968698248267e-06,
201
+ "error": "MCPTool.execute() missing 1 required positional argument: 'tool_name'",
202
+ "data": null,
203
+ "timestamp": "2026-04-09T19:39:32.705611"
204
+ },
205
+ {
206
+ "tool_name": "mcp_list_servers",
207
+ "load_success": true,
208
+ "execution_success": false,
209
+ "response_time": 1.7080019460991025e-06,
210
+ "error": "MCPServerListTool.execute() takes 1 positional argument but 2 were given",
211
+ "data": null,
212
+ "timestamp": "2026-04-09T19:39:32.705619"
213
+ },
214
+ {
215
+ "tool_name": "mcp_add_server",
216
+ "load_success": true,
217
+ "execution_success": false,
218
+ "response_time": 1.5420009731315076e-06,
219
+ "error": "MCPServerAddTool.execute() missing 1 required positional argument: 'command'",
220
+ "data": null,
221
+ "timestamp": "2026-04-09T19:39:32.705627"
222
+ },
223
+ {
224
+ "tool_name": "read_mcp_resource",
225
+ "load_success": true,
226
+ "execution_success": false,
227
+ "response_time": 1.7500060494057834e-06,
228
+ "error": "ReadMcpResourceTool.execute() missing 1 required positional argument: 'resource_uri'",
229
+ "data": null,
230
+ "timestamp": "2026-04-09T19:39:32.705635"
231
+ },
232
+ {
233
+ "tool_name": "message_send",
234
+ "load_success": true,
235
+ "execution_success": false,
236
+ "response_time": 1.4999968698248267e-06,
237
+ "error": "MessageSendTool.execute() missing 1 required positional argument: 'content'",
238
+ "data": null,
239
+ "timestamp": "2026-04-09T19:39:32.705644"
240
+ },
241
+ {
242
+ "tool_name": "message_list",
243
+ "load_success": true,
244
+ "execution_success": false,
245
+ "response_time": 9.458002750761807e-06,
246
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
247
+ "data": null,
248
+ "timestamp": "2026-04-09T19:39:32.705652"
249
+ },
250
+ {
251
+ "tool_name": "message_channel",
252
+ "load_success": true,
253
+ "execution_success": false,
254
+ "response_time": 8.791997970547527e-06,
255
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
256
+ "data": null,
257
+ "timestamp": "2026-04-09T19:39:32.705668"
258
+ },
259
+ {
260
+ "tool_name": "message_template",
261
+ "load_success": true,
262
+ "execution_success": false,
263
+ "response_time": 9.290997695643455e-06,
264
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
265
+ "data": null,
266
+ "timestamp": "2026-04-09T19:39:32.705695"
267
+ },
268
+ {
269
+ "tool_name": "EnterPlanMode",
270
+ "load_success": true,
271
+ "execution_success": true,
272
+ "response_time": 0.0015690829968662001,
273
+ "error": "",
274
+ "data": {
275
+ "message": "Entered plan mode. Explore the codebase read-only and design your implementation approach.",
276
+ "plan_text": "",
277
+ "context": "",
278
+ "entered_at": "2026-04-09T17:39:32.705964+00:00"
279
+ },
280
+ "timestamp": "2026-04-09T19:39:32.705711"
281
+ },
282
+ {
283
+ "tool_name": "ExitPlanMode",
284
+ "load_success": true,
285
+ "execution_success": true,
286
+ "response_time": 0.0003088749945163727,
287
+ "error": "",
288
+ "data": {
289
+ "message": "Exited plan mode. Ready to proceed.",
290
+ "plan_text": "",
291
+ "confirmed": true,
292
+ "exited_at": "2026-04-09T17:39:32.707341+00:00"
293
+ },
294
+ "timestamp": "2026-04-09T19:39:32.707292"
295
+ },
296
+ {
297
+ "tool_name": "remote_add",
298
+ "load_success": true,
299
+ "execution_success": false,
300
+ "response_time": 2.415996277704835e-06,
301
+ "error": "RemoteAddTool.execute() missing 1 required positional argument: 'url'",
302
+ "data": null,
303
+ "timestamp": "2026-04-09T19:39:32.707611"
304
+ },
305
+ {
306
+ "tool_name": "remote_list",
307
+ "load_success": true,
308
+ "execution_success": false,
309
+ "response_time": 2.124994352925569e-06,
310
+ "error": "RemoteListTool.execute() takes 1 positional argument but 2 were given",
311
+ "data": null,
312
+ "timestamp": "2026-04-09T19:39:32.707622"
313
+ },
314
+ {
315
+ "tool_name": "remote_trigger",
316
+ "load_success": true,
317
+ "execution_success": false,
318
+ "response_time": 1.5420009731315076e-06,
319
+ "error": "RemoteTriggerTool.execute() missing 1 required positional argument: 'action'",
320
+ "data": null,
321
+ "timestamp": "2026-04-09T19:39:32.707632"
322
+ },
323
+ {
324
+ "tool_name": "remote_remove",
325
+ "load_success": true,
326
+ "execution_success": false,
327
+ "response_time": 1.670899655437097e-05,
328
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
329
+ "data": null,
330
+ "timestamp": "2026-04-09T19:39:32.707641"
331
+ },
332
+ {
333
+ "tool_name": "CronCreate",
334
+ "load_success": true,
335
+ "execution_success": false,
336
+ "response_time": 4.082998202648014e-06,
337
+ "error": "Invalid cron expression: Expected 5 fields, got 0",
338
+ "data": null,
339
+ "timestamp": "2026-04-09T19:39:32.707665"
340
+ },
341
+ {
342
+ "tool_name": "CronList",
343
+ "load_success": true,
344
+ "execution_success": true,
345
+ "response_time": 0.00027791699540102854,
346
+ "error": "",
347
+ "data": {
348
+ "jobs": [],
349
+ "total": 0
350
+ },
351
+ "timestamp": "2026-04-09T19:39:32.707677"
352
+ },
353
+ {
354
+ "tool_name": "CronDelete",
355
+ "load_success": true,
356
+ "execution_success": false,
357
+ "response_time": 1.4589968486689031e-06,
358
+ "error": "Error: id is required",
359
+ "data": null,
360
+ "timestamp": "2026-04-09T19:39:32.707963"
361
+ },
362
+ {
363
+ "tool_name": "skill_list",
364
+ "load_success": true,
365
+ "execution_success": false,
366
+ "response_time": 1.2249998690094799e-05,
367
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
368
+ "data": null,
369
+ "timestamp": "2026-04-09T19:39:32.707972"
370
+ },
371
+ {
372
+ "tool_name": "skill_execute",
373
+ "load_success": true,
374
+ "execution_success": false,
375
+ "response_time": 9.957999282050878e-06,
376
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
377
+ "data": null,
378
+ "timestamp": "2026-04-09T19:39:32.707992"
379
+ },
380
+ {
381
+ "tool_name": "skill_info",
382
+ "load_success": true,
383
+ "execution_success": false,
384
+ "response_time": 9.791998309083283e-06,
385
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
386
+ "data": null,
387
+ "timestamp": "2026-04-09T19:39:32.708008"
388
+ },
389
+ {
390
+ "tool_name": "skill_chain",
391
+ "load_success": true,
392
+ "execution_success": false,
393
+ "response_time": 9.165996743831784e-06,
394
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
395
+ "data": null,
396
+ "timestamp": "2026-04-09T19:39:32.708025"
397
+ },
398
+ {
399
+ "tool_name": "skill_search",
400
+ "load_success": true,
401
+ "execution_success": false,
402
+ "response_time": 9.290997695643455e-06,
403
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
404
+ "data": null,
405
+ "timestamp": "2026-04-09T19:39:32.708041"
406
+ },
407
+ {
408
+ "tool_name": "sleep",
409
+ "load_success": true,
410
+ "execution_success": false,
411
+ "response_time": 1.1249998351559043e-05,
412
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
413
+ "data": null,
414
+ "timestamp": "2026-04-09T19:39:32.708057"
415
+ },
416
+ {
417
+ "tool_name": "wait_for",
418
+ "load_success": true,
419
+ "execution_success": false,
420
+ "response_time": 1.962499663932249e-05,
421
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
422
+ "data": null,
423
+ "timestamp": "2026-04-09T19:39:32.708076"
424
+ },
425
+ {
426
+ "tool_name": "synthetic_output",
427
+ "load_success": true,
428
+ "execution_success": false,
429
+ "response_time": 2.0830048015341163e-06,
430
+ "error": "SyntheticOutputTool.execute() missing 1 required positional argument: 'content'",
431
+ "data": null,
432
+ "timestamp": "2026-04-09T19:39:32.708102"
433
+ },
434
+ {
435
+ "tool_name": "structure_data",
436
+ "load_success": true,
437
+ "execution_success": false,
438
+ "response_time": 9.292001777794212e-06,
439
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
440
+ "data": null,
441
+ "timestamp": "2026-04-09T19:39:32.708112"
442
+ },
443
+ {
444
+ "tool_name": "TaskCreate",
445
+ "load_success": true,
446
+ "execution_success": false,
447
+ "response_time": 1.2499949662014842e-06,
448
+ "error": "Error: subject is required",
449
+ "data": null,
450
+ "timestamp": "2026-04-09T19:39:32.708128"
451
+ },
452
+ {
453
+ "tool_name": "TaskList",
454
+ "load_success": true,
455
+ "execution_success": true,
456
+ "response_time": 0.000200708003831096,
457
+ "error": "",
458
+ "data": {
459
+ "tasks": [
460
+ {
461
+ "id": "9d03b654",
462
+ "subject": "Test task",
463
+ "status": "pending",
464
+ "priority": "medium",
465
+ "tags": [],
466
+ "created_at": "2026-04-09T14:01:17.414719+00:00"
467
+ },
468
+ {
469
+ "id": "b8481a6f",
470
+ "subject": "Test task",
471
+ "status": "pending",
472
+ "priority": "medium",
473
+ "tags": [],
474
+ "created_at": "2026-04-09T14:01:42.697266+00:00"
475
+ },
476
+ {
477
+ "id": "72d5a5e9",
478
+ "subject": "Test task",
479
+ "status": "pending",
480
+ "priority": "medium",
481
+ "tags": [],
482
+ "created_at": "2026-04-09T14:04:23.970246+00:00"
483
+ },
484
+ {
485
+ "id": "6f80c043",
486
+ "subject": "Test task",
487
+ "status": "pending",
488
+ "priority": "medium",
489
+ "tags": [],
490
+ "created_at": "2026-04-09T14:04:47.025132+00:00"
491
+ },
492
+ {
493
+ "id": "4c9976bd",
494
+ "subject": "Test task",
495
+ "status": "pending",
496
+ "priority": "medium",
497
+ "tags": [],
498
+ "created_at": "2026-04-09T14:08:42.369611+00:00"
499
+ },
500
+ {
501
+ "id": "a0883efe",
502
+ "subject": "Test",
503
+ "status": "pending",
504
+ "priority": "medium",
505
+ "tags": [],
506
+ "created_at": "2026-04-09T14:09:40.319278+00:00"
507
+ },
508
+ {
509
+ "id": "fcdc7655",
510
+ "subject": "Test",
511
+ "status": "pending",
512
+ "priority": "medium",
513
+ "tags": [],
514
+ "created_at": "2026-04-09T14:10:02.638019+00:00"
515
+ },
516
+ {
517
+ "id": "58822bf8",
518
+ "subject": "Test",
519
+ "status": "pending",
520
+ "priority": "medium",
521
+ "tags": [],
522
+ "created_at": "2026-04-09T14:10:42.815505+00:00"
523
+ },
524
+ {
525
+ "id": "6f569705",
526
+ "subject": "Test",
527
+ "status": "pending",
528
+ "priority": "medium",
529
+ "tags": [],
530
+ "created_at": "2026-04-09T14:11:11.092777+00:00"
531
+ },
532
+ {
533
+ "id": "4b6ebce7",
534
+ "subject": "Test",
535
+ "status": "pending",
536
+ "priority": "medium",
537
+ "tags": [],
538
+ "created_at": "2026-04-09T14:11:36.125577+00:00"
539
+ },
540
+ {
541
+ "id": "f38ee9a3",
542
+ "subject": "Test",
543
+ "status": "pending",
544
+ "priority": "medium",
545
+ "tags": [],
546
+ "created_at": "2026-04-09T14:12:34.756438+00:00"
547
+ },
548
+ {
549
+ "id": "76f297e9",
550
+ "subject": "Test",
551
+ "status": "pending",
552
+ "priority": "medium",
553
+ "tags": [],
554
+ "created_at": "2026-04-09T14:13:16.661552+00:00"
555
+ },
556
+ {
557
+ "id": "bc735c85",
558
+ "subject": "Test task",
559
+ "status": "pending",
560
+ "priority": "medium",
561
+ "tags": [],
562
+ "created_at": "2026-04-09T14:13:44.904743+00:00"
563
+ },
564
+ {
565
+ "id": "23f75706",
566
+ "subject": "Audit test",
567
+ "status": "pending",
568
+ "priority": "medium",
569
+ "tags": [],
570
+ "created_at": "2026-04-09T16:13:02.202480+00:00"
571
+ }
572
+ ],
573
+ "total": 14
574
+ },
575
+ "timestamp": "2026-04-09T19:39:32.708136"
576
+ },
577
+ {
578
+ "tool_name": "TaskUpdate",
579
+ "load_success": true,
580
+ "execution_success": false,
581
+ "response_time": 1.4170000213198364e-06,
582
+ "error": "Error: id is required",
583
+ "data": null,
584
+ "timestamp": "2026-04-09T19:39:32.708345"
585
+ },
586
+ {
587
+ "tool_name": "TaskDelete",
588
+ "load_success": true,
589
+ "execution_success": false,
590
+ "response_time": 1.2919990695081651e-06,
591
+ "error": "Error: id is required",
592
+ "data": null,
593
+ "timestamp": "2026-04-09T19:39:32.708354"
594
+ },
595
+ {
596
+ "tool_name": "team_delete",
597
+ "load_success": true,
598
+ "execution_success": false,
599
+ "response_time": 1.0916002793237567e-05,
600
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
601
+ "data": null,
602
+ "timestamp": "2026-04-09T19:39:32.708362"
603
+ },
604
+ {
605
+ "tool_name": "team_leave",
606
+ "load_success": true,
607
+ "execution_success": false,
608
+ "response_time": 2.124994352925569e-06,
609
+ "error": "TeamLeaveTool.execute() missing 1 required positional argument: 'agent_name'",
610
+ "data": null,
611
+ "timestamp": "2026-04-09T19:39:32.708380"
612
+ },
613
+ {
614
+ "tool_name": "team_create",
615
+ "load_success": true,
616
+ "execution_success": false,
617
+ "response_time": 2.0839943317696452e-06,
618
+ "error": "TeamCreateTool.execute() missing 1 required positional argument: 'agents'",
619
+ "data": null,
620
+ "timestamp": "2026-04-09T19:39:32.708390"
621
+ },
622
+ {
623
+ "tool_name": "team_disband",
624
+ "load_success": true,
625
+ "execution_success": false,
626
+ "response_time": 9.667004633229226e-06,
627
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
628
+ "data": null,
629
+ "timestamp": "2026-04-09T19:39:32.708399"
630
+ },
631
+ {
632
+ "tool_name": "team_list",
633
+ "load_success": true,
634
+ "execution_success": false,
635
+ "response_time": 1.7500060494057834e-06,
636
+ "error": "TeamListTool.execute() takes 1 positional argument but 2 were given",
637
+ "data": null,
638
+ "timestamp": "2026-04-09T19:39:32.708415"
639
+ },
640
+ {
641
+ "tool_name": "team_status",
642
+ "load_success": true,
643
+ "execution_success": false,
644
+ "response_time": 9.125003998633474e-06,
645
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
646
+ "data": null,
647
+ "timestamp": "2026-04-09T19:39:32.708424"
648
+ },
649
+ {
650
+ "tool_name": "team_assign",
651
+ "load_success": true,
652
+ "execution_success": false,
653
+ "response_time": 1.8329956219531596e-06,
654
+ "error": "TeamAssignTaskTool.execute() missing 1 required positional argument: 'task'",
655
+ "data": null,
656
+ "timestamp": "2026-04-09T19:39:32.708441"
657
+ },
658
+ {
659
+ "tool_name": "TodoWrite",
660
+ "load_success": true,
661
+ "execution_success": false,
662
+ "response_time": 0.00019041700579691678,
663
+ "error": "Unknown operation: None",
664
+ "data": null,
665
+ "timestamp": "2026-04-09T19:39:32.708450"
666
+ },
667
+ {
668
+ "tool_name": "tool_search",
669
+ "load_success": true,
670
+ "execution_success": false,
671
+ "response_time": 1.1292002454865724e-05,
672
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
673
+ "data": null,
674
+ "timestamp": "2026-04-09T19:39:32.708649"
675
+ },
676
+ {
677
+ "tool_name": "tool_list_all",
678
+ "load_success": true,
679
+ "execution_success": false,
680
+ "response_time": 9.12499672267586e-06,
681
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
682
+ "data": null,
683
+ "timestamp": "2026-04-09T19:39:32.708667"
684
+ },
685
+ {
686
+ "tool_name": "tool_info",
687
+ "load_success": true,
688
+ "execution_success": false,
689
+ "response_time": 9.082999895326793e-06,
690
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
691
+ "data": null,
692
+ "timestamp": "2026-04-09T19:39:32.708684"
693
+ },
694
+ {
695
+ "tool_name": "tool_capabilities",
696
+ "load_success": true,
697
+ "execution_success": false,
698
+ "response_time": 1.7909987946040928e-06,
699
+ "error": "ToolCapabilitiesTool.execute() takes 1 positional argument but 2 were given",
700
+ "data": null,
701
+ "timestamp": "2026-04-09T19:39:32.708700"
702
+ },
703
+ {
704
+ "tool_name": "web_fetch",
705
+ "load_success": true,
706
+ "execution_success": false,
707
+ "response_time": 2.2291998902801424e-05,
708
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
709
+ "data": null,
710
+ "timestamp": "2026-04-09T19:39:32.708708"
711
+ },
712
+ {
713
+ "tool_name": "web_fetch_meta",
714
+ "load_success": true,
715
+ "execution_success": false,
716
+ "response_time": 9.249997674487531e-06,
717
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
718
+ "data": null,
719
+ "timestamp": "2026-04-09T19:39:32.708738"
720
+ },
721
+ {
722
+ "tool_name": "WebSearch",
723
+ "load_success": true,
724
+ "execution_success": true,
725
+ "response_time": 6.530023749997781,
726
+ "error": "",
727
+ "data": {
728
+ "query": "Python testing",
729
+ "results": [
730
+ {
731
+ "title": "tox (Python testing wrapper)",
732
+ "url": "https://grokipedia.com/page/tox_python_testing_wrapper",
733
+ "snippet": "tox is a command-line tool for Python that automates and standardizes testing by managing virtual environments and executing tests across multiple configurations, such as different Python versions, implementations, or dependencies. Developed by Holg\u2026"
734
+ },
735
+ {
736
+ "title": "PythonTesting: The Right Way toTestYour Code | Evytor Daily",
737
+ "url": "https://evytor.vercel.app/blogs/testing-your-python-code-the-right-way",
738
+ "snippet": "TestingyourPythoncode is essential for building robust, reliable, and maintainable applications. This guide provides a comprehensive overview ofPythontestingbest practices..."
739
+ },
740
+ {
741
+ "title": "RunPythonCode Online - FreePythonCompiler & Interpreter",
742
+ "url": "https://python.codeutility.io/",
743
+ "snippet": "RunPythonCode Online. A fast, browser-based CLI totestPythonscripts without setup, perfect for learning and quick debugging."
744
+ },
745
+ {
746
+ "title": "PythonQuiz",
747
+ "url": "https://www.w3schools.com/python/python_quiz.asp",
748
+ "snippet": "Thetestis not official, it's just a nice way to see how much you know, or don't know, aboutPython. Count Your Score. You will get 1 point for each correct answer."
749
+ },
750
+ {
751
+ "title": "unittest \u2014 Unittestingframework \u2014Python3.14.4 documentation",
752
+ "url": "https://docs.python.org/3/library/unittest.html",
753
+ "snippet": "python-m unittesttests/test_something.py. This allows you to use the shell filename completion to specify thetestmodule. The file specified must still be importable as a module."
754
+ },
755
+ {
756
+ "title": "Pytest Tutorial - UnitTestinginPythonusing Pytest... - GeeksforGeeks",
757
+ "url": "https://www.geeksforgeeks.org/python/pytest-tutorial-testing-python-application-using-pytest/",
758
+ "snippet": "Another functiontest_reverse_test()teststhis function using assert, verifying that reverse_text(\"python\") returns \"nohtyp\". If the condition is true thetestpasses..."
759
+ },
760
+ {
761
+ "title": "OnlinePythonCompiler",
762
+ "url": "https://www.online-python.com/online_python_compiler",
763
+ "snippet": "OnlinePythonIDE is a web-based tool powered by ACE code editor. This tool can be used to learn, build, run,testyourpythonscript."
764
+ },
765
+ {
766
+ "title": "AutomatedTestinginPythonwith pytest, tox, and GitHub... - YouTube",
767
+ "url": "https://www.youtube.com/watch?v=DhUpxWjOhME",
768
+ "snippet": "Take yourPythonproject to the next level of professionalism.AutomatedtestinginPythonis an important way to take yourPythonproject to the next level o..."
769
+ },
770
+ {
771
+ "title": "Easy Patterns forTestablePythonCode | by Just a ML guy | Medium",
772
+ "url": "https://medium.com/@justamlguy/easy-patterns-for-testable-python-code-6e103cc68616",
773
+ "snippet": "Patches dynamically overwrite real functions or objects at runtime, for example with mocks. These are powerful tools inPython. They enable you totestthe otherwise untestable code."
774
+ },
775
+ {
776
+ "title": "PythonOnlineTest& Quiz",
777
+ "url": "https://www.codechef.com/skill-test/basic-python",
778
+ "snippet": "Take thePythonOnlineTest& Quiz assessment to evaluate practical skills effectively using a set of well-designed, high-quality questions."
779
+ }
780
+ ],
781
+ "duration_seconds": 0.0,
782
+ "source": "duckduckgo"
783
+ },
784
+ "timestamp": "2026-04-09T19:39:32.708755"
785
+ },
786
+ {
787
+ "tool_name": "enter_worktree",
788
+ "load_success": true,
789
+ "execution_success": false,
790
+ "response_time": 0.00015958300355123356,
791
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
792
+ "data": null,
793
+ "timestamp": "2026-04-09T19:39:39.238786"
794
+ },
795
+ {
796
+ "tool_name": "exit_worktree",
797
+ "load_success": true,
798
+ "execution_success": false,
799
+ "response_time": 2.300000051036477e-05,
800
+ "error": "'coroutine' object has no attribute 'duration_seconds' and no __dict__ for setting new attributes",
801
+ "data": null,
802
+ "timestamp": "2026-04-09T19:39:39.238997"
803
+ },
804
+ {
805
+ "tool_name": "list_worktrees",
806
+ "load_success": true,
807
+ "execution_success": false,
808
+ "response_time": 7.791000825818628e-06,
809
+ "error": "ListWorktreesTool.execute() takes 1 positional argument but 2 were given",
810
+ "data": null,
811
+ "timestamp": "2026-04-09T19:39:39.239029"
812
+ }
813
+ ],
814
+ "skills": {
815
+ "skills_file": "/Users/walidsobhi/.stack-2.9/skills.json",
816
+ "skills_file_exists": false,
817
+ "skill_dirs": [
818
+ "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills",
819
+ "/Users/walidsobhi/.openclaw/workspace/skills"
820
+ ],
821
+ "skill_dirs_exist": [
822
+ true,
823
+ false
824
+ ],
825
+ "discovered_skills": [
826
+ {
827
+ "name": "nano-pdf",
828
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/nano-pdf",
829
+ "description": "---"
830
+ },
831
+ {
832
+ "name": "himalaya",
833
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/himalaya",
834
+ "description": "---"
835
+ },
836
+ {
837
+ "name": "bear-notes",
838
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/bear-notes",
839
+ "description": "---"
840
+ },
841
+ {
842
+ "name": "peekaboo",
843
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/peekaboo",
844
+ "description": "---"
845
+ },
846
+ {
847
+ "name": "model-usage",
848
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/model-usage",
849
+ "description": "---"
850
+ },
851
+ {
852
+ "name": "blogwatcher",
853
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/blogwatcher",
854
+ "description": "---"
855
+ },
856
+ {
857
+ "name": "discord",
858
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/discord",
859
+ "description": "---"
860
+ },
861
+ {
862
+ "name": "coding-agent",
863
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/coding-agent",
864
+ "description": "---"
865
+ },
866
+ {
867
+ "name": "openhue",
868
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/openhue",
869
+ "description": "---"
870
+ },
871
+ {
872
+ "name": "gemini",
873
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/gemini",
874
+ "description": "---"
875
+ },
876
+ {
877
+ "name": "gifgrep",
878
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/gifgrep",
879
+ "description": "---"
880
+ },
881
+ {
882
+ "name": "session-logs",
883
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/session-logs",
884
+ "description": "---"
885
+ },
886
+ {
887
+ "name": "node-connect",
888
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/node-connect",
889
+ "description": "---"
890
+ },
891
+ {
892
+ "name": "ordercli",
893
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/ordercli",
894
+ "description": "---"
895
+ },
896
+ {
897
+ "name": "openai-whisper",
898
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/openai-whisper",
899
+ "description": "---"
900
+ },
901
+ {
902
+ "name": "spotify-player",
903
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/spotify-player",
904
+ "description": "---"
905
+ },
906
+ {
907
+ "name": "healthcheck",
908
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/healthcheck",
909
+ "description": "---"
910
+ },
911
+ {
912
+ "name": "oracle",
913
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/oracle",
914
+ "description": "---"
915
+ },
916
+ {
917
+ "name": "xurl",
918
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/xurl",
919
+ "description": "---"
920
+ },
921
+ {
922
+ "name": "summarize",
923
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/summarize",
924
+ "description": "---"
925
+ },
926
+ {
927
+ "name": "clawhub",
928
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/clawhub",
929
+ "description": "---"
930
+ },
931
+ {
932
+ "name": "bluebubbles",
933
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/bluebubbles",
934
+ "description": "---"
935
+ },
936
+ {
937
+ "name": "sherpa-onnx-tts",
938
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/sherpa-onnx-tts",
939
+ "description": "---"
940
+ },
941
+ {
942
+ "name": "video-frames",
943
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/video-frames",
944
+ "description": "---"
945
+ },
946
+ {
947
+ "name": "eightctl",
948
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/eightctl",
949
+ "description": "---"
950
+ },
951
+ {
952
+ "name": "gog",
953
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/gog",
954
+ "description": "---"
955
+ },
956
+ {
957
+ "name": "canvas",
958
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/canvas",
959
+ "description": "# Canvas Skill"
960
+ },
961
+ {
962
+ "name": "notion",
963
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/notion",
964
+ "description": "---"
965
+ },
966
+ {
967
+ "name": "goplaces",
968
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/goplaces",
969
+ "description": "---"
970
+ },
971
+ {
972
+ "name": "apple-reminders",
973
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/apple-reminders",
974
+ "description": "---"
975
+ },
976
+ {
977
+ "name": "imsg",
978
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/imsg",
979
+ "description": "---"
980
+ },
981
+ {
982
+ "name": "skill-creator",
983
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/skill-creator",
984
+ "description": "---"
985
+ },
986
+ {
987
+ "name": "camsnap",
988
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/camsnap",
989
+ "description": "---"
990
+ },
991
+ {
992
+ "name": "github",
993
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/github",
994
+ "description": "---"
995
+ },
996
+ {
997
+ "name": "taskflow-inbox-triage",
998
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/taskflow-inbox-triage",
999
+ "description": "name: taskflow-inbox-triage"
1000
+ },
1001
+ {
1002
+ "name": "things-mac",
1003
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/things-mac",
1004
+ "description": "---"
1005
+ },
1006
+ {
1007
+ "name": "blucli",
1008
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/blucli",
1009
+ "description": "---"
1010
+ },
1011
+ {
1012
+ "name": "sonoscli",
1013
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/sonoscli",
1014
+ "description": "---"
1015
+ },
1016
+ {
1017
+ "name": "mcporter",
1018
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/mcporter",
1019
+ "description": "---"
1020
+ },
1021
+ {
1022
+ "name": "tmux",
1023
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/tmux",
1024
+ "description": "---"
1025
+ },
1026
+ {
1027
+ "name": "sag",
1028
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/sag",
1029
+ "description": "---"
1030
+ },
1031
+ {
1032
+ "name": "slack",
1033
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/slack",
1034
+ "description": "---"
1035
+ },
1036
+ {
1037
+ "name": "weather",
1038
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/weather",
1039
+ "description": "---"
1040
+ },
1041
+ {
1042
+ "name": "obsidian",
1043
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/obsidian",
1044
+ "description": "---"
1045
+ },
1046
+ {
1047
+ "name": "trello",
1048
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/trello",
1049
+ "description": "---"
1050
+ },
1051
+ {
1052
+ "name": "wacli",
1053
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/wacli",
1054
+ "description": "---"
1055
+ },
1056
+ {
1057
+ "name": "songsee",
1058
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/songsee",
1059
+ "description": "---"
1060
+ },
1061
+ {
1062
+ "name": "apple-notes",
1063
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/apple-notes",
1064
+ "description": "---"
1065
+ },
1066
+ {
1067
+ "name": "taskflow",
1068
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/taskflow",
1069
+ "description": "name: taskflow"
1070
+ },
1071
+ {
1072
+ "name": "openai-whisper-api",
1073
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/openai-whisper-api",
1074
+ "description": "---"
1075
+ },
1076
+ {
1077
+ "name": "gh-issues",
1078
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/gh-issues",
1079
+ "description": "---"
1080
+ },
1081
+ {
1082
+ "name": "1password",
1083
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/1password",
1084
+ "description": "---"
1085
+ },
1086
+ {
1087
+ "name": "voice-call",
1088
+ "path": "/Users/walidsobhi/.npm-global/lib/node_modules/openclaw/skills/voice-call",
1089
+ "description": "---"
1090
+ }
1091
+ ]
1092
+ }
1093
+ }
audit_tools.py ADDED
@@ -0,0 +1,476 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Comprehensive audit script for Stack 2.9 tools and skills.
3
+
4
+ This script:
5
+ 1. Imports all tools and skills
6
+ 2. Tests each tool with appropriate test input
7
+ 3. Measures execution time
8
+ 4. Reports pass/fail status
9
+ """
10
+
11
+ import asyncio
12
+ import json
13
+ import sys
14
+ import time
15
+ import traceback
16
+ from datetime import datetime
17
+ from pathlib import Path
18
+ from typing import Any, Callable, Dict, List, Optional
19
+
20
+ # Ensure proper imports
21
+ sys.path.insert(0, '/Users/walidsobhi/stack-2.9/src')
22
+
23
+ # Import tools module (triggers registration)
24
+ import tools
25
+ from tools.base import BaseTool, ToolResult
26
+ from tools.registry import get_registry
27
+
28
+
29
+ # Test input definitions for each tool
30
+ TOOL_TEST_INPUTS: Dict[str, Dict[str, Any]] = {
31
+ # File tools
32
+ "file_read": {"path": "/Users/walidsobhi/stack-2.9/audit_tools.py"},
33
+ "file_exists": {"path": "/Users/walidsobhi/stack-2.9/audit_tools.py"},
34
+ "file_write": {"path": "/tmp/audit_test.txt", "content": "Test content from audit"},
35
+ "file_edit": {
36
+ "file_path": "/tmp/audit_test_edit.txt",
37
+ "old_string": "old content",
38
+ "new_string": "new content",
39
+ "replace_all": False
40
+ },
41
+ "glob": {"pattern": "**/*.py", "path": "/Users/walidsobhi/stack-2.9/src"},
42
+ "grep": {"pattern": "def ", "path": "/Users/walidsobhi/stack-2.9/src/tools"},
43
+ # Web tools
44
+ "WebSearch": {"query": "Python testing"},
45
+ "WebFetch": {"url": "https://example.com", "prompt": "Extract the main heading"},
46
+ # Task tools
47
+ "task_create": {"subject": "Test task", "description": "Test description", "activeForm": "Testing"},
48
+ "task_list": {},
49
+ "task_update": {"taskId": "nonexistent", "status": "completed"},
50
+ "task_get": {"taskId": "test"},
51
+ # Todo tools
52
+ "todo_list": {},
53
+ "todo_add": {"content": "Test todo item"},
54
+ "todo_complete": {"item_id": "test"},
55
+ "todo_delete": {"item_id": "test"},
56
+ # Config tools
57
+ "config_get": {"key": "test.key"},
58
+ "config_set": {"key": "test.key", "value": "test_value"},
59
+ # Team tools
60
+ "team_list": {},
61
+ "team_create": {"team_name": "test_team", "members": ["user1"]},
62
+ "team_delete": {"team_name": "test_team"},
63
+ # Skill tools
64
+ "skill_list": {},
65
+ "skill_search": {"query": "test"},
66
+ "skill_info": {"skill_name": "nonexistent"},
67
+ "skill_execute": {"skill_name": "nonexistent"},
68
+ "skill_chain": {"skills": []},
69
+ # Scheduling
70
+ "schedule_list": {},
71
+ "schedule_add": {"title": "Test event", "time": "2025-01-01T10:00:00"},
72
+ "schedule_delete": {"event_id": "test"},
73
+ # Messaging
74
+ "message_send": {"recipient": "test_user", "message": "Test message"},
75
+ "message_list": {},
76
+ # Brief tool
77
+ "brief_generate": {"content": "This is test content for the brief tool."},
78
+ # Ask question
79
+ "ask_question": {"question": "What is 2+2?"},
80
+ # Sleep tool
81
+ "sleep": {"seconds": 0.1},
82
+ # Plan mode
83
+ "plan_create": {"prompt": "Create a test plan"},
84
+ "plan_execute": {"plan_id": "test"},
85
+ # MCP tool
86
+ "mcp_list": {},
87
+ "mcp_invoke": {"server": "test", "method": "test"},
88
+ # Worktree tool
89
+ "worktree_list": {},
90
+ "worktree_create": {"name": "test-branch", "base": "main"},
91
+ "worktree_remove": {"name": "test-branch"},
92
+ # Remote trigger
93
+ "remote_trigger_execute": {"target": "test-target", "action": "ping"},
94
+ "remote_trigger_status": {"job_id": "test"},
95
+ # Agent tool
96
+ "agent_execute": {"task": "Test task", "context": {}},
97
+ "agent_status": {"job_id": "test"},
98
+ # Synthetic output
99
+ "synthetic_generate": {"prompt": "Generate test data", "format": "json"},
100
+ # Tool discovery
101
+ "tool_discover": {"query": "file"},
102
+ "tool_search": {"pattern": "file"},
103
+ # Config
104
+ "config_list": {},
105
+ "config_delete": {"key": "test.key"},
106
+ }
107
+
108
+
109
+ def get_test_input(tool_name: str) -> Optional[Dict[str, Any]]:
110
+ """Get test input for a specific tool."""
111
+ return TOOL_TEST_INPUTS.get(tool_name)
112
+
113
+
114
+ class AuditResult:
115
+ """Result of auditing a single tool."""
116
+
117
+ def __init__(
118
+ self,
119
+ tool_name: str,
120
+ load_success: bool = False,
121
+ execution_success: bool = False,
122
+ response_time: float = 0.0,
123
+ error: str = "",
124
+ data: Any = None,
125
+ ):
126
+ self.tool_name = tool_name
127
+ self.load_success = load_success
128
+ self.execution_success = execution_success
129
+ self.response_time = response_time
130
+ self.error = error
131
+ self.data = data
132
+ self.timestamp = datetime.now().isoformat()
133
+
134
+ def to_dict(self) -> Dict[str, Any]:
135
+ return {
136
+ "tool_name": self.tool_name,
137
+ "load_success": self.load_success,
138
+ "execution_success": self.execution_success,
139
+ "response_time": self.response_time,
140
+ "error": self.error,
141
+ "data": self.data,
142
+ "timestamp": self.timestamp,
143
+ }
144
+
145
+
146
+ async def test_tool_async(tool: BaseTool, test_input: Dict[str, Any]) -> AuditResult:
147
+ """Test a tool with async execution."""
148
+ result = AuditResult(tool_name=tool.name, load_success=True)
149
+
150
+ try:
151
+ start_time = time.perf_counter()
152
+
153
+ # Check if tool has async execute method
154
+ if asyncio.iscoroutinefunction(tool.execute):
155
+ result_data = await tool.execute(**test_input)
156
+ else:
157
+ result_data = tool.execute(**test_input)
158
+
159
+ result.response_time = time.perf_counter() - start_time
160
+
161
+ # Check if result is a ToolResult
162
+ if isinstance(result_data, ToolResult):
163
+ result.execution_success = result_data.success
164
+ result.error = result_data.error or ""
165
+ result.data = result_data.data
166
+ else:
167
+ # Handle non-ToolResult returns
168
+ result.execution_success = True
169
+ result.data = result_data
170
+
171
+ except Exception as e:
172
+ result.response_time = time.perf_counter() - start_time
173
+ result.execution_success = False
174
+ result.error = f"{type(e).__name__}: {str(e)}"
175
+ result.data = traceback.format_exc()
176
+
177
+ return result
178
+
179
+
180
+ def test_tool_sync(tool: BaseTool, test_input: Dict[str, Any]) -> AuditResult:
181
+ """Test a tool with sync execution."""
182
+ result = AuditResult(tool_name=tool.name, load_success=True)
183
+
184
+ try:
185
+ start_time = time.perf_counter()
186
+ result_data = tool.execute(**test_input)
187
+ result.response_time = time.perf_counter() - start_time
188
+
189
+ # Check if result is a ToolResult
190
+ if isinstance(result_data, ToolResult):
191
+ result.execution_success = result_data.success
192
+ result.error = result_data.error or ""
193
+ result.data = result_data.data
194
+ else:
195
+ result.execution_success = True
196
+ result.data = result_data
197
+
198
+ except Exception as e:
199
+ result.response_time = time.perf_counter() - start_time
200
+ result.execution_success = False
201
+ result.error = f"{type(e).__name__}: {str(e)}"
202
+ result.data = traceback.format_exc()
203
+
204
+ return result
205
+
206
+
207
+ def test_tool_call_method(tool: BaseTool, test_input: Dict[str, Any]) -> AuditResult:
208
+ """Test a tool using the call method."""
209
+ result = AuditResult(tool_name=tool.name, load_success=True)
210
+
211
+ try:
212
+ start_time = time.perf_counter()
213
+ result_data = tool.call(test_input)
214
+ result.response_time = time.perf_counter() - start_time
215
+
216
+ if isinstance(result_data, ToolResult):
217
+ result.execution_success = result_data.success
218
+ result.error = result_data.error or ""
219
+ result.data = result_data.data
220
+ else:
221
+ result.execution_success = True
222
+ result.data = result_data
223
+
224
+ except Exception as e:
225
+ result.response_time = time.perf_counter() - start_time
226
+ result.execution_success = False
227
+ result.error = f"{type(e).__name__}: {str(e)}"
228
+ result.data = traceback.format_exc()
229
+
230
+ return result
231
+
232
+
233
+ async def audit_tool(tool: BaseTool) -> AuditResult:
234
+ """Audit a single tool."""
235
+ tool_name = tool.name
236
+
237
+ # Get test input for this tool
238
+ test_input = get_test_input(tool_name)
239
+ if not test_input:
240
+ # Use empty dict as default
241
+ test_input = {}
242
+
243
+ # Try different execution methods
244
+ try:
245
+ # First try the call method which handles timing and validation
246
+ return test_tool_call_method(tool, test_input)
247
+ except Exception as e:
248
+ # If call method fails, try async execute
249
+ if asyncio.iscoroutinefunction(tool.execute):
250
+ try:
251
+ return await test_tool_async(tool, test_input)
252
+ except Exception as e2:
253
+ return AuditResult(
254
+ tool_name=tool_name,
255
+ load_success=True,
256
+ execution_success=False,
257
+ error=f"Async execute failed: {type(e2).__name__}: {str(e2)}"
258
+ )
259
+ else:
260
+ # Try sync execute
261
+ try:
262
+ return test_tool_sync(tool, test_input)
263
+ except Exception as e2:
264
+ return AuditResult(
265
+ tool_name=tool_name,
266
+ load_success=True,
267
+ execution_success=False,
268
+ error=f"Sync execute failed: {type(e2).__name__}: {str(e2)}"
269
+ )
270
+
271
+
272
+ async def audit_tools() -> List[AuditResult]:
273
+ """Audit all registered tools."""
274
+ registry = get_registry()
275
+ tool_names = registry.list()
276
+
277
+ print(f"\n{'='*60}")
278
+ print(f"STACK 2.9 TOOLS AUDIT")
279
+ print(f"{'='*60}")
280
+ print(f"Found {len(tool_names)} registered tools:")
281
+ for name in sorted(tool_names):
282
+ print(f" - {name}")
283
+
284
+ results = []
285
+
286
+ for tool_name in tool_names:
287
+ tool = registry.get(tool_name)
288
+ if tool is None:
289
+ print(f"\n[ERROR] Tool '{tool_name}' not found in registry")
290
+ continue
291
+
292
+ print(f"\n[TESTING] {tool_name}...", end=" ", flush=True)
293
+
294
+ result = await audit_tool(tool)
295
+ results.append(result)
296
+
297
+ if result.execution_success:
298
+ print(f"PASS ({result.response_time:.4f}s)")
299
+ else:
300
+ print(f"FAIL ({result.response_time:.4f}s)")
301
+ if result.error:
302
+ error_preview = result.error[:100] if len(result.error) > 100 else result.error
303
+ print(f" Error: {error_preview}")
304
+
305
+ return results
306
+
307
+
308
+ def check_skills() -> Dict[str, Any]:
309
+ """Check for available skills."""
310
+ from tools.skill_tool import _discover_skills, SKILLS_FILE, SKILL_DIRS
311
+
312
+ print(f"\n{'='*60}")
313
+ print(f"SKILLS CHECK")
314
+ print(f"{'='*60}")
315
+
316
+ skills_info = {
317
+ "skills_file": str(SKILLS_FILE),
318
+ "skills_file_exists": SKILLS_FILE.exists(),
319
+ "skill_dirs": [str(d) for d in SKILL_DIRS],
320
+ "skill_dirs_exist": [d.exists() for d in SKILL_DIRS],
321
+ "discovered_skills": [],
322
+ }
323
+
324
+ try:
325
+ discovered = _discover_skills()
326
+ skills_info["discovered_skills"] = discovered
327
+ print(f"Discovered {len(discovered)} skills from directories")
328
+ for skill in discovered:
329
+ print(f" - {skill['name']}: {skill.get('description', 'No description')[:50]}")
330
+ except Exception as e:
331
+ print(f"Error discovering skills: {e}")
332
+ skills_info["error"] = str(e)
333
+
334
+ return skills_info
335
+
336
+
337
+ def generate_report(results: List[AuditResult], skills_info: Dict[str, Any]) -> str:
338
+ """Generate a comprehensive audit report."""
339
+
340
+ # Calculate statistics
341
+ total_tools = len(results)
342
+ passed = sum(1 for r in results if r.execution_success)
343
+ failed = total_tools - passed
344
+
345
+ response_times = [r.response_time for r in results if r.response_time > 0]
346
+ avg_response_time = sum(response_times) / len(response_times) if response_times else 0
347
+ min_response_time = min(response_times) if response_times else 0
348
+ max_response_time = max(response_times) if response_times else 0
349
+
350
+ report = f"""
351
+ ================================================================================
352
+ STACK 2.9 COMPREHENSIVE AUDIT REPORT
353
+ ================================================================================
354
+
355
+ Generated: {datetime.now().isoformat()}
356
+
357
+ --------------------------------------------------------------------------------
358
+ TOOLS SUMMARY
359
+ --------------------------------------------------------------------------------
360
+
361
+ Total Tools Tested: {total_tools}
362
+ Passed: {passed}
363
+ Failed: {failed}
364
+ Pass Rate: {passed/total_tools*100:.1f}%
365
+
366
+ --------------------------------------------------------------------------------
367
+ RESPONSE TIME STATISTICS
368
+ --------------------------------------------------------------------------------
369
+
370
+ Average Response Time: {avg_response_time:.4f}s
371
+ Minimum Response Time: {min_response_time:.4f}s
372
+ Maximum Response Time: {max_response_time:.4f}s
373
+
374
+ --------------------------------------------------------------------------------
375
+ DETAILED RESULTS
376
+ --------------------------------------------------------------------------------
377
+ """
378
+
379
+ # Sort results by tool name
380
+ sorted_results = sorted(results, key=lambda x: x.tool_name)
381
+
382
+ for result in sorted_results:
383
+ status = "PASS" if result.execution_success else "FAIL"
384
+ report += f"""
385
+
386
+ Tool: {result.tool_name}
387
+ Status: {status}
388
+ Load Success: {result.load_success}
389
+ Response Time: {result.response_time:.4f}s
390
+ """
391
+ if result.error:
392
+ error_lines = result.error.split('\n')
393
+ report += f" Error: {error_lines[0]}\n"
394
+
395
+ # Skills section
396
+ report += f"""
397
+
398
+ --------------------------------------------------------------------------------
399
+ SKILLS SUMMARY
400
+ --------------------------------------------------------------------------------
401
+
402
+ Skills File: {skills_info.get('skills_file', 'N/A')}
403
+ Skills File Exists: {skills_info.get('skills_file_exists', False)}
404
+
405
+ Skill Directories:
406
+ """
407
+ for i, (dir_exists, dir_path) in enumerate(zip(skills_info.get('skill_dirs_exist', []), skills_info.get('skill_dirs', []))):
408
+ status = "EXISTS" if dir_exists else "MISSING"
409
+ report += f" [{status}] {dir_path}\n"
410
+
411
+ discovered = skills_info.get('discovered_skills', [])
412
+ report += f"""
413
+ Discovered Skills: {len(discovered)}
414
+ """
415
+ for skill in discovered:
416
+ report += f" - {skill['name']}: {skill.get('description', 'N/A')[:50]}\n"
417
+
418
+ if skills_info.get('error'):
419
+ report += f"""
420
+ Skills Error: {skills_info['error']}
421
+ """
422
+
423
+ # Final summary
424
+ report += f"""
425
+
426
+ ================================================================================
427
+ END OF AUDIT REPORT
428
+ ================================================================================
429
+ """
430
+
431
+ return report
432
+
433
+
434
+ async def main():
435
+ """Main audit function."""
436
+ print("\nStarting Stack 2.9 Comprehensive Audit...")
437
+ print(f"Working directory: /Users/walidsobhi/stack-2.9")
438
+
439
+ # Audit all tools
440
+ results = await audit_tools()
441
+
442
+ # Check skills
443
+ skills_info = check_skills()
444
+
445
+ # Generate and print report
446
+ report = generate_report(results, skills_info)
447
+ print(report)
448
+
449
+ # Save report to file
450
+ report_path = "/Users/walidsobhi/stack-2.9/audit_report.txt"
451
+ with open(report_path, 'w') as f:
452
+ f.write(report)
453
+ print(f"\nReport saved to: {report_path}")
454
+
455
+ # Save JSON results
456
+ json_results = {
457
+ "timestamp": datetime.now().isoformat(),
458
+ "total_tools": len(results),
459
+ "passed": sum(1 for r in results if r.execution_success),
460
+ "failed": sum(1 for r in results if not r.execution_success),
461
+ "tools": [r.to_dict() for r in results],
462
+ "skills": skills_info,
463
+ }
464
+ json_path = "/Users/walidsobhi/stack-2.9/audit_results.json"
465
+ with open(json_path, 'w') as f:
466
+ json.dump(json_results, f, indent=2)
467
+ print(f"JSON results saved to: {json_path}")
468
+
469
+ # Return exit code based on failures
470
+ failed_count = sum(1 for r in results if not r.execution_success)
471
+ return failed_count
472
+
473
+
474
+ if __name__ == "__main__":
475
+ failed = asyncio.run(main())
476
+ sys.exit(0 if failed == 0 else 1)
audit_tools_async.py ADDED
@@ -0,0 +1,203 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Async Tool Audit for Stack 2.9 - Tests all tools properly with async execution"""
3
+
4
+ import sys
5
+ import asyncio
6
+ import time
7
+ from datetime import datetime
8
+
9
+ sys.path.insert(0, '/Users/walidsobhi/stack-2.9/src')
10
+
11
+ # Import tools to trigger registration
12
+ import tools
13
+
14
+
15
+ def print_header(title):
16
+ print("\n" + "=" * 60)
17
+ print(f" {title}")
18
+ print("=" * 60)
19
+
20
+
21
+ async def test_tool(tool, name, test_input):
22
+ """Test a single tool with async execution"""
23
+ start_time = time.time()
24
+
25
+ try:
26
+ if asyncio.iscoroutinefunction(tool.execute):
27
+ result = await tool.execute(**test_input)
28
+ else:
29
+ result = tool.execute(**test_input)
30
+
31
+ duration = time.time() - start_time
32
+
33
+ # Check result
34
+ if hasattr(result, 'success'):
35
+ if result.success:
36
+ return {
37
+ "status": "PASS",
38
+ "duration": duration,
39
+ "data": result.data if result.data else "OK"
40
+ }
41
+ else:
42
+ return {
43
+ "status": "FAIL",
44
+ "duration": duration,
45
+ "error": result.error
46
+ }
47
+ else:
48
+ return {
49
+ "status": "PASS",
50
+ "duration": duration,
51
+ "data": str(result)
52
+ }
53
+
54
+ except Exception as e:
55
+ duration = time.time() - start_time
56
+ return {
57
+ "status": "FAIL",
58
+ "duration": duration,
59
+ "error": str(e)
60
+ }
61
+
62
+
63
+ async def audit_all_tools():
64
+ """Run async audit on all tools"""
65
+
66
+ print_header("STACK 2.9 ASYNC TOOL AUDIT")
67
+ print(f"Started: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
68
+
69
+ from tools import tool_registry
70
+
71
+ all_tools = tool_registry.list()
72
+ print(f"\nFound {len(all_tools)} registered tools")
73
+
74
+ # Define test cases for each tool
75
+ test_cases = {
76
+ "file_read": {"path": "/Users/walidsobhi/stack-2.9/README.md"},
77
+ "file_exists": {"path": "/Users/walidsobhi/stack-2.9/README.md"},
78
+ "file_write": {"path": "/tmp/test_tool_audit.txt", "content": "test content"},
79
+ "file_delete": {"path": "/tmp/test_tool_audit.txt"},
80
+ "glob": {"pattern": "*.py", "path": "/Users/walidsobhi/stack-2.9/src"},
81
+ "grep": {"pattern": "def ", "path": "/Users/walidsobhi/stack-2.9/src/tools"},
82
+ "grep_count": {"pattern": "def ", "path": "/Users/walidsobhi/stack-2.9/src/tools"},
83
+ "WebSearch": {"query": "python async", "num_results": 3},
84
+ "web_fetch": {"url": "https://example.com"},
85
+ "tool_search": {"query": "file"},
86
+ "tool_list_all": {},
87
+ "tool_info": {"name": "file_read"},
88
+ "tool_capabilities": {},
89
+ "TaskCreate": {"subject": "Test Task", "description": "Test description"},
90
+ "TaskList": {},
91
+ "TaskUpdate": {"taskId": "1", "status": "completed"},
92
+ "TaskDelete": {"taskId": "1"},
93
+ "TodoWrite": {"subject": "Test Todo"},
94
+ "team_create": {"name": "test-team"},
95
+ "team_list": {},
96
+ "team_status": {"team_id": "test-team"},
97
+ "team_assign": {"team_id": "test-team", "user_id": "test-user"},
98
+ "team_delete": {"team_id": "test-team"},
99
+ "team_leave": {"team_id": "test-team"},
100
+ "team_disband": {"team_id": "test-team"},
101
+ "skill_list": {},
102
+ "skill_search": {"query": "code"},
103
+ "skill_info": {"name": "python"},
104
+ "skill_execute": {"name": "python", "args": "print('hello')"},
105
+ "skill_chain": {"skills": ["python"]},
106
+ "brief": {"content": "This is a test content for brief analysis."},
107
+ "brief_summary": {"content": "This is a test content."},
108
+ "sleep": {"seconds": 0.1},
109
+ "wait_for": {"condition": "true", "timeout": 1},
110
+ "synthetic_output": {"template": "Test output: {value}", "values": {"value": "hello"}},
111
+ "structure_data": {"data": {"name": "test"}, "format": "json"},
112
+ "agent_spawn": {"name": "test-agent", "capabilities": ["code"]},
113
+ "agent_list": {},
114
+ "agent_status": {"name": "test-agent"},
115
+ "ask_question": {"question": "Test question?"},
116
+ "get_pending_questions": {},
117
+ "answer_question": {"question_id": "1", "answer": "Test answer"},
118
+ "message_send": {"channel": "test", "content": "Test message"},
119
+ "message_list": {"channel": "test"},
120
+ "message_channel": {"action": "create", "name": "test-channel"},
121
+ "message_template": {"name": "test", "variables": {}},
122
+ "CronCreate": {"expression": "* * * * *", "command": "echo test"},
123
+ "CronList": {},
124
+ "CronDelete": {"id": "test-cron"},
125
+ "mcp_list_servers": {},
126
+ "mcp_add_server": {"name": "test", "command": "echo test"},
127
+ "mcp_call": {"server": "test", "tool_name": "test", "args": {}},
128
+ "read_mcp_resource": {"resource_uri": "test://resource"},
129
+ "remote_add": {"name": "test", "url": "https://example.com"},
130
+ "remote_list": {},
131
+ "remote_remove": {"name": "test"},
132
+ "remote_trigger": {"name": "test", "action": "test"},
133
+ "EnterPlanMode": {},
134
+ "ExitPlanMode": {},
135
+ "enter_worktree": {"path": "/tmp/test-worktree"},
136
+ "exit_worktree": {},
137
+ "list_worktrees": {},
138
+ "Config": {"operation": "get", "key": "test"},
139
+ }
140
+
141
+ results = {}
142
+ passed = 0
143
+ failed = 0
144
+
145
+ print("\n" + "-" * 60)
146
+ print("Testing tools...")
147
+ print("-" * 60)
148
+
149
+ for name in all_tools:
150
+ tool = tool_registry.get(name)
151
+ if not tool:
152
+ results[name] = {"status": "FAIL", "error": "Tool not found"}
153
+ failed += 1
154
+ continue
155
+
156
+ # Get test input or empty dict
157
+ test_input = test_cases.get(name, {})
158
+
159
+ # Skip tools without test cases
160
+ if not test_input:
161
+ results[name] = {"status": "SKIP", "error": "No test case"}
162
+ continue
163
+
164
+ result = await test_tool(tool, name, test_input)
165
+ results[name] = result
166
+
167
+ status = result["status"]
168
+ if status == "PASS":
169
+ passed += 1
170
+ print(f"✓ {name}: PASS ({result['duration']:.3f}s)")
171
+ elif status == "SKIP":
172
+ print(f"○ {name}: SKIP")
173
+ passed += 1 # Count skipped as OK
174
+ else:
175
+ failed += 1
176
+ print(f"✗ {name}: FAIL ({result['duration']:.3f}s)")
177
+ print(f" Error: {result.get('error', 'Unknown')}")
178
+
179
+ # Summary
180
+ print_header("AUDIT SUMMARY")
181
+
182
+ total = len(all_tools)
183
+ print(f"""
184
+ Total Tools: {total}
185
+ Passed: {passed}
186
+ Failed: {failed}
187
+ Success Rate: {(passed/total)*100:.1f}%
188
+ """)
189
+
190
+ # Show failures
191
+ if failed > 0:
192
+ print_header("FAILURES")
193
+ for name, result in results.items():
194
+ if result["status"] == "FAIL":
195
+ print(f" • {name}: {result.get('error', 'Unknown error')}")
196
+
197
+ print(f"\nCompleted: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
198
+
199
+ return results
200
+
201
+
202
+ if __name__ == "__main__":
203
+ asyncio.run(audit_all_tools())
run_mcp_server.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Run the Stack 2.9 MCP Server"""
3
+
4
+ import sys
5
+ import os
6
+
7
+ # Ensure src/ is on the path
8
+ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
9
+
10
+ from src.mcp_server import main
11
+
12
+ if __name__ == "__main__":
13
+ main()
src/mcp_server.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """MCP Server for Stack 2.9 - Exposes Stack tools via Model Context Protocol"""
2
+
3
+ import asyncio
4
+ from typing import Any
5
+
6
+ from mcp.server.fastmcp import FastMCP
7
+
8
+ # Import all Stack 2.9 tools (triggers auto-registration)
9
+ from src.tools import (
10
+ BaseTool,
11
+ ToolResult,
12
+ get_registry,
13
+ file_read,
14
+ grep_tool,
15
+ task_management,
16
+ team_tool,
17
+ agent_tool,
18
+ )
19
+
20
+
21
+ def _tool_to_mcp(tool: BaseTool) -> dict[str, Any]:
22
+ """Convert a Stack 2.9 tool to MCP tool schema."""
23
+ schema = tool.input_schema
24
+ if callable(schema):
25
+ schema = schema()
26
+ return {
27
+ "name": tool.name,
28
+ "description": tool.description,
29
+ "inputSchema": schema,
30
+ }
31
+
32
+
33
+ def _call_tool_sync(tool: BaseTool, arguments: dict[str, Any]) -> Any:
34
+ """Call a tool and extract result data, handling sync/async execute."""
35
+ import inspect
36
+
37
+ execute = tool.execute
38
+
39
+ # Determine if execute is async or sync
40
+ if inspect.iscoroutinefunction(execute):
41
+ # Run in event loop
42
+ loop = asyncio.get_event_loop()
43
+ if inspect.iscoroutinefunction(execute):
44
+ result = loop.run_until_complete(execute(**arguments))
45
+ else:
46
+ result = execute(**arguments)
47
+ else:
48
+ # Sync execute (uses input_data dict style)
49
+ if hasattr(tool, 'input_schema') and not callable(tool.input_schema):
50
+ result = execute(arguments)
51
+ else:
52
+ result = execute(**arguments)
53
+
54
+ if isinstance(result, ToolResult):
55
+ if result.success:
56
+ return {"success": True, "data": result.data}
57
+ else:
58
+ return {"success": False, "error": result.error}
59
+ return result
60
+
61
+
62
+ def _register_tool(mcp: FastMCP, tool: BaseTool) -> None:
63
+ """Register a single Stack tool as an MCP tool."""
64
+ tool_name = tool.name
65
+ schema = tool.input_schema
66
+ if callable(schema):
67
+ schema = schema()
68
+
69
+ async def handler(arguments: dict[str, Any]) -> dict[str, Any]:
70
+ return _call_tool_sync(tool, arguments)
71
+
72
+ mcp.add_tool(tool_name, tool.description, handler, schema)
73
+
74
+
75
+ def _register_all_tools(mcp: FastMCP) -> int:
76
+ """Register all tools from the Stack 2.9 registry."""
77
+ registry = get_registry()
78
+ count = 0
79
+ for tool in registry._tools.values():
80
+ try:
81
+ _register_tool(mcp, tool)
82
+ count += 1
83
+ except Exception as e:
84
+ print(f"Failed to register tool {getattr(tool, 'name', 'unknown')}: {e}")
85
+ return count
86
+
87
+
88
+ # Create the FastMCP server
89
+ mcp = FastMCP("stack29")
90
+
91
+
92
+ def main():
93
+ """Main entry point - register tools and run the server."""
94
+ # Import all tools to ensure registration
95
+ from src.tools import (
96
+ agent_tool,
97
+ ask_question,
98
+ brief_tool,
99
+ config_tool,
100
+ file_edit,
101
+ file_read,
102
+ file_write,
103
+ glob_tool,
104
+ grep_tool,
105
+ messaging,
106
+ plan_mode,
107
+ remote_trigger,
108
+ scheduling,
109
+ skill_tool,
110
+ sleep_tool,
111
+ synthetic_output,
112
+ task_get,
113
+ task_management,
114
+ team_delete,
115
+ team_tool,
116
+ todo_tool,
117
+ tool_discovery,
118
+ web_fetch,
119
+ web_search,
120
+ worktree_tool,
121
+ )
122
+
123
+ # Register all tools from registry
124
+ count = _register_all_tools(mcp)
125
+ print(f"Registered {count} Stack 2.9 tools as MCP tools")
126
+
127
+ # Run the MCP server
128
+ mcp.run()
129
+
130
+
131
+ if __name__ == "__main__":
132
+ main()
src/tools/__init__.py CHANGED
@@ -1,6 +1,44 @@
1
- """Stack 2.9 Tools - RTMP-compatible tool implementations in Python."""
 
 
 
2
 
3
  from .base import BaseTool, ToolResult, ToolParam
4
- from .registry import ToolRegistry, get_registry
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- __all__ = ["BaseTool", "ToolResult", "ToolParam", "ToolRegistry", "get_registry"]
 
 
 
 
 
 
 
 
1
+ """Stack 2.9 Tools - Auto-import all tools for registration"""
2
+
3
+ # Import all tools to trigger auto-registration
4
+ # The registration happens at module level in each tool file
5
 
6
  from .base import BaseTool, ToolResult, ToolParam
7
+ from .registry import ToolRegistry, get_registry, tool_registry
8
+
9
+ # Import all tool modules (triggers registration)
10
+ from . import agent_tool
11
+ from . import ask_question
12
+ from . import brief_tool
13
+ from . import config_tool
14
+ from . import file_edit
15
+ from . import file_read
16
+ from . import file_write
17
+ from . import glob_tool
18
+ from . import grep_tool
19
+ from . import mcp_tool
20
+ from . import messaging
21
+ from . import plan_mode
22
+ from . import remote_trigger
23
+ from . import scheduling
24
+ from . import skill_tool
25
+ from . import sleep_tool
26
+ from . import synthetic_output
27
+ from . import task_get
28
+ from . import task_management
29
+ from . import team_delete
30
+ from . import team_tool
31
+ from . import todo_tool
32
+ from . import tool_discovery
33
+ from . import web_fetch
34
+ from . import web_search
35
+ from . import worktree_tool
36
 
37
+ __all__ = [
38
+ "BaseTool",
39
+ "ToolResult",
40
+ "ToolParam",
41
+ "ToolRegistry",
42
+ "get_registry",
43
+ "tool_registry",
44
+ ]