Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"build:development": "vite build --mode development",
"build:production": "bash ./scripts/build-production.sh",
"build:staging": "bash ./scripts/build-staging.sh",
"docs:llms": "node scripts/generate-llms.mjs",
"preview": "vite preview",
"test": "vitest",
"test:run": "vitest run",
Expand Down
19 changes: 19 additions & 0 deletions public/llms.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# OpenScan

> OpenScan is a trustless, open-source, standalone web-app and multi-chain blockchain explorer for Ethereum and Layer 2 networks. It allows direct interaction with verified smart contracts and supports local development chains.

## Overview

- [OpenScan - Claude Code Instructions](https://raw.githubusercontent.com/openscan-explorer/explorer/8ccaa3283333eb4616efe3255398a77ca6d6ef93/.claude/CLAUDE.md): OpenScan is a trustless, open-source, standalone web-app and multi-chain blockchain explorer for Ethereum and Layer 2 networks. It allows direct interaction with verified smart contracts and supports local development chains.

## Project

- [OpenScan](https://raw.githubusercontent.com/openscan-explorer/explorer/8ccaa3283333eb4616efe3255398a77ca6d6ef93/README.md): Production IPFS Hash
- [Contributing to OpenScan](https://raw.githubusercontent.com/openscan-explorer/explorer/8ccaa3283333eb4616efe3255398a77ca6d6ef93/CONTRIBUTING.md): Thank you for your interest in contributing to OpenScan! This guide will help you get started.

## Development

- [Architecture Overview](https://raw.githubusercontent.com/openscan-explorer/explorer/8ccaa3283333eb4616efe3255398a77ca6d6ef93/.claude/rules/architecture.md): OpenScan follows a layered architecture with clear separation between data fetching, transformation, and presentation:
- [Testing](https://raw.githubusercontent.com/openscan-explorer/explorer/8ccaa3283333eb4616efe3255398a77ca6d6ef93/.claude/rules/testing.md): OpenScan has two test layers: Vitest for unit tests and Playwright for end-to-end browser tests against a real Vite dev server. Most test commands also live in commands.md; this file documents the *model* — projects, suites, mocking, and CI mapping — so future test work lands in the right place.
- [Git Workflow and Collaboration](https://raw.githubusercontent.com/openscan-explorer/explorer/8ccaa3283333eb4616efe3255398a77ca6d6ef93/.claude/rules/workflow.md): The project uses two workflows depending on the scope of changes:
- [Code Style and Quality](https://raw.githubusercontent.com/openscan-explorer/explorer/8ccaa3283333eb4616efe3255398a77ca6d6ef93/.claude/rules/code-style.md): ALWAYS run these commands before committing:
106 changes: 106 additions & 0 deletions scripts/generate-llms.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { execFileSync } from "node:child_process";
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";

const SOURCEY_VERSION = "3.6.5";
const SOURCE_COMMIT = "8ccaa3283333eb4616efe3255398a77ca6d6ef93";
const REPOSITORY = "openscan-explorer/explorer";

const SOURCE_LINKS = new Map([
["/claude/claude.html", ".claude/CLAUDE.md"],
["/project/readme.html", "README.md"],
["/project/contributing.html", "CONTRIBUTING.md"],
[
"/development/claude/rules/architecture.html",
".claude/rules/architecture.md",
],
["/development/claude/rules/testing.html", ".claude/rules/testing.md"],
["/development/claude/rules/workflow.html", ".claude/rules/workflow.md"],
["/development/claude/rules/code-style.html", ".claude/rules/code-style.md"],
]);

function assertPinnedSources() {
for (const sourcePath of SOURCE_LINKS.values()) {
const pinned = execFileSync(
"git",
["show", `${SOURCE_COMMIT}:${sourcePath}`],
{ encoding: "utf8" },
);
const current = readFileSync(resolve(sourcePath), "utf8");

if (current !== pinned) {
throw new Error(
`${sourcePath} differs from pinned commit ${SOURCE_COMMIT}. ` +
"Commit the source change, update SOURCE_COMMIT, and regenerate.",
);
}
}
}

function buildWithSourcey(outputDirectory) {
const version = execFileSync(
"npx",
["--yes", `sourcey@${SOURCEY_VERSION}`, "--version"],
{ encoding: "utf8" },
).trim();

if (version !== SOURCEY_VERSION) {
throw new Error(`Expected Sourcey ${SOURCEY_VERSION}, received ${version}`);
}

execFileSync(
"npx",
[
"--yes",
`sourcey@${SOURCEY_VERSION}`,
"build",
"--config",
"sourcey.config.ts",
"--output",
outputDirectory,
],
{ stdio: "inherit" },
);
}

function publishPinnedLinks(generated) {
let output = generated;
const rawBase = `https://raw.githubusercontent.com/${REPOSITORY}/${SOURCE_COMMIT}`;

for (const [generatedPath, sourcePath] of SOURCE_LINKS) {
const generatedLink = `](${generatedPath})`;
const occurrenceCount = output.split(generatedLink).length - 1;

if (occurrenceCount !== 1) {
throw new Error(
`Expected one generated link for ${generatedPath}, found ${occurrenceCount}`,
);
}

output = output.replace(generatedLink, `](${rawBase}/${sourcePath})`);
}

if (/\]\(\/[^)]+\.html\)/.test(output)) {
throw new Error(
"Generated llms.txt still contains an unpublished HTML link",
);
}

return `${output.trimEnd()}\n`;
}

assertPinnedSources();

const temporaryDirectory = mkdtempSync(join(tmpdir(), "openscan-llms-"));

try {
buildWithSourcey(temporaryDirectory);
const generated = readFileSync(join(temporaryDirectory, "llms.txt"), "utf8");
writeFileSync("public/llms.txt", publishPinnedLinks(generated), "utf8");
console.log(
`Published public/llms.txt from Sourcey ${SOURCEY_VERSION} and ${SOURCE_COMMIT}`,
);
} finally {
rmSync(temporaryDirectory, { recursive: true, force: true });
}
45 changes: 45 additions & 0 deletions sourcey.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Sourcey input manifest for OpenScan's agent-facing documentation index.
// The generated llms.txt is published by scripts/generate-llms.mjs.
export default {
name: "OpenScan",
repo: "https://github.com/openscan-explorer/explorer",
navigation: {
tabs: [
{
tab: "Overview",
slug: "",
groups: [
{
group: "Start here",
pages: [".claude/CLAUDE"],
},
],
},
{
tab: "Project",
slug: "project",
groups: [
{
group: "Project guides",
pages: ["README", "CONTRIBUTING"],
},
],
},
{
tab: "Development",
slug: "development",
groups: [
{
group: "Engineering guides",
pages: [
".claude/rules/architecture",
".claude/rules/testing",
".claude/rules/workflow",
".claude/rules/code-style",
],
},
],
},
],
},
};