felo-ai 0.2.41 → 0.2.43

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 CHANGED
@@ -240,37 +240,6 @@ We welcome contributions — report bugs, improve docs, or add new skills. Run t
240
240
 
241
241
  ---
242
242
 
243
- ## 发布到 npm(维护者)
244
-
245
- 本仓库通过 GitHub Actions 自动发布到 npm,**不要手动在 CI 里改版本号或重复发布相同版本**。
246
-
247
- - **发布一个新版本**
248
- 1. 确保代码已推到 `main`(或你用来发布的分支)
249
- 2. 选择一个**尚未在 npm 上使用过的新版本号**(语义化版本号 `MAJOR.MINOR.PATCH`)
250
- 3. 在本地打 tag 并推送,例如:
251
-
252
- ```bash
253
- git tag v0.2.24
254
- git push origin v0.2.24
255
- ```
256
-
257
- 4. GitHub Actions 会在 `push tag v*` 时自动运行:
258
- - 从 tag 名中取出版本号(`v0.2.24` → `0.2.24`)
259
- - 将 `package.json` 中的 `"version"` 同步为该版本号
260
- - 运行测试
261
- - 执行 `npm publish --provenance --access public`
262
-
263
- - **版本号约定(建议)**
264
- - **PATCH(补丁号)**:向下兼容的小修小补(bugfix、文档更新等),例如 `0.2.23` → `0.2.24`
265
- - **MINOR(次版本号)**:向下兼容的新功能,例如新增子命令或新的 skill,`0.2.0` → `0.3.0`
266
- - **MAJOR(主版本号)**:有破坏性改动时使用,例如 CLI 行为或配置不兼容旧版本,`0.x.x` → `1.0.0`
267
-
268
- - **注意事项**
269
- - npm 不允许覆盖已发布的版本,**不要重复推送同一个版本号的 tag**(例如已经发布了 `0.2.23`,再次尝试发布会收到 403 错误)
270
- - 如果某个已发布版本存在严重问题,可考虑通过 `npm deprecate` 标记为不推荐使用,而不是尝试覆盖它
271
-
272
- ---
273
-
274
243
  ## License
275
244
 
276
245
  MIT — see [LICENSE](./felo-search/LICENSE) for details.
@@ -0,0 +1,274 @@
1
+ ---
2
+ name: claw-deck
3
+ description: "Use when users need to manage ClawDeck project workspaces backed by Felo LiveDoc — creating, loading, switching workspaces, saving artifacts, querying history, or managing tasks. Triggers on workspace-related intent combined with project/client names, or on 401 UNAUTHORIZED errors from Felo API."
4
+ ---
5
+
6
+ # ClawDeck — Workspace Manager
7
+
8
+ The Agent's external brain for projects. Once active, the Agent continuously syncs tasks, artifacts, and knowledge to the corresponding LiveDoc, so anyone (including future sessions or colleagues) can load the workspace and immediately get full context.
9
+
10
+ ## Core Concepts
11
+
12
+ | Concept | Description |
13
+ |---------|-------------|
14
+ | Workspace | One project = one LiveDoc |
15
+ | Active Workspace | Session-level state; all operations auto-sync here |
16
+ | README | The Agent's memory of the project; Agent maintains proactively |
17
+ | Artifacts | Key outputs; Agent asks before saving |
18
+ | Tasks | Tracking records for substantive work; Agent maintains silently |
19
+ | Registry | `~/.claude/workspaces.json`, maps project names to LiveDoc IDs |
20
+
21
+ **Registry format:**
22
+ ```json
23
+ {
24
+ "workspaces": {
25
+ "client-acme": "abc123",
26
+ "project-x": "def456"
27
+ }
28
+ }
29
+ ```
30
+
31
+ ## Script Shorthand
32
+
33
+ In all commands below, `$SCRIPT` refers to:
34
+
35
+ ```
36
+ node felo-livedoc/scripts/run_livedoc.mjs
37
+ ```
38
+
39
+ ## When NOT to Use
40
+
41
+ - Simple chitchat or clarifying questions
42
+ - One-off generation unrelated to any project/workspace
43
+ - User has not installed the Felo LiveDoc NPM package
44
+
45
+ ---
46
+
47
+ ## Workflows
48
+
49
+ ### 0. First-Time Installation
50
+
51
+ User pastes GitHub install link → execute installation → after completion, automatically enter **Login flow (0a)**.
52
+
53
+ ### 0a. Login / Re-authorization
54
+
55
+ **Trigger conditions (either one):**
56
+ - First-time installation, Key not yet configured
57
+ - Any API call returns `{"status":401,"code":"UNAUTHORIZED","message":"Invalid API Key"}`
58
+
59
+ **Flow:**
60
+
61
+ 1. Send login link:
62
+ > "Please click the link to log in / register a Felo account: https://felo.ai/settings/api-keys
63
+ > Once done, paste the Key back to me and I'll complete the setup automatically."
64
+ 2. User pastes Key → write to config:
65
+ ```bash
66
+ export FELO_API_KEY="user's Key"
67
+ ```
68
+ Or persist to `~/.claude/env` (platform-dependent).
69
+ 3. Verify: `$SCRIPT list`
70
+ - Pass + first install → show usage introduction (see "First-Time Usage Introduction" below)
71
+ - Pass + re-authorization → "✅ Authorization updated." → retry the failed command
72
+ - Fail → "Invalid Key, please paste again."
73
+
74
+ **First-Time Usage Introduction** (show only on first install; skip on re-authorization):
75
+
76
+ > "🎉 Setup complete! You can now use the following commands to manage workspaces:
77
+ >
78
+ > 📁 **Create workspace** — Create a standalone workspace for a new project
79
+ > Example: 'Create a workspace called Client Acme'
80
+ >
81
+ > 📂 **Load workspace** — Open an existing project, restore context
82
+ > Example: 'Load the Acme workspace'
83
+ >
84
+ > 📋 **View workspace** — List all projects or view project contents
85
+ > Example: 'What workspaces do I have?' / 'What's in the Acme workspace?'
86
+ >
87
+ > 💾 **Save artifacts** — Important outputs will prompt you to save
88
+ >
89
+ > The workspace records everything we do. You can view it anytime on the web: https://felo.ai"
90
+
91
+ ### 1. Load Workspace
92
+
93
+ 1. Read `~/.claude/workspaces.json`, fuzzy-match project name. If not found locally, try `$SCRIPT list --keyword`.
94
+ 2. **Found:** Set as active → `$SCRIPT get-readme SHORT_ID` → present README as workspace briefing → append link `https://felo.ai/livedoc/SHORT_ID?from=claw`. If README is empty or missing, fall back to `$SCRIPT resources SHORT_ID` to show the resource list.
95
+ 3. **Not found:** "No workspace found for '[X]'. Want me to create one?"
96
+
97
+ ### 2. Create Workspace
98
+
99
+ ```bash
100
+ $SCRIPT create --name "Project Name" --description "workspace"
101
+ ```
102
+
103
+ Extract `short_id` → initialize README (see "README Structure Template" below) → update registry → set as active → reply:
104
+
105
+ > "✅ Workspace '[X]' created. 📎 https://felo.ai/livedoc/SHORT_ID?from=claw"
106
+
107
+ ### 3. Task Sync (Mandatory)
108
+
109
+ **Iron rule: When the workspace is active, if the user's request requires the Agent to invoke tools to produce new content (search, generate, analyze, etc.), the very first step is always `create-task` — before doing anything else.**
110
+
111
+ Decision flow:
112
+ 1. User sends a message
113
+ 2. Does this message require the Agent to invoke tools to produce new content? (search, generate, collect, analyze, write)
114
+ - Yes → **immediately `create-task`** → execute → **`update-task` to mark complete**
115
+ - No → execute directly, no task needed
116
+
117
+ **Requires task creation (invoking tools to produce new content):**
118
+ - "Collect client info on Mr. Zhang" → requires search + generation
119
+ - "Add 10 horror TikTok videos" → requires search + adding content
120
+ - "Write a competitive analysis report" → requires generation
121
+ - "Search for recent gold price trends" → requires search
122
+
123
+ **Does NOT require task creation (workspace operations):**
124
+ - "Load Mr. Zhang's workspace" → workspace operation
125
+ - "What's in my workspace?" → viewing workspace
126
+ - "Refresh the workspace" / "Create a new workspace" → workspace management
127
+ - "Save that report" → saving artifacts
128
+ - "Update the README" / "Rename the workspace" → workspace maintenance
129
+ - Chitchat, clarifying questions
130
+
131
+ **On load:** Pull pending and in-progress tasks:
132
+ ```bash
133
+ $SCRIPT tasks SHORT_ID --status 0
134
+ $SCRIPT tasks SHORT_ID --status 1
135
+ ```
136
+
137
+ **Step one — Create task (before doing anything else):**
138
+ ```bash
139
+ $SCRIPT create-task SHORT_ID --title "Task description" --status 1 --sort 0 [--operated-by "Agent Name"]
140
+ ```
141
+ Save the returned `task_id` in working memory.
142
+
143
+ `--title` should be a one-line summary of what the user wants done, e.g.:
144
+ - User says "Collect info on Mr. Zhang" → `--title "Collect client info on Mr. Zhang"`
145
+ - User says "Add 10 horror videos" → `--title "Add 10 horror TikTok trending videos"`
146
+
147
+ `--operated-by` rules:
148
+ - **Only pass it if the Agent has been given a name** in this session (e.g. an OpenClaw agent with an assigned name)
149
+ - **Omit it if no explicit name** (e.g. a plain Claude Code session)
150
+
151
+ **Last step — Mark complete (immediately after execution):**
152
+ ```bash
153
+ $SCRIPT update-task SHORT_ID TASK_ID --status 2 [--operated-by "Agent Name"]
154
+ ```
155
+ (`--operated-by` rule same as above — pass it when the Agent has a name.)
156
+
157
+ Execute silently — do not narrate the task sync to the user. If you forgot to create a task before starting, create it retroactively and mark it DONE immediately. Never skip it.
158
+
159
+ ### 4. Save Artifacts (Ask First)
160
+
161
+ After producing significant output, ask the user: "Want me to save this to the [project] workspace?"
162
+
163
+ Significant artifacts = research reports, competitive analyses, meeting summaries, generated documents, key data exports. Intermediate drafts do not need to be saved.
164
+
165
+ | Type | Command |
166
+ |------|---------|
167
+ | Document | `$SCRIPT add-doc SHORT_ID --title "Title" --content "content"` |
168
+ | URL | `$SCRIPT add-urls SHORT_ID --urls "URL"` |
169
+ | File | `$SCRIPT upload SHORT_ID --file ./path --convert` |
170
+
171
+ After saving, reply:
172
+ > "💾 Saved '[Title]' 📎 https://felo.ai/livedoc/SHORT_ID?from=claw"
173
+
174
+ ### 5. README Maintenance
175
+
176
+ The README is the Agent's memory of the project — not a work log. Agent maintains proactively, no need to ask the user.
177
+
178
+ **README Structure Template:**
179
+ ```markdown
180
+ # [Project Name]
181
+
182
+ ## What This Project Is
183
+ [Project background, objectives, stakeholders]
184
+
185
+ ## User Preferences & Work Patterns
186
+ [How the user likes to work, what dimensions they care about, specific requirements]
187
+
188
+ ## Current Progress
189
+ [Where things stand now — updated each session]
190
+ Last updated: YYYY-MM-DD
191
+ ```
192
+
193
+ **When to update README — core question: Did this operation bring any new understanding?**
194
+
195
+ Update (new understanding):
196
+ - When the project is first created — record "what this project is about"
197
+ - When the user expresses preferences or work patterns — record "how the user wants to work" (e.g. collection dimensions, focus areas, format requirements)
198
+ - When the project's nature or direction changes
199
+
200
+ Do NOT update (repeated execution):
201
+ - Same-pattern repeated operations (e.g. after collecting info on Mr. Zhang, collecting info on Mr. Li using the same pattern — no update needed)
202
+ - The mere fact of executing an action (generating a document is not worth recording by itself)
203
+
204
+ **Update method:** Read → merge in memory into the correct section → write back in full. Never blindly append to the end.
205
+
206
+ 1. `$SCRIPT get-readme SHORT_ID` to read current content
207
+ 2. Locate the target section in memory and insert:
208
+ - Project background → `## What This Project Is`
209
+ - User preferences → `## User Preferences & Work Patterns`
210
+ - Progress changes → replace `## Current Progress` section content
211
+ 3. Update `Last updated: YYYY-MM-DD` to today's date
212
+ 4. `$SCRIPT update-readme SHORT_ID --content "..."` to write back
213
+
214
+ **Initialization** (README is empty or missing): Skip step 1, write the full skeleton directly with `update-readme`.
215
+
216
+ After updating, inform the user:
217
+ > "📝 README updated. 📎 https://felo.ai/livedoc/SHORT_ID?from=claw"
218
+
219
+ ### 6. Query Workspace
220
+
221
+ Prefer `resources` + `content` (direct read, free) over `retrieve` (LLM-based semantic search, costs money).
222
+
223
+ ```bash
224
+ $SCRIPT resources SHORT_ID
225
+ $SCRIPT content SHORT_ID RESOURCE_ID
226
+ $SCRIPT retrieve SHORT_ID --query "question" # fallback
227
+ ```
228
+
229
+ Synthesize returned content into a direct answer. Do not dump raw results.
230
+
231
+ ### 7. Refresh Workspace
232
+
233
+ When the user says "refresh", or when the workspace may have been updated externally (by a colleague or another session), re-fetch:
234
+
235
+ ```bash
236
+ $SCRIPT get-readme SHORT_ID
237
+ $SCRIPT resources SHORT_ID
238
+ $SCRIPT tasks SHORT_ID --status 0
239
+ $SCRIPT tasks SHORT_ID --status 1
240
+ ```
241
+
242
+ Update the in-memory snapshot. Tell the user: "Workspace refreshed." If anything changed, briefly describe the differences (new resources, README updates, new tasks).
243
+
244
+ ### 8. List Contents
245
+
246
+ `$SCRIPT resources SHORT_ID`, grouped by type, artifacts newest first.
247
+
248
+ ---
249
+
250
+ ## Session State
251
+
252
+ ```
253
+ ACTIVE_WORKSPACE = { name: "project-x", short_id: "abc123" }
254
+ ```
255
+
256
+ - Set when user loads or creates a workspace; clear when user says "close workspace"
257
+ - If no active workspace and user tries to save/log/query/task: "No active workspace. Which project?"
258
+
259
+ ## Error Handling
260
+
261
+ | Error | Action |
262
+ |-------|--------|
263
+ | Key missing / 401 UNAUTHORIZED | Trigger Login flow (0a) |
264
+ | LiveDoc ID stale | Offer to re-link or create new |
265
+ | Registry missing | Auto-create with `{"workspaces": {}}` |
266
+ | Ambiguous fuzzy match | List all matches, ask user to pick |
267
+
268
+ ## Important Rules
269
+
270
+ - Write all content in the user's language — Chinese if they speak Chinese, English if English
271
+ - Always use `short_id` for all LiveDoc operations
272
+ - Execute commands immediately — don't describe, do
273
+ - Task sync and README updates are the Agent's responsibility — proactive, not reactive
274
+ - Append workspace link after every write operation
@@ -191,6 +191,7 @@ node ~/.agents/skills/felo-livedoc/scripts/run_livedoc.mjs tasks SHORT_ID --labe
191
191
  ```bash
192
192
  node ~/.agents/skills/felo-livedoc/scripts/run_livedoc.mjs create-task SHORT_ID --title "Write docs" --status 0 --sort 0
193
193
  node ~/.agents/skills/felo-livedoc/scripts/run_livedoc.mjs create-task SHORT_ID --title "Write docs" --status 0 --sort 0 --description "API docs" --labels "docs"
194
+ node ~/.agents/skills/felo-livedoc/scripts/run_livedoc.mjs create-task SHORT_ID --title "Write docs" --operated-by "claude-code"
194
195
  ```
195
196
 
196
197
  Task status values: `0`=TODO, `1`=IN_PROGRESS, `2`=DONE
@@ -199,6 +200,7 @@ Task status values: `0`=TODO, `1`=IN_PROGRESS, `2`=DONE
199
200
  ```bash
200
201
  node ~/.agents/skills/felo-livedoc/scripts/run_livedoc.mjs update-task SHORT_ID TASK_ID --status 1
201
202
  node ~/.agents/skills/felo-livedoc/scripts/run_livedoc.mjs update-task SHORT_ID TASK_ID --title "New title" --labels "docs,done"
203
+ node ~/.agents/skills/felo-livedoc/scripts/run_livedoc.mjs update-task SHORT_ID TASK_ID --status 2 --operated-by "claude-code"
202
204
  ```
203
205
 
204
206
  **Delete a task:**
@@ -215,6 +217,7 @@ node ~/.agents/skills/felo-livedoc/scripts/run_livedoc.mjs task-records SHORT_ID
215
217
  **Add a comment to a task:**
216
218
  ```bash
217
219
  node ~/.agents/skills/felo-livedoc/scripts/run_livedoc.mjs add-task-comment SHORT_ID TASK_ID --content "This is a comment."
220
+ node ~/.agents/skills/felo-livedoc/scripts/run_livedoc.mjs add-task-comment SHORT_ID TASK_ID --content "This is a comment." --operated-by "claude-code"
218
221
  ```
219
222
  ### Options
220
223
 
@@ -208,6 +208,7 @@ function usage() {
208
208
  ' --sort <n> Task sort order (non-negative integer)',
209
209
  ' --labels <labels> Comma-separated labels (tasks)',
210
210
  ' --record-type <type> Record type filter: comment, edit, status_change',
211
+ ' --operated-by <sig> Operator signature, e.g. claude-code, openclaw (max 100 chars)',
211
212
  ' -j, --json Output raw JSON',
212
213
  ' -t, --timeout <ms> Timeout in ms (default: 60000)',
213
214
  ' --help Show this help',
@@ -219,7 +220,7 @@ function parseArgs(argv) {
219
220
  keyword: '', page: '', size: '', type: '', content: '', title: '',
220
221
  urls: '', file: '', convert: false, query: '', resourceIds: '', maxResources: '',
221
222
  resourceId: '', pageNumber: '', maxChunk: '', expiresIn: '', output: '',
222
- status: '', sort: '', labels: '', recordType: '',
223
+ status: '', sort: '', labels: '', recordType: '', operatedBy: '',
223
224
  json: false, timeoutMs: DEFAULT_TIMEOUT_MS, help: false,
224
225
  };
225
226
  const positional = [];
@@ -251,6 +252,7 @@ function parseArgs(argv) {
251
252
  else if (a === '--sort') out.sort = argv[++i] || '';
252
253
  else if (a === '--labels') out.labels = argv[++i] || '';
253
254
  else if (a === '--record-type') out.recordType = argv[++i] || '';
255
+ else if (a === '--operated-by') out.operatedBy = argv[++i] || '';
254
256
  else if (a === '-t' || a === '--timeout') {
255
257
  const n = parseInt(argv[++i] || '', 10);
256
258
  if (Number.isFinite(n) && n > 0) out.timeoutMs = n;
@@ -615,6 +617,7 @@ async function main() {
615
617
  const body = { title: args.title, status, sort };
616
618
  if (args.description) body.description = args.description;
617
619
  body.labels = args.labels ? args.labels.split(',').map(l => l.trim()).filter(Boolean) : [];
620
+ if (args.operatedBy) body.operated_by = args.operatedBy;
618
621
  const payload = await apiRequest('POST', `/livedocs/${shortId}/tasks`, body, apiKey, apiBase, timeoutMs);
619
622
  if (json) { console.log(JSON.stringify(payload, null, 2)); }
620
623
  else { process.stdout.write('Task created!\n\n'); process.stdout.write(formatTask(payload?.data)); }
@@ -631,6 +634,7 @@ async function main() {
631
634
  if (args.status !== '') body.status = parseInt(args.status, 10);
632
635
  if (args.sort !== '') body.sort = parseInt(args.sort, 10);
633
636
  if (args.labels !== undefined) body.labels = args.labels.split(',').map(l => l.trim()).filter(Boolean);
637
+ if (args.operatedBy) body.operated_by = args.operatedBy;
634
638
  const payload = await apiRequest('PATCH', `/livedocs/${shortId}/tasks/${resourceId}`, body, apiKey, apiBase, timeoutMs);
635
639
  if (json) { console.log(JSON.stringify(payload, null, 2)); }
636
640
  else { process.stdout.write('Task updated!\n\n'); process.stdout.write(formatTask(payload?.data)); }
@@ -674,7 +678,9 @@ async function main() {
674
678
  if (!resourceId) { console.error('ERROR: task_id is required'); break; }
675
679
  if (!args.content) { console.error('ERROR: --content is required'); break; }
676
680
  spinnerId = startSpinner('Adding comment');
677
- const payload = await apiRequest('POST', `/livedocs/${shortId}/tasks/${resourceId}/comments`, { content: args.content }, apiKey, apiBase, timeoutMs);
681
+ const body = { content: args.content };
682
+ if (args.operatedBy) body.operated_by = args.operatedBy;
683
+ const payload = await apiRequest('POST', `/livedocs/${shortId}/tasks/${resourceId}/comments`, body, apiKey, apiBase, timeoutMs);
678
684
  if (json) { console.log(JSON.stringify(payload, null, 2)); }
679
685
  else { process.stdout.write('Comment added.\n'); process.stdout.write(formatTaskRecord(payload?.data)); }
680
686
  code = 0;
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "Felo Slides",
3
+ "tagline": "Generate PPT/slides with Felo PPT Task API in Claude Code",
4
+ "description": "Felo Slides creates presentation decks from a prompt using the Felo PPT Task API: API key check, task creation, polling, theme support, task resume, and final ppt_url output. Use from Claude Code when users ask to create or export slides/PPT.",
5
+ "category": "productivity",
6
+ "tags": ["felo", "slides", "ppt", "presentation", "api", "claude-code"],
7
+ "version": "1.0.1",
8
+ "license": "MIT",
9
+ "pricing": "free",
10
+ "support_url": "https://github.com/Felo-Inc/felo-skills/issues",
11
+ "homepage": "https://github.com/Felo-Inc/felo-skills"
12
+ }
@@ -0,0 +1,85 @@
1
+ # Felo Twitter Writer Skill
2
+
3
+ Dual-mode Twitter/X writing tool powered by [Felo SuperAgent](https://openapi.felo.ai/docs/api-reference/v2/superagent.html).
4
+
5
+ **Mode 1** — Fetch tweets from any X account and extract a writing style DNA document.
6
+ **Mode 2** — Compose tweets, threads, or X long-form posts based on a style DNA and topic.
7
+
8
+ ## Features
9
+
10
+ - **Style DNA extraction** — analyze an account's tone, sentence structure, hooks, hashtag strategy, and more
11
+ - **Tweet creation** — single tweets, threads, or X long-form posts, default 3 versions
12
+ - **Style imitation** — write in the voice of any public X account
13
+ - **Iterative editing** — refine generated content via follow-up conversation
14
+ - Powered by SuperAgent with `twitter-writer` skill, real-time SSE streaming
15
+ - Same `FELO_API_KEY` as other Felo skills
16
+
17
+ ## Prerequisites
18
+
19
+ - [`felo-superAgent`](../felo-superAgent/) skill available
20
+ - [`felo-x-search`](../felo-x-search/) skill available
21
+ - [`felo-livedoc`](../felo-livedoc/) skill available
22
+
23
+ ## Quick Start
24
+
25
+ ### 1) Configure API key
26
+
27
+ At [felo.ai](https://felo.ai) → Settings → API Keys, create a key, then:
28
+
29
+ ```bash
30
+ # Linux/macOS
31
+ export FELO_API_KEY="your-api-key-here"
32
+ ```
33
+
34
+ ```powershell
35
+ # Windows PowerShell
36
+ $env:FELO_API_KEY="your-api-key-here"
37
+ ```
38
+
39
+ ```cmd
40
+ :: Windows CMD
41
+ set FELO_API_KEY=your-api-key-here
42
+ ```
43
+
44
+ ### 2) Mode 1 — Extract style DNA
45
+
46
+ ```bash
47
+ # Step 1: Fetch tweets from an account
48
+ node felo-x-search/scripts/run_x_search.mjs --id "elonmusk" --user --tweets --limit 30
49
+ node felo-x-search/scripts/run_x_search.mjs --id "elonmusk" --user
50
+
51
+ # Step 2: Get your live_doc_id (list existing, or create one if empty)
52
+ node felo-livedoc/scripts/run_livedoc.mjs list --json
53
+ # node felo-livedoc/scripts/run_livedoc.mjs create --name "Twitter Writer" --json
54
+
55
+ # Step 3: Pass tweets to SuperAgent for style analysis
56
+ node felo-superAgent/scripts/run_superagent.mjs \
57
+ --query "Analyze the following tweets from @elonmusk and extract a writing style DNA document covering tone, sentence structure, opening hooks, hashtag strategy, and emoji usage.\n\nBio: [BIO]\n\nTweets:\n[TWEETS]" \
58
+ --live-doc-id "LIVE_DOC_ID" \
59
+ --skill-id twitter-writer \
60
+ --accept-language en
61
+ ```
62
+
63
+ ### 3) Mode 2 — Create content
64
+
65
+ ```bash
66
+ # New conversation (no prior style DNA)
67
+ node felo-superAgent/scripts/run_superagent.mjs \
68
+ --query "Write 3 versions of a tweet about AI trends in an engaging, punchy style." \
69
+ --live-doc-id "LIVE_DOC_ID" \
70
+ --skill-id twitter-writer \
71
+ --accept-language en
72
+
73
+ # Follow-up after Mode 1 (reuse thread — pass --thread-id from previous [state] output)
74
+ node felo-superAgent/scripts/run_superagent.mjs \
75
+ --query "Based on the style DNA above, write 3 tweets about startups. Keep each under 280 characters." \
76
+ --thread-id "THREAD_SHORT_ID" \
77
+ --live-doc-id "LIVE_DOC_ID" \
78
+ --accept-language en
79
+ ```
80
+
81
+ ## When to use (Agent)
82
+
83
+ Trigger keywords: `write a tweet`, `twitter thread`, `style DNA`, `imitate tweet style`, `tweet in the style of`, `X account analysis`, `ghostwrite tweets`, `how does [account] write`, `ツイートを書く`, `ツイートスタイル分析`, `スタイルDNA`, `ツイートを模倣`, `〇〇風のツイートを書く`, `ツイートを代筆`, `Xアカウント分析`, `/felo-twitter-writer`.
84
+
85
+ See [SKILL.md](SKILL.md) for full agent instructions.
@@ -0,0 +1,315 @@
1
+ ---
2
+ name: felo-twitter-writer
3
+ description: "Dual-mode Twitter/X writing tool. Mode 1: input a Twitter account, auto-fetch popular tweets and extract a writing style DNA document. Mode 2: based on style DNA and a topic, compose high-quality tweets, threads, or X long-form posts. Use when users want to analyze Twitter style, extract writing style, write tweets, write threads, imitate someone's tweet style, or ghostwrite tweets."
4
+ ---
5
+
6
+ # Felo Twitter Writer Skill
7
+
8
+ ## Constraints (MUST READ FIRST)
9
+
10
+ These rules are mandatory. Violating any of them will produce incorrect behavior.
11
+
12
+ 1. **This skill uses SuperAgent directly.** All generation is handled by `felo-superAgent/scripts/run_superagent.mjs` with `--skill-id twitter-writer`. Do NOT attempt to generate tweet content yourself.
13
+
14
+ 2. **NEVER use `--json` flag** when calling SuperAgent. The script MUST run in default streaming mode. State IDs are extracted from the `[state]` line in stderr.
15
+
16
+ 3. **NEVER summarize or re-output SuperAgent's stdout.** The answer is already streamed directly to the user. Only add supplementary info (LiveDoc URL) if needed.
17
+
18
+ 4. **`--live-doc-id` is REQUIRED** for every SuperAgent call. Follow the livedoc reuse rules from `felo-superAgent/SKILL.md`:
19
+ - Reuse any `live_doc_id` already available in this session
20
+ - If none: run `node felo-livedoc/scripts/run_livedoc.mjs list --json`, use `items[0].short_id`
21
+ - If list is empty: run `node felo-livedoc/scripts/run_livedoc.mjs create --name "Twitter Writer" --json`, use `data.short_id`
22
+
23
+ 5. **Always persist state.** After every SuperAgent call, extract `thread_short_id` and `live_doc_short_id` from the stderr `[state]` line. Use them in subsequent calls.
24
+
25
+ 6. **Output language follows the user's input language.** Default is `en`. Detect the user's language and pass the matching `--accept-language` value: `ja` for Japanese, `en` for English, `ko` for Korean, `zh` for Chinese. If unsure, use `en`.
26
+
27
+ 7. **Do NOT pass `--timeout` to the SuperAgent script.** The script manages its own connection lifecycle.
28
+
29
+ ## When to Use
30
+
31
+ Trigger this skill when the user wants to:
32
+
33
+ - Analyze a Twitter/X account's writing style
34
+ - Extract a writing style DNA document from tweets
35
+ - Write, draft, or compose tweets / X posts
36
+ - Write a Twitter thread (multi-tweet series)
37
+ - Write an X long-form article / long post
38
+ - Imitate or mimic someone's tweet style
39
+ - Ghostwrite tweets on behalf of someone
40
+ - Understand how a specific account writes
41
+
42
+ **Trigger keywords:**
43
+
44
+ - English: `analyze twitter style`, `twitter style analysis`, `extract writing style`, `style DNA`, `write a tweet`, `write tweets`, `draft a tweet`, `write a thread`, `twitter thread`, `X article`, `X long post`, `imitate tweet style`, `mimic tweet style`, `tweet in the style of`, `write like [account]`, `X account analysis`, `analyze X account`, `ghostwrite tweets`, `how does [account] write`
45
+ - 日本語: `ツイートを書く`, `ツイートスタイル分析`, `スタイルDNA`, `ツイートを模倣`, `Xアカウント分析`, `ツイートのスタイルを抽出`, `〇〇風のツイートを書く`, `ツイートを代筆`, `Xアカウントを分析`, `このアカウントはどう書いている`
46
+
47
+ **Explicit commands:** `/felo-twitter-writer`, `use felo twitter writer`
48
+
49
+ **Do NOT use for:**
50
+
51
+ - General Twitter/X search only (use `felo-x-search`)
52
+ - General SuperAgent conversation (use `felo-superAgent`)
53
+ - Web search (use `felo-search`)
54
+
55
+ ## Two Modes
56
+
57
+ ### Mode 1 — Style DNA Extraction
58
+
59
+ **When:** User provides a Twitter/X account name and wants to understand or extract its writing style.
60
+
61
+ **Steps:**
62
+
63
+ #### Step 1: Fetch tweets via felo-x-search
64
+
65
+ ```bash
66
+ node felo-x-search/scripts/run_x_search.mjs --id "USERNAME" --user --tweets --limit 30
67
+ ```
68
+
69
+ Also fetch the account profile:
70
+
71
+ ```bash
72
+ node felo-x-search/scripts/run_x_search.mjs --id "USERNAME" --user
73
+ ```
74
+
75
+ #### Step 2: Obtain live_doc_id
76
+
77
+ Follow Constraint #4 above.
78
+
79
+ #### Step 3: Call SuperAgent with tweet content
80
+
81
+ Construct a query that includes the fetched tweets and asks for style analysis. Pass `--skill-id twitter-writer` since this is a new conversation.
82
+
83
+ ```bash
84
+ node felo-superAgent/scripts/run_superagent.mjs \
85
+ --query "ENRICHED_QUERY_WITH_TWEET_CONTENT" \
86
+ --live-doc-id "LIVE_DOC_ID" \
87
+ --skill-id twitter-writer \
88
+ --accept-language LANG
89
+ ```
90
+
91
+ **Query construction example:**
92
+
93
+ > Please analyze the following tweets from @USERNAME and extract a writing style DNA document. Cover dimensions such as: tone, sentence structure, opening hooks, closing calls-to-action, frequently used words, hashtag strategy, emoji usage, and any other distinctive patterns.
94
+ >
95
+ > Account bio: [BIO]
96
+ >
97
+ > Tweets:
98
+ > [TWEET_1]
99
+ > [TWEET_2]
100
+ > ...
101
+
102
+ Keep the query under 2000 characters. If tweet content is too long, include the most representative 10–15 tweets.
103
+
104
+ #### Step 4: Save state
105
+
106
+ Extract `thread_short_id` and `live_doc_short_id` from stderr `[state]` line. Save for follow-up calls.
107
+
108
+ ---
109
+
110
+ ### Mode 2 — Content Creation
111
+
112
+ **When:** User wants to create tweets, threads, or X long-form posts (with or without a style DNA).
113
+
114
+ **Steps:**
115
+
116
+ #### Step 1: Determine if style DNA is available
117
+
118
+ - If Mode 1 was just run in this session → style DNA is already in the LiveDoc canvas, use follow-up mode
119
+ - If user provides a style DNA directly → include it in the query
120
+ - If user provides an account name → run Mode 1 first, then continue with Mode 2
121
+ - If no style DNA → proceed with general tweet writing (SuperAgent will use its default twitter-writer behavior)
122
+
123
+ #### Step 2: Obtain live_doc_id
124
+
125
+ Follow Constraint #4. If Mode 1 was already run, reuse the same `live_doc_id`.
126
+
127
+ #### Step 3: Determine conversation mode
128
+
129
+ | Condition | Mode | What to pass |
130
+ |-----------|------|--------------|
131
+ | First call in session, or switching to twitter-writer skill | New conversation | `--live-doc-id` + `--skill-id twitter-writer` |
132
+ | Continuing from Mode 1 or a previous Mode 2 call | Follow-up | `--thread-id` + `--live-doc-id` |
133
+ | User says "new topic" / "start over" | New conversation | `--live-doc-id` + `--skill-id twitter-writer` |
134
+
135
+ #### Step 4: Call SuperAgent
136
+
137
+ **New conversation:**
138
+
139
+ ```bash
140
+ node felo-superAgent/scripts/run_superagent.mjs \
141
+ --query "ENRICHED_QUERY" \
142
+ --live-doc-id "LIVE_DOC_ID" \
143
+ --skill-id twitter-writer \
144
+ --accept-language LANG
145
+ ```
146
+
147
+ **Follow-up (iterating on previous output):**
148
+
149
+ ```bash
150
+ node felo-superAgent/scripts/run_superagent.mjs \
151
+ --query "USER_FOLLOW_UP" \
152
+ --thread-id "THREAD_SHORT_ID" \
153
+ --live-doc-id "LIVE_DOC_ID" \
154
+ --accept-language LANG
155
+ ```
156
+
157
+ **Query construction guidelines:**
158
+
159
+ - Specify the content type: single tweet / thread / X long-form post
160
+ - Specify the topic clearly
161
+ - Include style DNA or reference account if available
162
+ - Default to 3 versions unless the user specifies otherwise
163
+ - Preserve the user's core intent; only add context and clarity
164
+
165
+ **Query examples:**
166
+
167
+ > Please write 3 versions of a tweet about [TOPIC] in the style of @USERNAME (style DNA above). Each version should feel distinct — vary the tone, structure, or angle.
168
+
169
+ > Based on the style DNA extracted above, write a Twitter thread (5–7 tweets) about [TOPIC]. Start with a strong hook tweet.
170
+
171
+ > Write an X long-form post about [TOPIC] following the writing style we analyzed. Aim for ~500 words.
172
+
173
+ #### Step 5: Save state
174
+
175
+ Extract `thread_short_id` and `live_doc_short_id` from stderr `[state]` line.
176
+
177
+ ---
178
+
179
+ ## Mode Decision Logic
180
+
181
+ ```
182
+ User input
183
+
184
+ ├── Contains account name + "analyze / style / DNA / how does X write"
185
+ │ OR: アカウント名 + "分析 / スタイル / DNA / どう書いている"
186
+ │ → Mode 1 (Style DNA Extraction)
187
+
188
+ ├── Contains account name + "write / create / imitate / in the style of"
189
+ │ OR: アカウント名 + "書いて / 作って / 風に / 真似て"
190
+ │ → Mode 1 first → then Mode 2 (Creation)
191
+
192
+ ├── Contains topic + "write / draft / tweet / thread / X post"
193
+ │ OR: トピック + "書いて / ツイート / スレッド / Xの投稿"
194
+ │ → Mode 2 directly
195
+ │ └── If no style DNA available: ask user if they want to provide
196
+ │ a reference account, or proceed with general twitter-writer style
197
+
198
+ └── Ambiguous (e.g., "help me with tweets" / "ツイートを手伝って")
199
+ → Ask user: do they want to analyze an account's style, or create content?
200
+ ```
201
+
202
+ ---
203
+
204
+ ## Complete Workflow Examples
205
+
206
+ ### Example A: Analyze an account's style
207
+
208
+ ```
209
+ User: "@paulg のツイートスタイルを分析して"
210
+ ```
211
+
212
+ **Step 1:** Fetch tweets:
213
+ ```bash
214
+ node felo-x-search/scripts/run_x_search.mjs --id "paulg" --user --tweets --limit 30
215
+ node felo-x-search/scripts/run_x_search.mjs --id "paulg" --user
216
+ ```
217
+
218
+ **Step 2:** Get `live_doc_id` (list or create)
219
+
220
+ **Step 3:** Call SuperAgent:
221
+ ```bash
222
+ node felo-superAgent/scripts/run_superagent.mjs \
223
+ --query "@paulg のツイートを分析し、文体のスタイルDNAドキュメントを作成してください。トーン、文章構造、冒頭フック、締めのアクション、頻出ワード、ハッシュタグ戦略、絵文字の使い方などを含めてください。\n\nアカウント概要:[BIO]\n\nツイート:\n[TWEETS]" \
224
+ --live-doc-id "LIVE_DOC_ID" \
225
+ --skill-id twitter-writer \
226
+ --accept-language ja
227
+ ```
228
+
229
+ **Step 4:** Save `thread_short_id` and `live_doc_short_id` from stderr `[state]`.
230
+
231
+ ---
232
+
233
+ ### Example B: Create tweets with a reference style
234
+
235
+ ```
236
+ User: "@paulg のスタイルでスタートアップについてのツイートを3つ書いて"
237
+ ```
238
+
239
+ **Step 1:** Run Mode 1 to extract style DNA (same as Example A)
240
+
241
+ **Step 2:** Reuse `live_doc_id` from Mode 1
242
+
243
+ **Step 3:** Follow-up call (continuing the same thread):
244
+ ```bash
245
+ node felo-superAgent/scripts/run_superagent.mjs \
246
+ --query "上記で抽出した @paulg のスタイルDNAをもとに、「スタートアップ」をテーマにしたツイートを3パターン作成してください。それぞれ異なるトーンや切り口で、280文字以内に収めてください。" \
247
+ --thread-id "THREAD_SHORT_ID" \
248
+ --live-doc-id "LIVE_DOC_ID" \
249
+ --accept-language ja
250
+ ```
251
+
252
+ ---
253
+
254
+ ### Example C: Write a thread directly
255
+
256
+ ```
257
+ User: "Write a Twitter thread about why most startups fail"
258
+ ```
259
+
260
+ **Step 1:** No style DNA needed, proceed directly
261
+
262
+ **Step 2:** Get `live_doc_id`
263
+
264
+ **Step 3:** New conversation with `twitter-writer`:
265
+ ```bash
266
+ node felo-superAgent/scripts/run_superagent.mjs \
267
+ --query "Write a Twitter thread (6–8 tweets) about why most startups fail. Start with a strong hook tweet that grabs attention. Each tweet should stand alone but flow naturally into the next." \
268
+ --live-doc-id "LIVE_DOC_ID" \
269
+ --skill-id twitter-writer \
270
+ --accept-language en
271
+ ```
272
+
273
+ ---
274
+
275
+ ### Example D: Iterate on generated content
276
+
277
+ ```
278
+ User: "2番目のツイートをもっとユーモラスにして、絵文字も追加して"
279
+ ```
280
+
281
+ **Step 1:** Already have `thread_short_id` and `live_doc_id` from the previous call (e.g., Example B or C). No new LiveDoc lookup needed.
282
+
283
+ **Step 2:** This is a follow-up — pass `--thread-id` with the saved `thread_short_id`.
284
+
285
+ **Step 3:** Follow-up call:
286
+ ```bash
287
+ node felo-superAgent/scripts/run_superagent.mjs \
288
+ --query "上記で生成した2番目のツイートを修正してください。トーンをよりユーモラスで軽快にし、適切な絵文字を追加してください。内容の意図は変えないでください。" \
289
+ --thread-id "THREAD_SHORT_ID" \
290
+ --live-doc-id "LIVE_DOC_ID" \
291
+ --accept-language ja
292
+ ```
293
+
294
+ **Step 4:** Save updated `thread_short_id` and `live_doc_short_id` from stderr `[state]`.
295
+
296
+ ---
297
+
298
+ ## Error Handling
299
+
300
+ | Scenario | Action |
301
+ |----------|--------|
302
+ | Account not found or no tweets returned | Inform user, suggest trying a different username or providing tweet samples manually |
303
+ | `FELO_API_KEY` not set | Stop and show setup instructions (same as `felo-superAgent` SKILL.md) |
304
+ | SuperAgent call fails | Check `live_doc_id` validity; retry once with the same parameters |
305
+ | User asks for Mode 2 with no style DNA and no account | Ask: "Would you like to provide a reference Twitter account to base the style on, or should I write in a general engaging style?" |
306
+ | User explicitly requests a new canvas | Create a new LiveDoc: `node felo-livedoc/scripts/run_livedoc.mjs create --name "Twitter Writer" --json` |
307
+ | Tweet content too long for query (>2000 chars) | Trim to the 10–15 most representative tweets; prioritize high-engagement ones |
308
+
309
+ ## References
310
+
311
+ - [felo-superAgent SKILL.md](../felo-superAgent/SKILL.md) — SuperAgent calling conventions and constraints
312
+ - [felo-x-search SKILL.md](../felo-x-search/SKILL.md) — X/Twitter search commands
313
+ - [felo-livedoc SKILL.md](../felo-livedoc/SKILL.md) — LiveDoc management commands
314
+ - [Felo Open Platform](https://openapi.felo.ai/docs/)
315
+ - [Get API Key](https://felo.ai) (Settings → API Keys)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "felo-ai",
3
- "version": "0.2.41",
3
+ "version": "0.2.43",
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",
package/src/cli.js CHANGED
@@ -911,6 +911,7 @@ livedocCmd
911
911
  .option("--sort <n>", "sort order (non-negative integer, default: 0)")
912
912
  .option("--description <desc>", "task description")
913
913
  .option("--labels <labels>", "comma-separated labels (max 10)")
914
+ .option("--operated-by <signature>", "operator signature, e.g. claude-code, openclaw (max 100 characters)")
914
915
  .option("-j, --json", "output raw JSON")
915
916
  .option("-t, --timeout <seconds>", "request timeout in seconds", "60")
916
917
  .action(async (shortId, opts) => {
@@ -921,6 +922,7 @@ livedocCmd
921
922
  sort: opts.sort,
922
923
  description: opts.description,
923
924
  labels: opts.labels,
925
+ operatedBy: opts.operatedBy,
924
926
  json: opts.json,
925
927
  timeoutMs: Number.isNaN(timeoutMs) ? 60000 : timeoutMs,
926
928
  });
@@ -936,6 +938,7 @@ livedocCmd
936
938
  .option("--status <n>", "new status: 0=TODO, 1=IN_PROGRESS, 2=DONE")
937
939
  .option("--sort <n>", "new sort order")
938
940
  .option("--labels <labels>", "new comma-separated labels")
941
+ .option("--operated-by <signature>", "operator signature, e.g. claude-code, openclaw (max 100 characters)")
939
942
  .option("-j, --json", "output raw JSON")
940
943
  .option("-t, --timeout <seconds>", "request timeout in seconds", "60")
941
944
  .action(async (shortId, taskId, opts) => {
@@ -946,6 +949,7 @@ livedocCmd
946
949
  status: opts.status,
947
950
  sort: opts.sort,
948
951
  labels: opts.labels,
952
+ operatedBy: opts.operatedBy,
949
953
  json: opts.json,
950
954
  timeoutMs: Number.isNaN(timeoutMs) ? 60000 : timeoutMs,
951
955
  });
@@ -993,12 +997,14 @@ livedocCmd
993
997
  .command("add-task-comment <short_id> <task_id>")
994
998
  .description("Add a comment to a task")
995
999
  .requiredOption("--content <text>", "comment content")
1000
+ .option("--operated-by <signature>", "operator signature, e.g. claude-code, openclaw (max 100 characters)")
996
1001
  .option("-j, --json", "output raw JSON")
997
1002
  .option("-t, --timeout <seconds>", "request timeout in seconds", "60")
998
1003
  .action(async (shortId, taskId, opts) => {
999
1004
  const timeoutMs = parseInt(opts.timeout, 10) * 1000;
1000
1005
  const code = await livedoc.createTaskComment(shortId, taskId, {
1001
1006
  content: opts.content,
1007
+ operatedBy: opts.operatedBy,
1002
1008
  json: opts.json,
1003
1009
  timeoutMs: Number.isNaN(timeoutMs) ? 60000 : timeoutMs,
1004
1010
  });
package/src/livedoc.js CHANGED
@@ -711,6 +711,7 @@ export async function createTask(shortId, opts = {}) {
711
711
  const body = { title: opts.title, status, sort };
712
712
  if (opts.description) body.description = opts.description;
713
713
  body.labels = opts.labels ? opts.labels.split(',').map(l => l.trim()).filter(Boolean) : [];
714
+ if (opts.operatedBy) body.operated_by = opts.operatedBy;
714
715
  const payload = await apiRequest('POST', `/livedocs/${shortId}/tasks`, body, apiKey, apiBase, timeoutMs);
715
716
  if (opts.json) { console.log(JSON.stringify(payload, null, 2)); return 0; }
716
717
  process.stdout.write('Task created!\n\n');
@@ -738,6 +739,7 @@ export async function updateTask(shortId, taskId, opts = {}) {
738
739
  if (opts.status !== undefined && opts.status !== '') body.status = parseInt(opts.status, 10);
739
740
  if (opts.sort !== undefined && opts.sort !== '') body.sort = parseInt(opts.sort, 10);
740
741
  if (opts.labels !== undefined) body.labels = opts.labels.split(',').map(l => l.trim()).filter(Boolean);
742
+ if (opts.operatedBy) body.operated_by = opts.operatedBy;
741
743
  const payload = await apiRequest('PATCH', `/livedocs/${shortId}/tasks/${taskId}`, body, apiKey, apiBase, timeoutMs);
742
744
  if (opts.json) { console.log(JSON.stringify(payload, null, 2)); return 0; }
743
745
  process.stdout.write('Task updated!\n\n');
@@ -808,7 +810,9 @@ export async function createTaskComment(shortId, taskId, opts = {}) {
808
810
  const spinnerId = startSpinner('Adding comment');
809
811
 
810
812
  try {
811
- const payload = await apiRequest('POST', `/livedocs/${shortId}/tasks/${taskId}/comments`, { content: opts.content }, apiKey, apiBase, timeoutMs);
813
+ const body = { content: opts.content };
814
+ if (opts.operatedBy) body.operated_by = opts.operatedBy;
815
+ const payload = await apiRequest('POST', `/livedocs/${shortId}/tasks/${taskId}/comments`, body, apiKey, apiBase, timeoutMs);
812
816
  if (opts.json) { console.log(JSON.stringify(payload, null, 2)); return 0; }
813
817
  process.stdout.write('Comment added.\n');
814
818
  process.stdout.write(formatTaskRecord(payload?.data));