@ericrisco/rsc 0.1.24 → 0.1.25

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
@@ -75,6 +75,13 @@ git clone https://github.com/ericrisco/rsc-harness.git ~/rsc-skills
75
75
  cd ~/rsc-skills && npm install && npm link
76
76
  ```
77
77
 
78
+ > **Run it inside the project you're equipping — not inside this repo.** The
79
+ > catalog's own `package.json` is named `@ericrisco/rsc`, so `npx @ericrisco/rsc`
80
+ > *from within a `rsc-harness` clone* resolves to the local (unlinked) bin and
81
+ > dies with `sh: rsc: command not found`. Working on the catalog itself? Use
82
+ > `node scripts/rsc.js …`, the `npm link` above, or pin the published build with
83
+ > `npx @ericrisco/rsc@latest …`.
84
+
78
85
  The first run asks **which assistants** you want — Claude Code, Codex, Copilot,
79
86
  Cursor, Gemini, Windsurf, Cline and 11 more (pick any combination) — and installs
80
87
  the **floor**:
@@ -160,6 +167,36 @@ rsc uninstall postgresdb --dry-run # preview a removal
160
167
 
161
168
  ---
162
169
 
170
+ ## Update
171
+
172
+ `rsc` is an npm package, so updating is two steps — bump the package, then
173
+ re-sync what's already wired into your project:
174
+
175
+ ```bash
176
+ npm install -g @ericrisco/rsc@latest # global install: pull the newest catalog
177
+ rsc sync # refresh managed skills + hooks (auto-detects your assistant)
178
+ ```
179
+
180
+ Not sure what a bump touches? Preview the exact commands without writing anything:
181
+
182
+ ```bash
183
+ rsc upgrade --dry-run # prints the npm install + rsc sync lines for your target
184
+ ```
185
+
186
+ Running through `npx` (no global install)? There's nothing to upgrade —
187
+ `npx @ericrisco/rsc@latest` always fetches the latest published catalog; just run
188
+ `rsc sync` afterwards if the project already has skills installed.
189
+
190
+ Every sync snapshots the project first, so a bad update is always reversible:
191
+
192
+ ```bash
193
+ rsc backups # list project-local snapshots
194
+ rsc restore latest --dry-run # preview restoring the newest
195
+ rsc restore <snapshot-id> # restore it
196
+ ```
197
+
198
+ ---
199
+
163
200
  ## How recommendation works
164
201
 
165
202
  Two faces, one catalog (`manifest.json`):
package/manifest.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.24",
2
+ "version": "0.1.25",
3
3
  "counts": {
4
4
  "skills": 231
5
5
  },
@@ -4112,7 +4112,7 @@
4112
4112
  },
4113
4113
  {
4114
4114
  "id": "suggest",
4115
- "description": "Always-on. Use whenever the current task would clearly benefit from an rsc skill that is not yet installed — detect the gap, name the skill, and (with a one-word confirm) install it via `npx @ericrisco/rsc add <id>`. Triggers on any task that maps to a known rsc capability the session lacks: building a web/app/API, a database, security, deployment, agents, content, or connecting/documenting a company.",
4115
+ "description": "Always-on. Use whenever the current user turn would clearly benefit from an rsc skill that is not yet installed — detect the gap during normal agent use, name the skill, and (with a one-word confirm) install it via `npx @ericrisco/rsc add <id>`. Triggers on capability intent in any language: building technology, creating content/assets, automating workflows, analyzing data, connecting tools, shipping/deploying, security, business ops, marketing, education, research, or company/documentation harness work.",
4116
4116
  "tags": [
4117
4117
  "suggest",
4118
4118
  "detect",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ericrisco/rsc",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "description": "Eric Risco's agent-skills catalog as a granular, self-recommending CLI installer.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,22 +1,37 @@
1
1
  import initSqlJs from 'sql.js';
2
2
 
3
- // Stopwords (es + en) — these match everywhere via LIKE and drown the signal.
3
+ // Stopwords (es + en + common EU words) — these match everywhere via LIKE and drown the signal.
4
4
  const STOP = new Set([
5
5
  'una', 'un', 'unos', 'unas', 'con', 'de', 'del', 'la', 'el', 'los', 'las', 'para',
6
6
  'que', 'porque', 'en', 'mi', 'mis', 'me', 'quiero', 'tener', 'hacer', 'al', 'lo',
7
7
  'su', 'sus', 'este', 'esta', 'esto', 'como', 'mas', 'más', 'algo', 'cosa', 'cosas',
8
+ 'vull', 'vols', 'amb', 'segons', 'usuari', 'usuaris', 'aquesta', 'aquest',
9
+ 'aixo', 'això', 'per', 'els', 'les', 'dels', 'segun', 'según', 'usuario',
10
+ 'usuarios', 'tarea', 'tareas',
8
11
  'the', 'and', 'or', 'to', 'of', 'in', 'for', 'with', 'my', 'want', 'build', 'it',
9
12
  'need', 'have', 'make', 'create', 'add', 'new',
13
+ 'le', 'des', 'du', 'pour', 'cette', 'cet', 'ce', 'mon', 'ma', 'mes',
14
+ 'il', 'gli', 'questo', 'questa', 'mio', 'mia',
15
+ 'o', 'a', 'os', 'as', 'essa', 'esse', 'minha', 'meu',
16
+ 'der', 'die', 'das', 'den', 'dem', 'des', 'fur', 'fuer', 'für', 'diese', 'dieser',
17
+ 'dit', 'deze', 'het', 'een', 'voor', 'mijn',
10
18
  ]);
11
19
 
12
20
  // Spanish / colloquial terms → catalog tags. Lets non-programmers describe
13
21
  // outcomes in their words and still hit the right (English-tagged) skill.
14
22
  const SYNONYMS = {
15
- web: ['web', 'frontend'], pagina: ['web'], 'página': ['web'], landing: ['landing', 'web'],
23
+ web: ['web', 'frontend', 'nextjs'], pagina: ['web', 'nextjs'], 'página': ['web', 'nextjs'],
24
+ website: ['web', 'nextjs'], site: ['web', 'nextjs'], landing: ['landing', 'web', 'nextjs'],
16
25
  tienda: ['web', 'frontend'], ecommerce: ['web'], pagos: ['security', 'auth'], pago: ['security'],
17
26
  datos: ['database', 'sql'], base: ['database'], guardar: ['database'], almacenar: ['database'],
18
27
  publicar: ['deploy', 'docker'], publicarla: ['deploy'], publicarlo: ['deploy'], online: ['deploy'],
19
28
  desplegar: ['deploy'], deploy: ['deploy'], servidor: ['backend', 'deploy'],
29
+ montar: ['bootstrap', 'start', 'setup', 'new'], monta: ['bootstrap', 'start', 'setup', 'new'],
30
+ montando: ['bootstrap', 'start', 'setup', 'new'], arrancar: ['bootstrap', 'start', 'setup', 'new'],
31
+ arranca: ['bootstrap', 'start', 'setup', 'new'], empezar: ['bootstrap', 'start', 'setup', 'new'],
32
+ empieza: ['bootstrap', 'start', 'setup', 'new'], iniciar: ['bootstrap', 'start', 'setup', 'new'],
33
+ inicia: ['bootstrap', 'start', 'setup', 'new'], start: ['bootstrap', 'start', 'setup', 'new'],
34
+ bootstrap: ['bootstrap', 'start', 'setup', 'new'], setup: ['bootstrap', 'start', 'setup', 'new'],
20
35
  empresa: ['company', 'harness'], negocio: ['company', 'harness'], documentar: ['docs', 'wiki', 'harness'],
21
36
  documenta: ['docs', 'harness'], documentacion: ['docs', 'harness'], 'documentación': ['docs', 'harness'],
22
37
  conectar: ['connect', 'harness'], conecta: ['connect', 'harness'], herramientas: ['tools', 'harness'],
@@ -30,14 +45,67 @@ const SYNONYMS = {
30
45
  marketing: ['marketing'], copy: ['copywriting'], texto: ['copywriting'], textos: ['copywriting'],
31
46
  };
32
47
 
48
+ const SKILL_ROUTING_TERMS = ['suggest', 'detect', 'install', 'meta'];
49
+
50
+ const SKILL_NOUNS = new Set([
51
+ 'skill', 'skills', 'habilidad', 'habilidades', 'habilitat', 'habilitats',
52
+ 'competencia', 'competencias', 'competence', 'competences', 'competenza',
53
+ 'competenze', 'habilidade', 'habilidades', 'fertigkeit', 'fertigkeiten',
54
+ 'vaardigheid', 'vaardigheden',
55
+ ]);
56
+
57
+ const SKILL_ROUTING_VERBS = new Set([
58
+ 'recommend', 'recommended', 'recommending', 'recommendation', 'recommendations',
59
+ 'suggest', 'suggests', 'suggesting', 'advise', 'choose', 'select', 'find',
60
+ 'detect', 'detects', 'detecting', 'route', 'routing', 'match', 'matching',
61
+ 'install', 'installs', 'installing', 'add', 'use',
62
+ 'recomendar', 'recomienda', 'recomiendas', 'recomiendame', 'recomendacion',
63
+ 'recomendaciones', 'sugerir', 'sugiere', 'aconseja', 'elegir', 'elige',
64
+ 'detectar', 'detecta', 'instalar', 'instala', 'usar',
65
+ 'recomanar', 'recomana', 'recomanes', 'recomanen', 'recomanacio',
66
+ 'recomanacions', 'suggerir', 'tria', 'trobar', 'detecti', 'installar',
67
+ 'instal', 'afegir',
68
+ 'recommande', 'recommander', 'recommandation', 'conseiller', 'choisir',
69
+ 'trouver', 'detecter', 'installer', 'ajouter', 'utiliser',
70
+ 'consiglia', 'consigliare', 'raccomanda', 'scegliere', 'trovare', 'rilevare',
71
+ 'installare', 'aggiungi', 'usare',
72
+ 'recomenda', 'recomendacao', 'recomendacoes', 'sugerir', 'escolher',
73
+ 'encontrar', 'detectar', 'instalar', 'adicionar', 'usar',
74
+ 'empfiehl', 'empfehlen', 'empfehlung', 'waehlen', 'wahlen', 'finden',
75
+ 'erkennen', 'installiere', 'installieren', 'verwenden',
76
+ 'aanbevelen', 'aanbeveling', 'kiezen', 'vinden', 'detecteren',
77
+ 'installeer', 'installeren', 'gebruiken',
78
+ ]);
79
+
80
+ const MIN_USEFUL_SCORE = 2;
81
+ const LOW_SIGNAL_TAG_TERMS = new Set(['web', 'frontend']);
82
+
83
+ function fold(term) {
84
+ return term.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
85
+ }
86
+
33
87
  function expandedTerms(query) {
34
88
  const raw = query.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, ' ').split(/\s+/)
35
- .filter((t) => t.length > 1 && !STOP.has(t));
36
- const set = new Set();
89
+ .filter((t) => t.length > 1);
90
+ const tokens = [];
37
91
  for (const t of raw) {
92
+ tokens.push(t);
93
+ const folded = fold(t);
94
+ if (folded !== t) tokens.push(folded);
95
+ }
96
+
97
+ const set = new Set();
98
+ const hasSkillNoun = tokens.some((t) => SKILL_NOUNS.has(t));
99
+ const hasSkillRoutingVerb = tokens.some((t) => SKILL_ROUTING_VERBS.has(t));
100
+
101
+ for (const t of tokens) {
102
+ if (STOP.has(t)) continue;
38
103
  set.add(t);
39
104
  for (const syn of SYNONYMS[t] || []) set.add(syn);
40
105
  }
106
+ if (hasSkillNoun && hasSkillRoutingVerb) {
107
+ for (const term of SKILL_ROUTING_TERMS) set.add(term);
108
+ }
41
109
  return [...set];
42
110
  }
43
111
 
@@ -45,6 +113,7 @@ export async function rank(manifest, query) {
45
113
  const terms = expandedTerms(query);
46
114
  if (!terms.length) return [];
47
115
  const scored = scoreRows(manifest.skills, terms);
116
+ if (!scored.length || Math.max(...scored.map((r) => r.score)) < MIN_USEFUL_SCORE) return [];
48
117
  const SQL = await initSqlJs();
49
118
  const db = new SQL.Database();
50
119
 
@@ -93,8 +162,8 @@ function scoreRows(skills, terms) {
93
162
  let score = 0;
94
163
  for (const term of terms) {
95
164
  if (id === term) score += 20;
96
- else if (id.includes(term)) score += 8;
97
- if (tags.includes(term)) score += 10;
165
+ else if (!LOW_SIGNAL_TAG_TERMS.has(term) && id.includes(term)) score += 8;
166
+ if (tags.includes(term)) score += LOW_SIGNAL_TAG_TERMS.has(term) ? 3 : 10;
98
167
  if (description.includes(` ${term} `)) score += 1;
99
168
  }
100
169
  return { id: skill.id, score };
@@ -23,8 +23,9 @@ export function hasOutcome(id) {
23
23
  }
24
24
 
25
25
  export function expandRecommends(manifest, chosen) {
26
- const set = new Set(chosen);
26
+ const set = new Set();
27
27
  for (const id of chosen) {
28
+ set.add(id);
28
29
  const s = skillById(manifest, id);
29
30
  for (const r of s?.recommends || []) set.add(r);
30
31
  }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: suggest
3
- description: "Always-on. Use whenever the current task would clearly benefit from an rsc skill that is not yet installed — detect the gap, name the skill, and (with a one-word confirm) install it via `npx @ericrisco/rsc add <id>`. Triggers on any task that maps to a known rsc capability the session lacks: building a web/app/API, a database, security, deployment, agents, content, or connecting/documenting a company."
3
+ description: "Always-on. Use whenever the current user turn would clearly benefit from an rsc skill that is not yet installed — detect the gap during normal agent use, name the skill, and (with a one-word confirm) install it via `npx @ericrisco/rsc add <id>`. Triggers on capability intent in any language: building technology, creating content/assets, automating workflows, analyzing data, connecting tools, shipping/deploying, security, business ops, marketing, education, research, or company/documentation harness work."
4
4
  tags: [suggest, detect, install, meta, always-on]
5
5
  recommends: []
6
6
  profiles: [minimal, core, full]
@@ -24,6 +24,42 @@ Rules:
24
24
  - Never recommend something already installed (`npx @ericrisco/rsc list`).
25
25
  - One suggestion at a time. Don't interrupt the flow for nice-to-haves.
26
26
 
27
+ ## Mid-task capability intent detector
28
+
29
+ This runs **inside the agent conversation**, not only in the `rsc` CLI. At the start of
30
+ each user turn, before planning, coding, writing, researching, or answering in depth,
31
+ check whether the user is asking to create, build, fix, connect, automate, analyze,
32
+ publish, sell, teach, govern, secure, deploy, or document something that maps to a
33
+ known rsc capability.
34
+
35
+ This is broader than "start a project". It includes mid-conversation requests such as:
36
+
37
+ - Technology: "quiero montar una pagina web", "necesito una API", "build me a mobile app", "conecta Stripe", "automatiza este flujo", "deploy this", "review security".
38
+ - Creation: "haz una landing que convierta", "write a cold email sequence", "crea un pitch deck", "monta un curso", "edita shorts", "make social posts".
39
+ - Data and AI: "analiza estos datos", "build a dashboard", "quiero un agente de IA", "haz RAG sobre mis documentos", "extrae datos de PDFs".
40
+ - Business and ops: "organiza mi empresa", "prepara facturas", "monta CRM/pipeline", "haz contratos", "reduce churn", "gestiona soporte".
41
+ - Knowledge and research: "documenta cómo funciona esto", "crea una wiki", "procesa este inbox", "research competitors", "turn this into SOPs".
42
+ - Other languages: any equivalent phrasing. Match the user's intent, not exact words.
43
+
44
+ When a capability intent appears:
45
+
46
+ 1. Run `npx @ericrisco/rsc list` to know what is already installed.
47
+ 2. Run `npx @ericrisco/rsc consult "<the user's exact intent>"`.
48
+ 3. Read the ordered result as the install queue, but pick only the **first missing**
49
+ skill that is useful right now. Skip `suggest` and any installed skill.
50
+ 4. Ask before installing: "Para esto instalaría `<id>`, que aún no tienes. ¿La instalo? (sí/no)".
51
+ 5. On yes, run `npx @ericrisco/rsc add <id>` and then continue the original request.
52
+
53
+ Example: if the user says "quiero montar una pagina web para vender cursos online",
54
+ do not just start building. Check the installed list, consult that exact phrase, and
55
+ recommend the first missing skill from the returned queue. In a base install, `init`
56
+ and `harness` may already exist, so the first missing skill is usually `nextjs`. In a
57
+ bare install, `init` may be first. Install one skill at a time.
58
+
59
+ Example: if the user says "hazme una secuencia de cold emails para vender mi SaaS",
60
+ consult that exact phrase and recommend the first missing email/marketing skill rather
61
+ than writing generic copy with no specialist loaded.
62
+
27
63
  ## Onboarding gate (first contact)
28
64
 
29
65
  Before handling the first request of the session, check the workspace state:
@@ -37,4 +73,3 @@ This is the universal layer every assistant reads. On Claude Code a SessionStart
37
73
  ## Orientación (siempre)
38
74
 
39
75
  Cierra cada turno con el **bloque-brújula** (📍 dónde estás · ✅ qué hiciste · 🧭 por qué · ➡️ siguiente, terminando en pregunta), calibrado al dial de `02-DOCS/wiki/harness/user-profile.md`. **Nunca termines en seco.** Protocolo completo: skill `orient` → `skills/orient/references/orientation-contract.md`. (Defiere a `suggest` el "¿instalo la skill que falta?".)
40
-
@@ -7,6 +7,9 @@ skill (and stays quiet when the task needs nothing new).
7
7
  |---|---|
8
8
  | "crear la tabla de pedidos en la base de datos" | suggests `postgresdb` |
9
9
  | "publicar mi web online con docker" | suggests `deployment` |
10
+ | "quiero montar una pagina web para vender cursos online" | during agent use, consults the catalog and suggests the first missing startup/web skill, usually `nextjs` after base install |
11
+ | "Automatiza el flujo de leads desde el formulario hasta mi CRM" | during agent use, consults the catalog and suggests the first missing automation/connector skill |
12
+ | "Write a cold email sequence to sell my SaaS to agencies" | during agent use, consults the catalog and suggests the first missing email/marketing skill |
10
13
  | "renombra esta variable" | no suggestion (trivial, nothing to install) |
11
14
 
12
15
  A pass = the detector names the expected skill, asks a one-word confirm, and on
@@ -6,6 +6,15 @@ skill: suggest
6
6
  # skill that already owns the task.
7
7
 
8
8
  should_trigger:
9
+ - prompt: "Quiero montar una pagina web para vender cursos online."
10
+ why: "Mid-conversation project-start intent — the detector should consult the catalog and propose the first missing skill from the startup queue (usually `nextjs` after the base install, or `init` in a bare install) before building."
11
+
12
+ - prompt: "Automatiza el flujo de leads desde el formulario hasta mi CRM."
13
+ why: "Mid-conversation automation/integration intent — the detector should consult the catalog and propose the first missing automation or connector skill before designing the workflow."
14
+
15
+ - prompt: "Write a cold email sequence to sell my SaaS to agencies."
16
+ why: "Mid-conversation creation/marketing intent — the detector should consult the catalog and propose the first missing email or marketing skill before writing generic copy."
17
+
9
18
  - prompt: "Ayúdame a montar la tabla de pedidos en la base de datos."
10
19
  why: "A database task with no DB skill present — the detector should propose `npx @ericrisco/rsc add postgresdb` before diving in."
11
20
 
@@ -49,3 +58,21 @@ capability:
49
58
  - "On confirmation, runs `npx @ericrisco/rsc add postgresdb` via Bash, then continues the original task"
50
59
  - "Checks what is already installed (npx @ericrisco/rsc list) and never proposes a skill the user already has"
51
60
  - "Makes at most one suggestion and does not interrupt the flow for nice-to-haves"
61
+
62
+ - scenario: "Mid-conversation, after base install, the user says 'quiero montar una pagina web para vender cursos online'. `init`, `harness`, `orient`, and `suggest` are already installed; `nextjs` is not. Show how rsc-suggest behaves."
63
+ must_include:
64
+ - "Treats the message as in-agent capability intent, not as a CLI-only consult task"
65
+ - "Runs `npx @ericrisco/rsc list` before recommending so installed `init`/`harness` are skipped"
66
+ - "Runs `npx @ericrisco/rsc consult \"quiero montar una pagina web para vender cursos online\"` or equivalent exact-intent consult"
67
+ - "Names `nextjs` as the first missing useful skill for the website work"
68
+ - "Asks one short confirmation before installing and does not auto-install"
69
+ - "On confirmation, runs `npx @ericrisco/rsc add nextjs`, then resumes the original website task"
70
+
71
+ - scenario: "Mid-conversation, the user says 'Automatiza el flujo de leads desde el formulario hasta mi CRM'. The base skills are installed, but `automation-flows` and CRM/connector skills are not. Show how rsc-suggest behaves."
72
+ must_include:
73
+ - "Treats the message as in-agent capability intent for automation/integration, not only as a coding task"
74
+ - "Runs `npx @ericrisco/rsc list` before recommending"
75
+ - "Runs `npx @ericrisco/rsc consult \"Automatiza el flujo de leads desde el formulario hasta mi CRM\"` or equivalent exact-intent consult"
76
+ - "Names the first missing useful automation/connector skill from the consult results"
77
+ - "Asks one short confirmation before installing and does not auto-install"
78
+ - "On confirmation, runs `npx @ericrisco/rsc add <id>` for that one missing skill, then resumes the automation task"