@ibalzam/codejitsu-core 0.8.0 → 0.9.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/CLAUDE.md CHANGED
@@ -27,6 +27,29 @@ CLIs (auto-discover `codejitsu.config.ts`):
27
27
  - `codejitsu-llms`
28
28
  - `codejitsu-check`
29
29
 
30
+ ## Always use latest stable versions
31
+
32
+ When scaffolding a new project, adding a dependency, or upgrading an existing one — **do not rely on package versions from training data**. They will be wrong. Always check the npm registry first.
33
+
34
+ ### Required checks before suggesting any version
35
+
36
+ - For a single package: `npm view <pkg> version` returns the actual current latest.
37
+ - For the whole project: `npx codejitsu doctor` runs `npm outdated` and flags drift, with extra emphasis on the critical stack (Astro, React, Tailwind, TypeScript, codejitsu-core, the astro integrations).
38
+ - For Node: `node --version` — current LTS major is what new projects should target.
39
+
40
+ ### When starting a new Codejitsu site
41
+
42
+ Run `codejitsu doctor` immediately after `npm install` and before writing real code. If anything critical is behind, upgrade first. **Never start a new project on an outdated framework.**
43
+
44
+ ### When upgrading an existing site
45
+
46
+ - Patch / minor bumps: `npm update` and re-run `codejitsu doctor` + `codejitsu audit`.
47
+ - Major bumps (e.g. Astro N → N+1, TypeScript N → N+1): read the framework's migration guide, plan separately. Don't bundle into other work.
48
+
49
+ ### When the user asks "what version of X should we use"
50
+
51
+ Run `npm view X version` and report the actual latest. Then offer pros/cons of any pinning concerns (peer-dep conflicts, etc.). Do NOT say a number from memory.
52
+
30
53
  ## Principles that apply to every Codejitsu site
31
54
 
32
55
  Non-negotiable unless the user explicitly opts out.
package/bin/codejitsu.mjs CHANGED
@@ -2,6 +2,7 @@
2
2
  import { parseArgs } from 'node:util';
3
3
  import { runBlog } from '../modules/cli/src/blog.mjs';
4
4
  import { runDeploySetup, runDeployTrigger } from '../modules/cli/src/deploy.mjs';
5
+ import { runDoctor } from '../modules/cli/src/doctor.mjs';
5
6
  import { runAudit } from '../modules/audit/src/run.mjs';
6
7
 
7
8
  const subcommand = process.argv[2];
@@ -12,6 +13,7 @@ const COMMANDS = {
12
13
  'blog:drafts': () => runBlog('blog:drafts'),
13
14
  'deploy:setup': () => runDeploySetup(),
14
15
  'deploy:run': () => runDeployTrigger(),
16
+ doctor: () => runDoctor(),
15
17
  audit: () => {
16
18
  const { values } = parseArgs({
17
19
  args: rest,
@@ -58,6 +60,8 @@ function printHelp() {
58
60
  console.log(` deploy:setup Wire up daily Cloudflare deploy (prompts for hook URL)`);
59
61
  console.log(` deploy:run Trigger the Daily Deploy workflow once now`);
60
62
  console.log(``);
63
+ console.log(` doctor Check Node + dependency versions are current (run BEFORE upgrading or scaffolding)`);
64
+ console.log(``);
61
65
  console.log(` audit Run pre-delivery audit. Flags:`);
62
66
  console.log(` --live <url> Add live-URL checks (SSL, headers, 404, broken links)`);
63
67
  console.log(` --a11y Add axe-core WCAG 2.1 AA scan (with --live)`);
@@ -0,0 +1,137 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { spawn } from 'child_process';
4
+ import { c } from './format.mjs';
5
+
6
+ const PACKAGE_NAME = '@ibalzam/codejitsu-core';
7
+
8
+ // Curated list of deps where being current matters most. We check ALL deps,
9
+ // but these get extra treatment in the output (changelog links, severity).
10
+ const CRITICAL_DEPS = new Set([
11
+ '@ibalzam/codejitsu-core',
12
+ 'astro',
13
+ 'react',
14
+ 'react-dom',
15
+ '@astrojs/react',
16
+ '@astrojs/sitemap',
17
+ 'tailwindcss',
18
+ '@tailwindcss/vite',
19
+ 'typescript',
20
+ ]);
21
+
22
+ /**
23
+ * `codejitsu doctor` — health-check the project's dependency versions vs
24
+ * what's currently published on npm. Catches "Claude scaffolded with stale
25
+ * versions from training data" mistakes before they ship.
26
+ */
27
+ export async function runDoctor() {
28
+ const cwd = process.cwd();
29
+ const pkgPath = path.join(cwd, 'package.json');
30
+ if (!fs.existsSync(pkgPath)) {
31
+ console.error(c.red('✗ No package.json in ' + cwd));
32
+ process.exit(1);
33
+ }
34
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
35
+
36
+ console.log(c.bold(`\nCodejitsu Doctor · ${pkg.name ?? '(unnamed)'}\n`));
37
+
38
+ // ─── Node version ────────────────────────────────────────────────────
39
+ const nodeVersion = process.versions.node;
40
+ const nodeMajor = parseInt(nodeVersion.split('.')[0], 10);
41
+ // Node LTS schedule: even majors. Active LTS = most recent even major.
42
+ // Conservative recommendation: 20 LTS or newer. (Reads schedule from API
43
+ // would be ideal but adds latency; static threshold is fine.)
44
+ const minRecommended = 20;
45
+ console.log(c.bold('◉ Runtime'));
46
+ if (nodeMajor >= minRecommended) {
47
+ console.log(' ' + c.green('✓') + ` Node v${nodeVersion}`);
48
+ } else {
49
+ console.log(' ' + c.yellow('!') + ` Node v${nodeVersion} — recommend ≥ v${minRecommended} LTS`);
50
+ }
51
+ console.log('');
52
+
53
+ // ─── npm outdated ────────────────────────────────────────────────────
54
+ console.log(c.bold('◉ Dependencies'));
55
+ console.log(c.gray(' Running npm outdated… (this hits the registry, may take a few seconds)'));
56
+ console.log('');
57
+
58
+ const result = await runCmd('npm', ['outdated', '--json'], 60_000);
59
+ // npm outdated exits 1 when anything is outdated; that's expected.
60
+ let outdated = {};
61
+ if (result.stdout.trim()) {
62
+ try { outdated = JSON.parse(result.stdout); }
63
+ catch {
64
+ console.log(' ' + c.yellow('!') + ' Could not parse npm outdated output');
65
+ console.log(' ' + c.gray(result.stdout.slice(0, 200)));
66
+ }
67
+ }
68
+
69
+ const entries = Object.entries(outdated);
70
+
71
+ if (entries.length === 0) {
72
+ console.log(' ' + c.green('✓') + ' All dependencies up to date');
73
+ } else {
74
+ const critical = entries.filter(([name]) => CRITICAL_DEPS.has(name));
75
+ const others = entries.filter(([name]) => !CRITICAL_DEPS.has(name));
76
+
77
+ if (critical.length > 0) {
78
+ console.log(c.bold(' Critical:'));
79
+ for (const [name, info] of critical) {
80
+ printDep(name, info, true);
81
+ }
82
+ console.log('');
83
+ }
84
+
85
+ if (others.length > 0) {
86
+ console.log(c.bold(' Other:'));
87
+ for (const [name, info] of others.slice(0, 10)) {
88
+ printDep(name, info, false);
89
+ }
90
+ if (others.length > 10) {
91
+ console.log(c.gray(` … (+${others.length - 10} more)`));
92
+ }
93
+ console.log('');
94
+ }
95
+ }
96
+
97
+ // ─── codejitsu-core specifically ─────────────────────────────────────
98
+ const cjEntry = outdated[PACKAGE_NAME];
99
+ if (cjEntry) {
100
+ console.log(c.bold('◉ ' + PACKAGE_NAME));
101
+ console.log(` Installed: ${cjEntry.current}`);
102
+ console.log(` Latest: ${cjEntry.latest}`);
103
+ console.log(c.gray(` Upgrade: npm install ${PACKAGE_NAME}@latest`));
104
+ console.log(c.gray(` Changelog: https://github.com/ikanc/codejitsu-site-kit/releases`));
105
+ } else if (pkg.dependencies?.[PACKAGE_NAME] || pkg.devDependencies?.[PACKAGE_NAME]) {
106
+ console.log(c.bold('◉ ' + PACKAGE_NAME));
107
+ console.log(' ' + c.green('✓') + ' On latest');
108
+ }
109
+ console.log('');
110
+
111
+ // ─── Tips ─────────────────────────────────────────────────────────────
112
+ if (entries.length > 0) {
113
+ console.log(c.gray('Tip: bump the critical deps first, then re-run `npx codejitsu doctor`.'));
114
+ console.log(c.gray(' A major-version jump (e.g. Astro 5 → 6) needs a migration guide; check the package changelog.'));
115
+ }
116
+ }
117
+
118
+ function printDep(name, info, isCritical) {
119
+ const { current, latest, wanted } = info;
120
+ const arrow = isCritical ? c.yellow('→') : c.gray('→');
121
+ const nameDisplay = isCritical ? c.bold(name) : name;
122
+ const currentDisplay = current === wanted ? current : c.dim(`${current} (npm install gets ${wanted})`);
123
+ console.log(` ${nameDisplay.padEnd(40)} ${currentDisplay} ${arrow} ${c.bold(latest)}`);
124
+ }
125
+
126
+ function runCmd(cmd, args, timeoutMs = 60_000) {
127
+ return new Promise((resolve) => {
128
+ const proc = spawn(cmd, args, { stdio: ['ignore', 'pipe', 'pipe'] });
129
+ let stdout = '';
130
+ let stderr = '';
131
+ proc.stdout.on('data', (d) => { stdout += d.toString(); });
132
+ proc.stderr.on('data', (d) => { stderr += d.toString(); });
133
+ const t = setTimeout(() => { proc.kill(); resolve({ code: 1, stdout, stderr: 'timeout' }); }, timeoutMs);
134
+ proc.on('close', (code) => { clearTimeout(t); resolve({ code, stdout, stderr }); });
135
+ proc.on('error', (err) => { clearTimeout(t); resolve({ code: 1, stdout, stderr: err.message }); });
136
+ });
137
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ibalzam/codejitsu-core",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "type": "module",
5
5
  "description": "Shared core for Codejitsu Astro sites — reusable code and Claude-facing instructions for blog, SEO, images, deploy, and llms.txt.",
6
6
  "keywords": [