@taurgis/bonsai 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +239 -0
- package/agents/agents/official-docs-researcher.agent.md +98 -0
- package/agents/instructions/web-research.instructions.md +29 -0
- package/agents/skills/web-research/SKILL.md +126 -0
- package/bin/cli.mjs +22 -0
- package/dist/base-command.d.ts +14 -0
- package/dist/base-command.js +56 -0
- package/dist/commands/config/base.d.ts +23 -0
- package/dist/commands/config/base.js +46 -0
- package/dist/commands/config/get.d.ts +20 -0
- package/dist/commands/config/get.js +48 -0
- package/dist/commands/config/list.d.ts +17 -0
- package/dist/commands/config/list.js +48 -0
- package/dist/commands/config/set.d.ts +31 -0
- package/dist/commands/config/set.js +94 -0
- package/dist/commands/config/unset.d.ts +21 -0
- package/dist/commands/config/unset.js +65 -0
- package/dist/commands/fetch.d.ts +38 -0
- package/dist/commands/fetch.js +311 -0
- package/dist/commands/import.d.ts +38 -0
- package/dist/commands/import.js +313 -0
- package/dist/commands/index.d.ts +35 -0
- package/dist/commands/index.js +306 -0
- package/dist/commands/inspect.d.ts +13 -0
- package/dist/commands/inspect.js +82 -0
- package/dist/commands/list.d.ts +25 -0
- package/dist/commands/list.js +143 -0
- package/dist/commands/prune.d.ts +24 -0
- package/dist/commands/prune.js +141 -0
- package/dist/commands/research/config/base.d.ts +23 -0
- package/dist/commands/research/config/base.js +46 -0
- package/dist/commands/research/config/get.d.ts +20 -0
- package/dist/commands/research/config/get.js +45 -0
- package/dist/commands/research/config/list.d.ts +17 -0
- package/dist/commands/research/config/list.js +42 -0
- package/dist/commands/research/config/set.d.ts +31 -0
- package/dist/commands/research/config/set.js +94 -0
- package/dist/commands/research/config/unset.d.ts +21 -0
- package/dist/commands/research/config/unset.js +59 -0
- package/dist/commands/research/import.d.ts +38 -0
- package/dist/commands/research/import.js +313 -0
- package/dist/commands/research/inspect.d.ts +13 -0
- package/dist/commands/research/inspect.js +86 -0
- package/dist/commands/research/list.d.ts +25 -0
- package/dist/commands/research/list.js +143 -0
- package/dist/commands/research/prune.d.ts +24 -0
- package/dist/commands/research/prune.js +141 -0
- package/dist/commands/research/search.d.ts +32 -0
- package/dist/commands/research/search.js +334 -0
- package/dist/commands/research/status.d.ts +17 -0
- package/dist/commands/research/status.js +108 -0
- package/dist/commands/research.d.ts +36 -0
- package/dist/commands/research.js +304 -0
- package/dist/commands/search.d.ts +32 -0
- package/dist/commands/search.js +334 -0
- package/dist/commands/status.d.ts +17 -0
- package/dist/commands/status.js +95 -0
- package/dist/commands.d.ts +24 -0
- package/dist/commands.js +24 -0
- package/dist/lib/config/index.d.ts +18 -0
- package/dist/lib/config/index.js +33 -0
- package/dist/lib/config/io.d.ts +8 -0
- package/dist/lib/config/io.js +75 -0
- package/dist/lib/config/resolve.d.ts +15 -0
- package/dist/lib/config/resolve.js +20 -0
- package/dist/lib/config/schema.d.ts +28 -0
- package/dist/lib/config/schema.js +32 -0
- package/dist/lib/research/artifact.d.ts +21 -0
- package/dist/lib/research/artifact.js +217 -0
- package/dist/lib/research/browser.d.ts +74 -0
- package/dist/lib/research/browser.js +359 -0
- package/dist/lib/research/cache-key.d.ts +5 -0
- package/dist/lib/research/cache-key.js +10 -0
- package/dist/lib/research/capture.d.ts +29 -0
- package/dist/lib/research/capture.js +181 -0
- package/dist/lib/research/compress.d.ts +5 -0
- package/dist/lib/research/compress.js +17 -0
- package/dist/lib/research/docs/__fixtures__/sphinx-searchindex.d.ts +1 -0
- package/dist/lib/research/docs/__fixtures__/sphinx-searchindex.js +2 -0
- package/dist/lib/research/docs/capabilities.d.ts +43 -0
- package/dist/lib/research/docs/capabilities.js +9 -0
- package/dist/lib/research/docs/clean-dom.d.ts +4 -0
- package/dist/lib/research/docs/clean-dom.js +41 -0
- package/dist/lib/research/docs/code-blocks.d.ts +5 -0
- package/dist/lib/research/docs/code-blocks.js +69 -0
- package/dist/lib/research/docs/detect.d.ts +6 -0
- package/dist/lib/research/docs/detect.js +239 -0
- package/dist/lib/research/docs/machine-readable.d.ts +21 -0
- package/dist/lib/research/docs/machine-readable.js +73 -0
- package/dist/lib/research/docs/markdown-source.d.ts +17 -0
- package/dist/lib/research/docs/markdown-source.js +194 -0
- package/dist/lib/research/docs/quality-gates.d.ts +9 -0
- package/dist/lib/research/docs/quality-gates.js +65 -0
- package/dist/lib/research/docs/remote-search-runner.d.ts +21 -0
- package/dist/lib/research/docs/remote-search-runner.js +54 -0
- package/dist/lib/research/docs/remote-search.d.ts +14 -0
- package/dist/lib/research/docs/remote-search.js +101 -0
- package/dist/lib/research/docs/rsc.d.ts +10 -0
- package/dist/lib/research/docs/rsc.js +30 -0
- package/dist/lib/research/docs/search-index.d.ts +13 -0
- package/dist/lib/research/docs/search-index.js +176 -0
- package/dist/lib/research/docs/section-artifacts.d.ts +12 -0
- package/dist/lib/research/docs/section-artifacts.js +75 -0
- package/dist/lib/research/docs/sections.d.ts +12 -0
- package/dist/lib/research/docs/sections.js +67 -0
- package/dist/lib/research/docs/source-map.d.ts +19 -0
- package/dist/lib/research/docs/source-map.js +87 -0
- package/dist/lib/research/docs/validate.d.ts +15 -0
- package/dist/lib/research/docs/validate.js +63 -0
- package/dist/lib/research/extract.d.ts +11 -0
- package/dist/lib/research/extract.js +83 -0
- package/dist/lib/research/fetcher.d.ts +29 -0
- package/dist/lib/research/fetcher.js +192 -0
- package/dist/lib/research/freshness.d.ts +21 -0
- package/dist/lib/research/freshness.js +82 -0
- package/dist/lib/research/markdown.d.ts +6 -0
- package/dist/lib/research/markdown.js +66 -0
- package/dist/lib/research/regression.d.ts +23 -0
- package/dist/lib/research/regression.js +93 -0
- package/dist/lib/research/resolve-target.d.ts +18 -0
- package/dist/lib/research/resolve-target.js +20 -0
- package/dist/lib/research/revalidate.d.ts +32 -0
- package/dist/lib/research/revalidate.js +152 -0
- package/dist/lib/research/schema.d.ts +47 -0
- package/dist/lib/research/schema.js +1 -0
- package/dist/lib/research/secret-scan.d.ts +5 -0
- package/dist/lib/research/secret-scan.js +37 -0
- package/dist/lib/research/secure-write.d.ts +16 -0
- package/dist/lib/research/secure-write.js +17 -0
- package/dist/lib/research/storage.d.ts +52 -0
- package/dist/lib/research/storage.js +182 -0
- package/dist/lib/research/store-roots.d.ts +29 -0
- package/dist/lib/research/store-roots.js +28 -0
- package/dist/lib/research/token-estimate.d.ts +5 -0
- package/dist/lib/research/token-estimate.js +7 -0
- package/dist/lib/research/url.d.ts +24 -0
- package/dist/lib/research/url.js +121 -0
- package/dist/sites/index.d.ts +4 -0
- package/dist/sites/index.js +28 -0
- package/dist/sites/salesforce/browser-search.d.ts +7 -0
- package/dist/sites/salesforce/browser-search.js +82 -0
- package/dist/sites/salesforce/coveo.d.ts +23 -0
- package/dist/sites/salesforce/coveo.js +161 -0
- package/dist/sites/salesforce/fetch-page.d.ts +6 -0
- package/dist/sites/salesforce/fetch-page.js +42 -0
- package/dist/sites/salesforce/index.d.ts +2 -0
- package/dist/sites/salesforce/index.js +16 -0
- package/dist/sites/salesforce/token.d.ts +26 -0
- package/dist/sites/salesforce/token.js +154 -0
- package/dist/sites/salesforce-developer/fetch-page.d.ts +3 -0
- package/dist/sites/salesforce-developer/fetch-page.js +18 -0
- package/dist/sites/salesforce-developer/index.d.ts +2 -0
- package/dist/sites/salesforce-developer/index.js +12 -0
- package/dist/sites/salesforce-doc-fetch.d.ts +14 -0
- package/dist/sites/salesforce-doc-fetch.js +321 -0
- package/dist/sites/types.d.ts +27 -0
- package/dist/sites/types.js +1 -0
- package/oclif.manifest.json +845 -0
- package/package.json +78 -0
package/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Bonsai
|
|
2
|
+
|
|
3
|
+
Bonsai is a standalone local research cache CLI for AI agents. It turns official documentation and web pages into source-cited Markdown artifacts, stores them in a durable local cache, and returns deterministic output that agents can reuse instead of repeatedly scraping the same pages.
|
|
4
|
+
|
|
5
|
+
The package is published as `@taurgis/bonsai` and installs a `bonsai` binary.
|
|
6
|
+
|
|
7
|
+
> Because the npm package is scoped, the correct one-shot command is `npx @taurgis/bonsai ...`. The unscoped `npx bonsai` name resolves to the separate `bonsai` package on npm unless an unscoped shim is published later.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## What it Does
|
|
12
|
+
|
|
13
|
+
| Feature | How it works | Typical use case |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| **Scrape and convert** | Fetches HTML, extracts main content, sanitizes unsafe markup, and converts it to deterministic Markdown | Capturing API docs, guides, standards, and changelogs |
|
|
16
|
+
| **Cache-first lookup** | Normalizes URLs to collision-resistant SHA-256 keys before fetching | Avoiding duplicate network requests and repeated agent research |
|
|
17
|
+
| **Token budgeting** | Returns `compressed` or `detailed` Markdown variants | Fitting research into limited context windows without losing full detail |
|
|
18
|
+
| **Freshness tiers** | Supports `stable`, `standard`, `volatile`, custom TTLs, and stale revalidation | Handling standards differently from release notes or beta docs |
|
|
19
|
+
| **Browser fallback** | Uses `--rendered` for pages that need client-side JavaScript | Capturing SPA documentation when static HTML is incomplete |
|
|
20
|
+
| **Manual import** | Stores agent-supplied Markdown from stdin or files | Caching synthesized notes, private docs, or manually extracted pages |
|
|
21
|
+
| **Search** | Ranks local cache entries by topic, tags, snippets, fuzzy terms, and phrases | Finding existing research before fetching again |
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
Requires Node.js 22 or newer.
|
|
28
|
+
|
|
29
|
+
Run Bonsai without installing it globally:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx @taurgis/bonsai https://nodejs.org/api/url.html
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Install it as a normal CLI:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npm install -g @taurgis/bonsai
|
|
39
|
+
bonsai https://nodejs.org/api/url.html
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Use the local development binary inside this repository:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pnpm install
|
|
46
|
+
pnpm build
|
|
47
|
+
node bin/cli.mjs --help
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Quick Start
|
|
51
|
+
|
|
52
|
+
### 1. Fetch and cache a URL
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx @taurgis/bonsai https://nodejs.org/api/url.html
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
The first run fetches the page, extracts the main article content, converts it to Markdown, estimates token size, and stores it locally.
|
|
59
|
+
|
|
60
|
+
### 2. Reuse the cache
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npx @taurgis/bonsai https://nodejs.org/api/url.html
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The second run returns from cache when the entry is still fresh.
|
|
67
|
+
|
|
68
|
+
### 3. Check cache state and metadata
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npx @taurgis/bonsai status https://nodejs.org/api/url.html
|
|
72
|
+
npx @taurgis/bonsai inspect https://nodejs.org/api/url.html
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 4. Import manual research notes
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
echo "# My Custom Node API Notes" | npx @taurgis/bonsai import https://nodejs.org/api/url.html --stdin
|
|
79
|
+
|
|
80
|
+
echo "# Synthesized React Cache Guide" | npx @taurgis/bonsai import --stdin --topic "React Suspense" --source-url https://react.dev/a --source-url https://react.dev/b
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 5. Search before fetching
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npx @taurgis/bonsai search "node api url"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Command Reference
|
|
92
|
+
|
|
93
|
+
### `<url>`
|
|
94
|
+
|
|
95
|
+
Fetch and format a webpage, or retrieve it from cache.
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npx @taurgis/bonsai <url> [flags]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Common flags:
|
|
102
|
+
|
|
103
|
+
* `--topic`, `-t`: Primary category/topic for metadata tagging.
|
|
104
|
+
* `--tags`, `-g`: Searchable tags. Can be repeated.
|
|
105
|
+
* `--format`, `-f`: Output density: `compressed` or `detailed`.
|
|
106
|
+
* `--tier`: Freshness tier: `stable`, `standard`, or `volatile`.
|
|
107
|
+
* `--ttl`, `-l`: Custom predicted lifespan, such as `2h`, `7d`, or `30d`.
|
|
108
|
+
* `--max-age`: Read-time freshness threshold.
|
|
109
|
+
* `--force`: Force a fresh fetch.
|
|
110
|
+
* `--dry-run`: Scrape and validate without writing to cache.
|
|
111
|
+
* `--allow-stale`: Serve stale cache if the remote site is offline.
|
|
112
|
+
* `--rendered`: Use browser-rendered extraction for SPA pages.
|
|
113
|
+
* `--json`: Return structured machine-readable output.
|
|
114
|
+
|
|
115
|
+
### `import [url]`
|
|
116
|
+
|
|
117
|
+
Save custom Markdown notes directly to the cache.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
npx @taurgis/bonsai import [url] --stdin [flags]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### `status <url>`
|
|
124
|
+
|
|
125
|
+
Inspect cache state and planned action without fetching.
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npx @taurgis/bonsai status <url> [flags]
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### `inspect <url>`
|
|
132
|
+
|
|
133
|
+
Display stored YAML frontmatter metadata for a URL.
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
npx @taurgis/bonsai inspect <url>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### `search <query>`
|
|
140
|
+
|
|
141
|
+
Search cached research by metadata and content snippets.
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
npx @taurgis/bonsai search "<keywords>" [flags]
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### `config`
|
|
148
|
+
|
|
149
|
+
Choose global or project-local cache storage.
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npx @taurgis/bonsai config set storage project --local
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Project config is stored in `.bonsai.json`; project cache files are stored under `.bonsai/research/`.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Freshness and Cache Rules
|
|
160
|
+
|
|
161
|
+
Global cache files live in Bonsai's oclif data directory, typically:
|
|
162
|
+
|
|
163
|
+
* **macOS**: `~/Library/Application Support/bonsai/research/`
|
|
164
|
+
* **Linux**: `~/.local/share/bonsai/research/`
|
|
165
|
+
* **Windows**: `%LOCALAPPDATA%\bonsai\research\`
|
|
166
|
+
|
|
167
|
+
Project-local storage uses:
|
|
168
|
+
|
|
169
|
+
* `.bonsai.json` for the project config
|
|
170
|
+
* `.bonsai/research/` for Markdown cache artifacts
|
|
171
|
+
|
|
172
|
+
If no `--ttl` is specified, freshness is computed from the tier:
|
|
173
|
+
|
|
174
|
+
| Tier | Fresh duration | Grace window | Use case |
|
|
175
|
+
| --- | --- | --- | --- |
|
|
176
|
+
| `volatile` | 7 days | 5 days | Latest releases, volatile changelogs, beta docs |
|
|
177
|
+
| `standard` | 30 days | 14 days | General API docs and developer guides |
|
|
178
|
+
| `stable` | 180 days | 60 days | RFCs, standards, long-lived references |
|
|
179
|
+
|
|
180
|
+
When stale entries still have `ETag` or `Last-Modified` metadata, Bonsai attempts cheap revalidation before a full refetch. If revalidation fails and `--allow-stale` is omitted, the CLI can serve the stale content while exiting with code `5`.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## JSON Envelope
|
|
185
|
+
|
|
186
|
+
When run with `--json`, commands return a stable envelope:
|
|
187
|
+
|
|
188
|
+
```json
|
|
189
|
+
{
|
|
190
|
+
"schemaVersion": 1,
|
|
191
|
+
"command": "bonsai",
|
|
192
|
+
"ok": true,
|
|
193
|
+
"exitCode": 0,
|
|
194
|
+
"stdout": "",
|
|
195
|
+
"stderr": "",
|
|
196
|
+
"data": {
|
|
197
|
+
"schemaVersion": 1,
|
|
198
|
+
"command": "bonsai",
|
|
199
|
+
"cache": {
|
|
200
|
+
"key": "0f115db062b7c0dd030b16878c99dea5c354b49dc37b38eb8846179c7783e9d7",
|
|
201
|
+
"status": "hit",
|
|
202
|
+
"freshness": "fresh",
|
|
203
|
+
"path": "/Users/user/Library/Application Support/bonsai/research/0f115db062b7c0dd030b16878c99dea5c354b49dc37b38eb8846179c7783e9d7.md"
|
|
204
|
+
},
|
|
205
|
+
"source": {
|
|
206
|
+
"url": "https://example.com",
|
|
207
|
+
"normalizedUrl": "https://example.com/",
|
|
208
|
+
"captureMethod": "static_fetch",
|
|
209
|
+
"extractionStatus": "extracted",
|
|
210
|
+
"extractionConfidence": "low",
|
|
211
|
+
"qualityNotes": ["readability extracted main article"],
|
|
212
|
+
"fetchedAt": "2026-06-24T07:33:20.519Z",
|
|
213
|
+
"validatedAt": "2026-06-24T07:33:20.519Z",
|
|
214
|
+
"staleAfter": "2026-07-24T07:33:20.519Z"
|
|
215
|
+
},
|
|
216
|
+
"format": "compressed",
|
|
217
|
+
"tokenEstimate": 29,
|
|
218
|
+
"content": "Cleaned main content markdown text..."
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Security and Limits
|
|
226
|
+
|
|
227
|
+
* Fetched HTML is treated as untrusted input.
|
|
228
|
+
* Only `http:` and `https:` URLs are accepted.
|
|
229
|
+
* Private and local IP ranges are blocked to reduce SSRF risk.
|
|
230
|
+
* Static extraction does not execute JavaScript; use `--rendered` when a page needs browser execution.
|
|
231
|
+
* Authenticated/private scraping is not supported in the default workflow. Import trusted Markdown manually instead.
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Reference Documentation
|
|
236
|
+
|
|
237
|
+
* [Command Reference](docs/commands.md)
|
|
238
|
+
* [Caching Protocol Specification](docs/cache-protocol.md)
|
|
239
|
+
* [Troubleshooting and Limitations](docs/troubleshooting.md)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: 'Official Docs Researcher'
|
|
3
|
+
description: 'Researches official documentation through Bonsai and returns source-cited findings'
|
|
4
|
+
model: 'Auto'
|
|
5
|
+
tools: ['vscode/askQuestions', 'execute', 'read', 'search', 'web', 'vscode/memory']
|
|
6
|
+
argument-hint: 'What topic should I research in official docs?'
|
|
7
|
+
metadata:
|
|
8
|
+
version: '3.0.0'
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Official Docs Researcher Agent
|
|
12
|
+
|
|
13
|
+
You are a Forward documentation research specialist. Your job is to find current official documentation, cache it through Bonsai, and return concise source-cited findings that another agent can apply safely.
|
|
14
|
+
|
|
15
|
+
## Scope
|
|
16
|
+
|
|
17
|
+
Use this agent for technical changes, platform behavior, APIs, SDKs, standards, release notes, changelogs, beta docs, security advisories, and other documentation-sensitive work.
|
|
18
|
+
|
|
19
|
+
Do not research from memory alone. Training-data knowledge does not satisfy this workflow.
|
|
20
|
+
|
|
21
|
+
## Default Workflow
|
|
22
|
+
|
|
23
|
+
1. Identify the product, version, edition, and source authority needed for the request.
|
|
24
|
+
2. Search the Bonsai cache first:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
bonsai search "<topic or keywords>"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
3. If the cache misses or does not cover the question, locate official source URLs. Prefer vendor docs, standards bodies, API references, and official release notes.
|
|
31
|
+
4. Capture each source through Bonsai:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
bonsai <official-url> --format detailed
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
5. Use `--rendered` for SPAs or pages where static extraction is incomplete:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
bonsai <official-url> --rendered --format detailed
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
6. Summarize only what the official sources support. Include source URLs, validation time when available, version notes, and any important limitations.
|
|
44
|
+
|
|
45
|
+
Inside the Bonsai repository, use the development binary instead of an installed global command:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
node bin/cli.mjs search "<topic or keywords>" --json
|
|
49
|
+
node bin/cli.mjs <official-url> --format detailed --json
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
For one-shot published usage outside this repo, use the scoped package name:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx @taurgis/bonsai <official-url> --format detailed
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Do not document or run bare `npx bonsai` unless an unscoped npm shim is actually published.
|
|
59
|
+
|
|
60
|
+
## Freshness Policy
|
|
61
|
+
|
|
62
|
+
Choose the freshness tier based on the source:
|
|
63
|
+
|
|
64
|
+
- `stable`: standards, specs, MDN reference pages, pinned major-version docs.
|
|
65
|
+
- `standard`: vendor API docs, SDK docs, normal technical references.
|
|
66
|
+
- `volatile`: release notes, changelogs, latest pages, security advisories, beta docs.
|
|
67
|
+
|
|
68
|
+
Stale volatile sources must be revalidated before they are trusted. Never overwrite a good cache entry with an error response.
|
|
69
|
+
|
|
70
|
+
## Fallbacks
|
|
71
|
+
|
|
72
|
+
If direct web access was unavoidable because Bonsai could not fetch the content, import the synthesized notes back into Bonsai before returning:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
bonsai import <url> --file <path>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
For multi-source synthesis:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
bonsai import \
|
|
82
|
+
--topic "<descriptive topic>" \
|
|
83
|
+
--source-url <url1> \
|
|
84
|
+
--source-url <url2> \
|
|
85
|
+
--file <path>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Response Contract
|
|
89
|
+
|
|
90
|
+
Return:
|
|
91
|
+
|
|
92
|
+
- Answer summary.
|
|
93
|
+
- Official source URLs used.
|
|
94
|
+
- Version, edition, or date constraints.
|
|
95
|
+
- Cache status or validation timestamp when Bonsai output provides it.
|
|
96
|
+
- Gaps or uncertainty where official docs do not answer the question.
|
|
97
|
+
|
|
98
|
+
Keep stdout clean when using `--json`; warnings and diagnostic notes belong in stderr or in the final prose summary.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 'Require the Official Docs Researcher subagent before technical repo changes.'
|
|
3
|
+
applyTo: '**'
|
|
4
|
+
skills:
|
|
5
|
+
- web-research
|
|
6
|
+
metadata:
|
|
7
|
+
version: '3.0.0'
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Web Research Requirement
|
|
11
|
+
|
|
12
|
+
## Mandatory Pre-Step
|
|
13
|
+
|
|
14
|
+
- Run the **Official Docs Researcher** subagent before creating, updating, refactoring, scaffolding, or deleting technical content.
|
|
15
|
+
- Use its findings to apply current official documentation and references in the change.
|
|
16
|
+
- The researcher keeps a freshness-tiered Bonsai cache (its data directory or project-local `.bonsai/research/`): it reuses fresh notes, cheaply revalidates stale ones, and re-fetches only on a miss. Re-running on a recent topic is cheap — invoke it rather than skipping research to "save" a fetch.
|
|
17
|
+
|
|
18
|
+
## When Not to Use
|
|
19
|
+
|
|
20
|
+
- No technical content is being modified (purely editorial changes).
|
|
21
|
+
- You already fetched and applied official docs **in the current task**. Training-data knowledge does not satisfy this — only docs fetched in the same task do.
|
|
22
|
+
- The request is too simple to warrant research (typo, lint fix not involving platform behavior).
|
|
23
|
+
|
|
24
|
+
## Examples
|
|
25
|
+
|
|
26
|
+
- ✅ Before editing instructions, prompts, agents, skills, workflows, or docs.
|
|
27
|
+
- ✅ Before auditing or refactoring source — reading source code does not replace checking current platform guidance.
|
|
28
|
+
- ✅ Include official source URLs when the change references platform behavior or standards.
|
|
29
|
+
- ❌ Don't modify technical content without running the subagent first, or treat cached/training knowledge as equivalent — official guidance evolves and must be verified per task.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: web-research
|
|
3
|
+
description: 'Bonsai-backed official documentation and web research workflow. Use before technical changes that depend on platform behavior, when fetching documentation or web pages, when searching/listing/pruning the local research cache, or when importing manually gathered notes.'
|
|
4
|
+
license: Forward Proprietary
|
|
5
|
+
compatibility: VS Code 1.x+, GitHub Copilot
|
|
6
|
+
metadata:
|
|
7
|
+
version: '2.1.0'
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Web Research Skill
|
|
11
|
+
|
|
12
|
+
Use Bonsai as the cache-first research path for official documentation and web content. Prefer current official sources, keep research reusable, and avoid direct one-off web fetches when Bonsai can capture the same page.
|
|
13
|
+
|
|
14
|
+
## Required Pre-Step
|
|
15
|
+
|
|
16
|
+
Before creating, updating, refactoring, scaffolding, or deleting technical content, verify relevant current official documentation in the same task.
|
|
17
|
+
|
|
18
|
+
Search the cache first:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
bonsai search "<topic or keywords>"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
If the cache misses or does not cover the question, fetch the source through Bonsai:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
bonsai <official-url> --format detailed
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Inside the Bonsai repository, use the development binary:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
node bin/cli.mjs search "<topic or keywords>" --json
|
|
34
|
+
node bin/cli.mjs <official-url> --format detailed --json
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For one-shot published usage outside this repo, use the scoped package name:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx @taurgis/bonsai <official-url> --format detailed
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Do not document or run bare `npx bonsai` unless an unscoped npm shim is actually published.
|
|
44
|
+
|
|
45
|
+
## Source Rules
|
|
46
|
+
|
|
47
|
+
- Prefer official vendor docs, standards, API references, SDK docs, release notes, changelogs, and security advisories.
|
|
48
|
+
- Include official source URLs when the change relies on platform behavior or standards.
|
|
49
|
+
- Use `--tier stable`, `--tier standard`, or `--tier volatile` when the source class is clear.
|
|
50
|
+
- Treat volatile sources as needing fresh validation before trusting them.
|
|
51
|
+
- Do not use the retired manual `artifacts/online-research/` protocol for new research.
|
|
52
|
+
|
|
53
|
+
## Fetch Rules
|
|
54
|
+
|
|
55
|
+
Use `--format compressed` for context-budgeted reading and `--format detailed` for exact technical details, links, tables, and code examples.
|
|
56
|
+
|
|
57
|
+
Use `--rendered` when static extraction is incomplete or the page is an SPA:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
bonsai <official-url> --rendered --format detailed
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Never reach for direct `WebFetch` or `WebSearch` to retrieve a specific page when Bonsai can fetch it. Bonsai returns reusable Markdown and keeps it cached for future agents.
|
|
64
|
+
|
|
65
|
+
Pure discovery searches are allowed when you do not yet know which URL to fetch. Once a URL is selected, capture it with Bonsai.
|
|
66
|
+
|
|
67
|
+
## Manual Fallbacks
|
|
68
|
+
|
|
69
|
+
If direct web access was unavoidable because of authentication, browser interaction, or a tool constraint, import the result into Bonsai before returning.
|
|
70
|
+
|
|
71
|
+
Single-source import:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
bonsai import <url> --file path/to/notes.md
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Stdin import:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
echo "# My Synthesis Note" | bonsai import <url> --stdin
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Multi-source synthesis:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
bonsai import \
|
|
87
|
+
--topic "<descriptive topic>" \
|
|
88
|
+
--source-url <url1> \
|
|
89
|
+
--source-url <url2> \
|
|
90
|
+
--file path/to/synthesized-notes.md
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Cache Operations
|
|
94
|
+
|
|
95
|
+
Check status without fetching:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
bonsai status <url>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Inspect stored metadata:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
bonsai inspect <url>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
List or search cached entries:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
bonsai list --tags node
|
|
111
|
+
bonsai search "react suspense"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Preview pruning before deleting:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
bonsai prune --older-than 90d --dry-run
|
|
118
|
+
bonsai prune --older-than 90d --yes
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## When This Does Not Apply
|
|
122
|
+
|
|
123
|
+
- No technical content or web content is involved.
|
|
124
|
+
- You already fetched and applied current official docs in the current task.
|
|
125
|
+
- The request is a trivial typo or formatting fix that does not involve platform behavior.
|
|
126
|
+
- Bonsai is not installed and `npx @taurgis/bonsai ...` is not available; use the best available official-source workflow and import notes later.
|
package/bin/cli.mjs
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execute } from '@oclif/core';
|
|
4
|
+
import { dirname } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
|
|
9
|
+
const rawArgv = process.argv.slice(2);
|
|
10
|
+
const [firstArg, ...restArgv] = rawArgv;
|
|
11
|
+
const args =
|
|
12
|
+
firstArg === 'help'
|
|
13
|
+
? [...restArgv, '--help']
|
|
14
|
+
: firstArg?.startsWith('http://') || firstArg?.startsWith('https://')
|
|
15
|
+
? ['fetch', ...rawArgv]
|
|
16
|
+
: rawArgv;
|
|
17
|
+
|
|
18
|
+
await execute({
|
|
19
|
+
args,
|
|
20
|
+
development: false,
|
|
21
|
+
dir: __dirname + '/../',
|
|
22
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Command, Interfaces } from '@oclif/core';
|
|
2
|
+
export declare abstract class BaseCommand<T extends typeof Command> extends Command {
|
|
3
|
+
static jsonEnvelope: boolean;
|
|
4
|
+
static baseFlags: {
|
|
5
|
+
json: Interfaces.BooleanFlag<boolean>;
|
|
6
|
+
};
|
|
7
|
+
flags: Interfaces.InferredFlags<T['flags'] & typeof BaseCommand.baseFlags>;
|
|
8
|
+
args: Interfaces.InferredArgs<T['args']>;
|
|
9
|
+
protected requestedJson(): boolean;
|
|
10
|
+
protected envelopeCommandId(): string;
|
|
11
|
+
protected _run<R>(): Promise<R>;
|
|
12
|
+
abstract execute(): Promise<unknown>;
|
|
13
|
+
run(): Promise<unknown>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
export class BaseCommand extends Command {
|
|
3
|
+
static jsonEnvelope = false;
|
|
4
|
+
static baseFlags = {
|
|
5
|
+
json: Flags.boolean({
|
|
6
|
+
description: 'Return the output as a machine-readable JSON object.',
|
|
7
|
+
}),
|
|
8
|
+
};
|
|
9
|
+
flags;
|
|
10
|
+
args;
|
|
11
|
+
requestedJson() {
|
|
12
|
+
return Boolean(this.flags?.json);
|
|
13
|
+
}
|
|
14
|
+
envelopeCommandId() {
|
|
15
|
+
return this.ctor.id || this.config.bin;
|
|
16
|
+
}
|
|
17
|
+
async _run() {
|
|
18
|
+
// Check argv directly for --json because if parsing fails in init(),
|
|
19
|
+
// we still want to output the failure in the JSON envelope format.
|
|
20
|
+
const isJson = this.argv.includes('--json');
|
|
21
|
+
if (isJson) {
|
|
22
|
+
try {
|
|
23
|
+
await this.init();
|
|
24
|
+
const result = await this.run();
|
|
25
|
+
const exitCode = process.exitCode || 0;
|
|
26
|
+
console.log(JSON.stringify({
|
|
27
|
+
schemaVersion: 1,
|
|
28
|
+
command: this.envelopeCommandId(),
|
|
29
|
+
ok: exitCode === 0 || exitCode === 5,
|
|
30
|
+
exitCode,
|
|
31
|
+
stdout: '',
|
|
32
|
+
stderr: '',
|
|
33
|
+
data: result,
|
|
34
|
+
}, null, 2));
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const exitCode = error.oclif?.exit ?? 1;
|
|
38
|
+
console.log(JSON.stringify({
|
|
39
|
+
schemaVersion: 1,
|
|
40
|
+
command: this.envelopeCommandId(),
|
|
41
|
+
ok: false,
|
|
42
|
+
exitCode,
|
|
43
|
+
stdout: '',
|
|
44
|
+
stderr: error.message || String(error),
|
|
45
|
+
data: null,
|
|
46
|
+
}, null, 2));
|
|
47
|
+
process.exitCode = exitCode;
|
|
48
|
+
}
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
return super._run();
|
|
52
|
+
}
|
|
53
|
+
async run() {
|
|
54
|
+
return this.execute();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import { BaseCommand } from '../../base-command.js';
|
|
3
|
+
import type { ConfigKey, ConfigScope } from '../../lib/config/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Shared base for the four `config` subcommands: the mutually-exclusive
|
|
6
|
+
* scope-flag guard, the binary write-scope helper, and known-key validation.
|
|
7
|
+
*/
|
|
8
|
+
export declare abstract class ConfigCommand<T extends typeof Command> extends BaseCommand<T> {
|
|
9
|
+
protected assertScopeFlagsExclusive(global?: boolean, local?: boolean): void;
|
|
10
|
+
/** `--local` writes/reads the project file; otherwise the user-level file. */
|
|
11
|
+
protected writeScope(local?: boolean): 'user' | 'project';
|
|
12
|
+
/** Read scope for get/list: an explicit `--global`/`--local`, else the merged effective view. */
|
|
13
|
+
protected readScope(global?: boolean, local?: boolean): ConfigScope;
|
|
14
|
+
protected assertKnownKey(keyArg: string): asserts keyArg is ConfigKey;
|
|
15
|
+
}
|
|
16
|
+
/** Build the `--global`/`--local` scope-flag pair with per-command descriptions. */
|
|
17
|
+
export declare function configScopeFlags(descriptions: {
|
|
18
|
+
global: string;
|
|
19
|
+
local: string;
|
|
20
|
+
}): {
|
|
21
|
+
global: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
22
|
+
local: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
23
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { BaseCommand } from '../../base-command.js';
|
|
3
|
+
import { isKnownKey, suggestKey, validKeysHint } from '../../lib/config/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Shared base for the four `config` subcommands: the mutually-exclusive
|
|
6
|
+
* scope-flag guard, the binary write-scope helper, and known-key validation.
|
|
7
|
+
*/
|
|
8
|
+
export class ConfigCommand extends BaseCommand {
|
|
9
|
+
assertScopeFlagsExclusive(global, local) {
|
|
10
|
+
if (global && local) {
|
|
11
|
+
this.error('--global and --local are mutually exclusive.', { exit: 2 });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/** `--local` writes/reads the project file; otherwise the user-level file. */
|
|
15
|
+
writeScope(local) {
|
|
16
|
+
return local ? 'project' : 'user';
|
|
17
|
+
}
|
|
18
|
+
/** Read scope for get/list: an explicit `--global`/`--local`, else the merged effective view. */
|
|
19
|
+
readScope(global, local) {
|
|
20
|
+
if (global)
|
|
21
|
+
return 'global';
|
|
22
|
+
if (local)
|
|
23
|
+
return 'local';
|
|
24
|
+
return 'effective';
|
|
25
|
+
}
|
|
26
|
+
assertKnownKey(keyArg) {
|
|
27
|
+
if (!isKnownKey(keyArg)) {
|
|
28
|
+
const suggestion = suggestKey(keyArg);
|
|
29
|
+
const hint = suggestion ? ` Did you mean: ${suggestion}?` : '';
|
|
30
|
+
this.error(`Unknown config key: "${keyArg}".${hint} Valid keys: ${validKeysHint()}.`, {
|
|
31
|
+
exit: 2,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Build the `--global`/`--local` scope-flag pair with per-command descriptions. */
|
|
37
|
+
export function configScopeFlags(descriptions) {
|
|
38
|
+
return {
|
|
39
|
+
global: Flags.boolean({ char: 'g', description: descriptions.global }),
|
|
40
|
+
local: Flags.boolean({
|
|
41
|
+
description: descriptions.local,
|
|
42
|
+
aliases: ['project'],
|
|
43
|
+
charAliases: ['p'],
|
|
44
|
+
}),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ConfigCommand } from './base.js';
|
|
2
|
+
export default class ConfigGet extends ConfigCommand<typeof ConfigGet> {
|
|
3
|
+
static id: string;
|
|
4
|
+
static summary: string;
|
|
5
|
+
static description: string;
|
|
6
|
+
static examples: {
|
|
7
|
+
description: string;
|
|
8
|
+
command: string;
|
|
9
|
+
}[];
|
|
10
|
+
static args: {
|
|
11
|
+
key: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
12
|
+
};
|
|
13
|
+
static flags: {
|
|
14
|
+
global: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
local: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
|
+
};
|
|
17
|
+
static stdoutIsPrimaryData: boolean;
|
|
18
|
+
init(): Promise<void>;
|
|
19
|
+
execute(): Promise<unknown>;
|
|
20
|
+
}
|