@bongos/core 1.19.545 → 1.19.547
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/.bongos-core.json +39 -34
- package/docs/copy-inventory.md +5 -5
- package/docs/copy-registry.json +7 -7
- package/docs/module-api-changelog.md +4 -0
- package/modules/hall-ui/public/hall-kit.js +76 -41
- package/modules/hall-ui/public/profile-charts.js +8 -2
- package/modules/ui-design/skills/brandkit/SKILL.md +5 -1
- package/modules/ui-design/skills/design-taste-frontend/SKILL.md +5 -1
- package/modules/ui-design/skills/design-taste-frontend-v1/SKILL.md +5 -1
- package/modules/ui-design/skills/gpt-taste/SKILL.md +5 -1
- package/modules/ui-design/skills/high-end-visual-design/SKILL.md +5 -1
- package/modules/ui-design/skills/image-to-code/SKILL.md +5 -1
- package/modules/ui-design/skills/imagegen-frontend-mobile/SKILL.md +5 -1
- package/modules/ui-design/skills/imagegen-frontend-web/SKILL.md +5 -1
- package/modules/ui-design/skills/impeccable/SKILL.md +5 -1
- package/modules/ui-design/skills/industrial-brutalist-ui/SKILL.md +5 -1
- package/modules/ui-design/skills/minimalist-ui/SKILL.md +5 -1
- package/modules/ui-design/skills/redesign-existing-projects/SKILL.md +5 -1
- package/modules/ui-design/skills/stitch-design-taste/SKILL.md +5 -1
- package/modules/ui-design/skills/style/SKILL.md +5 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/fitness.js +10 -3
- package/scripts/gds/seed-strict-versioning-tasks.js +275 -0
- package/scripts/gds/skill-lint.js +43 -9
- package/src/module-api.js +1 -1
- package/tests/hall_kit_charts.mjs +59 -0
- package/tests/skill_lint.mjs +51 -2
- package/tests/ui_design_skills.mjs +12 -2
package/tests/skill_lint.mjs
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import { strict as assert } from 'node:assert';
|
|
4
4
|
import { test } from 'node:test';
|
|
5
5
|
import { createRequire } from 'node:module';
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import os from 'node:os';
|
|
6
8
|
import path from 'node:path';
|
|
7
9
|
import { fileURLToPath } from 'node:url';
|
|
8
10
|
|
|
@@ -58,11 +60,58 @@ test('no frontmatter at all is an error', () => {
|
|
|
58
60
|
assert.equal(run('# Title\n\nBody.\n').errors.length, 1);
|
|
59
61
|
});
|
|
60
62
|
|
|
61
|
-
test('the hook path filter matches
|
|
63
|
+
test('the hook path filter matches a SKILL.md in either home — .claude/skills/ or a module (task 1003587)', () => {
|
|
62
64
|
assert.ok(lint.isSkillMd('C:\\repo\\.claude\\skills\\dev\\SKILL.md'));
|
|
63
65
|
assert.ok(lint.isSkillMd('/repo/.claude/skills/dev/SKILL.md'));
|
|
66
|
+
// The module copy is the file a builder EDITS; .claude/skills/ holds the generated
|
|
67
|
+
// copy, so a hook that only watched the tracked root never saw the real edit.
|
|
68
|
+
assert.ok(lint.isSkillMd('/repo/modules/ui-design/skills/dev/SKILL.md'));
|
|
69
|
+
assert.ok(lint.isSkillMd('C:\\repo\\modules\\ui-design\\skills\\vendor\\dev\\SKILL.md'));
|
|
64
70
|
assert.ok(!lint.isSkillMd('/repo/.claude/skills/dev/notes.md'));
|
|
65
|
-
assert.ok(!lint.isSkillMd('/repo/modules/ui-design/skills/dev/
|
|
71
|
+
assert.ok(!lint.isSkillMd('/repo/modules/ui-design/skills/dev/notes.md'));
|
|
72
|
+
assert.ok(!lint.isSkillMd('/repo/docs/skills/dev/SKILL.md'));
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// A fixture root, because a real dev checkout may or may not have materialized its
|
|
76
|
+
// module skills into .claude/skills/ (claude-materialize.js, task 1003330) and the
|
|
77
|
+
// lister's contract must hold either way.
|
|
78
|
+
function fixtureRoot(files) {
|
|
79
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'skill-lint-'));
|
|
80
|
+
for (const [rel, body] of Object.entries(files)) {
|
|
81
|
+
const abs = path.join(root, rel);
|
|
82
|
+
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
83
|
+
fs.writeFileSync(abs, body);
|
|
84
|
+
}
|
|
85
|
+
return root;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
test('the lister reaches module-owned skills, and a core copy of the same name wins (task 1003587)', () => {
|
|
89
|
+
const skill = fm('>-\n A description long enough for the harness to route on, written as a block scalar.');
|
|
90
|
+
const root = fixtureRoot({
|
|
91
|
+
'.claude/skills/only-core/SKILL.md': skill,
|
|
92
|
+
'.claude/skills/both/SKILL.md': skill,
|
|
93
|
+
'modules/ui-design/skills/both/SKILL.md': skill,
|
|
94
|
+
'modules/ui-design/skills/only-module/SKILL.md': skill,
|
|
95
|
+
'modules/ui-design/skills/vendor/vendored/SKILL.md': skill,
|
|
96
|
+
'modules/ui-design/skills/README.md': '# not a skill\n',
|
|
97
|
+
});
|
|
98
|
+
const files = lint.listSkillFiles(root).map((f) => path.relative(root, f).replace(/\\/g, '/'));
|
|
99
|
+
assert.deepEqual(files.sort(), [
|
|
100
|
+
'.claude/skills/both/SKILL.md',
|
|
101
|
+
'.claude/skills/only-core/SKILL.md',
|
|
102
|
+
'modules/ui-design/skills/only-module/SKILL.md',
|
|
103
|
+
'modules/ui-design/skills/vendor/vendored/SKILL.md',
|
|
104
|
+
], 'a module skill is linted at its source; a name already in .claude/skills/ is not counted twice');
|
|
105
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('every module-owned skill this repo ships parses too — the 14 that did not on 2026-09-04 (task 1003587)', () => {
|
|
109
|
+
const dir = path.join(ROOT, 'modules', 'ui-design', 'skills');
|
|
110
|
+
const files = lint.listSkillFiles(ROOT).filter((f) => f.startsWith(dir) || fs.existsSync(path.join(dir, path.basename(path.dirname(f)), 'SKILL.md')));
|
|
111
|
+
assert.ok(files.length >= 14, `the ui-design module ships 14+ skills; found ${files.length}`);
|
|
112
|
+
const report = lint.lintSkills(files);
|
|
113
|
+
const broken = report.results.filter((r) => r.errors.length).map((r) => path.relative(ROOT, r.file) + ': ' + r.errors.join('; '));
|
|
114
|
+
assert.deepEqual(broken, [], broken.join('\n'));
|
|
66
115
|
});
|
|
67
116
|
|
|
68
117
|
test('every committed skill parses (the ten that did not on 2026-09-03 must stay fixed)', () => {
|
|
@@ -34,14 +34,24 @@ const policy = readJson(path.join(SKILLS, 'policy.json'));
|
|
|
34
34
|
const manifest = readJson(path.join(MODULE, 'module.json'));
|
|
35
35
|
const materialize = require(path.join(ROOT, 'scripts', 'gds', 'claude-materialize.js'));
|
|
36
36
|
|
|
37
|
-
// Minimal YAML-frontmatter reader: `key: value` lines between the first two `---`
|
|
37
|
+
// Minimal YAML-frontmatter reader: `key: value` lines between the first two `---`
|
|
38
|
+
// fences, plus folded block scalars (`key: >-` then the text indented), which is the
|
|
39
|
+
// only safe way to write a description carrying a colon-space (task 1003587) — a plain
|
|
40
|
+
// scalar with ": " in it makes Claude Code's YAML reader drop EVERY field silently.
|
|
38
41
|
function frontmatter(text) {
|
|
39
42
|
const m = text.match(/^---\n([\s\S]*?)\n---\n/);
|
|
40
43
|
const out = {};
|
|
41
44
|
if (!m) return out;
|
|
45
|
+
let block = null; // the key whose folded value the indented lines below belong to
|
|
42
46
|
for (const line of m[1].split('\n')) {
|
|
43
47
|
const kv = line.match(/^([A-Za-z][A-Za-z0-9_-]*):\s*(.*)$/);
|
|
44
|
-
if (kv)
|
|
48
|
+
if (kv) {
|
|
49
|
+
const v = kv[2].trim();
|
|
50
|
+
block = /^[>|][+-]?\d*$/.test(v) ? kv[1] : null;
|
|
51
|
+
out[kv[1]] = block ? '' : v.replace(/^["']|["']$/g, '').trim();
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (block && /^\s+\S/.test(line)) out[block] = (out[block] ? out[block] + ' ' : '') + line.trim();
|
|
45
55
|
}
|
|
46
56
|
return out;
|
|
47
57
|
}
|