rsc-universal 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -85
- package/manifest.json +1 -1
- package/package.json +2 -1
- package/scripts/lib/domains.js +30 -0
- package/scripts/lib/recommend.js +15 -15
- package/scripts/lib/ui.js +128 -2
- package/scripts/rsc.js +102 -24
package/README.md
CHANGED
|
@@ -32,9 +32,9 @@ one is the opposite bet:
|
|
|
32
32
|
invoicing, hiring, GDPR, pitch decks, SEO, a YouTube/TikTok/LinkedIn presence —
|
|
33
33
|
each wired to a `02-DOCS/` knowledge loop that learns from your own results.
|
|
34
34
|
- **Honestly good.** Every skill was built by a research → spec → implement →
|
|
35
|
-
*adversarial review* pipeline and had to clear an objective rubric
|
|
36
|
-
|
|
37
|
-
were sent back and fixed, not waved through.
|
|
35
|
+
*adversarial review* pipeline and had to clear an objective rubric
|
|
36
|
+
(`scripts/skill-rubric.md`, written *before* any skill existed). The bar was
|
|
37
|
+
real: skills that scored 8.0 were sent back and fixed, not waved through.
|
|
38
38
|
|
|
39
39
|
`skills/<name>/` is the single source of truth. There are no bundles to argue
|
|
40
40
|
over: you start with a tiny floor and grow one piece at a time.
|
|
@@ -43,25 +43,19 @@ over: you start with a tiny floor and grow one piece at a time.
|
|
|
43
43
|
|
|
44
44
|
## Install
|
|
45
45
|
|
|
46
|
-
The catalog ships as the `rsc-universal` CLI. Until it's on npm, install from
|
|
47
|
-
source (one minute, once):
|
|
48
|
-
|
|
49
46
|
```bash
|
|
50
|
-
|
|
51
|
-
cd ~/rsc-skills && npm install && npm link # puts `rsc` on your PATH
|
|
47
|
+
npx rsc # no install step — runs the latest published catalog
|
|
52
48
|
```
|
|
53
49
|
|
|
54
|
-
|
|
50
|
+
Run it inside any project and describe what you want. Working on the catalog
|
|
51
|
+
itself? Clone and link:
|
|
55
52
|
|
|
56
53
|
```bash
|
|
57
|
-
|
|
58
|
-
rsc
|
|
54
|
+
git clone https://github.com/ericrisco/skills.git ~/rsc-skills
|
|
55
|
+
cd ~/rsc-skills && npm install && npm link
|
|
59
56
|
```
|
|
60
57
|
|
|
61
|
-
|
|
62
|
-
> directly: `node ~/rsc-skills/scripts/rsc.js <args>`.
|
|
63
|
-
|
|
64
|
-
The first run installs the **floor** — `rsc-suggest` (always-on detector) +
|
|
58
|
+
The first run installs the **floor** — `orient` + `rsc-suggest` (always-on) +
|
|
65
59
|
`harness` + `init` — and, in Claude Code, wires a `SessionStart` hook so your
|
|
66
60
|
assistant proposes new skills on its own from then on.
|
|
67
61
|
|
|
@@ -71,23 +65,29 @@ assistant proposes new skills on its own from then on.
|
|
|
71
65
|
|
|
72
66
|
```
|
|
73
67
|
$ rsc
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
• Llevar las cuentas → bookkeeping, finance-ops
|
|
82
|
-
• Publicarlo online → vercel
|
|
83
|
-
¿Lo monto? (sí / no) > sí
|
|
84
|
-
|
|
85
|
-
✅ Listo. Abre tu editor y empieza a pedir cosas en tu idioma.
|
|
86
|
-
💡 Cuando una tarea necesite algo más, te lo propongo yo.
|
|
68
|
+
👋 rsc — the skill catalog for your assistant.
|
|
69
|
+
|
|
70
|
+
What do you want to do? ↑↓ move · enter select
|
|
71
|
+
❯ Base install — the essentials (orient + suggest + harness + init)
|
|
72
|
+
Base + Spec-Driven Development — specify → plan → implement → ship
|
|
73
|
+
Pick skills by hand, by area
|
|
74
|
+
Describe my project and let rsc choose
|
|
87
75
|
```
|
|
88
76
|
|
|
89
|
-
|
|
90
|
-
|
|
77
|
+
Pick **by area** and you get a checkbox list — **↑↓ to move, space to toggle,
|
|
78
|
+
enter to confirm**:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
Languages: ↑↓ move · space toggle · a all · enter confirm
|
|
82
|
+
❯ ◉ typescript
|
|
83
|
+
◯ python
|
|
84
|
+
◉ go
|
|
85
|
+
◯ rust
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
It auto-detects your IDE and stack, installs only what you chose, then prints the
|
|
89
|
+
exact next steps for **Claude Code / Cursor / Codex / Gemini** — and from there
|
|
90
|
+
keeps proposing the skills a task needs.
|
|
91
91
|
|
|
92
92
|
---
|
|
93
93
|
|
|
@@ -97,11 +97,11 @@ matching skills, then suggests what usually comes next.
|
|
|
97
97
|
rsc # plain-language wizard (recommended)
|
|
98
98
|
rsc add fastapi postgresdb # install specific skills, by name
|
|
99
99
|
rsc add youtube-api remotion-video # …grow a channel, edit with Remotion
|
|
100
|
-
rsc install --profile minimal # the floor: suggest + harness + init
|
|
100
|
+
rsc install --profile minimal # the floor: orient + suggest + harness + init
|
|
101
101
|
rsc install --profile core # floor + the full SDD workflow
|
|
102
102
|
rsc install --profile full # everything (all 231)
|
|
103
103
|
rsc install --profile full --without go
|
|
104
|
-
rsc consult "
|
|
104
|
+
rsc consult "I want to launch a SaaS" # recommend only, no install
|
|
105
105
|
rsc registry refresh # write .rsc/skill-registry.{json,md}
|
|
106
106
|
rsc list # what rsc has installed
|
|
107
107
|
rsc doctor # health check (state, hook, counts)
|
|
@@ -128,94 +128,94 @@ just asks in plain language.
|
|
|
128
128
|
|
|
129
129
|
---
|
|
130
130
|
|
|
131
|
-
## The quality bar
|
|
132
|
-
|
|
133
|
-
This catalog was built to a test that was **written before any skill existed**
|
|
134
|
-
(`scripts/skill-rubric.md`). Each finished skill was scored 0-10 by an
|
|
135
|
-
independent adversarial reviewer across seven weighted dimensions — with
|
|
136
|
-
**freshness & grounding the heaviest (0.25)**: every load-bearing claim had to be
|
|
137
|
-
*current* (2025-2026 versions/APIs) and cited to a dated source. The ship gate
|
|
138
|
-
was **≥ 8.5**; anything below was sent back through a fix loop, not rounded up.
|
|
139
|
-
|
|
140
|
-
| | |
|
|
141
|
-
|---|---|
|
|
142
|
-
| **Skills** | 231 (30 core + 201 added) |
|
|
143
|
-
| **Domains** | 21 |
|
|
144
|
-
| **Median family score** | **~9.1 / 10** |
|
|
145
|
-
| **Below the 8.5 gate** | 0 (three were flagged, then remediated to 9.0-9.3) |
|
|
146
|
-
| **Deterministic gates** | eval-lint ✓ · frontmatter+`recommends` validate ✓ · 50/50 unit tests ✓ · every description ≤ 1024 chars ✓ |
|
|
147
|
-
|
|
148
|
-
Each skill is **hybrid**: a focused `SKILL.md` (120-400 lines), deep-dive
|
|
149
|
-
`references/`, an `evals/cases.yaml` (≥5 trigger / ≥4 near-miss / ≥1 capability
|
|
150
|
-
scenario), and — for anything with a checkable artifact — an executable
|
|
151
|
-
`scripts/verify.sh`.
|
|
152
|
-
|
|
153
|
-
---
|
|
154
|
-
|
|
155
131
|
## The catalog
|
|
156
132
|
|
|
157
|
-
231 skills, grouped by what you're trying to do.
|
|
158
|
-
|
|
133
|
+
231 skills, grouped by what you're trying to do. Click any skill to read its
|
|
134
|
+
`SKILL.md`. It fires on its own when a task matches.
|
|
159
135
|
|
|
160
136
|
### 🧭 Core & control plane
|
|
161
137
|
The front door and the workspace brain.
|
|
162
|
-
|
|
138
|
+
|
|
139
|
+
[init](skills/init/) · [harness](skills/harness/) · [orient](skills/orient/) · [suggest](skills/suggest/) · [author-skill](skills/author-skill/) · [sdd-init](skills/sdd-init/)
|
|
163
140
|
|
|
164
141
|
> **harness** is the Karpathy *chaos→knowledge* engine — a `01-TOOLS/` layer (one
|
|
165
142
|
> folder per provider, each with a working `test_connection`) and a `02-DOCS/`
|
|
166
|
-
> self-improving wiki. It governs software *or* a whole company.
|
|
143
|
+
> self-improving wiki. It governs software *or* a whole company. **orient** is the
|
|
144
|
+
> always-on compass that keeps a non-technical human oriented after every step.
|
|
167
145
|
|
|
168
146
|
### 📐 Spec-Driven Development
|
|
169
|
-
Take a fuzzy intent to a shipped, verified change — phase by phase. `rsc install --profile core`.
|
|
170
|
-
|
|
147
|
+
Take a fuzzy intent to a shipped, verified change — phase by phase. `npx rsc install --profile core`.
|
|
148
|
+
|
|
149
|
+
[sdd](skills/sdd/) · [constitution](skills/constitution/) · [specify](skills/specify/) · [clarify](skills/clarify/) · [plan](skills/plan/) · [tasks](skills/tasks/) · [analyze](skills/analyze/) · [implement](skills/implement/) · [verify](skills/verify/) · [review](skills/review/) · [ship](skills/ship/) · [debug](skills/debug/) · [worktrees](skills/worktrees/) · [parallel](skills/parallel/)
|
|
171
150
|
|
|
172
151
|
### 💼 Run a business
|
|
173
|
-
|
|
152
|
+
|
|
153
|
+
[finance-ops](skills/finance-ops/) · [invoicing](skills/invoicing/) · [bookkeeping](skills/bookkeeping/) · [pricing](skills/pricing/) · [sales-pipeline](skills/sales-pipeline/) · [lead-gen](skills/lead-gen/) · [cold-outreach](skills/cold-outreach/) · [proposals](skills/proposals/) · [contracts](skills/contracts/) · [customer-support](skills/customer-support/) · [client-onboarding](skills/client-onboarding/) · [retention](skills/retention/) · [hiring](skills/hiring/) · [people-ops](skills/people-ops/) · [inventory](skills/inventory/) · [logistics-ops](skills/logistics-ops/) · [procurement](skills/procurement/) · [meeting-notes](skills/meeting-notes/) · [sop-builder](skills/sop-builder/) · [project-ops](skills/project-ops/)
|
|
174
154
|
|
|
175
155
|
### 💸 Raise & model money
|
|
176
|
-
|
|
156
|
+
|
|
157
|
+
[pitch-deck](skills/pitch-deck/) · [investor-materials](skills/investor-materials/) · [financial-model](skills/financial-model/) · [fundraising](skills/fundraising/) · [unit-economics](skills/unit-economics/) · [grants](skills/grants/)
|
|
177
158
|
|
|
178
159
|
### ⚖️ Legal, privacy & compliance
|
|
179
|
-
|
|
160
|
+
|
|
161
|
+
[gdpr-privacy](skills/gdpr-privacy/) · [terms-conditions](skills/terms-conditions/) · [compliance](skills/compliance/) · [data-policy](skills/data-policy/) · [ip-trademark](skills/ip-trademark/)
|
|
180
162
|
|
|
181
163
|
### 📣 Market & brand
|
|
182
|
-
`seo-geo` · `content-engine` · `social-publisher` · `brand-voice` · `brand-identity` · `newsletter` · `landing-copy` · `ads` · `article-writing` · `case-studies` · `video-shorts` · `podcast` · `market-research` · `competitor-watch` · `press-kit` · `community` · `webinar` · `review-management` · `marketing`
|
|
183
164
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
165
|
+
[marketing](skills/marketing/) · [seo-geo](skills/seo-geo/) · [content-engine](skills/content-engine/) · [social-publisher](skills/social-publisher/) · [brand-voice](skills/brand-voice/) · [brand-identity](skills/brand-identity/) · [newsletter](skills/newsletter/) · [landing-copy](skills/landing-copy/) · [ads](skills/ads/) · [article-writing](skills/article-writing/) · [case-studies](skills/case-studies/) · [video-shorts](skills/video-shorts/) · [podcast](skills/podcast/) · [market-research](skills/market-research/) · [competitor-watch](skills/competitor-watch/) · [press-kit](skills/press-kit/) · [community](skills/community/) · [webinar](skills/webinar/) · [review-management](skills/review-management/)
|
|
166
|
+
|
|
167
|
+
### 🎬 Grow a channel
|
|
168
|
+
Each with a `02-DOCS` feedback loop that learns from your own results. `remotion-video` edits programmatically — transitions, Whisper captions, silence removal.
|
|
169
|
+
|
|
170
|
+
[youtube-api](skills/youtube-api/) · [youtube-strategy](skills/youtube-strategy/) · [youtube-ideation](skills/youtube-ideation/) · [youtube-thumbnails](skills/youtube-thumbnails/) · [youtube-packaging](skills/youtube-packaging/) · [remotion-video](skills/remotion-video/) · [tiktok-api](skills/tiktok-api/) · [instagram-api](skills/instagram-api/) · [shortform-strategy](skills/shortform-strategy/) · [shortform-ideation](skills/shortform-ideation/) · [shortform-packaging](skills/shortform-packaging/) · [shortform-editing](skills/shortform-editing/) · [linkedin-api](skills/linkedin-api/) · [linkedin-strategy](skills/linkedin-strategy/) · [linkedin-content](skills/linkedin-content/) · [linkedin-carousels](skills/linkedin-carousels/) · [linkedin-outreach](skills/linkedin-outreach/) · [medium-writing](skills/medium-writing/) · [medium-publishing](skills/medium-publishing/) · [medium-strategy](skills/medium-strategy/)
|
|
189
171
|
|
|
190
172
|
### 🔌 Connect & automate
|
|
191
|
-
|
|
173
|
+
|
|
174
|
+
[stripe](skills/stripe/) · [email-connector](skills/email-connector/) · [google-workspace](skills/google-workspace/) · [notion-connector](skills/notion-connector/) · [whatsapp-telegram](skills/whatsapp-telegram/) · [automation-flows](skills/automation-flows/) · [api-connector-builder](skills/api-connector-builder/) · [webhooks](skills/webhooks/) · [data-scraper](skills/data-scraper/) · [spreadsheet-ops](skills/spreadsheet-ops/) · [calendar-scheduling](skills/calendar-scheduling/) · [document-processing](skills/document-processing/) · [e-signature](skills/e-signature/)
|
|
192
175
|
|
|
193
176
|
### 📊 Data & analytics
|
|
194
|
-
`analytics` · `dashboard` · `kpi-framework` · `reporting` · `ab-testing` · `forecasting` · `data-cleaning` · `business-intelligence`
|
|
195
177
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
178
|
+
[analytics](skills/analytics/) · [dashboard](skills/dashboard/) · [kpi-framework](skills/kpi-framework/) · [reporting](skills/reporting/) · [ab-testing](skills/ab-testing/) · [forecasting](skills/forecasting/) · [data-cleaning](skills/data-cleaning/) · [business-intelligence](skills/business-intelligence/)
|
|
179
|
+
|
|
180
|
+
### 🤖 AI — build it in
|
|
181
|
+
|
|
182
|
+
[building-agents](skills/building-agents/) · [rag](skills/rag/) · [embeddings-search](skills/embeddings-search/) · [prompt-engineering](skills/prompt-engineering/) · [llm-pipeline](skills/llm-pipeline/) · [agent-eval](skills/agent-eval/) · [chatbot](skills/chatbot/) · [ai-media](skills/ai-media/) · [replicate-images](skills/replicate-images/) · [structured-extraction](skills/structured-extraction/) · [agent-safety](skills/agent-safety/) · [cost-tracking](skills/cost-tracking/)
|
|
183
|
+
|
|
184
|
+
### 🛰️ AI — run it on
|
|
185
|
+
|
|
186
|
+
[replicate](skills/replicate/) · [runpod](skills/runpod/) · [modal](skills/modal/) · [huggingface](skills/huggingface/) · [ollama](skills/ollama/) · [together-fireworks](skills/together-fireworks/) · [fal](skills/fal/)
|
|
199
187
|
|
|
200
188
|
### 🗣️ Languages
|
|
201
|
-
|
|
189
|
+
|
|
190
|
+
[typescript](skills/typescript/) · [python](skills/python/) · [java](skills/java/) · [csharp-dotnet](skills/csharp-dotnet/) · [php](skills/php/) · [ruby](skills/ruby/) · [cpp](skills/cpp/) · [elixir](skills/elixir/) · [bash-scripting](skills/bash-scripting/) · [sql](skills/sql/) · [go](skills/go/)
|
|
202
191
|
|
|
203
192
|
### 🏗️ Frameworks & app stacks
|
|
204
|
-
|
|
193
|
+
|
|
194
|
+
[fastapi](skills/fastapi/) · [nextjs](skills/nextjs/) · [react](skills/react/) · [react-native](skills/react-native/) · [vue-nuxt](skills/vue-nuxt/) · [angular](skills/angular/) · [svelte](skills/svelte/) · [astro](skills/astro/) · [solid-js](skills/solid-js/) · [htmx](skills/htmx/) · [nodejs](skills/nodejs/) · [nestjs](skills/nestjs/) · [django](skills/django/) · [laravel](skills/laravel/) · [rails](skills/rails/) · [spring-boot](skills/spring-boot/) · [phoenix](skills/phoenix/) · [flutter](skills/flutter/) · [swift-ios](skills/swift-ios/) · [kotlin-android](skills/kotlin-android/) · [compose-multiplatform](skills/compose-multiplatform/) · [expo](skills/expo/) · [tauri](skills/tauri/) · [electron](skills/electron/) · [rust](skills/rust/) · [wordpress](skills/wordpress/) · [shopify](skills/shopify/) · [no-code-app](skills/no-code-app/) · [chrome-extension](skills/chrome-extension/) · [api-design](skills/api-design/)
|
|
205
195
|
|
|
206
196
|
### 🗄️ Databases & data layer
|
|
207
|
-
`postgresdb` · `mysql` · `mongodb` · `redis` · `supabase` · `neon` · `planetscale` · `sqlite-turso` · `prisma-orm` · `drizzle-orm` · `firebase` · `dynamodb` · `vector-db` · `clickhouse-analytics` · `duckdb` · `db-migrations` · `backups`
|
|
208
197
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
198
|
+
[postgresdb](skills/postgresdb/) · [mysql](skills/mysql/) · [mongodb](skills/mongodb/) · [redis](skills/redis/) · [supabase](skills/supabase/) · [neon](skills/neon/) · [planetscale](skills/planetscale/) · [sqlite-turso](skills/sqlite-turso/) · [prisma-orm](skills/prisma-orm/) · [drizzle-orm](skills/drizzle-orm/) · [firebase](skills/firebase/) · [dynamodb](skills/dynamodb/) · [vector-db](skills/vector-db/) · [clickhouse-analytics](skills/clickhouse-analytics/) · [duckdb](skills/duckdb/) · [db-migrations](skills/db-migrations/) · [backups](skills/backups/)
|
|
199
|
+
|
|
200
|
+
### ☁️ Ship & operate — platforms
|
|
201
|
+
|
|
202
|
+
[vercel](skills/vercel/) · [netlify](skills/netlify/) · [cloudflare](skills/cloudflare/) · [railway](skills/railway/) · [render](skills/render/) · [fly-io](skills/fly-io/) · [coolify](skills/coolify/) · [hetzner](skills/hetzner/) · [digitalocean](skills/digitalocean/) · [aws-essentials](skills/aws-essentials/) · [gcp-essentials](skills/gcp-essentials/)
|
|
203
|
+
|
|
204
|
+
### 🛠️ Ship & operate — devops
|
|
205
|
+
|
|
206
|
+
[docker](skills/docker/) · [github-actions](skills/github-actions/) · [git-workflow](skills/git-workflow/) · [domains-dns](skills/domains-dns/) · [monitoring](skills/monitoring/) · [email-deliverability](skills/email-deliverability/) · [scaling](skills/scaling/) · [deployment](skills/deployment/)
|
|
207
|
+
|
|
208
|
+
### 🔒 Ship & operate — quality & security
|
|
209
|
+
|
|
210
|
+
[code-review](skills/code-review/) · [security-scan](skills/security-scan/) · [secure-coding](skills/secure-coding/) · [testing-py](skills/testing-py/) · [testing-web](skills/testing-web/) · [testing-go](skills/testing-go/) · [e2e-testing](skills/e2e-testing/) · [accessibility](skills/accessibility/) · [performance](skills/performance/) · [error-handling](skills/error-handling/) · [observability](skills/observability/)
|
|
213
211
|
|
|
214
212
|
### 🎨 Design & content craft
|
|
215
|
-
|
|
213
|
+
|
|
214
|
+
[design](skills/design/) · [presentations](skills/presentations/) · [course-storytelling](skills/course-storytelling/) · [course-builder](skills/course-builder/) · [technical-writing](skills/technical-writing/) · [translation-l10n](skills/translation-l10n/)
|
|
216
215
|
|
|
217
216
|
### 🧠 Knowledge & meta
|
|
218
|
-
|
|
217
|
+
|
|
218
|
+
[knowledge-ops](skills/knowledge-ops/) · [codebase-onboarding](skills/codebase-onboarding/) · [research-ops](skills/research-ops/) · [decision-records](skills/decision-records/) · [continuous-learning](skills/continuous-learning/) · [skill-scout](skills/skill-scout/) · [context-budget](skills/context-budget/)
|
|
219
219
|
|
|
220
220
|
---
|
|
221
221
|
|
package/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rsc-universal",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Eric Risco's agent-skills catalog as a granular, self-recommending CLI installer.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"validate": "node scripts/build-manifest.js --validate",
|
|
17
17
|
"test": "node --test",
|
|
18
18
|
"coverage": "c8 --reporter=text node --test",
|
|
19
|
+
"version": "npm run manifest && git add manifest.json",
|
|
19
20
|
"prepublishOnly": "npm run manifest && npm run validate"
|
|
20
21
|
},
|
|
21
22
|
"dependencies": {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// domains.js — the catalog grouped by what the user is trying to do.
|
|
2
|
+
// Single source for the CLI's manual picker. Every skill id in the manifest
|
|
3
|
+
// MUST appear in exactly one domain (enforced by tests/domains.test.js) so the
|
|
4
|
+
// catalog grows deliberately, never silently.
|
|
5
|
+
|
|
6
|
+
export const DOMAINS = [
|
|
7
|
+
{ title: 'Core & control plane', ids: ['init', 'harness', 'orient', 'suggest', 'author-skill', 'sdd-init'] },
|
|
8
|
+
{ title: 'Spec-Driven Development', ids: ['sdd', 'constitution', 'specify', 'clarify', 'plan', 'tasks', 'analyze', 'implement', 'verify', 'review', 'ship', 'debug', 'worktrees', 'parallel'] },
|
|
9
|
+
{ title: 'Run a business', ids: ['finance-ops', 'invoicing', 'bookkeeping', 'pricing', 'sales-pipeline', 'lead-gen', 'cold-outreach', 'proposals', 'contracts', 'customer-support', 'client-onboarding', 'retention', 'hiring', 'people-ops', 'inventory', 'logistics-ops', 'procurement', 'meeting-notes', 'sop-builder', 'project-ops'] },
|
|
10
|
+
{ title: 'Raise & model money', ids: ['pitch-deck', 'investor-materials', 'financial-model', 'fundraising', 'unit-economics', 'grants'] },
|
|
11
|
+
{ title: 'Legal, privacy & compliance', ids: ['gdpr-privacy', 'terms-conditions', 'compliance', 'data-policy', 'ip-trademark'] },
|
|
12
|
+
{ title: 'Market & brand', ids: ['marketing', 'seo-geo', 'content-engine', 'social-publisher', 'brand-voice', 'brand-identity', 'newsletter', 'landing-copy', 'ads', 'article-writing', 'case-studies', 'video-shorts', 'podcast', 'market-research', 'competitor-watch', 'press-kit', 'community', 'webinar', 'review-management'] },
|
|
13
|
+
{ title: 'Grow a channel (YouTube / TikTok / Reels / LinkedIn / Medium)', ids: ['youtube-api', 'youtube-strategy', 'youtube-ideation', 'youtube-thumbnails', 'youtube-packaging', 'remotion-video', 'tiktok-api', 'instagram-api', 'shortform-strategy', 'shortform-ideation', 'shortform-packaging', 'shortform-editing', 'linkedin-api', 'linkedin-strategy', 'linkedin-content', 'linkedin-carousels', 'linkedin-outreach', 'medium-writing', 'medium-publishing', 'medium-strategy'] },
|
|
14
|
+
{ title: 'Connect & automate', ids: ['stripe', 'email-connector', 'google-workspace', 'notion-connector', 'whatsapp-telegram', 'automation-flows', 'api-connector-builder', 'webhooks', 'data-scraper', 'spreadsheet-ops', 'calendar-scheduling', 'document-processing', 'e-signature'] },
|
|
15
|
+
{ title: 'Data & analytics', ids: ['analytics', 'dashboard', 'kpi-framework', 'reporting', 'ab-testing', 'forecasting', 'data-cleaning', 'business-intelligence'] },
|
|
16
|
+
{ title: 'AI — build it in', ids: ['building-agents', 'rag', 'embeddings-search', 'prompt-engineering', 'llm-pipeline', 'agent-eval', 'chatbot', 'ai-media', 'replicate-images', 'structured-extraction', 'agent-safety', 'cost-tracking'] },
|
|
17
|
+
{ title: 'AI — run it on', ids: ['replicate', 'runpod', 'modal', 'huggingface', 'ollama', 'together-fireworks', 'fal'] },
|
|
18
|
+
{ title: 'Languages', ids: ['typescript', 'python', 'java', 'csharp-dotnet', 'php', 'ruby', 'cpp', 'elixir', 'bash-scripting', 'sql', 'go'] },
|
|
19
|
+
{ title: 'Frameworks & app stacks', ids: ['fastapi', 'nextjs', 'react', 'react-native', 'vue-nuxt', 'angular', 'svelte', 'astro', 'solid-js', 'htmx', 'nodejs', 'nestjs', 'django', 'laravel', 'rails', 'spring-boot', 'phoenix', 'flutter', 'swift-ios', 'kotlin-android', 'compose-multiplatform', 'expo', 'tauri', 'electron', 'rust', 'wordpress', 'shopify', 'no-code-app', 'chrome-extension', 'api-design'] },
|
|
20
|
+
{ title: 'Databases & data layer', ids: ['postgresdb', 'mysql', 'mongodb', 'redis', 'supabase', 'neon', 'planetscale', 'sqlite-turso', 'prisma-orm', 'drizzle-orm', 'firebase', 'dynamodb', 'vector-db', 'clickhouse-analytics', 'duckdb', 'db-migrations', 'backups'] },
|
|
21
|
+
{ title: 'Ship & operate — platforms', ids: ['vercel', 'netlify', 'cloudflare', 'railway', 'render', 'fly-io', 'coolify', 'hetzner', 'digitalocean', 'aws-essentials', 'gcp-essentials'] },
|
|
22
|
+
{ title: 'Ship & operate — devops', ids: ['docker', 'github-actions', 'git-workflow', 'domains-dns', 'monitoring', 'email-deliverability', 'scaling', 'deployment'] },
|
|
23
|
+
{ title: 'Ship & operate — quality & security', ids: ['code-review', 'security-scan', 'secure-coding', 'testing-py', 'testing-web', 'testing-go', 'e2e-testing', 'accessibility', 'performance', 'error-handling', 'observability'] },
|
|
24
|
+
{ title: 'Design & content craft', ids: ['design', 'presentations', 'course-storytelling', 'course-builder', 'technical-writing', 'translation-l10n'] },
|
|
25
|
+
{ title: 'Knowledge & meta', ids: ['knowledge-ops', 'codebase-onboarding', 'research-ops', 'decision-records', 'continuous-learning', 'skill-scout', 'context-budget'] },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export function allDomainIds() {
|
|
29
|
+
return DOMAINS.flatMap((d) => d.ids);
|
|
30
|
+
}
|
package/scripts/lib/recommend.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { skillById } from './manifest.js';
|
|
2
2
|
|
|
3
3
|
const OUTCOMES = {
|
|
4
|
-
suggest: '
|
|
5
|
-
harness: '
|
|
6
|
-
init: '
|
|
7
|
-
nextjs: '
|
|
8
|
-
flutter: '
|
|
9
|
-
fastapi: '
|
|
10
|
-
go: '
|
|
11
|
-
postgresdb: '
|
|
12
|
-
design: '
|
|
13
|
-
marketing: '
|
|
14
|
-
presentations: '
|
|
15
|
-
'course-storytelling': '
|
|
16
|
-
'building-agents': '
|
|
17
|
-
'secure-coding': '
|
|
18
|
-
deployment: '
|
|
4
|
+
suggest: 'An assistant that proposes what you need as you go',
|
|
5
|
+
harness: 'A tidy workspace: connect and document your company',
|
|
6
|
+
init: 'Guided project bootstrap',
|
|
7
|
+
nextjs: 'Your website (fast, ready for Google)',
|
|
8
|
+
flutter: 'Your mobile app',
|
|
9
|
+
fastapi: 'Your API / Python backend',
|
|
10
|
+
go: 'Your Go backend service',
|
|
11
|
+
postgresdb: 'Store your data reliably',
|
|
12
|
+
design: 'Make it look good and convert',
|
|
13
|
+
marketing: 'Copy that sells',
|
|
14
|
+
presentations: 'On-brand presentations',
|
|
15
|
+
'course-storytelling': 'Teach so it actually lands',
|
|
16
|
+
'building-agents': 'Your own AI agent',
|
|
17
|
+
'secure-coding': 'Make it secure',
|
|
18
|
+
deployment: 'Put it online',
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
export function hasOutcome(id) {
|
package/scripts/lib/ui.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createInterface } from 'node:readline
|
|
1
|
+
import { createInterface, emitKeypressEvents } from 'node:readline';
|
|
2
2
|
import { stdin, stdout } from 'node:process';
|
|
3
3
|
|
|
4
4
|
export async function ask(question) {
|
|
@@ -13,5 +13,131 @@ export function say(...lines) {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export function yes(s) {
|
|
16
|
-
return /^(s|si|sí|y|yes|ok|
|
|
16
|
+
return /^(s|si|sí|y|yes|ok|sure|yeah)/i.test(s.trim());
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Interactive arrow-key / space TUI (zero dependencies).
|
|
21
|
+
// Falls back to a typed-number prompt when there is no interactive TTY
|
|
22
|
+
// (CI, pipes, dumb terminals) so scripts and tests never hang.
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
function interactive() {
|
|
26
|
+
return Boolean(stdin.isTTY && stdout.isTTY);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const C = {
|
|
30
|
+
dim: (s) => `\x1b[2m${s}\x1b[22m`,
|
|
31
|
+
bold: (s) => `\x1b[1m${s}\x1b[22m`,
|
|
32
|
+
cyan: (s) => `\x1b[36m${s}\x1b[39m`,
|
|
33
|
+
green: (s) => `\x1b[32m${s}\x1b[39m`,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Repaint a fixed block of lines in place (cursor ends just below the block).
|
|
37
|
+
function makePainter() {
|
|
38
|
+
let height = 0;
|
|
39
|
+
return (lines) => {
|
|
40
|
+
if (height) stdout.write(`\x1b[${height}A`);
|
|
41
|
+
stdout.write(lines.map((l) => `\x1b[2K${l}`).join('\n') + '\n');
|
|
42
|
+
height = lines.length;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function captureKeys(onKey) {
|
|
47
|
+
emitKeypressEvents(stdin);
|
|
48
|
+
if (stdin.isTTY) stdin.setRawMode(true);
|
|
49
|
+
stdin.resume();
|
|
50
|
+
const handler = (str, key) => onKey(str, key || {});
|
|
51
|
+
stdin.on('keypress', handler);
|
|
52
|
+
return () => {
|
|
53
|
+
stdin.off('keypress', handler);
|
|
54
|
+
if (stdin.isTTY) stdin.setRawMode(false);
|
|
55
|
+
stdin.pause();
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Single choice: ↑↓ to move, Enter to pick. options: [{ key, label }].
|
|
60
|
+
function tuiSelect(question, options) {
|
|
61
|
+
return new Promise((resolve) => {
|
|
62
|
+
let i = 0;
|
|
63
|
+
const paint = makePainter();
|
|
64
|
+
const render = () => paint([
|
|
65
|
+
C.bold(question),
|
|
66
|
+
C.dim(' ↑↓ move · enter select'),
|
|
67
|
+
...options.map((o, idx) =>
|
|
68
|
+
idx === i ? `${C.cyan('❯')} ${C.cyan(o.label)}` : ` ${o.label}`),
|
|
69
|
+
]);
|
|
70
|
+
render();
|
|
71
|
+
const stop = captureKeys((str, key) => {
|
|
72
|
+
if (key.name === 'up' || key.name === 'k') { i = (i - 1 + options.length) % options.length; render(); }
|
|
73
|
+
else if (key.name === 'down' || key.name === 'j') { i = (i + 1) % options.length; render(); }
|
|
74
|
+
else if (key.name === 'return') { stop(); resolve(options[i].key); }
|
|
75
|
+
else if (key.name === 'escape' || (key.ctrl && key.name === 'c')) { stop(); stdout.write('\n'); process.exit(130); }
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Multi-select: ↑↓ move, space toggle, a = all, enter confirm. Viewport scrolls.
|
|
81
|
+
function tuiChecklist(title, items) {
|
|
82
|
+
return new Promise((resolve) => {
|
|
83
|
+
const VISIBLE = Math.min(items.length, 12);
|
|
84
|
+
const sel = new Set();
|
|
85
|
+
let cur = 0; let top = 0;
|
|
86
|
+
const paint = makePainter();
|
|
87
|
+
const render = () => {
|
|
88
|
+
if (cur < top) top = cur;
|
|
89
|
+
if (cur >= top + VISIBLE) top = cur - VISIBLE + 1;
|
|
90
|
+
const rows = [];
|
|
91
|
+
for (let r = top; r < top + VISIBLE; r++) {
|
|
92
|
+
const it = items[r];
|
|
93
|
+
const box = sel.has(r) ? C.green('◉') : '◯';
|
|
94
|
+
const line = `${box} ${it.label}`;
|
|
95
|
+
rows.push(r === cur ? `${C.cyan('❯')} ${C.cyan(line)}` : ` ${line}`);
|
|
96
|
+
}
|
|
97
|
+
const more = items.length > VISIBLE ? C.dim(` (${cur + 1}/${items.length})`) : '';
|
|
98
|
+
paint([
|
|
99
|
+
C.bold(title),
|
|
100
|
+
C.dim(' ↑↓ move · space toggle · a all · enter confirm') + more,
|
|
101
|
+
...rows,
|
|
102
|
+
]);
|
|
103
|
+
};
|
|
104
|
+
render();
|
|
105
|
+
const stop = captureKeys((str, key) => {
|
|
106
|
+
if (key.name === 'up' || key.name === 'k') { cur = (cur - 1 + items.length) % items.length; render(); }
|
|
107
|
+
else if (key.name === 'down' || key.name === 'j') { cur = (cur + 1) % items.length; render(); }
|
|
108
|
+
else if (key.name === 'space') { sel.has(cur) ? sel.delete(cur) : sel.add(cur); render(); }
|
|
109
|
+
else if (str === 'a') { if (sel.size === items.length) sel.clear(); else items.forEach((_, idx) => sel.add(idx)); render(); }
|
|
110
|
+
else if (key.name === 'return') { stop(); resolve([...sel].sort((x, y) => x - y).map((idx) => items[idx].id)); }
|
|
111
|
+
else if (key.name === 'escape' || (key.ctrl && key.name === 'c')) { stop(); stdout.write('\n'); process.exit(130); }
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Public: single-choice menu. options: [{ key, label }]. Returns the chosen key.
|
|
117
|
+
export async function select(question, options) {
|
|
118
|
+
if (interactive()) return tuiSelect(question, options);
|
|
119
|
+
// Fallback: typed number.
|
|
120
|
+
say(question);
|
|
121
|
+
options.forEach((o, i) => say(` ${i + 1}) ${o.label}`));
|
|
122
|
+
const a = (await ask('> ')).toLowerCase();
|
|
123
|
+
const n = parseInt(a, 10);
|
|
124
|
+
if (n >= 1 && n <= options.length) return options[n - 1].key;
|
|
125
|
+
const byKey = options.find((o) => o.key.toLowerCase() === a);
|
|
126
|
+
return byKey ? byKey.key : null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Public: multi-select. items: array of strings or { id, label }. Returns ids.
|
|
130
|
+
export async function pickFrom(title, items) {
|
|
131
|
+
const norm = items.map((it) => (typeof it === 'string' ? { id: it, label: it } : it));
|
|
132
|
+
if (interactive()) return tuiChecklist(title, norm);
|
|
133
|
+
// Fallback: comma-separated numbers.
|
|
134
|
+
say(`\n${title}`);
|
|
135
|
+
norm.forEach((it, i) => say(` ${String(i + 1).padStart(2)}) ${it.label}`));
|
|
136
|
+
say(' Comma-separated numbers (e.g. 1,3,4), "all", or Enter for none.');
|
|
137
|
+
const a = (await ask('> ')).trim();
|
|
138
|
+
if (!a) return [];
|
|
139
|
+
if (/^(all|todo|todas)$/i.test(a)) return norm.map((it) => it.id);
|
|
140
|
+
return [...new Set(
|
|
141
|
+
a.split(',').map((s) => parseInt(s.trim(), 10)).filter((x) => x >= 1 && x <= norm.length).map((x) => norm[x - 1].id),
|
|
142
|
+
)];
|
|
17
143
|
}
|
package/scripts/rsc.js
CHANGED
|
@@ -6,8 +6,9 @@ import { rank } from './consult.js';
|
|
|
6
6
|
import { expandRecommends, toOutcomes, hasOutcome } from './lib/recommend.js';
|
|
7
7
|
import { applyInstall, listInstalled, uninstall } from './install-apply.js';
|
|
8
8
|
import { doctor } from './doctor.js';
|
|
9
|
-
import { ask, say, yes } from './lib/ui.js';
|
|
9
|
+
import { ask, say, yes, select, pickFrom } from './lib/ui.js';
|
|
10
10
|
import { refreshRegistry, registryStatus } from './lib/registry.js';
|
|
11
|
+
import { DOMAINS } from './lib/domains.js';
|
|
11
12
|
|
|
12
13
|
const argv = process.argv.slice(2);
|
|
13
14
|
const cmd = argv[0];
|
|
@@ -29,25 +30,102 @@ async function recommendIds(query, { labeledOnly = false } = {}) {
|
|
|
29
30
|
return out.slice(0, 6);
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
33
|
+
// Pick skills by browsing the domains, accumulating across rounds.
|
|
34
|
+
async function manualSelect() {
|
|
35
|
+
const chosen = new Set();
|
|
36
|
+
for (;;) {
|
|
37
|
+
const opts = DOMAINS.map((d, i) => ({ key: String(i), label: `${d.title} (${d.ids.length})` }));
|
|
38
|
+
opts.push({ key: 'done', label: chosen.size ? `✅ Finish & install (${chosen.size} chosen)` : 'Finish without choosing anything' });
|
|
39
|
+
const k = await select('\nWhich area do you want to install skills from?', opts);
|
|
40
|
+
if (k === 'done' || k === null) break;
|
|
41
|
+
const d = DOMAINS[parseInt(k, 10)];
|
|
42
|
+
(await pickFrom(`${d.title}:`, d.ids)).forEach((id) => chosen.add(id));
|
|
43
|
+
say(` → ${chosen.size} skills chosen so far.`);
|
|
44
|
+
}
|
|
45
|
+
return [...chosen];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Describe-your-project flow: rsc reads repo + words and proposes skills.
|
|
49
|
+
async function describeFlow() {
|
|
50
|
+
const goal = await ask('\nTell me in one sentence what you want to build or run:\n> ');
|
|
35
51
|
const ids = await recommendIds(goal, { labeledOnly: true });
|
|
36
52
|
if (!ids.length) {
|
|
37
|
-
say('
|
|
38
|
-
return;
|
|
53
|
+
say("I'm not sure. Try describing it in more detail, or pick by hand (option 3).");
|
|
54
|
+
return [];
|
|
39
55
|
}
|
|
40
|
-
say('\
|
|
56
|
+
say('\nI recommend installing:');
|
|
41
57
|
for (const o of toOutcomes(ids)) say(` • ${o.label}`);
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
58
|
+
return ids;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// After installing, remind the user how to actually start — per IDE — and that
|
|
62
|
+
// rsc keeps recommending skills as they work. The harness/SDD *init* runs INSIDE
|
|
63
|
+
// the assistant (with the user present), never blindly from this CLI.
|
|
64
|
+
function printNextSteps(target, ids) {
|
|
65
|
+
const hasHarness = ids.includes('harness');
|
|
66
|
+
const hasSdd = ids.includes('sdd') || ids.includes('sdd-init');
|
|
67
|
+
const openLine = {
|
|
68
|
+
claude: 'Open **Claude Code** in this project folder.',
|
|
69
|
+
cursor: 'Open **Cursor** in this project folder.',
|
|
70
|
+
codex: 'Open **Codex CLI** (it reads AGENTS.md) in this project folder.',
|
|
71
|
+
gemini: 'Open **Gemini CLI** in this project folder.',
|
|
72
|
+
}[target] || 'Open your assistant in this project folder.';
|
|
73
|
+
|
|
74
|
+
say('\n────────────────────────────────────────────────────────');
|
|
75
|
+
say('👉 When you start working (these steps happen in your assistant, not here):');
|
|
76
|
+
let n = 1;
|
|
77
|
+
say(` ${n++}. ${openLine}`);
|
|
78
|
+
if (hasHarness) {
|
|
79
|
+
say(` ${n++}. Set up the second brain — tell it:`);
|
|
80
|
+
say(' "set up the harness for this project"');
|
|
81
|
+
say(' → creates 01-TOOLS/ (connections) + 02-DOCS/ (wiki) + CLAUDE.md/AGENTS.md.');
|
|
82
|
+
}
|
|
83
|
+
if (hasSdd) {
|
|
84
|
+
say(` ${n++}. For a new feature, tell it:`);
|
|
85
|
+
say(' "sdd-init" then "I want <your idea>"');
|
|
86
|
+
say(' → walks you specify → plan → tasks → implement → verify → ship.');
|
|
87
|
+
}
|
|
88
|
+
say(` ${n++}. From there, work in your own words. orient + suggest stay always-on:`);
|
|
89
|
+
say(' they keep you oriented after each step and propose the missing skill (you confirm, it installs).');
|
|
90
|
+
say('\n Add something by hand anytime: npx rsc add <skill>');
|
|
91
|
+
say(' Browse the catalog / get picks: npx rsc consult "whatever you need"');
|
|
92
|
+
say('────────────────────────────────────────────────────────');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function wizard() {
|
|
96
|
+
const m = loadManifest();
|
|
97
|
+
say('👋 rsc — the skill catalog for your assistant (Claude Code · Cursor · Codex · Gemini).');
|
|
98
|
+
const choice = await select('\nWhat do you want to do?', [
|
|
99
|
+
{ key: 'base', label: 'Base install — the essentials (orient + suggest + harness + init)' },
|
|
100
|
+
{ key: 'sdd', label: 'Base + Spec-Driven Development — the specify → plan → implement → ship flow' },
|
|
101
|
+
{ key: 'manual', label: 'Pick skills by hand, by area' },
|
|
102
|
+
{ key: 'describe', label: 'Describe my project and let rsc choose' },
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
let ids = [];
|
|
106
|
+
if (choice === 'base') ids = skillsForProfile(m, 'minimal');
|
|
107
|
+
else if (choice === 'sdd') ids = skillsForProfile(m, 'core');
|
|
108
|
+
else if (choice === 'manual') ids = await manualSelect();
|
|
109
|
+
else if (choice === 'describe') ids = await describeFlow();
|
|
110
|
+
else { say("Didn't catch that. Run again: npx rsc"); return; }
|
|
111
|
+
|
|
112
|
+
// The floor is always installed: the compass + the detector.
|
|
113
|
+
ids = [...new Set(['orient', 'suggest', ...ids])];
|
|
114
|
+
if (ids.length <= 2 && choice !== 'base') {
|
|
115
|
+
say('\nNo skills were chosen. Anytime: npx rsc');
|
|
45
116
|
return;
|
|
46
117
|
}
|
|
118
|
+
|
|
47
119
|
const target = detectTarget();
|
|
48
|
-
|
|
49
|
-
say('
|
|
50
|
-
|
|
120
|
+
say(`\nI'll install ${ids.length} skills into ${target}:`);
|
|
121
|
+
say(' ' + ids.join(', '));
|
|
122
|
+
if (!yes(await ask('\nInstall it? (yes / no) > '))) {
|
|
123
|
+
say('No problem. Anytime: npx rsc');
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
await applyInstall({ skillIds: ids, target });
|
|
127
|
+
say(`\n✅ Installed ${ids.length} skills into ${target}.`);
|
|
128
|
+
printNextSteps(target, ids);
|
|
51
129
|
}
|
|
52
130
|
|
|
53
131
|
async function main() {
|
|
@@ -56,49 +134,49 @@ async function main() {
|
|
|
56
134
|
case undefined:
|
|
57
135
|
return wizard();
|
|
58
136
|
case 'add':
|
|
59
|
-
await applyInstall({ skillIds: [...new Set(['suggest', ...argv.slice(1)])], target });
|
|
60
|
-
return void say(`✅
|
|
137
|
+
await applyInstall({ skillIds: [...new Set(['orient', 'suggest', ...argv.slice(1)])], target });
|
|
138
|
+
return void say(`✅ Installed: ${argv.slice(1).join(', ')}`);
|
|
61
139
|
case 'install': {
|
|
62
140
|
const profile = flag('profile') || 'minimal';
|
|
63
141
|
const without = argv.filter((a, i) => argv[i - 1] === '--without');
|
|
64
142
|
let ids = skillsForProfile(loadManifest(), profile);
|
|
65
|
-
ids = [...new Set(['suggest', ...ids])].filter((id) => !without.includes(id));
|
|
143
|
+
ids = [...new Set(['orient', 'suggest', ...ids])].filter((id) => !without.includes(id));
|
|
66
144
|
await applyInstall({ skillIds: ids, target });
|
|
67
|
-
return void say(`✅
|
|
145
|
+
return void say(`✅ Profile '${profile}' installed into ${target} (${ids.length} skills)`);
|
|
68
146
|
}
|
|
69
147
|
case 'consult': {
|
|
70
148
|
const ids = await recommendIds(argv.slice(1).join(' '));
|
|
71
|
-
if (!ids.length) return void say('(
|
|
149
|
+
if (!ids.length) return void say('(no recommendations)');
|
|
72
150
|
for (const o of toOutcomes(ids)) say(`${o.id}\t${o.label}`);
|
|
73
151
|
return;
|
|
74
152
|
}
|
|
75
153
|
case 'list':
|
|
76
|
-
return void say(listInstalled({ target }).join('\n') || '(
|
|
154
|
+
return void say(listInstalled({ target }).join('\n') || '(nothing installed)');
|
|
77
155
|
case 'doctor':
|
|
78
156
|
return void say(JSON.stringify(doctor({ target }), null, 2));
|
|
79
157
|
case 'registry': {
|
|
80
158
|
const sub = argv[1];
|
|
81
159
|
if (sub === 'refresh') {
|
|
82
160
|
const registry = refreshRegistry({ target });
|
|
83
|
-
say(`✅ Registry
|
|
161
|
+
say(`✅ Registry updated: .rsc/skill-registry.md (${registry.counts.skills} skills)`);
|
|
84
162
|
return;
|
|
85
163
|
}
|
|
86
164
|
if (sub === 'status') {
|
|
87
165
|
say(JSON.stringify(registryStatus(), null, 2));
|
|
88
166
|
return;
|
|
89
167
|
}
|
|
90
|
-
say('
|
|
168
|
+
say('Use: npx rsc registry refresh | registry status');
|
|
91
169
|
return;
|
|
92
170
|
}
|
|
93
171
|
case 'uninstall': {
|
|
94
172
|
const dry = argv.includes('--dry-run');
|
|
95
173
|
const ids = argv.slice(1).filter((a) => !a.startsWith('--'));
|
|
96
174
|
const removed = await uninstall({ skillIds: ids, target, dryRun: dry });
|
|
97
|
-
return void say((dry ? '
|
|
175
|
+
return void say((dry ? 'Would remove:\n' : 'Removed:\n') + (removed.join('\n') || '(nothing)'));
|
|
98
176
|
}
|
|
99
177
|
default:
|
|
100
|
-
say(`rsc:
|
|
101
|
-
say('
|
|
178
|
+
say(`rsc: unknown command '${cmd}'.`);
|
|
179
|
+
say('Use: npx rsc | add <id...> | install --profile <p> | consult "<text>" | list | registry refresh | doctor | uninstall <id>');
|
|
102
180
|
}
|
|
103
181
|
}
|
|
104
182
|
|