@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
|
|
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
|
|
1784
|
-
console.log(`
|
|
1785
|
-
if (skipped > 0) console.log(`
|
|
1786
|
-
if (failed > 0) console.log(`
|
|
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("
|
|
1789
|
-
console.log("
|
|
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
|
|
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(
|
|
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
|
|
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": {
|