@professorragna/pokeapi-cli 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.
package/README.md ADDED
@@ -0,0 +1,101 @@
1
+ # pokeapi-cli
2
+
3
+ Command-line interface for the [PokeAPI](https://pokeapi.co/docs/v2). Fetches Pokemon data and prints the **exact, untransformed JSON** response from the API.
4
+
5
+ **Binary:** `pkmn` · **Package:** `pokeapi-cli` on npm
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ # Global install (pick one)
11
+ npm i -g pokeapi-cli
12
+ pnpm add -g pokeapi-cli
13
+ yarn global add pokeapi-cli
14
+
15
+ # Run without global install
16
+ npx pokeapi-cli pkmn pokemon pikachu
17
+ ```
18
+
19
+ ### Prerequisites
20
+
21
+ - **Node.js** >= 18.17
22
+
23
+ ### Verify
24
+
25
+ ```bash
26
+ pkmn --version
27
+ pkmn --help
28
+ ```
29
+
30
+ ## Commands
31
+
32
+ | Command | Endpoint | Description |
33
+ |---------|----------|-------------|
34
+ | `pkmn pokemon <nameOrId>` | `GET /pokemon/{name}` | Battle data: types, base stats, abilities, moves, sprites |
35
+ | `pkmn pokemon-species <nameOrId>` | `GET /pokemon-species/{name}` | Species data: evolution, egg groups, flavor text, legendary flags |
36
+ | `pkmn ability <nameOrId>` | `GET /ability/{name}` | Ability data: effect text, Pokemon with the ability, flavor text |
37
+ | `pkmn item <nameOrId>` | `GET /item/{name}` | Item data: cost, fling, attributes, category, effect/flavor text, held-by |
38
+ | `pkmn move <nameOrId>` | `GET /move/{name}` | Move data: power, PP, accuracy, type, damage class, effect, stat changes |
39
+ | `pkmn type <nameOrId>` | `GET /type/{name}` | Type data: damage relations (offensive/defensive), generation, move damage class, Pokemon and moves of the type |
40
+
41
+ ## Usage
42
+
43
+ ```bash
44
+ # By name
45
+ pkmn pokemon incineroar
46
+ pkmn pokemon-species wormadam
47
+ pkmn ability intimidate
48
+ pkmn item potion
49
+ pkmn move flamethrower
50
+ pkmn type fire
51
+
52
+ # By national dex id
53
+ pkmn pokemon 727
54
+ pkmn pokemon-species 413
55
+ pkmn ability 22
56
+ pkmn item 1
57
+ pkmn move 53
58
+ pkmn type 10
59
+
60
+ # Hyphenated names (spaces/underscores normalized automatically)
61
+ pkmn pokemon "flutter mane"
62
+ pkmn pokemon flutter-mane
63
+ ```
64
+
65
+ ## Output
66
+
67
+ Commands print the **raw PokeAPI JSON** to stdout with 2-space indentation. Keys and values are exactly as returned by the API (snake_case, no transformation). Errors are written to stderr as JSON with a non-zero exit code.
68
+
69
+ Pipe to [jq](https://jqlang.github.io/jq/) for field selection:
70
+
71
+ ```bash
72
+ pkmn pokemon pikachu | jq '.types'
73
+ pkmn pokemon-species pikachu | jq '.genera'
74
+ pkmn ability intimidate | jq '.effect_entries'
75
+ pkmn item potion | jq '.effect_entries'
76
+ pkmn move flamethrower | jq '.effect_entries'
77
+ pkmn type fire | jq '.damage_relations'
78
+ ```
79
+
80
+ ## Agent skill
81
+
82
+ This CLI ships an agent skill at [`skills/pokeapi-cli/SKILL.md`](skills/pokeapi-cli/SKILL.md) for any harness that supports the [Agent Skills specification](https://agentskills.io) (Claude Code, Cursor, Codex, and others).
83
+
84
+ Install it with the [skills CLI](https://github.com/vercel-labs/skills):
85
+
86
+ ```bash
87
+ npx skills add jpbullalayao/pokeapi-cli
88
+ ```
89
+
90
+ ## Development
91
+
92
+ ```bash
93
+ cd general/pokeapi-cli
94
+ npm install
95
+ npm run build
96
+ node bin/pkmn.js pokemon pikachu
97
+ ```
98
+
99
+ ## License
100
+
101
+ MIT
package/bin/pkmn.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../dist/cli.js';
package/dist/cli.js ADDED
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { isCliError, writeCliError } from './core/errors.js';
4
+ import { registerAbility } from './resources/ability.js';
5
+ import { registerItem } from './resources/item.js';
6
+ import { registerMove } from './resources/move.js';
7
+ import { registerPokemonSpecies } from './resources/pokemon-species.js';
8
+ import { registerPokemon } from './resources/pokemon.js';
9
+ import { registerType } from './resources/type.js';
10
+ import { getVersion } from './util/version.js';
11
+ const program = new Command();
12
+ program.version(getVersion(), '-V, --version');
13
+ program
14
+ .name('pkmn')
15
+ .description('Command-line interface for the PokeAPI (pokemon, pokemon-species, ability, item, move, and type endpoints).\n\nDocs: https://pokeapi.co/docs/v2');
16
+ registerPokemon(program);
17
+ registerPokemonSpecies(program);
18
+ registerAbility(program);
19
+ registerItem(program);
20
+ registerMove(program);
21
+ registerType(program);
22
+ program.addHelpText('after', `
23
+ Output:
24
+ Commands print the raw PokeAPI JSON response to stdout (2-space indentation).
25
+ Errors are written to stderr as JSON with a non-zero exit code.
26
+
27
+ Examples:
28
+ $ pkmn pokemon pikachu
29
+ $ pkmn pokemon-species pikachu
30
+ $ pkmn ability intimidate
31
+ $ pkmn item potion
32
+ $ pkmn move flamethrower
33
+ $ pkmn type fire
34
+ `);
35
+ program
36
+ .command('help [topic]')
37
+ .description('Show help (alias: pkmn --help)')
38
+ .action((topic) => {
39
+ if (topic) {
40
+ process.stderr.write(`Use: pkmn ${topic} --help (or: pkmn --help ${topic})\n`);
41
+ }
42
+ program.help();
43
+ });
44
+ program.action(() => {
45
+ program.help();
46
+ });
47
+ function printError(e) {
48
+ if (isCliError(e)) {
49
+ writeCliError(e);
50
+ return;
51
+ }
52
+ if (e instanceof Error) {
53
+ process.stderr.write(`${JSON.stringify({ error: { code: 'generic-error', message: e.message, exit: 1 } }, null, 2)}\n`);
54
+ }
55
+ else {
56
+ process.stderr.write(`${JSON.stringify({ error: { code: 'generic-error', message: 'Unknown error', exit: 1 } }, null, 2)}\n`);
57
+ }
58
+ process.exitCode = 1;
59
+ }
60
+ try {
61
+ await program.parseAsync(process.argv, { from: 'node' });
62
+ }
63
+ catch (e) {
64
+ printError(e);
65
+ if (process.exitCode == null || process.exitCode === undefined) {
66
+ process.exitCode = 1;
67
+ }
68
+ process.exit(process.exitCode);
69
+ }
70
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,eAAe,CAAC,CAAC;AAE/C,OAAO;KACJ,IAAI,CAAC,MAAM,CAAC;KACZ,WAAW,CACV,iJAAiJ,CAClJ,CAAC;AAEJ,eAAe,CAAC,OAAO,CAAC,CAAC;AACzB,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAChC,eAAe,CAAC,OAAO,CAAC,CAAC;AACzB,YAAY,CAAC,OAAO,CAAC,CAAC;AACtB,YAAY,CAAC,OAAO,CAAC,CAAC;AACtB,YAAY,CAAC,OAAO,CAAC,CAAC;AAEtB,OAAO,CAAC,WAAW,CACjB,OAAO,EACP;;;;;;;;;;;;CAYD,CACA,CAAC;AAEF,OAAO;KACJ,OAAO,CAAC,cAAc,CAAC;KACvB,WAAW,CAAC,gCAAgC,CAAC;KAC7C,MAAM,CAAC,CAAC,KAAc,EAAE,EAAE;IACzB,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,KAAK,4BAA4B,KAAK,KAAK,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,CAAC,IAAI,EAAE,CAAC;AACjB,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE;IAClB,OAAO,CAAC,IAAI,EAAE,CAAC;AACjB,CAAC,CAAC,CAAC;AAEH,SAAS,UAAU,CAAC,CAAU;IAC5B,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;QAClB,aAAa,CAAC,CAAC,CAAC,CAAC;QACjB,OAAO;IACT,CAAC;IACD,IAAI,CAAC,YAAY,KAAK,EAAE,CAAC;QACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAClG,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CACxG,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC;AAED,IAAI,CAAC;IACH,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AAC3D,CAAC;AAAC,OAAO,CAAC,EAAE,CAAC;IACX,UAAU,CAAC,CAAC,CAAC,CAAC;IACd,IAAI,OAAO,CAAC,QAAQ,IAAI,IAAI,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC/D,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,44 @@
1
+ const exitCodeByKind = {
2
+ 'generic-error': 1,
3
+ 'usage-error': 2,
4
+ 'not-found': 1,
5
+ 'validation-error': 4,
6
+ 'api-error': 5,
7
+ 'network-error': 6,
8
+ };
9
+ export class CliError extends Error {
10
+ code;
11
+ exit;
12
+ hint;
13
+ docsUrl;
14
+ cause;
15
+ constructor(message, options) {
16
+ super(message);
17
+ this.name = 'CliError';
18
+ this.code = options.code;
19
+ this.hint = options.hint;
20
+ this.docsUrl = options.docsUrl;
21
+ this.cause = options.cause;
22
+ this.exit = exitCodeByKind[options.code];
23
+ }
24
+ }
25
+ export function isCliError(e) {
26
+ return e instanceof CliError;
27
+ }
28
+ export function formatJsonError(e) {
29
+ return {
30
+ error: {
31
+ code: e.code,
32
+ message: e.message,
33
+ ...(e.hint ? { hint: e.hint } : {}),
34
+ exit: e.exit,
35
+ ...(e.docsUrl ? { docs: e.docsUrl } : {}),
36
+ },
37
+ };
38
+ }
39
+ /** Writes a CliError to stderr and sets `process.exitCode`. */
40
+ export function writeCliError(e) {
41
+ process.stderr.write(`${JSON.stringify(formatJsonError(e), null, 2)}\n`);
42
+ process.exitCode = e.exit;
43
+ }
44
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAQA,MAAM,cAAc,GAA6C;IAC/D,eAAe,EAAE,CAAC;IAClB,aAAa,EAAE,CAAC;IAChB,WAAW,EAAE,CAAC;IACd,kBAAkB,EAAE,CAAC;IACrB,WAAW,EAAE,CAAC;IACd,eAAe,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,OAAO,QAAS,SAAQ,KAAK;IACxB,IAAI,CAAY;IAChB,IAAI,CAAwB;IAC5B,IAAI,CAAU;IACd,OAAO,CAAU;IACjB,KAAK,CAAW;IAEzB,YACE,OAAe,EACf,OAKC;QAED,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,CAAC;CACF;AAED,MAAM,UAAU,UAAU,CAAC,CAAU;IACnC,OAAO,CAAC,YAAY,QAAQ,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,CAAW;IAGzC,OAAO;QACL,KAAK,EAAE;YACL,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,OAAO,EAAE,CAAC,CAAC,OAAO;YAClB,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnC,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1C;KACF,CAAC;AACJ,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,aAAa,CAAC,CAAW;IACvC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IACzE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,91 @@
1
+ import { getVersion } from '../util/version.js';
2
+ import { CliError } from './errors.js';
3
+ const API_BASE = 'https://pokeapi.co/api/v2';
4
+ const MAX_RETRIES = 3;
5
+ function sleep(ms) {
6
+ return new Promise((r) => setTimeout(r, ms));
7
+ }
8
+ function jitterMs(base) {
9
+ return base + Math.floor(Math.random() * 200);
10
+ }
11
+ function buildUrl(path) {
12
+ const p = path.startsWith('/') ? path : `/${path}`;
13
+ const base = API_BASE.endsWith('/') ? API_BASE.slice(0, -1) : API_BASE;
14
+ return base + p;
15
+ }
16
+ function userAgent() {
17
+ return `pokeapi-cli/${getVersion()} node/${process.version}`;
18
+ }
19
+ export class ApiClient {
20
+ async getJson(path, signal) {
21
+ const url = buildUrl(path);
22
+ const requestSignal = signal ?? AbortSignal.timeout(30_000);
23
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
24
+ let res;
25
+ try {
26
+ res = await fetch(url, {
27
+ method: 'GET',
28
+ headers: {
29
+ Accept: 'application/json',
30
+ 'User-Agent': userAgent(),
31
+ },
32
+ signal: requestSignal,
33
+ });
34
+ }
35
+ catch (e) {
36
+ const name = e?.name;
37
+ const isAbort = name === 'AbortError';
38
+ if (isAbort) {
39
+ throw new CliError('Request timed out', { code: 'network-error', cause: e });
40
+ }
41
+ if (attempt >= MAX_RETRIES) {
42
+ throw new CliError(e instanceof Error ? e.message : 'Network error', {
43
+ code: 'network-error',
44
+ cause: e,
45
+ });
46
+ }
47
+ await sleep(jitterMs(200 * 2 ** attempt));
48
+ continue;
49
+ }
50
+ if (res.status === 404) {
51
+ throw new CliError('Pokemon not found', {
52
+ code: 'not-found',
53
+ hint: `No resource at ${path}`,
54
+ docsUrl: 'https://pokeapi.co/docs/v2',
55
+ });
56
+ }
57
+ if (res.status === 429 || (res.status >= 500 && res.status < 600)) {
58
+ if (attempt >= MAX_RETRIES) {
59
+ const body = await this.safeReadText(res);
60
+ throw new CliError(`API request failed: ${res.status} ${res.statusText}${body ? ` — ${body.slice(0, 200)}` : ''}`, { code: 'api-error' });
61
+ }
62
+ const ra = res.headers.get('retry-after');
63
+ const waitSec = ra ? Number.parseInt(ra, 10) : Number.NaN;
64
+ const delay = Number.isFinite(waitSec) && waitSec > 0 ? waitSec * 1000 : jitterMs(200 * 2 ** attempt);
65
+ await sleep(delay);
66
+ continue;
67
+ }
68
+ if (!res.ok) {
69
+ const body = await this.safeReadText(res);
70
+ throw new CliError(`API error: ${res.status} ${res.statusText}${body ? ` — ${body.slice(0, 200)}` : ''}`, { code: 'api-error' });
71
+ }
72
+ const text = await res.text();
73
+ try {
74
+ return text ? JSON.parse(text) : null;
75
+ }
76
+ catch (e) {
77
+ throw new CliError('Invalid JSON in API response', { code: 'validation-error', cause: e });
78
+ }
79
+ }
80
+ throw new CliError('Request failed after retries', { code: 'network-error' });
81
+ }
82
+ async safeReadText(res) {
83
+ try {
84
+ return await res.text();
85
+ }
86
+ catch {
87
+ return '';
88
+ }
89
+ }
90
+ }
91
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/core/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,MAAM,QAAQ,GAAG,2BAA2B,CAAC;AAE7C,MAAM,WAAW,GAAG,CAAC,CAAC;AAEtB,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IACnD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IACvE,OAAO,IAAI,GAAG,CAAC,CAAC;AAClB,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,eAAe,UAAU,EAAE,SAAS,OAAO,CAAC,OAAO,EAAE,CAAC;AAC/D,CAAC;AAED,MAAM,OAAO,SAAS;IACpB,KAAK,CAAC,OAAO,CAAC,IAAY,EAAE,MAAoB;QAC9C,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC3B,MAAM,aAAa,GAAG,MAAM,IAAI,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAE5D,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC;YACxD,IAAI,GAAa,CAAC;YAClB,IAAI,CAAC;gBACH,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;oBACrB,MAAM,EAAE,KAAK;oBACb,OAAO,EAAE;wBACP,MAAM,EAAE,kBAAkB;wBAC1B,YAAY,EAAE,SAAS,EAAE;qBAC1B;oBACD,MAAM,EAAE,aAAa;iBACtB,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,MAAM,IAAI,GAAI,CAA2B,EAAE,IAAI,CAAC;gBAChD,MAAM,OAAO,GAAG,IAAI,KAAK,YAAY,CAAC;gBACtC,IAAI,OAAO,EAAE,CAAC;oBACZ,MAAM,IAAI,QAAQ,CAAC,mBAAmB,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;gBAC/E,CAAC;gBACD,IAAI,OAAO,IAAI,WAAW,EAAE,CAAC;oBAC3B,MAAM,IAAI,QAAQ,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE;wBACnE,IAAI,EAAE,eAAe;wBACrB,KAAK,EAAE,CAAC;qBACT,CAAC,CAAC;gBACL,CAAC;gBACD,MAAM,KAAK,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC;gBAC1C,SAAS;YACX,CAAC;YAED,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACvB,MAAM,IAAI,QAAQ,CAAC,mBAAmB,EAAE;oBACtC,IAAI,EAAE,WAAW;oBACjB,IAAI,EAAE,kBAAkB,IAAI,EAAE;oBAC9B,OAAO,EAAE,4BAA4B;iBACtC,CAAC,CAAC;YACL,CAAC;YAED,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC;gBAClE,IAAI,OAAO,IAAI,WAAW,EAAE,CAAC;oBAC3B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;oBAC1C,MAAM,IAAI,QAAQ,CAChB,uBAAuB,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAC9F,EAAE,IAAI,EAAE,WAAW,EAAE,CACtB,CAAC;gBACJ,CAAC;gBACD,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;gBAC1C,MAAM,OAAO,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;gBAC1D,MAAM,KAAK,GACT,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC;gBAC1F,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;gBACnB,SAAS;YACX,CAAC;YAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;gBAC1C,MAAM,IAAI,QAAQ,CAChB,cAAc,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EACrF,EAAE,IAAI,EAAE,WAAW,EAAE,CACtB,CAAC;YACJ,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;YAC9B,IAAI,CAAC;gBACH,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACxC,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,MAAM,IAAI,QAAQ,CAAC,8BAA8B,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;YAC7F,CAAC;QACH,CAAC;QAED,MAAM,IAAI,QAAQ,CAAC,8BAA8B,EAAE,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC;IAChF,CAAC;IAEO,KAAK,CAAC,YAAY,CAAC,GAAa;QACtC,IAAI,CAAC;YACH,OAAO,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,4 @@
1
+ export function printJson(data) {
2
+ process.stdout.write(`${JSON.stringify(data, null, 2)}\n`);
3
+ }
4
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/core/output.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,SAAS,CAAC,IAAa;IACrC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { ApiClient } from '../core/http.js';
2
+ import { printJson } from '../core/output.js';
3
+ import { normalizePokemonInput } from '../util/normalize.js';
4
+ const http = new ApiClient();
5
+ export function registerAbility(program) {
6
+ program
7
+ .command('ability <nameOrId>')
8
+ .description('Fetch an ability by name or id (GET /ability/{name})')
9
+ .addHelpText('after', `
10
+ Examples:
11
+ $ pkmn ability intimidate
12
+ $ pkmn ability 22
13
+ $ pkmn ability flame-body
14
+ `)
15
+ .action(async (nameOrId) => {
16
+ const id = normalizePokemonInput(nameOrId);
17
+ const data = await http.getJson(`/ability/${encodeURIComponent(id)}`);
18
+ printJson(data);
19
+ });
20
+ }
21
+ //# sourceMappingURL=ability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ability.js","sourceRoot":"","sources":["../../src/resources/ability.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,IAAI,GAAG,IAAI,SAAS,EAAE,CAAC;AAE7B,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,OAAO;SACJ,OAAO,CAAC,oBAAoB,CAAC;SAC7B,WAAW,CAAC,sDAAsD,CAAC;SACnE,WAAW,CACV,OAAO,EACP;;;;;CAKL,CACI;SACA,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,EAAE;QACjC,MAAM,EAAE,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,YAAY,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACtE,SAAS,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { ApiClient } from '../core/http.js';
2
+ import { printJson } from '../core/output.js';
3
+ import { normalizePokemonInput } from '../util/normalize.js';
4
+ const http = new ApiClient();
5
+ export function registerItem(program) {
6
+ program
7
+ .command('item <nameOrId>')
8
+ .description('Fetch an item by name or id (GET /item/{name})')
9
+ .addHelpText('after', `
10
+ Examples:
11
+ $ pkmn item potion
12
+ $ pkmn item 1
13
+ $ pkmn item master-ball
14
+ `)
15
+ .action(async (nameOrId) => {
16
+ const id = normalizePokemonInput(nameOrId);
17
+ const data = await http.getJson(`/item/${encodeURIComponent(id)}`);
18
+ printJson(data);
19
+ });
20
+ }
21
+ //# sourceMappingURL=item.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"item.js","sourceRoot":"","sources":["../../src/resources/item.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,IAAI,GAAG,IAAI,SAAS,EAAE,CAAC;AAE7B,MAAM,UAAU,YAAY,CAAC,OAAgB;IAC3C,OAAO;SACJ,OAAO,CAAC,iBAAiB,CAAC;SAC1B,WAAW,CAAC,gDAAgD,CAAC;SAC7D,WAAW,CACV,OAAO,EACP;;;;;CAKL,CACI;SACA,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,EAAE;QACjC,MAAM,EAAE,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACnE,SAAS,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { ApiClient } from '../core/http.js';
2
+ import { printJson } from '../core/output.js';
3
+ import { normalizePokemonInput } from '../util/normalize.js';
4
+ const http = new ApiClient();
5
+ export function registerMove(program) {
6
+ program
7
+ .command('move <nameOrId>')
8
+ .description('Fetch a move by name or id (GET /move/{name})')
9
+ .addHelpText('after', `
10
+ Examples:
11
+ $ pkmn move flamethrower
12
+ $ pkmn move 53
13
+ $ pkmn move close-combat
14
+ `)
15
+ .action(async (nameOrId) => {
16
+ const id = normalizePokemonInput(nameOrId);
17
+ const data = await http.getJson(`/move/${encodeURIComponent(id)}`);
18
+ printJson(data);
19
+ });
20
+ }
21
+ //# sourceMappingURL=move.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"move.js","sourceRoot":"","sources":["../../src/resources/move.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,IAAI,GAAG,IAAI,SAAS,EAAE,CAAC;AAE7B,MAAM,UAAU,YAAY,CAAC,OAAgB;IAC3C,OAAO;SACJ,OAAO,CAAC,iBAAiB,CAAC;SAC1B,WAAW,CAAC,+CAA+C,CAAC;SAC5D,WAAW,CACV,OAAO,EACP;;;;;CAKL,CACI;SACA,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,EAAE;QACjC,MAAM,EAAE,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACnE,SAAS,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { ApiClient } from '../core/http.js';
2
+ import { printJson } from '../core/output.js';
3
+ import { normalizePokemonInput } from '../util/normalize.js';
4
+ const http = new ApiClient();
5
+ export function registerPokemonSpecies(program) {
6
+ program
7
+ .command('pokemon-species <nameOrId>')
8
+ .description('Fetch a Pokemon species by name or id (GET /pokemon-species/{name})')
9
+ .addHelpText('after', `
10
+ Examples:
11
+ $ pkmn pokemon-species wormadam
12
+ $ pkmn pokemon-species 413
13
+ $ pkmn pokemon-species flutter-mane
14
+ `)
15
+ .action(async (nameOrId) => {
16
+ const id = normalizePokemonInput(nameOrId);
17
+ const data = await http.getJson(`/pokemon-species/${encodeURIComponent(id)}`);
18
+ printJson(data);
19
+ });
20
+ }
21
+ //# sourceMappingURL=pokemon-species.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pokemon-species.js","sourceRoot":"","sources":["../../src/resources/pokemon-species.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,IAAI,GAAG,IAAI,SAAS,EAAE,CAAC;AAE7B,MAAM,UAAU,sBAAsB,CAAC,OAAgB;IACrD,OAAO;SACJ,OAAO,CAAC,4BAA4B,CAAC;SACrC,WAAW,CAAC,qEAAqE,CAAC;SAClF,WAAW,CACV,OAAO,EACP;;;;;CAKL,CACI;SACA,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,EAAE;QACjC,MAAM,EAAE,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,oBAAoB,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9E,SAAS,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { ApiClient } from '../core/http.js';
2
+ import { printJson } from '../core/output.js';
3
+ import { normalizePokemonInput } from '../util/normalize.js';
4
+ const http = new ApiClient();
5
+ export function registerPokemon(program) {
6
+ program
7
+ .command('pokemon <nameOrId>')
8
+ .description('Fetch a Pokemon by name or id (GET /pokemon/{name})')
9
+ .addHelpText('after', `
10
+ Examples:
11
+ $ pkmn pokemon incineroar
12
+ $ pkmn pokemon 727
13
+ $ pkmn pokemon flutter-mane
14
+ `)
15
+ .action(async (nameOrId) => {
16
+ const id = normalizePokemonInput(nameOrId);
17
+ const data = await http.getJson(`/pokemon/${encodeURIComponent(id)}`);
18
+ printJson(data);
19
+ });
20
+ }
21
+ //# sourceMappingURL=pokemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pokemon.js","sourceRoot":"","sources":["../../src/resources/pokemon.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,IAAI,GAAG,IAAI,SAAS,EAAE,CAAC;AAE7B,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,OAAO;SACJ,OAAO,CAAC,oBAAoB,CAAC;SAC7B,WAAW,CAAC,qDAAqD,CAAC;SAClE,WAAW,CACV,OAAO,EACP;;;;;CAKL,CACI;SACA,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,EAAE;QACjC,MAAM,EAAE,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,YAAY,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACtE,SAAS,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { ApiClient } from '../core/http.js';
2
+ import { printJson } from '../core/output.js';
3
+ import { normalizePokemonInput } from '../util/normalize.js';
4
+ const http = new ApiClient();
5
+ export function registerType(program) {
6
+ program
7
+ .command('type <nameOrId>')
8
+ .description('Fetch a type by name or id (GET /type/{name})')
9
+ .addHelpText('after', `
10
+ Examples:
11
+ $ pkmn type fire
12
+ $ pkmn type 10
13
+ $ pkmn type ground
14
+ `)
15
+ .action(async (nameOrId) => {
16
+ const id = normalizePokemonInput(nameOrId);
17
+ const data = await http.getJson(`/type/${encodeURIComponent(id)}`);
18
+ printJson(data);
19
+ });
20
+ }
21
+ //# sourceMappingURL=type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type.js","sourceRoot":"","sources":["../../src/resources/type.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,IAAI,GAAG,IAAI,SAAS,EAAE,CAAC;AAE7B,MAAM,UAAU,YAAY,CAAC,OAAgB;IAC3C,OAAO;SACJ,OAAO,CAAC,iBAAiB,CAAC;SAC1B,WAAW,CAAC,+CAA+C,CAAC;SAC5D,WAAW,CACV,OAAO,EACP;;;;;CAKL,CACI;SACA,MAAM,CAAC,KAAK,EAAE,QAAgB,EAAE,EAAE;QACjC,MAAM,EAAE,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QAC3C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACnE,SAAS,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,9 @@
1
+ /** Normalize a Pokemon name or id for PokeAPI lookup. */
2
+ export function normalizePokemonInput(input) {
3
+ const trimmed = input.trim();
4
+ if (/^\d+$/.test(trimmed)) {
5
+ return trimmed;
6
+ }
7
+ return trimmed.toLowerCase().replace(/[\s_]+/g, '-');
8
+ }
9
+ //# sourceMappingURL=normalize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../src/util/normalize.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,MAAM,UAAU,qBAAqB,CAAC,KAAa;IACjD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1B,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,OAAO,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AACvD,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ const __dirname = dirname(fileURLToPath(import.meta.url));
5
+ export function getVersion() {
6
+ const pkgPath = join(__dirname, '../../package.json');
7
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
8
+ return pkg.version;
9
+ }
10
+ //# sourceMappingURL=version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/util/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE1D,MAAM,UAAU,UAAU;IACxB,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;IACtD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAwB,CAAC;IAC7E,OAAO,GAAG,CAAC,OAAO,CAAC;AACrB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@professorragna/pokeapi-cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI for the PokeAPI (pokemon and pokemon-species endpoints).",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=18.17"
8
+ },
9
+ "bin": {
10
+ "pkmn": "./bin/pkmn.js"
11
+ },
12
+ "files": [
13
+ "dist",
14
+ "bin",
15
+ "README.md",
16
+ "skills"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc -p .",
20
+ "dev": "tsc -w -p .",
21
+ "lint": "biome check ./src",
22
+ "format": "biome format --write ./src",
23
+ "typecheck": "tsc --noEmit -p ."
24
+ },
25
+ "keywords": [
26
+ "pokeapi-cli",
27
+ "pokeapi",
28
+ "pokemon",
29
+ "cli"
30
+ ],
31
+ "license": "MIT",
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/jpbullalayao/pokeapi-cli.git"
38
+ },
39
+ "dependencies": {
40
+ "commander": "^12.1.0"
41
+ },
42
+ "devDependencies": {
43
+ "@biomejs/biome": "^1.9.4",
44
+ "@types/node": "^22.9.0",
45
+ "typescript": "^5.6.2"
46
+ }
47
+ }
@@ -0,0 +1,163 @@
1
+ ---
2
+ name: pokeapi-cli
3
+ description: >-
4
+ PokeAPI CLI reference for fetching Pokemon, Pokemon species, ability, item, move,
5
+ and type data from pokeapi.co. Use when the user mentions pokeapi-cli, pkmn, Pokemon base
6
+ stats, typing, type matchups, weaknesses, resistances, abilities, moves, items, evolution,
7
+ egg groups, or needs to look up canonical Pokemon data from the command line or in agent workflows.
8
+ ---
9
+
10
+ # PokeAPI CLI (`pkmn`)
11
+
12
+ Command-line interface for the [PokeAPI](https://pokeapi.co/docs/v2): fetches Pokemon data and prints the exact JSON response.
13
+
14
+ **API documentation:** [pokeapi.co/docs/v2](https://pokeapi.co/docs/v2)
15
+
16
+ **Package:** `pokeapi-cli` on npm · **Binary:** `pkmn` · **Current version:** 0.1.0 (see `pkmn --version` after install)
17
+
18
+ ---
19
+
20
+ ## Authentication
21
+
22
+ PokeAPI is public and requires **no API key**.
23
+
24
+ ---
25
+
26
+ ## CLI structure
27
+
28
+ ```
29
+ pkmn # Root (default action shows help)
30
+ ├── pokemon <nameOrId> # GET /pokemon/{name}
31
+ ├── pokemon-species <nameOrId> # GET /pokemon-species/{name}
32
+ ├── ability <nameOrId> # GET /ability/{name}
33
+ ├── item <nameOrId> # GET /item/{name}
34
+ ├── move <nameOrId> # GET /move/{name}
35
+ └── type <nameOrId> # GET /type/{name}
36
+ ```
37
+
38
+ ---
39
+
40
+ ## Commands
41
+
42
+ ### `pkmn pokemon <nameOrId>`
43
+
44
+ Fetch a Pokemon by name or national dex id. Returns battle data from `GET /pokemon/{name}`.
45
+
46
+ **Includes:** types, base stats, abilities, moves, sprites, held items, cries, game indices.
47
+
48
+ ```bash
49
+ pkmn pokemon incineroar
50
+ pkmn pokemon 727
51
+ pkmn pokemon flutter-mane
52
+ ```
53
+
54
+ ### `pkmn pokemon-species <nameOrId>`
55
+
56
+ Fetch a Pokemon species by name or id. Returns species data from `GET /pokemon-species/{name}`.
57
+
58
+ **Includes:** gender rate, capture rate, egg groups, evolution chain, flavor text, genera, varieties, legendary/mythical flags.
59
+
60
+ ```bash
61
+ pkmn pokemon-species wormadam
62
+ pkmn pokemon-species 413
63
+ pkmn pokemon-species pikachu
64
+ ```
65
+
66
+ ### `pkmn ability <nameOrId>`
67
+
68
+ Fetch an ability by name or id. Returns ability data from `GET /ability/{name}`.
69
+
70
+ **Includes:** effect text, Pokemon that can have the ability, flavor text entries, generation, main-series flag.
71
+
72
+ ```bash
73
+ pkmn ability intimidate
74
+ pkmn ability 22
75
+ pkmn ability flame-body
76
+ ```
77
+
78
+ ### `pkmn item <nameOrId>`
79
+
80
+ Fetch an item by name or id. Returns item data from `GET /item/{name}`.
81
+
82
+ **Includes:** cost, fling power/effect, attributes, category, effect/flavor text, held-by Pokemon.
83
+
84
+ ```bash
85
+ pkmn item potion
86
+ pkmn item 1
87
+ pkmn item master-ball
88
+ ```
89
+
90
+ ### `pkmn move <nameOrId>`
91
+
92
+ Fetch a move by name or id. Returns move data from `GET /move/{name}`.
93
+
94
+ **Includes:** power, PP, accuracy, priority, type, damage class, effect entries, stat changes, target, learned-by Pokemon.
95
+
96
+ ```bash
97
+ pkmn move flamethrower
98
+ pkmn move 53
99
+ pkmn move close-combat
100
+ ```
101
+
102
+ ### `pkmn type <nameOrId>`
103
+
104
+ Fetch a type by name or id. Returns type data from `GET /type/{name}`.
105
+
106
+ **Includes:** damage relations (offensive/defensive), past damage relations, generation, move damage class, Pokemon of the type, moves of the type.
107
+
108
+ ```bash
109
+ pkmn type fire
110
+ pkmn type 10
111
+ pkmn type ground
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Output
117
+
118
+ - **Success:** raw PokeAPI JSON to stdout (2-space indentation). Keys are snake_case exactly as the API returns them. No transformation.
119
+ - **Error:** JSON object to stderr with `error.code`, `error.message`, and non-zero exit code.
120
+ - **404:** `error.code` is `not-found` when the name or id does not exist.
121
+
122
+ Use `jq` to select fields from the response:
123
+
124
+ ```bash
125
+ pkmn pokemon pikachu | jq '.stats'
126
+ pkmn pokemon-species pikachu | jq '.evolution_chain.url'
127
+ pkmn ability intimidate | jq '.effect_entries'
128
+ pkmn item potion | jq '.effect_entries'
129
+ pkmn move flamethrower | jq '.effect_entries'
130
+ pkmn type fire | jq '.damage_relations'
131
+ ```
132
+
133
+ ---
134
+
135
+ ## Agent routing
136
+
137
+ When the user asks about…
138
+
139
+ | Topic | Action |
140
+ |-------|--------|
141
+ | Pokemon typing, base stats, abilities, moves | `pkmn pokemon <name>` |
142
+ | Evolution, egg groups, legendary/mythical, flavor text | `pkmn pokemon-species <name>` |
143
+ | Ability effect text, which Pokemon have an ability | `pkmn ability <name>` |
144
+ | Item cost, effects, attributes, which Pokemon hold an item | `pkmn item <name>` |
145
+ | Move power, PP, accuracy, type, damage class, effects | `pkmn move <name>` |
146
+ | Type matchups, weaknesses, resistances, Pokemon/moves of a type | `pkmn type <name>` |
147
+
148
+ **Hard rule:** never guess base stats, typings, abilities, or learnsets — run `pkmn` and read the JSON.
149
+
150
+ Name normalization: spaces and underscores become hyphens; names are lowercased. Numeric ids pass through unchanged.
151
+
152
+ ---
153
+
154
+ ## Exit codes
155
+
156
+ | Code | Meaning |
157
+ |------|---------|
158
+ | 0 | Success |
159
+ | 1 | Not found or generic error |
160
+ | 2 | Usage error |
161
+ | 4 | Validation error (malformed API response) |
162
+ | 5 | API error |
163
+ | 6 | Network error / timeout |