@taurgis/bonsai 1.0.5 → 1.0.6
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 +53 -0
- package/agents/agents/official-docs-researcher.agent.md +10 -1
- package/agents/agents/salesforce-docs-researcher.agent.md +10 -1
- package/agents/instructions/salesforce-research.instructions.md +4 -2
- package/agents/instructions/web-research.instructions.md +4 -2
- package/agents/skills/web-research/SKILL.md +8 -3
- package/bin/cli.mjs +30 -13
- package/dist/base-command.d.ts +3 -0
- package/dist/base-command.js +40 -7
- package/dist/commands/config/base.d.ts +2 -0
- package/dist/commands/config/base.js +15 -1
- package/dist/commands/config/get.d.ts +1 -1
- package/dist/commands/config/get.js +4 -5
- package/dist/commands/config/index.d.ts +12 -0
- package/dist/commands/config/index.js +34 -0
- package/dist/commands/config/list.js +2 -2
- package/dist/commands/config/set.js +18 -12
- package/dist/commands/config/unset.d.ts +1 -1
- package/dist/commands/config/unset.js +9 -6
- package/dist/commands/fetch.js +44 -34
- package/dist/commands/import.d.ts +3 -0
- package/dist/commands/import.js +82 -52
- package/dist/commands/inspect.d.ts +4 -0
- package/dist/commands/inspect.js +24 -19
- package/dist/commands/list.d.ts +2 -2
- package/dist/commands/list.js +23 -15
- package/dist/commands/prune.d.ts +1 -1
- package/dist/commands/prune.js +30 -13
- package/dist/commands/search.d.ts +4 -0
- package/dist/commands/search.js +83 -21
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.js +50 -24
- package/dist/commands.d.ts +2 -0
- package/dist/commands.js +2 -0
- package/dist/hooks/command-not-found/suggest.js +37 -8
- package/dist/lib/argv.d.ts +10 -0
- package/dist/lib/argv.js +53 -0
- package/dist/lib/envelope.d.ts +36 -0
- package/dist/lib/envelope.js +76 -0
- package/dist/lib/json-meta.d.ts +10 -0
- package/dist/lib/json-meta.js +98 -0
- package/dist/lib/research/revalidate.js +3 -1
- package/dist/lib/research/schema.d.ts +5 -2
- package/dist/lib/research/schema.js +12 -1
- package/dist/lib/text.d.ts +2 -0
- package/dist/lib/text.js +2 -0
- package/dist/sites/algolia.d.ts +9 -0
- package/dist/sites/algolia.js +26 -0
- package/dist/sites/auto.d.ts +8 -0
- package/dist/sites/auto.js +30 -0
- package/dist/sites/index.js +17 -1
- package/dist/sites/nextjs/index.d.ts +2 -0
- package/dist/sites/nextjs/index.js +52 -0
- package/dist/sites/react/index.d.ts +2 -0
- package/dist/sites/react/index.js +17 -0
- package/dist/sites/tailwind/index.d.ts +2 -0
- package/dist/sites/tailwind/index.js +17 -0
- package/dist/sites/vue/index.d.ts +2 -0
- package/dist/sites/vue/index.js +17 -0
- package/oclif.manifest.json +149 -76
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -180,6 +180,35 @@ Both keys can also be overridden per-invocation via the `BONSAI_STORAGE` and `BO
|
|
|
180
180
|
|
|
181
181
|
---
|
|
182
182
|
|
|
183
|
+
## Shell Autocomplete
|
|
184
|
+
|
|
185
|
+
Bonsai ships with shell autocomplete via `@oclif/plugin-autocomplete`. After installing globally, set it up once for your shell:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
# Bash — add to ~/.bashrc
|
|
189
|
+
eval "$(bonsai autocomplete bash)"
|
|
190
|
+
|
|
191
|
+
# Zsh — add to ~/.zshrc
|
|
192
|
+
eval "$(bonsai autocomplete zsh)"
|
|
193
|
+
|
|
194
|
+
# Fish
|
|
195
|
+
bonsai autocomplete fish > ~/.config/fish/completions/bonsai.fish
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
To see full setup instructions including the copy-paste snippet for your shell:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
bonsai autocomplete
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Refresh cached completions after upgrading:
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
bonsai autocomplete --refresh-cache
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
183
212
|
## Freshness and Cache Rules
|
|
184
213
|
|
|
185
214
|
Global cache files live in Bonsai's oclif data directory, typically:
|
|
@@ -246,6 +275,30 @@ When run with `--json`, commands return a stable envelope:
|
|
|
246
275
|
|
|
247
276
|
---
|
|
248
277
|
|
|
278
|
+
## Environment Variables
|
|
279
|
+
|
|
280
|
+
| Variable | Effect |
|
|
281
|
+
| --- | --- |
|
|
282
|
+
| `BONSAI_STORAGE` | Override the default storage location per-invocation (`global` or `project`). |
|
|
283
|
+
| `BONSAI_SUMMARY` | Override the summary compression level per-invocation (`conservative`, `balanced`, or `aggressive`). |
|
|
284
|
+
| `NO_COLOR` | Disable all ANSI color output (also respected via `TERM=dumb`). |
|
|
285
|
+
| `NO_UPDATE_NOTIFIER` | Suppress the "update available" notification. |
|
|
286
|
+
| `CI` | Set to `1` or `true` to suppress the update notification in CI environments (detected automatically). |
|
|
287
|
+
| `DEBUG` | Enable debug output for specific Bonsai namespaces, e.g. `DEBUG=bonsai:*`. |
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Exit Codes
|
|
292
|
+
|
|
293
|
+
| Code | Meaning |
|
|
294
|
+
| --- | --- |
|
|
295
|
+
| `0` | Success. |
|
|
296
|
+
| `1` | Runtime failure (network error, fetch failed, file read error). |
|
|
297
|
+
| `2` | Usage error (invalid flag, bad URL, missing required argument). |
|
|
298
|
+
| `5` | Stale content served — the cache entry is past its grace window but `--allow-stale` is set. The returned content is still usable. |
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
249
302
|
## Security and Limits
|
|
250
303
|
|
|
251
304
|
* Fetched HTML is treated as untrusted input.
|
|
@@ -5,7 +5,7 @@ model: 'Auto'
|
|
|
5
5
|
tools: ['vscode/askQuestions', 'execute', 'read', 'search', 'web', 'vscode/memory']
|
|
6
6
|
argument-hint: 'What topic should I research in official docs?'
|
|
7
7
|
metadata:
|
|
8
|
-
version: '3.0.
|
|
8
|
+
version: '3.0.1'
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# Official Docs Researcher Agent
|
|
@@ -24,7 +24,16 @@ Do not research from memory alone. Training-data knowledge does not satisfy this
|
|
|
24
24
|
2. Search the Bonsai cache first:
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
+
# 1. Default search: Checks the local cache across EVERYTHING you've researched,
|
|
28
|
+
# including domains that do not support online search APIs.
|
|
27
29
|
bonsai search "<topic or keywords>"
|
|
30
|
+
|
|
31
|
+
# 2. Remote API search: Use this to quickly find URLs for documentation pages online
|
|
32
|
+
# ONLY if you don't find any information in the local cache.
|
|
33
|
+
bonsai search "<topic or keywords>" --domain <domain>
|
|
34
|
+
# Supported domains: help.salesforce.com, react.dev, vuejs.org, tailwindcss.com, nextjs.org,
|
|
35
|
+
# jestjs.io, cypress.io, vitest.dev, vitepress.dev, angular.dev, redux.js.org,
|
|
36
|
+
# vitejs.dev, fastify.dev, rollupjs.org, vueuse.org
|
|
28
37
|
```
|
|
29
38
|
|
|
30
39
|
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.
|
|
@@ -5,7 +5,7 @@ model: 'Auto'
|
|
|
5
5
|
tools: ['vscode/askQuestions', 'execute', 'read', 'search', 'web', 'vscode/memory']
|
|
6
6
|
argument-hint: 'What Salesforce topic should I research in official docs?'
|
|
7
7
|
metadata:
|
|
8
|
-
version: '1.0.
|
|
8
|
+
version: '1.0.1'
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# Salesforce Docs Researcher Agent
|
|
@@ -24,7 +24,16 @@ Do not research from memory alone. Training-data knowledge does not satisfy this
|
|
|
24
24
|
2. Search the Bonsai cache first:
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
+
# 1. Default search: Checks the local cache across EVERYTHING you've researched,
|
|
28
|
+
# including domains that do not support online search APIs.
|
|
27
29
|
bonsai search "<topic or keywords>"
|
|
30
|
+
|
|
31
|
+
# 2. Remote API search: Use this to quickly find URLs for documentation pages online
|
|
32
|
+
# ONLY if you don't find any information in the local cache.
|
|
33
|
+
bonsai search "<topic or keywords>" --domain <domain>
|
|
34
|
+
# Supported domains: help.salesforce.com, react.dev, vuejs.org, tailwindcss.com, nextjs.org,
|
|
35
|
+
# jestjs.io, cypress.io, vitest.dev, vitepress.dev, angular.dev, redux.js.org,
|
|
36
|
+
# vitejs.dev, fastify.dev, rollupjs.org, vueuse.org
|
|
28
37
|
```
|
|
29
38
|
|
|
30
39
|
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.
|
|
@@ -4,7 +4,7 @@ applyTo: '**'
|
|
|
4
4
|
skills:
|
|
5
5
|
- web-research
|
|
6
6
|
metadata:
|
|
7
|
-
version: '1.0.
|
|
7
|
+
version: '1.0.2'
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Web Research Requirement (Salesforce)
|
|
@@ -24,9 +24,11 @@ metadata:
|
|
|
24
24
|
|
|
25
25
|
## Shared Cache
|
|
26
26
|
|
|
27
|
-
-
|
|
27
|
+
- **Local Cache Search (Default)**: Always search the local cache first using `bonsai search "<query>"`. This checks everything you've researched across all domains, even ones that don't support online search APIs.
|
|
28
|
+
- **Online URL Discovery (Fallback)**: If the local cache comes up empty, use `--domain <domain>` or `--remote <docs-url>` to quickly hit a site's search API and find the official URLs you need to fetch. This is specifically for online URL discovery when local cache fails.
|
|
28
29
|
- If Bonsai is configured for project storage and `.bonsai/research/` is not ignored by git, treat those cache artifacts as intentional shared project files. It is OK to check them in, and agents must not delete them as incidental generated output without an explicit request.
|
|
29
30
|
- Re-running on a recent topic is cheap — research the topic rather than skipping it to "save" a fetch.
|
|
31
|
+
- Supported remote API domains: `help.salesforce.com`, `react.dev`, `vuejs.org`, `tailwindcss.com`, `nextjs.org`, `jestjs.io`, `cypress.io`, `vitest.dev`, `vitepress.dev`, `angular.dev`, `redux.js.org`, `vitejs.dev`, `fastify.dev`, `rollupjs.org`, `vueuse.org`.
|
|
30
32
|
|
|
31
33
|
## Working with Salesforce Sites
|
|
32
34
|
|
|
@@ -4,7 +4,7 @@ applyTo: '**'
|
|
|
4
4
|
skills:
|
|
5
5
|
- web-research
|
|
6
6
|
metadata:
|
|
7
|
-
version: '3.1.
|
|
7
|
+
version: '3.1.2'
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Web Research Requirement
|
|
@@ -24,9 +24,11 @@ metadata:
|
|
|
24
24
|
|
|
25
25
|
## Shared Cache
|
|
26
26
|
|
|
27
|
-
-
|
|
27
|
+
- **Local Cache Search (Default)**: Always search the local cache first using `bonsai search "<query>"`. This checks everything you've researched across all domains, even ones that don't support online search APIs.
|
|
28
|
+
- **Online URL Discovery (Fallback)**: If the local cache comes up empty, use `--domain <domain>` or `--remote <docs-url>` to quickly hit a site's search API and find the official URLs you need to fetch. This is specifically for online URL discovery when local cache fails.
|
|
28
29
|
- If Bonsai is configured for project storage and `.bonsai/research/` is not ignored by git, treat those cache artifacts as intentional shared project files. It is OK to check them in, and agents must not delete them as incidental generated output without an explicit request.
|
|
29
30
|
- Re-running on a recent topic is cheap — research the topic rather than skipping it to "save" a fetch.
|
|
31
|
+
- Supported remote API domains: `help.salesforce.com`, `react.dev`, `vuejs.org`, `tailwindcss.com`, `nextjs.org`, `jestjs.io`, `cypress.io`, `vitest.dev`, `vitepress.dev`, `angular.dev`, `redux.js.org`, `vitejs.dev`, `fastify.dev`, `rollupjs.org`, `vueuse.org`.
|
|
30
32
|
|
|
31
33
|
## When Not to Use
|
|
32
34
|
|
|
@@ -4,7 +4,7 @@ description: 'Bonsai-backed official documentation and web research workflow. Us
|
|
|
4
4
|
license: Forward Proprietary
|
|
5
5
|
compatibility: VS Code 1.x+, GitHub Copilot
|
|
6
6
|
metadata:
|
|
7
|
-
version: '2.1.
|
|
7
|
+
version: '2.1.1'
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Web Research Skill
|
|
@@ -15,10 +15,15 @@ Use Bonsai as the cache-first research path for official documentation and web c
|
|
|
15
15
|
|
|
16
16
|
Before creating, updating, refactoring, scaffolding, or deleting technical content, verify relevant current official documentation in the same task.
|
|
17
17
|
|
|
18
|
-
Search the cache first:
|
|
19
|
-
|
|
20
18
|
```bash
|
|
19
|
+
# 1. Default search: Checks the local cache across EVERYTHING you've researched,
|
|
20
|
+
# including domains that do not support online search APIs.
|
|
21
21
|
bonsai search "<topic or keywords>"
|
|
22
|
+
|
|
23
|
+
# 2. Remote API search: Use this to quickly find URLs for documentation pages online
|
|
24
|
+
# ONLY if you don't find any information in the local cache.
|
|
25
|
+
bonsai search "<topic or keywords>" --domain <domain>
|
|
26
|
+
# Supported domains: help.salesforce.com, react.dev, vuejs.org, tailwindcss.com, nextjs.org, jestjs.io, cypress.io, vitest.dev, vitepress.dev, angular.dev, redux.js.org, vitejs.dev, fastify.dev, rollupjs.org, vueuse.org
|
|
22
27
|
```
|
|
23
28
|
|
|
24
29
|
If the cache misses or does not cover the question, fetch the source through Bonsai:
|
package/bin/cli.mjs
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
import { execute } from '@oclif/core';
|
|
4
4
|
import { dirname } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import updateNotifier from 'update-notifier';
|
|
7
|
+
import { createRequire } from 'node:module';
|
|
8
|
+
|
|
9
|
+
import { normalizeArgv } from '../dist/lib/argv.js';
|
|
10
|
+
import { tryJsonMetaOutput } from '../dist/lib/json-meta.js';
|
|
11
|
+
|
|
12
|
+
const req = createRequire(import.meta.url);
|
|
13
|
+
const pkg = req('../package.json');
|
|
6
14
|
|
|
7
15
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
16
|
|
|
@@ -12,19 +20,28 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
12
20
|
// trying to resolve the bare URL as a command — which crashes with a stack trace. Rewrite
|
|
13
21
|
// process.argv itself so both the run path and the help/error path agree.
|
|
14
22
|
const rawArgv = process.argv.slice(2);
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
process.
|
|
23
|
+
const result = normalizeArgv(rawArgv);
|
|
24
|
+
|
|
25
|
+
if (result.exitWithJson) {
|
|
26
|
+
process.exitCode = result.exitWithJson.exitCode;
|
|
27
|
+
console.log(JSON.stringify(result.exitWithJson.envelope, null, 2));
|
|
28
|
+
process.exit();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const jsonMeta = await tryJsonMetaOutput(result.argv, __dirname + '/../');
|
|
32
|
+
if (jsonMeta) {
|
|
33
|
+
process.exitCode = jsonMeta.exitCode;
|
|
34
|
+
console.log(JSON.stringify(jsonMeta.envelope, null, 2));
|
|
35
|
+
process.exit();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Only notify after the exitWithJson fast-path so the notifier never fires before
|
|
39
|
+
// a JSON envelope exits. Also skip under --json so agent callers never see stderr noise.
|
|
40
|
+
if (!process.argv.includes('--json')) {
|
|
41
|
+
updateNotifier({ pkg }).notify();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
process.argv = [process.argv[0], process.argv[1], ...result.argv];
|
|
28
45
|
|
|
29
46
|
await execute({
|
|
30
47
|
development: false,
|
package/dist/base-command.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Command, Interfaces } from '@oclif/core';
|
|
2
|
+
import { type ResolveResearchTargetOptions, type ResolvedResearchTarget } from './lib/research/resolve-target.js';
|
|
2
3
|
/**
|
|
3
4
|
* Shared base for every Bonsai command. Enables oclif's native `--json` flag,
|
|
4
5
|
* parses args/flags once in `init()` so commands read `this.args`/`this.flags`
|
|
@@ -39,6 +40,8 @@ export declare abstract class BaseCommand<T extends typeof Command> extends Comm
|
|
|
39
40
|
}): Promise<any>;
|
|
40
41
|
/** Command id for the JSON envelope; falls back to the binary name when a command has no id. */
|
|
41
42
|
protected envelopeCommandId(): string;
|
|
43
|
+
/** Resolve a URL against the research cache, exiting with INVALID_URL on normalization failure. */
|
|
44
|
+
protected resolveResearchTargetOrFail(url: string, extra?: Pick<ResolveResearchTargetOptions, 'flagOverride' | 'lookup'>): ResolvedResearchTarget;
|
|
42
45
|
/** Single source of truth for the `--json` envelope shape, shared by success and error output. */
|
|
43
46
|
private envelope;
|
|
44
47
|
/** Wrap a command's return value in the machine-readable envelope emitted under `--json`. */
|
package/dist/base-command.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { Command, Errors } from '@oclif/core';
|
|
1
|
+
import { Command, Errors, toConfiguredId } from '@oclif/core';
|
|
2
2
|
import { invalidEnvOverrideWarnings } from './lib/config/index.js';
|
|
3
|
+
import { buildEnvelope, formatErrorForJson, normalizeCliErrorMessage, stableErrorCodeFrom, } from './lib/envelope.js';
|
|
4
|
+
import { resolveResearchTarget, } from './lib/research/resolve-target.js';
|
|
3
5
|
/**
|
|
4
6
|
* Shared base for every Bonsai command. Enables oclif's native `--json` flag,
|
|
5
7
|
* parses args/flags once in `init()` so commands read `this.args`/`this.flags`
|
|
@@ -53,24 +55,41 @@ export class BaseCommand extends Command {
|
|
|
53
55
|
* the shared resolver so the framework's `??` keeps it, then defer to the default behavior.
|
|
54
56
|
*/
|
|
55
57
|
async catch(err) {
|
|
58
|
+
// Parse failures throw before `this.parse()` sets `parsed`, which makes oclif emit a spurious
|
|
59
|
+
// [UnparsedCommand] warning to stderr even under `--json`. CLIParseError subclasses carry `parse`.
|
|
60
|
+
if (err && typeof err === 'object' && 'parse' in err)
|
|
61
|
+
this.parsed = true;
|
|
56
62
|
process.exitCode = process.exitCode ?? BaseCommand.exitCodeOf(err);
|
|
57
63
|
return super.catch(err);
|
|
58
64
|
}
|
|
59
65
|
/** Command id for the JSON envelope; falls back to the binary name when a command has no id. */
|
|
60
66
|
envelopeCommandId() {
|
|
61
|
-
return this.ctor.id
|
|
67
|
+
return this.ctor.id ? toConfiguredId(this.ctor.id, this.config) : this.config.bin;
|
|
68
|
+
}
|
|
69
|
+
/** Resolve a URL against the research cache, exiting with INVALID_URL on normalization failure. */
|
|
70
|
+
resolveResearchTargetOrFail(url, extra) {
|
|
71
|
+
try {
|
|
72
|
+
return resolveResearchTarget({
|
|
73
|
+
configDir: this.config.configDir,
|
|
74
|
+
cwd: process.cwd(),
|
|
75
|
+
dataDir: this.config.dataDir,
|
|
76
|
+
url,
|
|
77
|
+
...extra,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
this.error(`Invalid URL: ${err.message}`, { exit: 2, code: 'INVALID_URL' });
|
|
82
|
+
}
|
|
62
83
|
}
|
|
63
84
|
/** Single source of truth for the `--json` envelope shape, shared by success and error output. */
|
|
64
85
|
envelope(parts) {
|
|
65
|
-
return {
|
|
66
|
-
schemaVersion: 1,
|
|
86
|
+
return buildEnvelope({
|
|
67
87
|
command: this.envelopeCommandId(),
|
|
68
88
|
ok: parts.ok,
|
|
69
89
|
exitCode: parts.exitCode,
|
|
70
|
-
stdout: '',
|
|
71
90
|
stderr: parts.stderr,
|
|
72
91
|
data: parts.data,
|
|
73
|
-
};
|
|
92
|
+
});
|
|
74
93
|
}
|
|
75
94
|
/** Wrap a command's return value in the machine-readable envelope emitted under `--json`. */
|
|
76
95
|
toSuccessJson(data) {
|
|
@@ -85,6 +104,20 @@ export class BaseCommand extends Command {
|
|
|
85
104
|
toErrorJson(err) {
|
|
86
105
|
const e = err;
|
|
87
106
|
const exitCode = BaseCommand.exitCodeOf(e);
|
|
88
|
-
|
|
107
|
+
const code = stableErrorCodeFrom(e);
|
|
108
|
+
const message = typeof e?.message === 'string' ? normalizeCliErrorMessage(e.message) : undefined;
|
|
109
|
+
const stderr = message || code || e?.suggestions?.length || e?.ref
|
|
110
|
+
? formatErrorForJson({ ...e, message, code })
|
|
111
|
+
: String(err);
|
|
112
|
+
return buildEnvelope({
|
|
113
|
+
command: this.envelopeCommandId(),
|
|
114
|
+
ok: false,
|
|
115
|
+
exitCode,
|
|
116
|
+
stderr,
|
|
117
|
+
data: null,
|
|
118
|
+
code,
|
|
119
|
+
suggestions: e.suggestions?.length ? e.suggestions : undefined,
|
|
120
|
+
ref: e.ref,
|
|
121
|
+
});
|
|
89
122
|
}
|
|
90
123
|
}
|
|
@@ -12,6 +12,8 @@ export declare abstract class ConfigCommand<T extends typeof Command> extends Ba
|
|
|
12
12
|
/** Read scope for get/list: an explicit `--global`/`--local`, else the merged effective view. */
|
|
13
13
|
protected readScope(global?: boolean, local?: boolean): ConfigScope;
|
|
14
14
|
protected assertKnownKey(keyArg: string): asserts keyArg is ConfigKey;
|
|
15
|
+
protected requireConfigKey(key: string | undefined): asserts key is ConfigKey;
|
|
16
|
+
protected validateConfigKeyAndScope(key: string | undefined, global?: boolean, local?: boolean): asserts key is ConfigKey;
|
|
15
17
|
}
|
|
16
18
|
/** Build the `--global`/`--local` scope-flag pair with per-command descriptions. */
|
|
17
19
|
export declare function configScopeFlags(descriptions: {
|
|
@@ -8,7 +8,10 @@ import { isKnownKey, suggestKey, validKeysHint } from '../../lib/config/index.js
|
|
|
8
8
|
export class ConfigCommand extends BaseCommand {
|
|
9
9
|
assertScopeFlagsExclusive(global, local) {
|
|
10
10
|
if (global && local) {
|
|
11
|
-
this.error('--global and --local are mutually exclusive.', {
|
|
11
|
+
this.error('--global and --local are mutually exclusive.', {
|
|
12
|
+
exit: 2,
|
|
13
|
+
code: 'CONFLICTING_FLAGS',
|
|
14
|
+
});
|
|
12
15
|
}
|
|
13
16
|
}
|
|
14
17
|
/** `--local` writes/reads the project file; otherwise the user-level file. */
|
|
@@ -29,9 +32,20 @@ export class ConfigCommand extends BaseCommand {
|
|
|
29
32
|
const hint = suggestion ? ` Did you mean: ${suggestion}?` : '';
|
|
30
33
|
this.error(`Unknown config key: "${keyArg}".${hint} Valid keys: ${validKeysHint()}.`, {
|
|
31
34
|
exit: 2,
|
|
35
|
+
code: 'UNKNOWN_KEY',
|
|
32
36
|
});
|
|
33
37
|
}
|
|
34
38
|
}
|
|
39
|
+
requireConfigKey(key) {
|
|
40
|
+
if (!key) {
|
|
41
|
+
this.error('Missing required argument: key', { exit: 2, code: 'MISSING_ARGUMENT' });
|
|
42
|
+
}
|
|
43
|
+
this.assertKnownKey(key);
|
|
44
|
+
}
|
|
45
|
+
validateConfigKeyAndScope(key, global, local) {
|
|
46
|
+
this.requireConfigKey(key);
|
|
47
|
+
this.assertScopeFlagsExclusive(global, local);
|
|
48
|
+
}
|
|
35
49
|
}
|
|
36
50
|
/** Build the `--global`/`--local` scope-flag pair with per-command descriptions. */
|
|
37
51
|
export function configScopeFlags(descriptions) {
|
|
@@ -8,7 +8,7 @@ export default class ConfigGet extends ConfigCommand<typeof ConfigGet> {
|
|
|
8
8
|
command: string;
|
|
9
9
|
}[];
|
|
10
10
|
static args: {
|
|
11
|
-
key: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
11
|
+
key: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
12
12
|
};
|
|
13
13
|
static flags: {
|
|
14
14
|
global: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -7,16 +7,16 @@ export default class ConfigGet extends ConfigCommand {
|
|
|
7
7
|
static description = `Print a config value. Without --global/--local, shows the merged effective value (flag > env > project > user > default).\n\nValid keys: ${validKeysHint()}.`;
|
|
8
8
|
static examples = [
|
|
9
9
|
{
|
|
10
|
-
description: '
|
|
10
|
+
description: 'get the effective storage mode',
|
|
11
11
|
command: '<%= config.bin %> config get storage',
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
|
-
description: '
|
|
14
|
+
description: 'read only the project-level value',
|
|
15
15
|
command: '<%= config.bin %> config get storage --local',
|
|
16
16
|
},
|
|
17
17
|
];
|
|
18
18
|
static args = {
|
|
19
|
-
key: Args.string({ required:
|
|
19
|
+
key: Args.string({ required: false, description: 'the configuration key to read' }),
|
|
20
20
|
};
|
|
21
21
|
static flags = {
|
|
22
22
|
...configScopeFlags({
|
|
@@ -27,8 +27,7 @@ export default class ConfigGet extends ConfigCommand {
|
|
|
27
27
|
static stdoutIsPrimaryData = true;
|
|
28
28
|
async run() {
|
|
29
29
|
const key = this.args.key;
|
|
30
|
-
this.
|
|
31
|
-
this.assertScopeFlagsExclusive(this.flags.global, this.flags.local);
|
|
30
|
+
this.validateConfigKeyAndScope(key, this.flags.global, this.flags.local);
|
|
32
31
|
const scope = this.readScope(this.flags.global, this.flags.local);
|
|
33
32
|
const value = readScopedConfig(scope, this.config.configDir, process.cwd())[key];
|
|
34
33
|
const meta = KEY_META[key];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseCommand } from '../../base-command.js';
|
|
2
|
+
export default class ConfigIndex extends BaseCommand<typeof ConfigIndex> {
|
|
3
|
+
static id: string;
|
|
4
|
+
static summary: string;
|
|
5
|
+
static description: string;
|
|
6
|
+
static examples: {
|
|
7
|
+
description: string;
|
|
8
|
+
command: string;
|
|
9
|
+
}[];
|
|
10
|
+
static stdoutIsPrimaryData: boolean;
|
|
11
|
+
run(): Promise<unknown>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Help, toConfiguredId } from '@oclif/core';
|
|
2
|
+
import { BaseCommand } from '../../base-command.js';
|
|
3
|
+
export default class ConfigIndex extends BaseCommand {
|
|
4
|
+
static id = 'config';
|
|
5
|
+
static summary = 'Manage research cache storage configuration (global vs project).';
|
|
6
|
+
static description = 'Show config subcommands for reading and writing Bonsai research cache configuration.';
|
|
7
|
+
static examples = [
|
|
8
|
+
{
|
|
9
|
+
description: 'get a configuration value',
|
|
10
|
+
command: '<%= config.bin %> <%= command.id %> get storage',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
description: 'set a configuration value for the current project',
|
|
14
|
+
command: '<%= config.bin %> <%= command.id %> set storage project --local',
|
|
15
|
+
},
|
|
16
|
+
];
|
|
17
|
+
static stdoutIsPrimaryData = true;
|
|
18
|
+
async run() {
|
|
19
|
+
if (!this.jsonEnabled()) {
|
|
20
|
+
await new Help(this.config, this.config.pjson.oclif.helpOptions ?? {}).showHelp(['config']);
|
|
21
|
+
}
|
|
22
|
+
const commands = this.config.commands
|
|
23
|
+
.filter((c) => c.id.startsWith('config:') && !c.hidden)
|
|
24
|
+
.map((c) => ({
|
|
25
|
+
id: toConfiguredId(c.id, this.config),
|
|
26
|
+
summary: c.summary || c.description || '',
|
|
27
|
+
}))
|
|
28
|
+
.sort((a, b) => a.id.localeCompare(b.id));
|
|
29
|
+
return {
|
|
30
|
+
topic: 'config',
|
|
31
|
+
commands,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -6,11 +6,11 @@ export default class ConfigList extends ConfigCommand {
|
|
|
6
6
|
static description = `Show every configuration key with its current value. Use --global/--local to show a single scope.\n\nValid keys: ${validKeysHint()}.`;
|
|
7
7
|
static examples = [
|
|
8
8
|
{
|
|
9
|
-
description: '
|
|
9
|
+
description: 'show all effective config values',
|
|
10
10
|
command: '<%= config.bin %> config list',
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
|
-
description: '
|
|
13
|
+
description: 'show only project-level config',
|
|
14
14
|
command: '<%= config.bin %> config list --local',
|
|
15
15
|
},
|
|
16
16
|
];
|
|
@@ -8,17 +8,17 @@ export default class ConfigSet extends ConfigCommand {
|
|
|
8
8
|
`Valid keys: ${validKeysHint()}. The inline form \`<key>=<value>\` is also accepted.`;
|
|
9
9
|
static examples = [
|
|
10
10
|
{
|
|
11
|
-
description: '
|
|
11
|
+
description: 'store research cache inside the current project',
|
|
12
12
|
command: '<%= config.bin %> config set storage project --local',
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
|
-
description: '
|
|
15
|
+
description: 'set the user-level default using the inline key=value form',
|
|
16
16
|
command: '<%= config.bin %> config set storage=global',
|
|
17
17
|
},
|
|
18
18
|
];
|
|
19
19
|
static args = {
|
|
20
|
-
key: Args.string({ required: false, description: '
|
|
21
|
-
value: Args.string({ required: false, description: '
|
|
20
|
+
key: Args.string({ required: false, description: 'the configuration key to set' }),
|
|
21
|
+
value: Args.string({ required: false, description: 'the value to assign' }),
|
|
22
22
|
};
|
|
23
23
|
static flags = {
|
|
24
24
|
...configScopeFlags({
|
|
@@ -26,26 +26,28 @@ export default class ConfigSet extends ConfigCommand {
|
|
|
26
26
|
local: 'Write to project-level config (.bonsai.json in cwd).',
|
|
27
27
|
}),
|
|
28
28
|
'dry-run': Flags.boolean({
|
|
29
|
-
description: '
|
|
29
|
+
description: 'show what would be written without saving',
|
|
30
30
|
default: false,
|
|
31
31
|
}),
|
|
32
32
|
};
|
|
33
33
|
static stdoutIsPrimaryData = true;
|
|
34
34
|
async run() {
|
|
35
35
|
const { keyArg, valueArg } = splitInlineKeyValue(this.args.key, this.args.value);
|
|
36
|
-
|
|
37
|
-
this.error('Missing required argument: key', { exit: 2 });
|
|
38
|
-
}
|
|
39
|
-
this.assertKnownKey(keyArg);
|
|
40
|
-
this.assertScopeFlagsExclusive(this.flags.global, this.flags.local);
|
|
36
|
+
this.validateConfigKeyAndScope(keyArg, this.flags.global, this.flags.local);
|
|
41
37
|
const meta = KEY_META[keyArg];
|
|
42
38
|
if (valueArg === undefined) {
|
|
43
|
-
this.error(`Missing required argument: value for key "${keyArg}"`, {
|
|
39
|
+
this.error(`Missing required argument: value for key "${keyArg}"`, {
|
|
40
|
+
exit: 2,
|
|
41
|
+
code: 'MISSING_ARGUMENT',
|
|
42
|
+
});
|
|
44
43
|
}
|
|
45
44
|
const parsed = meta.parseValue(valueArg);
|
|
46
45
|
if (!meta.isValid(parsed)) {
|
|
47
46
|
const guidance = meta.values ? `Valid values: ${meta.values.join(', ')}.` : meta.description;
|
|
48
|
-
this.error(`Invalid value "${valueArg}" for "${keyArg}". ${guidance}`, {
|
|
47
|
+
this.error(`Invalid value "${valueArg}" for "${keyArg}". ${guidance}`, {
|
|
48
|
+
exit: 2,
|
|
49
|
+
code: 'INVALID_VALUE',
|
|
50
|
+
});
|
|
49
51
|
}
|
|
50
52
|
const scope = this.writeScope(this.flags.local);
|
|
51
53
|
const formatted = meta.format(parsed);
|
|
@@ -63,6 +65,10 @@ export default class ConfigSet extends ConfigCommand {
|
|
|
63
65
|
if (!configDir) {
|
|
64
66
|
this.error('Could not determine user config directory. Use --local to write project config.', {
|
|
65
67
|
exit: 1,
|
|
68
|
+
code: 'CONFIG_DIR_UNAVAILABLE',
|
|
69
|
+
suggestions: [
|
|
70
|
+
`Write project config instead: ${this.config.bin} config set <key> <value> --local`,
|
|
71
|
+
],
|
|
66
72
|
});
|
|
67
73
|
}
|
|
68
74
|
writeUserConfig(configDir, patch);
|
|
@@ -8,7 +8,7 @@ export default class ConfigUnset extends ConfigCommand<typeof ConfigUnset> {
|
|
|
8
8
|
command: string;
|
|
9
9
|
}[];
|
|
10
10
|
static args: {
|
|
11
|
-
key: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
11
|
+
key: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
12
12
|
};
|
|
13
13
|
static flags: {
|
|
14
14
|
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
@@ -7,16 +7,16 @@ export default class ConfigUnset extends ConfigCommand {
|
|
|
7
7
|
static description = `Delete a key from user-level config (default) or project-level config (--local), restoring the built-in default.\n\nValid keys: ${validKeysHint()}.`;
|
|
8
8
|
static examples = [
|
|
9
9
|
{
|
|
10
|
-
description: '
|
|
10
|
+
description: 'remove the user-level storage setting',
|
|
11
11
|
command: '<%= config.bin %> config unset storage',
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
|
-
description: '
|
|
14
|
+
description: 'remove the project-level storage setting',
|
|
15
15
|
command: '<%= config.bin %> config unset storage --local',
|
|
16
16
|
},
|
|
17
17
|
];
|
|
18
18
|
static args = {
|
|
19
|
-
key: Args.string({ required:
|
|
19
|
+
key: Args.string({ required: false, description: 'the configuration key to remove' }),
|
|
20
20
|
};
|
|
21
21
|
static flags = {
|
|
22
22
|
...configScopeFlags({
|
|
@@ -24,15 +24,14 @@ export default class ConfigUnset extends ConfigCommand {
|
|
|
24
24
|
local: 'Remove from project-level config (.bonsai.json in cwd).',
|
|
25
25
|
}),
|
|
26
26
|
'dry-run': Flags.boolean({
|
|
27
|
-
description: '
|
|
27
|
+
description: 'show what would be removed without saving',
|
|
28
28
|
default: false,
|
|
29
29
|
}),
|
|
30
30
|
};
|
|
31
31
|
static stdoutIsPrimaryData = true;
|
|
32
32
|
async run() {
|
|
33
33
|
const key = this.args.key;
|
|
34
|
-
this.
|
|
35
|
-
this.assertScopeFlagsExclusive(this.flags.global, this.flags.local);
|
|
34
|
+
this.validateConfigKeyAndScope(key, this.flags.global, this.flags.local);
|
|
36
35
|
const scope = this.writeScope(this.flags.local);
|
|
37
36
|
if (this.flags['dry-run']) {
|
|
38
37
|
if (!this.jsonEnabled())
|
|
@@ -48,6 +47,10 @@ export default class ConfigUnset extends ConfigCommand {
|
|
|
48
47
|
if (!configDir) {
|
|
49
48
|
this.error('Could not determine user config directory. Use --local to remove project config.', {
|
|
50
49
|
exit: 1,
|
|
50
|
+
code: 'CONFIG_DIR_UNAVAILABLE',
|
|
51
|
+
suggestions: [
|
|
52
|
+
`Remove project config instead: ${this.config.bin} config unset ${key} --local`,
|
|
53
|
+
],
|
|
51
54
|
});
|
|
52
55
|
}
|
|
53
56
|
writeUserConfig(configDir, patch);
|