kodelyth-ecc 2.23.2 → 2.24.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/CHANGELOG.md CHANGED
@@ -2,6 +2,61 @@
2
2
 
3
3
  All notable changes to Kodelyth ECC are documented here.
4
4
 
5
+ ## v2.24.0 — the update notice now tells you why (October 2026)
6
+
7
+ The CLI has always polled npm for new versions and cached the answer for 24
8
+ hours. That monthly touchpoint was spent on almost nothing: the result surfaced
9
+ only in the interactive menu, as a version number and an install command.
10
+
11
+ ```
12
+ Update to v2.23.2
13
+ npm i -g kodelyth-ecc (installs new version)
14
+ ```
15
+
16
+ A version number is a nag. It states a fact and gives no reason to act on it.
17
+
18
+ ### It now links what changed
19
+
20
+ ```
21
+ Update to v2.24.0
22
+ what changed: github.com/sifxprime/kodelyth-ecc/releases/tag/v2.24.0
23
+ ```
24
+
25
+ `check()` returns a `releaseNotes` URL alongside the version, so every consumer
26
+ gets it without doing its own string building.
27
+
28
+ ### doctor surfaces it too
29
+
30
+ A second natural touchpoint — `doctor` is run deliberately, usually when
31
+ somebody is already paying attention:
32
+
33
+ ```
34
+ ! version — v2.23.2 installed, v2.24.0 available
35
+ → npm i -g kodelyth-ecc what changed: .../releases/tag/v2.24.0
36
+ ```
37
+
38
+ ### Cache-only, on purpose
39
+
40
+ The new `cachedCheck()` reads the 24-hour cache and never touches the network.
41
+ `doctor` is the command people reach for when something is **already** wrong, so
42
+ it must not wait on the registry — and an unreachable npm is not a fact about
43
+ the health of your install. A cold or stale cache yields nothing and the row is
44
+ simply absent, rather than reported as a warning. A check that fires on "I could
45
+ not reach the network" teaches people to ignore the output.
46
+
47
+ `run()` stays synchronous and takes the result as a parameter, so it remains
48
+ testable and `doctor` adds no latency.
49
+
50
+ Verified across every state: a newer version warns with the link, being current
51
+ passes, a stale cache is ignored, a corrupt cache leaves `doctor` working on its
52
+ 11 checks, and a cold cache omits the row.
53
+
54
+ ### What this does not do
55
+
56
+ No nagging on every command. Nothing auto-updates. Nothing blocks.
57
+
58
+ **647 tests passing.**
59
+
5
60
  ## v2.23.2 — the README leads with the product, not the badges (October 2026)
6
61
 
7
62
  No change to the toolkit. The README **is** the npm landing page, and npm only
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.23.2
1
+ 2.24.0
@@ -707,7 +707,23 @@ if (args[0] === 'god' || args[0] === 'evil' || args[0] === 'arena') {
707
707
 
708
708
  if (args[0] === 'doctor') {
709
709
  const { run, PASS, WARN, FAIL } = require(path.join(ROOT, 'scripts', 'doctor-health.js'));
710
- const report = run();
710
+
711
+ // Cache-only, deliberately. doctor is what people run when something is
712
+ // already wrong, so it must not wait on the registry — and an unreachable npm
713
+ // is not a fact about the health of their install. The cache is warmed by the
714
+ // interactive menu; a cold one yields null and the version row is simply
715
+ // absent.
716
+ let update;
717
+ try {
718
+ const { cachedCheck } = require(path.join(ROOT, 'scripts', 'cli', 'update-check.js'));
719
+ const vf = path.join(ROOT, 'VERSION');
720
+ const current = fs.existsSync(vf)
721
+ ? fs.readFileSync(vf, 'utf8').trim()
722
+ : require(path.join(ROOT, 'package.json')).version;
723
+ update = cachedCheck(current) || undefined;
724
+ } catch { /* never let a version hint break the health check */ }
725
+
726
+ const report = run({ update });
711
727
  if (args.includes('--json')) {
712
728
  process.stdout.write(JSON.stringify(report, null, 2) + '\n');
713
729
  process.exit(report.summary.fail > 0 ? 1 : 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kodelyth-ecc",
3
- "version": "2.23.2",
3
+ "version": "2.24.0",
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",
@@ -47,7 +47,9 @@ async function buildOptions({ ROOT }) {
47
47
  if (update.updateAvailable) {
48
48
  opts.push({
49
49
  label: `Update to v${update.latest}`,
50
- hint: 'npm i -g kodelyth-ecc (installs new version)',
50
+ // Say what it is AND where to read what changed. A version number alone
51
+ // is a nag; a reason is an invitation.
52
+ hint: `what changed: ${update.releaseNotes || 'github.com/sifxprime/kodelyth-ecc/releases'}`,
51
53
  run: () => runUpdate(),
52
54
  badge: 'NEW',
53
55
  });
@@ -54,7 +54,19 @@ function cmpVersion(a, b) {
54
54
  return 0;
55
55
  }
56
56
 
57
- // Public: returns { current, latest, updateAvailable, cached } or nulls on failure.
57
+ /**
58
+ * Where to read what actually changed in a given version.
59
+ *
60
+ * "update available: v2.23.2" is a nag — it tells you a number and no reason.
61
+ * A link to the release notes turns the same notice into something worth acting
62
+ * on, which is the whole point of owning a monthly touchpoint.
63
+ */
64
+ function releaseNotesUrl(version) {
65
+ if (!version) return null;
66
+ return `https://github.com/sifxprime/kodelyth-ecc/releases/tag/v${version}`;
67
+ }
68
+
69
+ // Public: returns { current, latest, updateAvailable, cached, releaseNotes } or nulls on failure.
58
70
  async function check({ current, force = false } = {}) {
59
71
  if (!force) {
60
72
  const c = readCache();
@@ -63,6 +75,7 @@ async function check({ current, force = false } = {}) {
63
75
  current, latest: c.latest,
64
76
  updateAvailable: c.latest && cmpVersion(c.latest, current) > 0,
65
77
  cached: true,
78
+ releaseNotes: releaseNotesUrl(c.latest),
66
79
  };
67
80
  }
68
81
  }
@@ -72,7 +85,30 @@ async function check({ current, force = false } = {}) {
72
85
  current, latest,
73
86
  updateAvailable: !!(latest && cmpVersion(latest, current) > 0),
74
87
  cached: false,
88
+ releaseNotes: releaseNotesUrl(latest),
89
+ };
90
+ }
91
+
92
+ /**
93
+ * Cache-only read. No network, no promise, no timeout — safe to call from a
94
+ * synchronous command.
95
+ *
96
+ * doctor uses this rather than check(): it is the command people run when
97
+ * something is already wrong, so it must never wait on the registry. The cache
98
+ * is warmed by the interactive menu, which does hit the network. A cold cache
99
+ * simply yields null and the version row does not appear, which is correct —
100
+ * "I could not reach npm" is not a fact about the health of your install.
101
+ */
102
+ function cachedCheck(current) {
103
+ const c = readCache();
104
+ if (!c || !c.latest) return null;
105
+ return {
106
+ current,
107
+ latest: c.latest,
108
+ updateAvailable: cmpVersion(c.latest, current) > 0,
109
+ cached: true,
110
+ releaseNotes: releaseNotesUrl(c.latest),
75
111
  };
76
112
  }
77
113
 
78
- module.exports = { check, cmpVersion };
114
+ module.exports = { check, cachedCheck, cmpVersion, releaseNotesUrl };
@@ -155,7 +155,29 @@ function checkCodebaseGraph() {
155
155
 
156
156
  // ── Runner ───────────────────────────────────────────────────────────────────
157
157
 
158
- function run() {
158
+ /**
159
+ * Whether the installed version is current.
160
+ *
161
+ * Takes a pre-fetched result rather than doing its own network call, so run()
162
+ * stays synchronous and testable, and doctor never blocks on the registry. When
163
+ * nothing was fetched — offline, timed out, caller did not bother — this is
164
+ * silently absent rather than a warning. "Could not reach npm" is not a health
165
+ * problem with the user's install, and reporting it as one trains people to
166
+ * ignore the output.
167
+ */
168
+ function checkVersion(update) {
169
+ if (!update || !update.latest) return null;
170
+ if (!update.updateAvailable) {
171
+ return { status: PASS, detail: `v${update.current} is the latest` };
172
+ }
173
+ return {
174
+ status: WARN,
175
+ detail: `v${update.current} installed, v${update.latest} available`,
176
+ fix: `npm i -g kodelyth-ecc what changed: ${update.releaseNotes || 'github.com/sifxprime/kodelyth-ecc/releases'}`,
177
+ };
178
+ }
179
+
180
+ function run({ update } = {}) {
159
181
  const checks = [
160
182
  check('binaries', checkBinaries),
161
183
  check('install-target', checkInstallTarget),
@@ -169,6 +191,11 @@ function run() {
169
191
  check('terse', checkTerse),
170
192
  check('codebase-graph', checkCodebaseGraph),
171
193
  ];
194
+
195
+ // Appended rather than inlined: it is the only check that depends on the
196
+ // network, and it drops out entirely when no result was supplied.
197
+ const version = checkVersion(update);
198
+ if (version) checks.push({ id: 'version', ...version });
172
199
  const summary = {
173
200
  total: checks.length,
174
201
  pass: checks.filter(c => c.status === PASS).length,
@@ -178,4 +205,4 @@ function run() {
178
205
  return { checks, summary };
179
206
  }
180
207
 
181
- module.exports = { run, PASS, WARN, FAIL };
208
+ module.exports = { run, checkVersion, PASS, WARN, FAIL };