bamboo-tools 2026.5.1

Tool execution and integrations for the Bamboo agent framework
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
//! Built-in tool guides for the Claude-style tool surface.

use std::sync::Arc;

use serde_json::json;

use super::{ToolCategory, ToolExample, ToolGuide, ToolGuideSpec};

pub const BUILTIN_GUIDE_NAMES: [&str; 21] = [
    "conclusion_with_options",
    "Bash",
    "BashOutput",
    "Edit",
    "EnterPlanMode",
    "ExitPlanMode",
    "GetFileInfo",
    "Glob",
    "Grep",
    "js_repl",
    "KillShell",
    "session_note",
    "NotebookEdit",
    "Read",
    "request_permissions",
    "Sleep",
    "Task",
    "WebFetch",
    "WebSearch",
    "Workspace",
    "Write",
];

pub fn builtin_tool_guide(tool_name: &str) -> Option<Arc<dyn ToolGuide>> {
    builtin_guide_spec(tool_name).map(|guide| Arc::new(guide) as Arc<dyn ToolGuide>)
}

pub fn builtin_guide_spec(tool_name: &str) -> Option<ToolGuideSpec> {
    match tool_name {
        // apply_patch is now an alias for Edit; return the Edit guide with
        // the alias name so existing callers still see relevant documentation.
        "apply_patch" => Some(guide(
            "apply_patch",
            ToolCategory::FileWriting,
            "Use for patch-only in-place updates using SEARCH/REPLACE blocks, aligned with Edit patch mode.",
            "Do not use before Read on existing files; do not use for full-file rewrites; do not mix with old_string/new_string style arguments.",
            &["Read", "Edit", "Write"],
            vec![example(
                "Apply a patch block",
                json!({"file_path":"/workspace/project/src/main.rs","patch":"<<<<<<< SEARCH\nlet v = 1;\n=======\nlet v = 2;\n>>>>>>> REPLACE"}),
                "Use when you want a dedicated patch tool call instead of Edit.",
            )],
        )),
        "conclusion_with_options" => Some(guide(
            "conclusion_with_options",
            ToolCategory::UserInteraction,
            "Ask the user for confirmation or missing input with selectable options. Use this as the final interaction step when wrapping up a task turn, handing off execution, or asking the user to choose next steps.",
            "Do not use repeatedly for routine status updates during active execution; reserve it for true clarification points, explicit user decisions, or the required final confirmation flow.",
            &["ExitPlanMode"],
            vec![
                example(
                    "Confirm before finishing",
                    json!({
                        "question":"Any other requests before I finish?",
                        "conclusion":{
                            "title":"Conclusion",
                            "summary":"Core validation is complete and release is ready.",
                            "key_points":["All targeted tests passed","No blocking regressions"],
                            "next_steps":["Proceed with release train"],
                            "confidence":"high",
                            "mermaid":{"graph":"graph TD\nA[Validation]-->B[Ready to release]"}
                        }
                    }),
                    "Use when user intent is required before finalizing. Defaults to options [\"OK\", \"Need changes\"] when options are omitted.",
                ),
                example(
                    "Ask the user to choose the next step",
                    json!({
                        "question":"Which next step should I take?",
                        "options":["Implement it","Refine the plan","Stop here","OK"],
                        "conclusion":{
                            "summary":"I finished the review and identified two viable implementation paths.",
                            "key_points":["Minimal change path is lower risk","Broader refactor will simplify future maintenance"],
                            "mermaid":{"graph":"graph TD\nA[Review done]-->B[Choose next step]"}
                        }
                    }),
                    "Use when the user must choose among concrete next actions instead of receiving plain prose.",
                ),
                example(
                    "Wrap up a review turn",
                    json!({
                        "question":"Want me to proceed with the recommended fix?",
                        "options":["Proceed","Need changes","OK"],
                        "conclusion":{
                            "summary":"The review is complete and I found one blocking issue plus two minor cleanups.",
                            "key_points":["Blocking issue identified","Fix scope is localized"],
                            "mermaid":{"graph":"graph TD\nA[Review complete]-->B[Decision needed]"}
                        }
                    }),
                    "Use at the end of review/explanation turns instead of a plain final paragraph.",
                ),
            ],
        )),
        "Read" => Some(guide(
            "Read",
            ToolCategory::FileReading,
            "Use for reading file contents, especially before Edit/Write on existing files.",
            "Do not use for writing or cross-project search; do not substitute Bash cat/head/tail for this.",
            &["Edit", "Write", "Grep"],
            vec![example(
                "Read a config file",
                json!({"file_path":"/workspace/project/config.toml"}),
                "Use before making any edits.",
            )],
        )),
        "Write" => Some(guide(
            "Write",
            ToolCategory::FileWriting,
            "Use for creating files or replacing full file contents.",
            "Do not use for small in-place updates; prefer Edit after Read.",
            &["Read", "Edit"],
            vec![example(
                "Write a full file",
                json!({"file_path":"/workspace/project/.env.example","content":"API_KEY=\n"}),
                "Suitable for full replacements.",
            )],
        )),
        "Edit" => Some(guide(
            "Edit",
            ToolCategory::FileWriting,
            "Use for precise in-place updates in existing files. Prefer patch mode with SEARCH/REPLACE blocks and enough context to target unique locations.",
            "Do not use before Read on existing files; do not use for full-file rewrites; do not use ambiguous SEARCH blocks that match multiple locations.",
            &["Read", "Write"],
            vec![
                example(
                    "Patch a specific block",
                    json!({"file_path":"/workspace/project/src/main.rs","patch":"<<<<<<< SEARCH\nfn b() {\n    let v = 1;\n}\n=======\nfn b() {\n    let v = 2;\n}\n>>>>>>> REPLACE"}),
                    "Preferred for repeated code because context disambiguates the target.",
                ),
                example(
                    "Legacy single replacement",
                    json!({"file_path":"/workspace/project/src/main.rs","old_string":"foo","new_string":"bar"}),
                    "Use only when old_string is known to match exactly once.",
                ),
                example(
                    "Legacy replacement with line hint",
                    json!({"file_path":"/workspace/project/src/main.rs","old_string":"value = 1","new_string":"value = 2","line_number":42}),
                    "Use line_number when old_string may appear in multiple places.",
                ),
            ],
        )),
        "Glob" => Some(guide(
            "Glob",
            ToolCategory::CodeSearch,
            "Find files by glob pattern before deeper content search.",
            "Do not use for content search; avoid unbounded root patterns like **/* without narrowing path/pattern.",
            &["Grep", "Read"],
            vec![example(
                "Find all Rust files",
                json!({"pattern":"**/*.rs","path":"/workspace/project"}),
                "Use before Grep when scope is unknown.",
            )],
        )),
        "Grep" => Some(guide(
            "Grep",
            ToolCategory::CodeSearch,
            "Search project contents using regex; start with files_with_matches then narrow with Read/content mode.",
            "Do not run broad content or multiline searches across the full workspace; always narrow with path/glob/type first.",
            &["Glob", "Read"],
            vec![example(
                "Find function usages",
                json!({"pattern":"execute_with_context","glob":"**/*.rs","output_mode":"files_with_matches","head_limit":200}),
                "First identify candidate files, then use Read or scoped content mode.",
            )],
        )),
        "Bash" => Some(guide(
            "Bash",
            ToolCategory::CommandExecution,
            "Run terminal commands (build/test/git/npm/docker/gh), optionally in background.",
            "Do not use for file reads/edits/search when Read/Edit/Write/Glob/Grep can handle it; do not use shell echo/printf to communicate with the user.",
            &["BashOutput", "KillShell"],
            vec![example(
                "Run tests",
                json!({"command":"cargo test","timeout":120000}),
                "Use for build/test/CLI operations.",
            )],
        )),
        "BashOutput" => Some(guide(
            "BashOutput",
            ToolCategory::CommandExecution,
            "Read incremental output from a background shell.",
            "Do not use without a bash_id from Bash.",
            &["Bash", "KillShell"],
            vec![example(
                "Poll output",
                json!({"bash_id":"abc"}),
                "Use repeatedly until shell completes.",
            )],
        )),
        "KillShell" => Some(guide(
            "KillShell",
            ToolCategory::CommandExecution,
            "Terminate a background shell.",
            "Do not use for foreground commands; use the ID returned by Bash(run_in_background=true), not chat session_id.",
            &["Bash", "BashOutput"],
            vec![example(
                "Stop runaway process",
                json!({"shell_id":"<bash_id-from-Bash>"}),
                "Use when process should no longer run.",
            )],
        )),
        "NotebookEdit" => Some(guide(
            "NotebookEdit",
            ToolCategory::FileWriting,
            "Edit notebook cells by replace/insert/delete.",
            "Do not use for non-notebook files.",
            &["Read", "Write"],
            vec![example(
                "Replace first cell",
                json!({"notebook_path":"/workspace/project/demo.ipynb","new_source":"print('ok')"}),
                "Use with absolute notebook path.",
            )],
        )),
        "SlashCommand" => Some(guide(
            "SlashCommand",
            ToolCategory::UserInteraction,
            "Resolve and execute a slash command template.",
            "Do not use for arbitrary shell execution.",
            &["Bash", "Read"],
            vec![example(
                "Run review command",
                json!({"command":"/review"}),
                "Useful for reusable prompt workflows.",
            )],
        )),
        "Task" => Some(guide(
            "Task",
            ToolCategory::TaskManagement,
            "Create or update the shared task list for the current root session tree (root + child sessions share the same task list).",
            "Do not use for trivial one-step requests.",
            &["ExitPlanMode"],
            vec![example(
                "Update shared task statuses",
                json!({"tasks":[{"content":"Run tests","status":"in_progress","activeForm":"Running tests"}]}),
                "Keep exactly one item in_progress whenever possible.",
            )],
        )),
        "EnterPlanMode" => Some(guide(
            "EnterPlanMode",
            ToolCategory::UserInteraction,
            "Switch to plan mode for complex tasks requiring exploration and design before implementation.",
            "Do not use for simple tasks that can be implemented directly.",
            &["Task", "ExitPlanMode"],
            vec![example(
                "Start planning a complex refactor",
                json!({"reason":"This refactor touches multiple crates and needs careful design"}),
                "Use when facing a complex task that requires exploration before implementation.",
            )],
        )),
        "ExitPlanMode" => Some(guide(
            "ExitPlanMode",
            ToolCategory::UserInteraction,
            "Ask for confirmation before leaving plan mode.",
            "Do not use if implementation can proceed directly.",
            &["Task"],
            vec![example(
                "Plan complete",
                json!({"plan":"1. Do A\n2. Do B"}),
                "Use after producing a concrete implementation plan.",
            )],
        )),
        // FileExists guide moved next to GetFileInfo (see below).
        "WebFetch" => Some(guide(
            "WebFetch",
            ToolCategory::CommandExecution,
            "Fetch a webpage by URL when you need cleaned page text from a known target.",
            "Do not use for broad discovery queries.",
            &["WebSearch"],
            vec![example(
                "Fetch a target page",
                json!({"url":"https://target-host/path","prompt":"Extract setup steps"}),
                "The prompt field is context for downstream handling; WebFetch itself returns cleaned text + metadata.",
            )],
        )),
        "WebSearch" => Some(guide(
            "WebSearch",
            ToolCategory::CommandExecution,
            "Search the web with optional domain allow/block filters. When searching for recent information, documentation, or current events, include the current year in the query to get up-to-date results.",
            "Do not use for local codebase search. Do not specify both allowed_domains and blocked_domains in the same request.",
            &["WebFetch", "Grep"],
            vec![
                example(
                    "Search official docs",
                    json!({"query":"rust async trait object", "allowed_domains":["doc.rust-lang.org"]}),
                    "Use before WebFetch when URL is unknown.",
                ),
                example(
                    "Search recent documentation",
                    json!({"query":"React documentation 2026", "max_results": 5}),
                    "Include the current year for recent docs or current events.",
                ),
            ],
        )),
        // GetCurrentDir is now an alias for Workspace (get mode)
        "GetCurrentDir" => Some(guide(
            "GetCurrentDir",
            ToolCategory::CommandExecution,
            "Retrieve the session's current workspace directory. (Alias for Workspace with no path argument.)",
            "Do not use when absolute paths are already known.",
            &["Workspace", "Bash", "Read"],
            vec![example(
                "Inspect working directory",
                json!({}),
                "Useful before commands or file ops that rely on relative paths.",
            )],
        )),
        "GetFileInfo" => Some(guide(
            "GetFileInfo",
            ToolCategory::FileReading,
            "Check if a file/dir exists and read metadata (type, size, modified time) without loading content. Returns {exists: false} for missing paths, so this also covers existence checks.",
            "Do not use when you need actual content; use Read instead.",
            &["Read"],
            vec![example(
                "Check metadata before processing",
                json!({"path":"/workspace/project/logs/app.log"}),
                "Use to branch behavior based on file type/size.",
            )],
        )),
        // FileExists is now an alias for GetFileInfo
        "FileExists" => Some(guide(
            "FileExists",
            ToolCategory::FileReading,
            "Check quickly whether a path exists before reading, editing, or writing conditionally. (Alias for GetFileInfo)",
            "Do not use to inspect file content or metadata details.",
            &["GetFileInfo", "Read", "Write"],
            vec![example(
                "Guard before write",
                json!({"path":"/workspace/project/.env"}),
                "Use as a fast existence probe before deciding create vs update.",
            )],
        )),
        "session_note" | "memory_note" => Some(guide(
            if tool_name == "memory_note" {
                "memory_note"
            } else {
                "session_note"
            },
            ToolCategory::TaskManagement,
            "Store durable session-scoped notes and retrieve them across turns. Use it for local context, user preferences, constraints, and compression-resistant reminders within the current workstream.",
            "Do not store secrets/tokens, one-turn scratch text, or use it as the primary long-term knowledge base.",
            &["Task", "session_history"],
            vec![
                example(
                    "Persist a durable session constraint",
                    json!({"action":"append","content":"User prefers pnpm and strict TypeScript."}),
                    "Use append for stable session/workstream facts; use replace to compress long notes.",
                ),
                example(
                    "Store notes for a specific topic",
                    json!({"action":"append","topic":"backend-api","content":"REST endpoints finalized: /users, /orders."}),
                    "Use topic to keep separate workstreams isolated from each other.",
                ),
            ],
        )),
        "memory" => Some(guide(
            "memory",
            ToolCategory::TaskManagement,
            "Manage Bamboo's unified memory system. Use session_* actions only for current-session continuity notes, and use query/get/write/merge/purge/inspect/rebuild for durable project or global memories backed by canonical topic files.",
            "Do not use session actions for long-term project knowledge, and do not dump large bodies through query when query -> get(id) or inspect is more appropriate. Prefer query first, then get the specific durable item you need before writing or merging.",
            &["session_note", "session_history", "Task"],
            vec![
                example(
                    "Read the current session note topic",
                    json!({"action":"session_read","topic":"default","options":{"max_chars":4000}}),
                    "Use for continuity state inside the current session without touching durable project/global memory.",
                ),
                example(
                    "Shortlist durable memories before reading full content",
                    json!({"action":"query","scope":"project","query":"release freeze mobile","options":{"limit":5,"max_chars":3000}}),
                    "Prefer query first so the model gets a shortlist summary under budget; call get(id) only for the durable item that needs full context.",
                ),
                example(
                    "Read one durable memory in full after query",
                    json!({"action":"get","id":"mem_20260403_001","options":{"max_chars":5000}}),
                    "Use after query when you need the full body/frontmatter of a single durable memory item.",
                ),
                example(
                    "Write a durable project memory",
                    json!({"action":"write","scope":"project","type":"project","title":"Release freeze begins next week","content":"Merge freeze begins on Tuesday for the mobile release cut.","tags":["release","freeze"]}),
                    "Use when the fact should persist across sessions as canonical project memory and should not live only in session_note.",
                ),
                example(
                    "Merge follow-up details into an existing durable memory",
                    json!({"action":"merge","id":"mem_20260403_001","content":"Additional confirmation from a later session.","tags":["confirmed"],"source_memory_ids":["mem_20260403_002"]}),
                    "Use when new durable evidence belongs on an existing memory item and older overlapping items should be superseded.",
                ),
            ],
        )),

        // SetWorkspace is now an alias for Workspace (set mode)
        "SetWorkspace" => Some(guide(
            "SetWorkspace",
            ToolCategory::CommandExecution,
            "Change the current session workspace. (Alias for Workspace with path argument.)",
            "Do not use with non-directory or missing paths.",
            &["Workspace", "Bash", "Read"],
            vec![example(
                "Switch session workspace",
                json!({"path":"/workspace/project"}),
                "Use before running commands or edits in another repo root.",
            )],
        )),
        // Workspace is the unified tool replacing GetCurrentDir + SetWorkspace
        "Workspace" => Some(guide(
            "Workspace",
            ToolCategory::CommandExecution,
            "Get or set the current session workspace directory. Call without 'path' to read the current workspace; call with 'path' to change it.",
            "Do not set workspace to a non-directory or missing path.",
            &["Bash", "Read"],
            vec![
                example(
                    "Get current workspace",
                    json!({}),
                    "Useful before commands or file ops that rely on relative paths.",
                ),
                example(
                    "Set workspace",
                    json!({"path":"/workspace/project"}),
                    "Use before running commands or edits in another repo root.",
                ),
            ],
        )),
        "js_repl" => Some(guide(
            "js_repl",
            ToolCategory::CommandExecution,
            "Execute JavaScript code using Node.js with top-level await support. Each invocation runs in a fresh process.",
            "Do not use for tasks better handled by Bash. Requires Node.js installed on the host.",
            &["Bash"],
            vec![example(
                "Evaluate a JS expression",
                json!({"code":"console.log(2 + 2)"}),
                "Use for quick calculations, JSON manipulation, or any JS-specific task.",
            )],
        )),
        "request_permissions" => Some(guide(
            "request_permissions",
            ToolCategory::UserInteraction,
            "Request elevated permissions from the user when a needed operation would be blocked. The agent loop pauses until the user approves or denies.",
            "Do not use pre-emptively for every operation; only request when you know the current permission set is insufficient.",
            &["conclusion_with_options"],
            vec![example(
                "Request file write permission",
                json!({"reason":"Need to write deployment config to /etc/nginx","permissions":[{"type":"write_file","resource":"/etc/nginx/conf.d/*"}]}),
                "Use when a Write/Edit would be denied due to path restrictions.",
            )],
        )),
        "Sleep" => Some(guide(
            "Sleep",
            ToolCategory::CommandExecution,
            "Pause briefly when waiting for an external state change before polling again.",
            "Do not use for normal reasoning pauses or long waits when another tool can fetch status directly.",
            &["BashOutput", "WebFetch"],
            vec![example(
                "Wait before next poll",
                json!({"seconds":2,"reason":"wait for background process output"}),
                "Use short waits between repeated status checks.",
            )],
        )),
        "load_skill" => Some(guide(
            "load_skill",
            ToolCategory::TaskManagement,
            "Load a selected skill's SKILL.md instructions and metadata by skill_id.",
            "Do not use for auxiliary resource files; use read_skill_resource for references/assets.",
            &["read_skill_resource"],
            vec![example(
                "Load skill instructions",
                json!({"skill_id":"rust-best-practices"}),
                "Call this before following a matched skill's detailed workflow.",
            )],
        )),
        "read_skill_resource" => Some(guide(
            "read_skill_resource",
            ToolCategory::FileReading,
            "Read auxiliary files under a loaded skill directory with optional offset/limit paging.",
            "Do not use for SKILL.md itself; call load_skill for primary instructions.",
            &["load_skill"],
            vec![example(
                "Read a skill reference file",
                json!({"skill_id":"rust-best-practices","resource_path":"references/chapter_01.md","offset":0,"limit":80}),
                "Use when the loaded instructions point to additional files.",
            )],
        )),
        "session_history" | "recall" | "session_inspector" => Some(guide(
            match tool_name {
                "session_inspector" => "session_inspector",
                "recall" => "recall",
                _ => "session_history",
            },
            ToolCategory::FileReading,
            "Inspect prior Bamboo session history from local SQLite storage. Use list/get_meta before deep reads when possible, then read bounded message slices, compressed conversation cache, or search results to recover previous discussion context. Distinct from `memory` (durable cross-session knowledge).",
            "Do not use as a broad substitute for local code search, and do not delegate child-session inspection unless the user explicitly asks for delegated work.",
            &["session_note", "Read", "Task"],
            vec![
                example(
                    "Search prior discussion history",
                    json!({"action":"search","query":"release checklist","mode":"tail_messages","max_sessions":10,"tail_messages":6}),
                    "Use when you need to recover what was discussed previously before asking the user to repeat it.",
                ),
                example(
                    "Inspect a specific session with bounded reads",
                    json!({"action":"read_messages","session_id":"session-123","from_end":true,"limit":20,"include_system":false,"truncate_chars":200}),
                    "Prefer bounded slices rather than dumping an entire session at once.",
                ),
            ],
        )),
        "scheduler" | "schedule_tasks" => Some(guide(
            if tool_name == "schedule_tasks" {
                "schedule_tasks"
            } else {
                "scheduler"
            },
            ToolCategory::TaskManagement,
            "Manage Bamboo scheduled automation jobs for recurring or delayed work. Use it to create, inspect, modify, run, or delete schedules without going through HTTP.",
            "Do not use for normal one-shot task planning inside the current conversation; use Task for active execution tracking instead.",
            &["Task", "Workspace"],
            vec![
                example(
                    "Create a recurring schedule",
                    json!({"action":"create","name":"daily-review","trigger":{"type":"interval","every_seconds":86400},"enabled":true,"run_config":{"auto_execute":true,"task_message":"Review new tickets","workspace_path":"/workspace/project"}}),
                    "Use when work should recur automatically over time.",
                ),
                example(
                    "Inspect schedule sessions",
                    json!({"action":"list_sessions","schedule_id":"sch_123"}),
                    "Use to see the sessions a schedule created or ran.",
                ),
            ],
        )),
        "SubSession" => Some(guide(
            "SubSession",
            ToolCategory::TaskManagement,
            "Create, inspect, and manage child sessions for explicitly requested delegated, parallel, or sub-agent work. Use action=create to spawn a new child; use list/get to inspect existing children before creating duplicates; use update/run/send_message/cancel/delete to manage existing children.",
            "Do not use proactively for simple one-step tasks; do not spawn children from child sessions; do not create multiple overlapping children with unclear responsibilities.",
            &["Task"],
            vec![
                example(
                    "Create a read-only research child",
                    json!({"action":"create","title":"Inspect parser module","responsibility":"Find parser entrypoints and summarize data flow","prompt":"Read parser-related files and report key functions. Do not modify files.","subagent_type":"researcher"}),
                    "Use for isolated investigation that would otherwise fill the main context.",
                ),
                example(
                    "List existing children",
                    json!({"action":"list"}),
                    "Use before creating a new child when you might already have a relevant one.",
                ),
                example(
                    "Send follow-up to existing child",
                    json!({"action":"send_message","child_session_id":"child_123","message":"Focus on error handling paths and summarize risks.","auto_run":true}),
                    "Use follow-up instead of creating a duplicate child session.",
                ),
                example(
                    "Cancel a running child",
                    json!({"action":"cancel","child_session_id":"child_123"}),
                    "Use when a child session is no longer needed or has gone off track.",
                ),
            ],
        )),
        _ => None,
    }
}

pub fn builtin_guides() -> Vec<ToolGuideSpec> {
    BUILTIN_GUIDE_NAMES
        .iter()
        .filter_map(|name| builtin_guide_spec(name))
        .collect()
}

fn guide(
    tool_name: &str,
    category: ToolCategory,
    when_to_use: &str,
    when_not_to_use: &str,
    related_tools: &[&str],
    examples: Vec<ToolExample>,
) -> ToolGuideSpec {
    ToolGuideSpec {
        tool_name: tool_name.to_string(),
        when_to_use: when_to_use.to_string(),
        when_not_to_use: when_not_to_use.to_string(),
        examples,
        related_tools: related_tools.iter().map(|name| name.to_string()).collect(),
        category,
    }
}

fn example(scenario: &str, parameters: serde_json::Value, explanation: &str) -> ToolExample {
    ToolExample::new(scenario, parameters, explanation)
}

#[cfg(test)]
mod tests {
    use crate::BUILTIN_TOOL_NAMES;

    use super::{builtin_guide_spec, BUILTIN_GUIDE_NAMES};

    #[test]
    fn every_builtin_tool_has_a_guide() {
        for name in BUILTIN_GUIDE_NAMES {
            assert!(
                builtin_guide_spec(name).is_some(),
                "missing guide for {}",
                name
            );
        }
    }

    #[test]
    fn builtin_guides_cover_all_builtin_tool_names() {
        for name in BUILTIN_TOOL_NAMES {
            assert!(
                builtin_guide_spec(name).is_some(),
                "missing builtin guide coverage for {}",
                name
            );
        }
    }

    #[test]
    fn memory_server_tool_has_fallback_guide_spec() {
        let guide = builtin_guide_spec("memory").expect("memory guide should exist");
        assert_eq!(guide.tool_name, "memory");
        assert!(guide.examples.iter().any(|example| {
            example
                .parameters
                .get("action")
                .and_then(|value| value.as_str())
                == Some("merge")
        }));
    }
}