browse 0.7.0 → 0.7.1
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/dist/commands/skills/find.js +24 -6
- package/dist/commands/skills/list.js +18 -6
- package/dist/commands/templates/find.js +12 -9
- package/dist/commands/templates/list.js +6 -7
- package/dist/lib/skills/catalog.js +60 -13
- package/dist/lib/templates/output.js +32 -8
- package/oclif.manifest.json +395 -288
- package/package.json +1 -1
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Args, Flags } from "@oclif/core";
|
|
2
2
|
import { BrowseCommand } from "../../base.js";
|
|
3
|
-
import { outputJson } from "../../lib/output.js";
|
|
4
|
-
import { listCatalogSkills,
|
|
3
|
+
import { outputFormatFlags, outputJson, resolveOutputFormat, } from "../../lib/output.js";
|
|
4
|
+
import { exactSkillMatch, listCatalogSkills, outputSkillTable, printSkillDetail, prioritizeExactSkillMatch, } from "../../lib/skills/catalog.js";
|
|
5
5
|
export default class SkillsFind extends BrowseCommand {
|
|
6
6
|
static description = "Find Browse.sh catalog skills by slug, domain, title, description, category, alias, or tag.";
|
|
7
7
|
static examples = [
|
|
8
8
|
"browse skills find yelp",
|
|
9
9
|
"browse skills find reviews",
|
|
10
10
|
"browse skills find yelp.com/extract-reviews",
|
|
11
|
+
"browse skills find travel --limit 5",
|
|
11
12
|
'browse skills find "restaurant reviews" --json',
|
|
12
13
|
];
|
|
13
14
|
static args = {
|
|
@@ -17,17 +18,34 @@ export default class SkillsFind extends BrowseCommand {
|
|
|
17
18
|
}),
|
|
18
19
|
};
|
|
19
20
|
static flags = {
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
...outputFormatFlags,
|
|
22
|
+
all: Flags.boolean({
|
|
23
|
+
description: "Show all matching skills in table output.",
|
|
24
|
+
}),
|
|
25
|
+
limit: Flags.integer({
|
|
26
|
+
default: 25,
|
|
27
|
+
description: "Maximum matching skills to show in table output.",
|
|
28
|
+
helpValue: "<count>",
|
|
29
|
+
min: 1,
|
|
22
30
|
}),
|
|
23
31
|
};
|
|
24
32
|
async run() {
|
|
25
33
|
const { args, flags } = await this.parse(SkillsFind);
|
|
26
34
|
const skills = prioritizeExactSkillMatch(await listCatalogSkills({ query: args.query }), args.query);
|
|
27
|
-
|
|
35
|
+
const outputFormat = resolveOutputFormat(flags);
|
|
36
|
+
if (outputFormat === "json") {
|
|
28
37
|
outputJson({ query: args.query, skills });
|
|
29
38
|
return;
|
|
30
39
|
}
|
|
31
|
-
|
|
40
|
+
const exactMatch = exactSkillMatch(skills, args.query);
|
|
41
|
+
if (skills.length === 1 && exactMatch) {
|
|
42
|
+
printSkillDetail(exactMatch);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
outputSkillTable(skills, {
|
|
46
|
+
heading: `Skills matching "${args.query}"`,
|
|
47
|
+
limit: flags.all ? skills.length : flags.limit,
|
|
48
|
+
wide: flags.wide,
|
|
49
|
+
});
|
|
32
50
|
}
|
|
33
51
|
}
|
|
@@ -1,25 +1,37 @@
|
|
|
1
1
|
import { Flags } from "@oclif/core";
|
|
2
2
|
import { BrowseCommand } from "../../base.js";
|
|
3
|
-
import { outputJson } from "../../lib/output.js";
|
|
4
|
-
import { listCatalogSkills,
|
|
3
|
+
import { outputFormatFlags, outputJson, resolveOutputFormat, } from "../../lib/output.js";
|
|
4
|
+
import { listCatalogSkills, outputSkillTable, } from "../../lib/skills/catalog.js";
|
|
5
5
|
export default class SkillsList extends BrowseCommand {
|
|
6
6
|
static description = "List Browse.sh catalog skills.";
|
|
7
7
|
static examples = [
|
|
8
8
|
"browse skills list",
|
|
9
|
+
"browse skills list --limit 10",
|
|
10
|
+
"browse skills list --all",
|
|
9
11
|
"browse skills list --json",
|
|
10
12
|
];
|
|
11
13
|
static flags = {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
...outputFormatFlags,
|
|
15
|
+
all: Flags.boolean({
|
|
16
|
+
description: "Show all returned skills in table output.",
|
|
17
|
+
}),
|
|
18
|
+
limit: Flags.integer({
|
|
19
|
+
default: 25,
|
|
20
|
+
description: "Maximum skills to show in table output.",
|
|
21
|
+
helpValue: "<count>",
|
|
22
|
+
min: 1,
|
|
14
23
|
}),
|
|
15
24
|
};
|
|
16
25
|
async run() {
|
|
17
26
|
const { flags } = await this.parse(SkillsList);
|
|
18
27
|
const skills = await listCatalogSkills();
|
|
19
|
-
if (flags
|
|
28
|
+
if (resolveOutputFormat(flags) === "json") {
|
|
20
29
|
outputJson({ skills });
|
|
21
30
|
return;
|
|
22
31
|
}
|
|
23
|
-
|
|
32
|
+
outputSkillTable(skills, {
|
|
33
|
+
limit: flags.all ? skills.length : flags.limit,
|
|
34
|
+
wide: flags.wide,
|
|
35
|
+
});
|
|
24
36
|
}
|
|
25
37
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { Args
|
|
1
|
+
import { Args } from "@oclif/core";
|
|
2
2
|
import { BrowseCommand } from "../../base.js";
|
|
3
|
-
import { outputJson } from "../../lib/output.js";
|
|
3
|
+
import { outputFormatFlags, outputJson, resolveOutputFormat, } from "../../lib/output.js";
|
|
4
4
|
import { getTemplateIfExists, listTemplates } from "../../lib/templates/api.js";
|
|
5
|
-
import {
|
|
5
|
+
import { outputTemplateTable, printTemplateDetail, templateMatchesQuery, } from "../../lib/templates/output.js";
|
|
6
6
|
export default class TemplatesFind extends BrowseCommand {
|
|
7
7
|
static description = "Find Browserbase templates by slug, title, category, or tag.";
|
|
8
8
|
static examples = [
|
|
9
9
|
"browse templates find google-trends-keywords",
|
|
10
10
|
"browse templates find amazon",
|
|
11
|
+
"browse templates find Python --wide",
|
|
11
12
|
"browse templates find Python --json",
|
|
12
13
|
];
|
|
13
14
|
static args = {
|
|
@@ -17,15 +18,14 @@ export default class TemplatesFind extends BrowseCommand {
|
|
|
17
18
|
}),
|
|
18
19
|
};
|
|
19
20
|
static flags = {
|
|
20
|
-
|
|
21
|
-
description: "Print matching templates as JSON.",
|
|
22
|
-
}),
|
|
21
|
+
...outputFormatFlags,
|
|
23
22
|
};
|
|
24
23
|
async run() {
|
|
25
24
|
const { args, flags } = await this.parse(TemplatesFind);
|
|
26
25
|
const exactTemplate = await getTemplateIfExists(args.query);
|
|
26
|
+
const outputFormat = resolveOutputFormat(flags);
|
|
27
27
|
if (exactTemplate) {
|
|
28
|
-
if (
|
|
28
|
+
if (outputFormat === "json") {
|
|
29
29
|
outputJson({ query: args.query, templates: [exactTemplate] });
|
|
30
30
|
return;
|
|
31
31
|
}
|
|
@@ -34,10 +34,13 @@ export default class TemplatesFind extends BrowseCommand {
|
|
|
34
34
|
}
|
|
35
35
|
const templates = await listTemplates();
|
|
36
36
|
const matches = templates.filter((template) => templateMatchesQuery(template, args.query));
|
|
37
|
-
if (
|
|
37
|
+
if (outputFormat === "json") {
|
|
38
38
|
outputJson({ query: args.query, templates: matches });
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
outputTemplateTable(matches, {
|
|
42
|
+
heading: `Templates matching "${args.query}"`,
|
|
43
|
+
wide: flags.wide,
|
|
44
|
+
});
|
|
42
45
|
}
|
|
43
46
|
}
|
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
import { Flags } from "@oclif/core";
|
|
2
2
|
import { BrowseCommand } from "../../base.js";
|
|
3
|
-
import { outputJson } from "../../lib/output.js";
|
|
3
|
+
import { outputFormatFlags, outputJson, resolveOutputFormat, } from "../../lib/output.js";
|
|
4
4
|
import { listTemplates } from "../../lib/templates/api.js";
|
|
5
|
-
import {
|
|
5
|
+
import { outputTemplateTable } from "../../lib/templates/output.js";
|
|
6
6
|
export default class TemplatesList extends BrowseCommand {
|
|
7
7
|
static description = "List Browserbase templates.";
|
|
8
8
|
static examples = [
|
|
9
9
|
"browse templates list",
|
|
10
10
|
'browse templates list --category "Web Automation"',
|
|
11
11
|
"browse templates list --tag Python --source Browserbase",
|
|
12
|
+
"browse templates list --wide",
|
|
12
13
|
"browse templates list --json",
|
|
13
14
|
];
|
|
14
15
|
static flags = {
|
|
16
|
+
...outputFormatFlags,
|
|
15
17
|
category: Flags.string({
|
|
16
18
|
description: "Filter templates by category.",
|
|
17
19
|
helpValue: "<category>",
|
|
18
20
|
}),
|
|
19
|
-
json: Flags.boolean({
|
|
20
|
-
description: "Print templates as JSON.",
|
|
21
|
-
}),
|
|
22
21
|
source: Flags.string({
|
|
23
22
|
description: "Filter templates by source.",
|
|
24
23
|
helpValue: "<source>",
|
|
@@ -35,10 +34,10 @@ export default class TemplatesList extends BrowseCommand {
|
|
|
35
34
|
source: flags.source,
|
|
36
35
|
tag: flags.tag,
|
|
37
36
|
});
|
|
38
|
-
if (flags
|
|
37
|
+
if (resolveOutputFormat(flags) === "json") {
|
|
39
38
|
outputJson({ templates });
|
|
40
39
|
return;
|
|
41
40
|
}
|
|
42
|
-
|
|
41
|
+
outputTemplateTable(templates, { wide: flags.wide });
|
|
43
42
|
}
|
|
44
43
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { fail } from "../errors.js";
|
|
2
|
+
import { outputTable } from "../output.js";
|
|
2
3
|
import { defaultSkillsApiBaseUrl, isRecord, responseDetail } from "./shared.js";
|
|
3
4
|
export async function listCatalogSkills(options = {}) {
|
|
4
5
|
const payload = await requestSkillsJson(skillsCatalogApiUrl(options.query));
|
|
@@ -14,24 +15,70 @@ export function prioritizeExactSkillMatch(skills, query) {
|
|
|
14
15
|
return aExact ? -1 : 1;
|
|
15
16
|
});
|
|
16
17
|
}
|
|
17
|
-
export function
|
|
18
|
-
|
|
18
|
+
export function outputSkillTable(skills, options = {}) {
|
|
19
|
+
const visibleSkills = skills.slice(0, options.limit ?? skills.length);
|
|
20
|
+
if (visibleSkills.length === 0) {
|
|
19
21
|
console.log("No skills found.");
|
|
20
22
|
return;
|
|
21
23
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
if (options.heading) {
|
|
25
|
+
console.log(`${options.heading} (${skills.length})`);
|
|
26
|
+
}
|
|
27
|
+
outputTable(visibleSkills, [
|
|
28
|
+
{
|
|
29
|
+
header: "Skill",
|
|
30
|
+
maxWidth: 42,
|
|
31
|
+
value: (skill) => skill.slug,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
header: "Title",
|
|
35
|
+
maxWidth: 42,
|
|
36
|
+
value: (skill) => skill.title,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
header: "Method",
|
|
40
|
+
maxWidth: 7,
|
|
41
|
+
value: (skill) => skill.recommendedMethod,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
align: "right",
|
|
45
|
+
header: "Installs",
|
|
46
|
+
maxWidth: 8,
|
|
47
|
+
value: (skill) => skill.installCount,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
header: "Tags",
|
|
51
|
+
maxWidth: 36,
|
|
52
|
+
value: (skill) => formatList(skill.tags),
|
|
53
|
+
},
|
|
54
|
+
], { wide: options.wide });
|
|
55
|
+
console.log("Install with: browse skills add <skill>");
|
|
56
|
+
if (visibleSkills.length < skills.length) {
|
|
57
|
+
console.log(`Showing ${visibleSkills.length} of ${skills.length} skills. Use --limit, --all, --wide, or --json.`);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
console.log("Use --wide for full values or --json for full descriptions.");
|
|
33
61
|
}
|
|
34
62
|
}
|
|
63
|
+
export function printSkillDetail(skill) {
|
|
64
|
+
console.log(skill.title);
|
|
65
|
+
console.log(`Skill: ${skill.slug}`);
|
|
66
|
+
console.log(`Method: ${skill.recommendedMethod}`);
|
|
67
|
+
console.log(`Source: ${skill.source || "-"}`);
|
|
68
|
+
console.log(`Installs: ${skill.installCount}`);
|
|
69
|
+
console.log(`Tags: ${formatList(skill.tags)}`);
|
|
70
|
+
if (skill.description) {
|
|
71
|
+
console.log(`\n${skill.description}`);
|
|
72
|
+
}
|
|
73
|
+
console.log(`\nInstall: browse skills add ${skill.slug}`);
|
|
74
|
+
}
|
|
75
|
+
export function exactSkillMatch(skills, query) {
|
|
76
|
+
const normalizedQuery = query.toLowerCase();
|
|
77
|
+
return skills.find((skill) => skill.slug.toLowerCase() === normalizedQuery);
|
|
78
|
+
}
|
|
79
|
+
function formatList(values) {
|
|
80
|
+
return values.length > 0 ? values.join(", ") : "-";
|
|
81
|
+
}
|
|
35
82
|
function skillsCatalogApiUrl(query) {
|
|
36
83
|
const baseUrl = process.env.BROWSE_SKILLS_API_BASE_URL || defaultSkillsApiBaseUrl;
|
|
37
84
|
const url = new URL("api/skills", baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
|
|
@@ -1,16 +1,40 @@
|
|
|
1
|
-
|
|
1
|
+
import { outputTable } from "../output.js";
|
|
2
|
+
export function outputTemplateTable(templates, options = {}) {
|
|
2
3
|
if (templates.length === 0) {
|
|
3
4
|
console.log("No templates found.");
|
|
4
5
|
return;
|
|
5
6
|
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
console.log(`${template.slug} ${template.title}`);
|
|
9
|
-
if (template.shortDescription) {
|
|
10
|
-
console.log(` ${template.shortDescription}`);
|
|
11
|
-
}
|
|
12
|
-
console.log(` source: ${template.source ?? "-"} | categories: ${formatList(template.category)} | tags: ${formatList(template.tags)}`);
|
|
7
|
+
if (options.heading) {
|
|
8
|
+
console.log(`${options.heading} (${templates.length})`);
|
|
13
9
|
}
|
|
10
|
+
outputTable(templates, [
|
|
11
|
+
{
|
|
12
|
+
header: "Template",
|
|
13
|
+
maxWidth: 34,
|
|
14
|
+
value: (template) => template.slug,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
header: "Title",
|
|
18
|
+
maxWidth: 42,
|
|
19
|
+
value: (template) => template.title,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
header: "Source",
|
|
23
|
+
maxWidth: 12,
|
|
24
|
+
value: (template) => template.source,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
header: "Categories",
|
|
28
|
+
maxWidth: 34,
|
|
29
|
+
value: (template) => formatList(template.category),
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
header: "Tags",
|
|
33
|
+
maxWidth: 34,
|
|
34
|
+
value: (template) => formatList(template.tags),
|
|
35
|
+
},
|
|
36
|
+
], { wide: options.wide });
|
|
37
|
+
console.log("Use --wide for full values or --json for full descriptions.");
|
|
14
38
|
}
|
|
15
39
|
export function printTemplateDetail(template) {
|
|
16
40
|
console.log(`${template.title}`);
|