Skip to content

Commit 3d39574

Browse files
authored
feat(dictionary): add dictionary search, synonyms, and examples adapters (#241)
* feat(dictionary): add dictionary search, synonyms, and examples adapters * feat: Improve dictionary commands with positional word arguments, URL encoding, enhanced phonetic parsing, and new E2E tests.
1 parent b1067b6 commit 3d39574

9 files changed

Lines changed: 141 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ Run `opencli list` for the live registry.
145145
| **bloomberg** | `main` `markets` `economics` `industries` `tech` `politics` `businessweek` `opinions` `feeds` `news` | Public / Browser |
146146
| **ctrip** | `search` | Browser |
147147
| **devto** | `top` `tag` `user` | Public |
148+
| **dictionary** | `search` `synonyms` `examples` | Public |
148149
| **arxiv** | `search` `paper` | Public |
149150
| **wikipedia** | `search` `summary` `random` `trending` | Public |
150151
| **hackernews** | `top` `new` `best` `ask` `show` `jobs` `search` `user` | Public |

README.zh-CN.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ npm install -g @jackwener/opencli@latest
147147
| **bloomberg** | `main` `markets` `economics` `industries` `tech` `politics` `businessweek` `opinions` `feeds` `news` | 公共 API / 浏览器 |
148148
| **ctrip** | `search` | 浏览器 |
149149
| **devto** | `top` `tag` `user` | 公开 |
150+
| **dictionary** | `search` `synonyms` `examples` | 公开 |
150151
| **arxiv** | `search` `paper` | 公开 |
151152
| **wikipedia** | `search` `summary` `random` `trending` | 公开 |
152153
| **hackernews** | `top` `new` `best` `ask` `show` `jobs` `search` `user` | 公共 API |

docs/.vitepress/config.mts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export default defineConfig({
8181
items: [
8282
{ text: 'HackerNews', link: '/adapters/browser/hackernews' },
8383
{ text: 'Dev.to', link: '/adapters/browser/devto' },
84+
{ text: 'Dictionary', link: '/adapters/browser/dictionary' },
8485
{ text: 'BBC', link: '/adapters/browser/bbc' },
8586
{ text: 'Apple Podcasts', link: '/adapters/browser/apple-podcasts' },
8687
{ text: 'Xiaoyuzhou', link: '/adapters/browser/xiaoyuzhou' },
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Dictionary
2+
3+
**Mode**: 🌐 Public · **Domain**: `api.dictionaryapi.dev`
4+
5+
Search the open dictionary to quickly fetch native definitions, part of speech contexts, and phonetic pronunciations directly in your IDE terminal.
6+
7+
## Commands
8+
9+
| Command | Description |
10+
|---------|-------------|
11+
| `opencli dictionary search` | Fetch the exact definition of a word |
12+
| `opencli dictionary synonyms` | Find related synonyms for a word |
13+
| `opencli dictionary examples` | Read real-world sentence usage examples |
14+
15+
## Usage Examples
16+
17+
```bash
18+
# Look up a complex term
19+
opencli dictionary search serendipity
20+
21+
# Discover phonetics
22+
opencli dictionary search ephemeral
23+
```
24+
25+
## Prerequisites
26+
27+
- No browser required — utilizes the fast, open JSON definitions API.

docs/adapters/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Run `opencli list` for the live registry.
4545
| **[hackernews](/adapters/browser/hackernews)** | `top` `new` `best` `ask` `show` `jobs` `search` `user` | 🌐 Public |
4646
| **[bbc](/adapters/browser/bbc)** | `news` | 🌐 Public |
4747
| **[devto](/adapters/browser/devto)** | `top` `tag` `user` | 🌐 Public |
48+
| **[dictionary](/adapters/browser/dictionary)** | `search` `synonyms` `examples` | 🌐 Public |
4849
| **[apple-podcasts](/adapters/browser/apple-podcasts)** | `search` `episodes` `top` | 🌐 Public |
4950
| **[xiaoyuzhou](/adapters/browser/xiaoyuzhou)** | `podcast` `podcast-episodes` `episode` | 🌐 Public |
5051
| **[yahoo-finance](/adapters/browser/yahoo-finance)** | `quote` | 🌐 Public |

src/clis/dictionary/examples.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
site: dictionary
2+
name: examples
3+
description: Read real-world example sentences utilizing the word
4+
domain: api.dictionaryapi.dev
5+
strategy: public
6+
browser: false
7+
8+
args:
9+
word:
10+
type: string
11+
required: true
12+
positional: true
13+
description: Word to get example sentences for
14+
15+
pipeline:
16+
- fetch:
17+
url: "https://api.dictionaryapi.dev/api/v2/entries/en/${{ args.word | urlencode }}"
18+
19+
- map:
20+
word: "${{ item.word }}"
21+
example: "${{ (() => { if (item.meanings) { for (const m of item.meanings) { if (m.definitions) { for (const d of m.definitions) { if (d.example) return d.example; } } } } return 'No example found in API.'; })() }}"
22+
23+
- limit: 1
24+
25+
columns: [word, example]

src/clis/dictionary/search.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
site: dictionary
2+
name: search
3+
description: Search the Free Dictionary API for definitions, parts of speech, and pronunciations.
4+
domain: api.dictionaryapi.dev
5+
strategy: public
6+
browser: false
7+
8+
args:
9+
word:
10+
type: string
11+
required: true
12+
positional: true
13+
description: Word to define (e.g., serendipity)
14+
15+
pipeline:
16+
- fetch:
17+
url: "https://api.dictionaryapi.dev/api/v2/entries/en/${{ args.word | urlencode }}"
18+
19+
- map:
20+
word: "${{ item.word }}"
21+
phonetic: "${{ (() => { if (item.phonetic) return item.phonetic; if (item.phonetics) { for (const p of item.phonetics) { if (p.text) return p.text; } } return ''; })() }}"
22+
type: "${{ item.meanings[0].partOfSpeech }}"
23+
definition: "${{ item.meanings[0].definitions[0].definition }}"
24+
25+
- limit: 1
26+
27+
columns: [word, phonetic, type, definition]

src/clis/dictionary/synonyms.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
site: dictionary
2+
name: synonyms
3+
description: Find synonyms for a specific word
4+
domain: api.dictionaryapi.dev
5+
strategy: public
6+
browser: false
7+
8+
args:
9+
word:
10+
type: string
11+
required: true
12+
positional: true
13+
description: Word to find synonyms for (e.g., serendipity)
14+
15+
pipeline:
16+
- fetch:
17+
url: "https://api.dictionaryapi.dev/api/v2/entries/en/${{ args.word | urlencode }}"
18+
19+
- map:
20+
word: "${{ item.word }}"
21+
synonyms: "${{ (() => { const s = new Set(); if (item.meanings) { for (const m of item.meanings) { if (m.synonyms) { for (const syn of m.synonyms) s.add(syn); } if (m.definitions) { for (const d of m.definitions) { if (d.synonyms) { for (const syn of d.synonyms) s.add(syn); } } } } } const arr = Array.from(s); return arr.length > 0 ? arr.slice(0, 5).join(', ') : 'No synonyms found in API.'; })() }}"
22+
23+
- limit: 1
24+
25+
columns: [word, synonyms]

tests/e2e/public-commands.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,4 +489,37 @@ describe('public commands E2E', () => {
489489
expect(data.length).toBeGreaterThan(0);
490490
expect(data.every((d: any) => d.type === 'image')).toBe(true);
491491
}, 30_000);
492+
493+
// ── dictionary (public API, browser: false) ──
494+
it('dictionary search returns word definitions', async () => {
495+
const { stdout, code } = await runCli(['dictionary', 'search', 'serendipity', '-f', 'json']);
496+
expect(code).toBe(0);
497+
const data = parseJsonOutput(stdout);
498+
expect(Array.isArray(data)).toBe(true);
499+
expect(data.length).toBeGreaterThanOrEqual(1);
500+
expect(data[0]).toHaveProperty('word', 'serendipity');
501+
expect(data[0]).toHaveProperty('phonetic');
502+
expect(data[0]).toHaveProperty('definition');
503+
}, 30_000);
504+
505+
it('dictionary synonyms returns synonyms', async () => {
506+
const { stdout, code } = await runCli(['dictionary', 'synonyms', 'serendipity', '-f', 'json']);
507+
expect(code).toBe(0);
508+
const data = parseJsonOutput(stdout);
509+
expect(Array.isArray(data)).toBe(true);
510+
expect(data.length).toBeGreaterThanOrEqual(1);
511+
expect(data[0]).toHaveProperty('word', 'serendipity');
512+
expect(data[0]).toHaveProperty('synonyms');
513+
}, 30_000);
514+
515+
it('dictionary examples returns examples', async () => {
516+
const { stdout, code } = await runCli(['dictionary', 'examples', 'perfect', '-f', 'json']);
517+
expect(code).toBe(0);
518+
const data = parseJsonOutput(stdout);
519+
expect(Array.isArray(data)).toBe(true);
520+
expect(data.length).toBeGreaterThanOrEqual(1);
521+
expect(data[0]).toHaveProperty('word', 'perfect');
522+
expect(data[0]).toHaveProperty('example');
523+
}, 30_000);
524+
}, 30_000);
492525
});

0 commit comments

Comments
 (0)