Skip to content
Merged
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
16 changes: 8 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,19 @@ jobs:
with:
node-version: 22
cache: npm
cache-dependency-path: worker/package-lock.json
cache-dependency-path: loop-library/worker/package-lock.json

- name: Install Worker dependencies
run: npm ci --prefix worker
run: npm ci --prefix loop-library/worker

- name: Validate site and skill sources
run: |
node --check site/script.js
node scripts/check.mjs
python3 -m json.tool site/.herenow/data.json >/dev/null
python3 -m json.tool site/.herenow/proxy.json >/dev/null
python3 -m json.tool scripts/seo-geo-query-benchmark.json >/dev/null
node --check loop-library/site/script.js
node loop-library/scripts/check.mjs
python3 -m json.tool loop-library/site/.herenow/data.json >/dev/null
python3 -m json.tool loop-library/site/.herenow/proxy.json >/dev/null
python3 -m json.tool loop-library/scripts/seo-geo-query-benchmark.json >/dev/null
git diff --check

- name: Test form Worker
run: npm --prefix worker run check
run: npm --prefix loop-library/worker run check
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
.DS_Store
/.herenow/
site/.herenow/state.json
loop-library/site/.herenow/state.json
node_modules/
.wrangler/
__pycache__/
*.py[cod]
/.playwright-mcp/
worker/.dev.vars
worker/.wrangler/
loop-library/worker/.dev.vars
loop-library/worker/.wrangler/
54 changes: 35 additions & 19 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
# Loop Library Operating Rules
# Loopy Repository Operating Rules

This repository holds two separate but related parts:

- **Loop Library website** — the public catalog (site shell, database, and
rendering). All website code lives under [`loop-library/`](loop-library/)
(`loop-library/site/`, `loop-library/worker/`, `loop-library/scripts/`,
`loop-library/audits/`).
- **Loopy skill** — the installable agent skill in
[`skills/loopy/`](skills/loopy/), with the compatibility alias in
[`skills/loop-library/`](skills/loop-library/).

The operating rules below govern the Loop Library website unless they call out
the skill explicitly. Live URLs that contain `/loop-library/` and the
`loop-library-forms` Worker name are deployed identifiers and do not change with
this repository layout.

## Adding or editing loops

Expand All @@ -12,10 +27,10 @@

```bash
LOOP_PUBLISH_TOKEN=... \
npm --prefix worker run loop:publish -- /path/to/loop.json
npm --prefix loop-library/worker run loop:publish -- /path/to/loop.json
```

Use `worker/examples/loop.json` as the record template. The command validates
Use `loop-library/worker/examples/loop.json` as the record template. The command validates
the complete record before writing it, and the Worker records every revision.
- Every loop must have a stable slug, unique number, search title and
description, contributor attribution, published and modified dates,
Expand All @@ -27,18 +42,18 @@
reviewed HTTPS `socialImageUrl` is supplied.
- Keep bootstrap and backup exports outside the repository with owner-only
permissions. The one-time bootstrap command requires an explicit private
file path; routine recovery exports use `npm --prefix worker run loops:export`.
file path; routine recovery exports use `npm --prefix loop-library/worker run loops:export`.
Restore an export only into a fresh empty catalog with
`npm --prefix worker run loops:restore`; never overwrite a live catalog.
`npm --prefix loop-library/worker run loops:restore`; never overwrite a live catalog.
- Changes to the site shell, Worker, schema, or renderers still go through
GitHub. Run the full repository checks before committing those code changes:

```bash
node --check site/script.js
node scripts/check.mjs
npm --prefix worker run check
python3 -m json.tool site/.herenow/data.json >/dev/null
python3 -m json.tool scripts/seo-geo-query-benchmark.json >/dev/null
node --check loop-library/site/script.js
node loop-library/scripts/check.mjs
npm --prefix loop-library/worker run check
python3 -m json.tool loop-library/site/.herenow/data.json >/dev/null
python3 -m json.tool loop-library/scripts/seo-geo-query-benchmark.json >/dev/null
git diff --check
```

Expand All @@ -51,7 +66,7 @@
- The loop form writes to the here.now Site Data collection `suggestions`. The
weekly email form writes to `weekly_signups`.
- Keep both collections owner-write-only. Browser clients must send submissions
through the Cloudflare Worker in `worker/`; never expose here.now owner
through the Cloudflare Worker in `loop-library/worker/`; never expose here.now owner
credentials or allow direct public inserts.
- Keep Turnstile validation for the expected action, hostname, and origin, plus
the existing schema checks, rate limits, duplicate suppression, honeypot,
Expand All @@ -77,7 +92,7 @@ The production Worker serves at
deployment checkout:

```bash
cd worker
cd loop-library/worker
npm ci
npm exec -- wrangler secret put TURNSTILE_SITE_KEY
npm exec -- wrangler secret put TURNSTILE_SECRET_KEY
Expand Down Expand Up @@ -112,7 +127,7 @@ npm run deploy
browser code, logs, or committed development files. Configure them with:

```bash
cd worker
cd loop-library/worker
npm exec -- wrangler secret put SESSION_SECRET
npm exec -- wrangler secret put GITHUB_OAUTH_CLIENT_ID
npm exec -- wrangler secret put GITHUB_OAUTH_CLIENT_SECRET
Expand All @@ -132,12 +147,13 @@ npm run deploy
- Deploy and verify the Worker before publishing a shell or proxy manifest that
exposes voting or auth routes.

For local development, copy `worker/.dev.vars.example` to `worker/.dev.vars`,
replace the here.now development credentials, then run:
For local development, copy `loop-library/worker/.dev.vars.example` to
`loop-library/worker/.dev.vars`, replace the here.now development credentials,
then run:

```bash
npm --prefix worker run dev
python3 -m http.server 4173 --directory site
npm --prefix loop-library/worker run dev
python3 -m http.server 4173 --directory loop-library/site
```

Review or delete private records from the here.now dashboard under
Expand All @@ -157,8 +173,8 @@ curl -sS "https://here.now/api/v1/publishes/{slug}/data/weekly_signups?limit=50"
changes, then deploy the affected site from the newest `origin/main` commit
that contains those changes.
- Never deploy from a task worktree, dirty checkout, feature branch, or partial
file overlay. Publish the complete `site/` directory from a clean deployment
checkout on latest integrated main.
file overlay. Publish the complete `loop-library/site/` directory from a clean
deployment checkout on latest integrated main.
- Serialize deployments with
`$HOME/.codex/deploy-locks/loop-library.lock`. Wait for an
active deployment, then fetch and fast-forward again before selecting the
Expand Down
42 changes: 21 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Loop Library has two separate but related parts in this repository:

| Part | What it is | Where it lives |
| --- | --- | --- |
| **Loop Library website** | The public catalog where people and agents can browse published loops, read them, and copy their prompts. No installation is required. | [Live website](https://signals.forwardfuture.com/loop-library/) · shell in [`site/`](site/), database and rendering in [`worker/`](worker/) |
| **Loop Library website** | The public catalog where people and agents can browse published loops, read them, and copy their prompts. No installation is required. | [Live website](https://signals.forwardfuture.com/loop-library/) · all website code under [`loop-library/`](loop-library/) (shell in [`loop-library/site/`](loop-library/site/), database and rendering in [`loop-library/worker/`](loop-library/worker/)) |
| **Loopy skill** | An optional installable guide that helps an AI agent discover, find, audit, repair, craft, run, debrief, or prepare loops for publication. It uses the website's live catalog when recommending or publishing loops. | source in [`skills/loopy/`](skills/loopy/) |

The website is the library; Loopy is a companion way to work with it. You
Expand Down Expand Up @@ -96,14 +96,14 @@ You need Node.js and `npx`. Pick the platform you use:

| Platform | Install command |
| --- | --- |
| Codex | `npx skills add Forward-Future/loop-library --skill loopy --agent codex -g -y` |
| Cursor | `npx skills add Forward-Future/loop-library --skill loopy --agent cursor -g -y` |
| Claude Code | `npx skills add Forward-Future/loop-library --skill loopy --agent claude-code -g -y` |
| Codex | `npx skills add Forward-Future/loopy --skill loopy --agent codex -g -y` |
| Cursor | `npx skills add Forward-Future/loopy --skill loopy --agent cursor -g -y` |
| Claude Code | `npx skills add Forward-Future/loopy --skill loopy --agent claude-code -g -y` |

To install it for all three at once:

```bash
npx skills add Forward-Future/loop-library \
npx skills add Forward-Future/loopy \
--skill loopy \
--agent codex \
--agent cursor \
Expand All @@ -115,12 +115,12 @@ Using another agent? Run the interactive installer and choose from the agents
it detects:

```bash
npx skills add Forward-Future/loop-library --skill loopy -g
npx skills add Forward-Future/loopy --skill loopy -g
```

The command parts mean:

- `Forward-Future/loop-library` is the GitHub repository to install from.
- `Forward-Future/loopy` is the GitHub repository to install from.
- `--skill loopy` selects this skill from the repository.
- `--agent ...` selects the agent that should receive it.
- `-g` makes it available in all your projects. Leave `-g` off to install it
Expand Down Expand Up @@ -264,12 +264,12 @@ Public loops are stored in the catalog database attached to the Cloudflare
Worker. Publishing a reviewed loop does not require a GitHub commit or a static
site deployment.

Copy `worker/examples/loop.json` somewhere outside the repository, fill in the
record, and run:
Copy `loop-library/worker/examples/loop.json` somewhere outside the repository,
fill in the record, and run:

```bash
LOOP_PUBLISH_TOKEN=... \
npm --prefix worker run loop:publish -- /path/to/loop.json
npm --prefix loop-library/worker run loop:publish -- /path/to/loop.json
```

The command validates the record and publishes the homepage row, detail page,
Expand All @@ -283,7 +283,7 @@ GitHub:

```bash
LOOP_PUBLISH_TOKEN=... \
npm --prefix worker run loops:import -- /private/path/bootstrap.json
npm --prefix loop-library/worker run loops:import -- /private/path/bootstrap.json
```

Set a long random `LOOP_PUBLISH_TOKEN` as a Worker secret. The catalog uses a
Expand All @@ -295,14 +295,14 @@ Create a private backup of the current database with:

```bash
LOOP_PUBLISH_TOKEN=... \
npm --prefix worker run loops:export -- /private/path/catalog-backup.ndjson
npm --prefix loop-library/worker run loops:export -- /private/path/catalog-backup.ndjson
```

Restore that snapshot only into a fresh, empty catalog database:

```bash
LOOP_PUBLISH_TOKEN=... \
npm --prefix worker run loops:restore -- /private/path/catalog-backup.ndjson
npm --prefix loop-library/worker run loops:restore -- /private/path/catalog-backup.ndjson
```

Bootstrap and backup files must be owner-only (`chmod 600`). Exports include
Expand All @@ -318,20 +318,20 @@ this migration does not rewrite repository history or disrupt existing clones.
### Preview locally

```bash
python3 -m http.server 4173 --directory site
python3 -m http.server 4173 --directory loop-library/site
```

Then open `http://localhost:4173`.

### Validate a change

```bash
npm ci --prefix worker
node --check site/script.js
node scripts/check.mjs
npm --prefix worker run check
python3 -m json.tool site/.herenow/data.json >/dev/null
python3 -m json.tool scripts/seo-geo-query-benchmark.json >/dev/null
npm ci --prefix loop-library/worker
node --check loop-library/site/script.js
node loop-library/scripts/check.mjs
npm --prefix loop-library/worker run check
python3 -m json.tool loop-library/site/.herenow/data.json >/dev/null
python3 -m json.tool loop-library/scripts/seo-geo-query-benchmark.json >/dev/null
git diff --check
```

Expand All @@ -340,7 +340,7 @@ git diff --check
Voting is stored in a dedicated SQLite Durable Object. Reading totals is
public, but casting, changing, or removing a vote requires a GitHub login.
Set `SESSION_SECRET` and the GitHub OAuth client credentials as Worker
secrets; use `worker/.dev.vars.example` for local variable names only. Register
secrets; use `loop-library/worker/.dev.vars.example` for local variable names only. Register
the canonical callbacks shown in `AGENTS.md`, then deploy the Worker before the
site shell because the shell calls the new auth and vote routes.

Expand Down
File renamed without changes.
44 changes: 23 additions & 21 deletions scripts/check.mjs → loop-library/scripts/check.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import { access, readFile, readdir } from "node:fs/promises";
import { fileURLToPath } from "node:url";
import path from "node:path";

const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const siteRoot = path.join(root, "site");
const workerRoot = path.join(root, "worker");
const skillRoot = path.join(root, "skills", "loopy");
const legacySkillRoot = path.join(root, "skills", "loop-library");
const here = path.dirname(fileURLToPath(import.meta.url));
const websiteRoot = path.resolve(here, "..");
const repoRoot = path.resolve(here, "..", "..");
const siteRoot = path.join(websiteRoot, "site");
const workerRoot = path.join(websiteRoot, "worker");
const skillRoot = path.join(repoRoot, "skills", "loopy");
const legacySkillRoot = path.join(repoRoot, "skills", "loop-library");

const [
html,
Expand Down Expand Up @@ -65,8 +67,8 @@ const [
readFile(path.join(legacySkillRoot, "references", "run.md"), "utf8"),
readFile(path.join(legacySkillRoot, "references", "debrief.md"), "utf8"),
readFile(path.join(legacySkillRoot, "references", "publish.md"), "utf8"),
readFile(path.join(root, "README.md"), "utf8"),
readFile(path.join(root, "AGENTS.md"), "utf8"),
readFile(path.join(repoRoot, "README.md"), "utf8"),
readFile(path.join(repoRoot, "AGENTS.md"), "utf8"),
]);

const workerPackage = JSON.parse(workerPackageSource);
Expand All @@ -86,21 +88,21 @@ const collection = structuredData["@graph"].find(

// GitHub contains only the shell and application. Published loop records and
// generated public catalog surfaces must remain database-only.
for (const relativePath of [
"scripts/loop-data.mjs",
"scripts/build-loop-pages.mjs",
"scripts/build-skill-catalog.mjs",
"scripts/build-social-images.mjs",
"scripts/validate-loop-data.mjs",
"site/catalog.json",
"site/catalog.md",
"site/catalog.txt",
"site/feed.xml",
"site/sitemap.xml",
"site/llms.txt",
"skills/loopy/references/catalog.md",
for (const absolutePath of [
path.join(websiteRoot, "scripts/loop-data.mjs"),
path.join(websiteRoot, "scripts/build-loop-pages.mjs"),
path.join(websiteRoot, "scripts/build-skill-catalog.mjs"),
path.join(websiteRoot, "scripts/build-social-images.mjs"),
path.join(websiteRoot, "scripts/validate-loop-data.mjs"),
path.join(siteRoot, "catalog.json"),
path.join(siteRoot, "catalog.md"),
path.join(siteRoot, "catalog.txt"),
path.join(siteRoot, "feed.xml"),
path.join(siteRoot, "sitemap.xml"),
path.join(siteRoot, "llms.txt"),
path.join(skillRoot, "references", "catalog.md"),
]) {
await assert.rejects(access(path.join(root, relativePath)), undefined, relativePath);
await assert.rejects(access(absolutePath), undefined, absolutePath);
}

const loopPageFiles = await readdir(path.join(siteRoot, "loops"), {
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@
<a href="../learn/">Learn</a>
<a href="./" aria-current="page">For agents</a>
<a
href="https://github.com/Forward-Future/loop-library/tree/main/skills/loopy"
href="https://github.com/Forward-Future/loopy/tree/main/skills/loopy"
target="_blank"
rel="noopener noreferrer"
aria-label="Loopy skill on GitHub"
Expand Down Expand Up @@ -166,7 +166,7 @@
<a href="../learn/">Learn</a>
<a href="./" aria-current="page">For agents</a>
<a
href="https://github.com/Forward-Future/loop-library/tree/main/skills/loopy"
href="https://github.com/Forward-Future/loopy/tree/main/skills/loopy"
target="_blank"
rel="noopener noreferrer"
aria-label="Loopy skill on GitHub"
Expand Down Expand Up @@ -254,7 +254,7 @@ <h2 id="install-title">Install Loopy</h2>
approval-gated publication preparation.
</p>
<div class="agent-install-block">
<code>npx skills add Forward-Future/loop-library --skill loopy -g</code>
<code>npx skills add Forward-Future/loopy --skill loopy -g</code>
</div>
<p>
Installing Loopy still does not grant runtime permissions or
Expand Down
File renamed without changes
File renamed without changes
Loading
Loading