@taurgis/bonsai 3.2.2 → 4.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 +2 -2
- package/agents/agents/salesforce-docs-researcher.agent.md +4 -4
- package/bin/cli.mjs +7 -4
- package/dist/base-command.d.ts +37 -0
- package/dist/base-command.js +97 -6
- package/dist/commands/config/base.js +1 -0
- package/dist/commands/config/get.js +2 -2
- package/dist/commands/config/list.js +2 -2
- package/dist/commands/config/set.js +2 -2
- package/dist/commands/config/unset.js +2 -2
- package/dist/commands/fetch.js +4 -0
- package/dist/commands/inspect.js +11 -2
- package/dist/commands/list.d.ts +12 -9
- package/dist/commands/list.js +67 -39
- package/dist/commands/prune.d.ts +2 -0
- package/dist/commands/prune.js +35 -4
- package/dist/commands/status.js +10 -0
- package/dist/hooks/command-not-found/suggest.d.ts +1 -1
- package/dist/hooks/command-not-found/suggest.js +12 -2
- package/dist/lib/argv.d.ts +5 -0
- package/dist/lib/argv.js +27 -5
- package/dist/lib/cli-flag-manifest.d.ts +7 -0
- package/dist/lib/cli-flag-manifest.js +7 -0
- package/dist/lib/cli-presentation.d.ts +6 -4
- package/dist/lib/cli-presentation.js +7 -4
- package/dist/lib/cli-result-types.d.ts +25 -1
- package/dist/lib/config/index.d.ts +2 -2
- package/dist/lib/config/index.js +2 -2
- package/dist/lib/config/io.d.ts +9 -0
- package/dist/lib/config/io.js +71 -21
- package/dist/lib/config/schema.d.ts +2 -0
- package/dist/lib/config/schema.js +7 -0
- package/dist/lib/envelope.js +11 -4
- package/dist/lib/prune-flags.d.ts +2 -0
- package/dist/lib/prune-flags.js +18 -6
- package/dist/lib/research/artifact-index.js +7 -1
- package/dist/lib/research/artifact.js +20 -2
- package/dist/lib/research/browser.d.ts +12 -0
- package/dist/lib/research/browser.js +63 -8
- package/dist/lib/research/cli-io.d.ts +7 -0
- package/dist/lib/research/fetch-command-service.js +80 -23
- package/dist/lib/research/fetcher.js +3 -1
- package/dist/lib/research/import-command-service.js +22 -1
- package/dist/lib/research/metadata-filters.d.ts +18 -0
- package/dist/lib/research/metadata-filters.js +37 -0
- package/dist/lib/research/metadata-flags.d.ts +34 -0
- package/dist/lib/research/metadata-flags.js +45 -0
- package/dist/lib/research/prompt-injection.d.ts +2 -1
- package/dist/lib/research/prompt-injection.js +18 -2
- package/dist/lib/research/resolve-target.d.ts +2 -0
- package/dist/lib/research/resolve-target.js +1 -1
- package/dist/lib/research/storage.d.ts +4 -7
- package/dist/lib/research/storage.js +26 -25
- package/dist/lib/text.d.ts +14 -8
- package/dist/lib/text.js +21 -9
- package/oclif.manifest.json +120 -12
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -141,7 +141,7 @@ npx @taurgis/bonsai inspect <url>
|
|
|
141
141
|
|
|
142
142
|
### `list`
|
|
143
143
|
|
|
144
|
-
List cached artifacts by metadata filters (`--topic`, `--tags`, `--freshness`, `--artifact-type`, `--capture-method`, `--limit`), newest first, without printing content.
|
|
144
|
+
List cached artifacts by metadata filters (`--topic`, `--tags`, `--url`, `--freshness`, `--artifact-type`, `--capture-method`, `--limit`), newest first, without printing content. The default `--json`/`--toon` row is a minimal 4-field shape (`sourceUrls`, `topic`, `freshness`, `tokenEstimate`); pass `--full` for every metadata field. Running `bonsai` with no arguments at all is shorthand for `bonsai list`.
|
|
145
145
|
|
|
146
146
|
```bash
|
|
147
147
|
npx @taurgis/bonsai list [flags]
|
|
@@ -149,7 +149,7 @@ npx @taurgis/bonsai list [flags]
|
|
|
149
149
|
|
|
150
150
|
### `prune`
|
|
151
151
|
|
|
152
|
-
Delete old or inactive cache entries. Requires a filter (`--older-than`, `--inactive`, `--artifact-type`, or `--
|
|
152
|
+
Delete old or inactive cache entries. Requires a filter (`--older-than`, `--inactive`, `--artifact-type`, `--url`, `--topic`, or `--tags`) and exactly one of `--dry-run` or `--yes` (passing both is a usage error). In [read-only mode](#read-only--plan-mode), `--yes` is rejected and the safety check is implicitly satisfied by a preview.
|
|
153
153
|
|
|
154
154
|
```bash
|
|
155
155
|
npx @taurgis/bonsai prune --older-than 90d --dry-run
|
|
@@ -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: '2.
|
|
8
|
+
version: '2.2.0'
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# Salesforce Docs Researcher Agent
|
|
@@ -20,7 +20,7 @@ Do not research from memory alone. Training-data knowledge does not satisfy this
|
|
|
20
20
|
|
|
21
21
|
## Invocation
|
|
22
22
|
|
|
23
|
-
Run Bonsai as `npx @taurgis/bonsai ...`. Add `--
|
|
23
|
+
Run Bonsai as `npx @taurgis/bonsai ...`. Add `--toon` when you need structured output at a lower token cost; `--json` is also available if you specifically need JSON (mutually exclusive with `--toon`).
|
|
24
24
|
|
|
25
25
|
If you are operating in a read-only/plan mode (no filesystem writes allowed), add `--read-only` (alias `--plan`) to every Bonsai call, or export `BONSAI_READ_ONLY=1` (or `BONSAI_PLAN_MODE=1`) once for the session — fetches still run, but nothing is persisted to the local cache or config. See "Read-only / Plan Mode" below.
|
|
26
26
|
|
|
@@ -45,7 +45,7 @@ If you are operating in a read-only/plan mode (no filesystem writes allowed), ad
|
|
|
45
45
|
For structured output:
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
npx @taurgis/bonsai <official-url> --format detailed --
|
|
48
|
+
npx @taurgis/bonsai <official-url> --format detailed --toon
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
## Working with Salesforce Sites
|
|
@@ -109,4 +109,4 @@ Return:
|
|
|
109
109
|
- Cache status or validation timestamp when Bonsai output provides it.
|
|
110
110
|
- Gaps or uncertainty where official docs do not answer the question.
|
|
111
111
|
|
|
112
|
-
Keep stdout clean when using `--json`; warnings and diagnostic notes belong in stderr or in the final prose summary.
|
|
112
|
+
Keep stdout clean when using `--toon`/`--json`; warnings and diagnostic notes belong in stderr or in the final prose summary.
|
package/bin/cli.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import updateNotifier from 'update-notifier';
|
|
|
7
7
|
import { createRequire } from 'node:module';
|
|
8
8
|
|
|
9
9
|
import { normalizeArgv } from '../dist/lib/argv.js';
|
|
10
|
-
import { VALUE_TAKING_FLAG_TOKENS } from '../dist/lib/cli-flag-manifest.js';
|
|
10
|
+
import { VALUE_TAKING_FLAG_TOKENS, KNOWN_COMMAND_ROOT_TOKENS } from '../dist/lib/cli-flag-manifest.js';
|
|
11
11
|
import { exitWithPreflight } from '../dist/lib/cli-emit.js';
|
|
12
12
|
import { tryUnknownHelpOutput } from '../dist/lib/help-preflight.js';
|
|
13
13
|
import { tryJsonMetaOutput } from '../dist/lib/json-meta.js';
|
|
@@ -23,7 +23,10 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
23
23
|
// trying to resolve the bare URL as a command — which crashes with a stack trace. Rewrite
|
|
24
24
|
// process.argv itself so both the run path and the help/error path agree.
|
|
25
25
|
const rawArgv = process.argv.slice(2);
|
|
26
|
-
const result = normalizeArgv(rawArgv, {
|
|
26
|
+
const result = normalizeArgv(rawArgv, {
|
|
27
|
+
valueTakingFlags: VALUE_TAKING_FLAG_TOKENS,
|
|
28
|
+
knownCommandRoots: KNOWN_COMMAND_ROOT_TOKENS,
|
|
29
|
+
});
|
|
27
30
|
const root = __dirname + '/../';
|
|
28
31
|
|
|
29
32
|
if (result.earlyExit) {
|
|
@@ -41,8 +44,8 @@ if (jsonMeta) {
|
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
// Only notify after the earlyExit fast-path so the notifier never fires before
|
|
44
|
-
// a usage-error envelope exits. Also skip under --json so agent callers never see stderr noise.
|
|
45
|
-
if (!process.argv.includes('--json')) {
|
|
47
|
+
// a usage-error envelope exits. Also skip under --json/--toon so agent callers never see stderr noise.
|
|
48
|
+
if (!process.argv.includes('--json') && !process.argv.includes('--toon')) {
|
|
46
49
|
updateNotifier({ pkg }).notify();
|
|
47
50
|
}
|
|
48
51
|
|
package/dist/base-command.d.ts
CHANGED
|
@@ -17,11 +17,24 @@ export declare abstract class BaseCommand<T extends typeof Command> extends Comm
|
|
|
17
17
|
*/
|
|
18
18
|
static baseFlags: {
|
|
19
19
|
'read-only': Interfaces.BooleanFlag<boolean>;
|
|
20
|
+
toon: Interfaces.BooleanFlag<boolean>;
|
|
20
21
|
};
|
|
21
22
|
flags: Interfaces.InferredFlags<T['flags']>;
|
|
22
23
|
args: Interfaces.InferredArgs<T['args']>;
|
|
23
24
|
parsedArgv: string[];
|
|
24
25
|
init(): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Treat `--toon` as machine mode too, so every existing `!this.jsonEnabled()` human-output branch
|
|
28
|
+
* across every command (table rendering, tips, empty-state guidance) is suppressed under `--toon`
|
|
29
|
+
* for free, exactly as it already is under `--json`.
|
|
30
|
+
*/
|
|
31
|
+
jsonEnabled(): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Real `--json` keeps oclif's own colorized-JSON rendering untouched (zero regression risk).
|
|
34
|
+
* `--toon` (and no real `--json`) re-encodes the identical envelope as TOON instead — same data,
|
|
35
|
+
* ~40% fewer tokens for callers who opted in.
|
|
36
|
+
*/
|
|
37
|
+
protected logJson(json: unknown): void;
|
|
25
38
|
/**
|
|
26
39
|
* Always surface warnings on stderr, even under `--json`. oclif silences `warn()` in JSON mode,
|
|
27
40
|
* but our warnings (secret-redirect, stale-serve, prune failures) are
|
|
@@ -29,6 +42,30 @@ export declare abstract class BaseCommand<T extends typeof Command> extends Comm
|
|
|
29
42
|
* JSON envelope, so machine output stays clean.
|
|
30
43
|
*/
|
|
31
44
|
warn(input: string | Error): string | Error;
|
|
45
|
+
/**
|
|
46
|
+
* Sanitize the human-readable error surface before handing off to oclif. Error text throughout
|
|
47
|
+
* this CLI routinely echoes raw user input back (a rejected URL, `--ttl`, an unknown config key)
|
|
48
|
+
* so the message stays actionable, but that value is untrusted per the repo's trust-boundary
|
|
49
|
+
* rules — the same reasoning that already strips ANSI/control bytes from cached content before
|
|
50
|
+
* terminal render (see `sanitizeForTerminal`). Left unsanitized, a value containing raw escape
|
|
51
|
+
* bytes would replay as a terminal-injection attack the moment the rejected command's own error
|
|
52
|
+
* is printed. `--json` is untouched: `JSON.stringify` already escapes control characters, and the
|
|
53
|
+
* raw value there preserves exact input for programmatic callers.
|
|
54
|
+
*/
|
|
55
|
+
error(input: string | Error, options: {
|
|
56
|
+
code?: string;
|
|
57
|
+
exit: false;
|
|
58
|
+
} & Errors.PrettyPrintableError): void;
|
|
59
|
+
error(input: string | Error, options?: {
|
|
60
|
+
code?: string;
|
|
61
|
+
exit?: number;
|
|
62
|
+
} & Errors.PrettyPrintableError): never;
|
|
63
|
+
/**
|
|
64
|
+
* Human-mode-only contextual next-step suggestion after a successful command, mirroring the
|
|
65
|
+
* "Try this:" pattern already used on errors. A no-op under `--json`/`--toon`: the envelope's
|
|
66
|
+
* `data` is self-describing, so machine callers get no prose.
|
|
67
|
+
*/
|
|
68
|
+
protected tip(message: string): void;
|
|
32
69
|
/** Whether read-only/plan mode is active for this invocation (flag OR either env var). */
|
|
33
70
|
protected get readOnly(): boolean;
|
|
34
71
|
/**
|
package/dist/base-command.js
CHANGED
|
@@ -1,11 +1,24 @@
|
|
|
1
|
-
import { Command, Errors, Flags, toConfiguredId } from '@oclif/core';
|
|
2
|
-
import {
|
|
1
|
+
import { Command, Errors, Flags, toConfiguredId, ux } from '@oclif/core';
|
|
2
|
+
import { encode } from '@toon-format/toon';
|
|
3
|
+
import { invalidConfigFileWarnings, invalidEnvOverrideWarnings, resolveReadOnly, } from './lib/config/index.js';
|
|
3
4
|
import { CLI_FLAG_DESCRIPTIONS } from './lib/cli-presentation.js';
|
|
5
|
+
import { formatTip, sanitizeForTerminal } from './lib/text.js';
|
|
4
6
|
import { enrichErrorForDisplay, resolveExitCode, prepareCliError, EXIT_OK, EXIT_STALE_SERVED, } from './lib/cli-error-policy.js';
|
|
5
|
-
import { buildEnvelope, enrichCacheMissEnvelope, enrichRowErrorEnvelope
|
|
7
|
+
import { buildEnvelope, enrichCacheMissEnvelope, enrichRowErrorEnvelope } from './lib/envelope.js';
|
|
6
8
|
import { enrichParseError } from './lib/parse-error-ux.js';
|
|
7
9
|
import { resolveResearchTarget, } from './lib/research/resolve-target.js';
|
|
8
10
|
import { failInvalidUrl } from './lib/research/cli-io.js';
|
|
11
|
+
/**
|
|
12
|
+
* Mirrors oclif's own `--json` argv-scan (respecting a `--` pass-through boundary) so `--toon`
|
|
13
|
+
* gets identical treatment: present in argv, regardless of parse state.
|
|
14
|
+
*/
|
|
15
|
+
function argvHasFlag(argv, flag) {
|
|
16
|
+
const passThroughIndex = argv.indexOf('--');
|
|
17
|
+
const flagIndex = argv.indexOf(flag);
|
|
18
|
+
return passThroughIndex === -1
|
|
19
|
+
? flagIndex !== -1
|
|
20
|
+
: flagIndex !== -1 && flagIndex < passThroughIndex;
|
|
21
|
+
}
|
|
9
22
|
/**
|
|
10
23
|
* Shared base for every Bonsai command. Enables oclif's native `--json` flag,
|
|
11
24
|
* parses args/flags once in `init()` so commands read `this.args`/`this.flags`
|
|
@@ -26,6 +39,10 @@ export class BaseCommand extends Command {
|
|
|
26
39
|
default: false,
|
|
27
40
|
description: CLI_FLAG_DESCRIPTIONS.readOnly,
|
|
28
41
|
}),
|
|
42
|
+
toon: Flags.boolean({
|
|
43
|
+
default: false,
|
|
44
|
+
description: CLI_FLAG_DESCRIPTIONS.toon,
|
|
45
|
+
}),
|
|
29
46
|
};
|
|
30
47
|
flags;
|
|
31
48
|
args;
|
|
@@ -42,11 +59,52 @@ export class BaseCommand extends Command {
|
|
|
42
59
|
this.flags = flags;
|
|
43
60
|
this.args = args;
|
|
44
61
|
this.parsedArgv = argv;
|
|
62
|
+
// --json and --toon both claim the machine-output seat; picking one silently over the other
|
|
63
|
+
// would surprise a caller who set both by mistake, so reject the combination outright. Reuses
|
|
64
|
+
// the existing CONFLICTING_FLAGS code (not a new one) — the catalog already documents it as the
|
|
65
|
+
// one code for "choose one of the mutually exclusive options", regardless of which flags.
|
|
66
|
+
if (this.flags?.['json'] && this.flags?.['toon']) {
|
|
67
|
+
this.error('Cannot combine --json and --toon: pick one output format.', {
|
|
68
|
+
exit: 2,
|
|
69
|
+
code: 'CONFLICTING_FLAGS',
|
|
70
|
+
suggestions: ['Pass only one of --json or --toon'],
|
|
71
|
+
});
|
|
72
|
+
}
|
|
45
73
|
// Surface a set-but-invalid BONSAI_* override once per run. Resolution silently drops such a
|
|
46
74
|
// value, so without this a typo'd env var would take no effect with no signal. Warnings go to
|
|
47
75
|
// stderr (even under --json), so machine output stays clean.
|
|
48
76
|
for (const warning of invalidEnvOverrideWarnings(process.env))
|
|
49
77
|
this.warn(warning);
|
|
78
|
+
// Same signal for a corrupted or hand-edited config file: parsing silently degrades to `{}`
|
|
79
|
+
// (or drops just the offending key), which would otherwise mask the problem entirely.
|
|
80
|
+
for (const warning of invalidConfigFileWarnings(this.config.configDir, process.cwd(), this.config.bin)) {
|
|
81
|
+
this.warn(warning);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Treat `--toon` as machine mode too, so every existing `!this.jsonEnabled()` human-output branch
|
|
86
|
+
* across every command (table rendering, tips, empty-state guidance) is suppressed under `--toon`
|
|
87
|
+
* for free, exactly as it already is under `--json`.
|
|
88
|
+
*/
|
|
89
|
+
jsonEnabled() {
|
|
90
|
+
// Mirrors oclif's own enableJsonFlag guard (every command in this CLI sets it, but the base
|
|
91
|
+
// class's contract shouldn't silently break if a future command ever opts out).
|
|
92
|
+
return (super.jsonEnabled() ||
|
|
93
|
+
(Boolean(this.ctor?.enableJsonFlag) && argvHasFlag(this.argv, '--toon')));
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Real `--json` keeps oclif's own colorized-JSON rendering untouched (zero regression risk).
|
|
97
|
+
* `--toon` (and no real `--json`) re-encodes the identical envelope as TOON instead — same data,
|
|
98
|
+
* ~40% fewer tokens for callers who opted in.
|
|
99
|
+
*/
|
|
100
|
+
logJson(json) {
|
|
101
|
+
if (super.jsonEnabled()) {
|
|
102
|
+
super.logJson(json);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
// this.log() no-ops whenever jsonEnabled() is true (by design, everywhere else in this CLI) —
|
|
106
|
+
// ux.stdout writes directly, matching how oclif's own default logJson bypasses that guard too.
|
|
107
|
+
ux.stdout(encode(json));
|
|
50
108
|
}
|
|
51
109
|
/**
|
|
52
110
|
* Always surface warnings on stderr, even under `--json`. oclif silences `warn()` in JSON mode,
|
|
@@ -58,6 +116,35 @@ export class BaseCommand extends Command {
|
|
|
58
116
|
Errors.warn(input);
|
|
59
117
|
return input;
|
|
60
118
|
}
|
|
119
|
+
error(input, options = {}) {
|
|
120
|
+
if (!this.jsonEnabled()) {
|
|
121
|
+
if (typeof input === 'string')
|
|
122
|
+
input = sanitizeForTerminal(input);
|
|
123
|
+
if (options.suggestions) {
|
|
124
|
+
options = { ...options, suggestions: options.suggestions.map(sanitizeForTerminal) };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// `exit: false` is oclif's non-throwing error render (see `errorRow` on the CliIo port and
|
|
128
|
+
// `mapUrlsAllowingBatchErrors`/prune's per-row failures, all of which route through here for the
|
|
129
|
+
// same terminal-injection sanitization above). Destructure `exit` into its own binding so
|
|
130
|
+
// narrowing it to the `false` literal actually narrows the object passed to `super.error()` into
|
|
131
|
+
// each of oclif's two overloads — narrowing a property read alone (`options.exit === false`)
|
|
132
|
+
// doesn't narrow the enclosing object's type, only a local variable's.
|
|
133
|
+
const { exit, ...rest } = options;
|
|
134
|
+
if (exit === false)
|
|
135
|
+
return super.error(input, { ...rest, exit });
|
|
136
|
+
return super.error(input, { ...rest, exit });
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Human-mode-only contextual next-step suggestion after a successful command, mirroring the
|
|
140
|
+
* "Try this:" pattern already used on errors. A no-op under `--json`/`--toon`: the envelope's
|
|
141
|
+
* `data` is self-describing, so machine callers get no prose.
|
|
142
|
+
*/
|
|
143
|
+
tip(message) {
|
|
144
|
+
if (this.jsonEnabled())
|
|
145
|
+
return;
|
|
146
|
+
this.warn(formatTip(message));
|
|
147
|
+
}
|
|
61
148
|
/** Whether read-only/plan mode is active for this invocation (flag OR either env var). */
|
|
62
149
|
get readOnly() {
|
|
63
150
|
return resolveReadOnly({ flag: Boolean(this.flags?.['read-only']), env: process.env });
|
|
@@ -108,6 +195,7 @@ export class BaseCommand extends Command {
|
|
|
108
195
|
configDir: this.config.configDir,
|
|
109
196
|
cwd: process.cwd(),
|
|
110
197
|
dataDir: this.config.dataDir,
|
|
198
|
+
readOnly: this.readOnly,
|
|
111
199
|
url,
|
|
112
200
|
...extra,
|
|
113
201
|
});
|
|
@@ -131,6 +219,7 @@ export class BaseCommand extends Command {
|
|
|
131
219
|
warn: (msg) => void this.warn(msg),
|
|
132
220
|
log: (msg) => this.log(msg),
|
|
133
221
|
error: (msg, opts) => this.error(msg, opts),
|
|
222
|
+
errorRow: (msg, opts) => this.error(msg, { ...opts, exit: false }),
|
|
134
223
|
};
|
|
135
224
|
}
|
|
136
225
|
/** Single source of truth for the `--json` envelope shape, shared by success and error output. */
|
|
@@ -187,10 +276,12 @@ export class BaseCommand extends Command {
|
|
|
187
276
|
catch (err) {
|
|
188
277
|
if (!batch || !(err instanceof Errors.CLIError))
|
|
189
278
|
throw err;
|
|
190
|
-
//
|
|
191
|
-
//
|
|
279
|
+
// Render with the same "Error:" formatting as a single-URL failure (this.error's exit:false
|
|
280
|
+
// form prints the CLIError's own message/Code/Try this and returns instead of throwing) rather
|
|
281
|
+
// than this.warn, since a row failure still fails the command overall (status/inspect batch
|
|
282
|
+
// exit code, JSON ok:false) and should never read as merely a warning.
|
|
192
283
|
if (!this.jsonEnabled())
|
|
193
|
-
this.
|
|
284
|
+
this.error(err, { exit: false });
|
|
194
285
|
results.push(failureRow(url, err));
|
|
195
286
|
}
|
|
196
287
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Args } from '@oclif/core';
|
|
2
2
|
import { ConfigCommand, configScopeFlags } from './base.js';
|
|
3
|
-
import { formatConfigEntry, resolveConfigEntry,
|
|
3
|
+
import { formatConfigEntry, resolveConfigEntry, keyValuesHint } from '../../lib/config/index.js';
|
|
4
4
|
export default class ConfigGet extends ConfigCommand {
|
|
5
5
|
static id = 'config get';
|
|
6
6
|
static summary = 'Get a research configuration value';
|
|
7
|
-
static description = `Print a config value. Without --global/--local, shows the merged effective value, in precedence order: a per-command override flag (e.g. \`fetch --storage\`) > env var > project file > user file > built-in default.\n\nValid keys: ${
|
|
7
|
+
static description = `Print a config value. Without --global/--local, shows the merged effective value, in precedence order: a per-command override flag (e.g. \`fetch --storage\`) > env var > project file > user file > built-in default.\n\nValid keys: ${keyValuesHint()}.`;
|
|
8
8
|
static examples = [
|
|
9
9
|
{
|
|
10
10
|
description: 'get the effective storage mode',
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { ConfigCommand, configScopeFlags } from './base.js';
|
|
2
|
-
import { formatConfigEntry, resolveConfigEntries,
|
|
2
|
+
import { formatConfigEntry, resolveConfigEntries, keyValuesHint } from '../../lib/config/index.js';
|
|
3
3
|
export default class ConfigList extends ConfigCommand {
|
|
4
4
|
static id = 'config list';
|
|
5
5
|
static summary = 'List research configuration values';
|
|
6
|
-
static description = `Show every configuration key with its current value. Use --global/--local to show a single scope.\n\nValid keys: ${
|
|
6
|
+
static description = `Show every configuration key with its current value. Use --global/--local to show a single scope.\n\nValid keys: ${keyValuesHint()}.`;
|
|
7
7
|
static examples = [
|
|
8
8
|
{
|
|
9
9
|
description: 'show all effective config values',
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import { ConfigCommand, configScopeFlags } from './base.js';
|
|
3
|
-
import { KEY_META,
|
|
3
|
+
import { KEY_META, keyValuesHint } from '../../lib/config/index.js';
|
|
4
4
|
import { persistConfigPatch } from '../../lib/config-persist.js';
|
|
5
5
|
export default class ConfigSet extends ConfigCommand {
|
|
6
6
|
static id = 'config set';
|
|
7
7
|
static summary = 'Set a research configuration key';
|
|
8
8
|
static description = 'Persist a configuration value. Writes user-level config by default; pass --local for project-level config (.bonsai.json in cwd).\n\n' +
|
|
9
|
-
`Valid keys: ${
|
|
9
|
+
`Valid keys: ${keyValuesHint()}. The inline form \`<key>=<value>\` is also accepted.`;
|
|
10
10
|
static examples = [
|
|
11
11
|
{
|
|
12
12
|
description: 'store research cache in the current project',
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import { ConfigCommand, configScopeFlags } from './base.js';
|
|
3
|
-
import {
|
|
3
|
+
import { keyValuesHint } from '../../lib/config/index.js';
|
|
4
4
|
import { persistConfigPatch } from '../../lib/config-persist.js';
|
|
5
5
|
export default class ConfigUnset extends ConfigCommand {
|
|
6
6
|
static id = 'config unset';
|
|
7
7
|
static summary = 'Remove a research configuration key';
|
|
8
|
-
static description = `Delete a key from user-level config (default) or project-level config (--local), restoring the built-in default.\n\nValid keys: ${
|
|
8
|
+
static description = `Delete a key from user-level config (default) or project-level config (--local), restoring the built-in default.\n\nValid keys: ${keyValuesHint()}.`;
|
|
9
9
|
static examples = [
|
|
10
10
|
{
|
|
11
11
|
description: 'remove the user-level storage setting',
|
package/dist/commands/fetch.js
CHANGED
|
@@ -18,6 +18,10 @@ export default class FetchCommand extends BaseCommand {
|
|
|
18
18
|
description: 'cache a volatile page with compressed JSON output',
|
|
19
19
|
command: '<%= config.bin %> https://news.ycombinator.com/ --format compressed --ttl 2h --json',
|
|
20
20
|
},
|
|
21
|
+
{
|
|
22
|
+
description: 'fetch multiple URLs in one batch (space-separated)',
|
|
23
|
+
command: '<%= config.bin %> https://docs.nestjs.com/ https://docs.nestjs.com/first-steps',
|
|
24
|
+
},
|
|
21
25
|
];
|
|
22
26
|
static strict = false;
|
|
23
27
|
static args = {
|
package/dist/commands/inspect.js
CHANGED
|
@@ -6,6 +6,7 @@ import { getArtifactPath, scanCacheDirs } from '../lib/research/storage.js';
|
|
|
6
6
|
import { artifactMatchesUrlFilter } from '../lib/research/url.js';
|
|
7
7
|
import { colors } from '../lib/color.js';
|
|
8
8
|
import { formatHumanField } from '../lib/cli-presentation.js';
|
|
9
|
+
import { sanitizeForTerminal } from '../lib/text.js';
|
|
9
10
|
/** Inspect a cached artifact and its section children. */
|
|
10
11
|
export default class ResearchInspect extends BaseCommand {
|
|
11
12
|
static id = 'inspect';
|
|
@@ -20,6 +21,10 @@ export default class ResearchInspect extends BaseCommand {
|
|
|
20
21
|
description: 'inspect cached metadata as JSON',
|
|
21
22
|
command: '<%= config.bin %> <%= command.id %> https://example.com/docs --json',
|
|
22
23
|
},
|
|
24
|
+
{
|
|
25
|
+
description: 'inspect multiple URLs in one batch (space-separated)',
|
|
26
|
+
command: '<%= config.bin %> <%= command.id %> https://example.com/docs https://example.com/api',
|
|
27
|
+
},
|
|
23
28
|
];
|
|
24
29
|
static strict = false;
|
|
25
30
|
static args = {
|
|
@@ -110,6 +115,7 @@ export default class ResearchInspect extends BaseCommand {
|
|
|
110
115
|
this.logMetadata(cached.metadata);
|
|
111
116
|
if (sections.length)
|
|
112
117
|
this.logSections(sections);
|
|
118
|
+
this.tip(`${this.config.bin} status ${normalizedUrl} to check freshness.`);
|
|
113
119
|
const sep = batchSeparator(this.parsedArgv.length > 1);
|
|
114
120
|
if (sep)
|
|
115
121
|
this.log(sep);
|
|
@@ -130,14 +136,17 @@ export default class ResearchInspect extends BaseCommand {
|
|
|
130
136
|
this.log(formatHumanField(key, colors.bold(JSON.stringify(val))));
|
|
131
137
|
}
|
|
132
138
|
else {
|
|
133
|
-
|
|
139
|
+
const display = typeof val === 'string' ? sanitizeForTerminal(val) : String(val);
|
|
140
|
+
this.log(formatHumanField(key, colors.bold(display)));
|
|
134
141
|
}
|
|
135
142
|
}
|
|
136
143
|
}
|
|
137
144
|
logSections(sections) {
|
|
138
145
|
this.log(colors.cyan(`--- Sections (${sections.length}) ---`));
|
|
139
146
|
for (const s of sections) {
|
|
140
|
-
|
|
147
|
+
const headingPath = sanitizeForTerminal(s.headingPath || '');
|
|
148
|
+
const anchor = sanitizeForTerminal(s.anchor || '');
|
|
149
|
+
this.log(`${colors.cyan(headingPath)} [${colors.yellow(anchor)}] (${colors.magenta(String(s.tokenEstimate.detailed || 0))} tokens) ${colors.gray(s.cacheKey)}`);
|
|
141
150
|
}
|
|
142
151
|
}
|
|
143
152
|
// Section children link back via parent_cache_key; list the active ones for this page (T-22).
|
package/dist/commands/list.d.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { BaseCommand } from '../base-command.js';
|
|
2
|
-
import type { ListRow } from '../lib/cli-result-types.js';
|
|
2
|
+
import type { ListRow, ListRowMinimal } from '../lib/cli-result-types.js';
|
|
3
3
|
/** List cached research artifacts by metadata filters. */
|
|
4
4
|
export default class ResearchList extends BaseCommand<typeof ResearchList> {
|
|
5
5
|
static id: string;
|
|
6
6
|
static summary: string;
|
|
7
7
|
static description: string;
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* Aggregate counts (and an explicit empty/truncation signal) attached to the success envelope so
|
|
10
|
+
* agents get a cache-wide summary without a separate round trip (AXI principles: pre-computed
|
|
11
|
+
* aggregates, definitive empty states). Always set by `run()` before it returns.
|
|
11
12
|
*/
|
|
12
|
-
private
|
|
13
|
+
private jsonSummary;
|
|
13
14
|
static examples: {
|
|
14
15
|
description: string;
|
|
15
16
|
command: string;
|
|
@@ -22,17 +23,19 @@ export default class ResearchList extends BaseCommand<typeof ResearchList> {
|
|
|
22
23
|
'artifact-type': import("@oclif/core/interfaces").OptionFlag<"source" | "research_note" | "index" | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
23
24
|
'capture-method': import("@oclif/core/interfaces").OptionFlag<"static_fetch" | "browser_fallback" | "agent_supplied" | "route_markdown" | "github_source" | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
24
25
|
limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
26
|
+
full: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
25
27
|
};
|
|
26
28
|
static stdoutIsPrimaryData: boolean;
|
|
27
|
-
private matchesTopic;
|
|
28
|
-
private matchesTags;
|
|
29
29
|
private matchesFilters;
|
|
30
30
|
private scanCacheDirForList;
|
|
31
31
|
private hasActiveFilters;
|
|
32
32
|
private logListResults;
|
|
33
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Empty-cache / no-match tip (human mode only). Under `--json`/`--toon`, `data: []` is paired with
|
|
35
|
+
* `summary.empty: true` instead of prose — see {@link toSuccessJson}.
|
|
36
|
+
*/
|
|
34
37
|
private emitEmptyListGuidance;
|
|
35
|
-
run(): Promise<ListRow[]>;
|
|
36
|
-
/** Attach `
|
|
38
|
+
run(): Promise<ListRow[] | ListRowMinimal[]>;
|
|
39
|
+
/** Attach the aggregate `summary` computed in `run()` (see {@link ListSummary}). */
|
|
37
40
|
protected toSuccessJson(data: unknown): Record<string, unknown>;
|
|
38
41
|
}
|