@ericrisco/rsc 0.1.26 → 0.1.27
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/manifest.json +1 -1
- package/package.json +1 -1
- package/scripts/install-apply.js +33 -15
package/manifest.json
CHANGED
package/package.json
CHANGED
package/scripts/install-apply.js
CHANGED
|
@@ -14,22 +14,37 @@ const CLI_VERSION = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8'))
|
|
|
14
14
|
// materialized at — the single, target-agnostic source of truth for "installed
|
|
15
15
|
// skills version" (read by the SessionStart update check too).
|
|
16
16
|
const versionFile = (cwd) => join(cwd, '.rsc', '.version');
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
|
|
18
|
+
// Per-skill base version. A single global `.rsc/.version` cannot represent a
|
|
19
|
+
// partially-refreshed base set, which broke multi-target sync: the first target's pass
|
|
20
|
+
// bumped `.rsc/.version`, so later targets saw "current" and skipped refreshing their
|
|
21
|
+
// exclusive skills' bases. Tracking the version each base was materialized at makes the
|
|
22
|
+
// refresh decision per skill, independent of target ordering. (Absent file → every base
|
|
23
|
+
// is treated as stale and refreshed once, which self-heals installs from before this.)
|
|
24
|
+
const baseVersionsFile = (cwd) => join(cwd, '.rsc', '.base-versions.json');
|
|
25
|
+
function readBaseVersions(cwd) {
|
|
26
|
+
try { return JSON.parse(readFileSync(baseVersionsFile(cwd), 'utf8')); } catch { return {}; }
|
|
27
|
+
}
|
|
28
|
+
function writeBaseVersions(cwd, versions) {
|
|
29
|
+
mkdirSync(dirname(baseVersionsFile(cwd)), { recursive: true });
|
|
30
|
+
writeFileSync(baseVersionsFile(cwd), JSON.stringify(versions, null, 2) + '\n');
|
|
19
31
|
}
|
|
20
32
|
|
|
21
|
-
// Materialize the real skill files into the project-local base.
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
|
|
33
|
+
// Materialize the real skill files into the project-local base. Copied once and reused;
|
|
34
|
+
// when the recorded base version for THIS skill differs from the CLI version, the base is
|
|
35
|
+
// re-copied so a reinstall/sync actually updates content. Tracked per skill (see
|
|
36
|
+
// baseVersionsFile) so a multi-target sync refreshes every target's bases, not just the
|
|
37
|
+
// first target's. Skills are read-only catalog (user customization lives in 02-DOCS), so
|
|
38
|
+
// overwriting on a version change is safe. Mutates `baseVersions` with the new mark.
|
|
39
|
+
function ensureBase(id, cwd, baseVersions) {
|
|
27
40
|
const dest = baseDir(id, cwd);
|
|
28
|
-
|
|
41
|
+
const stale = baseVersions[id] !== CLI_VERSION;
|
|
42
|
+
if (stale && existsSync(dest)) rmSync(dest, { recursive: true, force: true });
|
|
29
43
|
if (!existsSync(dest)) {
|
|
30
44
|
mkdirSync(dirname(dest), { recursive: true });
|
|
31
45
|
cpSync(join(ROOT, 'skills', id), dest, { recursive: true });
|
|
32
46
|
}
|
|
47
|
+
baseVersions[id] = CLI_VERSION;
|
|
33
48
|
return dest;
|
|
34
49
|
}
|
|
35
50
|
|
|
@@ -46,7 +61,7 @@ function generatedHookFiles({ target, cwd }) {
|
|
|
46
61
|
function managedPathsForInstall({ skillIds, target, home, cwd }) {
|
|
47
62
|
const paths = targetPaths(target, home, cwd);
|
|
48
63
|
const plan = planInstall({ skillIds, target, home, cwd });
|
|
49
|
-
const out = [paths.stateFile, versionFile(cwd)];
|
|
64
|
+
const out = [paths.stateFile, versionFile(cwd), baseVersionsFile(cwd)];
|
|
50
65
|
for (const step of plan) {
|
|
51
66
|
if (step.kind === 'skill') {
|
|
52
67
|
out.push(step.to, baseDir(step.id, cwd));
|
|
@@ -64,18 +79,21 @@ export async function applyInstall({ skillIds, target, home, cwd = process.cwd()
|
|
|
64
79
|
if (dryRun) return { dryRun: true, skills: skillIds, paths: managedPaths };
|
|
65
80
|
const state = readState(paths.stateFile);
|
|
66
81
|
const backup = createBackup({ cwd, operation, target, paths: managedPaths, cliVersion: CLI_VERSION });
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
|
|
82
|
+
// Decide base refresh per skill (see baseVersionsFile): a base is re-materialized when
|
|
83
|
+
// its recorded version differs from the CLI version. Robust to multi-target installs/
|
|
84
|
+
// syncs — a single global marker would be bumped by the first target and make later
|
|
85
|
+
// targets skip refreshing their exclusive skills' bases.
|
|
86
|
+
const baseVersions = readBaseVersions(cwd);
|
|
70
87
|
for (const step of plan) {
|
|
71
88
|
if (step.kind === 'skill') {
|
|
72
|
-
const base = ensureBase(step.id, cwd,
|
|
89
|
+
const base = ensureBase(step.id, cwd, baseVersions);
|
|
73
90
|
const files = await writeSkill(target, step.id, base, step.to);
|
|
74
91
|
state.skills[step.id] = { files, base };
|
|
75
92
|
} else if (step.kind === 'hook') {
|
|
76
|
-
await wireHook(target, paths, join(ensureBase('suggest', cwd,
|
|
93
|
+
await wireHook(target, paths, join(ensureBase('suggest', cwd, baseVersions), 'SKILL.md'));
|
|
77
94
|
}
|
|
78
95
|
}
|
|
96
|
+
writeBaseVersions(cwd, baseVersions);
|
|
79
97
|
state.version = CLI_VERSION;
|
|
80
98
|
writeState(paths.stateFile, state);
|
|
81
99
|
mkdirSync(dirname(versionFile(cwd)), { recursive: true });
|