api-spec-cli 0.2.3 → 0.2.4

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.
@@ -1,64 +1,67 @@
1
- import { out } from "../output.js";
2
- import { parseArgs } from "../args.js";
3
- import { getRegistry, getEntry, getCachedSpec, saveCachedSpec } from "../registry.js";
4
- import { fetchSpec } from "./fetch.js";
5
- import { matchGlob } from "../glob.js";
6
-
7
- export async function grepCmd(args) {
8
- const { flags, positional } = parseArgs(args);
9
- const pattern = positional[0];
10
- if (!pattern) throw new Error(
11
- "Usage: spec grep <pattern> [--spec <name>]\n" +
12
- " Glob patterns: * matches anything, ? matches one char\n" +
13
- " Plain text: substring match across name and description"
14
- );
15
-
16
- const entries = flags.spec
17
- ? [getEntry(flags.spec)]
18
- : getRegistry().filter((e) => e.enabled);
19
-
20
- if (entries.length === 0) throw new Error("No registered specs. Run 'spec add' first.");
21
-
22
- const results = [];
23
-
24
- for (const entry of entries) {
25
- let spec = getCachedSpec(entry.name);
26
- if (!spec) {
27
- spec = await fetchSpec(entry);
28
- saveCachedSpec(entry.name, spec);
29
- }
30
-
31
- const matches = [];
32
-
33
- if (spec.type === "mcp") {
34
- for (const tool of spec.tools) {
35
- const nameMatch = matchGlob(pattern, tool.name);
36
- const descMatch = tool.description && matchGlob(pattern, tool.description);
37
- if (nameMatch || descMatch) {
38
- matches.push({ id: tool.name, description: tool.description });
39
- }
40
- }
41
- } else if (spec.type === "openapi") {
42
- for (const op of spec.operations) {
43
- if (matchGlob(pattern, op.id) || matchGlob(pattern, op.path) ||
44
- (op.summary && matchGlob(pattern, op.summary))) {
45
- matches.push({ id: op.id, method: op.method, path: op.path });
46
- }
47
- }
48
- } else if (spec.type === "graphql") {
49
- for (const op of spec.operations) {
50
- if (matchGlob(pattern, op.name) ||
51
- (op.description && matchGlob(pattern, op.description))) {
52
- matches.push({ id: op.name, kind: op.kind });
53
- }
54
- }
55
- }
56
-
57
- if (matches.length > 0) {
58
- results.push({ spec: entry.name, type: spec.type, matches });
59
- }
60
- }
61
-
62
- const total = results.reduce((s, r) => s + r.matches.length, 0);
63
- out({ pattern, total, results });
64
- }
1
+ import { out } from "../output.js";
2
+ import { parseArgs } from "../args.js";
3
+ import { getRegistry, getEntry, getCachedSpec, saveCachedSpec, allEntries } from "../registry.js";
4
+ import { fetchSpec } from "./fetch.js";
5
+ import { matchGlob } from "../glob.js";
6
+
7
+ export async function grepCmd(args) {
8
+ const { flags, positional } = parseArgs(args);
9
+ const pattern = positional[0];
10
+ if (!pattern)
11
+ throw new Error(
12
+ "Usage: spec grep <pattern> [--spec <name>]\n" +
13
+ " Glob patterns: * matches anything, ? matches one char\n" +
14
+ " Plain text: substring match across name and description"
15
+ );
16
+
17
+ const entries = flags.spec
18
+ ? [getEntry(flags.spec)]
19
+ : allEntries(getRegistry()).filter((e) => e.enabled);
20
+
21
+ if (entries.length === 0) throw new Error("No registered specs. Run 'spec add' first.");
22
+
23
+ const results = [];
24
+
25
+ for (const entry of entries) {
26
+ let spec = getCachedSpec(entry.name);
27
+ if (!spec) {
28
+ spec = await fetchSpec(entry);
29
+ saveCachedSpec(entry.name, spec);
30
+ }
31
+
32
+ const matches = [];
33
+
34
+ if (spec.type === "mcp") {
35
+ for (const tool of spec.tools) {
36
+ const nameMatch = matchGlob(pattern, tool.name);
37
+ const descMatch = tool.description && matchGlob(pattern, tool.description);
38
+ if (nameMatch || descMatch) {
39
+ matches.push({ id: tool.name, description: tool.description });
40
+ }
41
+ }
42
+ } else if (spec.type === "openapi") {
43
+ for (const op of spec.operations) {
44
+ if (
45
+ matchGlob(pattern, op.id) ||
46
+ matchGlob(pattern, op.path) ||
47
+ (op.summary && matchGlob(pattern, op.summary))
48
+ ) {
49
+ matches.push({ id: op.id, method: op.method, path: op.path });
50
+ }
51
+ }
52
+ } else if (spec.type === "graphql") {
53
+ for (const op of spec.operations) {
54
+ if (matchGlob(pattern, op.name) || (op.description && matchGlob(pattern, op.description))) {
55
+ matches.push({ id: op.name, kind: op.kind });
56
+ }
57
+ }
58
+ }
59
+
60
+ if (matches.length > 0) {
61
+ results.push({ spec: entry.name, type: spec.type, matches });
62
+ }
63
+ }
64
+
65
+ const total = results.reduce((s, r) => s + r.matches.length, 0);
66
+ out({ pattern, total, results });
67
+ }
@@ -1,80 +1,78 @@
1
- import { out } from "../output.js";
2
- import { parseArgs } from "../args.js";
3
- import { resolveSpec } from "../resolve.js";
4
-
5
- export async function listOperations(args) {
6
- const opts = parseArgs(args);
7
- const { flags } = opts;
8
-
9
- const { spec } = await resolveSpec(flags);
10
-
11
- const filter = flags.filter?.toLowerCase();
12
- const compact = flags.compact !== "false";
13
- const limit = parseInt(flags.limit) || 0;
14
- const offset = parseInt(flags.offset) || 0;
15
- const tag = flags.tag?.toLowerCase();
16
-
17
- let operations;
18
-
19
- if (spec.type === "openapi") {
20
- let source = spec.operations;
21
- if (tag) {
22
- source = source.filter((op) => op.tags?.some((t) => t.toLowerCase().includes(tag)));
23
- }
24
- operations = source.map((op) =>
25
- compact
26
- ? { id: op.id, method: op.method, path: op.path }
27
- : {
28
- id: op.id,
29
- method: op.method,
30
- path: op.path,
31
- summary: op.summary,
32
- tags: op.tags,
33
- deprecated: op.deprecated,
34
- }
35
- );
36
- } else if (spec.type === "mcp") {
37
- operations = spec.tools.map((t) =>
38
- compact
39
- ? { id: t.name, description: t.description }
40
- : { id: t.name, description: t.description, inputSchema: t.inputSchema }
41
- );
42
- } else {
43
- // graphql
44
- operations = spec.operations.map((op) =>
45
- compact
46
- ? { id: op.name, kind: op.kind }
47
- : {
48
- id: op.name,
49
- kind: op.kind,
50
- description: op.description,
51
- args: op.args.map((a) => a.name),
52
- returnType: op.returnType,
53
- isDeprecated: op.isDeprecated,
54
- }
55
- );
56
-
57
- if (tag) {
58
- operations = operations.filter((op) => op.kind === tag);
59
- }
60
- }
61
-
62
- if (filter) {
63
- operations = operations.filter((op) =>
64
- JSON.stringify(op).toLowerCase().includes(filter)
65
- );
66
- }
67
-
68
- const total = operations.length;
69
-
70
- if (offset > 0) operations = operations.slice(offset);
71
- if (limit > 0) operations = operations.slice(0, limit);
72
-
73
- out({
74
- type: spec.type,
75
- total,
76
- showing: operations.length,
77
- offset: offset || 0,
78
- operations,
79
- });
80
- }
1
+ import { out } from "../output.js";
2
+ import { parseArgs } from "../args.js";
3
+ import { resolveSpec } from "../resolve.js";
4
+
5
+ export async function listOperations(args) {
6
+ const opts = parseArgs(args);
7
+ const { flags } = opts;
8
+
9
+ const { spec } = await resolveSpec(flags);
10
+
11
+ const filter = flags.filter?.toLowerCase();
12
+ const compact = flags.compact !== "false";
13
+ const limit = parseInt(flags.limit) || 0;
14
+ const offset = parseInt(flags.offset) || 0;
15
+ const tag = flags.tag?.toLowerCase();
16
+
17
+ let operations;
18
+
19
+ if (spec.type === "openapi") {
20
+ let source = spec.operations;
21
+ if (tag) {
22
+ source = source.filter((op) => op.tags?.some((t) => t.toLowerCase().includes(tag)));
23
+ }
24
+ operations = source.map((op) =>
25
+ compact
26
+ ? { id: op.id, method: op.method, path: op.path }
27
+ : {
28
+ id: op.id,
29
+ method: op.method,
30
+ path: op.path,
31
+ summary: op.summary,
32
+ tags: op.tags,
33
+ deprecated: op.deprecated,
34
+ }
35
+ );
36
+ } else if (spec.type === "mcp") {
37
+ operations = spec.tools.map((t) =>
38
+ compact
39
+ ? { id: t.name, description: t.description }
40
+ : { id: t.name, description: t.description, inputSchema: t.inputSchema }
41
+ );
42
+ } else {
43
+ // graphql
44
+ operations = spec.operations.map((op) =>
45
+ compact
46
+ ? { id: op.name, kind: op.kind }
47
+ : {
48
+ id: op.name,
49
+ kind: op.kind,
50
+ description: op.description,
51
+ args: op.args.map((a) => a.name),
52
+ returnType: op.returnType,
53
+ isDeprecated: op.isDeprecated,
54
+ }
55
+ );
56
+
57
+ if (tag) {
58
+ operations = operations.filter((op) => op.kind === tag);
59
+ }
60
+ }
61
+
62
+ if (filter) {
63
+ operations = operations.filter((op) => JSON.stringify(op).toLowerCase().includes(filter));
64
+ }
65
+
66
+ const total = operations.length;
67
+
68
+ if (offset > 0) operations = operations.slice(offset);
69
+ if (limit > 0) operations = operations.slice(0, limit);
70
+
71
+ out({
72
+ type: spec.type,
73
+ total,
74
+ showing: operations.length,
75
+ offset: offset || 0,
76
+ operations,
77
+ });
78
+ }