@ericrisco/rsc 0.1.5 โ 0.1.6
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 +4 -2
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/scripts/lib/ui.js +62 -15
- package/scripts/rsc.js +3 -18
package/README.md
CHANGED
|
@@ -80,13 +80,15 @@ assistant proposes new skills on its own from then on.
|
|
|
80
80
|
|
|
81
81
|
```
|
|
82
82
|
$ rsc
|
|
83
|
-
|
|
83
|
+
โโโโโโโ โโโโโโโโ โโโโโโโ โ animated gradient wordmark
|
|
84
|
+
โโโโโโโโโโโโโโโโโโโโโโโโ
|
|
85
|
+
โโโโโโโโโโโโโโโโโโโ
|
|
86
|
+
231 skills ยท one CLI ยท zero bloat
|
|
84
87
|
|
|
85
88
|
What do you want to do? โโ move ยท enter select
|
|
86
89
|
โฏ Base install โ the essentials (orient + suggest + harness + init)
|
|
87
90
|
Base + Spec-Driven Development โ specify โ plan โ implement โ ship
|
|
88
91
|
Pick skills by hand, by area
|
|
89
|
-
Describe my project and let rsc choose
|
|
90
92
|
```
|
|
91
93
|
|
|
92
94
|
Pick **by area** and you get a checkbox list โ **โโ to move, space to toggle,
|
package/manifest.json
CHANGED
package/package.json
CHANGED
package/scripts/lib/ui.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { createInterface
|
|
1
|
+
import { createInterface } from 'node:readline/promises';
|
|
2
|
+
import { emitKeypressEvents } from 'node:readline';
|
|
2
3
|
import { stdin, stdout } from 'node:process';
|
|
3
4
|
|
|
5
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
6
|
+
|
|
4
7
|
export async function ask(question) {
|
|
5
8
|
const rl = createInterface({ input: stdin, output: stdout });
|
|
6
9
|
const a = await rl.question(question);
|
|
@@ -33,20 +36,52 @@ const C = {
|
|
|
33
36
|
green: (s) => `\x1b[32m${s}\x1b[39m`,
|
|
34
37
|
};
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
];
|
|
48
|
-
|
|
49
|
-
|
|
39
|
+
const ART = [
|
|
40
|
+
' โโโโโโโ โโโโโโโโ โโโโโโโ',
|
|
41
|
+
' โโโโโโโโโโโโโโโโโโโโโโโโ',
|
|
42
|
+
' โโโโโโโโโโโโโโโโโโโ ',
|
|
43
|
+
' โโโโโโโโโโโโโโโโโโโ ',
|
|
44
|
+
' โโโ โโโโโโโโโโโโโโโโโโโ',
|
|
45
|
+
' โโโ โโโโโโโโโโโ โโโโโโโ',
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
// Vertical truecolor gradient (sky โ violet), phase-shiftable for the wave.
|
|
49
|
+
function gradientLine(text, row, phase) {
|
|
50
|
+
const a = [56, 189, 248]; const b = [167, 139, 250];
|
|
51
|
+
const t = ((row + phase) % ART.length) / (ART.length - 1);
|
|
52
|
+
const r = Math.round(a[0] + (b[0] - a[0]) * t);
|
|
53
|
+
const g = Math.round(a[1] + (b[1] - a[1]) * t);
|
|
54
|
+
const bl = Math.round(a[2] + (b[2] - a[2]) * t);
|
|
55
|
+
return `\x1b[38;2;${r};${g};${bl}m${text}\x1b[39m`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Animated ASCII wordmark โ the "wooow". Static plain text when not a TTY.
|
|
59
|
+
export async function banner() {
|
|
60
|
+
if (!stdout.isTTY) {
|
|
61
|
+
say('');
|
|
62
|
+
for (const l of ART) say(l);
|
|
63
|
+
say(' 231 skills ยท one CLI ยท zero bloat');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const W = Math.max(...ART.map((l) => l.length));
|
|
67
|
+
stdout.write('\x1b[?25l'); // hide cursor
|
|
68
|
+
say('');
|
|
69
|
+
// 1) letters slide in left โ right, column by column
|
|
70
|
+
let first = true;
|
|
71
|
+
for (let w = 1; w <= W; w++) {
|
|
72
|
+
if (!first) stdout.write(`\x1b[${ART.length}A`);
|
|
73
|
+
first = false;
|
|
74
|
+
stdout.write(ART.map((l, i) => `\x1b[2K${gradientLine(l.slice(0, w), i, 0)}`).join('\n') + '\n');
|
|
75
|
+
await sleep(20);
|
|
76
|
+
}
|
|
77
|
+
// 2) one flowing color wave to settle
|
|
78
|
+
for (let phase = 1; phase <= ART.length; phase++) {
|
|
79
|
+
stdout.write(`\x1b[${ART.length}A`);
|
|
80
|
+
stdout.write(ART.map((l, i) => `\x1b[2K${gradientLine(l, i, phase)}`).join('\n') + '\n');
|
|
81
|
+
await sleep(45);
|
|
82
|
+
}
|
|
83
|
+
say(C.dim(' 231 skills ยท one CLI ยท zero bloat'));
|
|
84
|
+
stdout.write('\x1b[?25h'); // show cursor
|
|
50
85
|
}
|
|
51
86
|
|
|
52
87
|
// Repaint a fixed block of lines in place (cursor ends just below the block).
|
|
@@ -142,6 +177,18 @@ export async function select(question, options) {
|
|
|
142
177
|
return byKey ? byKey.key : null;
|
|
143
178
|
}
|
|
144
179
|
|
|
180
|
+
// Public: yes/no confirmation. Arrow TUI when interactive, typed prompt otherwise.
|
|
181
|
+
export async function confirm(question) {
|
|
182
|
+
if (interactive()) {
|
|
183
|
+
const k = await tuiSelect(question, [
|
|
184
|
+
{ key: 'yes', label: 'Yes, install it' },
|
|
185
|
+
{ key: 'no', label: 'No, cancel' },
|
|
186
|
+
]);
|
|
187
|
+
return k === 'yes';
|
|
188
|
+
}
|
|
189
|
+
return yes(await ask(`${question} (yes / no) > `));
|
|
190
|
+
}
|
|
191
|
+
|
|
145
192
|
// Public: multi-select. items: array of strings or { id, label }. Returns ids.
|
|
146
193
|
export async function pickFrom(title, items) {
|
|
147
194
|
const norm = items.map((it) => (typeof it === 'string' ? { id: it, label: it } : it));
|
package/scripts/rsc.js
CHANGED
|
@@ -6,7 +6,7 @@ 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 {
|
|
9
|
+
import { say, select, pickFrom, banner, confirm } from './lib/ui.js';
|
|
10
10
|
import { refreshRegistry, registryStatus } from './lib/registry.js';
|
|
11
11
|
import { DOMAINS } from './lib/domains.js';
|
|
12
12
|
|
|
@@ -45,19 +45,6 @@ async function manualSelect() {
|
|
|
45
45
|
return [...chosen];
|
|
46
46
|
}
|
|
47
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> ');
|
|
51
|
-
const ids = await recommendIds(goal, { labeledOnly: true });
|
|
52
|
-
if (!ids.length) {
|
|
53
|
-
say("I'm not sure. Try describing it in more detail, or pick by hand (option 3).");
|
|
54
|
-
return [];
|
|
55
|
-
}
|
|
56
|
-
say('\nI recommend installing:');
|
|
57
|
-
for (const o of toOutcomes(ids)) say(` โข ${o.label}`);
|
|
58
|
-
return ids;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
48
|
// After installing, remind the user how to actually start โ per IDE โ and that
|
|
62
49
|
// rsc keeps recommending skills as they work. The harness/SDD *init* runs INSIDE
|
|
63
50
|
// the assistant (with the user present), never blindly from this CLI.
|
|
@@ -94,20 +81,18 @@ function printNextSteps(target, ids) {
|
|
|
94
81
|
|
|
95
82
|
async function wizard() {
|
|
96
83
|
const m = loadManifest();
|
|
97
|
-
banner();
|
|
84
|
+
await banner();
|
|
98
85
|
say(' the skill catalog for your assistant (Claude Code ยท Cursor ยท Codex ยท Gemini)\n');
|
|
99
86
|
const choice = await select('What do you want to do?', [
|
|
100
87
|
{ key: 'base', label: 'Base install โ the essentials (orient + suggest + harness + init)' },
|
|
101
88
|
{ key: 'sdd', label: 'Base + Spec-Driven Development โ the specify โ plan โ implement โ ship flow' },
|
|
102
89
|
{ key: 'manual', label: 'Pick skills by hand, by area' },
|
|
103
|
-
{ key: 'describe', label: 'Describe my project and let rsc choose' },
|
|
104
90
|
]);
|
|
105
91
|
|
|
106
92
|
let ids = [];
|
|
107
93
|
if (choice === 'base') ids = skillsForProfile(m, 'minimal');
|
|
108
94
|
else if (choice === 'sdd') ids = skillsForProfile(m, 'core');
|
|
109
95
|
else if (choice === 'manual') ids = await manualSelect();
|
|
110
|
-
else if (choice === 'describe') ids = await describeFlow();
|
|
111
96
|
else { say("Didn't catch that. Run again: npx @ericrisco/rsc"); return; }
|
|
112
97
|
|
|
113
98
|
// The floor is always installed: the compass + the detector.
|
|
@@ -120,7 +105,7 @@ async function wizard() {
|
|
|
120
105
|
const target = detectTarget();
|
|
121
106
|
say(`\nI'll install ${ids.length} skills into ${target}:`);
|
|
122
107
|
say(' ' + ids.join(', '));
|
|
123
|
-
if (!
|
|
108
|
+
if (!(await confirm('Install it?'))) {
|
|
124
109
|
say('No problem. Anytime: npx @ericrisco/rsc');
|
|
125
110
|
return;
|
|
126
111
|
}
|