felo-ai 0.2.2 → 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.
package/README.en.md CHANGED
@@ -21,12 +21,9 @@ Search the web for up-to-date information and get AI-synthesized answers. Ideal
21
21
  - **Claude Code**: After installing the skill, it triggers automatically, or type `/felo-ai your question`
22
22
  - **Examples**: `felo search "Tokyo weather"`, `felo search "React 19 new features" --verbose`
23
23
 
24
- ### Capability 2: Generate PPT
24
+ ### Capability 2: Generate PPT (Felo Slides)
25
25
 
26
- Describe a topic in one sentence and Felo generates a slideshow. The job runs in the cloud; when done, you get an **online document link** to open in your browser.
27
-
28
- - **Terminal**: `felo slides "your topic or description"`
29
- - **Examples**: `felo slides "Felo product intro, 3 slides"`, `felo slides "Introduction to React" --poll-timeout 300`
26
+ Terminal: `felo slides "your topic"`. In Claude Code: install `npx @claude/skills add felo-slides`, then `/felo-slides your topic`. You get an online document link when done. Examples: `felo slides "Felo product intro, 3 slides"`, `felo slides "Introduction to React"`.
30
27
 
31
28
  ---
32
29
 
@@ -62,17 +59,39 @@ Get your API key at [felo.ai](https://felo.ai) (Settings → API Keys).
62
59
  | `felo config set FELO_API_KEY <key>` | Save API key |
63
60
  | `felo config get/list/path/unset` | View / list / path / remove config |
64
61
 
62
+ ### Examples
63
+
64
+ **Search**
65
+
66
+ ```bash
67
+ felo search "Tokyo weather"
68
+ felo search "MacBook Air M3 price"
69
+ felo search "React 19 new features" --verbose
70
+ npx felo-ai search "Tokyo weather"
71
+ ```
72
+
73
+ **Slides**
74
+
75
+ ```bash
76
+ felo slides "Felo product intro, 3 slides"
77
+ felo slides "Introduction to React"
78
+ felo slides "Q4 2024 business review, 10 pages" --poll-timeout 300
79
+ npx felo-ai slides "Tokyo travel guide, 5 slides"
80
+ ```
81
+
65
82
  ---
66
83
 
67
- ## Claude Code Skill
84
+ ## Claude Code Skills
68
85
 
69
- Install the skill:
86
+ **Search** — Install and use real-time search:
70
87
 
71
88
  ```bash
72
- npx @claude/skills add felo-ai
89
+ npx @claude/skills add felo-search
73
90
  ```
74
91
 
75
- After setting `FELO_API_KEY`, ask Claude things like “What’s the weather in Tokyo today?” or “React 19 new features and search will trigger automatically. PPT generation is available only via the terminal with `felo slides`.
92
+ After setting `FELO_API_KEY`, ask Claude things like “What’s the weather in Tokyo today?” or “React 19 new features”; the search skill triggers automatically (or use `/felo-search your question`).
93
+
94
+ **Slides (PPT)** — `npx @claude/skills add felo-slides`, then `/felo-slides your topic`. Same `FELO_API_KEY`. [Details →](./felo-slides/README.md)
76
95
 
77
96
  ---
78
97
 
package/README.md CHANGED
@@ -1,30 +1,27 @@
1
- # Felo Skills for Claude Code
1
+ # Felo AI CLI
2
2
 
3
- **Ask anything. Get current answers powered by AI.**
3
+ **Ask anything. Get current answers. Generate slides from a prompt.**
4
4
 
5
- Real-time web search powered by Felo AI. Works in Chinese, English, Japanese, and Korean.
5
+ [npm package: **felo-ai**](https://www.npmjs.com/package/felo-ai) — Real-time search and PPT generation from the terminal. Also works as Claude Code skills. Supports Chinese, English, Japanese, and Korean.
6
6
 
7
- [![Setup Time](https://img.shields.io/badge/setup-2%20minutes-blue)]() [![License](https://img.shields.io/badge/license-MIT-green)]()
7
+ [![npm version](https://img.shields.io/npm/v/felo-ai.svg)](https://www.npmjs.com/package/felo-ai) [![License](https://img.shields.io/badge/license-MIT-green)]()
8
8
 
9
9
  ---
10
10
 
11
- ## Felo CLI (Terminal)
12
-
13
- Use Felo search from the terminal without opening Claude Code.
14
-
15
- ### Install
11
+ ## Install (CLI)
16
12
 
17
13
  ```bash
18
- npm install -g felo-search
14
+ npm install -g felo-ai
19
15
  ```
20
16
 
21
- Or run without installing (uses latest published version):
17
+ Run without installing:
22
18
 
23
19
  ```bash
24
- npx felo-search search "Tokyo weather"
20
+ npx felo-ai search "Tokyo weather"
21
+ npx felo-ai slides "Introduction to React, 5 slides"
25
22
  ```
26
23
 
27
- After install, the command is `felo` (from the package name `felo-search`).
24
+ After install, the command is `felo` (package name: **felo-ai**).
28
25
 
29
26
  ### Configure API key
30
27
 
@@ -53,32 +50,47 @@ Get your API key from [felo.ai](https://felo.ai) (Settings → API Keys). Enviro
53
50
  | Command | Description |
54
51
  | ------------------------------------ | ----------------------------------------------------- |
55
52
  | `felo search "<query>"` | Search for current info (weather, news, prices, etc.) |
53
+ | `felo slides "<prompt>"` | Generate PPT; returns link when done |
56
54
  | `felo config set FELO_API_KEY <key>` | Save API key to config |
57
55
  | `felo config get FELO_API_KEY` | Print stored key |
58
56
  | `felo config list` | List config keys |
59
57
  | `felo config path` | Show config file path |
60
- | `felo summarize` / `felo translate` | Planned; use `felo search` for now |
61
58
 
62
59
  ### Examples
63
60
 
61
+ **Search**
62
+
64
63
  ```bash
65
64
  felo search "Tokyo weather"
66
65
  felo search "MacBook Air M3 price"
67
66
  felo search "React 19 new features" --verbose
68
67
  felo search "Hangzhou tomorrow weather" --json
69
- npx felo-search search "Tokyo weather"
68
+ npx felo-ai search "Tokyo weather"
69
+ ```
70
+
71
+ **Slides**
72
+
73
+ ```bash
74
+ felo slides "Felo product intro, 3 slides"
75
+ felo slides "Introduction to React"
76
+ felo slides "Q4 2024 business review, 10 pages" --poll-timeout 300
77
+ npx felo-ai slides "Tokyo travel guide, 5 slides"
70
78
  ```
71
79
 
72
80
  ### CLI FAQ
73
81
 
74
82
  - **Key not found?** Run `felo config set FELO_API_KEY <key>` or set the `FELO_API_KEY` environment variable.
75
83
  - **Request timeout?** Use `felo search "query" --timeout 120` (default 60 seconds). 5xx errors are retried automatically with backoff.
84
+ - **Slides taking long?** Use `felo slides "topic" --poll-timeout 300` (default 1200s) to limit wait.
76
85
  - **Where is config stored?** Run `felo config path` to see the file (e.g. `~/.felo/config.json`).
77
- - **Streaming?** Not yet; when the Felo API supports streaming, the CLI can be updated to stream output.
78
86
 
79
87
  ---
80
88
 
81
- ## Quick Start (Claude Code Skill)
89
+ ## Claude Code Skills (optional)
90
+
91
+ This repo also provides **Claude Code** skills. If you use [Claude Code](https://claude.ai/code), you can install search and/or slides as skills so Claude can run them in chat.
92
+
93
+ ### Quick Start (Search skill)
82
94
 
83
95
  Install the skill:
84
96
 
@@ -110,6 +122,8 @@ Ask Claude: "What's the weather in Tokyo today?"
110
122
 
111
123
  **You're done!** The skill triggers automatically for any question needing current information.
112
124
 
125
+ **Felo Slides (PPT):** In terminal run `felo slides "your topic"`. In Claude Code install with `npx @claude/skills add felo-slides`, then use `/felo-slides your topic`. See [felo-slides](./felo-slides/README.md).
126
+
113
127
  ---
114
128
 
115
129
  ## Usage Examples
@@ -284,6 +298,10 @@ Real-time web search with AI-generated answers.
284
298
 
285
299
  **[View skill documentation →](./felo-search/)**
286
300
 
301
+ ### felo-slides
302
+
303
+ Generate PPT: in terminal use `felo slides "your topic"`, in Claude Code use `/felo-slides your topic`. **[View skill documentation →](./felo-slides/)**
304
+
287
305
  ---
288
306
 
289
307
  ## Contributing
@@ -302,6 +320,7 @@ Run CLI tests: `npm test`
302
320
 
303
321
  ## Links
304
322
 
323
+ - **[npm: felo-ai](https://www.npmjs.com/package/felo-ai)** — CLI package
305
324
  - **[Felo Open Platform](https://openapi.felo.ai/docs/)** — Get your API key
306
325
  - **[API Documentation](https://openapi.felo.ai/docs/api-reference/v2/chat.html)** — API reference
307
326
  - **[Claude Code](https://claude.ai/code)** — AI assistant CLI
@@ -321,7 +340,7 @@ Run CLI tests: `npm test`
321
340
 
322
341
  ## License
323
342
 
324
- MIT License — see [LICENSE](./felo-search/LICENSE) for details.
343
+ MIT — see [LICENSE](./felo-search/LICENSE) in the repo for details.
325
344
 
326
345
  ---
327
346
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "felo-ai",
3
- "version": "0.2.2",
4
- "description": "Felo AI CLI - real-time search from the terminal",
3
+ "version": "0.2.4",
4
+ "description": "Felo AI CLI - real-time search and PPT generation from the terminal",
5
5
  "type": "module",
6
6
  "main": "src/cli.js",
7
7
  "bin": {
package/src/cli.js CHANGED
@@ -9,6 +9,31 @@ import * as config from './config.js';
9
9
  const require = createRequire(import.meta.url);
10
10
  const pkg = require('../package.json');
11
11
 
12
+ /** Delay (ms) before process.exit to let Windows libuv finish handle cleanup. */
13
+ const EXIT_DELAY_MS = 50;
14
+
15
+ /**
16
+ * Flush stdout then stderr, then exit after a short delay. Avoids Node.js
17
+ * Windows UV_HANDLE_CLOSING assertion when process.exit() runs while streams
18
+ * or other handles are still closing.
19
+ * @param {number} code - Exit code.
20
+ */
21
+ function flushStdioThenExit(code) {
22
+ const doExit = () => setTimeout(() => process.exit(code), EXIT_DELAY_MS);
23
+ const flushStderr = () => {
24
+ if (process.stderr?.writable && !process.stderr.destroyed) {
25
+ process.stderr.write('', () => doExit());
26
+ } else {
27
+ doExit();
28
+ }
29
+ };
30
+ if (process.stdout?.writable && !process.stdout.destroyed) {
31
+ process.stdout.write('', () => flushStderr());
32
+ } else {
33
+ flushStderr();
34
+ }
35
+ }
36
+
12
37
  const program = new Command();
13
38
 
14
39
  program
@@ -31,8 +56,7 @@ program
31
56
  timeoutMs: Number.isNaN(timeoutMs) ? 60000 : timeoutMs,
32
57
  });
33
58
  process.exitCode = code;
34
- // Defer exit so stdout/stderr can flush; avoids Node.js Windows UV_HANDLE_CLOSING assertion
35
- setTimeout(() => process.exit(code), 0);
59
+ flushStdioThenExit(code);
36
60
  });
37
61
 
38
62
  program
@@ -53,8 +77,7 @@ program
53
77
  pollTimeoutMs: Number.isNaN(pollTimeoutMs) ? 1_200_000 : pollTimeoutMs,
54
78
  });
55
79
  process.exitCode = code;
56
- // Defer exit so stderr can flush; reduces Node.js Windows assertion (UV_HANDLE_CLOSING)
57
- setTimeout(() => process.exit(code), 0);
80
+ flushStdioThenExit(code);
58
81
  });
59
82
 
60
83
  const configCmd = program
@@ -68,9 +91,10 @@ configCmd
68
91
  try {
69
92
  await config.setConfig(key, value);
70
93
  console.log(`Set ${key}`);
94
+ flushStdioThenExit(0);
71
95
  } catch (e) {
72
96
  console.error('Error:', e.message);
73
- process.exit(1);
97
+ flushStdioThenExit(1);
74
98
  }
75
99
  });
76
100
 
@@ -85,9 +109,10 @@ configCmd
85
109
  } else {
86
110
  console.log(config.maskValueForDisplay(key, value));
87
111
  }
112
+ flushStdioThenExit(0);
88
113
  } catch (e) {
89
114
  console.error('Error:', e.message);
90
- process.exit(1);
115
+ flushStdioThenExit(1);
91
116
  }
92
117
  });
93
118
 
@@ -100,12 +125,13 @@ configCmd
100
125
  const keys = Object.keys(c);
101
126
  if (keys.length === 0) {
102
127
  console.log('No config set. Use: felo config set FELO_API_KEY <key>');
103
- return;
128
+ } else {
129
+ keys.forEach((k) => console.log(k));
104
130
  }
105
- keys.forEach((k) => console.log(k));
131
+ flushStdioThenExit(0);
106
132
  } catch (e) {
107
133
  console.error('Error:', e.message);
108
- process.exit(1);
134
+ flushStdioThenExit(1);
109
135
  }
110
136
  });
111
137
 
@@ -116,9 +142,10 @@ configCmd
116
142
  try {
117
143
  await config.unsetConfig(key);
118
144
  console.log(`Unset ${key}`);
145
+ flushStdioThenExit(0);
119
146
  } catch (e) {
120
147
  console.error('Error:', e.message);
121
- process.exit(1);
148
+ flushStdioThenExit(1);
122
149
  }
123
150
  });
124
151
 
@@ -127,6 +154,7 @@ configCmd
127
154
  .description('Show config file path')
128
155
  .action(() => {
129
156
  console.log(config.getConfigPath());
157
+ flushStdioThenExit(0);
130
158
  });
131
159
 
132
160
  program
@@ -135,7 +163,7 @@ program
135
163
  .argument('[input]', 'text or URL to summarize')
136
164
  .action(() => {
137
165
  console.error('summarize: not yet implemented. Use felo search for now.');
138
- process.exit(1);
166
+ flushStdioThenExit(1);
139
167
  });
140
168
 
141
169
  program
@@ -144,7 +172,7 @@ program
144
172
  .argument('[text]', 'text to translate')
145
173
  .action(() => {
146
174
  console.error('translate: not yet implemented. Use felo search for now.');
147
- process.exit(1);
175
+ flushStdioThenExit(1);
148
176
  });
149
177
 
150
178
  program.parse();
package/src/search.js CHANGED
@@ -38,7 +38,6 @@ async function fetchWithTimeoutAndRetry(url, options, timeoutMs = DEFAULT_TIMEOU
38
38
  ...options,
39
39
  signal: controller.signal,
40
40
  });
41
- clearTimeout(timeoutId);
42
41
  // Retry on 5xx (server errors)
43
42
  if (res.status >= 500 && attempt < MAX_RETRIES) {
44
43
  const delay = RETRY_BASE_MS * Math.pow(2, attempt);
@@ -47,7 +46,6 @@ async function fetchWithTimeoutAndRetry(url, options, timeoutMs = DEFAULT_TIMEOU
47
46
  }
48
47
  return res;
49
48
  } catch (err) {
50
- clearTimeout(timeoutId);
51
49
  lastError = err;
52
50
  if (err.name === 'AbortError') {
53
51
  throw new Error(`Request timed out after ${timeoutMs / 1000}s`);
@@ -58,6 +56,8 @@ async function fetchWithTimeoutAndRetry(url, options, timeoutMs = DEFAULT_TIMEOU
58
56
  continue;
59
57
  }
60
58
  throw lastError;
59
+ } finally {
60
+ clearTimeout(timeoutId);
61
61
  }
62
62
  }
63
63
  throw lastError;