kodelyth-ecc 2.24.5 → 2.24.7

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 CHANGED
@@ -2,6 +2,122 @@
2
2
 
3
3
  All notable changes to Kodelyth ECC are documented here.
4
4
 
5
+ ## v2.24.7 — the brand render scripts could not run (October 2026)
6
+
7
+ Developer tooling only. Nothing here ships in the npm package — `brand/` is not in
8
+ the `files` allowlist — but it is what kept the published artwork correct.
9
+
10
+ 2.24.6 fixed brand assets that had drifted to v2.7.0 with counts seven releases
11
+ stale. This fixes the reason they drifted: **both scripts meant to regenerate them
12
+ were broken, and had been for months.**
13
+
14
+ `brand/convert.js` and `brand/veltix-convert.js` each required `puppeteer-core`,
15
+ which is neither installed nor declared in `package.json`, so a clean checkout
16
+ failed with module-not-found before rendering anything. Both also hardcoded
17
+ `/Applications/Google Chrome.app/...`, so neither could have run on Linux or
18
+ Windows even with the dependency present. `.gitignore` names `brand/convert.js`
19
+ as the way to regenerate `brand/*.png`; it pointed at a script that crashed on
20
+ line 9.
21
+
22
+ `convert.js` was wrong in two further ways. It referenced `favicon.svg`, which
23
+ does not exist in that directory, so three of its eleven entries rendered nothing.
24
+ And it omitted five assets that do have published PNGs — the ECC badge, the icon
25
+ at both 192 and 512, and both ECC lockups, plus the legacy post. Its list is now
26
+ derived from the files actually present.
27
+
28
+ ### What changed
29
+
30
+ Both now use `rsvg-convert`. That keeps ECC's no-runtime-dependency rule intact —
31
+ adding `puppeteer-core` would pull a large dependency into a package that
32
+ deliberately has none — and matches `scripts/brand/export-assets.js`, so the whole
33
+ brand pipeline renders through one tool.
34
+
35
+ Sizes reduce to a target **width**. Every published PNG was checked against its
36
+ SVG's viewBox and all twenty-one are exact aspect-preserving scales, with ratios
37
+ from 2x to 6x, so letting rsvg derive the height reproduces each one without a
38
+ second number that can drift from the artwork. Every output keeps its exact
39
+ published dimensions, captured before and after.
40
+
41
+ The renderer substitution was verified rather than assumed: re-rendering a
42
+ Chrome-produced export gave a byte-comparable file at identical dimensions, and
43
+ the text-bearing assets were viewed after rendering. The old `document.fonts.ready`
44
+ wait needs no counterpart — these SVGs ask for `'Space Grotesk'`, `'Inter'`,
45
+ `system-ui`, which are font-family names resolved against installed fonts rather
46
+ than `@font-face` webfonts, so rsvg resolves them the same way Chrome did.
47
+
48
+ ### One gate for all brand artwork
49
+
50
+ All four scripts now share a `--check` mode that exits non-zero when anything is
51
+ stale:
52
+
53
+ ```bash
54
+ node brand/convert.js --check \
55
+ && node brand/veltix-convert.js --check \
56
+ && node scripts/brand/sync-asset-version.js --check \
57
+ && node scripts/brand/export-assets.js --check
58
+ ```
59
+
60
+ This release is the first to run that pipeline end to end: bump, stamp the version
61
+ into 46 SVGs across both repositories, then render 70 PNGs.
62
+
63
+ **672 tests across 43 files.**
64
+
65
+ ## v2.24.6 — brand assets advertised the wrong version and stale counts (October 2026)
66
+
67
+ Assets and documentation only. No code changes to the toolkit.
68
+
69
+ ### What was wrong
70
+
71
+ Every social and brand SVG carries its version and headline counts as literal
72
+ artwork, and nothing kept them in step with releases.
73
+
74
+ ECC's 23 SVGs said `v2.24.1`, four releases behind. The website's copies said
75
+ `v2.18.0` — seven behind — and the two sets had drifted apart, so the marketing
76
+ images on the site advertised a different version from the ones in the README.
77
+
78
+ The counts were worse, and were nearly missed: grepping for the *correct* values
79
+ (70 agents, 196 skills) found them and suggested the counts were fine. Rendering
80
+ `github-social-preview.png` showed otherwise — **97 COMMANDS** and **22+ HOOKS**
81
+ against an actual 103 and 44, on the flagship image used for the repository's
82
+ social card. Fixed across 11 SVGs per repo: 97 to 103 commands, 22+/20+ to 44
83
+ hooks, 384 to 672 tests, 14 to 15 rules.
84
+
85
+ The PNG exports were worse still, at `v2.7.0`.
86
+
87
+ ### Root cause
88
+
89
+ `.gitignore` names `node brand/convert.js` as the way to regenerate the exports.
90
+ That script covers only the `brand/` lockups from a hardcoded list, never the 49
91
+ `social/` exports — and it cannot run at all, because `puppeteer-core` is neither
92
+ installed nor declared in `package.json`. The documented regeneration path was
93
+ broken, and because the PNGs are gitignored build artifacts the drift was
94
+ invisible.
95
+
96
+ ### What ships
97
+
98
+ Two scripts, so this cannot recur silently:
99
+
100
+ - `scripts/brand/sync-asset-version.js` stamps the version from `package.json`
101
+ into every SVG. Takes `--root` so the website's copies are driven from the same
102
+ source of truth, and `--check` exits non-zero when anything is stale.
103
+ - `scripts/brand/export-assets.js` renders all 49 exports at 7680px, taking each
104
+ height from the SVG's own viewBox. `--check` compares mtimes and fails when a
105
+ PNG is older than its source.
106
+
107
+ Counts are deliberately NOT stamped by script. They are laid out as artwork, and
108
+ some numbers that look like catalog counts are not: "20+ rules stacked" counts
109
+ accumulated lessons, and "4 agents" / "8 agents" describe how many a given
110
+ parallel command fires. A blanket replace would rewrite those into nonsense, so
111
+ each count fix here was read in context first and those were left alone.
112
+
113
+ ### Also
114
+
115
+ The README now asks for a star. The post-install CLI asked once; the README —
116
+ which is also the npm package page, and the first thing most people see — never
117
+ did.
118
+
119
+ **672 tests across 43 files.**
120
+
5
121
  ## v2.24.5 — fix path traversal in session bundles (October 2026)
6
122
 
7
123
  **Security fix.** A session bundle is a portable, shareable artifact — that is the
package/README.md CHANGED
@@ -1157,6 +1157,20 @@ command you ran, the full error, and your `kodelythecc doctor` output.
1157
1157
 
1158
1158
  <div align="center">
1159
1159
 
1160
+ ### If ECC saved you time
1161
+
1162
+ Starring the repo is the main way other developers find it — the project has no
1163
+ marketing budget and no telemetry, so visibility is the only signal that it is
1164
+ worth anyone's time.
1165
+
1166
+ [![Star on GitHub](https://img.shields.io/github/stars/sifxprime/kodelyth-ecc?style=social)](https://github.com/sifxprime/kodelyth-ecc)
1167
+
1168
+ </div>
1169
+
1170
+ ---
1171
+
1172
+ <div align="center">
1173
+
1160
1174
  [![npm](https://img.shields.io/badge/npm-kodelyth--ecc-red.svg)](https://www.npmjs.com/package/kodelyth-ecc)
1161
1175
  [![GitHub](https://img.shields.io/badge/GitHub-sifxprime%2Fkodelyth--ecc-181717.svg)](https://github.com/sifxprime/kodelyth-ecc)
1162
1176
  [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kodelyth-ecc",
3
- "version": "2.24.5",
3
+ "version": "2.24.7",
4
4
  "description": "Production-grade AI coding toolkit — 70 agents (incl. devil-mode adversarial crew), 196 skills, 103 commands, parallel multi-agent commands, semantic intent routing, self-learning memory, and a built-in MCP server (16 tools / 6 prompts / 377 resources) that bridges to Claude Desktop, LangGraph, AutoGen, CrewAI, and OpenAI Agents SDK. Works on macOS, Linux and Windows with Claude Code, Windsurf, Cursor, Codex, Antigravity, OpenCode, Cline, RooCode, Aider, Kimi, and Gemini CLI.",
5
5
  "author": "Kodelyth <github.com/sifxprime>",
6
6
  "license": "MIT",
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Render every social SVG to its 8K PNG export.
6
+ *
7
+ * The PNGs under social/exports/8k/ are rasterised copies of the SVGs, used where
8
+ * an SVG will not do — GitHub's social preview, and most social platforms, accept
9
+ * only raster uploads. Nothing regenerated them, so they drifted badly: by v2.24.5
10
+ * the exports still read v2.7.0, 97 commands and 22+ hooks, against an actual
11
+ * 103 and 44. The SVGs had been corrected several times; the PNGs never were.
12
+ *
13
+ * Every existing export is 7680px wide with the aspect ratio taken from the SVG's
14
+ * own viewBox, so that is what this reproduces.
15
+ *
16
+ * node scripts/brand/export-assets.js # render all
17
+ * node scripts/brand/export-assets.js --check # report stale, change nothing
18
+ * node scripts/brand/export-assets.js card-main # render one, by name
19
+ *
20
+ * --check compares mtimes and exits non-zero when any PNG is older than its SVG,
21
+ * so a release step can catch exports that no longer match their source.
22
+ *
23
+ * Requires rsvg-convert (librsvg). It is not installed by default anywhere, so its
24
+ * absence is reported as a clear instruction rather than a spawn error.
25
+ *
26
+ * On the choice of renderer: brand/convert.js already renders the brand/ lockups
27
+ * with puppeteer-core, chosen there for font fidelity, and consistency would argue
28
+ * for using it here too. It is not used because it cannot run — puppeteer-core is
29
+ * neither installed nor declared in package.json, so `node brand/convert.js` fails
30
+ * on a clean checkout. That is most likely why these exports went stale in the
31
+ * first place: the tool named in .gitignore for regenerating them does not work.
32
+ *
33
+ * rsvg-convert was checked against the output it replaces rather than assumed
34
+ * equivalent: re-rendering github-social-preview.png produced 491,233 bytes against
35
+ * the previous Chrome-rendered 491,564 at identical dimensions, and the two were
36
+ * compared side by side — same fonts, same geometry, differing only in the text
37
+ * that was actually corrected. If puppeteer-core is ever added as a real
38
+ * dependency, moving this to the same renderer would be a reasonable tidy-up.
39
+ */
40
+
41
+ const fs = require('fs');
42
+ const path = require('path');
43
+ const { execFileSync, spawnSync } = require('child_process');
44
+
45
+ const ROOT = path.join(__dirname, '..', '..');
46
+ const SVG_DIR = path.join(ROOT, 'social');
47
+ const PNG_DIR = path.join(SVG_DIR, 'exports', '8k');
48
+ const WIDTH = 7680;
49
+
50
+ function haveRenderer() {
51
+ try {
52
+ execFileSync('rsvg-convert', ['--version'], { stdio: 'ignore' });
53
+ return true;
54
+ } catch {
55
+ return false;
56
+ }
57
+ }
58
+
59
+ function listPairs(filter) {
60
+ if (!fs.existsSync(SVG_DIR)) return [];
61
+ return fs
62
+ .readdirSync(SVG_DIR)
63
+ .filter((f) => f.endsWith('.svg'))
64
+ .map((f) => path.basename(f, '.svg'))
65
+ .filter((name) => !filter || name === filter)
66
+ .sort()
67
+ .map((name) => ({
68
+ name,
69
+ svg: path.join(SVG_DIR, `${name}.svg`),
70
+ png: path.join(PNG_DIR, `${name}.png`),
71
+ }));
72
+ }
73
+
74
+ /** A PNG is stale when it is missing or older than the SVG it came from. */
75
+ function isStale({ svg, png }) {
76
+ if (!fs.existsSync(png)) return true;
77
+ return fs.statSync(png).mtimeMs < fs.statSync(svg).mtimeMs;
78
+ }
79
+
80
+ function run({ check = false, only = null } = {}) {
81
+ const pairs = listPairs(only);
82
+ if (pairs.length === 0) {
83
+ console.error(only ? `no such SVG: social/${only}.svg` : `no SVGs in ${SVG_DIR}`);
84
+ return 1;
85
+ }
86
+
87
+ const stale = pairs.filter(isStale);
88
+
89
+ if (check) {
90
+ for (const p of stale) console.log(` STALE ${p.name}.png`);
91
+ console.log(
92
+ stale.length === 0
93
+ ? ` all ${pairs.length} exports are newer than their SVG`
94
+ : ` ${stale.length} of ${pairs.length} export(s) stale`
95
+ );
96
+ return stale.length === 0 ? 0 : 1;
97
+ }
98
+
99
+ if (!haveRenderer()) {
100
+ console.error(' rsvg-convert not found. Install librsvg:');
101
+ console.error(' macOS: brew install librsvg');
102
+ console.error(' Debian: apt-get install librsvg2-bin');
103
+ return 1;
104
+ }
105
+
106
+ fs.mkdirSync(PNG_DIR, { recursive: true });
107
+
108
+ let ok = 0;
109
+ const failed = [];
110
+ for (const p of pairs) {
111
+ // Width only — rsvg-convert derives the height from the viewBox, so each
112
+ // card keeps its own aspect ratio without hardcoding 49 different heights.
113
+ const r = spawnSync('rsvg-convert', ['-w', String(WIDTH), p.svg, '-o', p.png], {
114
+ stdio: ['ignore', 'ignore', 'pipe'],
115
+ });
116
+ if (r.status === 0 && fs.existsSync(p.png) && fs.statSync(p.png).size > 0) {
117
+ ok += 1;
118
+ } else {
119
+ failed.push(`${p.name}: ${String(r.stderr || '').trim() || `exit ${r.status}`}`);
120
+ }
121
+ }
122
+
123
+ console.log(` rendered ${ok}/${pairs.length} at ${WIDTH}px wide`);
124
+ for (const f of failed) console.error(` FAILED ${f}`);
125
+ return failed.length === 0 ? 0 : 1;
126
+ }
127
+
128
+ if (require.main === module) {
129
+ const args = process.argv.slice(2);
130
+ const check = args.includes('--check');
131
+ const only = args.find((a) => !a.startsWith('--')) || null;
132
+ process.exit(run({ check, only }));
133
+ }
134
+
135
+ module.exports = { run, listPairs, isStale, WIDTH };
@@ -0,0 +1,100 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * Stamp the current package version into every brand and social SVG.
6
+ *
7
+ * These assets carry a literal "v2.24.1" in their artwork, and nothing kept them
8
+ * in step with releases. By 2.24.5 the ECC copies were four releases behind and
9
+ * the website's copies were SEVEN behind, at v2.18.0 — the two sets had drifted
10
+ * apart as well, so the marketing images on the site advertised a different
11
+ * version from the ones in the README.
12
+ *
13
+ * Only the version token is touched. Counts (70 agents, 196 skills, …) are laid
14
+ * out as artwork and change rarely, so they stay a deliberate edit; a script that
15
+ * silently reflowed them would do more harm than the staleness it fixed.
16
+ *
17
+ * node scripts/brand/sync-asset-version.js # ECC's own assets
18
+ * node scripts/brand/sync-asset-version.js --check # report, change nothing
19
+ * node scripts/brand/sync-asset-version.js --root ../ecc-web/public
20
+ *
21
+ * --check exits non-zero when anything is stale, so a release step can refuse to
22
+ * ship assets that advertise the wrong version.
23
+ */
24
+
25
+ const fs = require('fs');
26
+ const path = require('path');
27
+
28
+ const ROOT = path.join(__dirname, '..', '..');
29
+ const VERSION = require(path.join(ROOT, 'package.json')).version;
30
+
31
+ // A version token in artwork: "v" followed by semver, as a whole word. Anchored
32
+ // on the leading "v" so it cannot match an IP address — section-dashboard.svg
33
+ // contains "127.0.0.1", which a bare semver pattern happily mangles.
34
+ const VERSION_TOKEN = /\bv\d+\.\d+\.\d+\b/g;
35
+
36
+ function collectSvgs(root) {
37
+ const out = [];
38
+ for (const dir of ['social', 'brand']) {
39
+ const full = path.join(root, dir);
40
+ if (!fs.existsSync(full)) continue;
41
+ for (const name of fs.readdirSync(full)) {
42
+ if (name.endsWith('.svg')) out.push(path.join(full, name));
43
+ }
44
+ }
45
+ return out.sort();
46
+ }
47
+
48
+ function run({ root, check }) {
49
+ const files = collectSvgs(root);
50
+ if (files.length === 0) {
51
+ console.error(`no social/ or brand/ SVGs under ${root}`);
52
+ return 1;
53
+ }
54
+
55
+ const stale = [];
56
+ let changedFiles = 0;
57
+ let changedTokens = 0;
58
+
59
+ for (const file of files) {
60
+ const before = fs.readFileSync(file, 'utf8');
61
+ const found = before.match(VERSION_TOKEN);
62
+ if (!found) continue;
63
+
64
+ const outdated = found.filter((t) => t !== `v${VERSION}`);
65
+ if (outdated.length === 0) continue;
66
+
67
+ stale.push({ file, versions: [...new Set(outdated)], count: outdated.length });
68
+ changedTokens += outdated.length;
69
+ changedFiles += 1;
70
+
71
+ if (!check) {
72
+ fs.writeFileSync(file, before.replace(VERSION_TOKEN, `v${VERSION}`), 'utf8');
73
+ }
74
+ }
75
+
76
+ const rel = (f) => path.relative(root, f);
77
+ for (const s of stale) {
78
+ console.log(` ${check ? 'STALE' : 'updated'} ${rel(s.file).padEnd(40)} ${s.versions.join(', ')} -> v${VERSION}`);
79
+ }
80
+
81
+ if (stale.length === 0) {
82
+ console.log(` all ${files.length} SVGs already at v${VERSION}`);
83
+ return 0;
84
+ }
85
+
86
+ console.log(
87
+ ` ${check ? 'stale' : 'synced'}: ${changedFiles} file(s), ${changedTokens} token(s) -> v${VERSION}`
88
+ );
89
+ return check ? 1 : 0;
90
+ }
91
+
92
+ if (require.main === module) {
93
+ const args = process.argv.slice(2);
94
+ const check = args.includes('--check');
95
+ const rootFlag = args.indexOf('--root');
96
+ const root = rootFlag >= 0 ? path.resolve(args[rootFlag + 1]) : ROOT;
97
+ process.exit(run({ root, check }));
98
+ }
99
+
100
+ module.exports = { run, collectSvgs, VERSION_TOKEN };