@ericrisco/rsc 2.0.17 → 2.0.18
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 +3 -0
- package/manifest.json +1 -1
- package/package.json +5 -1
- package/scripts/install-apply.js +8 -0
- package/scripts/lib/backups.js +13 -0
- package/scripts/rsc.js +17 -2
package/README.md
CHANGED
|
@@ -71,6 +71,9 @@ accompaniment, project kind and goal **before it writes anything**, reads only t
|
|
|
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.
|
|
74
|
+
If the project already has a skill of its own with a catalog name, the plan lists it under
|
|
75
|
+
**Your own skills this plan replaces** — tell the user before accepting; renaming their folder and
|
|
76
|
+
regenerating the plan keeps theirs.
|
|
74
77
|
|
|
75
78
|
For a non-interactive agent, collect the answers and preview the same plan explicitly:
|
|
76
79
|
|
package/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ericrisco/rsc",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.18",
|
|
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": {
|
|
@@ -97,6 +97,10 @@
|
|
|
97
97
|
{
|
|
98
98
|
"fixedIn": "2.0.17",
|
|
99
99
|
"symptom": "Re-accepting an onboarding plan erased what init had learned about you, reset your explanation level, or dropped an assistant you added later"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"fixedIn": "2.0.18",
|
|
103
|
+
"symptom": "Onboarding silently replaced a skill you wrote yourself that had the same name as one of rsc's (review, plan, debug…)"
|
|
100
104
|
}
|
|
101
105
|
]
|
|
102
106
|
}
|
package/scripts/install-apply.js
CHANGED
|
@@ -422,6 +422,14 @@ export function collisions({ cwd = process.cwd(), target, home, skillIds = [] })
|
|
|
422
422
|
return skillIds.filter((id) => !managed.has(id) && existsSync(paths.skillDir(id)));
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
+
// The same test as `collisions`, across every target of a plan, as the paths the user would recognise.
|
|
426
|
+
// `onboard` puts these in the plan itself, so the id the user accepts is an id that names them.
|
|
427
|
+
export function ownSkillPaths({ cwd = process.cwd(), targets = [], skillIds = [], home }) {
|
|
428
|
+
const found = targets.flatMap((target) => collisions({ cwd, target, home, skillIds })
|
|
429
|
+
.map((id) => relative(cwd, targetPaths(target, home, cwd).skillDir(id)).split(sep).join('/')));
|
|
430
|
+
return [...new Set(found)].sort();
|
|
431
|
+
}
|
|
432
|
+
|
|
425
433
|
export function listInstalled({ target, home, cwd = process.cwd() }) {
|
|
426
434
|
const paths = targetPaths(target, home, cwd);
|
|
427
435
|
return Object.keys(readState(paths.stateFile).skills);
|
package/scripts/lib/backups.js
CHANGED
|
@@ -40,6 +40,19 @@ export function createBackup({ cwd = process.cwd(), operation, target, paths, cl
|
|
|
40
40
|
return manifest;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
// Where the newest snapshot kept a path, relative to the project — what to tell someone whose own work
|
|
44
|
+
// was just replaced, so recovering it is a copy they can find rather than a restore they never heard of.
|
|
45
|
+
// A transaction snapshot is the fallback: it is the install's rollback, not the user's copy.
|
|
46
|
+
export function whereKept({ cwd = process.cwd(), relPath }) {
|
|
47
|
+
const backups = listBackups({ cwd });
|
|
48
|
+
const ordered = [...backups.filter((b) => !/transaction/.test(b.operation)), ...backups.filter((b) => /transaction/.test(b.operation))];
|
|
49
|
+
for (const b of ordered) {
|
|
50
|
+
const kept = join(backupsDir(cwd), b.id, 'files', relPath);
|
|
51
|
+
if (existsSync(kept)) return relative(cwd, kept).split(sep).join('/');
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
43
56
|
export function listBackups({ cwd = process.cwd() } = {}) {
|
|
44
57
|
const dir = backupsDir(cwd);
|
|
45
58
|
if (!existsSync(dir)) return [];
|
package/scripts/rsc.js
CHANGED
|
@@ -4,14 +4,14 @@ import { detectTarget, installedTargets, resolveTargets, TARGETS } from '../targ
|
|
|
4
4
|
import { detectRepo } from './detect-repo.js';
|
|
5
5
|
import { rank } from './consult.js';
|
|
6
6
|
import { expandRecommends, toOutcomes, hasOutcome } from './lib/recommend.js';
|
|
7
|
-
import { applyInstall, listInstalled, listInstalledAgents, listInstalledCommands, uninstall, syncInstalled, purge, collisions } from './install-apply.js';
|
|
7
|
+
import { applyInstall, listInstalled, listInstalledAgents, listInstalledCommands, uninstall, syncInstalled, purge, collisions, ownSkillPaths } from './install-apply.js';
|
|
8
8
|
import { stackAgentNames } from '../targets/agents.js';
|
|
9
9
|
import { doctor } from './doctor.js';
|
|
10
10
|
import { ask, say, select, pickFrom, banner, confirm, isInteractive } from './lib/ui.js';
|
|
11
11
|
import { refreshRegistry, registryStatus } from './lib/registry.js';
|
|
12
12
|
import { audit, writeAuditReport } from './audit.js';
|
|
13
13
|
import { DOMAINS } from './lib/domains.js';
|
|
14
|
-
import { listBackups, restoreBackup } from './lib/backups.js';
|
|
14
|
+
import { listBackups, restoreBackup, whereKept } from './lib/backups.js';
|
|
15
15
|
import { runUpgrade } from './lib/upgrade.js';
|
|
16
16
|
import { diagnose, repair } from './lib/repair.js';
|
|
17
17
|
import { DEFAULT_SKILL_FLOOR, withDefaultSkillFloor } from './lib/default-skill-floor.js';
|
|
@@ -142,6 +142,12 @@ function renderPlan(plan, planId) {
|
|
|
142
142
|
for (const decision of plan.decisions.filter((d) => d.state === 'excluded')) say(` × ${decision.kind}/${decision.id} — ${decision.reason}`);
|
|
143
143
|
say('Managed paths:');
|
|
144
144
|
for (const path of plan.governedPaths) say(` ${path}`);
|
|
145
|
+
if (plan.replacesUserSkills?.length) {
|
|
146
|
+
say('Your own skills this plan replaces:');
|
|
147
|
+
for (const path of plan.replacesUserSkills) say(` ! ${path} — already here and not put by rsc; the catalog skill of the same name takes its place, and yours is kept in .rsc/backups/`);
|
|
148
|
+
say(' To keep yours, rename its folder (for example review → review-own) and generate the plan again.');
|
|
149
|
+
say(' AGENT: name each of these to the user before accepting, and ask whether to replace or rename.');
|
|
150
|
+
}
|
|
145
151
|
if (plan.evidence.parentHarness) say(`Parent harness detected at ${plan.evidence.parentHarness}; it is not inherited by this plan.`);
|
|
146
152
|
const pieces = [
|
|
147
153
|
'npx @ericrisco/rsc@latest onboard',
|
|
@@ -201,6 +207,11 @@ async function runOnboarding(targets) {
|
|
|
201
207
|
return;
|
|
202
208
|
}
|
|
203
209
|
const plan = buildOnboardingPlan(record, scanProject(process.cwd()));
|
|
210
|
+
// #275 — a folder where a catalog skill goes that rsc did not put is the user's own work. It goes
|
|
211
|
+
// INTO the plan, so the id they accept names it: an id made before that folder existed no longer
|
|
212
|
+
// applies, and accepting is never consent to something the plan did not show.
|
|
213
|
+
const replacesUserSkills = ownSkillPaths({ cwd: process.cwd(), targets: plan.policy.targets, skillIds: plan.policy.skills });
|
|
214
|
+
if (replacesUserSkills.length) plan.replacesUserSkills = replacesUserSkills;
|
|
204
215
|
const planId = identifyPlan(plan);
|
|
205
216
|
const accepted = flag('accept-plan');
|
|
206
217
|
if (!accepted) {
|
|
@@ -229,6 +240,10 @@ async function runOnboarding(targets) {
|
|
|
229
240
|
} catch (error) {
|
|
230
241
|
say(`RSC_SKELETON_FAILED ${error.message}`);
|
|
231
242
|
}
|
|
243
|
+
for (const path of plan.replacesUserSkills || []) {
|
|
244
|
+
const kept = whereKept({ cwd: process.cwd(), relPath: path });
|
|
245
|
+
if (kept) say(`Your previous ${path} is kept in ${kept}`);
|
|
246
|
+
}
|
|
232
247
|
emitHarnessReadiness(plan, planId);
|
|
233
248
|
}
|
|
234
249
|
|