@hidden-leaf/x-skill 1.0.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.
Files changed (53) hide show
  1. package/.env.example +50 -0
  2. package/CLAUDE.snippet.md +34 -0
  3. package/README.md +220 -0
  4. package/SKILL.md +265 -0
  5. package/dist/cache/store.d.ts +48 -0
  6. package/dist/cache/store.d.ts.map +1 -0
  7. package/dist/cache/store.js +381 -0
  8. package/dist/cache/store.js.map +1 -0
  9. package/dist/clients/types.d.ts +217 -0
  10. package/dist/clients/types.d.ts.map +1 -0
  11. package/dist/clients/types.js +77 -0
  12. package/dist/clients/types.js.map +1 -0
  13. package/dist/clients/x-client.d.ts +111 -0
  14. package/dist/clients/x-client.d.ts.map +1 -0
  15. package/dist/clients/x-client.js +421 -0
  16. package/dist/clients/x-client.js.map +1 -0
  17. package/dist/index.d.ts +20 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +52 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/skills/bookmarks/index.d.ts +79 -0
  22. package/dist/skills/bookmarks/index.d.ts.map +1 -0
  23. package/dist/skills/bookmarks/index.js +288 -0
  24. package/dist/skills/bookmarks/index.js.map +1 -0
  25. package/dist/skills/bookmarks/synthesize.d.ts +40 -0
  26. package/dist/skills/bookmarks/synthesize.d.ts.map +1 -0
  27. package/dist/skills/bookmarks/synthesize.js +164 -0
  28. package/dist/skills/bookmarks/synthesize.js.map +1 -0
  29. package/dist/skills/bookmarks/types.d.ts +71 -0
  30. package/dist/skills/bookmarks/types.d.ts.map +1 -0
  31. package/dist/skills/bookmarks/types.js +6 -0
  32. package/dist/skills/bookmarks/types.js.map +1 -0
  33. package/dist/utils/logger.d.ts +10 -0
  34. package/dist/utils/logger.d.ts.map +1 -0
  35. package/dist/utils/logger.js +43 -0
  36. package/dist/utils/logger.js.map +1 -0
  37. package/package.json +68 -0
  38. package/scripts/cache-report.ts +25 -0
  39. package/scripts/create-atlsk-ticket.ts +68 -0
  40. package/scripts/create-deep-dive-ticket.ts +66 -0
  41. package/scripts/create-jira-project.ts +31 -0
  42. package/scripts/create-launch-and-roadmap.ts +200 -0
  43. package/scripts/create-next-session.ts +71 -0
  44. package/scripts/create-roadmap.ts +150 -0
  45. package/scripts/debug-api.ts +45 -0
  46. package/scripts/debug-folder.ts +37 -0
  47. package/scripts/jira-close-v1-tickets.ts +83 -0
  48. package/scripts/jira-v1-close-and-post-v1.ts +272 -0
  49. package/scripts/oauth-flow.ts +216 -0
  50. package/scripts/postinstall.js +112 -0
  51. package/scripts/sync-test.ts +36 -0
  52. package/scripts/test-refresh-forced.ts +29 -0
  53. package/scripts/test-refresh.ts +25 -0
package/.env.example ADDED
@@ -0,0 +1,50 @@
1
+ # ============================================================================
2
+ # X API Authentication — OAuth 2.0 User Context
3
+ # ============================================================================
4
+ # Bookmarks require OAuth 2.0 User Context (not App-only Bearer token).
5
+ # Set up User Authentication in your app at: https://developer.x.com/en/portal/dashboard
6
+ #
7
+ # Step 1: Save your app credentials (shown once at app creation)
8
+ # Step 2: Set up User Authentication → Read permissions, Web App, PKCE
9
+ # Step 3: Run the OAuth flow script to get your User Access Token:
10
+ # npx tsx scripts/oauth-flow.ts
11
+
12
+ # App credentials (from app creation screen — shown once, save them!)
13
+ X_CONSUMER_KEY=
14
+ X_CONSUMER_SECRET=
15
+
16
+ # OAuth 2.0 User Access Token (generated via OAuth flow script)
17
+ # This is the token that actually reads YOUR bookmarks as YOU.
18
+ # Expires after ~2 hours — use the refresh token to get a new one.
19
+ X_USER_ACCESS_TOKEN=
20
+
21
+ # Refresh token — used to get a new access token when the current one expires.
22
+ # Long-lived. Run `npx tsx scripts/oauth-flow.ts` again if both expire.
23
+ X_REFRESH_TOKEN=
24
+
25
+ # Your numeric X user ID (printed by the OAuth flow script)
26
+ X_USER_ID=
27
+
28
+ # OAuth callback URL (must match what you set in app settings)
29
+ X_CALLBACK_URL=http://localhost:3000/callback
30
+
31
+ # ============================================================================
32
+ # Local Cache
33
+ # ============================================================================
34
+ # Path to SQLite database (default: ~/.x-skill/bookmarks.db)
35
+ # X_CACHE_DB_PATH=
36
+
37
+ # ============================================================================
38
+ # Client Options
39
+ # ============================================================================
40
+ # Max retries on 5xx/network errors (default: 3)
41
+ # X_MAX_RETRIES=3
42
+
43
+ # Request timeout in ms (default: 30000)
44
+ # X_TIMEOUT=30000
45
+
46
+ # ============================================================================
47
+ # Logging
48
+ # ============================================================================
49
+ # Log level: debug, info, warn, error (default: info)
50
+ LOG_LEVEL=info
@@ -0,0 +1,34 @@
1
+ # X Bookmark Intelligence (Library-Based Skill)
2
+
3
+ This project uses `@hidden-leaf/x-skill` — a TypeScript library for X (Twitter) bookmark intelligence.
4
+ This is NOT an MCP tool or registered skill. To use it, write a TypeScript script and execute it with `npx tsx <script>.ts`.
5
+
6
+ ## Setup
7
+ - Credentials are in `.env` (see `node_modules/@hidden-leaf/x-skill/.env.example`)
8
+ - Required: `X_BEARER_TOKEN`, `X_USER_ID`
9
+
10
+ ## Usage
11
+ When the user asks about X bookmarks, bookmark folders, research briefs from saved posts, or wants to analyze their X content — use this skill.
12
+ Read node_modules/@hidden-leaf/x-skill/SKILL.md for the full API before using.
13
+
14
+ ## How to Use
15
+ Write a .ts script, then run it with `npx tsx <script>.ts`:
16
+ ```typescript
17
+ import { createBookmarksSkillFromEnv } from '@hidden-leaf/x-skill';
18
+
19
+ const skill = createBookmarksSkillFromEnv();
20
+
21
+ // List folders
22
+ const { folders } = await skill.listFolders();
23
+
24
+ // Fetch posts from a folder
25
+ const result = await skill.fetchByFolderName('Robotics');
26
+
27
+ // Generate a research brief
28
+ const { brief } = await skill.brief({
29
+ folderName: 'AI MEDICINE',
30
+ hlnContext: ['Applied AI Studio'],
31
+ });
32
+ console.log(brief.content);
33
+ ```
34
+ Then execute: `npx tsx <script>.ts`
package/README.md ADDED
@@ -0,0 +1,220 @@
1
+ # @hidden-leaf/x-skill
2
+
3
+ **Turn your X bookmarks into structured research intelligence.**
4
+
5
+ [![CI](https://github.com/Hidden-Leaf-Networks/x-skill/actions/workflows/ci.yml/badge.svg)](https://github.com/Hidden-Leaf-Networks/x-skill/actions/workflows/ci.yml)
6
+ [![npm version](https://img.shields.io/npm/v/@hidden-leaf/x-skill.svg)](https://www.npmjs.com/package/@hidden-leaf/x-skill)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
8
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg)](https://nodejs.org)
9
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue.svg)](https://www.typescriptlang.org/)
10
+
11
+ ---
12
+
13
+ A TypeScript skill for [Claude Code](https://claude.ai/claude-code) that syncs your X (Twitter) bookmark folders into a local SQLite cache and synthesizes them into actionable research briefs. Sync once, read forever.
14
+
15
+ ```
16
+ X Bookmarks --> Sync (API) --> SQLite Cache --> Research Briefs
17
+ folders pennies local/free themes, voices,
18
+ posts per sync offline signal, actions
19
+ ```
20
+
21
+ ## Why
22
+
23
+ Your X bookmarks are a curated research goldmine — AI papers, robotics threads, crypto analysis, medicine breakthroughs — organized into folders. But they're trapped in the app with no way to search, analyze, or act on them programmatically.
24
+
25
+ x-skill fixes that. Pull your folders, cache locally, synthesize into structured intelligence. The cache-first architecture means you pay for the sync (~$5-15/month), but every read, brief, and analysis after that is **completely free**.
26
+
27
+ ## Install
28
+
29
+ ```bash
30
+ npm install @hidden-leaf/x-skill
31
+ ```
32
+
33
+ ## Setup
34
+
35
+ ### 1. Get X API Access
36
+
37
+ Sign up at [developer.x.com](https://developer.x.com) for pay-per-use API access.
38
+
39
+ Create an app with these OAuth 2.0 scopes:
40
+ - `bookmark.read`
41
+ - `tweet.read`
42
+ - `users.read`
43
+ - `offline.access`
44
+
45
+ ### 2. Run the OAuth Flow
46
+
47
+ ```bash
48
+ npx tsx node_modules/@hidden-leaf/x-skill/scripts/oauth-flow.ts
49
+ ```
50
+
51
+ This opens your browser, authenticates via PKCE, and prints your credentials.
52
+
53
+ ### 3. Configure Environment
54
+
55
+ Add the output to your `.env`:
56
+
57
+ ```env
58
+ X_CONSUMER_KEY=your-consumer-key
59
+ X_CONSUMER_SECRET=your-consumer-secret
60
+ X_USER_ACCESS_TOKEN=your-access-token
61
+ X_REFRESH_TOKEN=your-refresh-token
62
+ X_USER_ID=your-numeric-user-id
63
+ ```
64
+
65
+ Tokens auto-refresh on expiry — set it up once, runs forever.
66
+
67
+ ## Quick Start
68
+
69
+ ```typescript
70
+ import { createBookmarksSkillFromEnv } from '@hidden-leaf/x-skill';
71
+
72
+ const skill = createBookmarksSkillFromEnv();
73
+
74
+ // Sync bookmarks from X API into local cache (costs money)
75
+ const sync = await skill.syncAll();
76
+ console.log(`Synced ${sync.totalTweets} posts across ${sync.totalFolders} folders`);
77
+
78
+ // List your bookmark folders (free — reads from cache)
79
+ const { folders } = skill.listFolders();
80
+ folders.forEach(f => console.log(`${f.name} (${f.tweet_count} posts)`));
81
+
82
+ // Fetch posts from a folder (free)
83
+ const result = skill.fetchByFolderName('Robotics');
84
+ console.log(`${result.totalTweets} posts from ${result.uniqueAuthors} authors`);
85
+
86
+ // Generate a research brief (free)
87
+ const { brief } = await skill.brief({
88
+ folderName: 'AI Medicine',
89
+ hlnContext: ['Applied AI Studio'],
90
+ });
91
+ console.log(brief.content);
92
+ ```
93
+
94
+ Run with: `npx tsx script.ts`
95
+
96
+ ## API Reference
97
+
98
+ ### Sync Commands (hit X API — cost money)
99
+
100
+ | Method | Description |
101
+ |--------|-------------|
102
+ | `syncAll()` | Pull all bookmark folders and tweets into cache |
103
+ | `syncFolder(name)` | Sync a single folder by name (case-insensitive) |
104
+
105
+ ### Read Commands (from cache — free)
106
+
107
+ | Method | Description |
108
+ |--------|-------------|
109
+ | `listFolders()` | List all cached bookmark folders |
110
+ | `fetchByFolderName(name)` | Get enriched bookmarks from a folder |
111
+ | `fetchByFolderId(id)` | Get enriched bookmarks by folder ID |
112
+ | `fetchAll()` | Get all cached bookmarks |
113
+ | `stats()` | Cache statistics (folder/tweet/user counts, last sync) |
114
+ | `close()` | Close database connection |
115
+
116
+ ### Synthesis (from cache — free)
117
+
118
+ | Method | Description |
119
+ |--------|-------------|
120
+ | `brief(options?)` | Generate a research brief with themes, notable voices, signal scoring |
121
+ | `buildBriefPrompt(bookmarks, options)` | Build the raw synthesis prompt for custom use |
122
+
123
+ ### Brief Options
124
+
125
+ ```typescript
126
+ interface BriefOptions {
127
+ folderName?: string; // Scope to a specific folder
128
+ folderId?: string; // Or use folder ID directly
129
+ maxTweets?: number; // Max tweets to analyze (default: 50)
130
+ customPrompt?: string; // Append custom instructions
131
+ hlnContext?: string[]; // Relate to specific ventures/initiatives
132
+ }
133
+ ```
134
+
135
+ ### Research Brief Output
136
+
137
+ Each brief includes:
138
+ - **Key Themes** — 3-7 major trends with evidence citations
139
+ - **Notable Voices** — Most influential accounts, ranked by engagement
140
+ - **Signal vs. Noise** — Quality score (1-10), high-signal vs low-signal posts
141
+ - **Actionable Intelligence** — What to act on, emerging opportunities and risks
142
+ - **HLN Relevance** — How findings connect to your ventures and initiatives
143
+
144
+ ## Architecture
145
+
146
+ ```
147
+ @hidden-leaf/x-skill
148
+ ├── src/
149
+ │ ├── clients/
150
+ │ │ ├── x-client.ts # X API v2 HTTP client, OAuth 2.0, auto-refresh
151
+ │ │ └── types.ts # Full X API v2 type definitions, error classes
152
+ │ ├── cache/
153
+ │ │ └── store.ts # SQLite cache — WAL mode, upserts, 5-table schema
154
+ │ ├── skills/
155
+ │ │ └── bookmarks/
156
+ │ │ ├── index.ts # BookmarksSkill — sync, list, fetch, brief
157
+ │ │ ├── synthesize.ts # Prompt builder, theme/voice extraction
158
+ │ │ └── types.ts # EnrichedBookmark, ResearchBrief, outputs
159
+ │ └── utils/
160
+ │ └── logger.ts # Structured logger with levels
161
+ ├── scripts/
162
+ │ └── oauth-flow.ts # OAuth 2.0 PKCE setup flow
163
+ ├── SKILL.md # Full API reference for Claude Code
164
+ └── CLAUDE.snippet.md # Auto-injected into consuming projects
165
+ ```
166
+
167
+ ### Design Principles
168
+
169
+ - **Sync-then-read** — API calls only during sync. All reads hit local SQLite. You only pay when you pull new data
170
+ - **Cache-first** — SQLite with WAL mode for fast reads and crash-safe writes. Cross-session data access for downstream consumers
171
+ - **No Claude API dependency** — Brief synthesis builds prompts for Claude Code to execute inline. No separate API key required
172
+ - **Factory pattern** — `create*FromEnv()` functions for all major classes. Configuration via environment variables
173
+ - **Minimal API calls** — Smart hydration: fetch folder IDs (cheap), then hydrate only missing tweets via batch lookup (100 per call)
174
+
175
+ ## Cost
176
+
177
+ X API uses pay-per-use pricing (launched Feb 2026). Same post requested within a 24h UTC window counts as a single charge.
178
+
179
+ | Usage Pattern | Est. Monthly Cost |
180
+ |---------------|-------------------|
181
+ | Weekly sync, 5 folders | ~$5 |
182
+ | Daily sync, 10 folders | ~$15 |
183
+ | Multiple daily syncs, 20+ folders | ~$25-50 |
184
+
185
+ **After sync, everything is free.** Briefs, fetches, searches, exports — zero API calls.
186
+
187
+ ## Development
188
+
189
+ ```bash
190
+ git clone https://github.com/Hidden-Leaf-Networks/x-skill.git
191
+ cd x-skill
192
+ npm install
193
+
194
+ npm run build # Compile TypeScript
195
+ npm run lint # ESLint
196
+ npm test # Jest test suite
197
+ npm run watch # TypeScript watch mode
198
+ ```
199
+
200
+ ## Downstream Integration
201
+
202
+ x-skill is designed as an intelligence primitive that feeds into larger systems:
203
+
204
+ - **Jira/Confluence** — Create research tickets from briefs via `@hidden-leaf/atlassian-skill`
205
+ - **Training data** — Feed curated posts into fine-tuning pipelines
206
+ - **Planning context** — Ambient research input for AI assistants
207
+ - **Client research** — Structured deliverables from bookmark analysis
208
+
209
+ ## Roadmap
210
+
211
+ | Version | Focus |
212
+ |---------|-------|
213
+ | **1.0.0** | Bookmark intelligence — sync, cache, list, fetch, brief |
214
+ | **1.1** | OAuth flow polish, error recovery improvements |
215
+ | **2.0** | Search skill, thread parsing, profile management |
216
+ | **3.0** | Publish, schedule, reply (write operations) |
217
+
218
+ ## License
219
+
220
+ MIT — [Hidden Leaf Networks](https://github.com/Hidden-Leaf-Networks)
package/SKILL.md ADDED
@@ -0,0 +1,265 @@
1
+ # @hidden-leaf/x-skill — Claude Code Skill Reference
2
+
3
+ > **When to use this skill:** When the user mentions X bookmarks, Twitter bookmarks, bookmark folders, research briefs from X, or wants to analyze saved posts from X/Twitter.
4
+
5
+ ## Architecture: Sync + Cache
6
+
7
+ This skill uses a **sync-then-read** pattern:
8
+ - **`sync`** pulls data from the X API into a local SQLite cache (`~/.x-skill/bookmarks.db`). This is the only operation that costs money.
9
+ - **`list`**, **`fetch`**, **`brief`**, **`stats`** all read from the local cache — free, instant, offline-capable.
10
+
11
+ Always run `sync` first before reading. After that, reads are unlimited.
12
+
13
+ ## Setup
14
+
15
+ ### Required Environment Variables
16
+
17
+ ```
18
+ X_BEARER_TOKEN=<OAuth 2.0 User Context Bearer token>
19
+ X_USER_ID=<your numeric X user ID>
20
+ ```
21
+
22
+ ### Optional
23
+
24
+ ```
25
+ X_CACHE_DB_PATH=<custom path to SQLite DB, default: ~/.x-skill/bookmarks.db>
26
+ ```
27
+
28
+ ### How to Get Credentials
29
+
30
+ 1. **X Developer Account:** Sign up at [developer.x.com](https://developer.x.com) (pay-per-use pricing, ~$5-15/month for bookmark reads)
31
+ 2. **Bearer Token:** Create an app in the Developer Portal → generate OAuth 2.0 User Context token with `bookmark.read`, `tweet.read`, `users.read` scopes
32
+ 3. **User ID:** Use [tweeterid.com](https://tweeterid.com) or call `GET /2/users/me` with your token
33
+
34
+ ## API Reference
35
+
36
+ ### BookmarksSkill
37
+
38
+ The primary v1 skill. All methods use the `BookmarksSkill` class.
39
+
40
+ #### Create Instance
41
+
42
+ ```typescript
43
+ import { createBookmarksSkillFromEnv } from '@hidden-leaf/x-skill';
44
+ const bookmarks = createBookmarksSkillFromEnv();
45
+ ```
46
+
47
+ #### Sync All (hits X API — costs money)
48
+
49
+ Pull all bookmark folders and their tweets into the local cache.
50
+
51
+ ```typescript
52
+ const result = await bookmarks.syncAll();
53
+ // result.totalFolders: number
54
+ // result.totalTweets: number
55
+ // result.folders: [{ name, id, tweetCount }]
56
+ // result.syncedAt: ISO string
57
+ ```
58
+
59
+ #### Sync Single Folder (hits X API — costs money)
60
+
61
+ ```typescript
62
+ const result = await bookmarks.syncFolder('Robotics');
63
+ ```
64
+
65
+ #### List Folders (from cache — free)
66
+
67
+ ```typescript
68
+ const { folders, totalFolders } = bookmarks.listFolders();
69
+ // folders: [{ id, name, description?, tweet_count? }]
70
+ ```
71
+
72
+ #### Fetch by Folder Name (from cache — free)
73
+
74
+ ```typescript
75
+ const result = bookmarks.fetchByFolderName('Robotics');
76
+ // result.bookmarks: [{ tweet, author, formatted }]
77
+ // result.totalTweets: number
78
+ // result.uniqueAuthors: number
79
+ ```
80
+
81
+ #### Fetch by Folder ID (from cache — free)
82
+
83
+ ```typescript
84
+ const result = bookmarks.fetchByFolderId('folder-id-here');
85
+ ```
86
+
87
+ #### Fetch All Bookmarks (from cache — free)
88
+
89
+ ```typescript
90
+ const result = bookmarks.fetchAll();
91
+ ```
92
+
93
+ #### Generate Research Brief (from cache — free)
94
+
95
+ Synthesize a research brief from cached bookmarks. Builds a structured prompt that Claude analyzes.
96
+
97
+ ```typescript
98
+ const { brief, sourceBookmarks } = await bookmarks.brief({
99
+ folderName: 'Robotics',
100
+ maxTweets: 50,
101
+ hlnContext: ['HLOS', 'Asimov', 'Applied AI Studio'],
102
+ customPrompt: 'Focus on humanoid robotics breakthroughs',
103
+ });
104
+
105
+ // brief.topic: 'Robotics'
106
+ // brief.content: <synthesis prompt for Claude>
107
+ // brief.notableVoices: ['@user1', '@user2', ...]
108
+ // brief.themes: ['humanoid robotics', '#AI', ...]
109
+ // brief.hlnRelevance: ['HLOS', 'Asimov', 'Applied AI Studio']
110
+ ```
111
+
112
+ #### Cache Stats (free)
113
+
114
+ ```typescript
115
+ const stats = bookmarks.stats();
116
+ // { folders: 8, tweets: 342, users: 89, lastSync: '2026-03-27T...' }
117
+ ```
118
+
119
+ #### Cleanup
120
+
121
+ ```typescript
122
+ bookmarks.close(); // Close SQLite connection
123
+ ```
124
+
125
+ ### XClient (Low-Level)
126
+
127
+ Direct X API v2 access — bypasses cache, hits API directly.
128
+
129
+ ```typescript
130
+ import { createXClientFromEnv } from '@hidden-leaf/x-skill';
131
+ const x = createXClientFromEnv();
132
+
133
+ // Get bookmarks (paginated)
134
+ const page = await x.getBookmarks({ max_results: 100 });
135
+
136
+ // Get all bookmarks (auto-paginate)
137
+ const { tweets, users } = await x.getAllBookmarks();
138
+
139
+ // List bookmark folders
140
+ const folders = await x.getAllBookmarkFolders();
141
+
142
+ // Get tweets from a folder
143
+ const folderTweets = await x.getAllBookmarkFolderTweets('folder-id');
144
+
145
+ // Get authenticated user profile
146
+ const me = await x.getMe();
147
+ ```
148
+
149
+ ### BookmarkStore (Low-Level Cache)
150
+
151
+ Direct SQLite access for advanced queries.
152
+
153
+ ```typescript
154
+ import { createStoreFromEnv } from '@hidden-leaf/x-skill';
155
+ const store = createStoreFromEnv();
156
+
157
+ const folders = store.getFolders();
158
+ const tweets = store.getTweetsByFolder('folder-id');
159
+ const stats = store.getStats();
160
+ store.close();
161
+ ```
162
+
163
+ ### Build Brief Prompt (Standalone)
164
+
165
+ Build the synthesis prompt without calling the API — useful for custom pipelines.
166
+
167
+ ```typescript
168
+ import { buildBriefPrompt } from '@hidden-leaf/x-skill';
169
+
170
+ const prompt = buildBriefPrompt(enrichedBookmarks, {
171
+ topic: 'AI Medicine',
172
+ hlnContext: ['Applied AI Studio'],
173
+ });
174
+ // Send this prompt to Claude API yourself
175
+ ```
176
+
177
+ ## Usage Patterns
178
+
179
+ ### "Sync my bookmarks then list folders"
180
+ ```typescript
181
+ import { createBookmarksSkillFromEnv } from '@hidden-leaf/x-skill';
182
+ const skill = createBookmarksSkillFromEnv();
183
+
184
+ // First sync (hits API, costs money)
185
+ await skill.syncAll();
186
+
187
+ // Then list from cache (free)
188
+ const { folders } = skill.listFolders();
189
+ for (const f of folders) {
190
+ console.log(`${f.name} (${f.tweet_count ?? '?'} posts)`);
191
+ }
192
+
193
+ skill.close();
194
+ ```
195
+
196
+ ### "Give me a research brief on my AI Medicine bookmarks"
197
+ ```typescript
198
+ import { createBookmarksSkillFromEnv } from '@hidden-leaf/x-skill';
199
+ const skill = createBookmarksSkillFromEnv();
200
+ const { brief } = await skill.brief({
201
+ folderName: 'AI MEDICINE',
202
+ hlnContext: ['Applied AI Studio', 'Orvex'],
203
+ });
204
+ console.log(brief.content);
205
+ skill.close();
206
+ ```
207
+
208
+ ### "What's trending in my Quantum Computing saves?"
209
+ ```typescript
210
+ import { createBookmarksSkillFromEnv } from '@hidden-leaf/x-skill';
211
+ const skill = createBookmarksSkillFromEnv();
212
+ const result = skill.fetchByFolderName('Quantum Computing');
213
+ console.log(`${result.totalTweets} posts from ${result.uniqueAuthors} authors`);
214
+ for (const b of result.bookmarks.slice(0, 10)) {
215
+ console.log(b.formatted);
216
+ }
217
+ skill.close();
218
+ ```
219
+
220
+ ### "Create a Jira ticket from bookmark research" (with atlassian-skill)
221
+ ```typescript
222
+ import { createBookmarksSkillFromEnv } from '@hidden-leaf/x-skill';
223
+ import { createJiraClientFromEnv, adf, text } from '@hidden-leaf/atlassian-skill';
224
+
225
+ const skill = createBookmarksSkillFromEnv();
226
+ const jira = createJiraClientFromEnv();
227
+
228
+ const { brief } = await skill.brief({ folderName: 'CHIPS' });
229
+
230
+ await jira.createIssue({
231
+ project: 'HLN',
232
+ issuetype: 'Task',
233
+ summary: `Research Brief: ${brief.topic} (${brief.tweetCount} posts)`,
234
+ description: adf().paragraph(text().text(brief.content)).build(),
235
+ labels: ['research', 'x-skill', 'auto-generated'],
236
+ });
237
+
238
+ skill.close();
239
+ ```
240
+
241
+ ## Error Handling
242
+
243
+ | Error | Cause | Recovery |
244
+ |-------|-------|----------|
245
+ | `XAuthenticationError` | Invalid/expired Bearer token | Regenerate token at developer.x.com |
246
+ | `XRateLimitError` | Rate limit hit | Wait `retryAfter` seconds, then retry |
247
+ | `XNotFoundError` | Endpoint or resource not found | Check user ID and folder IDs |
248
+ | `XApiRequestError` (403) | Missing OAuth scope or access level | Ensure `bookmark.read` scope |
249
+ | `"not found in cache"` | Cache empty for folder | Run `syncAll()` or `syncFolder()` first |
250
+
251
+ ## Cost Notes
252
+
253
+ - X API uses pay-per-use pricing (Feb 2026+)
254
+ - **Sync is the only paid operation** — all reads hit the local SQLite cache for free
255
+ - Same post requested within 24h UTC window = single charge (deduplication)
256
+ - Estimated cost: ~$5-15/month for periodic syncs
257
+ - Recommend syncing once daily or on-demand, not continuously
258
+
259
+ ## Downstream Consumers
260
+
261
+ Brief output is structured for ingestion by:
262
+ - **Jira tickets** via `@hidden-leaf/atlassian-skill`
263
+ - **ARIA v4** planning context and ambient research
264
+ - **Scroll** training data corpus
265
+ - **Applied AI Studio** client research deliverables
@@ -0,0 +1,48 @@
1
+ /**
2
+ * SQLite-backed local cache for X bookmark data.
3
+ *
4
+ * Pattern: sync pulls from X API → upserts into local DB.
5
+ * All reads (list, fetch, brief) hit the cache, not the API.
6
+ * You only pay when you sync.
7
+ */
8
+ import type { Tweet, User, BookmarkFolder } from '../clients/types.js';
9
+ export declare class BookmarkStore {
10
+ private readonly db;
11
+ constructor(dbPath?: string);
12
+ private migrate;
13
+ upsertFolder(folder: BookmarkFolder): void;
14
+ upsertUser(user: User): void;
15
+ upsertTweet(tweet: Tweet): void;
16
+ linkTweetToFolder(folderId: string, tweetId: string, position: number): void;
17
+ /**
18
+ * Upsert a full folder sync result in a single transaction.
19
+ */
20
+ syncFolderData(folder: BookmarkFolder, tweets: Tweet[], users: Map<string, User>): void;
21
+ getFolders(): BookmarkFolder[];
22
+ getFolderByName(name: string): BookmarkFolder | undefined;
23
+ getTweetsByFolder(folderId: string): {
24
+ tweet: Tweet;
25
+ author: User | undefined;
26
+ }[];
27
+ getAllTweets(): {
28
+ tweet: Tweet;
29
+ author: User | undefined;
30
+ }[];
31
+ getStats(): {
32
+ folders: number;
33
+ tweets: number;
34
+ users: number;
35
+ lastSync: string | null;
36
+ };
37
+ logSyncStart(scope: string, folderId?: string): number;
38
+ logSyncComplete(logId: number, tweetCount: number): void;
39
+ logSyncError(logId: number, _error: string): void;
40
+ private rowToTweet;
41
+ private rowToUser;
42
+ close(): void;
43
+ }
44
+ /**
45
+ * Create a BookmarkStore with default or env-configured path.
46
+ */
47
+ export declare function createStoreFromEnv(): BookmarkStore;
48
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/cache/store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAKvE,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAoB;gBAE3B,MAAM,CAAC,EAAE,MAAM;IAmB3B,OAAO,CAAC,OAAO;IA6Ef,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAsB1C,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAkC5B,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAyC/B,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAgB5E;;OAEG;IACH,cAAc,CACZ,MAAM,EAAE,cAAc,EACtB,MAAM,EAAE,KAAK,EAAE,EACf,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,GACvB,IAAI;IAqBP,UAAU,IAAI,cAAc,EAAE;IAc9B,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAgBzD,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,IAAI,GAAG,SAAS,CAAA;KAAE,EAAE;IAoBjF,YAAY,IAAI;QAAE,KAAK,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,IAAI,GAAG,SAAS,CAAA;KAAE,EAAE;IAkB5D,QAAQ,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;IAevF,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM;IAStD,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAOxD,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAWjD,OAAO,CAAC,UAAU;IAkClB,OAAO,CAAC,SAAS;IAsBjB,KAAK,IAAI,IAAI;CAGd;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,aAAa,CAElD"}