@ericrisco/rsc 1.0.34 → 1.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/manifest.json +4 -2
- package/package.json +1 -1
- package/scripts/doctor.js +5 -0
- package/scripts/lib/design-identity.js +117 -0
- package/skills/animate/SKILL.md +623 -0
- package/skills/design/SKILL.md +1 -0
- package/skills/design-dna/SKILL.md +19 -0
- package/skills/design-eng/SKILL.md +16 -654
- package/skills/design-loop/SKILL.md +7 -1
- package/skills/review-animations/SKILL.md +51 -0
package/manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.0
|
|
2
|
+
"version": "1.1.0",
|
|
3
3
|
"counts": {
|
|
4
4
|
"skills": 278
|
|
5
5
|
},
|
|
@@ -1409,7 +1409,9 @@
|
|
|
1409
1409
|
"nextjs",
|
|
1410
1410
|
"marketing"
|
|
1411
1411
|
],
|
|
1412
|
-
"profiles": [
|
|
1412
|
+
"profiles": [
|
|
1413
|
+
"full"
|
|
1414
|
+
]
|
|
1413
1415
|
},
|
|
1414
1416
|
{
|
|
1415
1417
|
"id": "design-dna",
|
package/package.json
CHANGED
package/scripts/doctor.js
CHANGED
|
@@ -7,6 +7,7 @@ import { loadManifest } from './lib/manifest.js';
|
|
|
7
7
|
import { listBackups } from './lib/backups.js';
|
|
8
8
|
import { SDD_GATE_TEXT } from '../targets/hook-once.mjs';
|
|
9
9
|
import { isEnabled, checkSello, readSello, countFindings, readEffectiveConfig, validateRiskConfig } from '../targets/sello.mjs';
|
|
10
|
+
import { designIdentity } from './lib/design-identity.js';
|
|
10
11
|
import { countGaps } from './lib/capabilities.js';
|
|
11
12
|
|
|
12
13
|
// The sello's health, surfaced where the user already looks (spec: non-blocking
|
|
@@ -57,6 +58,10 @@ export function doctor({ target, home, cwd }) {
|
|
|
57
58
|
},
|
|
58
59
|
contextBudget: contextBudget({ target, home, cwd }),
|
|
59
60
|
sello: selloStatus(root),
|
|
61
|
+
// Whether this harness has a design identity at all. `design` has always DECLARED that it
|
|
62
|
+
// stops without one; until lib/design-identity.js nothing checked it (P2). Reported here,
|
|
63
|
+
// never nagged about and never blocking: a missing identity is low risk (P7).
|
|
64
|
+
designIdentity: designIdentity(root),
|
|
60
65
|
// An inert gate must never read as an armed one (the same rule the sello follows):
|
|
61
66
|
// the gitmoji guard has a per-project kill switch, so say which state it is in.
|
|
62
67
|
gitmojiGuard: existsSync(join(root, '.rsc', '.no-gitmoji')) ? 'opted-out' : 'armed',
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Does this harness have a design identity, and can we tell?
|
|
2
|
+
//
|
|
3
|
+
// `design` has declared for a long time that without a brand study it STOPS and does not design.
|
|
4
|
+
// Nothing checked it: `grep -rln "wiki/brand" scripts/` returned nothing before this file existed.
|
|
5
|
+
// That is P2 — a rule declared binding with no mechanism is a decorative gate. Spec:
|
|
6
|
+
// 02-DOCS/wiki/sdd/specs/design-area-cleanup.md
|
|
7
|
+
//
|
|
8
|
+
// Three states, and the third is the whole point. A checker that cannot look must never report
|
|
9
|
+
// green: that exact defect was found and fixed in the design-dna style checker two days earlier,
|
|
10
|
+
// where regex probes ran against empty text and returned vacuous passes. Absence of evidence is
|
|
11
|
+
// reported as absence of evidence.
|
|
12
|
+
//
|
|
13
|
+
// Read-only. It never writes: 02-DOCS is untracked (P9) and there is no undo there.
|
|
14
|
+
import { existsSync, lstatSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
|
|
17
|
+
// Where the harness keeps durable design knowledge, and the section in the root instructions file
|
|
18
|
+
// that points at it. Both are the `design`/`harness` convention, not invented here.
|
|
19
|
+
export const BRAND_DIR = join('02-DOCS', 'wiki', 'brand');
|
|
20
|
+
export const BRAND_SECTION = '## Brand & voice';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @returns {{state: 'present'|'missing'|'inconclusive', linked: boolean, articles: string[],
|
|
24
|
+
* reason: string, fix?: string}}
|
|
25
|
+
* `present` — articles exist under the brand dir. `linked` says whether the root
|
|
26
|
+
* instructions file points at them, which is how other skills find it.
|
|
27
|
+
* `missing` — looked, found nothing. Carries the fix (P6: a denial carries its way out).
|
|
28
|
+
* `inconclusive` — could not look. Never conflated with `missing`, and never with green.
|
|
29
|
+
*/
|
|
30
|
+
export function designIdentity(root = process.cwd()) {
|
|
31
|
+
const dir = join(root, BRAND_DIR);
|
|
32
|
+
|
|
33
|
+
let articles;
|
|
34
|
+
try {
|
|
35
|
+
if (!existsSync(dir)) {
|
|
36
|
+
// existsSync FOLLOWS symlinks, so a dangling one reports false and would be filed as "no
|
|
37
|
+
// identity here". It is not absence, it is unreadability — the same conflation this whole
|
|
38
|
+
// area was cleaning up. lstat sees the link itself and tells the two apart.
|
|
39
|
+
let danglingLink = false;
|
|
40
|
+
try { danglingLink = lstatSync(dir).isSymbolicLink(); } catch { /* genuinely absent */ }
|
|
41
|
+
if (danglingLink) {
|
|
42
|
+
return {
|
|
43
|
+
state: 'inconclusive',
|
|
44
|
+
linked: false,
|
|
45
|
+
articles: [],
|
|
46
|
+
reason: `${BRAND_DIR} is a symlink that does not resolve`,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
state: 'missing',
|
|
51
|
+
linked: false,
|
|
52
|
+
articles: [],
|
|
53
|
+
reason: `no ${BRAND_DIR}/ in this harness`,
|
|
54
|
+
fix: 'run `design-loop` to converge on a winner, then `design-dna` to write its record here',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// Not statSync: it follows symlinks, so a symlink here could report on a directory outside the
|
|
58
|
+
// declared zone. Same reason drift-check.js:167 uses dirents.
|
|
59
|
+
if (!statSync(dir).isDirectory()) {
|
|
60
|
+
return {
|
|
61
|
+
state: 'inconclusive',
|
|
62
|
+
linked: false,
|
|
63
|
+
articles: [],
|
|
64
|
+
reason: `${BRAND_DIR} exists but is not a directory`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
articles = readdirSync(dir).filter((f) => f.endsWith('.md') && f !== 'index.md');
|
|
68
|
+
} catch (e) {
|
|
69
|
+
// Unreadable for any reason (permissions, dangling symlink, ELOOP). We did not look, so we
|
|
70
|
+
// cannot say. Reporting `missing` here would invent a finding; reporting green would hide one.
|
|
71
|
+
return {
|
|
72
|
+
state: 'inconclusive',
|
|
73
|
+
linked: false,
|
|
74
|
+
articles: [],
|
|
75
|
+
reason: `cannot read ${BRAND_DIR}: ${e.code || e.message}`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// The link matters on its own: five skills reach the identity through the root instructions file,
|
|
80
|
+
// so an unlinked identity is one they will not find.
|
|
81
|
+
let linked = false;
|
|
82
|
+
const claudeMd = join(root, 'CLAUDE.md');
|
|
83
|
+
if (existsSync(claudeMd)) {
|
|
84
|
+
try {
|
|
85
|
+
linked = readFileSync(claudeMd, 'utf8').includes(BRAND_SECTION);
|
|
86
|
+
} catch {
|
|
87
|
+
// The articles are what we were asked about and we did read them. A CLAUDE.md we could not
|
|
88
|
+
// read makes `linked` unknown, not the whole answer unknown — report it in the reason.
|
|
89
|
+
return {
|
|
90
|
+
state: articles.length ? 'present' : 'missing',
|
|
91
|
+
linked: false,
|
|
92
|
+
articles,
|
|
93
|
+
reason: `${articles.length} article(s), but CLAUDE.md could not be read to confirm the link`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!articles.length) {
|
|
99
|
+
return {
|
|
100
|
+
state: 'missing',
|
|
101
|
+
linked,
|
|
102
|
+
articles: [],
|
|
103
|
+
reason: `${BRAND_DIR}/ exists but holds no identity article`,
|
|
104
|
+
fix: 'run `design-loop` to converge on a winner, then `design-dna` to write its record here',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
state: 'present',
|
|
110
|
+
linked,
|
|
111
|
+
articles,
|
|
112
|
+
reason: linked
|
|
113
|
+
? `${articles.length} article(s), linked from CLAUDE.md`
|
|
114
|
+
: `${articles.length} article(s), but no "${BRAND_SECTION}" section in CLAUDE.md points at them`,
|
|
115
|
+
...(linked ? {} : { fix: `add a "${BRAND_SECTION}" section to CLAUDE.md linking ${BRAND_DIR}/` }),
|
|
116
|
+
};
|
|
117
|
+
}
|