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.
- package/README.md +165 -0
- package/assets/design-system/design-system.css +833 -0
- package/assets/design-system/theme-example.css +83 -0
- package/bin/explainmyrepo.mjs +115 -0
- package/kb/ask-kb.mjs +1487 -0
- package/kb/build-kb.mjs +353 -0
- package/kb/corpus-rules.mjs +341 -0
- package/kb/dep-graph.mjs +184 -0
- package/kb/entrypoints.mjs +207 -0
- package/kb/extract-symbols.mjs +322 -0
- package/kb/index-primer.mjs +255 -0
- package/kb/kb-mcp-server.mjs +186 -0
- package/kb/kb.config.mjs +1362 -0
- package/kb/make-dropin.mjs +224 -0
- package/kb/resolve-deps.mjs +126 -0
- package/package.json +52 -0
- package/src/brain.mjs +298 -0
- package/src/build-context.mjs +66 -0
- package/src/claude.mjs +97 -0
- package/src/env.mjs +77 -0
- package/src/orchestrator.mjs +419 -0
- package/src/run-tool.mjs +49 -0
- package/tools/CONTRACT.md +301 -0
- package/tools/assemble-page.mjs +631 -0
- package/tools/build-kb.mjs +159 -0
- package/tools/clone-repo.mjs +161 -0
- package/tools/deploy.mjs +160 -0
- package/tools/generate-image.mjs +280 -0
- package/tools/make-diagrams.mjs +835 -0
- package/tools/make-favicon.mjs +145 -0
- package/tools/make-pack.mjs +295 -0
- package/tools/make-social-card.mjs +198 -0
- package/tools/notify.mjs +327 -0
- package/tools/publish-repo.mjs +156 -0
- package/tools/quality-grade.mjs +746 -0
- package/tools/readme-enhance.mjs +310 -0
- package/tools/repo-seo.mjs +143 -0
|
@@ -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();
|