aicoolies 0.1.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.
Files changed (3) hide show
  1. package/README.md +48 -0
  2. package/aicoolies.mjs +149 -0
  3. package/package.json +39 -0
package/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # aicoolies
2
+
3
+ Official CLI for the [aicoolies](https://aicoolies.com) developer-tools knowledge graph.
4
+
5
+ Every command is an unauthenticated read of the public API. There is no API key, no signup, and no
6
+ configuration — the CLI has zero runtime dependencies and talks to production over `fetch`.
7
+
8
+ ```bash
9
+ npx aicoolies meta
10
+ ```
11
+
12
+ ## Commands
13
+
14
+ | Command | What it does |
15
+ | --- | --- |
16
+ | `aicoolies meta` | Collection counts and freshness timestamps (`/api/agents/meta`) |
17
+ | `aicoolies context [--include tools,reviews] [--compact] [--category slug]` | Filterable catalog index (`/api/agents/context`) |
18
+ | `aicoolies search <query>` | Search tools by name or slug in the CC-BY dataset |
19
+ | `aicoolies tool <slug>` | One tool record from the CC-BY dataset |
20
+ | `aicoolies resources` | Official developer resource URLs, fetched through the aicoolies MCP server |
21
+ | `aicoolies openapi` | The OpenAPI 3.1 document |
22
+ | `aicoolies --version` | Print the CLI version |
23
+
24
+ Output is JSON on stdout, so it pipes into `jq` cleanly:
25
+
26
+ ```bash
27
+ npx aicoolies context --include tools --compact | jq '.tools | length'
28
+ ```
29
+
30
+ ## Environment
31
+
32
+ | Variable | Default | Purpose |
33
+ | --- | --- | --- |
34
+ | `AICOOLIES_URL` | `https://aicoolies.com` | API origin, for pointing the CLI at a local instance |
35
+
36
+ ## Errors
37
+
38
+ Failures are [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) problem documents. The CLI prints the
39
+ machine-readable `code` and the human-readable `detail`, and includes the retry delay when the public
40
+ rate limit is hit.
41
+
42
+ ## Related
43
+
44
+ - [Developer portal](https://aicoolies.com/developers) — API docs, versioning, rate limits, error model
45
+ - [OpenAPI](https://aicoolies.com/openapi.json) · [MCP server](https://aicoolies.com/mcp) · [llms.txt](https://aicoolies.com/llms.txt)
46
+ - [Open data](https://aicoolies.com/data) — CC-BY 4.0 tool, comparison, and review datasets
47
+
48
+ MIT licensed. The catalog datasets are CC-BY 4.0, attribution `aicoolies.com`.
package/aicoolies.mjs ADDED
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from 'node:fs'
3
+ import { fileURLToPath } from 'node:url'
4
+
5
+ const BASE = process.env.AICOOLIES_URL ?? 'https://aicoolies.com'
6
+
7
+ function packageVersion() {
8
+ try {
9
+ // The CLI ships as its own package: the manifest sits next to this file
10
+ // both in the repo and in the published tarball.
11
+ const manifest = readFileSync(fileURLToPath(new URL('./package.json', import.meta.url)), 'utf8')
12
+ return JSON.parse(manifest).version ?? '0.0.0'
13
+ } catch {
14
+ return '0.0.0'
15
+ }
16
+ }
17
+
18
+ const HELP = `aicoolies — official CLI for the aicoolies.com catalog
19
+
20
+ Usage:
21
+ aicoolies meta Collection counts and freshness timestamps
22
+ aicoolies context [options] Catalog index (--include tools,reviews --compact --category slug)
23
+ aicoolies search <query> Search tools by name or slug
24
+ aicoolies tool <slug> One tool record from the CC-BY dataset
25
+ aicoolies resources Official developer resource URLs (via the MCP server)
26
+ aicoolies openapi OpenAPI 3.1 document
27
+ aicoolies --version
28
+ aicoolies --help
29
+
30
+ Every command is an unauthenticated read of the public API — no API key, no signup.
31
+
32
+ Environment:
33
+ AICOOLIES_URL API origin (default ${BASE})
34
+ `
35
+
36
+ function flag(args, name) {
37
+ const index = args.indexOf(name)
38
+ if (index === -1) return undefined
39
+ return args[index + 1]
40
+ }
41
+
42
+ async function readJson(path) {
43
+ const response = await fetch(`${BASE}${path}`, {
44
+ headers: { Accept: 'application/json' },
45
+ })
46
+ if (!response.ok) {
47
+ // The API answers failures with RFC 9457 problem documents; surface the
48
+ // machine-readable code and the human detail instead of a bare status.
49
+ const problem = await response.json().catch(() => null)
50
+ if (problem && typeof problem === 'object' && problem.code) {
51
+ const retry = problem.retry_after_seconds ? ` (retry in ${problem.retry_after_seconds}s)` : ''
52
+ throw new Error(
53
+ `${path} failed: ${problem.code} — ${problem.detail ?? problem.title}${retry}`,
54
+ )
55
+ }
56
+ throw new Error(`${path} returned ${response.status}`)
57
+ }
58
+ return response.json()
59
+ }
60
+
61
+ async function callMcpTool(name, args = {}) {
62
+ const response = await fetch(`${BASE}/mcp`, {
63
+ method: 'POST',
64
+ headers: {
65
+ 'Content-Type': 'application/json',
66
+ Accept: 'application/json, text/event-stream',
67
+ },
68
+ body: JSON.stringify({
69
+ jsonrpc: '2.0',
70
+ id: 1,
71
+ method: 'tools/call',
72
+ params: { name, arguments: args },
73
+ }),
74
+ })
75
+ const payload = await response.json()
76
+ if (payload.error) throw new Error(`${name} failed: ${payload.error.message}`)
77
+ return payload.result?.structuredContent ?? payload.result
78
+ }
79
+
80
+ async function main(argv) {
81
+ const [command, ...rest] = argv
82
+ if (!command || command === '--help' || command === 'help' || command === '-h') {
83
+ process.stdout.write(HELP)
84
+ return
85
+ }
86
+
87
+ if (command === '--version' || command === '-v' || command === 'version') {
88
+ process.stdout.write(`${packageVersion()}\n`)
89
+ return
90
+ }
91
+
92
+ switch (command) {
93
+ case 'meta': {
94
+ process.stdout.write(`${JSON.stringify(await readJson('/api/agents/meta'), null, 2)}\n`)
95
+ return
96
+ }
97
+ case 'resources': {
98
+ process.stdout.write(
99
+ `${JSON.stringify(await callMcpTool('list_developer_resources'), null, 2)}\n`,
100
+ )
101
+ return
102
+ }
103
+ case 'openapi': {
104
+ process.stdout.write(`${JSON.stringify(await readJson('/openapi.json'), null, 2)}\n`)
105
+ return
106
+ }
107
+ case 'context': {
108
+ const params = new URLSearchParams()
109
+ const include = flag(rest, '--include')
110
+ const category = flag(rest, '--category')
111
+ if (include) params.set('include', include)
112
+ if (category) params.set('category', category)
113
+ if (rest.includes('--compact')) params.set('compact', 'true')
114
+ const query = params.toString()
115
+ process.stdout.write(
116
+ `${JSON.stringify(await readJson(`/api/agents/context${query ? `?${query}` : ''}`), null, 2)}\n`,
117
+ )
118
+ return
119
+ }
120
+ case 'search': {
121
+ const q = rest.join(' ').trim().toLowerCase()
122
+ if (!q) throw new Error('search needs a query')
123
+ const payload = await readJson('/data/tools.json')
124
+ const tools = Array.isArray(payload.tools) ? payload.tools : []
125
+ const hits = tools
126
+ .filter((tool) => `${tool.slug} ${tool.name}`.toLowerCase().includes(q))
127
+ .slice(0, 15)
128
+ process.stdout.write(`${JSON.stringify(hits, null, 2)}\n`)
129
+ return
130
+ }
131
+ case 'tool': {
132
+ const slug = rest[0]
133
+ if (!slug) throw new Error('tool needs a slug')
134
+ const payload = await readJson('/data/tools.json')
135
+ const tools = Array.isArray(payload.tools) ? payload.tools : []
136
+ const tool = tools.find((entry) => entry.slug === slug)
137
+ if (!tool) throw new Error(`unknown tool: ${slug}`)
138
+ process.stdout.write(`${JSON.stringify(tool, null, 2)}\n`)
139
+ return
140
+ }
141
+ default:
142
+ throw new Error(`unknown command: ${command}`)
143
+ }
144
+ }
145
+
146
+ main(process.argv.slice(2)).catch((error) => {
147
+ process.stderr.write(`${error instanceof Error ? error.message : error}\n`)
148
+ process.exitCode = 1
149
+ })
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "aicoolies",
3
+ "version": "0.1.0",
4
+ "description": "Official CLI for the aicoolies.com developer-tools catalog — unauthenticated reads of the public API, MCP, and CC-BY datasets",
5
+ "license": "MIT",
6
+ "homepage": "https://aicoolies.com/developers#cli",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/rasitakyol/aicoolies.git",
10
+ "directory": "packages/cli"
11
+ },
12
+ "bugs": {
13
+ "url": "https://aicoolies.com/contact"
14
+ },
15
+ "keywords": [
16
+ "aicoolies",
17
+ "cli",
18
+ "developer-tools",
19
+ "ai-tools",
20
+ "catalog",
21
+ "mcp",
22
+ "openapi",
23
+ "agents"
24
+ ],
25
+ "type": "module",
26
+ "bin": {
27
+ "aicoolies": "aicoolies.mjs"
28
+ },
29
+ "files": [
30
+ "aicoolies.mjs",
31
+ "README.md"
32
+ ],
33
+ "engines": {
34
+ "node": ">=20.9.0"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public"
38
+ }
39
+ }