@ericrisco/rsc 2.0.19 → 2.1.0
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 +40 -17
- package/manifest.json +18 -85
- package/package.json +9 -1
- package/scripts/consult.js +8 -8
- package/scripts/doctor.js +8 -2
- package/scripts/install-apply.js +40 -11
- package/scripts/lib/default-skill-floor.js +5 -4
- package/scripts/lib/divergence.js +7 -3
- package/scripts/lib/domains.js +2 -2
- package/scripts/lib/onboarding-apply.js +43 -26
- package/scripts/lib/onboarding.js +5 -5
- package/scripts/lib/recommend.js +1 -1
- package/scripts/lib/repair.js +4 -2
- package/scripts/lib/retired-skills.js +20 -0
- package/scripts/rsc.js +25 -19
- package/scripts/skill-behavior-rubric.md +1 -1
- package/scripts/skill-migration-brief.md +3 -3
- package/skills/analyze/SKILL.md +3 -8
- package/skills/analyze/evals/cases.yaml +1 -1
- package/skills/author-skill/SKILL.md +3 -3
- package/skills/author-skill/evals/cases.yaml +1 -1
- package/skills/author-skill/references/rsc-conventions.md +1 -1
- package/skills/clarify/SKILL.md +7 -14
- package/skills/clarify/evals/cases.yaml +2 -2
- package/skills/code-review/SKILL.md +1 -1
- package/skills/constitution/SKILL.md +10 -13
- package/skills/constitution/evals/cases.yaml +3 -3
- package/skills/continuous-learning/SKILL.md +1 -1
- package/skills/continuous-learning/evals/cases.yaml +1 -1
- package/skills/debug/SKILL.md +8 -12
- package/skills/debug/evals/cases.yaml +1 -1
- package/skills/decision-challenge/SKILL.md +1 -1
- package/skills/deprecation/SKILL.md +1 -1
- package/skills/ftd/SKILL.md +1 -3
- package/skills/ftd/evals/cases.yaml +1 -1
- package/skills/harness/SKILL.md +10 -9
- package/skills/harness/references/claude-md-template.md +1 -1
- package/skills/harness/references/wiki-protocol.md +5 -6
- package/skills/idea-refinement/SKILL.md +1 -1
- package/skills/implement/SKILL.md +14 -18
- package/skills/implement/evals/cases.yaml +1 -1
- package/skills/init/SKILL.md +26 -44
- package/skills/init/evals/README.md +3 -3
- package/skills/init/evals/cases.yaml +7 -7
- package/skills/init/references/accompaniment-and-profile.md +22 -40
- package/skills/init/references/discovery.md +2 -2
- package/skills/init/references/recommend-skills.md +5 -5
- package/skills/init/scripts/verify.sh +3 -7
- package/skills/knowledge-ops/SKILL.md +1 -1
- package/skills/orient/SKILL.md +52 -41
- package/skills/orient/evals/README.md +11 -9
- package/skills/orient/evals/cases.yaml +39 -29
- package/skills/orient/references/explain-ladder.md +93 -0
- package/skills/orient/references/orientation-contract.md +70 -18
- package/skills/parallel/SKILL.md +1 -1
- package/skills/parallel/evals/cases.yaml +2 -2
- package/skills/plan/SKILL.md +9 -13
- package/skills/review/SKILL.md +5 -8
- package/skills/roast-me/SKILL.md +1 -1
- package/skills/sdd/SKILL.md +10 -14
- package/skills/sdd/evals/README.md +2 -2
- package/skills/sdd/evals/cases.yaml +4 -4
- package/skills/sdd/references/model-routing.md +5 -12
- package/skills/sdd-init/SKILL.md +3 -3
- package/skills/ship/SKILL.md +6 -11
- package/skills/simplify-code/SKILL.md +1 -1
- package/skills/skill-scout/SKILL.md +1 -1
- package/skills/source-grounded-development/SKILL.md +1 -1
- package/skills/specify/SKILL.md +13 -14
- package/skills/specify/evals/cases.yaml +1 -1
- package/skills/specify/references/eliciting-requirements.md +5 -5
- package/skills/suggest/SKILL.md +3 -9
- package/skills/suggest/evals/cases.yaml +1 -1
- package/skills/tasks/SKILL.md +6 -7
- package/skills/tasks/evals/cases.yaml +1 -1
- package/skills/unslop/SKILL.md +39 -10
- package/skills/unslop/evals/README.md +7 -7
- package/skills/unslop/evals/cases.yaml +10 -9
- package/skills/verify/SKILL.md +5 -8
- package/skills/verify/evals/cases.yaml +1 -1
- package/skills/worktrees/SKILL.md +14 -18
- package/skills/worktrees/evals/cases.yaml +1 -1
- package/targets/clone-bootstrap.mjs +4 -1
- package/targets/session-start.mjs +1 -1
- package/skills/bro/SKILL.md +0 -76
- package/skills/bro/evals/README.md +0 -22
- package/skills/bro/evals/cases.yaml +0 -62
- package/skills/eli5/SKILL.md +0 -63
- package/skills/eli5/evals/README.md +0 -22
- package/skills/eli5/evals/cases.yaml +0 -54
- package/skills/show-me/SKILL.md +0 -140
- package/skills/show-me/evals/README.md +0 -23
- package/skills/show-me/evals/cases.yaml +0 -61
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/@ericrisco/rsc)
|
|
8
8
|
[](https://www.npmjs.com/package/@ericrisco/rsc)
|
|
9
|
-
[](#the-catalog)
|
|
10
10
|
[](LICENSE)
|
|
11
11
|
[](https://github.com/ericrisco/rsc-harness/stargazers)
|
|
12
12
|
|
|
@@ -42,8 +42,8 @@ Open standards already cover individual parts — [Agent Skills](https://agentsk
|
|
|
42
42
|
which complete harness this project needs. rsc-harness does that job through one guided flow:
|
|
43
43
|
|
|
44
44
|
1. **You state the outcome.** No skill, hook or MCP vocabulary required.
|
|
45
|
-
2. **The wizard reads only the project root** and asks technical
|
|
46
|
-
|
|
45
|
+
2. **The wizard reads only the project root** and asks how technical to talk, project kind, goal and
|
|
46
|
+
assistants.
|
|
47
47
|
3. **A proportional plan explains every choice.** Selected, deferred and excluded pieces all have a
|
|
48
48
|
reason. Nothing writes before you accept the exact plan.
|
|
49
49
|
4. **The result is verified.** Shared sources prevent duplication; knowledge loads progressively;
|
|
@@ -66,8 +66,8 @@ project they're equipping** — not inside a clone of this repo (see the note un
|
|
|
66
66
|
npx @ericrisco/rsc@latest onboard
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
That launches the binding plain-language onboarding. It asks the user
|
|
70
|
-
|
|
69
|
+
That launches the binding plain-language onboarding. It asks the user how technical to talk
|
|
70
|
+
(technical or with analogies), project kind and goal **before it writes anything**, reads only the selected project
|
|
71
71
|
root, and shows every selected or deferred skill, agent and hook with its reason. Relay those
|
|
72
72
|
questions to the user; never invent or default their answers. The first pass writes nothing and
|
|
73
73
|
prints a SHA-256 plan id. Only rerun with `--accept-plan <id>` after the user accepts that exact plan.
|
|
@@ -78,11 +78,11 @@ regenerating the plan keeps theirs.
|
|
|
78
78
|
For a non-interactive agent, collect the answers and preview the same plan explicitly:
|
|
79
79
|
|
|
80
80
|
```bash
|
|
81
|
-
npx @ericrisco/rsc@latest onboard --technical-level
|
|
81
|
+
npx @ericrisco/rsc@latest onboard --technical-level technical \
|
|
82
82
|
--project-kind software --goal "small compound-interest website" \
|
|
83
83
|
--software-scope small --target codex
|
|
84
84
|
# After the user accepts the printed plan:
|
|
85
|
-
npx @ericrisco/rsc@latest onboard --technical-level
|
|
85
|
+
npx @ericrisco/rsc@latest onboard --technical-level technical \
|
|
86
86
|
--project-kind software --goal "small compound-interest website" \
|
|
87
87
|
--software-scope small --target codex --accept-plan PRINTED_SHA256_ID
|
|
88
88
|
```
|
|
@@ -131,6 +131,24 @@ is which one you land in by default, and who decides.
|
|
|
131
131
|
|
|
132
132
|
---
|
|
133
133
|
|
|
134
|
+
## 🗣️ It talks so you understand
|
|
135
|
+
|
|
136
|
+
Agents assume you saw their reasoning, and they write too much. rsc fixes the voice, not the reader.
|
|
137
|
+
|
|
138
|
+
- **Short sentences, one idea each.** The style comes from ASD-STE100, the controlled writing of
|
|
139
|
+
aircraft maintenance manuals, applied at about 80% and in your language.
|
|
140
|
+
- **Every answer stands alone.** No "that fix" or "the second commit": the answer names the thing,
|
|
141
|
+
with the minimum context to understand it.
|
|
142
|
+
- **One question at install:** technical, or with analogies. That is the only setting.
|
|
143
|
+
- **A ladder when you do not follow.** Text first. Then one diagram. Then one HTML page. A video
|
|
144
|
+
only if you ask for it.
|
|
145
|
+
|
|
146
|
+
Every turn still closes with the compass: where you are, and the next step as a question. The voice
|
|
147
|
+
lives in `orient`, which every harness installs. Text you send to other people (emails, posts,
|
|
148
|
+
READMEs) goes through `unslop` instead: it sounds like a person and carries no AI tells.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
134
152
|
## Why this exists
|
|
135
153
|
|
|
136
154
|
An improvised harness tends to grow by accumulation: more files, more context and more rules, with
|
|
@@ -211,7 +229,7 @@ cd ~/rsc-skills && npm install && npm link
|
|
|
211
229
|
> `node scripts/rsc.js …`, the `npm link` above, or pin the published build with
|
|
212
230
|
> `npx @ericrisco/rsc@latest …`.
|
|
213
231
|
|
|
214
|
-
The first run asks **
|
|
232
|
+
The first run asks **one** question about conversation: technical, or with analogies. Then what the
|
|
215
233
|
project is for, its goal and the assistants to target. It then presents the complete plan. A small
|
|
216
234
|
website can defer SDD, agents and code guards; an operations harness does not receive them merely
|
|
217
235
|
because it lives in a repository. Deferred components record the evidence that would make rsc
|
|
@@ -235,10 +253,9 @@ $ rsc onboard
|
|
|
235
253
|
██████╗ ███████╗ ██████╗ ← animated gradient wordmark
|
|
236
254
|
██╔══██╗██╔════╝██╔════╝
|
|
237
255
|
██████╔╝███████╗██║
|
|
238
|
-
|
|
256
|
+
270 skills · one CLI · zero bloat
|
|
239
257
|
|
|
240
|
-
How
|
|
241
|
-
How much accompaniment do you want?
|
|
258
|
+
How should I talk to you?
|
|
242
259
|
What are you building or running?
|
|
243
260
|
What do you want this project to achieve?
|
|
244
261
|
|
|
@@ -404,6 +421,11 @@ Running through `npx` (no global install)? There's nothing to upgrade —
|
|
|
404
421
|
`npx @ericrisco/rsc@latest` always fetches the latest published catalog; just run
|
|
405
422
|
`rsc sync` afterwards if the project already has skills installed.
|
|
406
423
|
|
|
424
|
+
**From 2.0.x to 2.1.0:** `eli5` and `show-me` became part of `orient`, and `bro` became part of
|
|
425
|
+
`unslop`. The update removes the three old skills (only the copies rsc installed) and installs the
|
|
426
|
+
ones that replace them. An `accompaniment_level` line in your profile is ignored: the only setting
|
|
427
|
+
now is `technical_level`.
|
|
428
|
+
|
|
407
429
|
Every sync snapshots the project first, so a bad update is always reversible:
|
|
408
430
|
|
|
409
431
|
```bash
|
|
@@ -434,20 +456,21 @@ just asks in plain language.
|
|
|
434
456
|
|
|
435
457
|
## The catalog
|
|
436
458
|
|
|
437
|
-
|
|
459
|
+
270 skills, grouped by what you're trying to do. Click any skill to read its
|
|
438
460
|
`SKILL.md`. It fires on its own when a task matches.
|
|
439
461
|
|
|
440
462
|
### 🧭 Core & control plane
|
|
441
463
|
The front door and the workspace brain.
|
|
442
464
|
|
|
443
|
-
[init](skills/init/) · [harness](skills/harness/) · [orient](skills/orient/) · [suggest](skills/suggest/) · [
|
|
465
|
+
[init](skills/init/) · [harness](skills/harness/) · [orient](skills/orient/) · [suggest](skills/suggest/) · [unslop](skills/unslop/) · [author-skill](skills/author-skill/) · [sdd-init](skills/sdd-init/)
|
|
444
466
|
|
|
445
467
|
> **harness** is the Karpathy *chaos→knowledge* engine — a `01-TOOLS/` layer (one
|
|
446
468
|
> folder per provider, each with a working `test_connection`) and a `02-DOCS/`
|
|
447
469
|
> self-improving wiki. It governs software *or* a whole company. **orient** is the
|
|
448
|
-
> always-on
|
|
449
|
-
>
|
|
450
|
-
>
|
|
470
|
+
> always-on voice: short STE-style sentences, every answer understandable on its own,
|
|
471
|
+
> technical or with analogies, and a diagram or an HTML page when you do not follow.
|
|
472
|
+
> **unslop** is installed with every profile and makes text you send to others sound
|
|
473
|
+
> like a person, with no AI tells.
|
|
451
474
|
|
|
452
475
|
<img src="https://raw.githubusercontent.com/ericrisco/rsc-harness/main/site/company-brain.svg" alt="The rsc company brain as a 3D knowledge graph: loose material drifts in from the inbox on the left and is absorbed into the bright green wiki cluster, whose brightest hubs are its .base views; dim clusters behind are raw sources and the agent's own captured worklog; an amber cluster is gaps.md, what the wiki knows it is missing." width="960">
|
|
453
476
|
|
|
@@ -571,7 +594,7 @@ Three engines + engine-agnostic disciplines. Every engine skill pins the current
|
|
|
571
594
|
|
|
572
595
|
### 🧠 Knowledge & meta
|
|
573
596
|
|
|
574
|
-
[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/) · [roast-me](skills/roast-me/) · [
|
|
597
|
+
[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/) · [roast-me](skills/roast-me/) · [fable-operator](skills/fable-operator/)
|
|
575
598
|
|
|
576
599
|
---
|
|
577
600
|
|
package/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.0
|
|
2
|
+
"version": "2.1.0",
|
|
3
3
|
"counts": {
|
|
4
|
-
"skills":
|
|
4
|
+
"skills": 270,
|
|
5
5
|
"agents": 33,
|
|
6
6
|
"commands": 53,
|
|
7
7
|
"fixedCommands": 20
|
|
@@ -461,30 +461,6 @@
|
|
|
461
461
|
],
|
|
462
462
|
"profiles": []
|
|
463
463
|
},
|
|
464
|
-
{
|
|
465
|
-
"id": "bro",
|
|
466
|
-
"description": "Use whenever the user asks to make an answer or draft sound human, natural, plain-spoken or less AI-written — including a terse bro after the last response. Rewrites or drafts in the same language while preserving meaning, facts and channel. NOT a reusable brand voice (that is `brand-voice`), NOT translation (that is `translation-l10n`).",
|
|
467
|
-
"tags": [
|
|
468
|
-
"bro",
|
|
469
|
-
"human-writing",
|
|
470
|
-
"humano",
|
|
471
|
-
"plain-language",
|
|
472
|
-
"natural-language",
|
|
473
|
-
"rewrite",
|
|
474
|
-
"no-jargon"
|
|
475
|
-
],
|
|
476
|
-
"recommends": [
|
|
477
|
-
"unslop",
|
|
478
|
-
"brand-voice",
|
|
479
|
-
"technical-writing",
|
|
480
|
-
"translation-l10n"
|
|
481
|
-
],
|
|
482
|
-
"profiles": [
|
|
483
|
-
"minimal",
|
|
484
|
-
"core",
|
|
485
|
-
"full"
|
|
486
|
-
]
|
|
487
|
-
},
|
|
488
464
|
{
|
|
489
465
|
"id": "building-agents",
|
|
490
466
|
"description": "Use when building or restructuring an LLM agent — provider adapter, tool calling, structured output, RAG, agent loop, eval gate, cost routing, tracing, MCP server — model-agnostic across OpenAI/Anthropic/Gemini/OSS so a model swap is a config change. NOT vector-store SQL alone (that is `postgresdb`) or service deployment (that is `deployment`).",
|
|
@@ -1599,29 +1575,6 @@
|
|
|
1599
1575
|
],
|
|
1600
1576
|
"profiles": []
|
|
1601
1577
|
},
|
|
1602
|
-
{
|
|
1603
|
-
"id": "eli5",
|
|
1604
|
-
"description": "Use when a topic must be explained from zero to someone who knows nothing about it: one page, big pictures, very few words, everyday analogies, no jargon and nothing assumed. NOT the smallest in-conversation visual for someone already following (that is `show-me`), NOT a course with exercises (that is `course-builder`).",
|
|
1605
|
-
"tags": [
|
|
1606
|
-
"eli5",
|
|
1607
|
-
"explain",
|
|
1608
|
-
"beginner",
|
|
1609
|
-
"simple",
|
|
1610
|
-
"analogy",
|
|
1611
|
-
"no-jargon",
|
|
1612
|
-
"explicame"
|
|
1613
|
-
],
|
|
1614
|
-
"recommends": [
|
|
1615
|
-
"show-me",
|
|
1616
|
-
"technical-writing",
|
|
1617
|
-
"course-builder"
|
|
1618
|
-
],
|
|
1619
|
-
"profiles": [
|
|
1620
|
-
"minimal",
|
|
1621
|
-
"core",
|
|
1622
|
-
"full"
|
|
1623
|
-
]
|
|
1624
|
-
},
|
|
1625
1578
|
{
|
|
1626
1579
|
"id": "elixir",
|
|
1627
1580
|
"description": "Use when writing or refactoring Elixir/OTP on the BEAM — GenServers, supervision trees and restart strategies, pattern matching, mix projects and releases — or when processes misbehave (restart loops, mailbox growth, call timeouts). NOT a Phoenix web app, LiveView, Ecto or channels (that is `phoenix`).",
|
|
@@ -2475,7 +2428,7 @@
|
|
|
2475
2428
|
},
|
|
2476
2429
|
{
|
|
2477
2430
|
"id": "init",
|
|
2478
|
-
"description": "Use when starting from nothing or pointing rsc at an existing project — the front door.
|
|
2431
|
+
"description": "Use when starting from nothing or pointing rsc at an existing project — the front door. First asks one question — technical terms or analogies (analogies by default) — then discovers what the user wants to build or govern (any stack, or a non-code harness: company/ops, research, knowledge, content), writes the profile to 02-DOCS, and installs the skills discovery justified. NOT the scaffolder (that is `harness`), NOT a stack skill.",
|
|
2479
2432
|
"tags": [
|
|
2480
2433
|
"init",
|
|
2481
2434
|
"bootstrap",
|
|
@@ -3448,13 +3401,16 @@
|
|
|
3448
3401
|
},
|
|
3449
3402
|
{
|
|
3450
3403
|
"id": "orient",
|
|
3451
|
-
"description": "Always-on.
|
|
3404
|
+
"description": "Always-on. How the harness talks to the person: STE-style short sentences, every answer understandable on its own, technical or with analogies by the profile, and a ladder (text → diagram → HTML page → video on request) when the person does not follow. Closes every turn with the brújula. Fires on 'no entiendo', 'explícamelo', 'enséñame', 'dibújamelo', 'explain it from zero', '¿dónde estamos?'. NOT the missing-skill installer (that is `suggest`), NOT a text the user sends to others (that is `unslop`).",
|
|
3452
3405
|
"tags": [
|
|
3453
3406
|
"orient",
|
|
3454
|
-
"
|
|
3407
|
+
"voice",
|
|
3408
|
+
"ste",
|
|
3409
|
+
"explain",
|
|
3410
|
+
"diagram",
|
|
3411
|
+
"eli5",
|
|
3412
|
+
"show-me",
|
|
3455
3413
|
"compass",
|
|
3456
|
-
"dial",
|
|
3457
|
-
"meta",
|
|
3458
3414
|
"always-on"
|
|
3459
3415
|
],
|
|
3460
3416
|
"recommends": [],
|
|
@@ -4316,7 +4272,7 @@
|
|
|
4316
4272
|
},
|
|
4317
4273
|
{
|
|
4318
4274
|
"id": "sdd",
|
|
4319
|
-
"description": "Use when you want a disciplined, spec-driven path from a feature idea to shipped, verified software — the SDD dispatcher and front door, in any language. States the method, reads the
|
|
4275
|
+
"description": "Use when you want a disciplined, spec-driven path from a feature idea to shipped, verified software — the SDD dispatcher and front door, in any language. States the method, reads the user's register from 02-DOCS, and routes to the right phase: constitution -> specify -> clarify -> plan -> tasks -> analyze -> implement -> verify -> review -> ship, with debug / worktrees / parallel on demand. Use it to start a feature, to govern the whole flow, or when unsure which phase you are in. NOT a single phase itself (it dispatches), NOT the workspace harness (that is `harness`), NOT a stack build skill.",
|
|
4320
4276
|
"tags": [
|
|
4321
4277
|
"sdd",
|
|
4322
4278
|
"spec",
|
|
@@ -4542,31 +4498,6 @@
|
|
|
4542
4498
|
],
|
|
4543
4499
|
"profiles": []
|
|
4544
4500
|
},
|
|
4545
|
-
{
|
|
4546
|
-
"id": "show-me",
|
|
4547
|
-
"description": "Use when the user needs to see how something works instead of reading about it: picks the smallest visual that carries the point — pseudocode, call tree, file tree, component tree, Mermaid, a shaped diff, or one HTML page. NOT visual identity (that is `design-loop`), NOT slides (that is `presentations`), NOT a from-zero explainer (that is `eli5`).",
|
|
4548
|
-
"tags": [
|
|
4549
|
-
"show-me",
|
|
4550
|
-
"explain",
|
|
4551
|
-
"diagram",
|
|
4552
|
-
"visual",
|
|
4553
|
-
"mermaid",
|
|
4554
|
-
"diff",
|
|
4555
|
-
"call-tree",
|
|
4556
|
-
"ensename"
|
|
4557
|
-
],
|
|
4558
|
-
"recommends": [
|
|
4559
|
-
"eli5",
|
|
4560
|
-
"presentations",
|
|
4561
|
-
"technical-writing",
|
|
4562
|
-
"codebase-onboarding"
|
|
4563
|
-
],
|
|
4564
|
-
"profiles": [
|
|
4565
|
-
"minimal",
|
|
4566
|
-
"core",
|
|
4567
|
-
"full"
|
|
4568
|
-
]
|
|
4569
|
-
},
|
|
4570
4501
|
{
|
|
4571
4502
|
"id": "simplify-code",
|
|
4572
4503
|
"description": "Use when correct working code is unnecessarily hard to read, change or verify and needs behaviour-preserving simplification in a bounded scope. Characterizes the observable contract, removes accidental complexity one concept at a time and proves equivalence. NOT fixing a failure (`debug`), NOT read-only diff judgment (`review`/`code-review`).",
|
|
@@ -5264,18 +5195,20 @@
|
|
|
5264
5195
|
},
|
|
5265
5196
|
{
|
|
5266
5197
|
"id": "unslop",
|
|
5267
|
-
"description": "Use when a
|
|
5198
|
+
"description": "Use when a text that leaves the session (email, post, README, message, landing copy) must sound like a person and carry no AI tells: rewrites it in the user's language fitted to the channel and relationship, and audits it against a named catalogue of tells (puffery, `not just X but Y`, rule of three, em dashes, filler, hedging). Fires on 'bro', 'hazlo más humano', 'que no suene a IA', 'más natural', 'unslop this'. NOT how the harness talks to the user (that is `orient`), NOT a reusable voice guide (that is `brand-voice`).",
|
|
5268
5199
|
"tags": [
|
|
5269
5200
|
"unslop",
|
|
5201
|
+
"human-writing",
|
|
5202
|
+
"humano",
|
|
5203
|
+
"natural-language",
|
|
5204
|
+
"plain-language",
|
|
5270
5205
|
"ai-tells",
|
|
5271
5206
|
"editing",
|
|
5272
|
-
"
|
|
5273
|
-
"
|
|
5274
|
-
"publish-check",
|
|
5207
|
+
"rewrite",
|
|
5208
|
+
"bro",
|
|
5275
5209
|
"revisar-texto"
|
|
5276
5210
|
],
|
|
5277
5211
|
"recommends": [
|
|
5278
|
-
"bro",
|
|
5279
5212
|
"brand-voice",
|
|
5280
5213
|
"technical-writing"
|
|
5281
5214
|
],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ericrisco/rsc",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "A guided harness builder that turns your goal into a minimal, verified AI-agent setup using strict best practices.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -105,6 +105,14 @@
|
|
|
105
105
|
{
|
|
106
106
|
"fixedIn": "2.0.19",
|
|
107
107
|
"symptom": "rsc doctor reported missing pieces or an unwired hook but still exited with 0, so a CI or an editor extension could not tell"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"fixedIn": "2.0.20",
|
|
111
|
+
"symptom": "In a project whose path contains a space, rsc doctor reported every hook script as missing and exited 1, though the scripts were there and the hooks ran"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"fixedIn": "2.1.0",
|
|
115
|
+
"symptom": "Answers were long and leaned on hidden reasoning, and onboarding asked an accompaniment level few could answer; now: short stand-alone sentences, one question"
|
|
108
116
|
}
|
|
109
117
|
]
|
|
110
118
|
}
|
package/scripts/consult.js
CHANGED
|
@@ -43,14 +43,14 @@ const SYNONYMS = {
|
|
|
43
43
|
presentacion: ['presentations'], 'presentación': ['presentations'], diapositivas: ['slides'],
|
|
44
44
|
curso: ['course', 'teaching'], 'enseñar': ['teaching'], ensenar: ['teaching'],
|
|
45
45
|
marketing: ['marketing'], copy: ['copywriting'], texto: ['copywriting'], textos: ['copywriting'],
|
|
46
|
-
human: ['
|
|
47
|
-
humano: ['
|
|
48
|
-
humana: ['
|
|
49
|
-
huma: ['
|
|
50
|
-
natural: ['
|
|
51
|
-
robotico: ['
|
|
52
|
-
robotic: ['
|
|
53
|
-
jerga: ['
|
|
46
|
+
human: ['unslop', 'human-writing', 'natural-language', 'plain-language'],
|
|
47
|
+
humano: ['unslop', 'human-writing', 'natural-language', 'plain-language'],
|
|
48
|
+
humana: ['unslop', 'human-writing', 'natural-language', 'plain-language'],
|
|
49
|
+
huma: ['unslop', 'human-writing', 'natural-language', 'plain-language'],
|
|
50
|
+
natural: ['unslop', 'human-writing', 'natural-language', 'plain-language'],
|
|
51
|
+
robotico: ['unslop', 'human-writing'], robotica: ['unslop', 'human-writing'],
|
|
52
|
+
robotic: ['unslop', 'human-writing'], roboticamente: ['unslop', 'human-writing'],
|
|
53
|
+
jerga: ['unslop', 'plain-language', 'no-jargon'], jargon: ['unslop', 'plain-language', 'no-jargon'],
|
|
54
54
|
// Cross-cutting workflow vocabulary. These map multilingual/colloquial
|
|
55
55
|
// wording to the exact compound tags used by the catalog.
|
|
56
56
|
challenge: ['adversarial-review', 'decision-quality', 'preflight'], challenged: ['adversarial-review'],
|
package/scripts/doctor.js
CHANGED
|
@@ -65,6 +65,10 @@ function selloStatus(root) {
|
|
|
65
65
|
//
|
|
66
66
|
// Hookless targets get an empty list by construction: their always-on surface is a
|
|
67
67
|
// markdown block with no script behind it, so there is nothing that can go missing.
|
|
68
|
+
// A quoted span counts as a path only if it starts like one and holds no shell operator: a whole
|
|
69
|
+
// `sh -c '…'` snippet is not a path, and reading it as one reports a present script missing.
|
|
70
|
+
const SCRIPT_RE =
|
|
71
|
+
/"((?:[A-Za-z]:)?[\\/~][^"&;|]*[\\/]\.rsc[\\/][^"\s]+?\.mjs)"|'((?:[A-Za-z]:)?[\\/~][^'&;|]*[\\/]\.rsc[\\/][^'\s]+?\.mjs)'|([^"'\s]*[\\/]\.rsc[\\/][^"'\s]+\.mjs)/g;
|
|
68
72
|
export function missingHookScripts({ target, home = homedir(), cwd = process.cwd() } = {}) {
|
|
69
73
|
if (HOOKLESS_TARGETS.has(target)) return [];
|
|
70
74
|
const file = targetPaths(target, home, cwd).hookTarget;
|
|
@@ -84,8 +88,10 @@ export function missingHookScripts({ target, home = homedir(), cwd = process.cwd
|
|
|
84
88
|
const expand = (c) => c.split('${CLAUDE_PROJECT_DIR}').join(cwd);
|
|
85
89
|
for (const raw of commands) {
|
|
86
90
|
const cmd = expand(raw);
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
// Quoted forms first: a project path may hold a space, and stopping at whitespace kept only
|
|
92
|
+
// the tail (`entreno/.rsc/x.mjs`), which then resolved against the cwd and read as missing (#283).
|
|
93
|
+
for (const m of cmd.matchAll(SCRIPT_RE)) {
|
|
94
|
+
const script = m[1] ?? m[2] ?? m[3];
|
|
89
95
|
if (!existsSync(script)) seen.add(script);
|
|
90
96
|
}
|
|
91
97
|
}
|
package/scripts/install-apply.js
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { projectOptOuts } from '../targets/opt-outs.js';
|
|
14
14
|
import { readState, writeState } from './lib/state.js';
|
|
15
15
|
import { withDefaultSkillFloor } from './lib/default-skill-floor.js';
|
|
16
|
+
import { isRetired, replaceRetired } from './lib/retired-skills.js';
|
|
16
17
|
import { readManifest, writeManifest } from './lib/manifest-file.js';
|
|
17
18
|
import { createBackup } from './lib/backups.js';
|
|
18
19
|
import {
|
|
@@ -196,7 +197,9 @@ export function recordInManifest({ cwd, target, skillIds, agentIds = [], catalog
|
|
|
196
197
|
// `dropTarget` is the one exception — a MOVE, where leaving the old one declared would
|
|
197
198
|
// have every clone rebuild a harness that was deliberately abandoned.
|
|
198
199
|
targets: union(prev.targets, [target]).filter((t) => t !== dropTarget),
|
|
199
|
-
|
|
200
|
+
// Through the retirement map: the living list must stop naming a skill the catalog no longer
|
|
201
|
+
// ships, or every clone would go looking for it.
|
|
202
|
+
skills: replaceRetired(union(prev.skills, skillIds)).sort(),
|
|
200
203
|
agents: union(prev.agents, agentIds),
|
|
201
204
|
ownSkills: prev.ownSkills || [],
|
|
202
205
|
catalogVersion,
|
|
@@ -215,7 +218,23 @@ export async function applyInstall({ skillIds = [], agentIds = [], target, home,
|
|
|
215
218
|
const fromScratch = !existsSync(join(cwd, '.rsc'));
|
|
216
219
|
const paths = targetPaths(target, home, cwd);
|
|
217
220
|
const plan = planInstall({ skillIds, target, home, cwd, hooks: policy?.alwaysOn !== false });
|
|
218
|
-
|
|
221
|
+
// What this pass prunes, decided BEFORE the backup so the backup holds it: a skill link this
|
|
222
|
+
// target's state proves is ours and that is no longer wanted (always for a retired skill, and for
|
|
223
|
+
// any skill when a plan governs), and the shared base of a retired skill once no other assistant
|
|
224
|
+
// in this project still records it. Taking a base another assistant links to would leave that
|
|
225
|
+
// assistant dangling until its own sync.
|
|
226
|
+
const priorState = readState(paths.stateFile);
|
|
227
|
+
const prunedSkills = Object.keys(priorState.skills || {})
|
|
228
|
+
.filter((id) => !skillIds.includes(id) && (policy || isRetired(id)));
|
|
229
|
+
const stillRecordedElsewhere = (id) => TARGET_IDS.some((other) => other !== target
|
|
230
|
+
&& Object.hasOwn(readState(targetPaths(other, home, cwd).stateFile).skills || {}, id));
|
|
231
|
+
const prunedBases = Object.keys(readBaseVersions(cwd))
|
|
232
|
+
.filter((id) => isRetired(id) && !skillIds.includes(id) && !stillRecordedElsewhere(id));
|
|
233
|
+
const managedPaths = [...new Set([
|
|
234
|
+
...managedPathsForInstall({ skillIds, agentIds, target, home, cwd, policy }),
|
|
235
|
+
...prunedSkills.map((id) => paths.skillDir(id)),
|
|
236
|
+
...prunedBases.map((id) => baseDir(id, cwd)),
|
|
237
|
+
])];
|
|
219
238
|
if (dryRun) return { dryRun: true, skills: skillIds, agents: agentIds, paths: managedPaths };
|
|
220
239
|
// Rebuild the declared decisions first, so everything below (the agent tier especially) reads
|
|
221
240
|
// the state the team declared rather than the default it would otherwise assume.
|
|
@@ -227,12 +246,17 @@ export async function applyInstall({ skillIds = [], agentIds = [], target, home,
|
|
|
227
246
|
// syncs — a single global marker would be bumped by the first target and make later
|
|
228
247
|
// targets skip refreshing their exclusive skills' bases.
|
|
229
248
|
const baseVersions = readBaseVersions(cwd);
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
249
|
+
// A retired skill is pruned even without a governing plan: nothing can install it any more, so
|
|
250
|
+
// keeping it would only leave a dead copy behind. Ownership is proven by the state file — a
|
|
251
|
+
// same-named directory rsc never recorded is somebody's own work and is not touched.
|
|
252
|
+
for (const id of prunedSkills) {
|
|
253
|
+
rmSync(paths.skillDir(id), { recursive: true, force: true });
|
|
254
|
+
delete state.skills[id];
|
|
255
|
+
}
|
|
256
|
+
// Its shared base too, proven ours by the base-version ledger rather than by its name.
|
|
257
|
+
for (const id of prunedBases) {
|
|
258
|
+
rmSync(baseDir(id, cwd), { recursive: true, force: true });
|
|
259
|
+
delete baseVersions[id];
|
|
236
260
|
}
|
|
237
261
|
for (const step of plan) {
|
|
238
262
|
if (step.kind === 'skill') {
|
|
@@ -528,7 +552,9 @@ export function removeTargetInstall({ target, home, cwd = process.cwd() }) {
|
|
|
528
552
|
];
|
|
529
553
|
const declaredLocationsAreExact = Object.entries(state.skills || {}).every(([id, entry]) =>
|
|
530
554
|
/^[a-z0-9][a-z0-9._-]*$/i.test(id)
|
|
531
|
-
|
|
555
|
+
// A retired id no longer has a catalog directory, but `paths.skillDir` below still proves where
|
|
556
|
+
// it lives — refusing it made every re-onboard that drops an old assistant loop on recovery.
|
|
557
|
+
&& (existsSync(join(ROOT, 'skills', id)) || isRetired(id))
|
|
532
558
|
&& (entry.files || []).every((file) => resolve(file) === resolve(paths.skillDir(id))))
|
|
533
559
|
&& (state.agents || []).every((name) => {
|
|
534
560
|
if (!allAgentNames().includes(name)) return false;
|
|
@@ -607,9 +633,12 @@ export async function syncInstalled({ target, home, cwd = process.cwd(), dryRun
|
|
|
607
633
|
// then rebuilt from a governed list that predated it and pruned the difference, recursively.
|
|
608
634
|
// Reported by a user and reproduced 2026-09-17. The receipt stays untouched, because it is
|
|
609
635
|
// hash-checked against what the user accepted and is not a log of what happened since.
|
|
610
|
-
|
|
636
|
+
//
|
|
637
|
+
// Read through the retirement map: a declaration older than the retirement of eli5, show-me and
|
|
638
|
+
// bro names skills the catalog no longer ships, and the receipt that names them is never rewritten.
|
|
639
|
+
const declaredRaw = replaceRetired(governedSkills
|
|
611
640
|
? [...new Set([...governedSkills, ...(manifest?.skills || [])])].sort()
|
|
612
|
-
: (ids.length ? ids : (manifest?.skills || []));
|
|
641
|
+
: (ids.length ? ids : (manifest?.skills || []))).sort();
|
|
613
642
|
// A declaration is frozen at the moment it was written, so a skill the catalog later makes
|
|
614
643
|
// mandatory never reaches an existing harness: `add` and `install` apply the floor, sync did
|
|
615
644
|
// not, and sync is the only one an upgrade runs. 2.0.0 shipped the three-lane decisor inside
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
// Skills every installation path equips, even when the user picks a profile,
|
|
2
2
|
// adds one skill directly, or browses the catalog by hand.
|
|
3
3
|
//
|
|
4
|
-
// `orient` and `suggest` are runtime always-on. `
|
|
5
|
-
// but remains conditionally loaded: its description routes
|
|
6
|
-
// without paying for its body on unrelated turns.
|
|
4
|
+
// `orient` and `suggest` are runtime always-on. `unslop` is installed by default
|
|
5
|
+
// but remains conditionally loaded: its description routes "rewrite this for
|
|
6
|
+
// other people" asks without paying for its body on unrelated turns. It took the
|
|
7
|
+
// place of `bro`, which was retired into it (see retired-skills.js).
|
|
7
8
|
//
|
|
8
9
|
// `ftd` joined in 2.0.1 by the same criterion as `suggest`: the always-on decisor
|
|
9
10
|
// routes ordinary work to `../ftd/SKILL.md`, so a harness without it has a default
|
|
10
11
|
// lane that points at nothing. Membership of every profile was not enough — a
|
|
11
12
|
// declaration written before the skill existed never gains it (see `syncInstalled`).
|
|
12
|
-
export const DEFAULT_SKILL_FLOOR = Object.freeze(['orient', 'suggest', '
|
|
13
|
+
export const DEFAULT_SKILL_FLOOR = Object.freeze(['orient', 'suggest', 'unslop', 'ftd']);
|
|
13
14
|
|
|
14
15
|
export function withDefaultSkillFloor(skillIds = []) {
|
|
15
16
|
return [...new Set([...DEFAULT_SKILL_FLOOR, ...skillIds])];
|
|
@@ -3,6 +3,7 @@ import { readState } from './state.js';
|
|
|
3
3
|
import { targetPaths } from '../../targets/index.js';
|
|
4
4
|
import { existsSync } from 'node:fs';
|
|
5
5
|
import { DEFAULT_SKILL_FLOOR } from './default-skill-floor.js';
|
|
6
|
+
import { replaceRetired } from './retired-skills.js';
|
|
6
7
|
|
|
7
8
|
// What the team declared versus what is on this machine.
|
|
8
9
|
//
|
|
@@ -28,13 +29,16 @@ export function divergence({ cwd = process.cwd(), target, home } = {}) {
|
|
|
28
29
|
|
|
29
30
|
const paths = targetPaths(target, home, cwd);
|
|
30
31
|
const installed = new Set(Object.keys(readState(paths.stateFile).skills || {}));
|
|
32
|
+
// A declaration written before eli5, show-me and bro were retired names them; it is read as
|
|
33
|
+
// their successors, so a teammate who has not synced yet is not told to install a ghost.
|
|
34
|
+
const declared = replaceRetired(manifest.skills || []);
|
|
31
35
|
|
|
32
36
|
return {
|
|
33
|
-
missing:
|
|
34
|
-
extra: [...installed].filter((id) => !
|
|
37
|
+
missing: declared.filter((id) => !installed.has(id)),
|
|
38
|
+
extra: [...installed].filter((id) => !declared.includes(id)),
|
|
35
39
|
ownMissing: (manifest.ownSkills || []).filter((name) => !existsSync(paths.skillDir(name))),
|
|
36
40
|
// Read off the declaration, not off the disk: a skill installed by hand into a harness
|
|
37
41
|
// that still does not declare it is one sync away from being pruned again.
|
|
38
|
-
floorMissing: DEFAULT_SKILL_FLOOR.filter((id) => !
|
|
42
|
+
floorMissing: DEFAULT_SKILL_FLOOR.filter((id) => !declared.includes(id)),
|
|
39
43
|
};
|
|
40
44
|
}
|
package/scripts/lib/domains.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// catalog grows deliberately, never silently.
|
|
5
5
|
|
|
6
6
|
export const DOMAINS = [
|
|
7
|
-
{ title: 'Core & control plane', ids: ['init', 'harness', 'orient', 'suggest', '
|
|
7
|
+
{ title: 'Core & control plane', ids: ['init', 'harness', 'orient', 'suggest', 'unslop', 'author-skill', 'sdd-init'] },
|
|
8
8
|
{ title: 'Spec-Driven Development', ids: ['ftd', 'sdd', 'constitution', 'idea-refinement', 'specify', 'clarify', 'plan', 'tasks', 'analyze', 'decision-challenge', 'implement', 'source-grounded-development', 'verify', 'review', 'simplify-code', 'ship', 'debug', 'worktrees', 'parallel'] },
|
|
9
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
10
|
{ title: 'Raise & model money', ids: ['pitch-deck', 'investor-materials', 'financial-model', 'fundraising', 'unit-economics', 'grants'] },
|
|
@@ -26,7 +26,7 @@ export const DOMAINS = [
|
|
|
26
26
|
{ 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'] },
|
|
27
27
|
{ title: 'Motion & interface craft', ids: ['motion-craft', 'ui-engineering', 'variant-explorer'] },
|
|
28
28
|
{ title: 'Design & content craft', ids: ['design-loop', 'design', 'design-dna', 'presentations', 'course-storytelling', 'course-builder', 'technical-writing', 'translation-l10n'] },
|
|
29
|
-
{ title: 'Knowledge & meta', ids: ['knowledge-ops', 'codebase-onboarding', 'research-ops', 'decision-records', 'continuous-learning', 'skill-scout', 'context-budget', 'roast-me', '
|
|
29
|
+
{ title: 'Knowledge & meta', ids: ['knowledge-ops', 'codebase-onboarding', 'research-ops', 'decision-records', 'continuous-learning', 'skill-scout', 'context-budget', 'roast-me', 'fable-operator'] },
|
|
30
30
|
];
|
|
31
31
|
|
|
32
32
|
export function allDomainIds() {
|