@taurgis/bonsai 1.0.1 → 1.0.3
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/agents/instructions/web-research.instructions.md +21 -8
- package/dist/base-command.d.ts +23 -9
- package/dist/base-command.js +54 -46
- package/dist/commands/config/get.d.ts +1 -2
- package/dist/commands/config/get.js +2 -8
- package/dist/commands/config/list.d.ts +1 -2
- package/dist/commands/config/list.js +2 -7
- package/dist/commands/config/set.d.ts +1 -2
- package/dist/commands/config/set.js +3 -9
- package/dist/commands/config/unset.d.ts +1 -2
- package/dist/commands/config/unset.js +3 -9
- package/dist/commands/fetch.d.ts +1 -2
- package/dist/commands/fetch.js +2 -8
- package/dist/commands/import.d.ts +1 -2
- package/dist/commands/import.js +2 -8
- package/dist/commands/inspect.d.ts +1 -2
- package/dist/commands/inspect.js +2 -7
- package/dist/commands/list.d.ts +1 -2
- package/dist/commands/list.js +2 -7
- package/dist/commands/prune.d.ts +1 -2
- package/dist/commands/prune.js +3 -8
- package/dist/commands/search.d.ts +1 -2
- package/dist/commands/search.js +3 -9
- package/dist/commands/status.d.ts +1 -2
- package/dist/commands/status.js +2 -8
- package/dist/lib/research/docs/validate.js +3 -0
- package/dist/lib/research/revalidate.js +30 -0
- package/dist/lib/research/storage.js +1 -1
- package/oclif.manifest.json +30 -26
- package/package.json +2 -1
|
@@ -1,19 +1,31 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: '
|
|
2
|
+
description: 'Verify current official docs before technical repo changes — inline via the web-research skill for minor research, via the Official Docs Researcher subagent for extensive research.'
|
|
3
3
|
applyTo: '**'
|
|
4
4
|
skills:
|
|
5
5
|
- web-research
|
|
6
6
|
metadata:
|
|
7
|
-
version: '3.
|
|
7
|
+
version: '3.1.0'
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# Web Research Requirement
|
|
11
11
|
|
|
12
12
|
## Mandatory Pre-Step
|
|
13
13
|
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
14
|
+
- Verify the relevant current official documentation **in the same task** before creating, updating, refactoring, scaffolding, or deleting technical content.
|
|
15
|
+
- Apply what you find — cite official source URLs when the change relies on platform behavior or standards.
|
|
16
|
+
- Research is the requirement; how you run it scales with scope (below). Training-data knowledge never satisfies this — only docs fetched in the current task do.
|
|
17
|
+
|
|
18
|
+
## Choose How to Research by Scope
|
|
19
|
+
|
|
20
|
+
- **Minor research — run it inline.** For a single known page, one platform, or a quick flag/API/version check, the main agent invokes the **web-research** skill directly. A separate subagent is not required.
|
|
21
|
+
- **Extensive research — delegate to a subagent.** For multiple sources or platforms, unfamiliar territory, broad audits, or anything that produces verbose output you do not want in the main context, run the **Official Docs Researcher** subagent and apply its findings.
|
|
22
|
+
- The main agent may always use the **web-research** skill directly — choose the subagent to isolate large research, not because inline research is disallowed.
|
|
23
|
+
- When unsure, start inline and escalate to the subagent once the work spans several sources.
|
|
24
|
+
|
|
25
|
+
## Shared Cache
|
|
26
|
+
|
|
27
|
+
- Both paths use the same freshness-tiered Bonsai cache (data directory or project-local `.bonsai/research/`): it reuses fresh notes, cheaply revalidates stale ones, and re-fetches only on a miss.
|
|
28
|
+
- Re-running on a recent topic is cheap — research the topic rather than skipping it to "save" a fetch.
|
|
17
29
|
|
|
18
30
|
## When Not to Use
|
|
19
31
|
|
|
@@ -23,7 +35,8 @@ metadata:
|
|
|
23
35
|
|
|
24
36
|
## Examples
|
|
25
37
|
|
|
26
|
-
- ✅
|
|
27
|
-
- ✅
|
|
38
|
+
- ✅ Confirming one platform's current flag or frontmatter field before a small edit → run the web-research skill inline.
|
|
39
|
+
- ✅ Comparing conventions across several platforms, or auditing/refactoring source broadly → delegate to the Official Docs Researcher subagent.
|
|
40
|
+
- ✅ Before editing instructions, prompts, agents, skills, workflows, or docs — reading source code does not replace checking current platform guidance.
|
|
28
41
|
- ✅ Include official source URLs when the change references platform behavior or standards.
|
|
29
|
-
- ❌ Don't modify technical content without
|
|
42
|
+
- ❌ Don't modify technical content without researching first, and don't treat cached or training knowledge as equivalent — official guidance evolves and must be verified per task.
|
package/dist/base-command.d.ts
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
import { Command, Interfaces } from '@oclif/core';
|
|
2
|
+
/**
|
|
3
|
+
* Shared base for every Bonsai command. Enables oclif's native `--json` flag,
|
|
4
|
+
* parses args/flags once in `init()` so commands read `this.args`/`this.flags`
|
|
5
|
+
* directly in `run()`, and wraps `--json` output in the Bonsai envelope via the
|
|
6
|
+
* framework's `toSuccessJson`/`toErrorJson` hooks.
|
|
7
|
+
*/
|
|
2
8
|
export declare abstract class BaseCommand<T extends typeof Command> extends Command {
|
|
3
|
-
static
|
|
4
|
-
|
|
5
|
-
json: Interfaces.BooleanFlag<boolean>;
|
|
6
|
-
};
|
|
7
|
-
flags: Interfaces.InferredFlags<T['flags'] & typeof BaseCommand.baseFlags>;
|
|
9
|
+
static enableJsonFlag: boolean;
|
|
10
|
+
flags: Interfaces.InferredFlags<T['flags']>;
|
|
8
11
|
args: Interfaces.InferredArgs<T['args']>;
|
|
9
|
-
|
|
12
|
+
init(): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Always surface warnings on stderr, even under `--json`. oclif silences `warn()` in JSON mode,
|
|
15
|
+
* but our warnings (secret-redirect, stale-serve, remote-search fallback, prune failures) are
|
|
16
|
+
* security- and freshness-relevant side effects users must see. stderr never pollutes the stdout
|
|
17
|
+
* JSON envelope, so machine output stays clean.
|
|
18
|
+
*/
|
|
19
|
+
warn(input: string | Error): string | Error;
|
|
20
|
+
/** Command id for the JSON envelope; falls back to the binary name when a command has no id. */
|
|
10
21
|
protected envelopeCommandId(): string;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
22
|
+
/** Single source of truth for the `--json` envelope shape, shared by success and error output. */
|
|
23
|
+
private envelope;
|
|
24
|
+
/** Wrap a command's return value in the machine-readable envelope emitted under `--json`. */
|
|
25
|
+
protected toSuccessJson(data: unknown): Record<string, unknown>;
|
|
26
|
+
/** Mirror the success envelope for failures so JSON consumers get one consistent shape. */
|
|
27
|
+
protected toErrorJson(err: unknown): Record<string, unknown>;
|
|
14
28
|
}
|
package/dist/base-command.js
CHANGED
|
@@ -1,56 +1,64 @@
|
|
|
1
|
-
import { Command,
|
|
1
|
+
import { Command, Errors } from '@oclif/core';
|
|
2
|
+
/**
|
|
3
|
+
* Shared base for every Bonsai command. Enables oclif's native `--json` flag,
|
|
4
|
+
* parses args/flags once in `init()` so commands read `this.args`/`this.flags`
|
|
5
|
+
* directly in `run()`, and wraps `--json` output in the Bonsai envelope via the
|
|
6
|
+
* framework's `toSuccessJson`/`toErrorJson` hooks.
|
|
7
|
+
*/
|
|
2
8
|
export class BaseCommand extends Command {
|
|
3
|
-
static
|
|
4
|
-
static baseFlags = {
|
|
5
|
-
json: Flags.boolean({
|
|
6
|
-
description: 'Return the output as a machine-readable JSON object.',
|
|
7
|
-
}),
|
|
8
|
-
};
|
|
9
|
+
static enableJsonFlag = true;
|
|
9
10
|
flags;
|
|
10
11
|
args;
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
async init() {
|
|
13
|
+
await super.init();
|
|
14
|
+
const { args, flags } = await this.parse({
|
|
15
|
+
flags: this.ctor.flags,
|
|
16
|
+
enableJsonFlag: this.ctor.enableJsonFlag,
|
|
17
|
+
args: this.ctor.args,
|
|
18
|
+
strict: this.ctor.strict,
|
|
19
|
+
});
|
|
20
|
+
this.flags = flags;
|
|
21
|
+
this.args = args;
|
|
13
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Always surface warnings on stderr, even under `--json`. oclif silences `warn()` in JSON mode,
|
|
25
|
+
* but our warnings (secret-redirect, stale-serve, remote-search fallback, prune failures) are
|
|
26
|
+
* security- and freshness-relevant side effects users must see. stderr never pollutes the stdout
|
|
27
|
+
* JSON envelope, so machine output stays clean.
|
|
28
|
+
*/
|
|
29
|
+
warn(input) {
|
|
30
|
+
Errors.warn(input);
|
|
31
|
+
return input;
|
|
32
|
+
}
|
|
33
|
+
/** Command id for the JSON envelope; falls back to the binary name when a command has no id. */
|
|
14
34
|
envelopeCommandId() {
|
|
15
35
|
return this.ctor.id || this.config.bin;
|
|
16
36
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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();
|
|
37
|
+
/** Single source of truth for the `--json` envelope shape, shared by success and error output. */
|
|
38
|
+
envelope(parts) {
|
|
39
|
+
return {
|
|
40
|
+
schemaVersion: 1,
|
|
41
|
+
command: this.envelopeCommandId(),
|
|
42
|
+
ok: parts.ok,
|
|
43
|
+
exitCode: parts.exitCode,
|
|
44
|
+
stdout: '',
|
|
45
|
+
stderr: parts.stderr,
|
|
46
|
+
data: parts.data,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Wrap a command's return value in the machine-readable envelope emitted under `--json`. */
|
|
50
|
+
toSuccessJson(data) {
|
|
51
|
+
// ponytail: the stale-serve path signals "exit 5" by setting process.exitCode inside run()
|
|
52
|
+
// (e.g. fetch's handleStaleRevalidationResult); this reads it back to mark the envelope. If that
|
|
53
|
+
// signalling moves off process.exitCode, update here too. Number() normalizes Node's string codes.
|
|
54
|
+
const exitCode = Number(process.exitCode ?? 0);
|
|
55
|
+
// Exit 5 means "served stale" — a successful, usable result, so it reports ok.
|
|
56
|
+
return this.envelope({ ok: exitCode === 0 || exitCode === 5, exitCode, stderr: '', data });
|
|
52
57
|
}
|
|
53
|
-
|
|
54
|
-
|
|
58
|
+
/** Mirror the success envelope for failures so JSON consumers get one consistent shape. */
|
|
59
|
+
toErrorJson(err) {
|
|
60
|
+
const e = err;
|
|
61
|
+
const exitCode = e?.oclif?.exit ?? e?.exitCode ?? 1;
|
|
62
|
+
return this.envelope({ ok: false, exitCode, stderr: e?.message ?? String(err), data: null });
|
|
55
63
|
}
|
|
56
64
|
}
|
|
@@ -15,6 +15,5 @@ export default class ConfigGet extends ConfigCommand<typeof ConfigGet> {
|
|
|
15
15
|
local: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
16
|
};
|
|
17
17
|
static stdoutIsPrimaryData: boolean;
|
|
18
|
-
|
|
19
|
-
execute(): Promise<unknown>;
|
|
18
|
+
run(): Promise<unknown>;
|
|
20
19
|
}
|
|
@@ -25,13 +25,7 @@ export default class ConfigGet extends ConfigCommand {
|
|
|
25
25
|
}),
|
|
26
26
|
};
|
|
27
27
|
static stdoutIsPrimaryData = true;
|
|
28
|
-
async
|
|
29
|
-
await super.init();
|
|
30
|
-
const { args, flags } = await this.parse(ConfigGet);
|
|
31
|
-
this.args = args;
|
|
32
|
-
this.flags = flags;
|
|
33
|
-
}
|
|
34
|
-
async execute() {
|
|
28
|
+
async run() {
|
|
35
29
|
const key = this.args.key;
|
|
36
30
|
this.assertKnownKey(key);
|
|
37
31
|
this.assertScopeFlagsExclusive(this.flags.global, this.flags.local);
|
|
@@ -41,7 +35,7 @@ export default class ConfigGet extends ConfigCommand {
|
|
|
41
35
|
const displayValue = value !== undefined ? value : BUILT_IN_DEFAULTS[key];
|
|
42
36
|
const formatted = meta.format(displayValue);
|
|
43
37
|
const suffix = value === undefined ? ' (not configured)' : '';
|
|
44
|
-
if (!this.
|
|
38
|
+
if (!this.jsonEnabled())
|
|
45
39
|
this.log(formatted + suffix);
|
|
46
40
|
return { key, value: displayValue };
|
|
47
41
|
}
|
|
@@ -12,6 +12,5 @@ export default class ConfigList extends ConfigCommand<typeof ConfigList> {
|
|
|
12
12
|
local: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
13
|
};
|
|
14
14
|
static stdoutIsPrimaryData: boolean;
|
|
15
|
-
|
|
16
|
-
execute(): Promise<unknown>;
|
|
15
|
+
run(): Promise<unknown>;
|
|
17
16
|
}
|
|
@@ -21,16 +21,11 @@ export default class ConfigList extends ConfigCommand {
|
|
|
21
21
|
}),
|
|
22
22
|
};
|
|
23
23
|
static stdoutIsPrimaryData = true;
|
|
24
|
-
async
|
|
25
|
-
await super.init();
|
|
26
|
-
const { flags } = await this.parse(ConfigList);
|
|
27
|
-
this.flags = flags;
|
|
28
|
-
}
|
|
29
|
-
async execute() {
|
|
24
|
+
async run() {
|
|
30
25
|
this.assertScopeFlagsExclusive(this.flags.global, this.flags.local);
|
|
31
26
|
const scope = this.readScope(this.flags.global, this.flags.local);
|
|
32
27
|
const scoped = readScopedConfig(scope, this.config.configDir, process.cwd());
|
|
33
|
-
if (!this.
|
|
28
|
+
if (!this.jsonEnabled()) {
|
|
34
29
|
const width = Math.max(...ALL_KEYS.map((k) => k.length)) + 2;
|
|
35
30
|
for (const key of ALL_KEYS) {
|
|
36
31
|
const raw = scoped[key];
|
|
@@ -17,6 +17,5 @@ export default class ConfigSet extends ConfigCommand<typeof ConfigSet> {
|
|
|
17
17
|
local: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
18
18
|
};
|
|
19
19
|
static stdoutIsPrimaryData: boolean;
|
|
20
|
-
|
|
21
|
-
execute(): Promise<unknown>;
|
|
20
|
+
run(): Promise<unknown>;
|
|
22
21
|
}
|
|
@@ -31,13 +31,7 @@ export default class ConfigSet extends ConfigCommand {
|
|
|
31
31
|
}),
|
|
32
32
|
};
|
|
33
33
|
static stdoutIsPrimaryData = true;
|
|
34
|
-
async
|
|
35
|
-
await super.init();
|
|
36
|
-
const { args, flags } = await this.parse(ConfigSet);
|
|
37
|
-
this.args = args;
|
|
38
|
-
this.flags = flags;
|
|
39
|
-
}
|
|
40
|
-
async execute() {
|
|
34
|
+
async run() {
|
|
41
35
|
const { keyArg, valueArg } = splitInlineKeyValue(this.args.key, this.args.value);
|
|
42
36
|
if (!keyArg) {
|
|
43
37
|
this.error('Missing required argument: key', { exit: 2 });
|
|
@@ -56,7 +50,7 @@ export default class ConfigSet extends ConfigCommand {
|
|
|
56
50
|
const scope = this.writeScope(this.flags.local);
|
|
57
51
|
const formatted = meta.format(parsed);
|
|
58
52
|
if (this.flags['dry-run']) {
|
|
59
|
-
if (!this.
|
|
53
|
+
if (!this.jsonEnabled())
|
|
60
54
|
this.log(`[dry-run] Would set ${keyArg} = ${formatted} (${scope})`);
|
|
61
55
|
return { key: keyArg, value: parsed, scope, dryRun: true };
|
|
62
56
|
}
|
|
@@ -73,7 +67,7 @@ export default class ConfigSet extends ConfigCommand {
|
|
|
73
67
|
}
|
|
74
68
|
writeUserConfig(configDir, patch);
|
|
75
69
|
}
|
|
76
|
-
if (!this.
|
|
70
|
+
if (!this.jsonEnabled())
|
|
77
71
|
this.log(`Set ${keyArg} = ${formatted} (${scope})`);
|
|
78
72
|
return { key: keyArg, value: parsed, scope, dryRun: false };
|
|
79
73
|
}
|
|
@@ -16,6 +16,5 @@ export default class ConfigUnset extends ConfigCommand<typeof ConfigUnset> {
|
|
|
16
16
|
local: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
17
|
};
|
|
18
18
|
static stdoutIsPrimaryData: boolean;
|
|
19
|
-
|
|
20
|
-
execute(): Promise<unknown>;
|
|
19
|
+
run(): Promise<unknown>;
|
|
21
20
|
}
|
|
@@ -29,19 +29,13 @@ export default class ConfigUnset extends ConfigCommand {
|
|
|
29
29
|
}),
|
|
30
30
|
};
|
|
31
31
|
static stdoutIsPrimaryData = true;
|
|
32
|
-
async
|
|
33
|
-
await super.init();
|
|
34
|
-
const { args, flags } = await this.parse(ConfigUnset);
|
|
35
|
-
this.args = args;
|
|
36
|
-
this.flags = flags;
|
|
37
|
-
}
|
|
38
|
-
async execute() {
|
|
32
|
+
async run() {
|
|
39
33
|
const key = this.args.key;
|
|
40
34
|
this.assertKnownKey(key);
|
|
41
35
|
this.assertScopeFlagsExclusive(this.flags.global, this.flags.local);
|
|
42
36
|
const scope = this.writeScope(this.flags.local);
|
|
43
37
|
if (this.flags['dry-run']) {
|
|
44
|
-
if (!this.
|
|
38
|
+
if (!this.jsonEnabled())
|
|
45
39
|
this.log(`[dry-run] Would unset ${key} (${scope})`);
|
|
46
40
|
return { key, scope, dryRun: true };
|
|
47
41
|
}
|
|
@@ -58,7 +52,7 @@ export default class ConfigUnset extends ConfigCommand {
|
|
|
58
52
|
}
|
|
59
53
|
writeUserConfig(configDir, patch);
|
|
60
54
|
}
|
|
61
|
-
if (!this.
|
|
55
|
+
if (!this.jsonEnabled())
|
|
62
56
|
this.log(`Unset ${key} (${scope})`);
|
|
63
57
|
return { key, scope, dryRun: false };
|
|
64
58
|
}
|
package/dist/commands/fetch.d.ts
CHANGED
|
@@ -26,7 +26,6 @@ export default class FetchCommand extends BaseCommand<typeof FetchCommand> {
|
|
|
26
26
|
};
|
|
27
27
|
static stdoutIsPrimaryData: boolean;
|
|
28
28
|
protected envelopeCommandId(): string;
|
|
29
|
-
init(): Promise<void>;
|
|
30
29
|
private executeCacheHit;
|
|
31
30
|
private executeCacheMiss;
|
|
32
31
|
private buildResultData;
|
|
@@ -34,5 +33,5 @@ export default class FetchCommand extends BaseCommand<typeof FetchCommand> {
|
|
|
34
33
|
private resolveArtifact;
|
|
35
34
|
private persistFreshArtifact;
|
|
36
35
|
private persistSectionsIfFresh;
|
|
37
|
-
|
|
36
|
+
run(): Promise<unknown>;
|
|
38
37
|
}
|
package/dist/commands/fetch.js
CHANGED
|
@@ -92,12 +92,6 @@ export default class FetchCommand extends BaseCommand {
|
|
|
92
92
|
envelopeCommandId() {
|
|
93
93
|
return this.config.bin;
|
|
94
94
|
}
|
|
95
|
-
async init() {
|
|
96
|
-
await super.init();
|
|
97
|
-
const { args, flags } = await this.parse(FetchCommand);
|
|
98
|
-
this.args = args;
|
|
99
|
-
this.flags = flags;
|
|
100
|
-
}
|
|
101
95
|
async executeCacheHit(cached, targetDir, currentTime) {
|
|
102
96
|
const { ttl, 'max-age': maxAge, 'allow-stale': allowStale, rendered } = this.flags;
|
|
103
97
|
const isExpired = checkMaxAgeExpired(cached, currentTime, maxAge);
|
|
@@ -246,7 +240,7 @@ export default class FetchCommand extends BaseCommand {
|
|
|
246
240
|
/* section generation is non-essential; ignore failures */
|
|
247
241
|
}
|
|
248
242
|
}
|
|
249
|
-
async
|
|
243
|
+
async run() {
|
|
250
244
|
const { url } = this.args;
|
|
251
245
|
const { format, ttl, 'max-age': maxAge, 'dry-run': dryRun } = this.flags;
|
|
252
246
|
this.validateDurationFlags(ttl, maxAge);
|
|
@@ -272,7 +266,7 @@ export default class FetchCommand extends BaseCommand {
|
|
|
272
266
|
this.persistSectionsIfFresh(tmpDir ?? storageDir, artifact, currentTime, cacheStatus);
|
|
273
267
|
const content = format === 'compressed' ? artifact.compressed : artifact.detailed;
|
|
274
268
|
const resultData = this.buildResultData(url, normalizedUrl, cacheKey, storageDir, roots.mode, cacheStatus, freshnessState, format, artifact, content, redirectedToGlobal);
|
|
275
|
-
if (!this.
|
|
269
|
+
if (!this.jsonEnabled()) {
|
|
276
270
|
this.log(content);
|
|
277
271
|
}
|
|
278
272
|
return resultData;
|
|
@@ -23,7 +23,6 @@ export default class ResearchImport extends BaseCommand<typeof ResearchImport> {
|
|
|
23
23
|
storage: import("@oclif/core/interfaces").OptionFlag<"global" | "project" | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
24
24
|
};
|
|
25
25
|
static stdoutIsPrimaryData: boolean;
|
|
26
|
-
init(): Promise<void>;
|
|
27
26
|
private readStdin;
|
|
28
27
|
private getSourceUrls;
|
|
29
28
|
private validateSourceMode;
|
|
@@ -34,5 +33,5 @@ export default class ResearchImport extends BaseCommand<typeof ResearchImport> {
|
|
|
34
33
|
private readAndValidateInput;
|
|
35
34
|
private deriveImportCacheKey;
|
|
36
35
|
private buildImportArtifact;
|
|
37
|
-
|
|
36
|
+
run(): Promise<unknown>;
|
|
38
37
|
}
|
package/dist/commands/import.js
CHANGED
|
@@ -76,12 +76,6 @@ export default class ResearchImport extends BaseCommand {
|
|
|
76
76
|
})(),
|
|
77
77
|
};
|
|
78
78
|
static stdoutIsPrimaryData = true;
|
|
79
|
-
async init() {
|
|
80
|
-
await super.init();
|
|
81
|
-
const { args, flags } = await this.parse(ResearchImport);
|
|
82
|
-
this.args = args;
|
|
83
|
-
this.flags = flags;
|
|
84
|
-
}
|
|
85
79
|
async readStdin(limitBytes = 1024 * 1024) {
|
|
86
80
|
return new Promise((resolve, reject) => {
|
|
87
81
|
let data = '';
|
|
@@ -254,7 +248,7 @@ export default class ResearchImport extends BaseCommand {
|
|
|
254
248
|
provenance: `Imported via agent-supplied research at ${currentTime.toISOString()}`,
|
|
255
249
|
};
|
|
256
250
|
}
|
|
257
|
-
async
|
|
251
|
+
async run() {
|
|
258
252
|
const hasSingle = Boolean(this.args.url);
|
|
259
253
|
const multiUrls = this.flags['source-url'] || [];
|
|
260
254
|
const hasMulti = multiUrls.length > 0;
|
|
@@ -275,7 +269,7 @@ export default class ResearchImport extends BaseCommand {
|
|
|
275
269
|
if (writeResult.redirected) {
|
|
276
270
|
this.warn(`Detected ${writeResult.secretLabel} in the imported content; stored in the global cache instead of the project to avoid committing secrets.`);
|
|
277
271
|
}
|
|
278
|
-
if (!this.
|
|
272
|
+
if (!this.jsonEnabled()) {
|
|
279
273
|
this.log(`Successfully imported research artifact.`);
|
|
280
274
|
this.log(`Cache Key: ${cacheKey}`);
|
|
281
275
|
this.log(`Storage Path: ${storagePath}`);
|
|
@@ -7,7 +7,6 @@ export default class ResearchInspect extends BaseCommand<typeof ResearchInspect>
|
|
|
7
7
|
url: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
8
8
|
};
|
|
9
9
|
static stdoutIsPrimaryData: boolean;
|
|
10
|
-
|
|
11
|
-
execute(): Promise<unknown>;
|
|
10
|
+
run(): Promise<unknown>;
|
|
12
11
|
private findSections;
|
|
13
12
|
}
|
package/dist/commands/inspect.js
CHANGED
|
@@ -13,12 +13,7 @@ export default class ResearchInspect extends BaseCommand {
|
|
|
13
13
|
}),
|
|
14
14
|
};
|
|
15
15
|
static stdoutIsPrimaryData = true;
|
|
16
|
-
async
|
|
17
|
-
await super.init();
|
|
18
|
-
const { args } = await this.parse(ResearchInspect);
|
|
19
|
-
this.args = args;
|
|
20
|
-
}
|
|
21
|
-
async execute() {
|
|
16
|
+
async run() {
|
|
22
17
|
const { url } = this.args;
|
|
23
18
|
let target;
|
|
24
19
|
try {
|
|
@@ -39,7 +34,7 @@ export default class ResearchInspect extends BaseCommand {
|
|
|
39
34
|
const cached = located.artifact;
|
|
40
35
|
const artifactPath = located.path;
|
|
41
36
|
const sections = this.findSections(roots.readRoots, cacheKey);
|
|
42
|
-
if (!this.
|
|
37
|
+
if (!this.jsonEnabled()) {
|
|
43
38
|
this.log(`Cache Key: ${cacheKey}`);
|
|
44
39
|
this.log(`Cache Path: ${artifactPath}`);
|
|
45
40
|
this.log(`--- Metadata ---`);
|
package/dist/commands/list.d.ts
CHANGED
|
@@ -16,10 +16,9 @@ export default class ResearchList extends BaseCommand<typeof ResearchList> {
|
|
|
16
16
|
limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
17
|
};
|
|
18
18
|
static stdoutIsPrimaryData: boolean;
|
|
19
|
-
init(): Promise<void>;
|
|
20
19
|
private validateListFlags;
|
|
21
20
|
private matchesFilters;
|
|
22
21
|
private scanCacheDirForList;
|
|
23
22
|
private logListResults;
|
|
24
|
-
|
|
23
|
+
run(): Promise<unknown>;
|
|
25
24
|
}
|
package/dist/commands/list.js
CHANGED
|
@@ -49,11 +49,6 @@ export default class ResearchList extends BaseCommand {
|
|
|
49
49
|
}),
|
|
50
50
|
};
|
|
51
51
|
static stdoutIsPrimaryData = true;
|
|
52
|
-
async init() {
|
|
53
|
-
await super.init();
|
|
54
|
-
const { flags } = await this.parse(ResearchList);
|
|
55
|
-
this.flags = flags;
|
|
56
|
-
}
|
|
57
52
|
validateListFlags() {
|
|
58
53
|
const limit = this.flags.limit;
|
|
59
54
|
if (limit !== undefined && (limit < 1 || limit > 100)) {
|
|
@@ -107,7 +102,7 @@ export default class ResearchList extends BaseCommand {
|
|
|
107
102
|
});
|
|
108
103
|
}
|
|
109
104
|
logListResults(finalResults) {
|
|
110
|
-
if (this.
|
|
105
|
+
if (this.jsonEnabled())
|
|
111
106
|
return;
|
|
112
107
|
if (finalResults.length === 0) {
|
|
113
108
|
this.log('No cached research entries found matching filters.');
|
|
@@ -121,7 +116,7 @@ export default class ResearchList extends BaseCommand {
|
|
|
121
116
|
this.log(` Source URLs: ${res.sourceUrls.join(', ')}\n`);
|
|
122
117
|
});
|
|
123
118
|
}
|
|
124
|
-
async
|
|
119
|
+
async run() {
|
|
125
120
|
this.validateListFlags();
|
|
126
121
|
const roots = loadStoreRoots({
|
|
127
122
|
configDir: this.config.configDir,
|
package/dist/commands/prune.d.ts
CHANGED
|
@@ -15,10 +15,9 @@ export default class ResearchPrune extends BaseCommand<typeof ResearchPrune> {
|
|
|
15
15
|
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
16
|
};
|
|
17
17
|
static stdoutIsPrimaryData: boolean;
|
|
18
|
-
init(): Promise<void>;
|
|
19
18
|
private validatePruneFlags;
|
|
20
19
|
private shouldPrune;
|
|
21
20
|
private findPruneCandidates;
|
|
22
21
|
private deletePruneCandidates;
|
|
23
|
-
|
|
22
|
+
run(): Promise<unknown>;
|
|
24
23
|
}
|
package/dist/commands/prune.js
CHANGED
|
@@ -41,11 +41,6 @@ export default class ResearchPrune extends BaseCommand {
|
|
|
41
41
|
}),
|
|
42
42
|
};
|
|
43
43
|
static stdoutIsPrimaryData = true;
|
|
44
|
-
async init() {
|
|
45
|
-
await super.init();
|
|
46
|
-
const { flags } = await this.parse(ResearchPrune);
|
|
47
|
-
this.flags = flags;
|
|
48
|
-
}
|
|
49
44
|
validatePruneFlags() {
|
|
50
45
|
if (!this.flags['older-than'] && !this.flags.inactive && !this.flags['artifact-type']) {
|
|
51
46
|
this.error('Must specify at least one pruning filter: --older-than, --inactive, or --artifact-type.', { exit: 2 });
|
|
@@ -103,7 +98,7 @@ export default class ResearchPrune extends BaseCommand {
|
|
|
103
98
|
}
|
|
104
99
|
return prunedCount;
|
|
105
100
|
}
|
|
106
|
-
async
|
|
101
|
+
async run() {
|
|
107
102
|
this.validatePruneFlags();
|
|
108
103
|
const roots = loadStoreRoots({
|
|
109
104
|
configDir: this.config.configDir,
|
|
@@ -115,7 +110,7 @@ export default class ResearchPrune extends BaseCommand {
|
|
|
115
110
|
const dryRun = this.flags['dry-run'];
|
|
116
111
|
const count = filesToPrune.length;
|
|
117
112
|
if (dryRun) {
|
|
118
|
-
if (!this.
|
|
113
|
+
if (!this.jsonEnabled()) {
|
|
119
114
|
this.log(`[Dry Run] Found ${count} research cache entries that would be pruned:\n`);
|
|
120
115
|
filesToPrune.forEach((f) => {
|
|
121
116
|
this.log(`- [${f.topic || 'No Topic'}] Key: ${f.cacheKey} (${f.url || 'Imported note'})`);
|
|
@@ -124,7 +119,7 @@ export default class ResearchPrune extends BaseCommand {
|
|
|
124
119
|
}
|
|
125
120
|
else {
|
|
126
121
|
const prunedCount = this.deletePruneCandidates(filesToPrune);
|
|
127
|
-
if (!this.
|
|
122
|
+
if (!this.jsonEnabled()) {
|
|
128
123
|
this.log(`Successfully pruned ${prunedCount} of ${count} research cache entries.`);
|
|
129
124
|
}
|
|
130
125
|
}
|
|
@@ -16,7 +16,6 @@ export default class ResearchSearch extends BaseCommand<typeof ResearchSearch> {
|
|
|
16
16
|
remote: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
17
|
};
|
|
18
18
|
static stdoutIsPrimaryData: boolean;
|
|
19
|
-
init(): Promise<void>;
|
|
20
19
|
private validateSearchFlags;
|
|
21
20
|
private getSearchQueryTerms;
|
|
22
21
|
private matchesFilters;
|
|
@@ -28,5 +27,5 @@ export default class ResearchSearch extends BaseCommand<typeof ResearchSearch> {
|
|
|
28
27
|
private printResults;
|
|
29
28
|
private executeSiteSearch;
|
|
30
29
|
private executeRemoteSearch;
|
|
31
|
-
|
|
30
|
+
run(): Promise<unknown>;
|
|
32
31
|
}
|
package/dist/commands/search.js
CHANGED
|
@@ -58,12 +58,6 @@ export default class ResearchSearch extends BaseCommand {
|
|
|
58
58
|
}),
|
|
59
59
|
};
|
|
60
60
|
static stdoutIsPrimaryData = true;
|
|
61
|
-
async init() {
|
|
62
|
-
await super.init();
|
|
63
|
-
const { args, flags } = await this.parse(ResearchSearch);
|
|
64
|
-
this.args = args;
|
|
65
|
-
this.flags = flags;
|
|
66
|
-
}
|
|
67
61
|
validateSearchFlags() {
|
|
68
62
|
const limit = this.flags.limit;
|
|
69
63
|
if (limit !== undefined && (limit < 1 || limit > 50)) {
|
|
@@ -187,7 +181,7 @@ export default class ResearchSearch extends BaseCommand {
|
|
|
187
181
|
});
|
|
188
182
|
}
|
|
189
183
|
logSearchResults(finalResults) {
|
|
190
|
-
if (this.
|
|
184
|
+
if (this.jsonEnabled())
|
|
191
185
|
return;
|
|
192
186
|
this.log(`Found ${finalResults.length} matching cached research entries:\n`);
|
|
193
187
|
finalResults.forEach((res, index) => {
|
|
@@ -200,7 +194,7 @@ export default class ResearchSearch extends BaseCommand {
|
|
|
200
194
|
// Prints a numbered result list to stdout for human (non-JSON) output. Shared by site and remote
|
|
201
195
|
// search so the two paths can't drift in formatting.
|
|
202
196
|
printResults(heading, results) {
|
|
203
|
-
if (this.
|
|
197
|
+
if (this.jsonEnabled())
|
|
204
198
|
return;
|
|
205
199
|
this.log(heading);
|
|
206
200
|
results.forEach((r, i) => {
|
|
@@ -235,7 +229,7 @@ export default class ResearchSearch extends BaseCommand {
|
|
|
235
229
|
return undefined;
|
|
236
230
|
}
|
|
237
231
|
}
|
|
238
|
-
async
|
|
232
|
+
async run() {
|
|
239
233
|
const { query } = this.args;
|
|
240
234
|
const { domain, remote } = this.flags;
|
|
241
235
|
if (domain) {
|
|
@@ -12,6 +12,5 @@ export default class ResearchStatus extends BaseCommand<typeof ResearchStatus> {
|
|
|
12
12
|
'max-age': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
13
|
};
|
|
14
14
|
static stdoutIsPrimaryData: boolean;
|
|
15
|
-
|
|
16
|
-
execute(): Promise<unknown>;
|
|
15
|
+
run(): Promise<unknown>;
|
|
17
16
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -43,13 +43,7 @@ export default class ResearchStatus extends BaseCommand {
|
|
|
43
43
|
}),
|
|
44
44
|
};
|
|
45
45
|
static stdoutIsPrimaryData = true;
|
|
46
|
-
async
|
|
47
|
-
await super.init();
|
|
48
|
-
const { args, flags } = await this.parse(ResearchStatus);
|
|
49
|
-
this.args = args;
|
|
50
|
-
this.flags = flags;
|
|
51
|
-
}
|
|
52
|
-
async execute() {
|
|
46
|
+
async run() {
|
|
53
47
|
const { url } = this.args;
|
|
54
48
|
const { ttl, tier, 'max-age': maxAge } = this.flags;
|
|
55
49
|
let target;
|
|
@@ -76,7 +70,7 @@ export default class ResearchStatus extends BaseCommand {
|
|
|
76
70
|
}
|
|
77
71
|
// On a hit, report where it actually lives; on a miss, where a fetch would write it.
|
|
78
72
|
const artifactPath = located?.path ?? getArtifactPath(roots.writeRoot, cacheKey);
|
|
79
|
-
if (!this.
|
|
73
|
+
if (!this.jsonEnabled()) {
|
|
80
74
|
this.log(`URL: ${normalizedUrl}`);
|
|
81
75
|
this.log(`Cache Key: ${cacheKey}`);
|
|
82
76
|
this.log(`Cache Path: ${artifactPath}`);
|
|
@@ -6,6 +6,9 @@ const ERROR_PAGE_PATTERNS = [
|
|
|
6
6
|
/\bnot\s*found\b/i,
|
|
7
7
|
/there\s*isn'?t\s*a\s*github\s*pages\s*site\s*here/i,
|
|
8
8
|
/site\s*not\s*found/i,
|
|
9
|
+
// Client-rendered (SPA) failure shells that return HTTP 200 but render only an error notice.
|
|
10
|
+
/something\s+went\s+wrong/i,
|
|
11
|
+
/an?\s+error\s+occurred/i,
|
|
9
12
|
];
|
|
10
13
|
/** True when short text reads like an error/soft-404 page rather than real content. */
|
|
11
14
|
export function looksLikeErrorPage(text) {
|
|
@@ -7,6 +7,7 @@ import { evaluateFreshness, getPolicy } from './freshness.js';
|
|
|
7
7
|
import { compressMarkdown } from './compress.js';
|
|
8
8
|
import { estimateTokens } from './token-estimate.js';
|
|
9
9
|
import { fetchRenderedHtml } from './browser.js';
|
|
10
|
+
import { looksLikeErrorPage } from './docs/validate.js';
|
|
10
11
|
function buildMetadata(url, normalizedUrl, cacheKey, fetchResult, extraction, tier, ttl, currentTime, compressed) {
|
|
11
12
|
const contentHash = createHash('sha256').update(extraction.detailedMarkdown).digest('hex');
|
|
12
13
|
const staleAfterTime = new Date(currentTime);
|
|
@@ -51,10 +52,39 @@ function buildMetadata(url, normalizedUrl, cacheKey, fetchResult, extraction, ti
|
|
|
51
52
|
section_heading_path: null,
|
|
52
53
|
};
|
|
53
54
|
}
|
|
55
|
+
// Builds a compact error marker instead of caching a full error page. Subsequent lookups serve this
|
|
56
|
+
// tiny marker (a handful of tokens) instead of re-fetching, and revalidation re-checks it when stale
|
|
57
|
+
// so a transient failure recovers into a real artifact.
|
|
58
|
+
function buildErrorArtifact(url, normalizedUrl, cacheKey, fetchResult, tier, ttl, currentTime, reason) {
|
|
59
|
+
const marker = `Error: ${url} could not be cached — ${reason}. ` +
|
|
60
|
+
'The page returned an error, so its content was not stored; it will be re-fetched when this entry goes stale.';
|
|
61
|
+
const extraction = {
|
|
62
|
+
title: `Error: ${reason}`,
|
|
63
|
+
detailedMarkdown: marker,
|
|
64
|
+
confidence: 'low',
|
|
65
|
+
qualityNotes: [`error: ${reason}`],
|
|
66
|
+
};
|
|
67
|
+
const metadata = buildMetadata(url, normalizedUrl, cacheKey, fetchResult, extraction, tier, ttl, currentTime, marker);
|
|
68
|
+
metadata.extraction_status = 'failed';
|
|
69
|
+
metadata.extraction_confidence = null;
|
|
70
|
+
return {
|
|
71
|
+
metadata,
|
|
72
|
+
summary: extraction.title,
|
|
73
|
+
compressed: marker,
|
|
74
|
+
detailed: marker,
|
|
75
|
+
provenance: `Fetched from ${url} on ${currentTime.toISOString()} (error page; content not cached)`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
54
78
|
/**
|
|
55
79
|
* Helper to construct a ResearchArtifact from a fresh HTML fetch.
|
|
56
80
|
*/
|
|
57
81
|
export function createArtifactFromFetch(url, normalizedUrl, cacheKey, fetchResult, extraction, tier, ttl, currentTime) {
|
|
82
|
+
// A managed platform / SPA can return HTTP 200 but render only a "not found" / "something went
|
|
83
|
+
// wrong" shell. Cache a compact marker for those instead of the full error markdown, so repeat
|
|
84
|
+
// lookups cost a few tokens and revalidation still re-checks the page when the entry goes stale.
|
|
85
|
+
if (looksLikeErrorPage(extraction.detailedMarkdown)) {
|
|
86
|
+
return buildErrorArtifact(url, normalizedUrl, cacheKey, fetchResult, tier, ttl, currentTime, 'page reported an error or was not found');
|
|
87
|
+
}
|
|
58
88
|
const compressed = compressMarkdown(extraction.detailedMarkdown);
|
|
59
89
|
const metadata = buildMetadata(url, normalizedUrl, cacheKey, fetchResult, extraction, tier, ttl, currentTime, compressed);
|
|
60
90
|
return {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, renameSync } from 'node:fs';
|
|
2
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, renameSync, } from 'node:fs';
|
|
3
3
|
import { atomicWriteFile } from '../atomic-write.js';
|
|
4
4
|
import { parseArtifact, serializeArtifact } from './artifact.js';
|
|
5
5
|
function isResearchFile(file) {
|
package/oclif.manifest.json
CHANGED
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
],
|
|
23
23
|
"flags": {
|
|
24
24
|
"json": {
|
|
25
|
-
"description": "
|
|
25
|
+
"description": "Format output as json.",
|
|
26
|
+
"helpGroup": "GLOBAL",
|
|
26
27
|
"name": "json",
|
|
27
28
|
"allowNo": false,
|
|
28
29
|
"type": "boolean"
|
|
@@ -129,8 +130,7 @@
|
|
|
129
130
|
"pluginType": "core",
|
|
130
131
|
"strict": true,
|
|
131
132
|
"summary": "An advanced, locally cached web research tool optimized for LLM ingestion.",
|
|
132
|
-
"enableJsonFlag":
|
|
133
|
-
"jsonEnvelope": false,
|
|
133
|
+
"enableJsonFlag": true,
|
|
134
134
|
"stdoutIsPrimaryData": true
|
|
135
135
|
},
|
|
136
136
|
"import": {
|
|
@@ -159,7 +159,8 @@
|
|
|
159
159
|
],
|
|
160
160
|
"flags": {
|
|
161
161
|
"json": {
|
|
162
|
-
"description": "
|
|
162
|
+
"description": "Format output as json.",
|
|
163
|
+
"helpGroup": "GLOBAL",
|
|
163
164
|
"name": "json",
|
|
164
165
|
"allowNo": false,
|
|
165
166
|
"type": "boolean"
|
|
@@ -253,8 +254,7 @@
|
|
|
253
254
|
"pluginType": "core",
|
|
254
255
|
"strict": true,
|
|
255
256
|
"summary": "Import agent-supplied clean Markdown notes into the local cache.",
|
|
256
|
-
"enableJsonFlag":
|
|
257
|
-
"jsonEnvelope": false,
|
|
257
|
+
"enableJsonFlag": true,
|
|
258
258
|
"stdoutIsPrimaryData": true
|
|
259
259
|
},
|
|
260
260
|
"inspect": {
|
|
@@ -269,7 +269,8 @@
|
|
|
269
269
|
"description": "Inspects local storage and displays full frontmatter metadata for the URL (without printing the content body).",
|
|
270
270
|
"flags": {
|
|
271
271
|
"json": {
|
|
272
|
-
"description": "
|
|
272
|
+
"description": "Format output as json.",
|
|
273
|
+
"helpGroup": "GLOBAL",
|
|
273
274
|
"name": "json",
|
|
274
275
|
"allowNo": false,
|
|
275
276
|
"type": "boolean"
|
|
@@ -283,8 +284,7 @@
|
|
|
283
284
|
"pluginType": "core",
|
|
284
285
|
"strict": true,
|
|
285
286
|
"summary": "Inspect cached research metadata for a URL.",
|
|
286
|
-
"enableJsonFlag":
|
|
287
|
-
"jsonEnvelope": false,
|
|
287
|
+
"enableJsonFlag": true,
|
|
288
288
|
"stdoutIsPrimaryData": true
|
|
289
289
|
},
|
|
290
290
|
"list": {
|
|
@@ -307,7 +307,8 @@
|
|
|
307
307
|
],
|
|
308
308
|
"flags": {
|
|
309
309
|
"json": {
|
|
310
|
-
"description": "
|
|
310
|
+
"description": "Format output as json.",
|
|
311
|
+
"helpGroup": "GLOBAL",
|
|
311
312
|
"name": "json",
|
|
312
313
|
"allowNo": false,
|
|
313
314
|
"type": "boolean"
|
|
@@ -380,8 +381,7 @@
|
|
|
380
381
|
"pluginType": "core",
|
|
381
382
|
"strict": true,
|
|
382
383
|
"summary": "List cached research artifacts by metadata filters.",
|
|
383
|
-
"enableJsonFlag":
|
|
384
|
-
"jsonEnvelope": false,
|
|
384
|
+
"enableJsonFlag": true,
|
|
385
385
|
"stdoutIsPrimaryData": true
|
|
386
386
|
},
|
|
387
387
|
"prune": {
|
|
@@ -400,7 +400,8 @@
|
|
|
400
400
|
],
|
|
401
401
|
"flags": {
|
|
402
402
|
"json": {
|
|
403
|
-
"description": "
|
|
403
|
+
"description": "Format output as json.",
|
|
404
|
+
"helpGroup": "GLOBAL",
|
|
404
405
|
"name": "json",
|
|
405
406
|
"allowNo": false,
|
|
406
407
|
"type": "boolean"
|
|
@@ -452,8 +453,7 @@
|
|
|
452
453
|
"pluginType": "core",
|
|
453
454
|
"strict": true,
|
|
454
455
|
"summary": "Clean up old or inactive research cache entries.",
|
|
455
|
-
"enableJsonFlag":
|
|
456
|
-
"jsonEnvelope": false,
|
|
456
|
+
"enableJsonFlag": true,
|
|
457
457
|
"stdoutIsPrimaryData": true
|
|
458
458
|
},
|
|
459
459
|
"search": {
|
|
@@ -468,7 +468,8 @@
|
|
|
468
468
|
"description": "Scans the local research cache database and ranks matching entries based on topic, tags, source URLs, summary, and content.",
|
|
469
469
|
"flags": {
|
|
470
470
|
"json": {
|
|
471
|
-
"description": "
|
|
471
|
+
"description": "Format output as json.",
|
|
472
|
+
"helpGroup": "GLOBAL",
|
|
472
473
|
"name": "json",
|
|
473
474
|
"allowNo": false,
|
|
474
475
|
"type": "boolean"
|
|
@@ -539,8 +540,7 @@
|
|
|
539
540
|
"pluginType": "core",
|
|
540
541
|
"strict": true,
|
|
541
542
|
"summary": "Search locally cached research artifacts by keywords.",
|
|
542
|
-
"enableJsonFlag":
|
|
543
|
-
"jsonEnvelope": false,
|
|
543
|
+
"enableJsonFlag": true,
|
|
544
544
|
"stdoutIsPrimaryData": true
|
|
545
545
|
},
|
|
546
546
|
"status": {
|
|
@@ -555,7 +555,8 @@
|
|
|
555
555
|
"description": "Reports whether the URL is cached, its freshness state, and what action (fetch, revalidate, or cached return) the root fetch command would take.",
|
|
556
556
|
"flags": {
|
|
557
557
|
"json": {
|
|
558
|
-
"description": "
|
|
558
|
+
"description": "Format output as json.",
|
|
559
|
+
"helpGroup": "GLOBAL",
|
|
559
560
|
"name": "json",
|
|
560
561
|
"allowNo": false,
|
|
561
562
|
"type": "boolean"
|
|
@@ -597,8 +598,7 @@
|
|
|
597
598
|
"pluginType": "core",
|
|
598
599
|
"strict": true,
|
|
599
600
|
"summary": "Check the cache status of a URL without fetching or writing.",
|
|
600
|
-
"enableJsonFlag":
|
|
601
|
-
"jsonEnvelope": false,
|
|
601
|
+
"enableJsonFlag": true,
|
|
602
602
|
"stdoutIsPrimaryData": true
|
|
603
603
|
},
|
|
604
604
|
"config:get": {
|
|
@@ -623,7 +623,8 @@
|
|
|
623
623
|
],
|
|
624
624
|
"flags": {
|
|
625
625
|
"json": {
|
|
626
|
-
"description": "
|
|
626
|
+
"description": "Format output as json.",
|
|
627
|
+
"helpGroup": "GLOBAL",
|
|
627
628
|
"name": "json",
|
|
628
629
|
"allowNo": false,
|
|
629
630
|
"type": "boolean"
|
|
@@ -674,7 +675,8 @@
|
|
|
674
675
|
],
|
|
675
676
|
"flags": {
|
|
676
677
|
"json": {
|
|
677
|
-
"description": "
|
|
678
|
+
"description": "Format output as json.",
|
|
679
|
+
"helpGroup": "GLOBAL",
|
|
678
680
|
"name": "json",
|
|
679
681
|
"allowNo": false,
|
|
680
682
|
"type": "boolean"
|
|
@@ -736,7 +738,8 @@
|
|
|
736
738
|
],
|
|
737
739
|
"flags": {
|
|
738
740
|
"json": {
|
|
739
|
-
"description": "
|
|
741
|
+
"description": "Format output as json.",
|
|
742
|
+
"helpGroup": "GLOBAL",
|
|
740
743
|
"name": "json",
|
|
741
744
|
"allowNo": false,
|
|
742
745
|
"type": "boolean"
|
|
@@ -799,7 +802,8 @@
|
|
|
799
802
|
],
|
|
800
803
|
"flags": {
|
|
801
804
|
"json": {
|
|
802
|
-
"description": "
|
|
805
|
+
"description": "Format output as json.",
|
|
806
|
+
"helpGroup": "GLOBAL",
|
|
803
807
|
"name": "json",
|
|
804
808
|
"allowNo": false,
|
|
805
809
|
"type": "boolean"
|
|
@@ -841,5 +845,5 @@
|
|
|
841
845
|
"stdoutIsPrimaryData": true
|
|
842
846
|
}
|
|
843
847
|
},
|
|
844
|
-
"version": "1.0.
|
|
848
|
+
"version": "1.0.3"
|
|
845
849
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@taurgis/bonsai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "Bonsai is a standalone local research cache CLI for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"regression:suite": "tsc -b && node testing/regression/run-suite.mjs",
|
|
27
27
|
"regression:promote": "tsc -b && node testing/regression/run-suite.mjs --promote",
|
|
28
28
|
"regression:check": "tsc -b && node testing/regression/run-suite.mjs --strict",
|
|
29
|
+
"nexus:sync": "npx forward-nexus update && npx forward-nexus sync",
|
|
29
30
|
"test": "vitest",
|
|
30
31
|
"test:coverage": "vitest run --coverage",
|
|
31
32
|
"type-check": "tsc --noEmit"
|