@natjswenson/press 0.6.1 → 0.7.1
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/CHANGELOG.md +57 -0
- package/SKILL.md +9 -3
- package/bin/press.js +1 -1
- package/lib/check.mjs +14 -4
- package/lib/emit.mjs +28 -2
- package/lib/propagate.mjs +28 -3
- package/lib/region.mjs +16 -1
- package/lib/targets.mjs +29 -1
- package/package.json +1 -1
- package/targets.json +116 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,61 @@ All notable changes to the **press** skill are documented here.
|
|
|
5
5
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
|
|
6
6
|
this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.7.1] - 2026-08-01
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Declaring a new target broke every consumer's CI.** 0.7.0 added README
|
|
13
|
+
targets for the external repos without seeding the regions, so the moment a
|
|
14
|
+
consumer's pin reached 0.7.0 its `check` failed `missing`. `propagate` now
|
|
15
|
+
**seeds** a region the consumer does not have yet, in the same pull request
|
|
16
|
+
that raises the pin — the two are one change, not two. Only targets declaring
|
|
17
|
+
an `init` anchor are seedable, so a deliberately deleted region is still
|
|
18
|
+
reported rather than silently recreated.
|
|
19
|
+
- **`check` now enforces the recorded version, not just the values.** In-repo
|
|
20
|
+
regions sat at `v0.1.0` through six releases with a green check, because only
|
|
21
|
+
content was compared. One version per consumer means `check` has to say so.
|
|
22
|
+
- New `init.insertAfter` anchor, so a seeded README badge lands under the title
|
|
23
|
+
instead of at the bottom of the file.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- Version badges for the in-repo skills' READMEs (`city-report`, `resume`,
|
|
28
|
+
`ghostwriter`, `ghostwriter-x`, `devlog`), and `press` itself is now listed in
|
|
29
|
+
the monorepo's root README skill table.
|
|
30
|
+
- `natejswenson` has no README target on purpose: its README is generated by
|
|
31
|
+
`build_svg.py` and a region there would be clobbered on the next build.
|
|
32
|
+
|
|
33
|
+
## [0.7.0] - 2026-08-01
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- **`version-badge` emitter** — a generated `press:version` line for a
|
|
38
|
+
consumer's README. Until now a repo's press version appeared only in a CI pin
|
|
39
|
+
and a comment marker, neither of which anyone reads when landing on the repo.
|
|
40
|
+
- Emitters now receive a small run context (`{ version }`), kept separate from
|
|
41
|
+
target `params` because it is a property of the invocation, not the config.
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
|
|
45
|
+
- **Propagation now covers every long-lived branch, not just `dev`.** Landing on
|
|
46
|
+
the integration branch alone left `main` — the branch that actually ships —
|
|
47
|
+
stale until someone remembered to promote, and made the next promotion carry
|
|
48
|
+
an unrelated brand diff. Branch names are suffixed per base so the two pushes
|
|
49
|
+
cannot collide.
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- **Target selection could match the wrong repository.** Selection was by file
|
|
54
|
+
presence alone, and `README.md` exists everywhere — so a README target
|
|
55
|
+
selected inside *any* checkout and would have been compared against the wrong
|
|
56
|
+
file. Selection now checks the checkout's own `origin` remote against the
|
|
57
|
+
target's `github`/`repo`, falling back to presence only when there is no
|
|
58
|
+
remote.
|
|
59
|
+
- Goldens pin the **shape** of each emitter's output using a fixed placeholder
|
|
60
|
+
version. Using the real release would rewrite every golden on every release
|
|
61
|
+
and turn a drift detector into noise.
|
|
62
|
+
|
|
8
63
|
## [0.6.1] - 2026-08-01
|
|
9
64
|
|
|
10
65
|
### Fixed
|
|
@@ -264,6 +319,8 @@ source of truth and a CI drift gate.
|
|
|
264
319
|
rasterised cards, whose eyebrow legitimately runs at `.16em`. Scoped rather
|
|
265
320
|
than waived, and caught by linting the real shipped corpus.
|
|
266
321
|
|
|
322
|
+
[0.7.1]: https://github.com/natejswenson/claude-skills/releases/tag/press-v0.7.1
|
|
323
|
+
[0.7.0]: https://github.com/natejswenson/claude-skills/releases/tag/press-v0.7.0
|
|
267
324
|
[0.6.1]: https://github.com/natejswenson/claude-skills/releases/tag/press-v0.6.1
|
|
268
325
|
[0.6.0]: https://github.com/natejswenson/claude-skills/releases/tag/press-v0.6.0
|
|
269
326
|
[0.5.1]: https://github.com/natejswenson/claude-skills/releases/tag/press-v0.5.1
|
package/SKILL.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: press
|
|
3
3
|
description: The one brand system for everything produced in Claude — design tokens, the visual laws, the run-presentation contract, and the universal voice core. Use when composing or restyling any artifact (report, résumé, card, cover, PDF, HTML page, chart), when asked about brand colors, fonts, the accent law or "the PRESS look", when adding a new skill that renders anything, or when a brand value needs to change everywhere at once. Also handles "check the brand is in sync", "why do my colors differ", and onboarding a new consumer repo.
|
|
4
4
|
user_invocable: true
|
|
5
|
-
version: 0.
|
|
5
|
+
version: 0.7.1
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# /press — the brand system
|
|
@@ -121,8 +121,14 @@ is that the PR says which kind it is:
|
|
|
121
121
|
| **Brand values moved** | `press vX — BRAND VALUES CHANGED` | read the diff as a design change |
|
|
122
122
|
| **Version only** | `adopt press vX` | one line; nothing renders differently |
|
|
123
123
|
|
|
124
|
-
`.github/workflows/press-propagate.yml` runs this on every release and weekly
|
|
125
|
-
|
|
124
|
+
`.github/workflows/press-propagate.yml` runs this on every release and weekly.
|
|
125
|
+
It propagates to **every long-lived branch** a consumer has — `dev` *and* the
|
|
126
|
+
default branch — because landing on `dev` alone leaves the branch that actually
|
|
127
|
+
ships stale until someone remembers to promote, and makes the next promotion
|
|
128
|
+
carry an unrelated brand diff.
|
|
129
|
+
|
|
130
|
+
Each consumer's README carries a generated `press:version` line, so the release
|
|
131
|
+
a repo is on is visible without opening a workflow file.
|
|
126
132
|
|
|
127
133
|
## Changing a brand value
|
|
128
134
|
|
package/bin/press.js
CHANGED
|
@@ -86,7 +86,7 @@ function cmdEmit({ tokens, root, values }) {
|
|
|
86
86
|
const rows = [];
|
|
87
87
|
for (const target of targets) {
|
|
88
88
|
const path = targetPath(target, root);
|
|
89
|
-
const body = emitBody(tokens, target.emitter, target.params ?? {});
|
|
89
|
+
const body = emitBody(tokens, target.emitter, target.params ?? {}, { version: VERSION });
|
|
90
90
|
const before = readFileSync(path, 'utf8');
|
|
91
91
|
const has = findRegion(before, target.region, target.syntax);
|
|
92
92
|
|
package/lib/check.mjs
CHANGED
|
@@ -37,12 +37,21 @@ export function checkTarget(target, root, tokens, version) {
|
|
|
37
37
|
}
|
|
38
38
|
if (!found) return { target, path, status: 'missing' };
|
|
39
39
|
|
|
40
|
-
const expected = emitBody(tokens, target.emitter, target.params ?? {}
|
|
40
|
+
const expected = emitBody(tokens, target.emitter, target.params ?? {}, { version })
|
|
41
|
+
.replace(/\s+$/, '');
|
|
41
42
|
const actual = found.body.replace(/\s+$/, '');
|
|
42
|
-
if (expected
|
|
43
|
-
return { target, path, status: '
|
|
43
|
+
if (expected !== actual) {
|
|
44
|
+
return { target, path, status: 'drift', diff: lineDiff(expected, actual), writtenBy: found.version };
|
|
44
45
|
}
|
|
45
|
-
|
|
46
|
+
// The values match, but the region must also record the release it belongs
|
|
47
|
+
// to. Without this the version is decoration: in-repo regions sat at v0.1.0
|
|
48
|
+
// through six releases with a green check, which is precisely the "three
|
|
49
|
+
// different version numbers and no way to read them" problem this policy
|
|
50
|
+
// exists to end. One version per consumer means check has to say so.
|
|
51
|
+
if (found.version !== version) {
|
|
52
|
+
return { target, path, status: 'stale-version', writtenBy: found.version };
|
|
53
|
+
}
|
|
54
|
+
return { target, path, status: 'ok', writtenBy: found.version };
|
|
46
55
|
}
|
|
47
56
|
|
|
48
57
|
export function checkAll({ tokens, targets, root, ids, version }) {
|
|
@@ -85,6 +94,7 @@ export function lineDiff(expected, actual) {
|
|
|
85
94
|
export const EXPLAIN = {
|
|
86
95
|
ok: 'in sync',
|
|
87
96
|
drift: 'region content differs from what the tokens produce',
|
|
97
|
+
'stale-version': 'values are correct but the region records an older press release — re-emit',
|
|
88
98
|
missing: 'file has no press region — the generated block was removed',
|
|
89
99
|
absent: 'declared file does not exist at this path',
|
|
90
100
|
corrupt: 'region markers are malformed',
|
package/lib/emit.mjs
CHANGED
|
@@ -292,23 +292,49 @@ function pythonConsts(tokens, params) {
|
|
|
292
292
|
return out.join('\n');
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* A short "this repo is on press vX" note for a consumer's README.
|
|
298
|
+
*
|
|
299
|
+
* The version is the point: without it, the only places a repo's press version
|
|
300
|
+
* appears are a CI pin and a comment marker, neither of which anyone reads. A
|
|
301
|
+
* reader landing on the repo should be able to see which brand release it is on
|
|
302
|
+
* without opening a workflow file.
|
|
303
|
+
*/
|
|
304
|
+
function versionBadge(tokens, params, ctx) {
|
|
305
|
+
const version = ctx.version ?? '0.0.0';
|
|
306
|
+
const repo = params.repo_url ?? 'https://github.com/natejswenson/claude-skills/tree/main/skills/press';
|
|
307
|
+
const what = params.what ?? 'Design tokens, brand laws and run presentation';
|
|
308
|
+
return [
|
|
309
|
+
`**Brand:** PRESS v${version} — ${what} are generated from the`,
|
|
310
|
+
`[\`press\`](${repo}) skill, not maintained here. The token blocks marked`,
|
|
311
|
+
'`press:tokens` are generated; edit `press/brand/tokens.json` and re-emit instead.',
|
|
312
|
+
].join('\n');
|
|
313
|
+
}
|
|
314
|
+
|
|
295
315
|
export const EMITTERS = {
|
|
296
316
|
'python-theme': pythonTheme,
|
|
297
317
|
'python-consts': pythonConsts,
|
|
318
|
+
'version-badge': versionBadge,
|
|
298
319
|
'css-vars': cssVars,
|
|
299
320
|
'md-palette': mdPalette,
|
|
300
321
|
'markdown-block': markdownBlock,
|
|
301
322
|
json: jsonTokens,
|
|
302
323
|
};
|
|
303
324
|
|
|
304
|
-
|
|
325
|
+
/**
|
|
326
|
+
* @param ctx Run context available to emitters — currently `{ version }`.
|
|
327
|
+
* Kept separate from `params` because it is a property of the
|
|
328
|
+
* invocation, not of the target's configuration.
|
|
329
|
+
*/
|
|
330
|
+
export function emitBody(tokens, emitter, params = {}, ctx = {}) {
|
|
305
331
|
const fn = EMITTERS[emitter];
|
|
306
332
|
if (!fn) {
|
|
307
333
|
throw new EmitError(
|
|
308
334
|
`unknown emitter "${emitter}" (expected one of: ${Object.keys(EMITTERS).join(', ')})`,
|
|
309
335
|
);
|
|
310
336
|
}
|
|
311
|
-
return fn(tokens, params);
|
|
337
|
+
return fn(tokens, params, ctx);
|
|
312
338
|
}
|
|
313
339
|
|
|
314
340
|
// --------------------------------------------------------------------------
|
package/lib/propagate.mjs
CHANGED
|
@@ -25,7 +25,7 @@ import { readFileSync, writeFileSync, existsSync, readdirSync } from 'node:fs';
|
|
|
25
25
|
import { join } from 'node:path';
|
|
26
26
|
|
|
27
27
|
import { emitBody } from './emit.mjs';
|
|
28
|
-
import { findRegion, spliceRegion } from './region.mjs';
|
|
28
|
+
import { findRegion, initRegion, spliceRegion } from './region.mjs';
|
|
29
29
|
import { selectTargets, targetPath } from './targets.mjs';
|
|
30
30
|
|
|
31
31
|
/** The npm reference consumers pin in CI, e.g. `@natjswenson/press@0.3.0`. */
|
|
@@ -47,10 +47,35 @@ export function propagate({ tokens, targets, root, version, dryRun = false }) {
|
|
|
47
47
|
const before = readFileSync(path, 'utf8');
|
|
48
48
|
const found = findRegion(before, target.region, target.syntax);
|
|
49
49
|
if (!found) {
|
|
50
|
-
|
|
50
|
+
// A newly declared target has no region in the consumer yet, and that
|
|
51
|
+
// consumer's `check` starts failing the moment its pin reaches the
|
|
52
|
+
// release that declares it. So the PR that raises the pin must also
|
|
53
|
+
// create the region — the two are one change, not two.
|
|
54
|
+
//
|
|
55
|
+
// This is not "silently creating": it lands in a reviewed pull request
|
|
56
|
+
// alongside the pin bump. A region that was deliberately DELETED is a
|
|
57
|
+
// different case, and one this cannot distinguish, which is why the
|
|
58
|
+
// target must declare an `init` anchor to be seedable at all.
|
|
59
|
+
if (!target.init) {
|
|
60
|
+
regions.push({ id: target.id, path: target.path, status: 'missing' });
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const body = emitBody(tokens, target.emitter, target.params ?? {}, { version });
|
|
64
|
+
if (!dryRun) {
|
|
65
|
+
writeFileSync(path, initRegion(before, target.region, target.syntax, body, version, target.init), 'utf8');
|
|
66
|
+
}
|
|
67
|
+
regions.push({
|
|
68
|
+
id: target.id,
|
|
69
|
+
path: target.path,
|
|
70
|
+
changed: true,
|
|
71
|
+
brandChanged: false,
|
|
72
|
+
versionChanged: true,
|
|
73
|
+
status: dryRun ? 'would seed' : 'seeded',
|
|
74
|
+
wroteBy: null,
|
|
75
|
+
});
|
|
51
76
|
continue;
|
|
52
77
|
}
|
|
53
|
-
const body = emitBody(tokens, target.emitter, target.params ?? {});
|
|
78
|
+
const body = emitBody(tokens, target.emitter, target.params ?? {}, { version });
|
|
54
79
|
const after = spliceRegion(before, target.region, target.syntax, body, version);
|
|
55
80
|
// Two different kinds of "changed", kept apart on purpose.
|
|
56
81
|
//
|
package/lib/region.mjs
CHANGED
|
@@ -153,7 +153,22 @@ export function initRegion(text, region, syntaxName, body, version, anchor = {})
|
|
|
153
153
|
throw new RegionError(`file already has a press:${region} region — use emit without --init`);
|
|
154
154
|
}
|
|
155
155
|
const block = renderRegion(region, syntaxName, body, version);
|
|
156
|
-
const { replaceFrom, replaceTo } = anchor;
|
|
156
|
+
const { replaceFrom, replaceTo, insertAfter } = anchor;
|
|
157
|
+
|
|
158
|
+
// Seeding a brand-new region (a README badge) rather than taking over a
|
|
159
|
+
// hand-written block: place it after the first line matching `insertAfter`,
|
|
160
|
+
// usually the title, instead of appending to the bottom where nobody looks.
|
|
161
|
+
if (insertAfter) {
|
|
162
|
+
const lines = text.split('\n');
|
|
163
|
+
const re = new RegExp(insertAfter);
|
|
164
|
+
const at = lines.findIndex((l) => re.test(l));
|
|
165
|
+
if (at === -1) throw new RegionError(`anchor insertAfter /${insertAfter}/ matched no line`);
|
|
166
|
+
let i = at + 1;
|
|
167
|
+
while (i < lines.length && lines[i].trim() === '') i += 1;
|
|
168
|
+
lines.splice(i, 0, ...block.split('\n'), '');
|
|
169
|
+
return lines.join('\n');
|
|
170
|
+
}
|
|
171
|
+
|
|
157
172
|
if (!replaceFrom) return `${text.replace(/\s+$/, '')}\n\n${block}\n`;
|
|
158
173
|
|
|
159
174
|
const lines = text.split('\n');
|
package/lib/targets.mjs
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* `check`, which is why `doctor` exists to show the whole registry rather than
|
|
7
7
|
* only what resolved locally.
|
|
8
8
|
*/
|
|
9
|
+
import { execFileSync } from 'node:child_process';
|
|
9
10
|
import { existsSync, readFileSync } from 'node:fs';
|
|
10
11
|
import { fileURLToPath } from 'node:url';
|
|
11
12
|
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
@@ -43,6 +44,26 @@ export function repoRoot(start = process.cwd()) {
|
|
|
43
44
|
export const targetPath = (target, root) =>
|
|
44
45
|
isAbsolute(target.path) ? target.path : join(root, target.path);
|
|
45
46
|
|
|
47
|
+
/**
|
|
48
|
+
* The repository a checkout actually is, from its origin remote.
|
|
49
|
+
*
|
|
50
|
+
* File presence alone cannot identify a consumer: `README.md` exists in every
|
|
51
|
+
* repo, so a README target would select inside any checkout and be checked
|
|
52
|
+
* against the wrong file. Returns null when there is no remote (a temp dir in a
|
|
53
|
+
* test), in which case callers fall back to presence.
|
|
54
|
+
*/
|
|
55
|
+
export function repoIdentity(root) {
|
|
56
|
+
try {
|
|
57
|
+
const url = execFileSync('git', ['-C', root, 'config', '--get', 'remote.origin.url'], {
|
|
58
|
+
encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
|
|
59
|
+
}).trim();
|
|
60
|
+
const m = /([^/:]+?)(?:\.git)?$/.exec(url);
|
|
61
|
+
return m ? m[1] : null;
|
|
62
|
+
} catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
46
67
|
/**
|
|
47
68
|
* Which targets this invocation is responsible for.
|
|
48
69
|
*
|
|
@@ -63,5 +84,12 @@ export function selectTargets(targets, { root, ids }) {
|
|
|
63
84
|
return found;
|
|
64
85
|
});
|
|
65
86
|
}
|
|
66
|
-
|
|
87
|
+
const identity = repoIdentity(root);
|
|
88
|
+
return targets.filter((t) => {
|
|
89
|
+
if (!existsSync(targetPath(t, root))) return false;
|
|
90
|
+
// When the checkout names itself, trust that over a path that happens to
|
|
91
|
+
// exist — otherwise every repo's README matches every README target.
|
|
92
|
+
if (identity) return identity === (t.github ?? t.repo);
|
|
93
|
+
return true;
|
|
94
|
+
});
|
|
67
95
|
}
|
package/package.json
CHANGED
package/targets.json
CHANGED
|
@@ -395,9 +395,122 @@
|
|
|
395
395
|
]
|
|
396
396
|
},
|
|
397
397
|
"init": {
|
|
398
|
-
"
|
|
399
|
-
|
|
398
|
+
"insertAfter": "^# "
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
"id": "natejswenson.io-readme",
|
|
403
|
+
"repo": "natejswenson.io",
|
|
404
|
+
"path": "README.md",
|
|
405
|
+
"region": "version",
|
|
406
|
+
"syntax": "md",
|
|
407
|
+
"emitter": "version-badge",
|
|
408
|
+
"params": {
|
|
409
|
+
"what": "The site's colour, type and panel tokens"
|
|
410
|
+
},
|
|
411
|
+
"init": {
|
|
412
|
+
"insertAfter": "^# "
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
"id": "budget-readme",
|
|
417
|
+
"repo": "budget",
|
|
418
|
+
"path": "README.md",
|
|
419
|
+
"region": "version",
|
|
420
|
+
"syntax": "md",
|
|
421
|
+
"emitter": "version-badge",
|
|
422
|
+
"params": {
|
|
423
|
+
"what": "The report's colour and type tokens"
|
|
424
|
+
},
|
|
425
|
+
"github": "local-budget",
|
|
426
|
+
"init": {
|
|
427
|
+
"insertAfter": "^# "
|
|
428
|
+
}
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
"id": "local-fitness-readme",
|
|
432
|
+
"repo": "local-fitness",
|
|
433
|
+
"path": "README.md",
|
|
434
|
+
"region": "version",
|
|
435
|
+
"syntax": "md",
|
|
436
|
+
"emitter": "version-badge",
|
|
437
|
+
"params": {
|
|
438
|
+
"what": "The brief and report-card colour and type tokens"
|
|
439
|
+
},
|
|
440
|
+
"init": {
|
|
441
|
+
"insertAfter": "^# "
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
"id": "city-report-readme",
|
|
446
|
+
"repo": "claude-skills",
|
|
447
|
+
"path": "skills/city-report/README.md",
|
|
448
|
+
"region": "version",
|
|
449
|
+
"syntax": "md",
|
|
450
|
+
"emitter": "version-badge",
|
|
451
|
+
"params": {
|
|
452
|
+
"what": "The report's colour and type tokens"
|
|
453
|
+
},
|
|
454
|
+
"init": {
|
|
455
|
+
"insertAfter": "^# "
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
"id": "resume-readme",
|
|
460
|
+
"repo": "claude-skills",
|
|
461
|
+
"path": "skills/resume/README.md",
|
|
462
|
+
"region": "version",
|
|
463
|
+
"syntax": "md",
|
|
464
|
+
"emitter": "version-badge",
|
|
465
|
+
"params": {
|
|
466
|
+
"what": "The résumé themes' colour and type tokens"
|
|
467
|
+
},
|
|
468
|
+
"init": {
|
|
469
|
+
"insertAfter": "^# "
|
|
470
|
+
}
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
"id": "ghostwriter-readme",
|
|
474
|
+
"repo": "claude-skills",
|
|
475
|
+
"path": "skills/ghostwriter/README.md",
|
|
476
|
+
"region": "version",
|
|
477
|
+
"syntax": "md",
|
|
478
|
+
"emitter": "version-badge",
|
|
479
|
+
"params": {
|
|
480
|
+
"what": "The card system's colour, type and terminal tokens"
|
|
481
|
+
},
|
|
482
|
+
"init": {
|
|
483
|
+
"insertAfter": "^# "
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
"id": "ghostwriter-x-readme",
|
|
488
|
+
"repo": "claude-skills",
|
|
489
|
+
"path": "skills/ghostwriter-x/README.md",
|
|
490
|
+
"region": "version",
|
|
491
|
+
"syntax": "md",
|
|
492
|
+
"emitter": "version-badge",
|
|
493
|
+
"params": {
|
|
494
|
+
"what": "The card system's colour, type and terminal tokens"
|
|
495
|
+
},
|
|
496
|
+
"init": {
|
|
497
|
+
"insertAfter": "^# "
|
|
498
|
+
}
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
"id": "devlog-readme",
|
|
502
|
+
"repo": "claude-skills",
|
|
503
|
+
"path": "skills/devlog/README.md",
|
|
504
|
+
"region": "version",
|
|
505
|
+
"syntax": "md",
|
|
506
|
+
"emitter": "version-badge",
|
|
507
|
+
"params": {
|
|
508
|
+
"what": "The cover-image palette"
|
|
509
|
+
},
|
|
510
|
+
"init": {
|
|
511
|
+
"insertAfter": "^# "
|
|
400
512
|
}
|
|
401
513
|
}
|
|
402
|
-
]
|
|
514
|
+
],
|
|
515
|
+
"$readme_note": "natejswenson has no README target on purpose: its README.md is generated by build_svg.py, so a region there would be clobbered on the next build. Its press version is visible in build_svg.py's own region marker instead."
|
|
403
516
|
}
|