@pushrec/skills 0.2.0 → 0.3.2

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.
@@ -0,0 +1,9 @@
1
+ # Bundled Skills
2
+
3
+ This directory contains skills bundled with the CLI package.
4
+ These skills are installed automatically during `npx @pushrec/skills setup`.
5
+
6
+ ## pushrec-skills
7
+
8
+ Universal entry point for all pushREC skill operations.
9
+ Copied to ~/.claude/skills/pushrec-skills/ during setup if not already installed.
@@ -0,0 +1,360 @@
1
+ ---
2
+ name: pushrec-skills
3
+ version: 1.0.0
4
+ description: "Universal entry point for all pushREC skill operations. Onboarding, skill discovery, troubleshooting, API key setup, and update guidance. Use when: first time using pushREC skills, need help finding the right skill, skill isn't working, setting up API keys, updating skills, or asking how skills work. Aliases: /onboarding, /start, /get-started, /help, /skills, /pushrec."
5
+ category: meta-tools
6
+ aliases: [onboarding, start, get-started, help, skills, pushrec]
7
+ session_behavior: on-demand
8
+ allowed-tools: [Read, Write, Edit, Bash, Glob, Grep, AskUserQuestion]
9
+ ---
10
+
11
+ # pushREC Skills
12
+
13
+ Universal entry point for all pushREC skill operations. Handles first-time onboarding, skill discovery, troubleshooting, API key setup, and update guidance.
14
+
15
+ ## Capabilities
16
+
17
+ | Mode | Trigger | What It Does |
18
+ |------|---------|-------------|
19
+ | **Onboarding** | First-time user (no profile.json) | Diagnostic, profiling, CC 101, first win, skill map, unlock guide |
20
+ | **Recommendation** | "Which skill for X?" | Scan loaded skill descriptions, rank matches, explain WHY |
21
+ | **Troubleshooting** | "X not working" | Run diagnostic, decision tree, escalation path |
22
+ | **API Key Setup** | "Set up API keys" | Read prerequisites.yaml, guide step-by-step by level |
23
+ | **Update Guidance** | "Update skills" | CLI commands for update, list, install new |
24
+ | **System Info** | General questions | Explain how skills work, architecture, concepts |
25
+
26
+ ### Paradigm: LLM-Native Recommendations
27
+
28
+ Claude Code auto-loads ALL installed skill names and descriptions into every session context. You already know what every skill does. No catalog file or recommendation script needed. You ARE the recommendation engine. The only hardcoded data is `references/prerequisites.yaml` (API key requirements cannot be inferred from descriptions).
29
+
30
+ ---
31
+
32
+ ## Intent Detection Protocol
33
+
34
+ Think step by step. Before responding, determine the user's mode.
35
+
36
+ ### Step 1: Check State Files
37
+
38
+ ```bash
39
+ ls ~/.claude/skills/pushrec-skills/state/profile.json 2>/dev/null
40
+ ls ~/.claude/skills/pushrec-skills/state/progress.json 2>/dev/null
41
+ ```
42
+
43
+ ### Step 2: Route Based on State + Input
44
+
45
+ | State | User Signal | Route To |
46
+ |-------|-------------|----------|
47
+ | No profile.json | Any | First-Time Onboarding |
48
+ | Profile exists, onboarding incomplete | Any | Resume Onboarding (pick up where they left off) |
49
+ | Profile exists | "X not working", error messages | Troubleshooting Protocol |
50
+ | Profile exists | "update", "new skills" | Update Guidance |
51
+ | Profile exists | "API key", "set up Google", credential terms | API Key Setup |
52
+ | Profile exists | Skill-related question | Recommendation Protocol |
53
+ | Profile exists | General system question | System Info (explain concepts) |
54
+ | Any | "reset", "start over" | Delete state/ directory, restart onboarding |
55
+ | Ambiguous | Cannot determine intent | Ask ONE clarifying question |
56
+
57
+ ---
58
+
59
+ ## First-Time Onboarding
60
+
61
+ Six phases, executed sequentially. Track progress in `state/progress.json`.
62
+
63
+ ### Phase 0: Diagnostic
64
+
65
+ Run the system readiness check:
66
+
67
+ ```bash
68
+ python3 ~/.claude/skills/pushrec-skills/scripts/diagnostic.py
69
+ ```
70
+
71
+ Parse the JSON output. Present results as a checklist:
72
+ - PASS items: brief confirmation
73
+ - FAIL items (exit code 1): STOP. Guide the user to fix critical prerequisites before continuing.
74
+ - WARN items (exit code 2): note but continue
75
+
76
+ If Claude Code, Node.js, or npm are missing, provide platform-specific install instructions and STOP. Do not proceed with onboarding until critical prerequisites pass.
77
+
78
+ Write `state/progress.json` with `diagnostic: {status: "completed"}`.
79
+
80
+ ### Phase 1: Adaptive Profiling
81
+
82
+ Gather business context to personalize skill recommendations. Use conversational questions, not a rigid form. Write answers to `state/profile.json`.
83
+
84
+ **Round 1 (always ask -- 3 questions):**
85
+ 1. What type of business do you run? (agency, coaching, SaaS, e-commerce, freelance, creator, other)
86
+ 2. What is your primary content platform? (LinkedIn, X/Twitter, YouTube, Instagram, email, podcast, blog, other)
87
+ 3. What is your biggest challenge right now? (not enough content, low engagement, no leads, poor conversion, time management, scaling, other)
88
+
89
+ **Round 2 (ask if user seems engaged and responsive -- 3 questions):**
90
+ 4. Who is your target audience? (open text)
91
+ 5. What is your approximate monthly revenue range? (pre-revenue, <$10K, $10-50K, $50-100K, $100K+)
92
+ 6. How much experience do you have with AI tools? (never used, tried a few times, use weekly, use daily)
93
+
94
+ **Round 3 (ask if terminal comfort or CC experience is NOT obvious from prior answers -- 2 questions):**
95
+ 7. How comfortable are you with the terminal/command line? (never used, basic, comfortable, advanced)
96
+ 8. How much experience do you have with Claude Code specifically? (never, barely, sometimes, regularly, daily)
97
+
98
+ **Adaptive rules:**
99
+ - If the user gives short or impatient answers, skip remaining rounds
100
+ - If diagnostic shows many installed skills and API keys, skip Round 3 (infer advanced user)
101
+ - Write profile.json after each round completes (progressive save)
102
+
103
+ **profile.json schema:** `{"version":"1.0.0", "created_at":"ISO", "business_type":"agency", "content_platform":"linkedin", "biggest_challenge":"not-enough-content", "target_audience":"B2B SaaS founders", "revenue_range":"$50-100K", "ai_experience":"use-daily", "terminal_comfort":"comfortable", "cc_experience":"barely"}`
104
+
105
+ Write `progress.json` with `profiling: {status: "completed"}`.
106
+
107
+ ### Phase 2: Claude Code 101
108
+
109
+ **Conditional:** Skip if `cc_experience` is "regularly" or "daily". Mark as `skipped` with reason in progress.json.
110
+
111
+ Deliver a 60-second overview covering ONLY these basics:
112
+ - Skills are invoked by typing `/skill-name` (for example, `/hormozi`, `/linkedin-copywriting`)
113
+ - Claude Code already sees all your installed skill descriptions -- just describe what you need and it finds the right skill
114
+ - Skills produce real output: copy, strategies, images, automations
115
+ - You can chain skills together in a single conversation
116
+
117
+ Do NOT cover: hooks, sub-agents, settings files, MCP servers, teams, or advanced orchestration. Those are Level 5 concepts.
118
+
119
+ Write `progress.json` with `cc101: {status: "completed"}` or `{status: "skipped", reason: "cc_experience=regularly"}`.
120
+
121
+ ### Phase 3: First Win
122
+
123
+ This is the most important phase. Generate a real, valuable output using ONE skill.
124
+
125
+ **Protocol:**
126
+ 1. Read `state/profile.json`
127
+ 2. Consider ALL loaded skill descriptions in your context
128
+ 3. Select the single best Level 0 skill (NO API keys required) that matches their profile
129
+ 4. Explain your choice: "Based on your profile -- [business_type] focused on [content_platform] with [biggest_challenge] -- I recommend starting with `/[skill-name]` because [specific reason]."
130
+ 5. Generate a custom context prompt they can use with that skill. For example: "Try typing: `/linkedin-copywriting` and then describe your business and target audience."
131
+ 6. WAIT for the user to try it and confirm before proceeding. Ask: "Go ahead and try it now. Let me know when you're ready to continue."
132
+
133
+ **Selection criteria:**
134
+ - Must be Level 0 (no API keys required)
135
+ - Must directly address their biggest_challenge or content_platform
136
+ - Prefer specific skills over general ones (e.g., `/linkedin-copywriting` over `/copywriting` for LinkedIn users)
137
+ - Never fabricate skill names -- only recommend skills that exist in your loaded context
138
+
139
+ Write `progress.json` with `first_win: {status: "completed", skill_recommended: "skill-name"}`.
140
+
141
+ ### Phase 4: Skill Map
142
+
143
+ Present a personalized overview of available skills organized by level. Show only levels relevant to their profile.
144
+
145
+ **Structure:**
146
+ - **Level 0 -- Foundation (available now):** Group by category relevant to their profile. Highlight 5-8 most relevant skills with one-line descriptions. Mention total count (80+ skills).
147
+ - **Level 1 -- Google Workspace:** List capabilities unlocked (Gmail, Sheets, Docs, Calendar, Drive, Contacts, Slides). Note: "~15 minutes one-time setup."
148
+ - **Level 2 -- Creative and Data:** List key capabilities (AI image generation, voice synthesis, vision analysis, CRM). Note per-service setup time.
149
+ - **Level 3 -- Communication:** List capabilities (SMS, AI phone agents, web scraping). Note KYC requirements.
150
+ - **Level 4 -- Knowledge Management:** Mention Obsidian vault integration. Only highlight if user shows interest in knowledge management.
151
+ - **Level 5 -- Advanced:** Custom agents, multi-skill workflows, hook automation, custom skill creation. Mention for power users only.
152
+
153
+ Write `progress.json` with `skill_map: {status: "completed"}`.
154
+
155
+ ### Phase 5: Progressive Unlock
156
+
157
+ Recommend the NEXT level to unlock based on their profile. One level only -- do not overwhelm.
158
+
159
+ **Selection logic:**
160
+ - If they work with Google Workspace tools: recommend Level 1
161
+ - If they create visual content: recommend Level 2 (fal-ai, gemini-vision)
162
+ - If they need outreach automation: recommend Level 3 (twilio)
163
+ - Default: recommend Level 1 (broadest utility)
164
+
165
+ Provide the specific setup steps for that one level. Reference `/pushrec-skills` for future API key setup help.
166
+
167
+ Write `progress.json` with `unlock_guide: {status: "completed"}` and `completed_at: ISO-8601`.
168
+
169
+ **progress.json schema:** `{"version":"1.0.0", "started_at":"ISO", "completed_at":null, "phases":{"diagnostic":{"status":"completed"}, "profiling":{"status":"completed"}, "cc101":{"status":"skipped","reason":"cc_experience=regularly"}, "first_win":{"status":"completed","skill_recommended":"linkedin-copywriting"}, "skill_map":{"status":"completed"}, "unlock_guide":{"status":"completed"}}, "current_level":"L0", "skills_invoked":["linkedin-copywriting"], "api_keys_configured":[], "last_session_at":"ISO"}`
170
+
171
+ ---
172
+
173
+ ## Returning User Flow
174
+
175
+ If `state/profile.json` exists and `state/progress.json` shows incomplete onboarding:
176
+ 1. Read progress.json to find the last completed phase
177
+ 2. Greet: "Welcome back. You left off at [phase]. Ready to continue?"
178
+ 3. Resume from the next incomplete phase
179
+
180
+ If onboarding is complete, route based on their input signal (see Intent Detection).
181
+
182
+ If the user returns after a long gap (check `last_session_at`), offer a brief refresher:
183
+ - "It has been a while. Want a quick recap of your skill map, or do you have a specific question?"
184
+
185
+ Update `last_session_at` in progress.json at every session.
186
+
187
+ ---
188
+
189
+ ## Recommendation Protocol
190
+
191
+ When a user asks "which skill should I use for X?" or describes a need:
192
+
193
+ 1. **Parse the request** into: domain (marketing, sales, content, automation, research), platform (LinkedIn, YouTube, email), and output type (copy, strategy, image, automation)
194
+ 2. **Scan ALL skill descriptions** loaded in your context. You already have them -- do not read registry.json or any catalog file.
195
+ 3. **Select top 1-3 matches.** For each match, provide:
196
+ - Skill name (invocable as `/name`)
197
+ - WHY it matches their request (one sentence)
198
+ - API key requirement: "No setup needed" (L0) or "Requires [KEY_NAME]" (L1-L4)
199
+ 4. **Rank by specificity:** specific skills beat general ones. Single-purpose skills beat orchestrators unless the user explicitly needs a workflow.
200
+ 5. **If uncertain,** read the candidate skill's SKILL.md before recommending to verify fit.
201
+ 6. **Never fabricate skill names.** Only recommend skills that exist in your loaded context. If no skill matches, say so honestly.
202
+
203
+ Example: "For LinkedIn carousel posts, I recommend: 1. `/linkedin-copywriting` -- designed for LinkedIn content with proven copywriting psychology. No setup needed. 2. `/copywriting` -- broader foundations that work across platforms. No setup needed."
204
+
205
+ ---
206
+
207
+ ## Troubleshooting Protocol
208
+
209
+ When a user reports a problem ("X is not working", errors, unexpected behavior):
210
+
211
+ ### Step 1: Gather Information
212
+
213
+ Ask: Which skill? What command triggered the error? What error message? (Ask them to paste it.)
214
+
215
+ ### Step 2: Run Diagnostic
216
+
217
+ ```bash
218
+ python3 ~/.claude/skills/pushrec-skills/scripts/diagnostic.py
219
+ ```
220
+
221
+ Check for missing prerequisites, missing API keys, or license issues.
222
+
223
+ ### Step 3: Decision Tree
224
+
225
+ | Symptom | Likely Cause | Fix |
226
+ |---------|-------------|-----|
227
+ | "command not found: claude" | Claude Code not installed | Install from anthropic.com |
228
+ | "command not found: node" | Node.js not installed | Install from nodejs.org (v20+) |
229
+ | "ENOENT" or file not found | Skill not installed | Run `npx @pushrec/skills install --all` |
230
+ | Authentication/401 errors | Missing or expired API key | Guide through API key setup for that skill |
231
+ | "Permission denied" | File permission issue | `chmod +x` the script, or check directory permissions |
232
+ | Skill produces no output | Skill invocation issue | Verify skill name, try `/skill-name` syntax |
233
+ | Import errors in scripts | Missing Python dependencies | Check skill's requirements, run `pip install` or `uv run --with` |
234
+ | License validation fails | License issue | Run `npx @pushrec/skills auth login` to re-authenticate |
235
+
236
+ ### Step 4: Escalation
237
+
238
+ If the issue cannot be resolved through troubleshooting:
239
+ 1. Suggest posting in the community: **skool.com/pushrec-2909**
240
+ 2. Include in their post: skill name, error message, diagnostic output, steps already tried
241
+ 3. Mention live Q&A sessions in Skool for real-time help
242
+
243
+ ---
244
+
245
+ ## API Key Setup
246
+
247
+ When a user asks about setting up API keys or unlocking a specific level:
248
+
249
+ ### Step 1: Identify What They Need
250
+
251
+ Read `references/prerequisites.yaml` to check requirements for the requested skill or level.
252
+
253
+ ### Step 2: Guide by Level
254
+
255
+ **Level 1 -- Google OAuth2 (one-time, ~15 minutes):**
256
+ 1. Go to Google Cloud Console (console.cloud.google.com)
257
+ 2. Create a new project (or use existing)
258
+ 3. Enable required APIs: Gmail, Sheets, Docs, Calendar, Drive, Contacts, Slides
259
+ 4. Create OAuth 2.0 credentials (Desktop app type)
260
+ 5. Download the JSON credentials file
261
+ 6. Place it in the location specified by each skill (varies per skill -- check prerequisites.yaml)
262
+ 7. Run the skill's authenticate.py script to complete the OAuth consent flow
263
+
264
+ **Level 2 -- Individual API Keys (~2-5 minutes each):**
265
+ Walk through each service's signup:
266
+ - Provide the setup_url from prerequisites.yaml
267
+ - Tell them which env var to set in `~/.claude/.env`
268
+ - Format: `echo 'KEY_NAME=your-key-here' >> ~/.claude/.env`
269
+
270
+ **Level 3 -- Service Accounts (~10 minutes each):**
271
+ - Note KYC or billing requirements upfront
272
+ - Guide through account creation and credential setup
273
+ - Warn about usage-based billing where applicable
274
+
275
+ **Level 4 -- Obsidian:**
276
+ - Download from obsidian.md (free)
277
+ - Create or open a vault
278
+ - Run `/vault-manager` for guided vault setup
279
+
280
+ After setup, update `progress.json` with new entries in `api_keys_configured`.
281
+
282
+ ---
283
+
284
+ ## Update Guidance
285
+
286
+ When a user asks about updating skills or finding new ones:
287
+
288
+ ### Update Existing Skills
289
+ ```bash
290
+ npx @pushrec/skills update # Update all installed skills
291
+ npx @pushrec/skills update [name] # Update a specific skill
292
+ ```
293
+
294
+ ### Install New Skills
295
+ ```bash
296
+ npx @pushrec/skills install --all # Install any skills added since last install
297
+ npx @pushrec/skills list # See all available vs. installed
298
+ ```
299
+
300
+ ### Check System Health
301
+ ```bash
302
+ python3 ~/.claude/skills/pushrec-skills/scripts/diagnostic.py
303
+ ```
304
+
305
+ Note: `install --all` skips already-installed skills (no duplicates). Running it again after new skills are published picks up the new ones automatically. The `update` command handles version bumps for already-installed skills.
306
+
307
+ ---
308
+
309
+ ## Anti-Patterns
310
+
311
+ | Anti-Pattern | Correct Behavior |
312
+ |-------------|-----------------|
313
+ | Recommending skills not in loaded context | Only recommend skills you can see in the system context |
314
+ | Hardcoding skill-to-profile mappings | Read profile + scan descriptions dynamically every time |
315
+ | Dumping the full skill catalog | Show only relevant skills, grouped by the user's needs |
316
+ | Skipping the diagnostic on first run | Always run diagnostic.py in Phase 0 |
317
+ | Proceeding after critical failures | STOP and help fix prerequisites before continuing |
318
+ | Recommending L1+ skills without mentioning API key requirements | Always state the setup requirement alongside the recommendation |
319
+
320
+ ### Verification Checklist
321
+
322
+ Before finishing any response, verify:
323
+ - Think step by step: Did I follow the correct protocol for this mode?
324
+ - Before you finish: Are all skill names I mentioned real skills in my loaded context?
325
+ - If you don't know: Am I certain about API key requirements, or should I check prerequisites.yaml?
326
+ - Did I update state files (profile.json, progress.json) if the user completed a phase?
327
+ - Did I provide clear next steps the user can act on?
328
+
329
+ ---
330
+
331
+ ## Quick Reference
332
+
333
+ | Need | Command or Action |
334
+ |------|-------------------|
335
+ | Start onboarding | Type `/pushrec-skills` |
336
+ | Find a skill | Describe what you need -- the LLM matches automatically |
337
+ | Set up API keys | Type `/pushrec-skills` and ask about API key setup |
338
+ | Update skills | `npx @pushrec/skills update` |
339
+ | Install new skills | `npx @pushrec/skills install --all` |
340
+ | List all skills | `npx @pushrec/skills list` |
341
+ | Get help | Post in skool.com/pushrec-2909 |
342
+ | Reset onboarding | Type `/pushrec-skills` and say "reset" |
343
+
344
+ ### Prerequisites
345
+
346
+ - **Claude Code subscription:** Pro ($20/mo) or Max ($100/mo) from anthropic.com
347
+ - **Node.js:** v20 or higher (nodejs.org)
348
+ - **npm:** included with Node.js
349
+
350
+ ### State Files
351
+
352
+ | File | Purpose |
353
+ |------|---------|
354
+ | `state/profile.json` | Business profile from adaptive profiling |
355
+ | `state/progress.json` | Completed phases, unlocked levels, skills invoked |
356
+
357
+ ### Community
358
+
359
+ - **Skool:** skool.com/pushrec-2909
360
+ - **Live Q&A:** check Skool for scheduled sessions
@@ -0,0 +1,375 @@
1
+ # pushREC Skills Prerequisites
2
+ # version: 1.0.0
3
+ #
4
+ # Skills NOT listed here require NO API keys or credentials (Level 0).
5
+ # 80+ skills work immediately after install with zero configuration.
6
+ #
7
+ # Level definitions:
8
+ # L1: Google OAuth2 - one-time browser consent flow (~15 min total)
9
+ # L2: API keys - individual service signups (~2-5 min each)
10
+ # L3: Service accounts - KYC verification or usage-based billing (~10 min each)
11
+ # L4: Applications - requires desktop app (Obsidian) installed
12
+
13
+ # ============================================================================
14
+ # LEVEL 1: Google OAuth2
15
+ # One-time setup: Create a Google Cloud project, enable APIs, create OAuth
16
+ # credentials. All Google skills share the same GCP project.
17
+ # ============================================================================
18
+
19
+ gmail:
20
+ level: 1
21
+ requires:
22
+ - env: GOOGLE_OAUTH_CREDENTIALS
23
+ type: oauth2
24
+ service: Google Cloud Console
25
+ setup_url: https://console.cloud.google.com/apis/credentials
26
+ setup_time: "15 minutes (one-time for all Google services)"
27
+ credentials_path: "~/.gmail-mcp/gcp-oauth.keys.json"
28
+ notes: "OAuth2 browser consent flow. Tokens stored at ~/.gmail-mcp/credentials.json"
29
+
30
+ google-sheets:
31
+ level: 1
32
+ requires:
33
+ - env: GOOGLE_OAUTH_CREDENTIALS
34
+ type: oauth2
35
+ service: Google Cloud Console
36
+ setup_url: https://console.cloud.google.com/apis/credentials
37
+ setup_time: "15 minutes (shared with other Google services)"
38
+ credentials_path: "~/.claude/.credentials/sheets-client-secret.json"
39
+ notes: "OAuth2 preferred. Service Account fallback supported."
40
+
41
+ google-docs:
42
+ level: 1
43
+ requires:
44
+ - env: GOOGLE_OAUTH_CREDENTIALS
45
+ type: oauth2
46
+ service: Google Cloud Console
47
+ setup_url: https://console.cloud.google.com/apis/credentials
48
+ setup_time: "15 minutes (shared with other Google services)"
49
+ credentials_path: "~/.claude/.credentials/docs-client-secret.json"
50
+ notes: "OAuth2 preferred. Service Account fallback supported."
51
+
52
+ google-calendar:
53
+ level: 1
54
+ requires:
55
+ - env: GOOGLE_OAUTH_CREDENTIALS
56
+ type: oauth2
57
+ service: Google Cloud Console
58
+ setup_url: https://console.cloud.google.com/apis/credentials
59
+ setup_time: "15 minutes (shared with other Google services)"
60
+ credentials_path: "~/.calendar-mcp/gcp-oauth.keys.json"
61
+ notes: "OAuth2 browser consent flow."
62
+
63
+ google-drive:
64
+ level: 1
65
+ requires:
66
+ - env: GOOGLE_OAUTH_CREDENTIALS
67
+ type: oauth2
68
+ service: Google Cloud Console
69
+ setup_url: https://console.cloud.google.com/apis/credentials
70
+ setup_time: "15 minutes (shared with other Google services)"
71
+ credentials_path: "~/.drive-mcp/gcp-oauth.keys.json"
72
+ notes: "OAuth2 browser consent flow."
73
+
74
+ google-contacts:
75
+ level: 1
76
+ requires:
77
+ - env: GOOGLE_OAUTH_CREDENTIALS
78
+ type: oauth2
79
+ service: Google Cloud Console
80
+ setup_url: https://console.cloud.google.com/apis/credentials
81
+ setup_time: "15 minutes (shared with other Google services)"
82
+ credentials_path: "~/.contacts-mcp/gcp-oauth.keys.json"
83
+ notes: "Read-only. Shares GCP project with Gmail."
84
+
85
+ google-slides:
86
+ level: 1
87
+ requires:
88
+ - env: GOOGLE_OAUTH_CREDENTIALS
89
+ type: oauth2
90
+ service: Google Cloud Console
91
+ setup_url: https://console.cloud.google.com/apis/credentials
92
+ setup_time: "15 minutes (shared with other Google services)"
93
+ credentials_path: "~/.claude/.credentials/slides-token.json"
94
+ notes: "OAuth2 or Service Account (GOOGLE_SERVICE_ACCOUNT_JSON)."
95
+
96
+ finance-manager:
97
+ level: 1
98
+ requires:
99
+ - env: GOOGLE_OAUTH_CREDENTIALS
100
+ type: oauth2
101
+ service: Google Cloud Console
102
+ setup_url: https://console.cloud.google.com/apis/credentials
103
+ setup_time: "15 minutes (shared with other Google services)"
104
+ notes: "Composes gmail + google-sheets + google-docs + ocr-processor. Set up Google OAuth first."
105
+
106
+ # ============================================================================
107
+ # LEVEL 2: API Keys
108
+ # Individual service signups. Each key takes 2-5 minutes to obtain.
109
+ # ============================================================================
110
+
111
+ fal-ai:
112
+ level: 2
113
+ requires:
114
+ - env: FAL_KEY
115
+ type: api_key
116
+ service: FAL.ai
117
+ setup_url: https://fal.ai/dashboard/keys
118
+ setup_time: "2 minutes"
119
+ notes: "Set in ~/.claude/.env as FAL_KEY=your-key"
120
+
121
+ ultimate-infographics:
122
+ level: 2
123
+ requires:
124
+ - env: FAL_KEY
125
+ type: api_key
126
+ service: FAL.ai
127
+ setup_url: https://fal.ai/dashboard/keys
128
+ setup_time: "2 minutes (shared with fal-ai)"
129
+ notes: "Uses fal-ai for Seedream 4.5 image generation."
130
+
131
+ elevenlabs:
132
+ level: 2
133
+ requires:
134
+ - env: ELEVENLABS_API_KEY
135
+ type: api_key
136
+ service: ElevenLabs
137
+ setup_url: https://elevenlabs.io/app/settings/api-keys
138
+ setup_time: "2 minutes"
139
+ notes: "Set in ~/.claude/.env as ELEVENLABS_API_KEY=sk_xxx"
140
+
141
+ gemini-vision:
142
+ level: 2
143
+ requires:
144
+ - env: GOOGLE_API_KEY
145
+ type: api_key
146
+ service: Google AI Studio
147
+ setup_url: https://aistudio.google.com/apikey
148
+ setup_time: "2 minutes"
149
+ notes: "Shared with ocr-processor. Set in ~/.claude/.env as GOOGLE_API_KEY=AIza..."
150
+
151
+ ocr-processor:
152
+ level: 2
153
+ requires:
154
+ - env: GOOGLE_API_KEY
155
+ type: api_key
156
+ service: Google AI Studio
157
+ setup_url: https://aistudio.google.com/apikey
158
+ setup_time: "2 minutes (shared with gemini-vision)"
159
+ notes: "Primary provider: Gemini. Also supports MISTRAL_API_KEY as alternative."
160
+
161
+ airtable:
162
+ level: 2
163
+ requires:
164
+ - env: AIRTABLE_API_KEY
165
+ type: api_key
166
+ service: Airtable
167
+ setup_url: https://airtable.com/create/tokens
168
+ setup_time: "3 minutes"
169
+ notes: "Personal Access Token (pat prefix). Set as AIRTABLE_API_KEY=patXXX"
170
+
171
+ crm:
172
+ level: 2
173
+ requires:
174
+ - env: AIRTABLE_API_KEY
175
+ type: api_key
176
+ service: Airtable
177
+ setup_url: https://airtable.com/create/tokens
178
+ setup_time: "3 minutes (shared with airtable)"
179
+ notes: "CRM orchestration layer built on Airtable. Same API key."
180
+
181
+ deploy-vercel:
182
+ level: 2
183
+ requires:
184
+ - env: VERCEL_TOKEN
185
+ type: api_key
186
+ service: Vercel
187
+ setup_url: https://vercel.com/account/tokens
188
+ setup_time: "2 minutes"
189
+ notes: "Personal access token. Set in ~/.claude/.env as VERCEL_TOKEN=xxx"
190
+
191
+ retell-ai:
192
+ level: 2
193
+ requires:
194
+ - env: RETELL_API_KEY
195
+ type: api_key
196
+ service: Retell AI
197
+ setup_url: https://www.retellai.com/dashboard
198
+ setup_time: "3 minutes"
199
+ notes: "Set in ~/.claude/.env as RETELL_API_KEY=key_xxx"
200
+
201
+ exa:
202
+ level: 2
203
+ requires:
204
+ - env: EXA_API_KEY
205
+ type: api_key
206
+ service: Exa.ai
207
+ setup_url: https://dashboard.exa.ai/api-keys
208
+ setup_time: "2 minutes"
209
+ notes: "AI-native search API. Set in ~/.claude/.env as EXA_API_KEY=xxx"
210
+
211
+ gumroad:
212
+ level: 2
213
+ requires:
214
+ - env: GUMROAD_ACCESS_TOKEN
215
+ type: api_key
216
+ service: Gumroad
217
+ setup_url: https://app.gumroad.com/settings/advanced#application-form
218
+ setup_time: "3 minutes"
219
+ notes: "OAuth access token. Also supports ~/.gumroad/credentials file."
220
+
221
+ convex:
222
+ level: 2
223
+ requires:
224
+ - env: CONVEX_DEPLOY_KEY
225
+ type: api_key
226
+ service: Convex
227
+ setup_url: https://dashboard.convex.dev
228
+ setup_time: "3 minutes"
229
+ notes: "Deploy key for Convex projects. Set in ~/.claude/.env as CONVEX_DEPLOY_KEY=xxx"
230
+
231
+ youtube-api:
232
+ level: 2
233
+ requires:
234
+ - env: YOUTUBE_API_KEY
235
+ type: api_key
236
+ service: Google Cloud Console
237
+ setup_url: https://console.cloud.google.com/apis/credentials
238
+ setup_time: "3 minutes (shared with GCP project)"
239
+ notes: "YouTube Data API v3. Optional - many operations work without key via yt-dlp."
240
+
241
+ skool-api:
242
+ level: 2
243
+ requires:
244
+ - env: SKOOL_COOKIES
245
+ type: cookie_auth
246
+ service: Skool.com
247
+ setup_url: https://www.skool.com
248
+ setup_time: "5 minutes (browser login)"
249
+ notes: "Cookie-based auth persisted at ~/.skool/cookies.json. No API key - uses browser login."
250
+
251
+ # ============================================================================
252
+ # LEVEL 3: Service Accounts
253
+ # Require KYC verification, usage-based billing, or special configuration.
254
+ # ============================================================================
255
+
256
+ twilio:
257
+ level: 3
258
+ requires:
259
+ - env: TWILIO_ACCOUNT_SID
260
+ type: service_account
261
+ service: Twilio
262
+ setup_url: https://console.twilio.com
263
+ setup_time: "10 minutes"
264
+ - env: TWILIO_AUTH_TOKEN
265
+ type: service_account
266
+ service: Twilio
267
+ setup_url: https://console.twilio.com
268
+ setup_time: "included above"
269
+ - env: TWILIO_PHONE_NUMBER
270
+ type: service_account
271
+ service: Twilio
272
+ setup_url: https://console.twilio.com
273
+ setup_time: "included above"
274
+ notes: "Three env vars required. KYC may be needed for full access."
275
+
276
+ bright-data:
277
+ level: 3
278
+ requires:
279
+ - env: API_TOKEN
280
+ type: service_account
281
+ service: Bright Data
282
+ setup_url: https://brightdata.com/cp/setting
283
+ setup_time: "10 minutes"
284
+ notes: "Usage-based billing. Set in ~/.claude/.env as API_TOKEN=xxx"
285
+
286
+ google-maps:
287
+ level: 3
288
+ requires:
289
+ - env: GOOGLE_MAPS_API_KEY
290
+ type: api_key
291
+ service: Google Cloud Console
292
+ setup_url: https://console.cloud.google.com/apis/credentials
293
+ setup_time: "5 minutes"
294
+ notes: "Requires billing enabled on GCP project. Set as GOOGLE_MAPS_API_KEY=AIza..."
295
+
296
+ weather:
297
+ level: 3
298
+ requires:
299
+ - env: OPENWEATHERMAP_API_KEY
300
+ type: api_key
301
+ service: OpenWeatherMap
302
+ setup_url: https://home.openweathermap.org/api_keys
303
+ setup_time: "3 minutes"
304
+ notes: "Free tier available. One Call 3.0 requires paid subscription."
305
+
306
+ flight-tracker:
307
+ level: 3
308
+ requires:
309
+ - env: AVIATIONSTACK_API_KEY
310
+ type: api_key
311
+ service: AviationStack
312
+ setup_url: https://aviationstack.com/signup/free
313
+ setup_time: "3 minutes"
314
+ notes: "Free tier available with limited requests."
315
+
316
+ claude-gateway:
317
+ level: 3
318
+ requires:
319
+ - env: DISCORD_BOT_TOKEN
320
+ type: api_key
321
+ service: Discord
322
+ setup_url: https://discord.com/developers/applications
323
+ setup_time: "10 minutes"
324
+ - env: DISCORD_CHANNEL_ID
325
+ type: config
326
+ service: Discord
327
+ setup_time: "included above"
328
+ - env: DISCORD_GUILD_ID
329
+ type: config
330
+ service: Discord
331
+ setup_time: "included above"
332
+ notes: "Three env vars required. Create Discord bot application and invite to server."
333
+
334
+ voice-agent-v2:
335
+ level: 3
336
+ requires:
337
+ - env: LIVEKIT_API_KEY
338
+ type: api_key
339
+ service: LiveKit
340
+ setup_url: https://cloud.livekit.io
341
+ setup_time: "5 minutes"
342
+ - env: LIVEKIT_API_SECRET
343
+ type: api_key
344
+ service: LiveKit
345
+ setup_time: "included above"
346
+ - env: LIVEKIT_URL
347
+ type: config
348
+ service: LiveKit
349
+ setup_time: "included above"
350
+ notes: "Three env vars required. LiveKit Cloud or self-hosted."
351
+
352
+ # ============================================================================
353
+ # LEVEL 4: Applications
354
+ # Requires desktop software installed on the user's machine.
355
+ # ============================================================================
356
+
357
+ vault-manager:
358
+ level: 4
359
+ requires:
360
+ - env: OBSIDIAN_INSTALLED
361
+ type: application
362
+ service: Obsidian
363
+ setup_url: https://obsidian.md
364
+ setup_time: "30 minutes (install + vault setup)"
365
+ notes: "Free app. Requires an Obsidian vault configured with PARA structure."
366
+
367
+ life-os:
368
+ level: 4
369
+ requires:
370
+ - env: OBSIDIAN_INSTALLED
371
+ type: application
372
+ service: Obsidian
373
+ setup_url: https://obsidian.md
374
+ setup_time: "30 minutes (install + vault setup)"
375
+ notes: "Requires vault-manager configured first. Life Operating System orchestrator."
@@ -0,0 +1,232 @@
1
+ #!/usr/bin/env python3
2
+ """pushREC Skills diagnostic: checks system readiness for Claude Code skills.
3
+
4
+ Standalone Python 3.8+, zero external dependencies.
5
+ Output: JSON to stdout (parseable by Claude Code).
6
+ Exit codes: 0 = all good, 1 = missing critical prereqs, 2 = warnings only.
7
+ """
8
+
9
+ import json
10
+ import os
11
+ import platform
12
+ import subprocess
13
+ import sys
14
+ from datetime import datetime, timezone
15
+ from pathlib import Path
16
+
17
+ VERSION = "1.0.0"
18
+ SKILLS_DIR = Path.home() / ".claude" / "skills"
19
+ CONFIG_PATH = Path.home() / ".pushrec" / "config.json"
20
+ ENV_PATH = Path.home() / ".claude" / ".env"
21
+
22
+ KNOWN_KEY_PREFIXES = [
23
+ "ELEVENLABS_API_KEY",
24
+ "OPENAI_API_KEY",
25
+ "ANTHROPIC_API_KEY",
26
+ "FAL_KEY",
27
+ "GOOGLE_API_KEY",
28
+ "GOOGLE_MAPS_API_KEY",
29
+ "YOUTUBE_API_KEY",
30
+ "RAPIDAPI_KEY",
31
+ "TWILIO_ACCOUNT_SID",
32
+ "TWILIO_AUTH_TOKEN",
33
+ "RETELL_API_KEY",
34
+ "API_TOKEN",
35
+ "AIRTABLE_API_KEY",
36
+ "VERCEL_TOKEN",
37
+ "OPENWEATHERMAP_API_KEY",
38
+ "AVIATIONSTACK_API_KEY",
39
+ "DISCORD_BOT_TOKEN",
40
+ "MISTRAL_API_KEY",
41
+ ]
42
+
43
+
44
+ def run_command(args):
45
+ """Run a shell command and return (success, stdout_stripped)."""
46
+ try:
47
+ result = subprocess.run(
48
+ args,
49
+ capture_output=True,
50
+ text=True,
51
+ timeout=10,
52
+ )
53
+ return result.returncode == 0, result.stdout.strip()
54
+ except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
55
+ return False, ""
56
+
57
+
58
+ def parse_version(version_string):
59
+ """Extract numeric version tuple from a version string like 'v22.1.0' or '2.1.45'."""
60
+ cleaned = version_string.lstrip("v").strip()
61
+ parts = []
62
+ for segment in cleaned.split("."):
63
+ digits = ""
64
+ for char in segment:
65
+ if char.isdigit():
66
+ digits += char
67
+ else:
68
+ break
69
+ if digits:
70
+ parts.append(int(digits))
71
+ return tuple(parts) if parts else (0,)
72
+
73
+
74
+ def version_gte(current, minimum):
75
+ """Check if current version >= minimum version."""
76
+ current_tuple = parse_version(current)
77
+ minimum_tuple = parse_version(minimum)
78
+ return current_tuple >= minimum_tuple
79
+
80
+
81
+ def check_claude_code():
82
+ """Check Claude Code installation and version."""
83
+ success, output = run_command(["claude", "--version"])
84
+ if not success:
85
+ return {"status": "fail", "version": None, "minimum": "2.1.0"}
86
+ version = output.strip()
87
+ if version_gte(version, "2.1.0"):
88
+ return {"status": "pass", "version": version, "minimum": "2.1.0"}
89
+ return {"status": "fail", "version": version, "minimum": "2.1.0"}
90
+
91
+
92
+ def check_node():
93
+ """Check Node.js installation and version."""
94
+ success, output = run_command(["node", "--version"])
95
+ if not success:
96
+ return {"status": "fail", "version": None, "minimum": "20.0.0"}
97
+ version = output.strip()
98
+ if version_gte(version, "20.0.0"):
99
+ return {"status": "pass", "version": version, "minimum": "20.0.0"}
100
+ return {"status": "fail", "version": version, "minimum": "20.0.0"}
101
+
102
+
103
+ def check_npm():
104
+ """Check npm installation."""
105
+ success, output = run_command(["npm", "--version"])
106
+ if not success:
107
+ return {"status": "fail", "version": None}
108
+ return {"status": "pass", "version": output.strip()}
109
+
110
+
111
+ def check_skills_installed():
112
+ """Count installed skills by looking for SKILL.md files."""
113
+ if not SKILLS_DIR.is_dir():
114
+ return {"status": "fail", "count": 0}
115
+ count = 0
116
+ for child in SKILLS_DIR.iterdir():
117
+ if child.is_dir() and (child / "SKILL.md").is_file():
118
+ count += 1
119
+ if count == 0:
120
+ return {"status": "fail", "count": 0}
121
+ return {"status": "pass", "count": count}
122
+
123
+
124
+ def check_license():
125
+ """Check pushREC license status via config.json."""
126
+ if not CONFIG_PATH.is_file():
127
+ return {"status": "fail", "installed_skills": 0}
128
+ try:
129
+ with open(CONFIG_PATH, "r") as f:
130
+ config = json.load(f)
131
+ installed = config.get("installedSkills", {})
132
+ count = len(installed) if isinstance(installed, dict) else 0
133
+ if count > 0:
134
+ return {"status": "pass", "installed_skills": count}
135
+ return {"status": "fail", "installed_skills": 0}
136
+ except (json.JSONDecodeError, OSError):
137
+ return {"status": "fail", "installed_skills": 0}
138
+
139
+
140
+ def check_api_keys():
141
+ """Detect configured API keys from ~/.claude/.env."""
142
+ detected = []
143
+ if not ENV_PATH.is_file():
144
+ return {"status": "info", "detected": 0, "keys": []}
145
+ try:
146
+ with open(ENV_PATH, "r") as f:
147
+ for line in f:
148
+ line = line.strip()
149
+ if not line or line.startswith("#"):
150
+ continue
151
+ for prefix in KNOWN_KEY_PREFIXES:
152
+ if line.startswith(prefix + "="):
153
+ value = line.split("=", 1)[1].strip().strip('"').strip("'")
154
+ if value:
155
+ detected.append(prefix)
156
+ break
157
+ except OSError:
158
+ pass
159
+ return {"status": "info", "detected": len(detected), "keys": detected}
160
+
161
+
162
+ def check_obsidian():
163
+ """Check if Obsidian is installed (platform-specific)."""
164
+ system = platform.system()
165
+ if system == "Darwin":
166
+ installed = Path("/Applications/Obsidian.app").exists()
167
+ elif system == "Linux":
168
+ success, _ = run_command(["which", "obsidian"])
169
+ installed = success
170
+ elif system == "Windows":
171
+ success, _ = run_command(["where", "obsidian"])
172
+ installed = success
173
+ else:
174
+ installed = False
175
+ return {"status": "info", "installed": installed}
176
+
177
+
178
+ def main():
179
+ if os.environ.get("CLAUDE_HEADLESS"):
180
+ sys.exit(0)
181
+
182
+ checks = {
183
+ "claude_code": check_claude_code(),
184
+ "node": check_node(),
185
+ "npm": check_npm(),
186
+ "skills_installed": check_skills_installed(),
187
+ "license": check_license(),
188
+ "api_keys": check_api_keys(),
189
+ "obsidian": check_obsidian(),
190
+ }
191
+
192
+ critical_failures = []
193
+ warnings = []
194
+
195
+ if checks["claude_code"]["status"] == "fail":
196
+ critical_failures.append("Claude Code")
197
+ if checks["node"]["status"] == "fail":
198
+ critical_failures.append("Node.js v20+")
199
+ if checks["npm"]["status"] == "fail":
200
+ critical_failures.append("npm")
201
+ if checks["skills_installed"]["status"] == "fail":
202
+ warnings.append("No skills installed")
203
+ if checks["license"]["status"] == "fail":
204
+ warnings.append("No active license")
205
+
206
+ if critical_failures:
207
+ exit_code = 1
208
+ summary = f"Missing critical prerequisites: {', '.join(critical_failures)}."
209
+ elif warnings:
210
+ exit_code = 2
211
+ summary = f"Warnings: {', '.join(warnings)}."
212
+ else:
213
+ skills_count = checks["skills_installed"].get("count", 0)
214
+ api_count = checks["api_keys"].get("detected", 0)
215
+ summary = f"All systems ready. {skills_count} skills installed, {api_count} API keys detected."
216
+ exit_code = 0
217
+
218
+ report = {
219
+ "version": VERSION,
220
+ "timestamp": datetime.now(timezone.utc).isoformat(),
221
+ "platform": platform.system().lower(),
222
+ "checks": checks,
223
+ "exit_code": exit_code,
224
+ "summary": summary,
225
+ }
226
+
227
+ print(json.dumps(report, indent=2))
228
+ sys.exit(exit_code)
229
+
230
+
231
+ if __name__ == "__main__":
232
+ main()
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/index.ts
27
- var import_commander10 = require("commander");
27
+ var import_commander11 = require("commander");
28
28
 
29
29
  // src/commands/auth.ts
30
30
  var import_commander = require("commander");
@@ -949,6 +949,16 @@ function uninstallSkill(name) {
949
949
  function isSkillInstalled(name) {
950
950
  return (0, import_fs5.existsSync)((0, import_path5.join)(SKILLS_DIR, name, "SKILL.md"));
951
951
  }
952
+ function getBundledSkillPath(name) {
953
+ return (0, import_path5.join)(__dirname, "..", "bundled-skills", name);
954
+ }
955
+ function copyBundledSkill(name) {
956
+ const sourcePath = getBundledSkillPath(name);
957
+ if (!(0, import_fs5.existsSync)(sourcePath)) return;
958
+ const targetPath = (0, import_path5.join)(SKILLS_DIR, name);
959
+ if ((0, import_fs5.existsSync)((0, import_path5.join)(targetPath, "SKILL.md"))) return;
960
+ (0, import_fs5.cpSync)(sourcePath, targetPath, { recursive: true });
961
+ }
952
962
 
953
963
  // src/commands/install.ts
954
964
  async function ensureLicense(forFreeSkill) {
@@ -1768,6 +1778,10 @@ function setupCommand() {
1768
1778
  failed++;
1769
1779
  }
1770
1780
  }
1781
+ try {
1782
+ copyBundledSkill("pushrec-skills");
1783
+ } catch {
1784
+ }
1771
1785
  const skipped = catalog.items.length - toInstall.length;
1772
1786
  if (opts.json) {
1773
1787
  console.log(JSON.stringify({
@@ -1780,23 +1794,257 @@ function setupCommand() {
1780
1794
  }));
1781
1795
  } else {
1782
1796
  console.log("");
1783
- console.log(import_chalk4.default.green.bold("Setup complete!"));
1784
- console.log(` ${import_chalk4.default.green("+")} ${installed} skills installed`);
1785
- if (skipped > 0) console.log(` ${import_chalk4.default.dim("-")} ${skipped} already installed`);
1786
- if (failed > 0) console.log(` ${import_chalk4.default.red("x")} ${failed} failed`);
1797
+ console.log(import_chalk4.default.green(" Setup complete!"));
1798
+ console.log(` ${import_chalk4.default.green("+")} ${installed} skills installed`);
1799
+ if (skipped > 0) console.log(` ${import_chalk4.default.dim("-")} ${skipped} already installed`);
1800
+ if (failed > 0) console.log(` ${import_chalk4.default.red("x")} ${failed} failed`);
1787
1801
  console.log("");
1788
- console.log("Your skills are ready. Use " + import_chalk4.default.cyan("/<skill-name>") + " in your next Claude Code session.");
1789
- console.log("Run " + import_chalk4.default.cyan("pushrec-skills health --full") + " to verify everything works.");
1802
+ console.log(` ${import_chalk4.default.bold("NEXT STEP:")} Open Claude Code and type ${import_chalk4.default.cyan("/pushrec-skills")}`);
1803
+ console.log("");
1804
+ console.log(" Quick reference:");
1805
+ console.log(` ${import_chalk4.default.cyan("/pushrec-skills")} \u2014 Your guided setup (start here)`);
1806
+ console.log(` ${import_chalk4.default.cyan("/hormozi")} \u2014 Business strategy frameworks`);
1807
+ console.log(` ${import_chalk4.default.cyan("/linkedin-copywriting")} \u2014 LinkedIn content creation`);
1808
+ console.log(` ${import_chalk4.default.cyan("/copywriting")} \u2014 Master copywriting foundations`);
1809
+ console.log("");
1810
+ console.log(` Community: ${import_chalk4.default.underline("skool.com/pushrec-2909")}`);
1811
+ console.log(` Full catalog: ${import_chalk4.default.dim("npx @pushrec/skills list")}`);
1812
+ }
1813
+ });
1814
+ return cmd;
1815
+ }
1816
+
1817
+ // src/commands/dashboard.ts
1818
+ var import_commander10 = require("commander");
1819
+ var import_http = require("http");
1820
+ var import_fs6 = require("fs");
1821
+ var import_path6 = require("path");
1822
+ var import_os5 = require("os");
1823
+ var import_child_process4 = require("child_process");
1824
+ var DEFAULT_PORT = 5174;
1825
+ var DATA_SOURCES = {
1826
+ "/api/config": "~/.pushrec/config.json",
1827
+ "/api/profile": "~/.claude/skills/pushrec-skills/state/profile.json",
1828
+ "/api/progress": "~/.claude/skills/pushrec-skills/state/progress.json",
1829
+ "/api/catalog": "~/.claude/skills/pushrec-skills/state/catalog.json",
1830
+ "/api/diagnostic": "~/.claude/skills/pushrec-skills/state/diagnostic.json",
1831
+ "/api/teams": "~/.claude/skills/pushrec-skills/state/teams.json",
1832
+ "/api/tasks": "~/.claude/skills/pushrec-skills/state/tasks.json"
1833
+ };
1834
+ var YAML_SOURCES = {
1835
+ "/api/prerequisites": "~/.claude/skills/pushrec-skills/references/prerequisites.yaml"
1836
+ };
1837
+ function expandPath(filepath) {
1838
+ if (filepath.startsWith("~/")) {
1839
+ return (0, import_path6.join)((0, import_os5.homedir)(), filepath.slice(2));
1840
+ }
1841
+ return filepath;
1842
+ }
1843
+ function serveJson(res, data) {
1844
+ res.setHeader("Content-Type", "application/json");
1845
+ res.setHeader("Cache-Control", "no-cache");
1846
+ res.setHeader("Access-Control-Allow-Origin", "*");
1847
+ res.end(data);
1848
+ }
1849
+ function serveError(res, status, message) {
1850
+ res.statusCode = status;
1851
+ res.setHeader("Content-Type", "application/json");
1852
+ res.end(JSON.stringify({ error: message }));
1853
+ }
1854
+ function parseYaml(raw) {
1855
+ try {
1856
+ return JSON.parse(raw);
1857
+ } catch {
1858
+ return { raw };
1859
+ }
1860
+ }
1861
+ function handleApiRequest(req, res) {
1862
+ const url = req.url ?? "";
1863
+ if (url === "/api/mtime") {
1864
+ const mtimes = {};
1865
+ for (const [endpoint, filepath] of Object.entries(DATA_SOURCES)) {
1866
+ try {
1867
+ mtimes[endpoint] = (0, import_fs6.statSync)(expandPath(filepath)).mtimeMs;
1868
+ } catch {
1869
+ mtimes[endpoint] = 0;
1870
+ }
1871
+ }
1872
+ for (const [endpoint, filepath] of Object.entries(YAML_SOURCES)) {
1873
+ try {
1874
+ mtimes[endpoint] = (0, import_fs6.statSync)(expandPath(filepath)).mtimeMs;
1875
+ } catch {
1876
+ mtimes[endpoint] = 0;
1877
+ }
1878
+ }
1879
+ serveJson(res, JSON.stringify(mtimes));
1880
+ return true;
1881
+ }
1882
+ if (url in DATA_SOURCES) {
1883
+ const filepath = DATA_SOURCES[url];
1884
+ const resolved = expandPath(filepath);
1885
+ try {
1886
+ const data = (0, import_fs6.readFileSync)(resolved, "utf-8");
1887
+ serveJson(res, data);
1888
+ } catch {
1889
+ serveError(res, 500, `Failed to read ${filepath}`);
1890
+ }
1891
+ return true;
1892
+ }
1893
+ if (url in YAML_SOURCES) {
1894
+ const filepath = YAML_SOURCES[url];
1895
+ const resolved = expandPath(filepath);
1896
+ try {
1897
+ const raw = (0, import_fs6.readFileSync)(resolved, "utf-8");
1898
+ const data = parseYaml(raw);
1899
+ serveJson(res, JSON.stringify(data));
1900
+ } catch {
1901
+ serveError(res, 500, `Failed to read ${filepath}`);
1790
1902
  }
1903
+ return true;
1904
+ }
1905
+ return false;
1906
+ }
1907
+ function findDistDirectory() {
1908
+ const candidates = [
1909
+ (0, import_path6.join)((0, import_os5.homedir)(), ".claude", "skills", "generative-ui", "dist"),
1910
+ (0, import_path6.join)((0, import_os5.homedir)(), ".cache", "genui-dashboard", "dist")
1911
+ ];
1912
+ for (const candidate of candidates) {
1913
+ if ((0, import_fs6.existsSync)((0, import_path6.join)(candidate, "index.html"))) {
1914
+ return candidate;
1915
+ }
1916
+ }
1917
+ return null;
1918
+ }
1919
+ function serveStatic(distDir, req, res) {
1920
+ let urlPath = (req.url ?? "/").split("?")[0];
1921
+ if (urlPath === "/") urlPath = "/index.html";
1922
+ const filePath = (0, import_path6.join)(distDir, urlPath);
1923
+ if (!filePath.startsWith(distDir)) {
1924
+ serveError(res, 403, "Forbidden");
1925
+ return;
1926
+ }
1927
+ try {
1928
+ const content = (0, import_fs6.readFileSync)(filePath);
1929
+ const ext = filePath.split(".").pop() ?? "";
1930
+ const mimeTypes = {
1931
+ html: "text/html",
1932
+ js: "application/javascript",
1933
+ css: "text/css",
1934
+ json: "application/json",
1935
+ png: "image/png",
1936
+ svg: "image/svg+xml",
1937
+ ico: "image/x-icon",
1938
+ woff2: "font/woff2",
1939
+ woff: "font/woff"
1940
+ };
1941
+ res.setHeader("Content-Type", mimeTypes[ext] ?? "application/octet-stream");
1942
+ res.end(content);
1943
+ } catch {
1944
+ try {
1945
+ const indexContent = (0, import_fs6.readFileSync)((0, import_path6.join)(distDir, "index.html"));
1946
+ res.setHeader("Content-Type", "text/html");
1947
+ res.end(indexContent);
1948
+ } catch {
1949
+ serveError(res, 404, "Not found");
1950
+ }
1951
+ }
1952
+ }
1953
+ function openBrowser(url) {
1954
+ const platform4 = process.platform;
1955
+ const command = platform4 === "darwin" ? `open "${url}"` : platform4 === "win32" ? `start "${url}"` : `xdg-open "${url}"`;
1956
+ (0, import_child_process4.exec)(command, () => {
1791
1957
  });
1958
+ }
1959
+ function dashboardCommand() {
1960
+ const cmd = new import_commander10.Command("dashboard").description("Launch the Generative UI dashboard").option("--port <port>", "Server port", String(DEFAULT_PORT)).option("--dev", "Start in dev mode with Vite HMR").option("--no-open", "Skip opening browser").action(
1961
+ async (opts) => {
1962
+ const port = parseInt(opts.port, 10);
1963
+ if (isNaN(port) || port < 1024 || port > 65535) {
1964
+ console.error(
1965
+ `ERROR: Port must be between 1024 and 65535, got "${opts.port}".`
1966
+ );
1967
+ process.exit(1);
1968
+ }
1969
+ if (opts.dev) {
1970
+ console.log("Dev mode is not yet supported via CLI.");
1971
+ console.log(
1972
+ "Use the Vite dev server directly: cd dashboard && npm run dev"
1973
+ );
1974
+ process.exit(0);
1975
+ }
1976
+ const distDir = findDistDirectory();
1977
+ if (!distDir) {
1978
+ console.error("ERROR: No built dashboard found.");
1979
+ console.error(
1980
+ " Build the dashboard first, or use --dev mode."
1981
+ );
1982
+ console.error(
1983
+ " Expected: ~/.claude/skills/generative-ui/dist/index.html"
1984
+ );
1985
+ process.exit(1);
1986
+ }
1987
+ const server = (0, import_http.createServer)((req, res) => {
1988
+ if (req.url?.startsWith("/api/")) {
1989
+ const handled = handleApiRequest(req, res);
1990
+ if (handled) return;
1991
+ }
1992
+ serveStatic(distDir, req, res);
1993
+ });
1994
+ server.listen(port, () => {
1995
+ const url = `http://localhost:${port}`;
1996
+ console.log(`Dashboard running at ${url}`);
1997
+ console.log(` Serving: ${distDir}`);
1998
+ console.log(` Press Ctrl+C to stop`);
1999
+ console.log();
2000
+ if (opts.open !== false) {
2001
+ openBrowser(url);
2002
+ }
2003
+ });
2004
+ server.on("error", (err) => {
2005
+ if (err.code === "EADDRINUSE") {
2006
+ console.error(`ERROR: Port ${port} is already in use.`);
2007
+ console.error(` Run: lsof -i :${port} -t | xargs kill`);
2008
+ } else {
2009
+ console.error(`ERROR: ${err.message}`);
2010
+ }
2011
+ process.exit(1);
2012
+ });
2013
+ const shutdown = () => {
2014
+ console.log("\nShutting down dashboard...");
2015
+ server.close(() => {
2016
+ process.exit(0);
2017
+ });
2018
+ setTimeout(() => process.exit(0), 3e3);
2019
+ };
2020
+ process.on("SIGINT", shutdown);
2021
+ process.on("SIGTERM", shutdown);
2022
+ }
2023
+ );
1792
2024
  return cmd;
1793
2025
  }
1794
2026
 
2027
+ // src/version.ts
2028
+ var import_node_fs = require("fs");
2029
+ var import_node_path = require("path");
2030
+ function getCliVersion() {
2031
+ try {
2032
+ const packageJsonPath = (0, import_node_path.join)(__dirname, "..", "package.json");
2033
+ const raw = (0, import_node_fs.readFileSync)(packageJsonPath, "utf8");
2034
+ const parsed = JSON.parse(raw);
2035
+ if (typeof parsed.version === "string" && parsed.version.length > 0) {
2036
+ return parsed.version;
2037
+ }
2038
+ } catch {
2039
+ }
2040
+ return "0.0.0";
2041
+ }
2042
+
1795
2043
  // src/index.ts
1796
- var program = new import_commander10.Command();
2044
+ var program = new import_commander11.Command();
1797
2045
  program.name("pushrec-skills").description(
1798
2046
  "Install, update, and manage premium Claude Code skills from Pushrec"
1799
- ).version("0.2.0");
2047
+ ).version(getCliVersion());
1800
2048
  program.addCommand(authCommand());
1801
2049
  program.addCommand(installCommand());
1802
2050
  program.addCommand(updateCommand());
@@ -1806,6 +2054,7 @@ program.addCommand(infoCommand());
1806
2054
  program.addCommand(createUninstallCommand());
1807
2055
  program.addCommand(healthCommand());
1808
2056
  program.addCommand(setupCommand());
2057
+ program.addCommand(dashboardCommand());
1809
2058
  program.parseAsync().catch((err) => {
1810
2059
  console.error(err instanceof Error ? err.message : String(err));
1811
2060
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushrec/skills",
3
- "version": "0.2.0",
3
+ "version": "0.3.2",
4
4
  "description": "Install, update, and manage premium Claude Code skills from Pushrec",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {
@@ -11,6 +11,7 @@
11
11
  },
12
12
  "files": [
13
13
  "dist",
14
+ "bundled-skills",
14
15
  "README.md"
15
16
  ],
16
17
  "scripts": {