felo-ai 0.2.45 → 0.2.47
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 +39 -10
- package/felo-livedoc/SKILL.md +6 -0
- package/felo-livedoc/scripts/run_livedoc.mjs +12 -0
- package/felo-slides/SKILL.md +1 -0
- package/felo-slides/scripts/run_ppt_task.mjs +10 -2
- package/package.json +3 -2
- package/src/cli.js +19 -0
- package/src/livedoc.js +23 -0
- package/src/slides.js +7 -2
package/README.md
CHANGED
|
@@ -127,23 +127,48 @@ felo apple-buy-advisor "Is it worth upgrading to iPad Air 13?"
|
|
|
127
127
|
/apple-buy-advisor Is it worth upgrading to iPad Air 13?
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
+
**Twitter Writer** — [full options →](./felo-twitter-writer/SKILL.md)
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# Use as Claude Code skill
|
|
134
|
+
/felo-twitter-writer Analyze @paulg's tweet style and extract a style DNA
|
|
135
|
+
/felo-twitter-writer Write 3 tweets about AI trends in @paulg's style
|
|
136
|
+
/felo-twitter-writer Write a Twitter thread about why most startups fail
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**SuperAgent** — [full options →](./felo-superAgent/README.md)
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# Use as Claude Code skill
|
|
143
|
+
/felo-superagent What is the latest news about AI?
|
|
144
|
+
/felo-superagent Tell me more --thread-id <thread_short_id>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Run script directly
|
|
149
|
+
node felo-superAgent/scripts/run_superagent.mjs --query "What is quantum computing?"
|
|
150
|
+
node felo-superAgent/scripts/run_superagent.mjs --query "Tell me more" --thread-id <thread_short_id>
|
|
151
|
+
```
|
|
152
|
+
|
|
130
153
|
**[See 40+ more examples →](./docs/EXAMPLES.md)**
|
|
131
154
|
|
|
132
155
|
---
|
|
133
156
|
|
|
134
157
|
## Skills Overview
|
|
135
158
|
|
|
136
|
-
|
|
159
|
+
9 skills across search, content generation, web scraping, social media, knowledge base, shopping advice, Twitter writing, and AI conversation:
|
|
137
160
|
|
|
138
|
-
| Skill | Description | Docs
|
|
139
|
-
| --------------------------- | ------------------------------------------------------------- |
|
|
140
|
-
| **felo-search** | Real-time web search with AI answers. Triggers automatically. | [→](./felo-search/)
|
|
141
|
-
| **felo-slides** | Generate PPT from a prompt | [→](./felo-slides/)
|
|
142
|
-
| **felo-web-fetch** | Fetch and extract webpage content | [→](./felo-web-fetch/)
|
|
143
|
-
| **felo-youtube-subtitling** | Fetch YouTube video subtitles | [→](./felo-youtube-subtitling/)
|
|
144
|
-
| **felo-x-search** | Search X (Twitter) tweets, users, replies | [→](./felo-x-search/SKILL.md)
|
|
145
|
-
| **felo-livedoc** | Manage knowledge bases and semantic retrieval | [→](./felo-livedoc/)
|
|
146
|
-
| **apple-buy-advisor** | Research and compare Apple products before you buy | [→](./apple-buy-advisor/)
|
|
161
|
+
| Skill | Description | Docs |
|
|
162
|
+
| --------------------------- | ------------------------------------------------------------- | ----------------------------------------- |
|
|
163
|
+
| **felo-search** | Real-time web search with AI answers. Triggers automatically. | [→](./felo-search/) |
|
|
164
|
+
| **felo-slides** | Generate PPT from a prompt | [→](./felo-slides/) |
|
|
165
|
+
| **felo-web-fetch** | Fetch and extract webpage content | [→](./felo-web-fetch/) |
|
|
166
|
+
| **felo-youtube-subtitling** | Fetch YouTube video subtitles | [→](./felo-youtube-subtitling/) |
|
|
167
|
+
| **felo-x-search** | Search X (Twitter) tweets, users, replies | [→](./felo-x-search/SKILL.md) |
|
|
168
|
+
| **felo-livedoc** | Manage knowledge bases and semantic retrieval | [→](./felo-livedoc/) |
|
|
169
|
+
| **apple-buy-advisor** | Research and compare Apple products before you buy | [→](./apple-buy-advisor/) |
|
|
170
|
+
| **felo-twitter-writer** | Analyze tweet style DNA and compose tweets, threads, X posts | [→](./felo-twitter-writer/SKILL.md) |
|
|
171
|
+
| **felo-superAgent** | AI conversation with real-time streaming, continuous threads | [→](./felo-superAgent/README.md) |
|
|
147
172
|
|
|
148
173
|
---
|
|
149
174
|
|
|
@@ -163,6 +188,8 @@ felo apple-buy-advisor "Is it worth upgrading to iPad Air 13?"
|
|
|
163
188
|
/plugin install felo-x-search@felo-ai
|
|
164
189
|
/plugin install felo-livedoc@felo-ai
|
|
165
190
|
/plugin install apple-buy-advisor@felo-ai
|
|
191
|
+
/plugin install felo-twitter-writer@felo-ai
|
|
192
|
+
/plugin install felo-superAgent@felo-ai
|
|
166
193
|
```
|
|
167
194
|
|
|
168
195
|
### ClawHub
|
|
@@ -177,6 +204,8 @@ clawhub install felo-youtube-subtitling
|
|
|
177
204
|
clawhub install felo-x-search
|
|
178
205
|
clawhub install felo-livedoc
|
|
179
206
|
clawhub install apple-buy-advisor
|
|
207
|
+
clawhub install felo-twitter-writer
|
|
208
|
+
clawhub install felo-superAgent
|
|
180
209
|
```
|
|
181
210
|
|
|
182
211
|
### Gemini CLI
|
package/felo-livedoc/SKILL.md
CHANGED
|
@@ -119,6 +119,11 @@ node ~/.agents/skills/felo-livedoc/scripts/run_livedoc.mjs update-resource SHORT
|
|
|
119
119
|
node ~/.agents/skills/felo-livedoc/scripts/run_livedoc.mjs update-resource SHORT_ID RESOURCE_ID --snippet "New summary" --thumbnail "https://example.com/thumb.png"
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
+
**Update resource content (ai_doc type only — also auto-updates snippet from first 2000 bytes):**
|
|
123
|
+
```bash
|
|
124
|
+
node ~/.agents/skills/felo-livedoc/scripts/run_livedoc.mjs update-resource-content SHORT_ID RESOURCE_ID --content "New content here"
|
|
125
|
+
```
|
|
126
|
+
|
|
122
127
|
### Semantic Retrieval
|
|
123
128
|
|
|
124
129
|
**Route relevant resources by query:**
|
|
@@ -240,6 +245,7 @@ The API returns JSON with this structure:
|
|
|
240
245
|
- `short_id` — unique identifier (use this for all operations)
|
|
241
246
|
- `name` — LiveDoc name
|
|
242
247
|
- `description` — LiveDoc description
|
|
248
|
+
- `is_shared` — `true` if this LiveDoc was shared with you (not owned by you)
|
|
243
249
|
- `created_at` / `modified_at` — timestamps
|
|
244
250
|
|
|
245
251
|
**Resource object:**
|
|
@@ -95,6 +95,7 @@ function formatLiveDoc(doc) {
|
|
|
95
95
|
out += `- ID: \`${doc.short_id}\`\n`;
|
|
96
96
|
if (doc.description) out += `- Description: ${doc.description}\n`;
|
|
97
97
|
if (doc.icon) out += `- Icon: ${doc.icon}\n`;
|
|
98
|
+
if (doc.is_shared != null) out += `- Shared: ${doc.is_shared}\n`;
|
|
98
99
|
if (doc.created_at) out += `- Created: ${doc.created_at}\n`;
|
|
99
100
|
if (doc.modified_at) out += `- Modified: ${doc.modified_at}\n`;
|
|
100
101
|
out += '\n';
|
|
@@ -167,6 +168,7 @@ function usage() {
|
|
|
167
168
|
' upload <short_id> Upload file (--file required, --convert optional)',
|
|
168
169
|
' remove-resource <short_id> <resource_id> Delete a resource',
|
|
169
170
|
' update-resource <short_id> <resource_id> Update resource title/snippet/thumbnail',
|
|
171
|
+
' update-resource-content <short_id> <resource_id> Update ai_doc content (--content required)',
|
|
170
172
|
' retrieve <short_id> Semantic search (--query required, --resource-ids optional)',
|
|
171
173
|
' route <short_id> Route relevant resources by query (--query required)',
|
|
172
174
|
' download <short_id> <resource_id> Download source file to disk',
|
|
@@ -432,6 +434,16 @@ async function main() {
|
|
|
432
434
|
code = 0;
|
|
433
435
|
break;
|
|
434
436
|
}
|
|
437
|
+
case 'update-resource-content': {
|
|
438
|
+
if (!shortId || !resourceId) { console.error('ERROR: short_id and resource_id are required'); break; }
|
|
439
|
+
if (!args.content) { console.error('ERROR: --content is required'); break; }
|
|
440
|
+
spinnerId = startSpinner('Updating resource content');
|
|
441
|
+
const payload = await apiRequest('PUT', `/livedocs/${shortId}/resources/${resourceId}/content`, { content: args.content }, apiKey, apiBase, timeoutMs);
|
|
442
|
+
if (json) { console.log(JSON.stringify(payload, null, 2)); }
|
|
443
|
+
else { process.stdout.write('Resource content updated.\n'); }
|
|
444
|
+
code = 0;
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
435
447
|
case 'retrieve': {
|
|
436
448
|
if (!shortId) { console.error('ERROR: short_id is required'); break; }
|
|
437
449
|
if (!args.query) { console.error('ERROR: --query is required'); break; }
|
package/felo-slides/SKILL.md
CHANGED
|
@@ -90,6 +90,7 @@ Script behavior:
|
|
|
90
90
|
|
|
91
91
|
- Creates task via `POST https://openapi.felo.ai/v2/ppts`
|
|
92
92
|
- Supports optional `--theme <id>` to apply a PPT theme (sends `ppt_config.ai_theme_id`)
|
|
93
|
+
- Supports optional `--livedoc-id <id>` to reuse an existing LiveDoc instead of auto-creating a new one
|
|
93
94
|
- Supports optional `--task-id <id>` to resume polling an existing task (skips creation)
|
|
94
95
|
- Polls via `GET https://openapi.felo.ai/v2/tasks/{task_id}/historical`
|
|
95
96
|
- Treats `COMPLETED`/`SUCCESS` as success terminal (case-insensitive)
|
|
@@ -15,6 +15,7 @@ function usage() {
|
|
|
15
15
|
' --query <text> PPT prompt (required unless --task-id is given)',
|
|
16
16
|
' --task-id <id> Resume polling an existing task (skip creation)',
|
|
17
17
|
' --theme <id> PPT theme ID (from ppt-themes)',
|
|
18
|
+
' --livedoc-id <id> Reuse an existing LiveDoc instead of auto-creating one',
|
|
18
19
|
' --interval <seconds> Poll interval, default 10',
|
|
19
20
|
' --max-wait <seconds> Max wait time, default 1800',
|
|
20
21
|
' --timeout <seconds> Request timeout, default 60',
|
|
@@ -30,6 +31,7 @@ function parseArgs(argv) {
|
|
|
30
31
|
query: '',
|
|
31
32
|
taskId: '',
|
|
32
33
|
theme: '',
|
|
34
|
+
livedocId: '',
|
|
33
35
|
intervalSec: DEFAULT_INTERVAL_SEC,
|
|
34
36
|
maxWaitSec: DEFAULT_MAX_WAIT_SEC,
|
|
35
37
|
timeoutSec: DEFAULT_TIMEOUT_SEC,
|
|
@@ -54,6 +56,9 @@ function parseArgs(argv) {
|
|
|
54
56
|
} else if (a === '--theme') {
|
|
55
57
|
out.theme = argv[i + 1] ?? '';
|
|
56
58
|
i += 1;
|
|
59
|
+
} else if (a === '--livedoc-id') {
|
|
60
|
+
out.livedocId = argv[i + 1] ?? '';
|
|
61
|
+
i += 1;
|
|
57
62
|
} else if (a === '--interval') {
|
|
58
63
|
out.intervalSec = Number.parseInt(argv[i + 1] ?? '', 10);
|
|
59
64
|
i += 1;
|
|
@@ -138,11 +143,14 @@ function extractTaskUrls(historicalData, createData) {
|
|
|
138
143
|
};
|
|
139
144
|
}
|
|
140
145
|
|
|
141
|
-
async function createTask(apiKey, apiBase, query, timeoutMs, theme) {
|
|
146
|
+
async function createTask(apiKey, apiBase, query, timeoutMs, theme, livedocId) {
|
|
142
147
|
const reqBody = { query };
|
|
143
148
|
if (theme) {
|
|
144
149
|
reqBody.ppt_config = { ai_theme_id: theme };
|
|
145
150
|
}
|
|
151
|
+
if (livedocId) {
|
|
152
|
+
reqBody.livedoc_short_id = livedocId;
|
|
153
|
+
}
|
|
146
154
|
const payload = await fetchJson(
|
|
147
155
|
`${apiBase}/v2/ppts`,
|
|
148
156
|
{
|
|
@@ -211,7 +219,7 @@ async function main() {
|
|
|
211
219
|
}
|
|
212
220
|
} else {
|
|
213
221
|
// Create a new task
|
|
214
|
-
createData = await createTask(apiKey, apiBase, args.query, timeoutMs, args.theme);
|
|
222
|
+
createData = await createTask(apiKey, apiBase, args.query, timeoutMs, args.theme, args.livedocId || undefined);
|
|
215
223
|
taskId = createData.task_id;
|
|
216
224
|
if (args.verbose) {
|
|
217
225
|
console.error(`Task ID: ${taskId}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "felo-ai",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.47",
|
|
4
4
|
"description": "Felo AI CLI - real-time search, PPT generation, SuperAgent conversation, LiveDoc management, web fetch, YouTube subtitles, LiveDoc knowledge base, and X (Twitter) search from the terminal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/cli.js",
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"url": "git+https://github.com/Felo-Inc/felo-skills.git"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"commander": "^12.0.0"
|
|
35
|
+
"commander": "^12.0.0",
|
|
36
|
+
"felo-ai": "^0.2.43"
|
|
36
37
|
},
|
|
37
38
|
"scripts": {
|
|
38
39
|
"test": "node --test tests/",
|
package/src/cli.js
CHANGED
|
@@ -89,6 +89,7 @@ program
|
|
|
89
89
|
)
|
|
90
90
|
.option("--theme <id>", "PPT theme ID (from ppt-themes command)")
|
|
91
91
|
.option("--task-id <id>", "resume polling an existing task (skip creation)")
|
|
92
|
+
.option("--livedoc-id <id>", "reuse an existing LiveDoc short_id instead of auto-creating a new one")
|
|
92
93
|
.action(async (query, opts) => {
|
|
93
94
|
if (!query && !opts.taskId) {
|
|
94
95
|
console.error("Error: provide a <query> or --task-id to resume an existing task");
|
|
@@ -105,6 +106,7 @@ program
|
|
|
105
106
|
pollTimeoutMs: Number.isNaN(pollTimeoutMs) ? 1_200_000 : pollTimeoutMs,
|
|
106
107
|
pptConfig,
|
|
107
108
|
taskId: opts.taskId,
|
|
109
|
+
livedocShortId: opts.livedocId || undefined,
|
|
108
110
|
});
|
|
109
111
|
process.exitCode = code;
|
|
110
112
|
flushStdioThenExit(code);
|
|
@@ -774,6 +776,23 @@ livedocCmd
|
|
|
774
776
|
flushStdioThenExit(code);
|
|
775
777
|
});
|
|
776
778
|
|
|
779
|
+
livedocCmd
|
|
780
|
+
.command("update-resource-content <short_id> <resource_id>")
|
|
781
|
+
.description("Update the content of an ai_doc resource (also auto-updates snippet)")
|
|
782
|
+
.requiredOption("--content <text>", "new content for the resource")
|
|
783
|
+
.option("-j, --json", "output raw JSON")
|
|
784
|
+
.option("-t, --timeout <seconds>", "request timeout in seconds", "60")
|
|
785
|
+
.action(async (shortId, resourceId, opts) => {
|
|
786
|
+
const timeoutMs = parseInt(opts.timeout, 10) * 1000;
|
|
787
|
+
const code = await livedoc.updateResourceContent(shortId, resourceId, {
|
|
788
|
+
content: opts.content,
|
|
789
|
+
json: opts.json,
|
|
790
|
+
timeoutMs: Number.isNaN(timeoutMs) ? 60000 : timeoutMs,
|
|
791
|
+
});
|
|
792
|
+
process.exitCode = code;
|
|
793
|
+
flushStdioThenExit(code);
|
|
794
|
+
});
|
|
795
|
+
|
|
777
796
|
livedocCmd
|
|
778
797
|
.command("content <short_id> <resource_id>")
|
|
779
798
|
.description("Get extracted text content of a resource")
|
package/src/livedoc.js
CHANGED
|
@@ -91,6 +91,7 @@ function formatLiveDoc(doc) {
|
|
|
91
91
|
out += `- ID: \`${doc.short_id}\`\n`;
|
|
92
92
|
if (doc.description) out += `- Description: ${doc.description}\n`;
|
|
93
93
|
if (doc.icon) out += `- Icon: ${doc.icon}\n`;
|
|
94
|
+
if (doc.is_shared != null) out += `- Shared: ${doc.is_shared}\n`;
|
|
94
95
|
if (doc.created_at) out += `- Created: ${doc.created_at}\n`;
|
|
95
96
|
if (doc.modified_at) out += `- Modified: ${doc.modified_at}\n`;
|
|
96
97
|
out += '\n';
|
|
@@ -823,6 +824,28 @@ export async function createTaskComment(shortId, taskId, opts = {}) {
|
|
|
823
824
|
} finally { stopSpinner(spinnerId); }
|
|
824
825
|
}
|
|
825
826
|
|
|
827
|
+
export async function updateResourceContent(shortId, resourceId, opts = {}) {
|
|
828
|
+
const apiKey = await getApiKey();
|
|
829
|
+
if (!apiKey) { console.error(NO_KEY_MESSAGE.trim()); return 1; }
|
|
830
|
+
if (!shortId) { process.stderr.write('ERROR: short_id is required.\n'); return 1; }
|
|
831
|
+
if (!resourceId) { process.stderr.write('ERROR: resource_id is required.\n'); return 1; }
|
|
832
|
+
if (!opts.content) { process.stderr.write('ERROR: --content is required.\n'); return 1; }
|
|
833
|
+
|
|
834
|
+
const apiBase = await getApiBase();
|
|
835
|
+
const timeoutMs = opts.timeoutMs || DEFAULT_TIMEOUT_MS;
|
|
836
|
+
const spinnerId = startSpinner('Updating resource content');
|
|
837
|
+
|
|
838
|
+
try {
|
|
839
|
+
const payload = await apiRequest('PUT', `/livedocs/${shortId}/resources/${resourceId}/content`, { content: opts.content }, apiKey, apiBase, timeoutMs);
|
|
840
|
+
if (opts.json) { console.log(JSON.stringify(payload, null, 2)); return 0; }
|
|
841
|
+
process.stdout.write('Resource content updated.\n');
|
|
842
|
+
return 0;
|
|
843
|
+
} catch (err) {
|
|
844
|
+
process.stderr.write(`Failed to update resource content: ${err?.message || err}\n`);
|
|
845
|
+
return 1;
|
|
846
|
+
} finally { stopSpinner(spinnerId); }
|
|
847
|
+
}
|
|
848
|
+
|
|
826
849
|
export async function pptRetrieve(shortId, opts = {}) {
|
|
827
850
|
const apiKey = await getApiKey();
|
|
828
851
|
if (!apiKey) { console.error(NO_KEY_MESSAGE.trim()); return 1; }
|
package/src/slides.js
CHANGED
|
@@ -53,13 +53,17 @@ function normalizeTaskStatus(status) {
|
|
|
53
53
|
* @param {number} timeoutMs
|
|
54
54
|
* @param {string} apiBase
|
|
55
55
|
* @param {{ ai_theme_id?: string }} [pptConfig]
|
|
56
|
+
* @param {string} [livedocShortId]
|
|
56
57
|
*/
|
|
57
|
-
async function createPptTask(apiKey, query, timeoutMs, apiBase, pptConfig) {
|
|
58
|
+
async function createPptTask(apiKey, query, timeoutMs, apiBase, pptConfig, livedocShortId) {
|
|
58
59
|
const url = `${apiBase}/v2/ppts`;
|
|
59
60
|
const body = { query: query.trim() };
|
|
60
61
|
if (pptConfig && Object.keys(pptConfig).length > 0) {
|
|
61
62
|
body.ppt_config = pptConfig;
|
|
62
63
|
}
|
|
64
|
+
if (livedocShortId) {
|
|
65
|
+
body.livedoc_short_id = livedocShortId;
|
|
66
|
+
}
|
|
63
67
|
const res = await fetchWithTimeoutAndRetry(
|
|
64
68
|
url,
|
|
65
69
|
{
|
|
@@ -176,7 +180,8 @@ export async function slides(query, options = {}) {
|
|
|
176
180
|
query,
|
|
177
181
|
requestTimeoutMs,
|
|
178
182
|
apiBase,
|
|
179
|
-
options.pptConfig
|
|
183
|
+
options.pptConfig,
|
|
184
|
+
options.livedocShortId
|
|
180
185
|
);
|
|
181
186
|
taskId = createResult.task_id;
|
|
182
187
|
|