explainmyrepo 0.1.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.
@@ -0,0 +1,83 @@
1
+ /* ============================================================================
2
+ THEME OVERRIDE — example: "PhotonLayer / Prism"
3
+ ----------------------------------------------------------------------------
4
+ This is the ENTIRE per-repo art direction. Load it AFTER design-system.css:
5
+
6
+ <link rel="stylesheet" href="design-system.css" />
7
+ <link rel="stylesheet" href="theme.css" /> (this file)
8
+
9
+ It overrides ~14 EXPRESSION knobs and NOTHING in the skeleton. Every layout,
10
+ breakpoint, spacing rhythm, and accessibility behaviour is inherited
11
+ unchanged — only the look changes. Swap this block to re-skin any explainer.
12
+
13
+ The metaphor here: a near-black optical bench with a full visible-light
14
+ spectrum as the signature gradient (lenses, rays, prisms). Distinct from the
15
+ neutral default and from every sibling explainer.
16
+ ============================================================================ */
17
+
18
+ /* Type personality: geometric-modern (Space Grotesk + Inter + JetBrains Mono).
19
+ Load the fonts in <head>; the skeleton already references these var names. */
20
+ @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap');
21
+
22
+ :root {
23
+ color-scheme: dark;
24
+
25
+ /* --- Substrate: true optical-bench black with a faint blue cast --- */
26
+ --bg: #08080d;
27
+ --bg-2: #0c0c14;
28
+ --surface: #11111c;
29
+ --surface-2: #161624;
30
+ --ridge: #262640;
31
+
32
+ /* --- Ink: cool off-white (never warm) --- */
33
+ --ink: #eef0fb;
34
+ --ink-2: #c3c6dd;
35
+ --muted: #8b8fab;
36
+ --faint: #5a5e7a;
37
+ --on-accent: #08080d;
38
+
39
+ /* --- Accents drawn from the spectrum --- */
40
+ --accent: #1fd6d6; /* cyan — links, primary */
41
+ --accent-2: #3ee68a; /* green — hover, secondary */
42
+ --accent-3: #a06bff; /* violet — micro-labels */
43
+
44
+ /* --- The signature gradient: the FULL prism, not a single hue --- */
45
+ --spectrum: linear-gradient(90deg,
46
+ #a06bff 0%, #6b7bff 16%, #4f8cff 30%, #1fd6d6 46%,
47
+ #3ee68a 60%, #ffd23e 74%, #ff9a3e 86%, #ff5e62 100%);
48
+
49
+ /* --- Type personality knobs (geometric-modern, not industrial caps) --- */
50
+ --display: "Space Grotesk", ui-sans-serif, system-ui, sans-serif;
51
+ --sans: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
52
+ --mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;
53
+ --display-weight: 700;
54
+ --display-case: none;
55
+ --display-tracking: -0.02em;
56
+
57
+ /* --- Shape: a touch crisper than default --- */
58
+ --radius: 16px;
59
+ --radius-s: 10px;
60
+ }
61
+
62
+ /* ----------------------------------------------------------------------------
63
+ OPTIONAL: a single motif flourish is the only place a theme may add (not
64
+ override) a rule — kept tiny and self-contained. Here, the prismatic edge
65
+ under the sticky header. Skeleton classes are never touched.
66
+ ---------------------------------------------------------------------------- */
67
+ .site-head { border-bottom: 0; box-shadow: 0 1px 0 0 var(--line); }
68
+ .site-head::after { content: ""; display: block; height: 2px; background: var(--spectrum); opacity: 0.9; }
69
+
70
+ /* ----------------------------------------------------------------------------
71
+ For reference — a LIGHT identity (e.g. ruvn's "warm dossier") is the SAME
72
+ technique: flip color-scheme + invert the substrate/ink roles. Example:
73
+
74
+ :root {
75
+ color-scheme: light;
76
+ --bg:#f4ecdc; --bg-2:#efe5d2; --surface:#fbf6ea; --surface-2:#f0e7d4; --ridge:#d8c9a8;
77
+ --ink:#3a2f25; --ink-2:#5a4a39; --muted:#84715a; --faint:#a8966f; --on-accent:#fbf6ea;
78
+ --accent:#9e2b25; --accent-2:#3f6b3a; --accent-3:#b07d35;
79
+ --spectrum:linear-gradient(96deg,#9e2b25,#3f6b3a);
80
+ --display:"Spectral",Georgia,serif; --sans:"Source Sans 3",system-ui,sans-serif;
81
+ --mono:"Spline Sans Mono",ui-monospace,monospace;
82
+ }
83
+ ---------------------------------------------------------------------------- */
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env node
2
+ // bin/explainmyrepo.mjs — the `npx explainmyrepo <github-url>` entry point.
3
+ //
4
+ // Turns any GitHub repo into a bespoke, art-directed explainer site — for humans AND their AI — in
5
+ // one command. It runs the deterministic pipeline tools (tools/*.mjs, per tools/CONTRACT.md) in
6
+ // order and calls Claude in the loop to author the judgment slots (concept, content, the Station-4
7
+ // image briefs + diagram ASCII, and the primer). See src/orchestrator.mjs for the station map.
8
+ //
9
+ // Usage: npx explainmyrepo <github-url> [flags]
10
+ // Reads credentials from the environment / a gitignored .env (never printed): ANTHROPIC_API_KEY
11
+ // (or CLAUDE_API_KEY), OPENAI_API_KEY (or OPEN_AI_KEY), NETLIFY_AUTH_TOKEN, plus GitHub via `gh`.
12
+
13
+ import { readFileSync } from 'node:fs';
14
+ import path from 'node:path';
15
+ import { fileURLToPath } from 'node:url';
16
+ import { run } from '../src/orchestrator.mjs';
17
+
18
+ const HERE = path.dirname(fileURLToPath(import.meta.url));
19
+ function version() {
20
+ try { return JSON.parse(readFileSync(path.join(HERE, '..', 'package.json'), 'utf8')).version || '0.0.0'; }
21
+ catch { return '0.0.0'; }
22
+ }
23
+
24
+ const HELP = `
25
+ explainmyrepo — turn any GitHub repo into a bespoke explainer site (for humans + AI).
26
+
27
+ USAGE
28
+ npx explainmyrepo <github-url> [options]
29
+
30
+ ARGUMENTS
31
+ <github-url> https://github.com/owner/name (also git@… or bare owner/name)
32
+
33
+ OPTIONS
34
+ --out <dir> build directory (default: ./explainer-builds/<repo>)
35
+ --model <id> Anthropic model for the brain steps (default: claude-sonnet-4-6)
36
+ --no-deploy skip the deploy station (build + grade locally only)
37
+ --no-publish skip publish-repo + repo-seo (no GitHub explainer repo)
38
+ --no-notify skip the email notify station
39
+ --no-quality skip the local vision quality gate (faster dry iterations)
40
+ --no-refine grade once but don't auto-iterate the copy to lift weak axes
41
+ --max-refine <n> max content-refine passes when below the quality bar (default 2)
42
+ --register-kb OPT-IN: if the repo isn't a kb.config target, inject a generated
43
+ entry into kb/kb.config.mjs (the one step that edits the shared registry)
44
+ --from <station> resume: start at this station id (needs an existing --out build)
45
+ --to <station> stop after this station id
46
+ --only <station> run just one station id
47
+ --dry-run print the station plan and exit (no tools, no API calls)
48
+ -h, --help show this help
49
+ -v, --version print version
50
+
51
+ STATIONS (ids for --from/--to/--only)
52
+ clone-repo · kb:register · build-kb · primer · concept · content · visual-brief ·
53
+ generate-image · make-favicon · make-social-card · make-diagrams · assemble-page ·
54
+ make-pack · quality-grade · deploy · publish-repo · repo-seo · readme-enhance · notify
55
+
56
+ ENV (from .env, never printed)
57
+ ANTHROPIC_API_KEY|CLAUDE_API_KEY (required — brain steps)
58
+ OPENAI_API_KEY|OPEN_AI_KEY (images + vision grade)
59
+ NETLIFY_AUTH_TOKEN (deploy) gh auth / GH_TOKEN (publish, repo-seo, readme)
60
+ SMTP_USER/SMTP_PASS/EMAIL_TO (notify — optional; failure is non-blocking)
61
+
62
+ EXAMPLES
63
+ npx explainmyrepo https://github.com/owner/cool-lib
64
+ npx explainmyrepo owner/cool-lib --no-deploy --no-publish
65
+ npx explainmyrepo owner/cool-lib --from concept --out ./explainer-builds/cool-lib
66
+ `;
67
+
68
+ const BOOL_FLAGS = new Set(['--no-deploy', '--no-publish', '--no-notify', '--no-quality', '--no-refine', '--register-kb', '--dry-run', '-h', '--help', '-v', '--version']);
69
+ const VALUE_FLAGS = new Set(['--out', '--model', '--from', '--to', '--only', '--max-refine']);
70
+
71
+ function parseArgs(argv) {
72
+ const opts = {};
73
+ const positional = [];
74
+ for (let i = 0; i < argv.length; i++) {
75
+ const a = argv[i];
76
+ if (a === '-h' || a === '--help') { opts.help = true; continue; }
77
+ if (a === '-v' || a === '--version') { opts.version = true; continue; }
78
+ if (VALUE_FLAGS.has(a)) {
79
+ const v = argv[++i];
80
+ if (v === undefined) throw new Error(`${a} needs a value`);
81
+ opts[a.replace(/^--/, '').replace(/-([a-z])/g, (_, c) => c.toUpperCase())] = v;
82
+ continue;
83
+ }
84
+ const eq = a.indexOf('=');
85
+ if (a.startsWith('--') && eq !== -1 && VALUE_FLAGS.has(a.slice(0, eq))) {
86
+ opts[a.slice(2, eq).replace(/-([a-z])/g, (_, c) => c.toUpperCase())] = a.slice(eq + 1);
87
+ continue;
88
+ }
89
+ if (BOOL_FLAGS.has(a)) { opts[a.replace(/^--/, '').replace(/-([a-z])/g, (_, c) => c.toUpperCase())] = true; continue; }
90
+ if (a.startsWith('-')) throw new Error(`unknown flag: ${a}`);
91
+ positional.push(a);
92
+ }
93
+ return { opts, positional };
94
+ }
95
+
96
+ async function main() {
97
+ let parsed;
98
+ try { parsed = parseArgs(process.argv.slice(2)); }
99
+ catch (e) { process.stderr.write(`error: ${e.message}\n\nRun --help for usage.\n`); process.exit(2); }
100
+ const { opts, positional } = parsed;
101
+
102
+ if (opts.version) { process.stdout.write(version() + '\n'); return; }
103
+ if (opts.help || positional.length === 0) { process.stdout.write(HELP); process.exit(opts.help ? 0 : 2); }
104
+
105
+ const url = positional[0];
106
+ try {
107
+ const res = await run(url, opts);
108
+ process.exit(res.ok ? 0 : 1);
109
+ } catch (e) {
110
+ process.stderr.write(`\n\x1b[31merror:\x1b[0m ${e.message}\n`);
111
+ process.exit(1);
112
+ }
113
+ }
114
+
115
+ main();