From 9d4a4b01aa2206e16ab39b614ef5a6fd15f56bc6 Mon Sep 17 00:00:00 2001 From: Juber Shaikh <40266375+CodeWithJuber@users.noreply.github.com> Date: Sun, 9 Aug 2026 23:38:21 +0400 Subject: [PATCH] feat(site): redesign the ForgeKit landing page --- .github/workflows/static.yml | 1 + ARCHITECTURE.md | 2 +- CHANGELOG.md | 8 + landing/app.js | 186 ++++ landing/index.html | 1940 +++++++++++++++++++++++++++++++--- scripts/build-pages.mjs | 5 +- scripts/bump.mjs | 7 +- test/bump.test.js | 6 +- test/pages.test.js | 68 +- 9 files changed, 2020 insertions(+), 203 deletions(-) create mode 100644 landing/app.js diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 43233de..e5c0061 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -39,6 +39,7 @@ jobs: BUILD_PAGES_LIVE=1 node scripts/build-pages.mjs mkdir -p _site/status cp landing/index.html _site/index.html + cp landing/app.js _site/app.js cp public/index.html _site/status/index.html # Brand assets referenced by absolute URL from both pages (favicon, # apple-touch-icon, 1200x630 og card). Served from the Pages root. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index c50d304..171eb68 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -554,7 +554,7 @@ from the tree it describes. flowchart LR test["test
100 files"] src["src
94 files"] - landing["landing
60 files"] + landing["landing
61 files"] research["research
35 files"] bench["bench
2 files"] global["global
2 files"] diff --git a/CHANGELOG.md b/CHANGELOG.md index ea0ef28..74ed70c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed + +- **Landing: source-owned technical instrument.** Replaced the delayed, externally pinned + SPA shell with an immediate-rendering, dependency-free editorial page. The new experience + explains memory, foresight, and guardrails through accessible interactive panels; shows + repository-measured evidence and beta limits; and keeps version, palette, install paths, + responsive behavior, and runtime ownership inside the repository’s existing quality gates. + ## [0.31.0] - 2026-08-07 ### Changed diff --git a/landing/app.js b/landing/app.js new file mode 100644 index 0000000..6499c88 --- /dev/null +++ b/landing/app.js @@ -0,0 +1,186 @@ +const capabilities = [ + { + id: "memory", + index: "01", + title: "Context that survives the chat.", + description: + "Forge keeps decisions, lessons, and project state in the repository—so Claude, Codex, Cursor, and the next agent all inherit the same working memory.", + status: "3 records recalled", + rows: [ + ["decision", "Use SQLite for local-first state", "94%"], + ["lesson", "Run schema checks before generation", "88%"], + ["preference", "Keep the CLI dependency-free", "82%"], + ], + }, + { + id: "foresight", + index: "02", + title: "See the blast radius first.", + description: + "Before a meaningful edit, Forge maps likely downstream effects and asks the agent to account for tests, interfaces, documentation, and release surfaces.", + status: "4 surfaces mapped", + rows: [ + ["source", "src/config.js", "changed"], + ["downstream", "generated tool configs", "review"], + ["verification", "doctor + docs checks", "required"], + ], + }, + { + id: "guardrails", + index: "03", + title: "Slow down the irreversible move.", + description: + "Pre-action gates catch destructive commands, missing evidence, and high-cost choices while there is still time to change course—not after the damage is done.", + status: "gate passed in 118 ms", + rows: [ + ["scope", "working tree only", "verified"], + ["risk", "no destructive operation", "clear"], + ["proof", "tests + docs queued", "ready"], + ], + }, +]; + +const installs = [ + { + id: "plugin", + command: + "/plugin marketplace add CodeWithJuber/forgekit\n/plugin install forgekit", + note: "Recommended · ambient guards on every prompt", + }, + { + id: "npm", + command: "npm install -g @codewithjuber/forgekit\nforge init", + note: "Emits every tool’s native config from one source", + }, + { + id: "github", + command: "npm install -g github:CodeWithJuber/forgekit\nforge init", + note: "Install directly from the public repository", + }, +]; + +const capabilityTabs = [...document.querySelectorAll(".capability-tabs [role='tab']")]; +const capabilityPanel = document.querySelector(".capability-panel"); + +function selectCapability(index, { focus = false } = {}) { + const data = capabilities[index]; + if (!data || !capabilityPanel) return; + capabilityTabs.forEach((tab, tabIndex) => { + tab.setAttribute("aria-selected", String(tabIndex === index)); + tab.tabIndex = tabIndex === index ? 0 : -1; + }); + const activeTab = capabilityTabs[index]; + capabilityPanel.setAttribute("aria-labelledby", activeTab.id); + capabilityPanel.querySelector(".console-label").textContent = + `ACTIVE CAPABILITY / ${data.index}`; + capabilityPanel.querySelector("h3").textContent = data.title; + capabilityPanel.querySelector(".capability-copy p").textContent = data.description; + capabilityPanel.querySelector(".capability-status").innerHTML = + ` ${data.status}`; + const records = capabilityPanel.querySelector(".memory-records"); + records.innerHTML = ` +
TYPERECORDSTATE
+ ${data.rows + .map( + ([type, record, state]) => + `
${type}${record}${state}
`, + ) + .join("")}`; + if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches) { + capabilityPanel.animate( + [ + { opacity: 0.35, transform: "translateY(6px)" }, + { opacity: 1, transform: "translateY(0)" }, + ], + { duration: 220, easing: "ease-out" }, + ); + } + if (focus) activeTab.focus(); +} + +capabilityTabs.forEach((tab, index) => { + tab.tabIndex = index === 0 ? 0 : -1; + tab.addEventListener("click", () => selectCapability(index)); + tab.addEventListener("keydown", (event) => { + if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) return; + event.preventDefault(); + const next = + event.key === "Home" + ? 0 + : event.key === "End" + ? capabilityTabs.length - 1 + : (index + (event.key === "ArrowRight" ? 1 : -1) + capabilityTabs.length) % + capabilityTabs.length; + selectCapability(next, { focus: true }); + }); +}); + +const installTabs = [...document.querySelectorAll(".install-tabs [role='tab']")]; +const installPanel = document.querySelector(".terminal-panel"); +const copyButton = installPanel?.querySelector(".terminal-bar button"); +let activeInstall = 0; + +function selectInstall(index, { focus = false } = {}) { + const data = installs[index]; + if (!data || !installPanel) return; + activeInstall = index; + installTabs.forEach((tab, tabIndex) => { + tab.setAttribute("aria-selected", String(tabIndex === index)); + tab.tabIndex = tabIndex === index ? 0 : -1; + }); + const activeTab = installTabs[index]; + installPanel.setAttribute("aria-labelledby", activeTab.id); + installPanel.querySelector("code").innerHTML = data.command + .split("\n") + .map((line) => ` ${line}\n`) + .join(""); + installPanel.querySelector(":scope > p").innerHTML = ` ${data.note}`; + if (copyButton) copyButton.lastChild.textContent = "Copy"; + if (focus) activeTab.focus(); +} + +installTabs.forEach((tab, index) => { + tab.tabIndex = index === 0 ? 0 : -1; + tab.addEventListener("click", () => selectInstall(index)); + tab.addEventListener("keydown", (event) => { + if (!["ArrowLeft", "ArrowRight", "Home", "End"].includes(event.key)) return; + event.preventDefault(); + const next = + event.key === "Home" + ? 0 + : event.key === "End" + ? installTabs.length - 1 + : (index + (event.key === "ArrowRight" ? 1 : -1) + installTabs.length) % + installTabs.length; + selectInstall(next, { focus: true }); + }); +}); + +copyButton?.addEventListener("click", async () => { + try { + await navigator.clipboard.writeText(installs[activeInstall].command); + copyButton.lastChild.textContent = "Copied"; + window.setTimeout(() => { + copyButton.lastChild.textContent = "Copy"; + }, 1800); + } catch { + copyButton.lastChild.textContent = "Select command"; + installPanel.querySelector("code").parentElement.focus(); + } +}); + +const progress = document.querySelector(".scroll-progress"); +let progressFrame = 0; +function updateProgress() { + const scrollable = document.documentElement.scrollHeight - window.innerHeight; + progress.style.transform = `scaleX(${scrollable > 0 ? window.scrollY / scrollable : 0})`; + progressFrame = 0; +} +window.addEventListener( + "scroll", + () => { + if (!progressFrame) progressFrame = window.requestAnimationFrame(updateProgress); + }, + { passive: true }, +); +updateProgress(); diff --git a/landing/index.html b/landing/index.html index e0ee2af..7807791 100644 --- a/landing/index.html +++ b/landing/index.html @@ -1,156 +1,1790 @@ - - - - forgekit — cognitive infrastructure for AI coding agents - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- + + + + ForgeKit — One operating memory for every coding agent + + + + + + + + + + + + + + + + + + + + + + + +
Open source cognitive substrateforgekit v0.31.0 · beta

One operating
memory. Every
coding agent.

ForgeKit gives every AI coding tool the same memory, foresight, and guardrails—without locking your work inside one vendor or one chat window.

Runtime deps
0
Native targets
9
License
MIT
FK / PREFLIGHTSYSTEM READY
01
REQUESTRefactor authentication flow
00:118
  1. 01Memory recalledPASS
  2. 02Blast radius mappedPASS
  3. 03Guardrails checkedPASS
TRACE FK-031-7D4PROCEED →
01 / The substrateState before action

The missing layer between
your intent and your agent.

Models are capable. Their operating context is fragile. ForgeKit supplies the durable layer that travels with the repository and shows up before the next action.

ACTIVE CAPABILITY / 01

Context that survives the chat.

Forge keeps decisions, lessons, and project state in the repository—so Claude, Codex, Cursor, and the next agent all inherit the same working memory.

3 records recalled
TYPERECORDSTATE
decisionUse SQLite for local-first state94%
lessonRun schema checks before generation88%
preferenceKeep the CLI dependency-free82%
02 / The protocolOne request · five checks · one trace

Action should leave evidence.

Forge turns agent behavior into a reviewable sequence. Each meaningful move begins with context and ends with proof.

  1. 01Recall

    Load relevant decisions and lessons.

  2. 02Classify

    Measure scope, cost, and reversibility.

  3. 03Foresee

    Map downstream surfaces before editing.

  4. 04Gate

    Pause risky or under-specified actions.

  5. 05Trace

    Record what changed and how it was verified.

03 / One sourceNine native targets

Change the agent. Keep the operating system.

One source emits each tool’s native configuration. Your rules and memory stay with the project—not the provider.

Plus MCP configuration for Roo Code and VS Code-compatible clients.

04 / Evidence ledgerMeasured, not invented

Fast enough to stay in the loop.

ForgeKit publishes the measurements behind its claims. The numbers below come from repository benchmarks and evaluation reports—not a marketing dashboard.

Pre-action gate
118ms
End-to-end benchmark
Blast-radius scan
0.43ms
Heuristic analysis
Evaluated cost saved
62.1%
Repository evaluation
Runtime dependencies
0
Node.js standard library
Inspect the evidence
05 / Honest limitsProfessional, not magical

The guardrail is not the road.

ForgeKit improves agent judgment; it does not replace yours. The project labels its assumptions so you can decide where to trust, test, or intervene.

  • 01

    Claude Code is the deepest-tested integration. Other targets have less real-world exercise today.

  • 02

    Blast-radius analysis is heuristic. It guides review; it is not a formal dependency proof.

  • 03

    Guardrails are not a sandbox. Keep permissions, review, and backups appropriate to the work.

06 / Start hereAbout sixty seconds

Give the next agent a better starting point.

Install ForgeKit, run forge init in your repository, and keep one shared operating context across every tool.

Open the quickstart
forgekit / install
 /plugin marketplace add CodeWithJuber/forgekit
+ /plugin install forgekit
+

Recommended · ambient guards on every prompt

+ diff --git a/scripts/build-pages.mjs b/scripts/build-pages.mjs index 16a9584..e42d7fe 100644 --- a/scripts/build-pages.mjs +++ b/scripts/build-pages.mjs @@ -176,9 +176,8 @@ export async function collect({ live = process.env.BUILD_PAGES_LIVE === "1" } = // ember/near-black color tokens, one accent, a system font stack. test/pages.test.js // enforces that color + font parity across both public surfaces, plus a non-empty changes // list and no phantom webfont — so the two can't silently drift into two different -// "school-project" looks again. The fluid type/space scale is enforced on this page only: -// the landing page is a built SPA that computes its own scale, and its shell HTML carries -// just the critical-CSS tokens its pre-hydration paint actually uses. +// "school-project" looks again. The fluid type/space scale is enforced on this page only; +// the landing intentionally uses a separate editorial scale while sharing the brand source. export function render(d) { const live = d.github ? `${esc(d.github.stars)} stars${esc(d.github.forks)} forks${esc(d.github.issues)} open issues` diff --git a/scripts/bump.mjs b/scripts/bump.mjs index 0e5e4e7..d95d195 100644 --- a/scripts/bump.mjs +++ b/scripts/bump.mjs @@ -337,7 +337,12 @@ export function applyBump(root, currentVersion, newVersion, date) { const landingRel = "landing/index.html"; const landing = readIfExists(path.join(root, landingRel)); if (landing !== null && /forgekit v\d+\.\d+\.\d+/.test(landing)) { - write(landingRel, landing.replace(/forgekit v\d+\.\d+\.\d+/g, `forgekit v${newVersion}`)); + write( + landingRel, + landing + .replace(/forgekit v\d+\.\d+\.\d+/g, `forgekit v${newVersion}`) + .replace(/("softwareVersion"\s*:\s*")\d+\.\d+\.\d+("?)/g, `$1${newVersion}$2`), + ); } const roadmapRel = "ROADMAP.md"; diff --git a/test/bump.test.js b/test/bump.test.js index 76e0170..cd0433b 100644 --- a/test/bump.test.js +++ b/test/bump.test.js @@ -300,7 +300,10 @@ function makeFixture() { w(".claude-plugin/plugin.json", '{\n "name": "fixture",\n "version": "0.4.0"\n}\n'); w(".codex-plugin/plugin.json", '{\n "name": "fixture",\n "version": "0.4.0"\n}\n'); w("CITATION.cff", 'cff-version: 1.2.0\nversion: 0.4.0\ndate-released: "2026-07-06"\n'); - w("landing/index.html", '
forgekit v0.4.0 · MIT
\n'); + w( + "landing/index.html", + '
forgekit v0.4.0 · MIT
\n', + ); w("ROADMAP.md", "# Roadmap\n\n## Now (`master`, v0.4.0)\n\nSome text.\n"); w("CHANGELOG.md", CHANGELOG); return dir; @@ -334,6 +337,7 @@ test("applyBump updates every version field in a fixture tree", () => { assert.match(read("CITATION.cff"), /^version: 0\.5\.0$/m); assert.match(read("CITATION.cff"), /^date-released: "2026-07-07"$/m); assert.match(read("landing/index.html"), /forgekit v0\.5\.0/); + assert.match(read("landing/index.html"), /"softwareVersion":"0\.5\.0"/); assert.match(read("ROADMAP.md"), /## Now \(`master`, v0\.5\.0\)/); assert.match(read("CHANGELOG.md"), /## \[0\.5\.0\] - 2026-07-07/); } finally { diff --git a/test/pages.test.js b/test/pages.test.js index 0849d8d..b6294fa 100644 --- a/test/pages.test.js +++ b/test/pages.test.js @@ -50,11 +50,8 @@ test("the status page derives its fluid type scale + spacing scale from the form // page may not hand-pick its own font-size or margin/padding/gap magic numbers. // // Scope note: this is enforced on the generated status page only. The landing page - // is now a built SPA (landing/assets/*, loaded from jsDelivr) that computes its own - // scale; its shell HTML carries only the critical-CSS color + font tokens that the - // pre-hydration paint actually consumes. Inlining --fs-N / --sp-N into that shell - // would satisfy this assertion with markup nothing reads — a green test asserting - // nothing. Color and font-stack parity ARE still enforced on both pages above. + // intentionally uses an editorial scale tuned for its product narrative. Color and + // font-stack parity ARE still enforced on both pages above. const norm = (s) => s.replace(/\s+/g, ""); const status = norm(render(await collect({ live: false }))); for (const decl of typeScaleCss().split(";")) @@ -93,15 +90,13 @@ test("landing benchmark metrics are numbers reports/benchmarks.md actually measu for (const m of line.matchAll(/(\d+(?:\.\d+)?)\s*(ms|µs|s)\b/g)) measured.add(`${m[1]} ${m[2]}`); } - // The landing SPA renders its metrics client-side from a built chunk, so the shell - // HTML states none. This no longer demands that a metric be present — it demands that - // any metric the shell DOES state is one reports/benchmarks.md actually measured, so - // the check still bites the moment a hardcoded number reappears. The "numbers must be - // measured" guarantee itself is not lost: src/docs_check.js (check: "benchmarks") - // enforces README <-> reports/benchmarks.md and runs in the same CI gate. - const metrics = [...landing.matchAll(/\s*(\d+(?:\.\d+)?)\s*ms\s*<\/b/g)]; - for (const [, n] of metrics) - assert.ok(measured.has(`${n} ms`), `landing claims ${n} ms but no benchmark row measures it`); + const metrics = [...landing.matchAll(/data-benchmark="(\d+(?:\.\d+)?)\s*(ms|µs|s)"/g)]; + assert.ok(metrics.length > 0, "landing exposes at least one measured benchmark"); + for (const [, n, unit] of metrics) + assert.ok( + measured.has(`${n} ${unit}`), + `landing claims ${n} ${unit} but no benchmark row measures it`, + ); }); // Metadata + freshness enforcement — each assertion below is a defect this change @@ -144,17 +139,13 @@ test("canonical == og:url on both pages", async () => { }); test("landing never states a stale package version", () => { - // KNOWN DEBT: the landing SPA states its version inside a built chunk - // (landing/assets/c-*.js currently say "forgekit v0.27.0" while package.json has moved - // on). That string cannot be corrected from here — the SPA's source is not in this - // repo, only its minified output, and hand-patching a build artifact to satisfy a test - // would be worse than the drift. So this asserts the shell HTML states no WRONG - // version, rather than requiring it to state one. Committing the landing source is the - // real fix, after which the `shown.length > 0` requirement should come back. const { version } = JSON.parse(repo("package.json")); const shown = [...landing.matchAll(/forgekit v(\d+\.\d+\.\d+)/g)].map((m) => m[1]); + assert.ok(shown.length > 0, "landing states its package version"); for (const v of shown) assert.equal(v, version, `landing shows v${v}, package.json is ${version}`); + const schemaVersion = landing.match(/"softwareVersion"\s*:\s*"(\d+\.\d+\.\d+)"/)?.[1]; + assert.equal(schemaVersion, version, "landing structured data matches package.json"); }); test("sticky-nav blur stays compositor-light (<=8px)", () => { @@ -162,29 +153,18 @@ test("sticky-nav blur stays compositor-light (<=8px)", () => { assert.ok(Number(px) <= 8, `backdrop blur ${px}px > 8px is repaint-heavy on scroll`); }); -test("every jsDelivr-pinned landing asset exists in landing/assets", () => { - // The landing shell loads its JS/CSS chunks from jsDelivr pinned to a commit SHA, - // because .github/workflows/static.yml copies only landing/index.html into _site — it - // never deploys landing/assets/. So a pin naming a chunk that isn't in the repo 404s - // the entire site with a green build and no other test noticing. - // - // This deliberately does NOT assert the SHA equals HEAD: the pin is only re-cut when a - // chunk actually changes, so an == HEAD check would fail on every unrelated commit. - // It checks the two things that are always true of a valid pin — a full-length SHA, - // and a file that exists to be served. - const pins = [ - ...landing.matchAll( - /cdn\.jsdelivr\.net\/gh\/CodeWithJuber\/forgekit@([^/]+)\/landing\/assets\/([^"']+)/g, - ), - ]; - assert.ok(pins.length > 0, "landing pins at least one asset"); - for (const [, sha, file] of pins) { - assert.match(sha, /^[0-9a-f]{40}$/, `pin for ${file} must be a full 40-char commit SHA`); - assert.ok( - existsSync(fileURLToPath(new URL(`../landing/assets/${file}`, import.meta.url))), - `landing/index.html pins landing/assets/${file}, which does not exist`, - ); - } +test("landing runtime is source-owned and dependency-free", () => { + assert.doesNotMatch( + landing, + /cdn\.jsdelivr\.net|fonts\.googleapis\.com|esm\.sh/, + "landing must not depend on an external runtime or webfont", + ); + const scripts = [...landing.matchAll(/]+src="([^"]+)"/g)].map((m) => m[1]); + assert.deepEqual(scripts, ["./app.js"], "landing loads only its readable local runtime"); + assert.ok( + existsSync(fileURLToPath(new URL("../landing/app.js", import.meta.url))), + "the local landing runtime exists", + ); }); test("the generated status page is not shipped in the npm tarball", () => {