@codewithjuber/forgekit 0.8.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-plugin/marketplace.json +12 -0
- package/.claude-plugin/plugin.json +20 -0
- package/.codex-plugin/plugin.json +29 -0
- package/.mcp.json +8 -0
- package/ARCHITECTURE.md +314 -0
- package/CHANGELOG.md +467 -0
- package/LICENSE +21 -0
- package/ONBOARDING.md +180 -0
- package/README.md +286 -0
- package/bin/claude-init.sh +90 -0
- package/bin/claude-taste.sh +33 -0
- package/bin/learn-consolidate.sh +51 -0
- package/brand.json +15 -0
- package/global/CLAUDE.md +31 -0
- package/global/crew/frontend-verifier.md +36 -0
- package/global/crew/independent-reviewer.md +29 -0
- package/global/crew/scout.md +24 -0
- package/global/crew/verifier.md +28 -0
- package/global/guards/_guardlib.sh +36 -0
- package/global/guards/cortex.sh +14 -0
- package/global/guards/cost-budget.sh +41 -0
- package/global/guards/doom-loop.sh +25 -0
- package/global/guards/format-on-edit.sh +32 -0
- package/global/guards/lean-guard.sh +20 -0
- package/global/guards/protect-paths.sh +45 -0
- package/global/guards/recall-load.sh +22 -0
- package/global/guards/secret-redact.sh +18 -0
- package/global/guards/session-learner.sh +72 -0
- package/global/recall/MEMORY.md +7 -0
- package/global/rules/self-correction.md +17 -0
- package/global/rules/stack-notes.md +24 -0
- package/global/rules/tech-currency.md +19 -0
- package/global/settings.template.json +183 -0
- package/global/statusline.sh +51 -0
- package/global/taste/brutalist.json +9 -0
- package/global/taste/brutalist.md +19 -0
- package/global/taste/corporate.json +9 -0
- package/global/taste/corporate.md +19 -0
- package/global/taste/editorial.json +9 -0
- package/global/taste/editorial.md +19 -0
- package/global/taste/minimalist.json +9 -0
- package/global/taste/minimalist.md +20 -0
- package/global/taste/playful.json +9 -0
- package/global/taste/playful.md +19 -0
- package/global/tools/atlas/SKILL.md +27 -0
- package/global/tools/code-modernization/SKILL.md +275 -0
- package/global/tools/code-modernization/references/cost-impact-preflight.md +54 -0
- package/global/tools/code-modernization/references/design-patterns-cheatsheet.md +24 -0
- package/global/tools/code-modernization/references/research-protocol.md +42 -0
- package/global/tools/code-modernization/scripts/preflight_scan.py +190 -0
- package/global/tools/cognitive-substrate/SKILL.md +56 -0
- package/global/tools/cognitive-substrate/references/capability-map.md +17 -0
- package/global/tools/cost-guard/SKILL.md +50 -0
- package/global/tools/design-md/SKILL.md +54 -0
- package/global/tools/dev-radar/SKILL.md +56 -0
- package/global/tools/explore-plan-code/SKILL.md +24 -0
- package/global/tools/lean/SKILL.md +41 -0
- package/global/tools/recall/SKILL.md +31 -0
- package/global/tools/reuse-first/SKILL.md +64 -0
- package/global/tools/self-improve/SKILL.md +44 -0
- package/global/tools/taste/SKILL.md +26 -0
- package/global/tools/tech-selector/SKILL.md +35 -0
- package/global/tools/ui-workflow/SKILL.md +44 -0
- package/hooks/hooks.json +107 -0
- package/install.sh +88 -0
- package/package.json +93 -0
- package/public/index.html +45 -0
- package/scripts/build-pages.mjs +180 -0
- package/scripts/bump.mjs +322 -0
- package/skills/cognitive-substrate/SKILL.md +56 -0
- package/skills/cognitive-substrate/references/capability-map.md +17 -0
- package/source/mcp.json +10 -0
- package/source/rules.json +106 -0
- package/source/substrate.json +41 -0
- package/src/adjudicate.js +84 -0
- package/src/anchor.js +210 -0
- package/src/atlas.js +487 -0
- package/src/brain.js +84 -0
- package/src/brand.js +25 -0
- package/src/cli.js +1509 -0
- package/src/context.js +273 -0
- package/src/cortex.js +251 -0
- package/src/cortex_distill.js +55 -0
- package/src/cortex_features.js +81 -0
- package/src/cortex_hook.js +197 -0
- package/src/cortex_hook_main.js +139 -0
- package/src/cortex_mcp.js +352 -0
- package/src/cost_report.js +271 -0
- package/src/dash.html +396 -0
- package/src/dash.js +220 -0
- package/src/diagnose.js +0 -0
- package/src/doctor.js +315 -0
- package/src/embed.js +244 -0
- package/src/emit/_shared.js +39 -0
- package/src/emit/aider.js +22 -0
- package/src/emit/claude.js +44 -0
- package/src/emit/codex.js +17 -0
- package/src/emit/continue.js +28 -0
- package/src/emit/copilot.js +12 -0
- package/src/emit/cursor.js +23 -0
- package/src/emit/gemini.js +40 -0
- package/src/emit/mcp.js +94 -0
- package/src/emit/windsurf.js +22 -0
- package/src/emit/zed.js +34 -0
- package/src/eval.js +47 -0
- package/src/extract.js +82 -0
- package/src/harden.js +44 -0
- package/src/imagine.js +301 -0
- package/src/init.js +178 -0
- package/src/lean.js +149 -0
- package/src/ledger.js +475 -0
- package/src/ledger_bridge.js +279 -0
- package/src/ledger_read.js +152 -0
- package/src/ledger_store.js +360 -0
- package/src/lessons.js +185 -0
- package/src/lessons_store.js +137 -0
- package/src/metrics.js +54 -0
- package/src/model_tiers.js +17 -0
- package/src/model_tiers.json +39 -0
- package/src/predictor.js +143 -0
- package/src/preflight.js +410 -0
- package/src/providers.js +320 -0
- package/src/recall.js +103 -0
- package/src/reuse.js +0 -0
- package/src/route.js +323 -0
- package/src/scope.js +122 -0
- package/src/skillgate.js +89 -0
- package/src/speclock.js +64 -0
- package/src/substrate.js +492 -0
- package/src/sync.js +132 -0
- package/src/taste.js +55 -0
- package/src/uicheck.js +96 -0
- package/src/uifingerprint.js +861 -0
- package/src/uivisual.js +334 -0
- package/src/util.js +71 -0
- package/src/verify.js +117 -0
- package/templates/project-layer/.claude/settings.json +22 -0
- package/templates/project-layer/.claude/skills/hostlelo-deploy/SKILL.md +38 -0
- package/templates/project-layer/AGENTS.md +28 -0
- package/templates/project-layer/CLAUDE.md +40 -0
package/install.sh
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Forge installer — idempotent, reversible, offline.
|
|
3
|
+
# bash install.sh install (symlink global/ into ~/.forge and ~/.claude, put `forge` on PATH)
|
|
4
|
+
# bash install.sh --dry-run print what it would do, change nothing
|
|
5
|
+
# bash install.sh --uninstall remove Forge's own symlinks (never touches your other files)
|
|
6
|
+
# It never downloads anything and never edits settings.json for you — it prints the
|
|
7
|
+
# hook/statusline block to merge by hand, so your existing config is untouched.
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
11
|
+
FORGE_HOME="${FORGE_HOME:-$HOME/.forge}"
|
|
12
|
+
CLAUDE_DIR="$HOME/.claude"
|
|
13
|
+
BIN_DIR="$HOME/.local/bin"
|
|
14
|
+
STAMP="$(date +%Y%m%d-%H%M%S)"
|
|
15
|
+
|
|
16
|
+
DRY=0; MODE=install
|
|
17
|
+
for arg in "$@"; do
|
|
18
|
+
case "$arg" in
|
|
19
|
+
--dry-run) DRY=1 ;;
|
|
20
|
+
--uninstall) MODE=uninstall ;;
|
|
21
|
+
-h|--help) sed -n '2,9p' "$0"; exit 0 ;;
|
|
22
|
+
*) echo "unknown option: $arg" >&2; exit 2 ;;
|
|
23
|
+
esac
|
|
24
|
+
done
|
|
25
|
+
|
|
26
|
+
say() { printf ' %s\n' "$*"; }
|
|
27
|
+
act() { if [ "$DRY" = 1 ]; then say "[dry-run] $*"; else eval "$*"; fi; }
|
|
28
|
+
|
|
29
|
+
# link SRC DEST — back up an existing real file/dir, then symlink.
|
|
30
|
+
link() {
|
|
31
|
+
local src="$1" dest="$2"
|
|
32
|
+
[ -e "$src" ] || { say "skip (missing in bundle): $src"; return; }
|
|
33
|
+
if [ -e "$dest" ] && [ ! -L "$dest" ]; then
|
|
34
|
+
act "mv \"$dest\" \"$dest.forge-bak-$STAMP\""; say "backed up existing $dest"
|
|
35
|
+
fi
|
|
36
|
+
act "mkdir -p \"$(dirname "$dest")\""
|
|
37
|
+
act "ln -sfn \"$src\" \"$dest\""
|
|
38
|
+
say "linked $dest -> $src"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# unlink DEST — remove only if it is a symlink pointing back into this repo.
|
|
42
|
+
unlink_ours() {
|
|
43
|
+
local dest="$1"
|
|
44
|
+
if [ -L "$dest" ] && case "$(readlink "$dest")" in "$REPO"/*) true;; *) false;; esac; then
|
|
45
|
+
act "rm -f \"$dest\""; say "removed $dest"
|
|
46
|
+
fi
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
install_forge() {
|
|
50
|
+
say "Installing Forge from $REPO"
|
|
51
|
+
link "$REPO/global" "$FORGE_HOME"
|
|
52
|
+
for d in "$REPO"/global/tools/*/; do [ -d "$d" ] && link "$d" "$CLAUDE_DIR/skills/$(basename "$d")"; done
|
|
53
|
+
for f in "$REPO"/global/crew/*.md; do [ -e "$f" ] && link "$f" "$CLAUDE_DIR/agents/$(basename "$f")"; done
|
|
54
|
+
link "$REPO/src/cli.js" "$BIN_DIR/forge"
|
|
55
|
+
[ "$DRY" = 1 ] || chmod +x "$REPO/src/cli.js" 2>/dev/null || true
|
|
56
|
+
|
|
57
|
+
case ":$PATH:" in *":$BIN_DIR:"*) : ;; *) say "note: add $BIN_DIR to PATH (e.g. echo 'export PATH=\"\$HOME/.local/bin:\$PATH\"' >> ~/.zshrc)";; esac
|
|
58
|
+
|
|
59
|
+
cat <<EOF
|
|
60
|
+
|
|
61
|
+
Done. Guards + statusline need ONE manual merge into $CLAUDE_DIR/settings.json
|
|
62
|
+
(kept manual so your existing settings are never clobbered):
|
|
63
|
+
|
|
64
|
+
"statusLine": { "type": "command", "command": "bash $FORGE_HOME/statusline.sh" },
|
|
65
|
+
"hooks": {
|
|
66
|
+
"UserPromptSubmit": [ { "hooks": [ { "type": "command",
|
|
67
|
+
"command": "bash $FORGE_HOME/guards/cortex.sh preflight" } ] } ],
|
|
68
|
+
"PreToolUse": [ { "matcher": "Edit|Write|MultiEdit|Bash",
|
|
69
|
+
"hooks": [ { "type": "command", "command": "bash $FORGE_HOME/guards/protect-paths.sh" } ] } ],
|
|
70
|
+
"PostToolUse": [ { "matcher": "Edit|Write|MultiEdit",
|
|
71
|
+
"hooks": [ { "type": "command", "command": "bash $FORGE_HOME/guards/format-on-edit.sh" } ] } ]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
Or install the plugin instead (guards auto-wire): /plugin marketplace add <this-repo> then /plugin install forgekit.
|
|
75
|
+
Run \`forge doctor\` to verify.
|
|
76
|
+
EOF
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
uninstall_forge() {
|
|
80
|
+
say "Uninstalling Forge (symlinks only; your files are untouched)"
|
|
81
|
+
for d in "$REPO"/global/tools/*/; do [ -d "$d" ] && unlink_ours "$CLAUDE_DIR/skills/$(basename "$d")"; done
|
|
82
|
+
for f in "$REPO"/global/crew/*.md; do [ -e "$f" ] && unlink_ours "$CLAUDE_DIR/agents/$(basename "$f")"; done
|
|
83
|
+
unlink_ours "$BIN_DIR/forge"
|
|
84
|
+
unlink_ours "$FORGE_HOME"
|
|
85
|
+
say "Done. Any backed-up files remain as *.forge-bak-* next to their originals."
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
[ "$MODE" = uninstall ] && uninstall_forge || install_forge
|
package/package.json
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codewithjuber/forgekit",
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "One brain for every AI coding agent \u2014 the cognitive substrate every frozen model is missing (proof-carrying memory, impact foresight, enforced guardrails), authored once and delivered as native config to Claude Code, Codex, Cursor, Gemini, Aider, and more.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"forge": "src/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=20"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": "./src/cli.js",
|
|
14
|
+
"./model-tiers": "./src/model_tiers.js",
|
|
15
|
+
"./package.json": "./package.json"
|
|
16
|
+
},
|
|
17
|
+
"author": "CodeWithJuber",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/CodeWithJuber/forgekit.git"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/CodeWithJuber/forgekit/issues"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/CodeWithJuber/forgekit#readme",
|
|
27
|
+
"funding": "https://github.com/sponsors/CodeWithJuber",
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public",
|
|
31
|
+
"provenance": true
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"src",
|
|
35
|
+
"source",
|
|
36
|
+
"global",
|
|
37
|
+
"templates",
|
|
38
|
+
"plugin",
|
|
39
|
+
"hooks",
|
|
40
|
+
".claude-plugin",
|
|
41
|
+
".codex-plugin",
|
|
42
|
+
"skills",
|
|
43
|
+
".mcp.json",
|
|
44
|
+
"bin",
|
|
45
|
+
"brand.json",
|
|
46
|
+
"install.sh",
|
|
47
|
+
"README.md",
|
|
48
|
+
"ARCHITECTURE.md",
|
|
49
|
+
"ONBOARDING.md",
|
|
50
|
+
"CHANGELOG.md",
|
|
51
|
+
"LICENSE",
|
|
52
|
+
"public",
|
|
53
|
+
"scripts"
|
|
54
|
+
],
|
|
55
|
+
"scripts": {
|
|
56
|
+
"test": "node --test test/*.test.js",
|
|
57
|
+
"bench": "node bench/bench.mjs",
|
|
58
|
+
"lint": "biome lint .",
|
|
59
|
+
"format": "biome format --write .",
|
|
60
|
+
"check": "biome check .",
|
|
61
|
+
"check:fix": "biome check --write .",
|
|
62
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
63
|
+
"coverage": "node --test --experimental-test-coverage test/*.test.js",
|
|
64
|
+
"bump": "node scripts/bump.mjs",
|
|
65
|
+
"forge": "node src/cli.js",
|
|
66
|
+
"pages:build": "node scripts/build-pages.mjs",
|
|
67
|
+
"pages:build:live": "BUILD_PAGES_LIVE=1 node scripts/build-pages.mjs"
|
|
68
|
+
},
|
|
69
|
+
"keywords": [
|
|
70
|
+
"ai-agents",
|
|
71
|
+
"claude-code",
|
|
72
|
+
"claude-code-plugin",
|
|
73
|
+
"codex",
|
|
74
|
+
"cursor",
|
|
75
|
+
"agents-md",
|
|
76
|
+
"agent-memory",
|
|
77
|
+
"team-memory",
|
|
78
|
+
"crdt",
|
|
79
|
+
"blast-radius",
|
|
80
|
+
"cost-optimization",
|
|
81
|
+
"code-quality",
|
|
82
|
+
"mcp",
|
|
83
|
+
"cli",
|
|
84
|
+
"zero-dependency",
|
|
85
|
+
"llm",
|
|
86
|
+
"developer-tools"
|
|
87
|
+
],
|
|
88
|
+
"devDependencies": {
|
|
89
|
+
"@biomejs/biome": "2.5.2",
|
|
90
|
+
"@types/node": "^26",
|
|
91
|
+
"typescript": "^6"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>forgekit status — live repository data</title><meta name="description" content="One config for every AI coding agent — a cross-tool config layer plus a cognitive substrate (memory, blast-radius, guardrails) for Claude Code, Codex, Cursor, Gemini, Aider, and more."><meta name="theme-color" content="#171310"><style>
|
|
2
|
+
:root{--ink:#171310;--surface:#201a15;--surface-2:#272019;--line:#372c22;--faint:#7d7263;--muted:#a99e90;--ember:#f26430;--fg:#f2ede7;--r-s:6px;--r-m:12px;--r-pill:999px;--shadow:0 24px 64px rgba(23,19,16,.56);--mono:ui-monospace,"SF Mono",SFMono-Regular,Menlo,Consolas,monospace;--sans:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif}
|
|
3
|
+
*{box-sizing:border-box}
|
|
4
|
+
body{margin:0;background:radial-gradient(960px 400px at 85% -10%,rgba(242,100,48,.16),transparent 70%),var(--ink);color:var(--fg);font:16px/1.65 var(--sans);-webkit-font-smoothing:antialiased}
|
|
5
|
+
::selection{background:var(--ember);color:var(--ink)}
|
|
6
|
+
p{margin:16px 0}
|
|
7
|
+
a{color:inherit}
|
|
8
|
+
a:focus-visible,button:focus-visible{outline:2px solid var(--ember);outline-offset:4px;border-radius:var(--r-s)}
|
|
9
|
+
.wrap{width:min(1080px,calc(100% - 48px));margin:0 auto}
|
|
10
|
+
.nav{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:16px 0;border-bottom:1px solid var(--line)}
|
|
11
|
+
.brand{font:600 16px var(--mono);text-decoration:none}
|
|
12
|
+
.brand em{font-style:normal;color:var(--ember)}
|
|
13
|
+
.links{display:flex;gap:24px;flex-wrap:wrap;font-size:14px}
|
|
14
|
+
.links a{color:var(--muted);text-decoration:none}
|
|
15
|
+
.links a:hover{color:var(--fg)}
|
|
16
|
+
.hero{padding:64px 0 48px}
|
|
17
|
+
.eyebrow{font:500 13px var(--mono);color:var(--ember);letter-spacing:.12em;text-transform:uppercase;margin:0 0 16px}
|
|
18
|
+
h1{font-size:clamp(32px,5vw,56px);line-height:1.05;letter-spacing:-.02em;margin:0 0 16px;max-width:800px;font-weight:700}
|
|
19
|
+
.lead{font-size:18px;color:var(--muted);max-width:680px;margin:0 0 32px}
|
|
20
|
+
.btn{display:inline-block;text-decoration:none;font:600 15px var(--sans);border-radius:var(--r-pill);padding:12px 28px;border:1px solid var(--line)}
|
|
21
|
+
.btn.primary{background:var(--ember);border-color:var(--ember);color:var(--ink)}
|
|
22
|
+
.btn.primary:hover{background:var(--fg);border-color:var(--fg)}
|
|
23
|
+
.btn:not(.primary):hover{border-color:var(--faint)}
|
|
24
|
+
.meta{display:flex;gap:12px;flex-wrap:wrap;margin-top:32px}
|
|
25
|
+
.chip{font:500 13px var(--mono);color:var(--muted);border:1px solid var(--line);border-radius:var(--r-pill);padding:8px 16px}
|
|
26
|
+
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin:48px 0}
|
|
27
|
+
.cell{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-m);padding:32px 24px}
|
|
28
|
+
.metric{font:600 32px var(--mono);letter-spacing:-.02em}
|
|
29
|
+
.metric em{font-style:normal;color:var(--ember)}
|
|
30
|
+
.cell strong{display:block;margin-top:8px;font-size:14px}
|
|
31
|
+
.muted{color:var(--muted);font-size:14px}
|
|
32
|
+
.src{font:400 12px var(--mono);color:var(--faint);margin-top:12px}
|
|
33
|
+
section{padding:48px 0;border-bottom:1px solid var(--line)}
|
|
34
|
+
h2{font-size:24px;letter-spacing:-.01em;margin:0 0 16px}
|
|
35
|
+
.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-m);padding:32px}
|
|
36
|
+
.terminal{font:400 14px/1.7 var(--mono);overflow-x:auto;white-space:pre;background:var(--surface-2);color:var(--fg);border-radius:var(--r-m);padding:24px;border:1px solid var(--line);box-shadow:var(--shadow)}
|
|
37
|
+
.list{display:grid;gap:12px;padding:0;list-style:none;margin:0}
|
|
38
|
+
.list li{border-left:2px solid var(--ember);padding-left:16px;color:var(--muted);font-size:15px}
|
|
39
|
+
code{font:500 13px var(--mono);background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-s);padding:0 8px}
|
|
40
|
+
footer{padding:32px 0;color:var(--faint);font-size:14px}
|
|
41
|
+
@media(max-width:800px){.grid{grid-template-columns:1fr}}
|
|
42
|
+
</style></head><body><div class="wrap"><header class="nav"><a class="brand" href="../">forge<em>kit</em></a><nav class="links" aria-label="Primary"><a href="../">Landing</a><a href="#quickstart">Quickstart</a><a href="#changes">Latest</a><a href="#sources">Data Sources</a><a href="https://github.com/CodeWithJuber/forgekit">GitHub ↗</a></nav></header><main id="top"><section class="hero" style="border-bottom:0;padding-bottom:0"><p class="eyebrow">@codewithjuber/forgekit · v0.6.0 · Node >=20</p><h1>Live status, straight from the repository.</h1><p class="lead">One config for every AI coding agent — a cross-tool config layer plus a cognitive substrate (memory, blast-radius, guardrails) for Claude Code, Codex, Cursor, Gemini, Aider, and more.</p><p><a class="btn primary" href="#quickstart">Install in 60 seconds</a> <a class="btn" href="https://github.com/CodeWithJuber/forgekit#readme">Read the docs</a></p><div class="meta"><span class="chip">MIT license</span><span class="chip">0 runtime dependencies</span><span class="chip">claude/whitepaper-implementation-plan-x8f7bh @ 26b76cd</span><span class="chip">live GitHub stats disabled</span></div></section><div class="grid" aria-label="Measured outcomes"><article class="cell"><div class="metric"><em>0.43 ms</em></div><strong>blast-radius lookup</strong><p class="muted">Measured from this repo's benchmark report, not a marketing placeholder.</p><p class="src">reports/benchmarks.md</p></article><article class="cell"><div class="metric"><em>118 ms</em></div><strong>pre-action gate</strong><p class="muted">Assumptions, routing, reuse, context, impact, scope, and anchoring.</p><p class="src">reports/benchmarks.md</p></article><article class="cell"><div class="metric"><em>62.1%</em></div><strong>cost saved vs always-premium</strong><p class="muted">Documented from the white-paper prototype and exposed by Forge cost reports.</p><p class="src">whitepaper prototype</p></article></div><section id="quickstart"><h2>Quickstart</h2><div class="terminal">npm install -g @codewithjuber/forgekit
|
|
43
|
+
forge init
|
|
44
|
+
forge doctor
|
|
45
|
+
forge substrate "Change auth validation and update tests"</div></section><section id="changes"><h2>Latest repo changes</h2><div class="card"><ul class="list"><li>Optional embeddings tier (src/embed.js, ADR-0005; ROADMAP "Next"): set</li><li>forge uicheck visual <file-or-url> — the Playwright visual loop</li><li>Ledger read-path flip (P2). Reads are now a merged view (legacy ∪ ledger) via the</li></ul><p class="muted">Benchmark sections indexed: 3 · benchmarks file updated 2026-07-07.</p></div></section><section id="sources"><h2>Data Sources</h2><div class="card"><p class="muted">No mock data is used. This page is regenerated from repository files during CI (generated 2026-07-07T21:15:58.111Z from 26b76cd). Enable <code>BUILD_PAGES_LIVE=1</code> to refresh public GitHub counters with ETag/Last-Modified caching.</p><ul class="list"><li>package.json</li><li>README.md</li><li>CHANGELOG.md</li><li>reports/benchmarks.md</li><li>https://api.github.com/repos/CodeWithJuber/forgekit (optional, no auth, only when BUILD_PAGES_LIVE=1)</li></ul></div></section></main><footer>WCAG-minded semantic HTML, keyboard focus, responsive 320px–1920px+, and reduced-motion-safe. Same design tokens as the landing page — <code>forge uicheck design</code> gates both.</footer></div></body></html>
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Data sources: local package.json, README.md, CHANGELOG.md, reports/benchmarks.md,
|
|
3
|
+
// and optionally https://api.github.com/repos/CodeWithJuber/forgekit when BUILD_PAGES_LIVE=1.
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
import { mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
|
|
9
|
+
const root = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
10
|
+
const out = join(root, "public", "index.html");
|
|
11
|
+
const cacheFile = join(root, ".cache", "pages", "github-repo.json");
|
|
12
|
+
const api = "https://api.github.com/repos/CodeWithJuber/forgekit";
|
|
13
|
+
|
|
14
|
+
const esc = (s) =>
|
|
15
|
+
String(s ?? "").replace(
|
|
16
|
+
/[&<>"]/g,
|
|
17
|
+
(c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c],
|
|
18
|
+
);
|
|
19
|
+
const read = (p) => readFileSync(join(root, p), "utf8");
|
|
20
|
+
const pkg = JSON.parse(read("package.json"));
|
|
21
|
+
const readme = read("README.md");
|
|
22
|
+
const changelog = read("CHANGELOG.md");
|
|
23
|
+
const benchmarks = read("reports/benchmarks.md");
|
|
24
|
+
|
|
25
|
+
function lineMatch(text, re, fallback = "") {
|
|
26
|
+
const m = text.match(re);
|
|
27
|
+
return m?.[1]?.trim() ?? fallback;
|
|
28
|
+
}
|
|
29
|
+
function latestChanges() {
|
|
30
|
+
const section =
|
|
31
|
+
changelog.match(/## \[[^\]]+\][\s\S]*?(?=\n## \[|\n\[Unreleased\]:|$)/)?.[0] ?? "";
|
|
32
|
+
// Strip inline markdown (bold/code) — these bullets render as plain HTML text.
|
|
33
|
+
return [...section.matchAll(/^- (.+)$/gm)].slice(0, 4).map((m) =>
|
|
34
|
+
m[1]
|
|
35
|
+
.trim()
|
|
36
|
+
.replace(/\*\*([^*]+)\*\*/g, "$1")
|
|
37
|
+
.replace(/`([^`]+)`/g, "$1"),
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
function git(args, fallback = "unknown") {
|
|
41
|
+
try {
|
|
42
|
+
return execFileSync("git", args, { cwd: root, encoding: "utf8" }).trim() || fallback;
|
|
43
|
+
} catch {
|
|
44
|
+
return fallback;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function fetchJsonWithRetry(url, { timeoutMs = 5000, attempts = 3 } = {}) {
|
|
48
|
+
let last;
|
|
49
|
+
for (let i = 0; i < attempts; i++) {
|
|
50
|
+
const ac = new AbortController();
|
|
51
|
+
const timer = setTimeout(() => ac.abort(), timeoutMs);
|
|
52
|
+
try {
|
|
53
|
+
const headers = {
|
|
54
|
+
accept: "application/vnd.github+json",
|
|
55
|
+
"user-agent": "forgekit-pages-build",
|
|
56
|
+
};
|
|
57
|
+
try {
|
|
58
|
+
const cached = JSON.parse(readFileSync(cacheFile, "utf8"));
|
|
59
|
+
if (cached.etag) headers["if-none-match"] = cached.etag;
|
|
60
|
+
if (cached.lastModified) headers["if-modified-since"] = cached.lastModified;
|
|
61
|
+
} catch {}
|
|
62
|
+
const res = await fetch(url, { headers, signal: ac.signal });
|
|
63
|
+
if (res.status === 304) return JSON.parse(readFileSync(cacheFile, "utf8")).data;
|
|
64
|
+
if (res.status === 403 || res.status === 429) throw new Error(`rate limited (${res.status})`);
|
|
65
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
66
|
+
const data = await res.json();
|
|
67
|
+
mkdirSync(dirname(cacheFile), { recursive: true });
|
|
68
|
+
writeFileSync(
|
|
69
|
+
cacheFile,
|
|
70
|
+
JSON.stringify(
|
|
71
|
+
{ etag: res.headers.get("etag"), lastModified: res.headers.get("last-modified"), data },
|
|
72
|
+
null,
|
|
73
|
+
2,
|
|
74
|
+
),
|
|
75
|
+
);
|
|
76
|
+
return data;
|
|
77
|
+
} catch (e) {
|
|
78
|
+
last = e;
|
|
79
|
+
await new Promise((r) =>
|
|
80
|
+
setTimeout(r, Math.min(1000, 100 * 2 ** i) + Math.floor(Math.random() * 50)),
|
|
81
|
+
);
|
|
82
|
+
} finally {
|
|
83
|
+
clearTimeout(timer);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
throw last;
|
|
87
|
+
}
|
|
88
|
+
export async function collect({ live = process.env.BUILD_PAGES_LIVE === "1" } = {}) {
|
|
89
|
+
let github = null;
|
|
90
|
+
if (live) {
|
|
91
|
+
try {
|
|
92
|
+
const data = await fetchJsonWithRetry(api);
|
|
93
|
+
github = {
|
|
94
|
+
stars: Number.isFinite(data?.stargazers_count) ? data.stargazers_count : null,
|
|
95
|
+
forks: Number.isFinite(data?.forks_count) ? data.forks_count : null,
|
|
96
|
+
issues: Number.isFinite(data?.open_issues_count) ? data.open_issues_count : null,
|
|
97
|
+
};
|
|
98
|
+
} catch (e) {
|
|
99
|
+
console.warn(`GitHub live data unavailable: ${e.message}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
name: pkg.name,
|
|
104
|
+
version: pkg.version,
|
|
105
|
+
description: pkg.description,
|
|
106
|
+
node: pkg.engines?.node ?? "",
|
|
107
|
+
license: pkg.license,
|
|
108
|
+
deps: Object.keys(pkg.dependencies ?? {}).length,
|
|
109
|
+
commit: git(["rev-parse", "--short", "HEAD"]),
|
|
110
|
+
branch: git(["branch", "--show-current"]),
|
|
111
|
+
generated: new Date().toISOString(),
|
|
112
|
+
github,
|
|
113
|
+
claim: lineMatch(readme, /\*\*(Author[\s\S]*?)\*\*/m, pkg.description),
|
|
114
|
+
speed: lineMatch(readme, /\*\*A full pre-action gate in ([^*]+)\*\*/m, "118 ms"),
|
|
115
|
+
impact: lineMatch(readme, /answers in \*\*([^*]+)\*\*/m, "0.43 ms"),
|
|
116
|
+
saved: lineMatch(readme, /measured \*\*([^*]+)\*\*/m, "62.1% cost saved"),
|
|
117
|
+
benchUpdated: statSync(join(root, "reports/benchmarks.md")).mtime.toISOString().slice(0, 10),
|
|
118
|
+
latest: latestChanges(),
|
|
119
|
+
benchMentions: (benchmarks.match(/^## /gm) ?? []).length,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// The status page shares the landing page's design system verbatim: the same eight
|
|
123
|
+
// color tokens (forge dash palette), a strict 4px spacing base, three radius levels,
|
|
124
|
+
// one shadow. `forge uicheck design public/index.html` enforces this.
|
|
125
|
+
export function render(d) {
|
|
126
|
+
const live = d.github
|
|
127
|
+
? `<span class="chip">${esc(d.github.stars)} stars</span><span class="chip">${esc(d.github.forks)} forks</span><span class="chip">${esc(d.github.issues)} open issues</span>`
|
|
128
|
+
: `<span class="chip">live GitHub stats disabled</span>`;
|
|
129
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>forgekit status — live repository data</title><meta name="description" content="${esc(d.description)}"><meta name="theme-color" content="#171310"><style>
|
|
130
|
+
:root{--ink:#171310;--surface:#201a15;--surface-2:#272019;--line:#372c22;--faint:#7d7263;--muted:#a99e90;--ember:#f26430;--fg:#f2ede7;--r-s:6px;--r-m:12px;--r-pill:999px;--shadow:0 24px 64px rgba(23,19,16,.56);--mono:ui-monospace,"SF Mono",SFMono-Regular,Menlo,Consolas,monospace;--sans:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif}
|
|
131
|
+
*{box-sizing:border-box}
|
|
132
|
+
body{margin:0;background:radial-gradient(960px 400px at 85% -10%,rgba(242,100,48,.16),transparent 70%),var(--ink);color:var(--fg);font:16px/1.65 var(--sans);-webkit-font-smoothing:antialiased}
|
|
133
|
+
::selection{background:var(--ember);color:var(--ink)}
|
|
134
|
+
p{margin:16px 0}
|
|
135
|
+
a{color:inherit}
|
|
136
|
+
a:focus-visible,button:focus-visible{outline:2px solid var(--ember);outline-offset:4px;border-radius:var(--r-s)}
|
|
137
|
+
.wrap{width:min(1080px,calc(100% - 48px));margin:0 auto}
|
|
138
|
+
.nav{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:16px 0;border-bottom:1px solid var(--line)}
|
|
139
|
+
.brand{font:600 16px var(--mono);text-decoration:none}
|
|
140
|
+
.brand em{font-style:normal;color:var(--ember)}
|
|
141
|
+
.links{display:flex;gap:24px;flex-wrap:wrap;font-size:14px}
|
|
142
|
+
.links a{color:var(--muted);text-decoration:none}
|
|
143
|
+
.links a:hover{color:var(--fg)}
|
|
144
|
+
.hero{padding:64px 0 48px}
|
|
145
|
+
.eyebrow{font:500 13px var(--mono);color:var(--ember);letter-spacing:.12em;text-transform:uppercase;margin:0 0 16px}
|
|
146
|
+
h1{font-size:clamp(32px,5vw,56px);line-height:1.05;letter-spacing:-.02em;margin:0 0 16px;max-width:800px;font-weight:700}
|
|
147
|
+
.lead{font-size:18px;color:var(--muted);max-width:680px;margin:0 0 32px}
|
|
148
|
+
.btn{display:inline-block;text-decoration:none;font:600 15px var(--sans);border-radius:var(--r-pill);padding:12px 28px;border:1px solid var(--line)}
|
|
149
|
+
.btn.primary{background:var(--ember);border-color:var(--ember);color:var(--ink)}
|
|
150
|
+
.btn.primary:hover{background:var(--fg);border-color:var(--fg)}
|
|
151
|
+
.btn:not(.primary):hover{border-color:var(--faint)}
|
|
152
|
+
.meta{display:flex;gap:12px;flex-wrap:wrap;margin-top:32px}
|
|
153
|
+
.chip{font:500 13px var(--mono);color:var(--muted);border:1px solid var(--line);border-radius:var(--r-pill);padding:8px 16px}
|
|
154
|
+
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin:48px 0}
|
|
155
|
+
.cell{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-m);padding:32px 24px}
|
|
156
|
+
.metric{font:600 32px var(--mono);letter-spacing:-.02em}
|
|
157
|
+
.metric em{font-style:normal;color:var(--ember)}
|
|
158
|
+
.cell strong{display:block;margin-top:8px;font-size:14px}
|
|
159
|
+
.muted{color:var(--muted);font-size:14px}
|
|
160
|
+
.src{font:400 12px var(--mono);color:var(--faint);margin-top:12px}
|
|
161
|
+
section{padding:48px 0;border-bottom:1px solid var(--line)}
|
|
162
|
+
h2{font-size:24px;letter-spacing:-.01em;margin:0 0 16px}
|
|
163
|
+
.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-m);padding:32px}
|
|
164
|
+
.terminal{font:400 14px/1.7 var(--mono);overflow-x:auto;white-space:pre;background:var(--surface-2);color:var(--fg);border-radius:var(--r-m);padding:24px;border:1px solid var(--line);box-shadow:var(--shadow)}
|
|
165
|
+
.list{display:grid;gap:12px;padding:0;list-style:none;margin:0}
|
|
166
|
+
.list li{border-left:2px solid var(--ember);padding-left:16px;color:var(--muted);font-size:15px}
|
|
167
|
+
code{font:500 13px var(--mono);background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-s);padding:0 8px}
|
|
168
|
+
footer{padding:32px 0;color:var(--faint);font-size:14px}
|
|
169
|
+
@media(max-width:800px){.grid{grid-template-columns:1fr}}
|
|
170
|
+
</style></head><body><div class="wrap"><header class="nav"><a class="brand" href="../">forge<em>kit</em></a><nav class="links" aria-label="Primary"><a href="../">Landing</a><a href="#quickstart">Quickstart</a><a href="#changes">Latest</a><a href="#sources">Data Sources</a><a href="https://github.com/CodeWithJuber/forgekit">GitHub ↗</a></nav></header><main id="top"><section class="hero" style="border-bottom:0;padding-bottom:0"><p class="eyebrow">${esc(d.name)} · v${esc(d.version)} · Node ${esc(d.node)}</p><h1>Live status, straight from the repository.</h1><p class="lead">${esc(d.description)}</p><p><a class="btn primary" href="#quickstart">Install in 60 seconds</a> <a class="btn" href="https://github.com/CodeWithJuber/forgekit#readme">Read the docs</a></p><div class="meta"><span class="chip">${esc(d.license)} license</span><span class="chip">${esc(d.deps)} runtime dependencies</span><span class="chip">${esc(d.branch)} @ ${esc(d.commit)}</span>${live}</div></section><div class="grid" aria-label="Measured outcomes"><article class="cell"><div class="metric"><em>${esc(d.impact)}</em></div><strong>blast-radius lookup</strong><p class="muted">Measured from this repo's benchmark report, not a marketing placeholder.</p><p class="src">reports/benchmarks.md</p></article><article class="cell"><div class="metric"><em>${esc(d.speed)}</em></div><strong>pre-action gate</strong><p class="muted">Assumptions, routing, reuse, context, impact, scope, and anchoring.</p><p class="src">reports/benchmarks.md</p></article><article class="cell"><div class="metric"><em>${esc(d.saved.match(/^[\d.]+\s*%?/)?.[0] ?? d.saved)}</em></div><strong>${esc(d.saved.replace(/^[\d.]+\s*%?\s*/, "") || "routing signal")}</strong><p class="muted">Documented from the white-paper prototype and exposed by Forge cost reports.</p><p class="src">whitepaper prototype</p></article></div><section id="quickstart"><h2>Quickstart</h2><div class="terminal">npm install -g @codewithjuber/forgekit
|
|
171
|
+
forge init
|
|
172
|
+
forge doctor
|
|
173
|
+
forge substrate "Change auth validation and update tests"</div></section><section id="changes"><h2>Latest repo changes</h2><div class="card"><ul class="list">${d.latest.map((x) => `<li>${esc(x)}</li>`).join("")}</ul><p class="muted">Benchmark sections indexed: ${esc(d.benchMentions)} · benchmarks file updated ${esc(d.benchUpdated)}.</p></div></section><section id="sources"><h2>Data Sources</h2><div class="card"><p class="muted">No mock data is used. This page is regenerated from repository files during CI (generated ${esc(d.generated)} from ${esc(d.commit)}). Enable <code>BUILD_PAGES_LIVE=1</code> to refresh public GitHub counters with ETag/Last-Modified caching.</p><ul class="list"><li>package.json</li><li>README.md</li><li>CHANGELOG.md</li><li>reports/benchmarks.md</li><li>${api} (optional, no auth, only when BUILD_PAGES_LIVE=1)</li></ul></div></section></main><footer>WCAG-minded semantic HTML, keyboard focus, responsive 320px–1920px+, and reduced-motion-safe. Same design tokens as the landing page — <code>forge uicheck design</code> gates both.</footer></div></body></html>`;
|
|
174
|
+
}
|
|
175
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
176
|
+
const data = await collect();
|
|
177
|
+
mkdirSync(dirname(out), { recursive: true });
|
|
178
|
+
writeFileSync(out, render(data));
|
|
179
|
+
console.log(`wrote ${out}`);
|
|
180
|
+
}
|