@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.
Files changed (159) hide show
  1. package/README.md +239 -0
  2. package/agents/agents/official-docs-researcher.agent.md +98 -0
  3. package/agents/instructions/web-research.instructions.md +29 -0
  4. package/agents/skills/web-research/SKILL.md +126 -0
  5. package/bin/cli.mjs +22 -0
  6. package/dist/base-command.d.ts +14 -0
  7. package/dist/base-command.js +56 -0
  8. package/dist/commands/config/base.d.ts +23 -0
  9. package/dist/commands/config/base.js +46 -0
  10. package/dist/commands/config/get.d.ts +20 -0
  11. package/dist/commands/config/get.js +48 -0
  12. package/dist/commands/config/list.d.ts +17 -0
  13. package/dist/commands/config/list.js +48 -0
  14. package/dist/commands/config/set.d.ts +31 -0
  15. package/dist/commands/config/set.js +94 -0
  16. package/dist/commands/config/unset.d.ts +21 -0
  17. package/dist/commands/config/unset.js +65 -0
  18. package/dist/commands/fetch.d.ts +38 -0
  19. package/dist/commands/fetch.js +311 -0
  20. package/dist/commands/import.d.ts +38 -0
  21. package/dist/commands/import.js +313 -0
  22. package/dist/commands/index.d.ts +35 -0
  23. package/dist/commands/index.js +306 -0
  24. package/dist/commands/inspect.d.ts +13 -0
  25. package/dist/commands/inspect.js +82 -0
  26. package/dist/commands/list.d.ts +25 -0
  27. package/dist/commands/list.js +143 -0
  28. package/dist/commands/prune.d.ts +24 -0
  29. package/dist/commands/prune.js +141 -0
  30. package/dist/commands/research/config/base.d.ts +23 -0
  31. package/dist/commands/research/config/base.js +46 -0
  32. package/dist/commands/research/config/get.d.ts +20 -0
  33. package/dist/commands/research/config/get.js +45 -0
  34. package/dist/commands/research/config/list.d.ts +17 -0
  35. package/dist/commands/research/config/list.js +42 -0
  36. package/dist/commands/research/config/set.d.ts +31 -0
  37. package/dist/commands/research/config/set.js +94 -0
  38. package/dist/commands/research/config/unset.d.ts +21 -0
  39. package/dist/commands/research/config/unset.js +59 -0
  40. package/dist/commands/research/import.d.ts +38 -0
  41. package/dist/commands/research/import.js +313 -0
  42. package/dist/commands/research/inspect.d.ts +13 -0
  43. package/dist/commands/research/inspect.js +86 -0
  44. package/dist/commands/research/list.d.ts +25 -0
  45. package/dist/commands/research/list.js +143 -0
  46. package/dist/commands/research/prune.d.ts +24 -0
  47. package/dist/commands/research/prune.js +141 -0
  48. package/dist/commands/research/search.d.ts +32 -0
  49. package/dist/commands/research/search.js +334 -0
  50. package/dist/commands/research/status.d.ts +17 -0
  51. package/dist/commands/research/status.js +108 -0
  52. package/dist/commands/research.d.ts +36 -0
  53. package/dist/commands/research.js +304 -0
  54. package/dist/commands/search.d.ts +32 -0
  55. package/dist/commands/search.js +334 -0
  56. package/dist/commands/status.d.ts +17 -0
  57. package/dist/commands/status.js +95 -0
  58. package/dist/commands.d.ts +24 -0
  59. package/dist/commands.js +24 -0
  60. package/dist/lib/config/index.d.ts +18 -0
  61. package/dist/lib/config/index.js +33 -0
  62. package/dist/lib/config/io.d.ts +8 -0
  63. package/dist/lib/config/io.js +75 -0
  64. package/dist/lib/config/resolve.d.ts +15 -0
  65. package/dist/lib/config/resolve.js +20 -0
  66. package/dist/lib/config/schema.d.ts +28 -0
  67. package/dist/lib/config/schema.js +32 -0
  68. package/dist/lib/research/artifact.d.ts +21 -0
  69. package/dist/lib/research/artifact.js +217 -0
  70. package/dist/lib/research/browser.d.ts +74 -0
  71. package/dist/lib/research/browser.js +359 -0
  72. package/dist/lib/research/cache-key.d.ts +5 -0
  73. package/dist/lib/research/cache-key.js +10 -0
  74. package/dist/lib/research/capture.d.ts +29 -0
  75. package/dist/lib/research/capture.js +181 -0
  76. package/dist/lib/research/compress.d.ts +5 -0
  77. package/dist/lib/research/compress.js +17 -0
  78. package/dist/lib/research/docs/__fixtures__/sphinx-searchindex.d.ts +1 -0
  79. package/dist/lib/research/docs/__fixtures__/sphinx-searchindex.js +2 -0
  80. package/dist/lib/research/docs/capabilities.d.ts +43 -0
  81. package/dist/lib/research/docs/capabilities.js +9 -0
  82. package/dist/lib/research/docs/clean-dom.d.ts +4 -0
  83. package/dist/lib/research/docs/clean-dom.js +41 -0
  84. package/dist/lib/research/docs/code-blocks.d.ts +5 -0
  85. package/dist/lib/research/docs/code-blocks.js +69 -0
  86. package/dist/lib/research/docs/detect.d.ts +6 -0
  87. package/dist/lib/research/docs/detect.js +239 -0
  88. package/dist/lib/research/docs/machine-readable.d.ts +21 -0
  89. package/dist/lib/research/docs/machine-readable.js +73 -0
  90. package/dist/lib/research/docs/markdown-source.d.ts +17 -0
  91. package/dist/lib/research/docs/markdown-source.js +194 -0
  92. package/dist/lib/research/docs/quality-gates.d.ts +9 -0
  93. package/dist/lib/research/docs/quality-gates.js +65 -0
  94. package/dist/lib/research/docs/remote-search-runner.d.ts +21 -0
  95. package/dist/lib/research/docs/remote-search-runner.js +54 -0
  96. package/dist/lib/research/docs/remote-search.d.ts +14 -0
  97. package/dist/lib/research/docs/remote-search.js +101 -0
  98. package/dist/lib/research/docs/rsc.d.ts +10 -0
  99. package/dist/lib/research/docs/rsc.js +30 -0
  100. package/dist/lib/research/docs/search-index.d.ts +13 -0
  101. package/dist/lib/research/docs/search-index.js +176 -0
  102. package/dist/lib/research/docs/section-artifacts.d.ts +12 -0
  103. package/dist/lib/research/docs/section-artifacts.js +75 -0
  104. package/dist/lib/research/docs/sections.d.ts +12 -0
  105. package/dist/lib/research/docs/sections.js +67 -0
  106. package/dist/lib/research/docs/source-map.d.ts +19 -0
  107. package/dist/lib/research/docs/source-map.js +87 -0
  108. package/dist/lib/research/docs/validate.d.ts +15 -0
  109. package/dist/lib/research/docs/validate.js +63 -0
  110. package/dist/lib/research/extract.d.ts +11 -0
  111. package/dist/lib/research/extract.js +83 -0
  112. package/dist/lib/research/fetcher.d.ts +29 -0
  113. package/dist/lib/research/fetcher.js +192 -0
  114. package/dist/lib/research/freshness.d.ts +21 -0
  115. package/dist/lib/research/freshness.js +82 -0
  116. package/dist/lib/research/markdown.d.ts +6 -0
  117. package/dist/lib/research/markdown.js +66 -0
  118. package/dist/lib/research/regression.d.ts +23 -0
  119. package/dist/lib/research/regression.js +93 -0
  120. package/dist/lib/research/resolve-target.d.ts +18 -0
  121. package/dist/lib/research/resolve-target.js +20 -0
  122. package/dist/lib/research/revalidate.d.ts +32 -0
  123. package/dist/lib/research/revalidate.js +152 -0
  124. package/dist/lib/research/schema.d.ts +47 -0
  125. package/dist/lib/research/schema.js +1 -0
  126. package/dist/lib/research/secret-scan.d.ts +5 -0
  127. package/dist/lib/research/secret-scan.js +37 -0
  128. package/dist/lib/research/secure-write.d.ts +16 -0
  129. package/dist/lib/research/secure-write.js +17 -0
  130. package/dist/lib/research/storage.d.ts +52 -0
  131. package/dist/lib/research/storage.js +182 -0
  132. package/dist/lib/research/store-roots.d.ts +29 -0
  133. package/dist/lib/research/store-roots.js +28 -0
  134. package/dist/lib/research/token-estimate.d.ts +5 -0
  135. package/dist/lib/research/token-estimate.js +7 -0
  136. package/dist/lib/research/url.d.ts +24 -0
  137. package/dist/lib/research/url.js +121 -0
  138. package/dist/sites/index.d.ts +4 -0
  139. package/dist/sites/index.js +28 -0
  140. package/dist/sites/salesforce/browser-search.d.ts +7 -0
  141. package/dist/sites/salesforce/browser-search.js +82 -0
  142. package/dist/sites/salesforce/coveo.d.ts +23 -0
  143. package/dist/sites/salesforce/coveo.js +161 -0
  144. package/dist/sites/salesforce/fetch-page.d.ts +6 -0
  145. package/dist/sites/salesforce/fetch-page.js +42 -0
  146. package/dist/sites/salesforce/index.d.ts +2 -0
  147. package/dist/sites/salesforce/index.js +16 -0
  148. package/dist/sites/salesforce/token.d.ts +26 -0
  149. package/dist/sites/salesforce/token.js +154 -0
  150. package/dist/sites/salesforce-developer/fetch-page.d.ts +3 -0
  151. package/dist/sites/salesforce-developer/fetch-page.js +18 -0
  152. package/dist/sites/salesforce-developer/index.d.ts +2 -0
  153. package/dist/sites/salesforce-developer/index.js +12 -0
  154. package/dist/sites/salesforce-doc-fetch.d.ts +14 -0
  155. package/dist/sites/salesforce-doc-fetch.js +321 -0
  156. package/dist/sites/types.d.ts +27 -0
  157. package/dist/sites/types.js +1 -0
  158. package/oclif.manifest.json +845 -0
  159. 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
+ }