mirror of
https://github.com/prdlk/leetcode.git
synced 2026-09-16 23:16:26 +00:00
docs(AGENTS): add terminal dashboard info and expand documentation for stats, work layout, and tooling preferences
This commit is contained in:
@@ -23,6 +23,7 @@ flowchart LR
|
|||||||
D1 -->|Sat midnight ET| Review[Review — Week N issue]
|
D1 -->|Sat midnight ET| Review[Review — Week N issue]
|
||||||
PI -->|"spaced-repetition.yml (6 AM ET cron, closed +3d/+7d)"| SR[Spaced Repetition — Month D issue]
|
PI -->|"spaced-repetition.yml (6 AM ET cron, closed +3d/+7d)"| SR[Spaced Repetition — Month D issue]
|
||||||
D1 --> Charts[SVG charts + /api/stats]
|
D1 --> Charts[SVG charts + /api/stats]
|
||||||
|
Charts -->|bun run stats| TUI[terminal dashboard]
|
||||||
```
|
```
|
||||||
|
|
||||||
- **Reconcile, don't react.** `close-solved.ts`, `close-topics.ts`, and the Worker's catalog sync recompute desired state from scratch each run: re-runs are no-ops, backfills need no special casing, closing is one-directional. `close-solved` posts its *whole* implemented set to `/admin/solved`, not just the issues it closed this run.
|
- **Reconcile, don't react.** `close-solved.ts`, `close-topics.ts`, and the Worker's catalog sync recompute desired state from scratch each run: re-runs are no-ops, backfills need no special casing, closing is one-directional. `close-solved` posts its *whole* implemented set to `/admin/solved`, not just the issues it closed this run.
|
||||||
@@ -56,6 +57,10 @@ bun run pick # scaffold a solution via leetcode-cli (fuzzy picker; Ta
|
|||||||
# re-solves → work/3, work/7. Each section hides what its own bucket
|
# re-solves → work/3, work/7. Each section hides what its own bucket
|
||||||
# already holds; index+descriptions cached in
|
# already holds; index+descriptions cached in
|
||||||
# ~/.local/share/leetcode/data.db — see apps/cli/db.ts)
|
# ~/.local/share/leetcode/data.db — see apps/cli/db.ts)
|
||||||
|
bun run stats # terminal dashboard over GET /api/stats — four panes (Overview,
|
||||||
|
# Ladder, Concepts, Activity); Tab/1-4 switches, ↑↓ scrolls,
|
||||||
|
# r refetches, q quits. Outside a TTY it prints every pane once.
|
||||||
|
# SRS_API points it at a local Worker like close-solved does.
|
||||||
bun run test # run ONE solution against LeetCode's judge (not a test suite;
|
bun run test # run ONE solution against LeetCode's judge (not a test suite;
|
||||||
# most recently tested first, stamped in the db on exit 0)
|
# most recently tested first, stamped in the db on exit 0)
|
||||||
bun run submit # submit ONE solution to LeetCode (newest scaffold first, nothing
|
bun run submit # submit ONE solution to LeetCode (newest scaffold first, nothing
|
||||||
@@ -92,12 +97,13 @@ curl -X POST -H "Authorization: Bearer $LINK_KEY" \
|
|||||||
|
|
||||||
## Important Files
|
## Important Files
|
||||||
|
|
||||||
- `apps/api/src/srs.ts` — SRS domain: the ladder (`new → +3 → +7 → retired`; fail resets to `+3`; first-ever log enters at `+3`), `WINDOWS`/`STAGES` (the one vocabulary shared by the charts, the `work/<n>` buckets and the review issues), ET date math, `workingDay()` (no scheduled date is ever a Sunday), seeded sampling, `logAttempt()` (the ONE write path — email taps, webhook, gate scoring all converge here), and overload leveling: at most `REVIEW_CAP` (3) reviews surface per day — `levelReviews()` (run by `sendDigest` on real sends only, never dry) gives everything past the cap a concrete future WORKING day, ≤ 3 per day, oldest first, instead of letting the due pile grow.
|
- `apps/api/src/srs.ts` — SRS domain: the ladder (`new → +3 → +7 → retired`; fail resets to `+3`; first-ever log enters at `+3`), `WINDOWS`/`STAGES`/`TEMPERATURES`/`DIFFICULTIES`/`SETS` (the one vocabulary shared by the charts, the `work/<n>` buckets, the review issues and the stats dashboard), ET date math, `workingDay()` (no scheduled date is ever a Sunday), `temperatureOf()` (days since a concept was last exercised → `hot`/`fresh`/`fading`/`cold`, bands derived from `WINDOWS` so they cannot drift; read-side only, nothing is scheduled off it), seeded sampling, `logAttempt()` (the ONE write path — email taps, webhook, gate scoring all converge here), and overload leveling: at most `REVIEW_CAP` (3) reviews surface per day — `levelReviews()` (run by `sendDigest` on real sends only, never dry) gives everything past the cap a concrete future WORKING day, ≤ 3 per day, oldest first, instead of letting the due pile grow.
|
||||||
- `apps/api/src/index.ts` — router + cron dispatch; `apps/api/wrangler.jsonc` — bindings (`DB`, `EMAIL`), crons, vars; secrets `GH_PAT`/`WEBHOOK_SECRET`/`LINK_KEY` via `wrangler secret put`.
|
- `apps/api/src/index.ts` — router + cron dispatch; `apps/api/wrangler.jsonc` — bindings (`DB`, `EMAIL`), crons, vars; secrets `GH_PAT`/`WEBHOOK_SECRET`/`LINK_KEY` via `wrangler secret put`.
|
||||||
- `apps/api/src/digest.ts` / `apps/api/src/email.tsx` — the daily digest, split data/presentation. `digest.ts` reads D1 into a `DigestData`; `email.tsx` owns every colour and every sentence, and renders both the HTML and (via its own `plainDigest`, not React Email's `plainText` mode, which flattens the tables) the text alternative. The retrieval rules hold by construction: `DigestRow` has no title and no issue field, so a review or drill line *cannot* leak the topic or a solution link. Subject is `(Day N/56) LeetCode Daily Digest`.
|
- `apps/api/src/digest.ts` / `apps/api/src/email.tsx` — the daily digest, split data/presentation. `digest.ts` reads D1 into a `DigestData`; `email.tsx` owns every colour and every sentence, and renders both the HTML and (via its own `plainDigest`, not React Email's `plainText` mode, which flattens the tables) the text alternative. The retrieval rules hold by construction: `DigestRow` has no title and no issue field, so a review or drill line *cannot* leak the topic or a solution link. Subject is `(Day N/56) LeetCode Daily Digest`.
|
||||||
- `apps/api/src/png.ts` — hand-rolled PNG encoder (RGB8, one IDAT, zlib via `CompressionStream("deflate")`, CRC32, 5×7 bitmap font). It exists because every major email client refuses remote SVG, so `/chart/heatmap.png` rasters the heatmap for the digest while `/chart/heatmap.svg` keeps serving the README byte-for-byte. Both come from one `heatmapCells()` so the two pictures cannot drift.
|
- `apps/api/src/png.ts` — hand-rolled PNG encoder (RGB8, one IDAT, zlib via `CompressionStream("deflate")`, CRC32, 5×7 bitmap font). It exists because every major email client refuses remote SVG, so `/chart/heatmap.png` rasters the heatmap for the digest while `/chart/heatmap.svg` keeps serving the README byte-for-byte. Both come from one `heatmapCells()` so the two pictures cannot drift.
|
||||||
- `apps/cli/sync.ts` — work→docs contract: only `## Solution` onward is script-owned on existing pages; human prose is never touched; never hand-write solution pages or edit inside `## Solution`.
|
- `apps/cli/sync.ts` — work→docs contract: only `## Solution` onward is script-owned on existing pages; human prose is never touched; never hand-write solution pages or edit inside `## Solution`.
|
||||||
- `apps/cli/work.ts` — the `work/` layout: bucket paths, `firstSolved()` (one `git log`, ET dates, keyed by LC number so past layout moves don't matter), and `reviewQueues()` (what each window still owes). Everything that needs to know where a solution file lives, or when it was solved, goes through here.
|
- `apps/cli/work.ts` — the `work/` layout: bucket paths, `isSolution()`/`solvedInBucket()` (the stub test, shared with `close-solved.ts`), `firstSolved()` (one `git log`, ET dates, keyed by LC number so past layout moves don't matter), and `reviewQueues()` (what each window still owes). Everything that needs to know where a solution file lives, or when it was solved, goes through here.
|
||||||
|
- `apps/api/src/stats.ts` / `apps/cli/stats.ts` / `apps/cli/tui.ts` / `apps/cli/api.ts` — the read side. `buildStats()` is the ONE aggregation: phases, ladder, difficulty, per-topic temperature, gates, the 14-day due queue, and `heat` (every campaign day, zeros and future days included — the docs page windows the tail off it rather than asking a second question). Two readers consume it, the docs island and `bun run stats`, so its shape is both their contract: change all three together. `tui.ts` is hand-rolled ANSI (bars, sparklines, heat cells, alternate screen) on charts.ts's palette, and every chart differs by GLYPH as well as by colour so a piped or `NO_COLOR` dashboard still reads. The TUI recomputes nothing: the only local fact it shows is the `work/` solution count, which is inventory, not schedule.
|
||||||
- `README.md` live charts are Worker endpoints (`/chart/*.svg`, `/badge/gate.svg`, 5-min Camo cache); the docs `/progress` page fetches `/api/stats` client-side (`apps/docs/islands/ProgressDashboard.tsx`).
|
- `README.md` live charts are Worker endpoints (`/chart/*.svg`, `/badge/gate.svg`, 5-min Camo cache); the docs `/progress` page fetches `/api/stats` client-side (`apps/docs/islands/ProgressDashboard.tsx`).
|
||||||
- `apps/docs/blume.config.ts` — site base `/leetcode`; `README.md` campaign table doubles as topic-map input to `sync.ts` (`readmeTopics()`, `TOPIC_ALIASES`).
|
- `apps/docs/blume.config.ts` — site base `/leetcode`; `README.md` campaign table doubles as topic-map input to `sync.ts` (`readmeTopics()`, `TOPIC_ALIASES`).
|
||||||
- Old `.github/srs/` JSON state, its `srs-*` Actions, and the one-shot `import-srs.ts` importer are RETIRED and deleted — do not resurrect; git history is the record.
|
- Old `.github/srs/` JSON state, its `srs-*` Actions, and the one-shot `import-srs.ts` importer are RETIRED and deleted — do not resurrect; git history is the record.
|
||||||
@@ -105,8 +111,7 @@ curl -X POST -H "Authorization: Bearer $LINK_KEY" \
|
|||||||
## Runtime/Tooling Preferences
|
## Runtime/Tooling Preferences
|
||||||
|
|
||||||
- **Bun only**: run scripts with `bun apps/cli/<name>.ts` (or the root `bun run` aliases), install with `bun install --frozen-lockfile`. One workspace (`apps/*`), one root `bun.lock`. Node 22 appears only in `deploy.yml` because Blume requires it.
|
- **Bun only**: run scripts with `bun apps/cli/<name>.ts` (or the root `bun run` aliases), install with `bun install --frozen-lockfile`. One workspace (`apps/*`), one root `bun.lock`. Node 22 appears only in `deploy.yml` because Blume requires it.
|
||||||
- **No root tsconfig, no ESLint, no Prettier** — match surrounding style by hand. `apps/api/` has a strict `tsconfig.json`; `Env` types are generated (`bunx wrangler types`), never hand-written.
|
- **Near-zero runtime npm dependencies** — SVG, PNG, terminal ANSI, HMAC (WebCrypto), GraphQL are hand-rolled; Actions scripts use Bun builtins + `fetch` only. The **one** exception is the digest email: `apps/api/src/email.tsx` renders React Email (`react`, `react-dom`, `@react-email/components`, `@react-email/render`) inside the Worker, because hand-rolling table-based email HTML that survives Gmail *and* Outlook is not worth owning. It costs ~235 KB gzipped of a 3 MB budget, needs no `nodejs_compat` (it resolves to `renderToReadableStream`), and requires `"jsx": "react-jsx"` in `apps/api/tsconfig.json`. Do not extend this exception to any other module.
|
||||||
- **Near-zero runtime npm dependencies** — SVG, PNG, HMAC (WebCrypto), GraphQL are hand-rolled; Actions scripts use Bun builtins + `fetch` only. The **one** exception is the digest email: `apps/api/src/email.tsx` renders React Email (`react`, `react-dom`, `@react-email/components`, `@react-email/render`) inside the Worker, because hand-rolling table-based email HTML that survives Gmail *and* Outlook is not worth owning. It costs ~235 KB gzipped of a 3 MB budget, needs no `nodejs_compat` (it resolves to `renderToReadableStream`), and requires `"jsx": "react-jsx"` in `apps/api/tsconfig.json`. Do not extend this exception to any other module.
|
|
||||||
- Local GitHub auth falls back to `gh auth token`; scripts run fine outside Actions.
|
- Local GitHub auth falls back to `gh auth token`; scripts run fine outside Actions.
|
||||||
|
|
||||||
## Testing & QA
|
## Testing & QA
|
||||||
|
|||||||
Reference in New Issue
Block a user