Skip to content

Commit a1370cc

Browse files
committed
fix: 모든 패키지의 repository URL을 올바른 GitHub 링크로 업데이트
1 parent 9a0b61b commit a1370cc

10 files changed

Lines changed: 346 additions & 0 deletions

File tree

.claude/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# scrolloop AI harness
2+
3+
This directory configures Claude Code for scrolloop. The guiding principle from harness engineering: **enforce quality with mechanisms, not prompts**, and push every check to the fastest feedback layer.
4+
5+
```
6+
PostToolUse hook (ms) → pre-commit / husky (s) → CI (min) → human review (h)
7+
```
8+
9+
## What's here
10+
11+
| Path | Role |
12+
| -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
13+
| `../CLAUDE.md`, `../packages/core/CLAUDE.md` | Short, pointer-style context loaded at session start. Root persists through compaction; the nested one loads when you touch `core`. |
14+
| `settings.json` | Team-shared permissions + hooks. `settings.local.json` is personal (git-ignored), keep it minimal. |
15+
| `hooks/format.sh` | **PostToolUse** — prettier-writes and `oxlint --fix`es every file Claude edits; reports unfixable lint back so Claude self-corrects. You never hand-run prettier. |
16+
| `hooks/guard-bash.sh` | **PreToolUse(Bash)** — blocks `rm -rf /`, fork bombs, npm/yarn (pnpm-only), lockfile overwrites, force-push to master/main. |
17+
| `hooks/guard-write.sh` | **PreToolUse(Write/Edit)** — blocks edits to lockfiles, `dist/`, `coverage/`, `.turbo/`, build info. |
18+
| `agents/adapter-parity-reviewer.md` | Subagent: audits public-API parity across framework adapters in an isolated context. |
19+
| `commands/check.md` | `/check` — full local gate (oxlint + typecheck + test). |
20+
| `commands/release.md` | `/release` — changesets release flow (stops before publish). |
21+
22+
## Layering rationale
23+
24+
- **CLAUDE.md** = always-on, so it's deliberately tiny (the model's compliance drops as instructions pile up). Anything procedural moved to `commands/`; anything "always do X" moved to hooks.
25+
- **Hooks** = deterministic. They can't be ignored, hallucinated, or argued with, and cost little context.
26+
- **Permissions** in `settings.json` are defense-in-depth alongside the guard hooks: `allow` skips prompts for safe commands, `deny` hard-blocks, `ask` gates irreversible ones (push/publish).
27+
- **Subagent** keeps a noisy cross-package audit out of the main context.
28+
29+
## Maintenance
30+
31+
Review this harness **every 3–6 months and after every major model release.** Newer models need fewer hand-holding rules — a constraint that helped an old model becomes friction on a new one. When Claude repeatedly makes the same mistake, don't add a CLAUDE.md sentence — add a lint rule, a test, or a hook (the durable fix). When it reliably does the right thing without a rule, delete the rule.
32+
33+
Test a hook manually:
34+
35+
```bash
36+
echo '{"tool_input":{"file_path":"packages/core/src/index.ts"}}' | bash .claude/hooks/format.sh
37+
echo '{"tool_input":{"command":"npm install lodash"}}' | bash .claude/hooks/guard-bash.sh # should print BLOCKED, exit 2
38+
```
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
name: adapter-parity-reviewer
3+
description: Use after changing the public API of @scrolloop/core or any framework adapter. Audits that every adapter (react, preact, vue, svelte, react-native) exposes an equivalent public API and that core changes have been propagated. Returns a concrete parity report listing gaps and where to fix them.
4+
tools: Read, Grep, Glob, Bash
5+
model: inherit
6+
---
7+
8+
You audit cross-framework API parity for the scrolloop monorepo. Logic lives in `@scrolloop/core`; the adapters (`packages/{react,preact,vue,svelte,react-native}`) are thin bindings that must expose an equivalent public surface.
9+
10+
## What to do
11+
12+
1. Read `packages/core/src/index.ts` (and `@scrolloop/shared` exports) to establish the source-of-truth public API: component props, exported types, hooks/composables, and behaviors.
13+
2. For each adapter, read its `src/index.ts`, component(s) (`VirtualList`/`InfiniteList`), and `types.ts`. Map each adapter's public surface against core.
14+
3. Identify divergences:
15+
- props/options present in some adapters but missing in others (account for legitimate framework idioms: React `renderItem` vs Vue/Svelte slots/snippets — same capability, different shape; flag only true capability gaps).
16+
- types/defaults that drifted from core.
17+
- a core change not yet reflected in an adapter.
18+
- missing test coverage for a newly added prop/behavior.
19+
20+
## Output
21+
22+
A short report:
23+
24+
- **Source API**: the core surface you compared against.
25+
- **Parity table**: one row per capability × adapter (✓ present / ✗ missing / ≈ idiomatic-equivalent).
26+
- **Gaps**: each as `package/file:symbol — what's missing — suggested fix`.
27+
- **Verdict**: in-parity, or the ordered list of fixes.
28+
29+
Be precise with `file:line` references. Do not edit files — report only. If everything is in parity, say so plainly.

.claude/commands/check.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
description: Run the full local verification gate (fast lint, typecheck, tests) and summarize failures.
3+
allowed-tools: Bash(pnpm lint:fast), Bash(pnpm typecheck), Bash(pnpm test), Bash(pnpm lint:*), Bash(pnpm run:*), Bash(pnpm --filter:*)
4+
argument-hint: "[package filter, e.g. @scrolloop/core]"
5+
---
6+
7+
Run scrolloop's local verification gate and report results concisely. If `$ARGUMENTS` names a package, scope each step with `pnpm --filter $ARGUMENTS …`; otherwise run repo-wide.
8+
9+
Run in order and capture output:
10+
11+
1. `pnpm lint:fast` (oxlint)
12+
2. `pnpm typecheck`
13+
3. `pnpm test`
14+
15+
Then summarize:
16+
17+
- A ✓/✗ line per step.
18+
- For any failure, the specific file:line and error, plus the minimal fix — do not dump full logs.
19+
- If all pass: state it's ready for commit (remember: atomic Conventional Commits, no `Co-Authored-By`).
20+
21+
Do not commit or push. Stop after reporting unless asked to fix.

.claude/commands/release.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
description: Guide a changesets-based release for scrolloop (changeset → version → build → publish).
3+
allowed-tools: Bash(git status:*), Bash(git diff:*), Bash(git log:*), Bash(pnpm changeset:*), Bash(pnpm build:*), Bash(pnpm test:*), Bash(pnpm typecheck:*)
4+
---
5+
6+
Drive a scrolloop release using changesets. Base branch is `master`; `access` is public.
7+
8+
1. Confirm a clean tree on the right branch (`git status`) and review what changed since the last release (`git log` / `git diff`).
9+
2. Determine affected packages and the correct semver bump (patch/minor/major) per package. Internal deps bump as `patch`.
10+
3. Create the changeset: `pnpm changeset` — write a clear, user-facing summary line per changed package. If running non-interactively, write the changeset markdown under `.changeset/` directly with the right frontmatter.
11+
4. Verify before versioning: `pnpm typecheck`, `pnpm test`, `pnpm build`.
12+
5. Stop and report the plan (packages + bumps + changeset) for confirmation. Do **not** run `changeset version` or publish without explicit go-ahead — publishing is irreversible.
13+
14+
Reference: `RELEASING.md`. Commit style: atomic Conventional Commits, no `Co-Authored-By`.

.claude/hooks/format.sh

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/usr/bin/env bash
2+
# PostToolUse(Write|Edit|MultiEdit): auto-format + fast-lint the edited file.
3+
# Fastest feedback layer (ms). prettier writes; oxlint --fix fixes what it can and
4+
# reports the rest back to Claude (exit 2) so it self-corrects instead of moving on.
5+
set -uo pipefail
6+
7+
input=$(cat)
8+
file=$(printf '%s' "$input" | jq -r '.tool_input.file_path // empty')
9+
[ -z "${file:-}" ] && exit 0
10+
[ -f "$file" ] || exit 0
11+
cd "${CLAUDE_PROJECT_DIR:-.}" || exit 0
12+
13+
case "$file" in
14+
*.ts|*.tsx|*.js|*.jsx|*.mjs|*.cjs|*.vue|*.svelte|*.json|*.jsonc|*.md|*.css|*.scss|*.html|*.yml|*.yaml)
15+
pnpm exec prettier --write --log-level silent "$file" >/dev/null 2>&1 || true
16+
;;
17+
*)
18+
exit 0
19+
;;
20+
esac
21+
22+
case "$file" in
23+
*.ts|*.tsx|*.js|*.jsx|*.mjs|*.cjs|*.vue|*.svelte)
24+
if ! out=$(pnpm exec oxlint --fix "$file" 2>&1); then
25+
{
26+
echo "oxlint reports issues in $file that need a real fix (not auto-fixable):"
27+
echo "$out"
28+
} >&2
29+
exit 2
30+
fi
31+
;;
32+
esac
33+
34+
exit 0

.claude/hooks/guard-bash.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/env bash
2+
# PreToolUse(Bash): block clearly destructive / wrong-tool commands before they run.
3+
# Deterministic enforcement — not a suggestion in CLAUDE.md.
4+
set -uo pipefail
5+
6+
input=$(cat)
7+
cmd=$(printf '%s' "$input" | jq -r '.tool_input.command // empty')
8+
[ -z "${cmd:-}" ] && exit 0
9+
10+
block() { echo "BLOCKED by harness: $1" >&2; exit 2; }
11+
12+
case "$cmd" in
13+
*"rm -rf /"*|*"rm -fr /"*|*"rm -rf ~"*|*"rm -rf /*"*)
14+
block "destructive recursive delete of a root/home path" ;;
15+
*":(){ :|:& };:"*)
16+
block "fork bomb" ;;
17+
*"npm install"*|*"npm i "*|*"yarn add"*|*"yarn install"*|*"bun install"*|*"bun add"*)
18+
block "this repo uses pnpm — use 'pnpm install' / 'pnpm add'" ;;
19+
*"> pnpm-lock.yaml"*|*"> package-lock.json"*)
20+
block "do not overwrite lockfiles — let the package manager regenerate them" ;;
21+
esac
22+
23+
case "$cmd" in
24+
*"git push"*--force*|*"git push"*" -f"*)
25+
case "$cmd" in
26+
*master*|*main*) block "force-push to a protected branch (master/main)" ;;
27+
esac ;;
28+
esac
29+
30+
exit 0

.claude/hooks/guard-write.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env bash
2+
# PreToolUse(Write|Edit|MultiEdit): protect generated & lock files from edits,
3+
# with a teaching message so Claude does the right thing instead.
4+
set -uo pipefail
5+
6+
input=$(cat)
7+
file=$(printf '%s' "$input" | jq -r '.tool_input.file_path // empty')
8+
[ -z "${file:-}" ] && exit 0
9+
10+
block() { echo "BLOCKED by harness: $1 ($file)" >&2; exit 2; }
11+
12+
case "$file" in
13+
*/pnpm-lock.yaml|*/package-lock.json|*/yarn.lock)
14+
block "lockfiles are generated — run 'pnpm install'/'pnpm add' instead of editing" ;;
15+
*/dist/*|*/coverage/*|*/.turbo/*|*/node_modules/*)
16+
block "generated/build output — edit the source, then rebuild" ;;
17+
*.tsbuildinfo)
18+
block "generated TypeScript build info" ;;
19+
esac
20+
21+
exit 0

.claude/settings.json

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
{
2+
"$schema": "https://json.schemastore.org/claude-code-settings.json",
3+
"permissions": {
4+
"allow": [
5+
"Bash(pnpm test:*)",
6+
"Bash(pnpm run test:*)",
7+
"Bash(pnpm typecheck:*)",
8+
"Bash(pnpm run typecheck:*)",
9+
"Bash(pnpm lint:*)",
10+
"Bash(pnpm run lint:*)",
11+
"Bash(pnpm format:*)",
12+
"Bash(pnpm run format:*)",
13+
"Bash(pnpm build:*)",
14+
"Bash(pnpm run build:*)",
15+
"Bash(pnpm size:*)",
16+
"Bash(pnpm knip:*)",
17+
"Bash(pnpm changeset:*)",
18+
"Bash(pnpm --filter:*)",
19+
"Bash(pnpm exec vitest:*)",
20+
"Bash(pnpm exec tsc:*)",
21+
"Bash(pnpm exec eslint:*)",
22+
"Bash(pnpm exec oxlint:*)",
23+
"Bash(pnpm exec prettier:*)",
24+
"Bash(pnpm exec playwright:*)",
25+
"Bash(turbo run:*)",
26+
"Bash(git status:*)",
27+
"Bash(git diff:*)",
28+
"Bash(git log:*)",
29+
"Bash(git show:*)",
30+
"Bash(git branch:*)",
31+
"Bash(git stash list:*)",
32+
"Bash(gh pr view:*)",
33+
"Bash(gh pr diff:*)",
34+
"Bash(gh pr list:*)",
35+
"Bash(gh issue view:*)",
36+
"Bash(gh issue list:*)",
37+
"Bash(gh run list:*)",
38+
"Bash(gh run view:*)",
39+
"Bash(ls:*)",
40+
"Bash(rg:*)",
41+
"Bash(find:*)",
42+
"Bash(node:*)"
43+
],
44+
"deny": [
45+
"Read(./.env)",
46+
"Read(./.env.*)",
47+
"Read(./**/.env)",
48+
"Read(./**/.env.*)",
49+
"Write(./pnpm-lock.yaml)",
50+
"Edit(./pnpm-lock.yaml)",
51+
"Write(./package-lock.json)",
52+
"Edit(./package-lock.json)",
53+
"Write(./**/dist/**)",
54+
"Edit(./**/dist/**)",
55+
"Bash(npm install:*)",
56+
"Bash(npm i:*)",
57+
"Bash(yarn add:*)",
58+
"Bash(yarn install:*)"
59+
],
60+
"ask": [
61+
"Bash(git push:*)",
62+
"Bash(pnpm publish:*)",
63+
"Bash(pnpm release:*)",
64+
"Bash(npm publish:*)",
65+
"Bash(changeset publish:*)"
66+
]
67+
},
68+
"hooks": {
69+
"PostToolUse": [
70+
{
71+
"matcher": "Write|Edit|MultiEdit",
72+
"hooks": [
73+
{
74+
"type": "command",
75+
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/format.sh\""
76+
}
77+
]
78+
}
79+
],
80+
"PreToolUse": [
81+
{
82+
"matcher": "Bash",
83+
"hooks": [
84+
{
85+
"type": "command",
86+
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/guard-bash.sh\""
87+
}
88+
]
89+
},
90+
{
91+
"matcher": "Write|Edit|MultiEdit",
92+
"hooks": [
93+
{
94+
"type": "command",
95+
"command": "bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/guard-write.sh\""
96+
}
97+
]
98+
}
99+
]
100+
}
101+
}

CLAUDE.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# scrolloop
2+
3+
Framework-agnostic virtual & infinite scrolling. A platform-neutral **core** engine with thin **adapters** per framework. pnpm + turbo monorepo.
4+
5+
## Packages (`packages/*`)
6+
7+
| Package | Role |
8+
| ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
9+
| `@scrolloop/core` | Source of truth. Virtualizer, strategies, plugins, `InfiniteSource`. Zero framework deps. |
10+
| `@scrolloop/shared` | Shared infinite-loading state/utilities used by adapters. |
11+
| `react` `preact` `vue` `svelte` `react-native` | Thin adapters. They bind core to a framework's reactivity/render — they do **not** reimplement scrolling logic. |
12+
13+
**Architecture rule:** logic lives in `core`. Adapters only translate. A behavior change belongs in `core` (or `shared`); adapters then expose it. Keep the public API (`VirtualList` / `InfiniteList` props) equivalent across adapters — after an API change, use the `adapter-parity-reviewer` subagent.
14+
15+
## Commands
16+
17+
- Test: `pnpm test` · single pkg: `pnpm --filter @scrolloop/react test`
18+
- Typecheck: `pnpm typecheck` · Fast lint: `pnpm lint:fast` (oxlint) · Full lint: `pnpm lint`
19+
- Build: `pnpm build` · Bundle size: `pnpm size` · Dead code: `pnpm knip`
20+
- Full local gate: **`/check`** · Release flow: **`/release`**
21+
22+
Formatting & fast-lint run automatically on every file you edit (PostToolUse hook) — don't hand-run prettier.
23+
24+
## Conventions
25+
26+
- **pnpm only** (v10, node 24). Never `npm`/`yarn`. Don't edit lockfiles or `dist/` (generated; hooks block this).
27+
- **Commits:** Conventional Commits, minimal atomic units as work progresses (not one big batch). English scope, Korean body OK. **No `Co-Authored-By` trailer.**
28+
- TypeScript strict; let linters/prettier own style — don't add style notes here.
29+
- Tests colocated as `*.test.ts(x)` / under `__tests__/`; SSR tests `pnpm test:ssr`, E2E `pnpm test:e2e` (react).
30+
31+
## Workflow
32+
33+
- Releases use **changesets** (`pnpm changeset`); base branch `master`.
34+
- The automated AI pipeline opens `ai/issue-N` branches → PR to `develop` (see `docs/ai-pipeline.md`). Respect `ai:*` issue labels.
35+
36+
## Pointers
37+
38+
- Harness internals & maintenance: `.claude/README.md`
39+
- Core engine details: `packages/core/CLAUDE.md`
40+
- AI pipeline & labels: `docs/ai-pipeline.md`

packages/core/CLAUDE.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# @scrolloop/core
2+
3+
The framework-agnostic engine. **All scrolling/virtualization logic lives here**; adapters only bind it to a framework. If you're tempted to put logic in an adapter, it almost certainly belongs here or in `@scrolloop/shared`.
4+
5+
## Layout (`src/`)
6+
7+
- `virtualizer/` — windowing math: which items are visible, their offsets/sizes, overscan.
8+
- `strategies/` — measurement strategies (fixed / dynamic / estimated item sizes).
9+
- `plugins/` — opt-in behaviors layered onto the virtualizer.
10+
- `InfiniteSource.ts` — infinite-loading state machine (paging, thresholds, status).
11+
- `utils/`, `types/` — pure helpers and shared types. `index.ts` is the public surface.
12+
13+
## Rules
14+
15+
- **No framework imports.** No `react`, `vue`, DOM-framework, or `react-native` deps. Browser/DOM APIs only behind capability checks so SSR and RN stay safe.
16+
- Keep the public API (`index.ts`) stable and intentional — every adapter depends on it. After changing it, propagate to all adapters and run the `adapter-parity-reviewer` subagent.
17+
- Hot paths (scroll handlers, range computation) run per frame — avoid per-item allocations and O(n) scans where a binary search / cached offset works.
18+
- Cover new logic with colocated `*.test.ts` (vitest). `pnpm --filter @scrolloop/core test`.

0 commit comments

Comments
 (0)