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 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 (see
36
- [The quality bar](#the-quality-bar)). The bar was real: skills that scored 8.0
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
- git clone https://github.com/ericrisco/skills.git ~/rsc-skills
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
- Then, inside any project of yours:
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
- cd ~/my-project
58
- rsc # the wizard: describe what you want, it installs what fits
54
+ git clone https://github.com/ericrisco/skills.git ~/rsc-skills
55
+ cd ~/rsc-skills && npm install && npm link
59
56
  ```
60
57
 
61
- > Once published: `npx rsc` with no install step. Prefer no global link? Call it
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
- Hola 👋 ¿Qué quieres hacer?
75
- > una tienda web con base de datos, y llevar la contabilidad
76
-
77
- He preparado esto para ti:
78
- Tu tienda (rápida y lista para Google) nextjs, design, seo-geo
79
- Guardar tus datos de forma fiable → postgresdb
80
- Cobrar y facturar → stripe, invoicing
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
- It detects your stack from the repo, maps your words to outcomes, installs the
90
- matching skills, then suggests what usually comes next.
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 "quiero lanzar un saas" # recommend only, no install
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. Invoke any installed skill by
158
- name in your assistant; it fires on its own when a task matches.
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
- `init` · `harness` · `author-skill` · `suggest` · `sdd-init`
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
- `sdd` · `constitution` · `specify` · `clarify` · `plan` · `tasks` · `analyze` · `implement` · `verify` · `review` · `ship` · `debug` · `worktrees` · `parallel`
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
- `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`
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
- `pitch-deck` · `investor-materials` · `financial-model` · `fundraising` · `unit-economics` · `grants`
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
- `gdpr-privacy` · `terms-conditions` · `compliance` · `data-policy` · `ip-trademark`
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
- ### 🎬 Grow a channel (each with a `02-DOCS` feedback loop)
185
- - **YouTube** — `youtube-api` · `youtube-strategy` · `youtube-ideation` · `youtube-thumbnails` · `youtube-packaging` · `remotion-video` *(Remotion edits: transitions, Whisper captions, silence removal)*
186
- - **TikTok / Reels** — `tiktok-api` · `instagram-api` · `shortform-strategy` · `shortform-ideation` · `shortform-packaging` · `shortform-editing`
187
- - **LinkedIn** `linkedin-api` · `linkedin-strategy` · `linkedin-content` · `linkedin-carousels` · `linkedin-outreach`
188
- - **Medium** — `medium-writing` · `medium-publishing` · `medium-strategy`
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
- `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`
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
- ### 🤖 AI features & infra
197
- - **Build AI in** — `building-agents` · `rag` · `embeddings-search` · `prompt-engineering` · `llm-pipeline` · `agent-eval` · `chatbot` · `ai-media` · `replicate-images` · `structured-extraction` · `agent-safety` · `cost-tracking`
198
- - **Run AI on** `replicate` · `runpod` · `modal` · `huggingface` · `ollama` · `together-fireworks` · `fal`
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
- `typescript` · `python` · `java` · `csharp-dotnet` · `php` · `ruby` · `cpp` · `elixir` · `bash-scripting` · `sql` · `go` *(+ `fastapi` for async Python services)*
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
- `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`
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
- ### ☁️ Ship & operate
210
- - **Platforms** — `vercel` · `netlify` · `cloudflare` · `railway` · `render` · `fly-io` · `coolify` · `hetzner` · `digitalocean` · `aws-essentials` · `gcp-essentials`
211
- - **DevOps** `docker` · `github-actions` · `git-workflow` · `domains-dns` · `monitoring` · `email-deliverability` · `scaling` · `deployment`
212
- - **Quality & security** — `code-review` · `security-scan` · `secure-coding` · `testing-py` · `testing-web` · `testing-go` · `e2e-testing` · `accessibility` · `performance` · `error-handling` · `observability`
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
- `design` · `presentations` · `course-storytelling` · `course-builder` · `technical-writing` · `translation-l10n`
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
- `knowledge-ops` · `codebase-onboarding` · `research-ops` · `decision-records` · `continuous-learning` · `skill-scout` · `context-budget`
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
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.1",
2
+ "version": "0.1.3",
3
3
  "counts": {
4
4
  "skills": 231
5
5
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rsc-universal",
3
- "version": "0.1.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
+ }
@@ -1,21 +1,21 @@
1
1
  import { skillById } from './manifest.js';
2
2
 
3
3
  const OUTCOMES = {
4
- suggest: 'Un asistente que te va proponiendo lo que necesitas',
5
- harness: 'Un espacio de trabajo ordenado: conecta y documenta tu empresa',
6
- init: 'Arranque guiado de tu proyecto',
7
- nextjs: 'Tu web (rápida y lista para Google)',
8
- flutter: 'Tu app de móvil',
9
- fastapi: 'Tu API / backend en Python',
10
- go: 'Tu servicio backend en Go',
11
- postgresdb: 'Guardar tus datos de forma fiable',
12
- design: 'Que se vea bien y convierta',
13
- marketing: 'Textos que venden',
14
- presentations: 'Presentaciones con tu marca',
15
- 'course-storytelling': 'Enseñar de forma que se entienda',
16
- 'building-agents': 'Un agente de IA propio',
17
- 'secure-coding': 'Que sea seguro',
18
- deployment: 'Publicarlo online',
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/promises';
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|vale|dale)/i.test(s.trim());
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
- async function wizard() {
33
- say('Hola 👋 ¿Qué quieres hacer?');
34
- const goal = await ask('> ');
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('No estoy seguro de qué necesitas. Prueba a describir tu proyecto con más detalle.');
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('\nHe preparado esto para ti:');
56
+ say('\nI recommend installing:');
41
57
  for (const o of toOutcomes(ids)) say(` • ${o.label}`);
42
- const ok = yes(await ask('\n¿Lo monto? (sí / no) > '));
43
- if (!ok) {
44
- say('Sin problema. Cuando quieras: npx rsc');
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
- await applyInstall({ skillIds: [...new Set(['suggest', ...ids])], target });
49
- say('\n✅ Listo. Abre tu editor y empieza a pedir cosas en tu idioma.');
50
- say(' 💡 Para añadir más cuando lo necesites, vuelve a escribir: npx rsc');
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(`✅ Instalado: ${argv.slice(1).join(', ')}`);
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(`✅ Perfil '${profile}' instalado en ${target} (${ids.length} skills)`);
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('(sin recomendaciones)');
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') || '(nada instalado)');
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 actualizado: .rsc/skill-registry.md (${registry.counts.skills} skills)`);
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('Usa: npx rsc registry refresh | registry status');
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 ? 'Se borraría:\n' : 'Borrado:\n') + (removed.join('\n') || '(nada)'));
175
+ return void say((dry ? 'Would remove:\n' : 'Removed:\n') + (removed.join('\n') || '(nothing)'));
98
176
  }
99
177
  default:
100
- say(`rsc: comando desconocido '${cmd}'.`);
101
- say('Usa: npx rsc | add <id...> | install --profile <p> | consult "<texto>" | list | registry refresh | doctor | uninstall <id>');
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